563 lines
No EOL
29 KiB
C++
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);
|
|
}
|
|
} |