Descent3/netgames/dmfc/dmfcremote.cpp

711 lines
19 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* 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/>.
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/Main/Dmfc/dmfcremote.cpp $
* $Revision: 1.1.1.1 $
* $Date: 2003/08/26 03:57:21 $
* $Author: kevinb $
*
* Remote administration
*
* $Log: dmfcremote.cpp,v $
* Revision 1.1.1.1 2003/08/26 03:57:21 kevinb
* initial 1.5 import
*
*
* 6 7/14/99 11:48a Jeff
* localized text for patch fixes
*
* 5 7/09/99 6:18p Jeff
* added $remoteadminlogout and $wait commands
*
* 4 7/08/99 6:25p Jeff
* remote admin in and working
*
* 3 7/08/99 2:38a Jeff
* rough implementation of remote administration checked in. Still needs
* some polishing, but should work basically.
*
* 2 7/07/99 7:07p Jeff
* created dmfcremote.cpp
*
* $NoKeywords: $
*/
#include "gamedll_header.h"
#include "DMFC.h"
#include "dmfcinternal.h"
#include "encryption.h"
#include <stdlib.h>
#include <memory.h>
extern DMFCBase *basethis;
struct tRemotePlayerData {
bool authorized; // whether the player is authorized
uint8_t curr_key[8]; // current encryption key
};
static tRemotePlayerData Authorized_players[MAX_PLAYER_RECORDS];
static char Remote_Admin_pass[64];
bool Use_remote_admin = false;
static bool IAmAnAdmin = false;
static void Remote_encrypt(uint8_t key[8], int size_buffer, uint8_t *buffer, int *new_size, uint8_t **new_buffer);
static void Remote_decrypt(uint8_t key[8], int size_buffer, uint8_t *buffer, int *new_size, uint8_t **new_buffer);
// Sends a string to the server (function encrypts it before sending)
void Remote_SendStringToServer(const char *string);
// Recieves a string from the server (function decrypts it)
void Remote_RecieveStringFromServer(uint8_t *data, char *buffer, int size);
// Sends a string to a client (function encrypts it before sending)
void Remote_SendStringToClient(int client, const char *string);
// Recieves a string from a client (function decrypts it)
int Remote_RecieveStringFromClient(uint8_t *data, char *buffer, int size);
// Handles remote admin packets from a client
void Remote_HandleClientPacket(uint8_t *data);
// Handles remote admin packets from a client
void Remote_HandleServerPacket(uint8_t *data);
// Sends a message from the server to a client
void Remote_SendMessage(int client, const char *message);
// Receives a message from the server
void Remote_GetMessage(uint8_t *data);
// Logs a player out from being an administrator
void Remote_Logout(int precnum);
// filters any remote commands not allowed to be executed remotely
// -1 : not found
// 0 : can't call
// 1 : can call
int FilterNonRemoteCommands(char *command);
// Initializes the remote administration system
void Remote_Initialize(void) {
// initialize encryption keys
srand((uint32_t)time(0));
int i, j;
for (i = 0; i < MAX_PLAYER_RECORDS; i++) {
Authorized_players[i].authorized = false;
for (j = 0; j < 8; j++) {
Authorized_players[i].curr_key[j] = rand() % 255;
}
}
memset(Remote_Admin_pass, 0, 64);
// setup packet recievers
#ifdef WIN32
basethis->RegisterPacketReceiver(SPID_REMOTETOSERVER, Remote_HandleClientPacket);
basethis->RegisterPacketReceiver(SPID_REMOTETOCLIENT, Remote_HandleServerPacket);
basethis->RegisterPacketReceiver(SPID_SERVERREMOTEMSG, Remote_GetMessage);
#else
basethis->RegisterPacketReceiver(SPID_REMOTETOSERVER, &Remote_HandleClientPacket);
basethis->RegisterPacketReceiver(SPID_REMOTETOCLIENT, &Remote_HandleServerPacket);
basethis->RegisterPacketReceiver(SPID_SERVERREMOTEMSG, &Remote_GetMessage);
#endif
if (basethis->GetLocalRole() == LR_SERVER) {
bool bTemp;
int pass_len = 64;
DatabaseRead1("DMFCRemoteAdminPass", Remote_Admin_pass, &pass_len);
if (DatabaseRead3("DMFCRemoteAdmin", &bTemp)) {
Remote_Enable(bTemp);
}
}
}
// Processes remote administration for the frame
void Remote_ProcessFrame(void) {
if (basethis->GetLocalRole() != LR_SERVER)
return;
// go through all the players, and update their state (whether
// they are in the game or not)
int p;
for (p = 0; p < MAX_PLAYER_RECORDS; p++) {
player_record *pr = PRec_GetPRecord(p);
if (Authorized_players[p].authorized) {
// this is an authorized player, update them
// make sure they are in the game
if (!pr || pr->state != STATE_INGAME) {
// the player is no longer in the game
mprintf(0, "REMOTE: Removing authorization for %s\n", (pr->callsign) ? (pr->callsign) : "<UNKNOWN>");
Remote_Logout(p);
// change the key for the slot
int j;
for (j = 0; j < 8; j++) {
Authorized_players[p].curr_key[j] = rand() % 255;
}
} else {
// the player is authorized and in the game
}
}
}
}
// Gets the key for the given player record
uint8_t *Remote_GetKey(int prec) {
ASSERT(prec >= 0 && prec < MAX_PLAYER_RECORDS);
return Authorized_players[prec].curr_key;
}
// Enables/Disables remote administration
void Remote_Enable(bool enable) {
if (basethis->GetLocalRole() != LR_SERVER)
return;
Use_remote_admin = enable;
if (*Remote_Admin_pass == '\0' && enable) {
Use_remote_admin = false;
}
mprintf(0, "Remote Administration: %s\n", (Use_remote_admin) ? "On" : "Off");
if (!Use_remote_admin) {
// log out all the logged in players
int p;
for (p = 0; p < MAX_PLAYER_RECORDS; p++) {
if (Authorized_players[p].authorized) {
// log the player out
Remote_Logout(p);
}
}
}
DatabaseWrite2("DMFCRemoteAdmin", Use_remote_admin);
}
// sets the administration password for the server
// pass in an unencrypted password
void Remote_SetPassword(char *password) {
if (basethis->GetLocalRole() != LR_SERVER)
return;
strncpy(Remote_Admin_pass, password, 63);
Remote_Admin_pass[63] = '\0';
DatabaseWrite1("DMFCRemoteAdminPass", Remote_Admin_pass, strlen(Remote_Admin_pass));
}
// Logs a player record in as an administrator
bool Remote_Login(int precnum, char *pass) {
if (precnum < 0 || precnum >= MAX_PLAYER_RECORDS)
return false;
player_record *pr = PRec_GetPRecord(precnum);
if (!pr || pr->state != STATE_INGAME) {
return false;
}
if (Authorized_players[precnum].authorized)
return true;
if (strcmp(pass, Remote_Admin_pass) == 0) {
Authorized_players[precnum].authorized = true;
}
return Authorized_players[precnum].authorized;
}
// Logs a player out from being an administrator
void Remote_Logout(int precnum) {
if (precnum < 0 || precnum >= MAX_PLAYER_RECORDS)
return;
if (!Authorized_players[precnum].authorized)
return;
player_record *pr = PRec_GetPRecord(precnum);
if (!pr)
return;
if (pr->state == STATE_INGAME) {
Remote_SendStringToClient(pr->pnum, "LOGOUTSUCCESS");
Remote_SendMessage(pr->pnum, DTXT_REMOTELOGGEDOUT);
}
Authorized_players[precnum].authorized = false;
DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), DTXT_HUDMSGLOGGEDOUT, pr->callsign);
}
// Lists the players logged in
void Remote_ListLogins(void) {
int prec;
player_record *pr;
char buffer[512];
char temp[64];
int len, curlen;
curlen = 0;
*buffer = '\0';
for (prec = 0; prec < MAX_PLAYER_RECORDS; prec++) {
if (Authorized_players[prec].authorized) {
pr = PRec_GetPRecord(prec);
if (pr && pr->state != STATE_EMPTY) {
len = strlen(pr->callsign);
if (curlen + len > 500) {
// output the hudmessage
DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), buffer);
*buffer = '\0';
curlen = 0;
}
if (*buffer != '\0') {
strncat(buffer, ":", sizeof(buffer) - strlen(buffer) - 1);
}
snprintf(temp, sizeof(temp), "%s[%d]", pr->callsign, prec);
strncat(buffer, temp, sizeof(buffer) - strlen(buffer) - 1);
curlen = strlen(buffer);
}
}
}
if (*buffer != '\0') {
DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), buffer);
}
}
extern player_record Player_records[MAX_PLAYER_RECORDS];
int translate_precptr_to_index(player_record *pr) {
int index = (pr - Player_records);
if (index < 0 || index >= MAX_PLAYER_RECORDS)
index = -1;
return index;
}
// Sets a clients key
void Remote_SetMyKey(uint8_t key[8]) {
player_record *pr;
pr = PRec_GetPRecordByPnum(basethis->GetPlayerNum());
int prec = translate_precptr_to_index(pr);
if (prec == -1) {
mprintf(0, "INVALID PREC!\n");
Int3();
return;
}
memcpy(Authorized_players[prec].curr_key, key, 8);
}
// handles a remote command (client side)
void Remote_ClientProcess(const char *command) {
if (basethis->GetLocalRole() == LR_SERVER)
return;
if (!IAmAnAdmin) {
// only let "login" go through
if (!strnicmp(command, "login", strlen("login"))) {
mprintf(0, "Sending login attempt for remote administration\n");
} else {
return;
}
}
const char *ptr = command;
while (*ptr == ' ')
ptr++;
if (*ptr) {
Remote_SendStringToServer(ptr);
}
}
// handles a remote command (server side)
void Remote_ServerProcess(int precnum, char *command) {
if (basethis->GetLocalRole() != LR_SERVER)
return;
if (precnum < 0 || precnum >= MAX_PLAYER_RECORDS)
return;
if (!Use_remote_admin)
return;
if (!Authorized_players[precnum].authorized) {
// check to see if the player is trying to login
int loginlen = strlen("login ");
if (!strnicmp(command, "login ", loginlen)) {
char *ptr = &command[loginlen - 1];
while (*ptr == ' ')
ptr++;
if (Remote_Login(precnum, ptr)) {
// successful login
player_record *pr = PRec_GetPRecord(precnum);
DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), DTXT_HUDMSGLOGGEDIN, pr->callsign);
Remote_SendStringToClient(pr->pnum, "LOGINSUCCESS");
Remote_SendMessage(pr->pnum, DTXT_REMOTELOGGEDIN);
}
return;
}
}
player_record *pr = PRec_GetPRecord(precnum);
// check to see if they are logging out
if (!stricmp(command, "logout")) {
// log the player out
Remote_Logout(precnum);
return;
}
// process the command
if (command[0] == '$') {
switch (FilterNonRemoteCommands(command)) {
case 1: {
DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), DTXT_REMOTEEXECUTE, pr->callsign, command);
if (basethis->InputCommandHandle(command)) {
// command handled
Remote_SendMessage(pr->pnum, DTXT_REMOTESUCCESS);
} else {
// command not handled
Remote_SendMessage(pr->pnum, DTXT_REMOTENOTSUCCESS);
}
} break;
case 0: {
// command not handled
Remote_SendMessage(pr->pnum, DTXT_REMOTENOTALLOWED);
} break;
default: {
// command not handled
Remote_SendMessage(pr->pnum, DTXT_REMOTENOTFOUND);
} break;
}
} else {
// command not handled
Remote_SendMessage(pr->pnum, DTXT_REMOTENOTFOUND);
}
}
// filters any remote commands not allowed to be executed remotely
int FilterNonRemoteCommands(char *command) {
// cut off whats after the first word (the command)
char cmd[64];
strncpy(cmd, command, 63);
cmd[63] = '\0';
char *ptr = cmd;
while (*ptr != ' ' && *ptr != '\0')
ptr++;
*ptr = '\0';
return basethis->CanInputCommandBeUsedRemotely((*cmd == '$') ? cmd + 1 : cmd);
}
// Sends a message from the server to a client
void Remote_SendMessage(int client, const char *message) {
if (basethis->GetLocalRole() != LR_SERVER)
return;
char *allocated = NULL;
int len = strlen(message);
const int maxlen = ((MAX_GAME_DATA_SIZE - 5) < 511) ? (MAX_GAME_DATA_SIZE - 5) : 511;
if (len > maxlen) {
allocated = new char[maxlen + 1];
memcpy(allocated, message, maxlen);
allocated[maxlen] = '\0';
message = allocated;
len = maxlen;
}
int count = 0;
uint8_t data[MAX_GAME_DATA_SIZE];
basethis->StartPacket(data, SPID_SERVERREMOTEMSG, &count);
MultiAddString(message, data, &count);
basethis->SendPacket(data, count, client);
delete[] allocated;
}
// Receives a message from the server
void Remote_GetMessage(uint8_t *data) {
int count = 0;
char buffer[512];
MultiGetString(buffer, data, &count);
DLLAddColoredHUDMessage(GR_RGB(200, 200, 40), "==%s==", buffer);
}
// Sends a string to the server (function encrypts it before sending)
void Remote_SendStringToServer(const char *string) {
if (basethis->GetLocalRole() == LR_SERVER) {
Int3();
return;
}
int slen;
int new_strlen;
uint8_t *packet_data;
slen = strlen(string);
player_record *pr;
pr = PRec_GetPRecordByPnum(basethis->GetPlayerNum());
int prec = translate_precptr_to_index(pr);
if (prec == -1) {
mprintf(0, "INVALID PREC!\n");
Int3();
return;
}
Remote_encrypt(Authorized_players[prec].curr_key, slen, (uint8_t *)string, &new_strlen, &packet_data);
if (new_strlen == 0) {
mprintf(0, "COULDN'T ENCRYPT\n");
Int3();
return;
}
// now we can send off the packet
int count = 0;
uint8_t data[MAX_GAME_DATA_SIZE];
basethis->StartPacket(data, SPID_REMOTETOSERVER, &count);
MultiAddByte(prec, data, &count);
MultiAddInt(new_strlen, data, &count);
memcpy(&data[count], packet_data, new_strlen);
count += new_strlen;
basethis->SendPacket(data, count, SP_SERVER);
free(packet_data);
}
// Handles remote admin packets from a client
void Remote_HandleServerPacket(uint8_t *data) {
char buffer[512];
Remote_RecieveStringFromServer(data, buffer, 512);
if (!strcmp(buffer, "LOGOUTSUCCESS")) {
// we logged out
IAmAnAdmin = false;
} else if (!strcmp(buffer, "LOGINSUCCESS")) {
// we logged in
IAmAnAdmin = true;
}
}
// Recieves a string from the server (function decrypts it)
void Remote_RecieveStringFromServer(uint8_t *data, char *buffer, int size) {
int count = 0;
int len = MultiGetInt(data, &count);
player_record *pr;
pr = PRec_GetPRecordByPnum(basethis->GetPlayerNum());
int prec = translate_precptr_to_index(pr);
if (prec == -1) {
mprintf(0, "INVALID PREC!\n");
Int3();
return;
}
ASSERT(len > 0);
if (len <= 0)
return;
uint8_t *packet;
packet = (uint8_t *)malloc(len);
memcpy(packet, &data[count], len);
count += len;
int new_size;
uint8_t *new_buffer;
Remote_decrypt(Authorized_players[prec].curr_key, len, packet, &new_size, &new_buffer);
if (new_buffer) {
strncpy(buffer, (char *)new_buffer, size - 1);
buffer[size - 1] = '\0';
free(new_buffer);
} else {
*buffer = '\0';
}
free(packet);
}
// Sends a string to a client (function encrypts it before sending)
void Remote_SendStringToClient(int client, const char *string) {
if (basethis->GetLocalRole() != LR_SERVER) {
Int3();
return;
}
int slen;
int new_strlen;
uint8_t *packet_data;
slen = strlen(string);
player_record *pr;
pr = PRec_GetPRecordByPnum(client);
int prec = translate_precptr_to_index(pr);
if (prec == -1) {
mprintf(0, "INVALID PREC!\n");
Int3();
return;
}
// check to make sure the player is authorized before sending
if (!Authorized_players[prec].authorized) {
Int3();
return;
}
Remote_encrypt(Authorized_players[prec].curr_key, slen, (uint8_t *)string, &new_strlen, &packet_data);
if (new_strlen == 0) {
mprintf(0, "COULDN'T ENCRYPT\n");
Int3();
return;
}
// now we can send off the packet
int count = 0;
uint8_t data[MAX_GAME_DATA_SIZE];
basethis->StartPacket(data, SPID_REMOTETOCLIENT, &count);
MultiAddInt(new_strlen, data, &count);
memcpy(&data[count], packet_data, new_strlen);
count += new_strlen;
basethis->SendPacket(data, count, client);
free(packet_data);
}
// Handles remote admin packets from a client
void Remote_HandleClientPacket(uint8_t *data) {
char buffer[512];
int prec = Remote_RecieveStringFromClient(data, buffer, 512);
if (prec == -1)
return;
Remote_ServerProcess(prec, buffer);
}
// Recieves a string from a client (function decrypts it)
int Remote_RecieveStringFromClient(uint8_t *data, char *buffer, int size) {
int count = 0;
int prec = MultiGetByte(data, &count);
int len = MultiGetInt(data, &count);
ASSERT(len > 0);
ASSERT(prec >= 0 && prec < MAX_PLAYER_RECORDS);
if (prec < 0 || prec >= MAX_PLAYER_RECORDS)
return -1;
if (len <= 0)
return -1;
uint8_t *packet;
packet = (uint8_t *)malloc(len);
memcpy(packet, &data[count], len);
count += len;
int new_size;
uint8_t *new_buffer;
Remote_decrypt(Authorized_players[prec].curr_key, len, packet, &new_size, &new_buffer);
if (new_buffer) {
size = (len < size) ? len + 1 : size;
strncpy(buffer, (char *)new_buffer, size - 1);
buffer[size - 1] = '\0';
free(new_buffer);
} else {
*buffer = '\0';
}
free(packet);
return prec;
}
void Remote_pad_buffer(int orig_size, const uint8_t *orig_buffer, int *new_size, uint8_t **new_buffer) {
// bump buffer up to size of mod 8
*new_size = orig_size;
if (orig_size < 8 || (orig_size % 8) != 0) {
// we need to pad up
int chunks = orig_size / 8;
*new_size = (chunks + 1) * 8;
}
*new_buffer = (uint8_t *)malloc(*new_size);
if (!(*new_buffer)) {
*new_size = 0;
return;
}
memset(*new_buffer, 0, *new_size);
memcpy(*new_buffer, orig_buffer, orig_size);
}
static void Remote_encrypt(uint8_t key[8], int size_buffer, uint8_t *buffer, int *new_size, uint8_t **new_buffer) {
*new_buffer = NULL;
uint8_t *buff;
Remote_pad_buffer(size_buffer, buffer, new_size, &buff);
if (*new_size == 0)
return;
IceKey ice(0);
ice.set((uint8_t *)key);
int i, num_chunks = 0;
int chunk_offset = 0;
uint8_t *cipher;
cipher = (uint8_t *)malloc(*new_size);
if (!cipher)
return;
num_chunks = (*new_size) / 8;
for (i = 0; i < num_chunks; i++) {
ice.encrypt(&buff[chunk_offset], &cipher[chunk_offset]);
chunk_offset += 8;
}
free(buff);
*new_buffer = cipher;
}
static void Remote_decrypt(uint8_t key[8], int size_buffer, uint8_t *buffer, int *new_size, uint8_t **new_buffer) {
*new_buffer = NULL;
uint8_t *buff;
Remote_pad_buffer(size_buffer, buffer, new_size, &buff);
if (*new_size == 0)
return;
IceKey ice(0);
ice.set((uint8_t *)key);
int i, num_chunks = 0;
int chunk_offset = 0;
uint8_t *cipher;
cipher = (uint8_t *)malloc(*new_size);
if (!cipher)
return;
num_chunks = (*new_size) / 8;
for (i = 0; i < num_chunks; i++) {
ice.decrypt(&buff[chunk_offset], &cipher[chunk_offset]);
chunk_offset += 8;
}
free(buff);
*new_buffer = cipher;
}