/* ** 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/Commando/natsock.cpp $* * * * $Author:: Steve_t $* * * * $Modtime:: 12/09/01 6:58p $* * * * $Revision:: 4 $* * * *---------------------------------------------------------------------------------------------* * 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 "natsock.h" #include "nataddr.h" #include "systimer.h" /* ** All instances are tracked here. */ DynamicVectorClass SocketHandlerClass::AllSocketHandlers; /*********************************************************************************************** * SocketHandlerClass::SocketHandlerClass -- Class constructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 2/24/00 12:23PM ST : Created * *=============================================================================================*/ SocketHandlerClass::SocketHandlerClass(void) { Socket = INVALID_SOCKET; IncomingPort = -1; OutgoingPort = -1; CanService = true; for (int i=0 ; ih_addr_list); for ( ;; ) { if ( !*addresses ) break; /* ** Read the next address */ unsigned long address = **addresses++; DebugString(("SocketHandlerClass - Found local address: %d.%d.%d.%d\n", address & 0xff, (address & 0xff00) >> 8, (address & 0xff0000) >> 16, (address & 0xff000000) >> 24)); /* ** Add it to the local address list. */ unsigned char *a = new unsigned char [4]; * ((unsigned long*) a) = address; LocalAddresses.Add (a); } /* ** Set linger options for the UDP socket. Don't think this is needed. */ ling.l_onoff = 0; // linger off ling.l_linger = 0; // timeout in seconds (ie close now) setsockopt (Socket, SOL_SOCKET, SO_LINGER, (LPSTR)&ling, sizeof(ling)); /* ** Specify the size of the receive buffer. */ int err = setsockopt(Socket, SOL_SOCKET, SO_RCVBUF, (char*)&socket_receive_buffer_size, 4); if (err == INVALID_SOCKET) { DebugString(("SocketHandlerClass - Failed to set socket option SO_RCVBUF - error code %d.\n", LAST_ERROR)); fw_assert ( err != INVALID_SOCKET); } else { DebugString(("SocketHandlerClass - Socket option SO_RCVBUF set OK\n")); } /* ** Specify the size of the send buffer. */ err = setsockopt ( Socket, SOL_SOCKET, SO_SNDBUF, (char*)&socket_transmit_buffer_size, 4); if ( err == INVALID_SOCKET ) { DebugString(("SocketHandlerClass - Failed to set socket option SO_SNDBUF - error code %d.\n", LAST_ERROR)); fw_assert ( err != INVALID_SOCKET ); } else { DebugString(("SocketHandlerClass - Socket option SO_SNDBUF set OK\n")); } /* ** Set the blocking mode of the socket to non-blocking. */ unsigned long nonblocking = true; err = ioctlsocket(Socket, FIONBIO, &nonblocking); if (err) { DebugString(("SocketHandlerClass - Failed to set socket to non-blocking - error code %d.\n", LAST_ERROR)); } DebugString(("SocketHandlerClass - UDP Socket init complete\n")); return (true); } /*********************************************************************************************** * SocketHandlerClass::Close -- Closes the socket if it's open. Shuts down the class. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 2/24/00 12:43PM ST : Created * *=============================================================================================*/ void SocketHandlerClass::Close(void) { /* ** Clear out any old local addresses from the local address list. */ while (LocalAddresses.Count()) { delete LocalAddresses[0]; LocalAddresses.Delete(0); } if (Socket != INVALID_SOCKET) { DebugString(("SocketHandlerClass - Closing socket %d bound to port %d\n", Socket, IncomingPort)); if (closesocket(Socket) != 0) { DebugString(("SocketHandlerClass - closesocket failed with error code %d\n", LAST_ERROR)); } Socket = INVALID_SOCKET; Service_Never(); } } /*********************************************************************************************** * SocketHandlerClass::Discard_In_Buffers -- Discard any packets in our incoming buffers * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 3/1/00 11:52AM ST : Created * *=============================================================================================*/ void SocketHandlerClass::Discard_In_Buffers(void) { WinsockBufferType *packet; while (InBuffers.Count()) { packet = InBuffers[0]; if (packet->IsAllocated) { delete packet; } else { packet->InUse = false; InBuffersUsed--; } InBuffers.Delete(0); } InBuffersUsed = 0; InBufferArrayPos = 0; } /*********************************************************************************************** * SocketHandlerClass::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 SocketHandlerClass::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; } /*********************************************************************************************** * SocketHandlerClass::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 SocketHandlerClass::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); } } /*********************************************************************************************** * SocketHandlerClass::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 SocketHandlerClass::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; Build_Packet_CRC(packet); /* ** Add it to our out list. */ OutBuffers.Add(packet); /* ** Send it now if possible. */ Service(); } /*********************************************************************************************** * SocketHandlerClass::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 SocketHandlerClass::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); #ifdef SIM_BAD_CONNECTION bool found = false; for (int b=0 ; bLag == 0) { packetnum = b; found = true; break; } } if (!found) { return (0); } #endif //_SIM_BAD_CONNECTION /* ** 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(("SocketHandlerClass - SocketHandlerClass::Read - Returning %d bytes. %d InBuffers left\n", bytes, InBuffers.Count())); return (bytes); } /*********************************************************************************************** * SocketHandlerClass::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 SocketHandlerClass::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(("SocketHandlerClass - SocketHandlerClass::Peek - Returning packet %d of %d bytes. %d InBuffers left\n", packetnum, bytes, InBuffers.Count())); return (bytes); } /*********************************************************************************************** * SocketHandlerClass::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 SocketHandlerClass::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 SocketHandlerClass::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(("SocketHandlerClass - Error in Winsock packet CRC\n")); return (false); } /*********************************************************************************************** * SocketHandlerClass::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 *SocketHandlerClass::Get_New_Out_Buffer(void) { WinsockBufferType *buffer = NULL; int pos; fw_assert (OutBuffersUsed <= MAX_STATIC_BUFFERS); /* ** If there are no more free buffers in the heap then allocate one. */ if (OutBuffersUsed == 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 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); } /*********************************************************************************************** * SocketHandlerClass::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 *SocketHandlerClass::Get_New_In_Buffer(void) { WinsockBufferType *buffer = NULL; int pos; fw_assert (InBuffersUsed <= MAX_STATIC_BUFFERS); /* ** If there are no more free buffers in the heap then allocate one. */ if (InBuffersUsed == 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 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); } /*********************************************************************************************** * SocketHandlerClass::Service_All -- Service all socket handlers. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/6/2001 2:08PM ST : Created * *=============================================================================================*/ void SocketHandlerClass::Service_All(void) { for (int i=0 ; iService(); } } /*********************************************************************************************** * SocketHandlerClass::Service -- Service the connection - do all reads and writes * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 3/2/00 1:04PM ST : Created * *=============================================================================================*/ void SocketHandlerClass::Service(void) { unsigned long bytes; struct sockaddr_in addr; int addr_len; WinsockBufferType *packet; int result; unsigned long timeout_check = TIMEGETTIME(); int times = 0; if (!CanService) { return; } 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(("SocketHandlerClass - recvfrom returned error code %d\n", LAST_ERROR)); Clear_Socket_Error(); break; } else { /* ** Possibly throw away packet if loss testing is enabled. */ #ifdef PACKET_LOSS_PERCENTAGE fw_assert(RAND_MAX < 0x10000 * 100); int r = rand(); if (((r*100) / RAND_MAX) < PACKET_LOSS_PERCENTAGE) { continue; } #endif //PACKET_LOSS_PERCENTAGE /* ** No error, result should be the requested number of bytes. ** Not so, it can be less if there are multiple packets waiting. */ //fw_assert(result == (int)bytes); /* ** 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(("SocketHandlerClass - 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(("SocketHandlerClass - recvfrom %s ; %d\n", IPAddressClass(packet->Address).As_String(), (unsigned int)((unsigned short)ntohs(addr.sin_port)))); #ifdef SIM_BAD_CONNECTION /* ** Add in any simulated lag. */ int lag = Sim_Random_Pick(MINIMUM_LAG, MAXIMUM_LAG); packet->Lag = (lag * TIMER_SECOND) / 1000; #endif //SIM_BAD_CONNECTION /* ** Add the holding buffer to the packet list. */ InBuffers.Add (packet); DebugString(("SocketHandlerClass - InBuffers.Count() == %d\n", InBuffers.Count())); } } } } } //DebugString(("SocketHandlerClass - 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)OutgoingPort); } else { addr.sin_port = htons(packet->Port); } memcpy (&addr.sin_addr.s_addr, packet->Address, 4); DebugString(("SocketHandlerClass - 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(("SocketHandlerClass - sendto returned error code %d\n", LAST_ERROR)); Clear_Socket_Error(); } else { /* ** No more room for outgoing packets. */ DebugString(("SocketHandlerClass - 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 SocketHandlerClass::Add_CRC(unsigned long *crc, unsigned long val) { int hibit; if ((*crc) & 0x80000000) { hibit = 1; } else { hibit = 0; } (*crc) <<= 1; (*crc) += val; (*crc) += hibit; }