openmohaa/code/gamespy/sv_gqueryreporting.c

538 lines
14 KiB
C
Raw Normal View History

2023-02-05 13:27:22 +01:00
/*
===========================================================================
Copyright (C) 2023 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code 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 2 of the License,
or (at your option) any later version.
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
2023-06-17 01:43:42 +02:00
#include "../qcommon/q_shared.h"
2023-02-05 01:40:14 +01:00
#include "sv_gqueryreporting.h"
#include "common/gsPlatformSocket.h"
#include "common/gsPlatformUtil.h"
#include "gutil.h"
2023-02-05 01:40:14 +01:00
2023-07-05 21:24:14 +02:00
static char *queries[] = {"basic", "info", "rules", "players", "status", "packets", "echo", "secure"};
static qr_implementation_t static_rec;
static qr_implementation_t *current_rec = &static_rec;
struct sockaddr_in hbaddr;
char qr_hostname[64];
void qr_check_queries(qr_t qrec);
void qr_check_send_heartbeat(qr_t qrec);
static void parse_query(qr_t qrec, char *query, struct sockaddr *sender);
2023-02-05 01:40:14 +01:00
static void send_heartbeat(qr_t qrec, int statechanged);
void qr_process_queries(qr_t qrec)
{
2023-07-05 21:24:14 +02:00
if (!qrec) {
qrec = current_rec;
}
2023-02-05 01:40:14 +01:00
2023-07-05 21:24:14 +02:00
qr_check_send_heartbeat(qrec);
qr_check_queries(qrec);
2023-02-05 01:40:14 +01:00
}
void qr_process_queries_no_heartbeat(qr_t qrec)
{
2023-07-05 21:24:14 +02:00
if (!qrec) {
qrec = current_rec;
}
2023-02-05 01:40:14 +01:00
2023-07-05 21:24:14 +02:00
qr_check_queries(qrec);
2023-02-05 01:40:14 +01:00
}
void qr_check_queries(qr_t qrec)
{
#define INBUF_LEN 256
2023-07-05 21:24:14 +02:00
static char indata[INBUF_LEN]; //256 byte input buffer
struct sockaddr_in saddr;
int error;
2023-02-05 01:40:14 +01:00
#if defined(_LINUX)
2023-07-05 21:24:14 +02:00
unsigned int saddrlen = sizeof(struct sockaddr_in);
2023-02-05 01:40:14 +01:00
#else
2023-07-05 21:24:14 +02:00
int saddrlen = sizeof(struct sockaddr_in);
2023-02-05 01:40:14 +01:00
#endif
2023-07-05 21:24:14 +02:00
while (CanReceiveOnSocket((SOCKET)qrec->hbsock)) {
//else we have data
error = (int)recvfrom((SOCKET)qrec->hbsock, indata, (INBUF_LEN - 1), 0, (struct sockaddr *)&saddr, &saddrlen);
if (gsiSocketIsNotError(error)) {
indata[error] = '\0';
parse_query(qrec, indata, (struct sockaddr *)&saddr);
}
}
2023-02-05 01:40:14 +01:00
}
void qr_check_send_heartbeat(qr_t qrec)
{
2023-07-05 21:24:14 +02:00
unsigned long tc;
tc = current_time();
if ((SOCKET)qrec->hbsock != INVALID_SOCKET) {
if (tc - qrec->lastheartbeat > MAX_HEARTBEAT_TIME || tc < qrec->lastheartbeat) {
2023-07-05 21:24:14 +02:00
send_heartbeat(qrec, 0);
2023-08-26 21:13:14 +02:00
} else if (qrec->no_query > 0 && tc - qrec->lastheartbeat > MIN_HEARTBEAT_TIME) {
send_heartbeat(qrec, 0);
qrec->no_query++;
if (qrec->no_query > 10) {
qrec->no_query = 0;
}
2023-07-05 21:24:14 +02:00
}
}
2023-02-05 01:40:14 +01:00
}
void qr_send_statechanged(qr_t qrec)
{
2023-07-05 21:24:14 +02:00
if (!qrec) {
qrec = current_rec;
}
send_heartbeat(qrec, 1);
2023-02-05 01:40:14 +01:00
}
void qr_shutdown(qr_t qrec)
{
2023-07-05 21:24:14 +02:00
if (qrec == NULL) {
qrec = current_rec;
}
if (INVALID_SOCKET != (SOCKET)qrec->hbsock && (SOCKET)qrec->querysock) //if we own the socket
{
closesocket((SOCKET)qrec->hbsock);
}
qrec->hbsock = (void *)INVALID_SOCKET;
qrec->lastheartbeat = 0;
if (qrec->querysock) //if we own the socket
{
SocketShutDown();
}
if (qrec != &static_rec) //need to free it, it was dynamically allocated
{
free(qrec);
}
2023-02-05 01:40:14 +01:00
}
/* Return a sockaddrin for the given host (numeric or DNS) and port)
Returns the hostent in savehent if it is not NULL */
2023-07-05 21:24:14 +02:00
static int get_sockaddrin(const char *host, int port, struct sockaddr_in *saddr, struct hostent **savehent)
2023-02-05 01:40:14 +01:00
{
2023-07-05 21:24:14 +02:00
struct hostent *hent = NULL;
saddr->sin_family = AF_INET;
saddr->sin_port = htons((unsigned short)port);
if (host == NULL) {
saddr->sin_addr.s_addr = INADDR_ANY;
} else {
saddr->sin_addr.s_addr = inet_addr(host);
}
if (saddr->sin_addr.s_addr == INADDR_NONE && strcmp(host, "255.255.255.255") != 0) {
hent = gethostbyname(host);
if (!hent) {
return 0;
}
saddr->sin_addr.s_addr = *(unsigned int *)hent->h_addr_list[0];
}
if (savehent != NULL) {
*savehent = hent;
}
return 1;
2023-02-05 01:40:14 +01:00
}
/* value_for_key: this returns a value for a certain key in s, where s is a string
containing key\value pairs. If the key does not exist, it returns NULL.
Note: the value is stored in a common buffer. If you want to keep it, make a copy! */
2023-07-05 21:24:14 +02:00
static char *value_for_key(const char *s, const char *key)
2023-02-05 01:40:14 +01:00
{
2023-07-05 21:24:14 +02:00
static int valueindex;
char *pos, *pos2;
char keyspec[256] = "\\";
static char value[2][256];
valueindex ^= 1;
strcat(keyspec, key);
strcat(keyspec, "\\");
pos = strstr(s, keyspec);
if (!pos) {
return NULL;
}
pos += strlen(keyspec);
pos2 = value[valueindex];
while (*pos && *pos != '\\') {
*pos2++ = *pos++;
}
*pos2 = '\0';
return value[valueindex];
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static void packet_send(qr_t qrec, struct sockaddr *addr, char *buffer)
2023-02-05 01:40:14 +01:00
{
2023-07-05 21:24:14 +02:00
char keyvalue[80];
2023-02-05 01:40:14 +01:00
2023-07-05 21:24:14 +02:00
if (!strlen(buffer)) {
return;
}
2023-02-05 01:40:14 +01:00
qrec->packetnumber += 1;
2023-08-26 21:13:14 +02:00
Com_sprintf(keyvalue, sizeof(keyvalue), "\\queryid\\%d.%d", qrec->queryid, qrec->packetnumber);
2023-07-05 21:24:14 +02:00
strcat(buffer, keyvalue);
2023-02-05 01:40:14 +01:00
sendto((SOCKET)qrec->querysock, buffer, (int)strlen(buffer), 0, addr, sizeof(*addr));
2023-07-05 21:24:14 +02:00
*buffer = 0;
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static void buffer_send(qr_t qrec, struct sockaddr *sender, char *buffer, char *newdata)
2023-02-05 01:40:14 +01:00
{
2023-07-05 21:24:14 +02:00
int bcount;
int i;
char *lastkey;
char *pos;
bcount = 0;
if (strlen(newdata) + strlen(buffer) < MAX_INFO_STRING) {
2023-07-05 21:24:14 +02:00
strcat(buffer, newdata);
return;
}
pos = newdata;
while (strlen(pos) > MAX_INFO_STRING) {
2023-07-05 21:24:14 +02:00
lastkey = pos;
for (i = 0; i < MAX_INFO_STRING; ++i) {
2023-07-05 21:24:14 +02:00
if (pos[i] == '\\') {
if (!(bcount % 2)) {
lastkey = pos + i;
}
++bcount;
}
}
if (lastkey == pos) {
return;
}
*lastkey = 0;
buffer_send(qrec, sender, buffer, pos);
*lastkey = '\\';
pos = lastkey;
bcount = 0;
if (strlen(buffer) + strlen(lastkey) < MAX_INFO_STRING) {
2023-07-05 21:24:14 +02:00
strcat(buffer, pos);
return;
}
}
packet_send(qrec, sender, buffer);
strcpy(buffer, pos);
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static void send_basic(qr_t qrec, struct sockaddr *sender, char *outbuf)
2023-02-05 01:40:14 +01:00
{
2023-08-26 21:13:14 +02:00
char keyvalue[MAX_KEYVALUES_LENGTH] = {0};
2023-02-05 01:40:14 +01:00
2023-08-26 21:13:14 +02:00
qrec->qr_basic_callback(keyvalue, MAX_KEYVALUES_LENGTH, qrec->udata);
2023-07-05 21:24:14 +02:00
buffer_send(qrec, sender, outbuf, keyvalue);
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static void send_info(qr_t qrec, struct sockaddr *sender, char *outbuf)
2023-02-05 01:40:14 +01:00
{
2023-08-26 21:13:14 +02:00
char keyvalue[MAX_KEYVALUES_LENGTH] = {0};
2023-02-05 01:40:14 +01:00
2023-08-26 21:13:14 +02:00
qrec->qr_info_callback(keyvalue, MAX_KEYVALUES_LENGTH, qrec->udata);
2023-07-05 21:24:14 +02:00
buffer_send(qrec, sender, outbuf, keyvalue);
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static void send_rules(qr_t qrec, struct sockaddr *sender, char *outbuf)
2023-02-05 01:40:14 +01:00
{
2023-08-26 21:13:14 +02:00
char keyvalue[MAX_KEYVALUES_LENGTH] = {0};
2023-02-05 01:40:14 +01:00
2023-08-26 21:13:14 +02:00
qrec->qr_rules_callback(keyvalue, MAX_KEYVALUES_LENGTH, qrec->udata);
2023-07-05 21:24:14 +02:00
buffer_send(qrec, sender, outbuf, keyvalue);
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static void send_players(qr_t qrec, struct sockaddr *sender, char *outbuf)
2023-02-05 01:40:14 +01:00
{
2023-08-26 21:13:14 +02:00
char keyvalue[MAX_KEYVALUES_LENGTH] = {0};
2023-02-05 01:40:14 +01:00
2023-08-26 21:13:14 +02:00
qrec->qr_players_callback(keyvalue, MAX_KEYVALUES_LENGTH, qrec->udata);
2023-07-05 21:24:14 +02:00
buffer_send(qrec, sender, outbuf, keyvalue);
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static void send_echo(qr_t qrec, struct sockaddr *sender, char *outbuf, const char *echostr)
2023-02-05 01:40:14 +01:00
{
2023-08-26 21:13:14 +02:00
char keyvalue[MAX_KEYVALUES_LENGTH] = {0};
2023-02-05 01:40:14 +01:00
2023-08-26 21:13:14 +02:00
if (strlen(echostr) > 50) {
return;
2023-07-05 21:24:14 +02:00
}
2023-08-26 21:13:14 +02:00
Com_sprintf(keyvalue, sizeof(keyvalue), "\\echoresponse\\%s", echostr);
buffer_send(qrec, sender, outbuf, keyvalue);
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static void send_final(qr_t qrec, struct sockaddr *sender, char *outbuf, char *validation)
2023-02-05 01:40:14 +01:00
{
2023-07-05 21:24:14 +02:00
char keyvalue[256];
2024-06-07 20:34:13 +02:00
uchar encrypted_val[128];
uchar encoded_val[200];
2023-07-05 21:24:14 +02:00
int keylen;
2023-02-05 01:40:14 +01:00
2023-07-05 21:24:14 +02:00
if (*validation) {
keylen = (int)strlen(validation);
2023-02-05 01:40:14 +01:00
2023-08-26 21:13:14 +02:00
if (keylen >= ARRAY_LEN(encrypted_val)) {
2023-07-05 21:24:14 +02:00
return;
}
2023-02-05 01:40:14 +01:00
2024-06-11 21:24:01 +02:00
strcpy((char*)encrypted_val, validation);
2023-02-05 01:40:14 +01:00
2023-07-05 21:24:14 +02:00
gs_encrypt((uchar *)qrec->secret_key, (int)strlen(qrec->secret_key), encrypted_val, keylen);
gs_encode(encrypted_val, keylen, encoded_val);
2023-02-05 01:40:14 +01:00
2023-08-26 21:13:14 +02:00
Com_sprintf(keyvalue, sizeof(keyvalue), "\\validate\\%s", encoded_val);
2023-07-05 21:24:14 +02:00
buffer_send(qrec, sender, outbuf, keyvalue);
}
2023-02-05 01:40:14 +01:00
2023-08-26 21:13:14 +02:00
Com_sprintf(keyvalue, sizeof(keyvalue), "\\final\\");
2023-07-05 21:24:14 +02:00
buffer_send(qrec, sender, outbuf, keyvalue);
packet_send(qrec, sender, outbuf);
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static void parse_query(qr_t qrec, char *query, struct sockaddr *sender)
2023-02-05 01:40:14 +01:00
{
2023-07-05 21:24:14 +02:00
query_t querytype = qtunknown;
2023-08-26 21:13:14 +02:00
char buffer[MAX_KEYVALUES_LENGTH] = {0};
2023-07-05 21:24:14 +02:00
const char *value;
char validation[256] = {0};
if (!qrec) {
qrec = current_rec;
}
if (*query == ';') {
// custom handler
2023-08-26 21:13:14 +02:00
if (qrec->qr_custom_handler) {
qrec->qr_custom_handler(query, sender);
}
2023-07-05 21:24:14 +02:00
return;
}
qrec->packetnumber = 0;
qrec->queryid++;
if (qrec->no_query > 0) {
qrec->no_query = 0;
}
for (querytype = qtbasic; querytype <= qtsecure; ++querytype) {
value = value_for_key(query, queries[querytype - 1]);
2023-07-05 21:24:14 +02:00
if (value) {
switch (querytype) {
case qtbasic:
send_basic(qrec, sender, buffer);
break;
case qtinfo:
send_info(qrec, sender, buffer);
break;
case qtrules:
send_rules(qrec, sender, buffer);
break;
case qtplayers:
send_players(qrec, sender, buffer);
break;
case qtstatus:
send_basic(qrec, sender, buffer);
send_info(qrec, sender, buffer);
send_rules(qrec, sender, buffer);
send_players(qrec, sender, buffer);
break;
case qtpackets:
send_basic(qrec, sender, buffer);
packet_send(qrec, sender, buffer);
send_info(qrec, sender, buffer);
packet_send(qrec, sender, buffer);
send_rules(qrec, sender, buffer);
packet_send(qrec, sender, buffer);
send_players(qrec, sender, buffer);
break;
case qtecho:
send_echo(qrec, sender, buffer, value);
break;
case qtsecure:
strcpy(validation, value);
break;
default:
continue;
}
}
}
send_final(qrec, sender, buffer, validation);
2023-02-05 01:40:14 +01:00
}
static void send_heartbeat(qr_t qrec, int statechanged)
{
2023-07-05 21:24:14 +02:00
char buf[256];
2023-02-05 01:40:14 +01:00
2023-07-05 21:24:14 +02:00
sprintf(buf, "\\heartbeat\\%d\\gamename\\%s", qrec->qport, qrec->gamename);
2023-02-05 01:40:14 +01:00
2023-07-05 21:24:14 +02:00
if (statechanged) {
sprintf(buf + strlen(buf), "\\statechanged\\%d", statechanged);
}
2023-02-05 01:40:14 +01:00
2023-08-25 22:54:57 +02:00
sendto((SOCKET)qrec->hbsock, buf, (int)strlen(buf), 0, (const struct sockaddr *)&qrec->master_saddr, sizeof(qrec->master_saddr));
2023-07-05 21:24:14 +02:00
qrec->lastheartbeat = current_time();
2023-02-05 01:40:14 +01:00
}
2023-07-05 21:24:14 +02:00
static int do_connect(void *sock, char *addr, int port, struct sockaddr_in *master_saddr)
2023-02-05 01:40:14 +01:00
{
2023-07-05 21:24:14 +02:00
get_sockaddrin(addr, port, master_saddr, NULL);
return 0;
2023-02-05 01:40:14 +01:00
}
void init_qrec(
2023-07-05 21:24:14 +02:00
qr_t *qrec,
int baseport,
SOCKET hbsock,
SOCKET querysock,
2023-07-28 21:44:43 +02:00
const char *gamename,
const char *secret_key,
2023-07-05 21:24:14 +02:00
qr_querycallback_t qr_basic_callback,
qr_querycallback_t qr_info_callback,
qr_querycallback_t qr_rules_callback,
qr_querycallback_t qr_players_callback,
void *userdata
2023-02-05 01:40:14 +01:00
)
{
2023-07-05 21:24:14 +02:00
qr_t qr;
if (qrec) {
qr = (qr_t)malloc(sizeof(qr_implementation_t));
*qrec = qr;
} else {
qr = &static_rec;
}
strcpy(qr->gamename, gamename);
strcpy(qr->secret_key, secret_key);
qr->qport = baseport;
qr->hbsock = (void *)hbsock;
qr->querysock = (void *)querysock;
qr->queryid = 1;
qr->no_query = 1;
qr->udata = userdata;
qr->qr_basic_callback = qr_basic_callback;
qr->qr_info_callback = qr_info_callback;
qr->lastheartbeat = 0;
qr->packetnumber = 0;
qr->qr_players_callback = qr_players_callback;
qr->qr_rules_callback = qr_rules_callback;
qr->qr_custom_handler = NULL;
2023-02-05 01:40:14 +01:00
}
#define NUM_PORTS_TO_TRY 100
int qr_init(
2023-07-05 21:24:14 +02:00
qr_t *qrec,
2023-07-28 21:44:43 +02:00
const char *ip,
2023-07-05 21:24:14 +02:00
int baseport,
2023-07-28 21:44:43 +02:00
const char *gamename,
const char *secret_key,
2023-07-05 21:24:14 +02:00
qr_querycallback_t qr_basic_callback,
qr_querycallback_t qr_info_callback,
qr_querycallback_t qr_rules_callback,
qr_querycallback_t qr_players_callback,
void *userdata
2023-02-05 01:40:14 +01:00
)
{
2023-07-05 21:24:14 +02:00
struct sockaddr_in saddr;
SOCKET hbsock;
int maxport;
int lasterror = 0;
2023-02-05 01:40:14 +01:00
#if defined(_LINUX)
2023-07-05 21:24:14 +02:00
unsigned int saddrlen;
2023-02-05 01:40:14 +01:00
#else
2023-07-05 21:24:14 +02:00
int saddrlen;
2023-02-05 01:40:14 +01:00
#endif
2023-07-05 21:24:14 +02:00
hbsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (hbsock == INVALID_SOCKET) {
return 1;
}
maxport = baseport + NUM_PORTS_TO_TRY;
while (baseport < maxport) {
get_sockaddrin(ip, baseport, &saddr, NULL);
if (saddr.sin_addr.s_addr == htonl(0x7F000001)) { //localhost -- we don't want that!
saddr.sin_addr.s_addr = INADDR_ANY;
}
lasterror = bind(hbsock, (struct sockaddr *)&saddr, sizeof(saddr));
if (lasterror == 0) {
break; //we found a port
}
baseport++;
}
if (lasterror != 0) //we weren't able to find a port
{
return 2;
}
if (baseport == 0) //we bound it dynamically
{
saddrlen = sizeof(saddr);
lasterror = getsockname(hbsock, (struct sockaddr *)&saddr, &saddrlen);
if (lasterror) {
return 2;
}
baseport = ntohs(saddr.sin_port);
}
init_qrec(
qrec,
baseport,
hbsock,
hbsock,
gamename,
secret_key,
qr_basic_callback,
qr_info_callback,
qr_rules_callback,
qr_players_callback,
userdata
);
if (!qrec) {
qrec = &current_rec;
}
2023-07-29 19:30:39 +02:00
return do_connect((void *)hbsock, MASTER_SERVER_HOST, 27900, &(*qrec)->master_saddr);
2023-02-05 01:40:14 +01:00
}