This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/SControl/servercontrol.cpp

563 lines
No EOL
29 KiB
C++

/*
** 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 <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** 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 <stdlib.h>
#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 ; i<RemoteControllers.Count() ; i++) {
control = RemoteControllers[i];
if (control && control->Secure) {
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 ; i<RemoteControllers.Count() ; i++) {
control = RemoteControllers[i];
if (control->IP == 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 ; i<RemoteControllers.Count() ; i++) {
control = RemoteControllers[i];
if (control->IP == 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);
}
}