/* ** 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/servercontrol.cpp $* * * * $Author:: Bhayes $* * * * $Modtime:: 3/18/02 6:04p $* * * * $Revision:: 7 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * * * * * * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include #include "servercontrol.h" #include "systimer.h" /* ** Single instance of server control. */ ServerControlClass ServerControl; /*********************************************************************************************** * ServerControlClass::ServerControlClass -- Class constructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:00PM ST : Created * *=============================================================================================*/ ServerControlClass::ServerControlClass(void) { AppRequestCallback = NULL; AppResponseCallback = NULL; LocalPort = 63999; strcpy(Password, ""); Listening = false; WelcomeMessage[0] = 0; RemoteAdminAllowed = false; } /*********************************************************************************************** * ServerControlClass::ServerControlClass -- Class destructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:01PM ST : Created * *=============================================================================================*/ ServerControlClass::~ServerControlClass(void) { Stop_Listening(); } /*********************************************************************************************** * ServerControlClass::Start_Listening -- Start listening for server control messages * * * * * * * * INPUT: Port to listen on * * Password to authenticate controllers * * App request callback * * App response callback * * Loopback - if set then only bind to the loopback address * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:01PM ST : Created * *=============================================================================================*/ bool ServerControlClass::Start_Listening(unsigned short port, char *password, const char*(*app_request_callback)(char*), void(*app_response_callback)(char*), bool loopback, unsigned long ip) { LocalPort = port; if (LocalPort != 0) { strcpy(Password, password); if (Comms.Open(LocalPort, loopback, ip)) { Listening = true; AppRequestCallback = app_request_callback; AppResponseCallback = app_response_callback; Comms.Set_Encryption_Key(password); return(true); } } return(false); } /*********************************************************************************************** * ServerControlClass::Stop_Listening -- Stop listening to control messages * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:02PM ST : Created * *=============================================================================================*/ void ServerControlClass::Stop_Listening(void) { if (Listening) { /* ** Throw off any connected controllers. */ RemoteControlStruct *control; while (RemoteControllers.Count()) { control = RemoteControllers[0]; if (control && control->Secure) { Respond("** Server exiting - Connection closed! **\n", control->IP, control->Port); } Remove_Remote_Control(control->IP, control->Port); } Comms.Close(); Listening = false; } while (RemoteControllers.Count()) { delete RemoteControllers[0]; RemoteControllers.Delete(0); } } /*********************************************************************************************** * ServerControlClass::Service -- Service control messages * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:03PM ST : Created * *=============================================================================================*/ void ServerControlClass::Service(void) { if (Listening) { char buffer[1024]; int buffer_len = sizeof(buffer); unsigned long address; unsigned short port; Comms.Service(); int bytes = Comms.Read(buffer, buffer_len, &address, &port); if (bytes > 0 && bytes <= sizeof(ControlMessageStruct)) { if (Is_Authenticated(address, port)) { Reset_Timeout(address, port); } Parse_Message(buffer, bytes, address, port); } /* ** See if anyone timed out. */ RemoteControlStruct *control; for (int i=0 ; iSecure) { if (TIMEGETTIME() - control->Time > CONTROL_TIMEOUT) { Respond("** Connection timed out - Bye! **\n", control->IP, control->Port); Remove_Remote_Control(control->IP, control->Port); break; } } } } } /*********************************************************************************************** * ServerControlClass::Parse_Message -- Parse server control message * * * * * * * * INPUT: Buffer containing message struct * * Length of message * * Address message came from * * Port message came from * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:03PM ST : Created * *=============================================================================================*/ void ServerControlClass::Parse_Message(void *buffer, int len, unsigned long address, unsigned short port) { ControlMessageStruct *message = (ControlMessageStruct*) buffer; assert(len <= sizeof(ControlMessageStruct)); assert(message != NULL); /* ** This line prevents external control by only accepting messages from the loopback address. */ if (ntohl(address) == INADDR_LOOPBACK || RemoteAdminAllowed) { /* ** Convert to upper case for parsing. */ char text[MAX_SERVER_CONTROL_MESSAGE_SIZE + 1]; memcpy(text, message->Message, MAX_SERVER_CONTROL_MESSAGE_SIZE); text[MAX_SERVER_CONTROL_MESSAGE_SIZE] = 0; strupr(text); switch (message->Type) { /* ** This is a request from a remote controller. */ case CONTROL_REQUEST: if (strstr(text, "CONNECT")) { /* ** Someone is requesting to talk to us. */ if (strlen(Password)) { Respond("Password required:", address, port); } else { Add_Remote_Control(address, port); } break; } if (strstr(text, "BYE")) { /* ** Remote controller is going away. */ if (Is_Authenticated(address, port)) { Respond("Goodbye!\n", address, port); Remove_Remote_Control(address, port); } break; } /* ** Check for password authentication. */ if (strlen(Password) && strstr(message->Message, Password)) { char buildbuf[256]; strcpy(buildbuf, "Password accepted.\n"); strcat(buildbuf, WelcomeMessage); strcat(buildbuf, "\n"); Respond(buildbuf, address, port); Add_Remote_Control(address, port); break; } /* ** If we know about this controller and we have got a valid password from him then pass his message to the game. */ if (AppRequestCallback && Is_Authenticated(address, port)) { const char * response = AppRequestCallback(message->Message); if (response && strlen(response)) { Respond(response, address, port); } } break; /* ** This is a response to a control message we sent out. */ case CONTROL_RESPONSE: if (AppResponseCallback) { AppResponseCallback(message->Message); } break; } } } /*********************************************************************************************** * ServerControlClass::Add_Remote_Control -- Add a new remote controller * * * * * * * * INPUT: IP of remote controller * * Port of remote controller * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:06PM ST : Created * *=============================================================================================*/ void ServerControlClass::Add_Remote_Control(unsigned long ip, unsigned short port) { RemoteControlStruct *control = Get_Controller(ip, port); if (control == NULL) { control = new RemoteControlStruct; RemoteControllers.Add(control); } control->Time = TIMEGETTIME(); control->Secure = true; control->IP = ip; control->Port = port; } /*********************************************************************************************** * ServerControlClass::Remove_Remote_Control -- Remove a remote controller from our list * * * * * * * * INPUT: IP of remote controller * * Port of remote controller * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:07PM ST : Created * *=============================================================================================*/ void ServerControlClass::Remove_Remote_Control(unsigned long ip, unsigned short port) { RemoteControlStruct *control; for (int i=0 ; iIP == ip && control->Port == port) { delete control; RemoteControllers.Delete(i); break; } } } /*********************************************************************************************** * ServerControlClass::Is_Authenticated -- Has this controller presented a valid password? * * * * * * * * INPUT: IP, port * * * * OUTPUT: True if controller knows password * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:07PM ST : Created * *=============================================================================================*/ bool ServerControlClass::Is_Authenticated(unsigned long ip, unsigned short port) { RemoteControlStruct *control = Get_Controller(ip, port); if (control && control->Secure && TIMEGETTIME() - control->Time < CONTROL_TIMEOUT) { return(true); } return(false); } /*********************************************************************************************** * ServerControlClass::Get_Controller -- Find remote controller info based on ip and port * * * * * * * * INPUT: Ip, port * * * * OUTPUT: Ptr to remote control struct. Null if not found * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:08PM ST : Created * *=============================================================================================*/ ServerControlClass::RemoteControlStruct *ServerControlClass::Get_Controller(unsigned long ip, unsigned short port) { RemoteControlStruct *control; for (int i=0 ; iIP == ip && control->Port == port) { return(control); } } return(NULL); } /*********************************************************************************************** * ServerControlClass::Reset_Timeout -- Reset idle timeout for remote controller * * * * * * * * INPUT: Ip, port * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:09PM ST : Created * *=============================================================================================*/ void ServerControlClass::Reset_Timeout(unsigned long ip, unsigned short port) { RemoteControlStruct *control = Get_Controller(ip, port); if (control && control->Secure && TIMEGETTIME() - control->Time < CONTROL_TIMEOUT) { control->Time = TIMEGETTIME(); } } /*********************************************************************************************** * ServerControlClass::Send_Message -- Send control message to remote server * * * * * * * * INPUT: Message text * * IP, port to send to * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:09PM ST : Created * *=============================================================================================*/ void ServerControlClass::Send_Message(char *text, unsigned long ip, unsigned short port) { ControlMessageStruct message; message.Type = CONTROL_REQUEST; strcpy(message.Message, text); Comms.Write(&message, sizeof(message.Type) + strlen(text) + 1, &ip, port); Comms.Service(); } /*********************************************************************************************** * ServerControlClass::Respond -- Send a control request response to a remote controller * * * * * * * * INPUT: Text of response * * IP to send to * * port to send to * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:10PM ST : Created * *=============================================================================================*/ void ServerControlClass::Respond(const char *text, unsigned long ip, unsigned short port) { ControlMessageStruct message; const char *outmsg = text; while (strlen(outmsg)) { message.Type = CONTROL_RESPONSE; strncpy(message.Message, outmsg, sizeof(message.Message)-1); message.Message[sizeof(message.Message)-1] = 0; int outlen = 0; if (strlen(outmsg) > sizeof(message.Message)-1) { outlen = sizeof(message.Message)-1; outmsg += (sizeof(message.Message)-1); } else { outlen = strlen(outmsg); outmsg += outlen; } Comms.Write(&message, sizeof(message.Type) + outlen + 1, &ip, port); Comms.Service(); } } /*********************************************************************************************** * ServerControlClass::Set_Welcome_Message -- Set the new connection welcome message * * * * * * * * INPUT: Text of message * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/16/2001 4:11PM ST : Created * *=============================================================================================*/ void ServerControlClass::Set_Welcome_Message(char *message) { if (message) { memset(WelcomeMessage, 0, sizeof(WelcomeMessage)); strncpy(WelcomeMessage, message, sizeof(WelcomeMessage)-1); } }