Files
quakespasm/Quake/net_dgrm.cpp
T
2026-08-30 12:38:12 -05:00

1305 lines
39 KiB
C++

/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// This is enables a simple IP banning mechanism
#define BAN_TEST
#include "q_stdinc.hpp"
#include "arch_def.hpp"
#include "net_sys.hpp"
#include "quakedef.hpp"
#include "net_defs.hpp"
#include "net_dgrm.hpp"
#include <cstring>
// these two macros are to make the code more readable
#define sfunc net_landrivers[sock->landriver]
#define dfunc net_landrivers[net_landriverlevel]
static int net_landriverlevel;
/* statistic counters */
static int packetsSent = 0;
static int packetsReSent = 0;
static int packetsReceived = 0;
static int receivedDuplicateCount = 0;
static int shortPacketCount = 0;
static int droppedDatagrams;
static struct {
unsigned int length;
unsigned int sequence;
byte data[MAX_DATAGRAM];
} packetBuffer;
static int myDriverLevel;
extern bool m_return_onerror;
extern char m_return_reason[32];
static char *StrAddr(struct qsockaddr *addr) {
static char buf[34];
byte *p = (byte *) addr;
int n;
for (n = 0; n < 16; n++)
sprintf(buf + n * 2, "%02x", *p++);
return buf;
}
#ifdef BAN_TEST
static struct in_addr banAddr;
static struct in_addr banMask;
static void NET_Ban_f(void) {
char addrStr[32];
char maskStr[32];
void (*print_fn)(const char *fmt, ...) FUNCP_PRINTF(1, 2);
if (command::last_source == command::source::command) {
if (!sv.active) {
command::forward_to_server();
return;
}
print_fn = console::info;
} else {
if (pr_global_struct->deathmatch)
return;
print_fn = SV_ClientPrintf;
}
switch (command::argc()) {
case 1:
if (banAddr.s_addr != INADDR_ANY) {
std::strcpy(addrStr, inet_ntoa(banAddr));
std::strcpy(maskStr, inet_ntoa(banMask));
print_fn("Banning %s [%s]\n", addrStr, maskStr);
} else
print_fn("Banning not active\n");
break;
case 2:
if (q_strcasecmp(command::argv(1)->c_str(), "off") == 0)
banAddr.s_addr = INADDR_ANY;
else
banAddr.s_addr = inet_addr(command::argv(1)->c_str());
banMask.s_addr = INADDR_NONE;
break;
case 3:
banAddr.s_addr = inet_addr(command::argv(1)->c_str());
banMask.s_addr = inet_addr(command::argv(2)->c_str());
break;
default:
print_fn("BAN ip_address [mask]\n");
break;
}
}
#endif // BAN_TEST
int Datagram_SendMessage(qsocket_t *sock, net::msg &msg) {
unsigned int packetLen;
unsigned int dataLen;
unsigned int eom;
#ifdef DEBUG
if (data->cursize == 0)
Sys_Error("Datagram_SendMessage: zero length message");
if (data->cursize > NET_MAXMESSAGE)
Sys_Error("Datagram_SendMessage: message too big: %u", data->cursize);
if (sock->canSend == false)
Sys_Error("SendMessage: called with canSend == false");
#endif
std::memcpy(sock->sendMessage, msg.data(), msg.size());
sock->sendMessageLength = msg.size();
if (msg.size() <= MAX_DATAGRAM) {
dataLen = msg.size();
eom = NETFLAG_EOM;
} else {
dataLen = MAX_DATAGRAM;
eom = 0;
}
packetLen = NET_HEADERSIZE + dataLen;
packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
packetBuffer.sequence = BigLong(sock->sendSequence++);
std::memcpy(packetBuffer.data, sock->sendMessage, dataLen);
sock->canSend = false;
if (sfunc.Write(sock->socket, (byte *) &packetBuffer, packetLen, &sock->addr) == -1)
return -1;
sock->lastSendTime = net_time;
packetsSent++;
return 1;
}
static int SendMessageNext(qsocket_t *sock) {
unsigned int packetLen;
unsigned int dataLen;
unsigned int eom;
if (sock->sendMessageLength <= MAX_DATAGRAM) {
dataLen = sock->sendMessageLength;
eom = NETFLAG_EOM;
} else {
dataLen = MAX_DATAGRAM;
eom = 0;
}
packetLen = NET_HEADERSIZE + dataLen;
packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
packetBuffer.sequence = BigLong(sock->sendSequence++);
std::memcpy(packetBuffer.data, sock->sendMessage, dataLen);
sock->sendNext = false;
if (sfunc.Write(sock->socket, (byte *) &packetBuffer, packetLen, &sock->addr) == -1)
return -1;
sock->lastSendTime = net_time;
packetsSent++;
return 1;
}
static int ReSendMessage(qsocket_t *sock) {
unsigned int packetLen;
unsigned int dataLen;
unsigned int eom;
if (sock->sendMessageLength <= MAX_DATAGRAM) {
dataLen = sock->sendMessageLength;
eom = NETFLAG_EOM;
} else {
dataLen = MAX_DATAGRAM;
eom = 0;
}
packetLen = NET_HEADERSIZE + dataLen;
packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
packetBuffer.sequence = BigLong(sock->sendSequence - 1);
std::memcpy(packetBuffer.data, sock->sendMessage, dataLen);
sock->sendNext = false;
if (sfunc.Write(sock->socket, (byte *) &packetBuffer, packetLen, &sock->addr) == -1)
return -1;
sock->lastSendTime = net_time;
packetsReSent++;
return 1;
}
bool Datagram_CanSendMessage(qsocket_t *sock) {
if (sock->sendNext)
SendMessageNext(sock);
return sock->canSend;
}
bool Datagram_CanSendUnreliableMessage(qsocket_t *sock) {
return true;
}
int Datagram_SendUnreliableMessage(qsocket_t *sock, net::msg &msg) {
int packetLen;
#ifdef DEBUG
if (data->cursize == 0)
Sys_Error("Datagram_SendUnreliableMessage: zero length message");
if (data->cursize > MAX_DATAGRAM)
Sys_Error("Datagram_SendUnreliableMessage: message too big: %u", data->cursize);
#endif
packetLen = NET_HEADERSIZE + msg.size();
packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE);
packetBuffer.sequence = BigLong(sock->unreliableSendSequence++);
std::memcpy(packetBuffer.data, msg.data(), msg.size());
if (sfunc.Write(sock->socket, (byte *) &packetBuffer, packetLen, &sock->addr) == -1)
return -1;
packetsSent++;
return 1;
}
int Datagram_GetMessage(qsocket_t *sock) {
unsigned int length;
unsigned int flags;
int ret = 0;
struct qsockaddr readaddr;
unsigned int sequence;
unsigned int count;
if (!sock->canSend)
if ((net_time - sock->lastSendTime) > 1.0)
ReSendMessage(sock);
while (1) {
length = (unsigned int) sfunc.Read(sock->socket, (byte *) &packetBuffer,
NET_DATAGRAMSIZE, &readaddr);
// if ((rand() & 255) > 220)
// continue;
if (length == 0)
break;
if (length == (unsigned int) -1) {
console::info("Read error\n");
return -1;
}
if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0) {
console::info("Forged packet received\n");
console::info("Expected: %s\n", StrAddr(&sock->addr));
console::info("Received: %s\n", StrAddr(&readaddr));
continue;
}
if (length < NET_HEADERSIZE) {
shortPacketCount++;
continue;
}
length = BigLong(packetBuffer.length);
flags = length & (~NETFLAG_LENGTH_MASK);
length &= NETFLAG_LENGTH_MASK;
if (flags & NETFLAG_CTL)
continue;
sequence = BigLong(packetBuffer.sequence);
packetsReceived++;
if (flags & NETFLAG_UNRELIABLE) {
if (sequence < sock->unreliableReceiveSequence) {
console::debug("Got a stale datagram\n");
ret = 0;
break;
}
if (sequence != sock->unreliableReceiveSequence) {
count = sequence - sock->unreliableReceiveSequence;
droppedDatagrams += count;
console::debug("Dropped %u datagram(s)\n", count);
}
sock->unreliableReceiveSequence = sequence + 1;
length -= NET_HEADERSIZE;
net_message.clear();
net_message.write(packetBuffer.data, length);
ret = 2;
break;
}
if (flags & NETFLAG_ACK) {
if (sequence != (sock->sendSequence - 1)) {
console::debug("Stale ACK received\n");
continue;
}
if (sequence == sock->ackSequence) {
sock->ackSequence++;
if (sock->ackSequence != sock->sendSequence)
console::debug("ack sequencing error\n");
} else {
console::debug("Duplicate ACK received\n");
continue;
}
sock->sendMessageLength -= MAX_DATAGRAM;
if (sock->sendMessageLength > 0) {
memmove(sock->sendMessage, sock->sendMessage + MAX_DATAGRAM, sock->sendMessageLength);
sock->sendNext = true;
} else {
sock->sendMessageLength = 0;
sock->canSend = true;
}
continue;
}
if (flags & NETFLAG_DATA) {
packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK);
packetBuffer.sequence = BigLong(sequence);
sfunc.Write(sock->socket, (byte *) &packetBuffer, NET_HEADERSIZE, &readaddr);
if (sequence != sock->receiveSequence) {
receivedDuplicateCount++;
continue;
}
sock->receiveSequence++;
length -= NET_HEADERSIZE;
if (flags & NETFLAG_EOM) {
net_message.clear();
net_message.write(sock->receiveMessage, sock->receiveMessageLength);
net_message.write(packetBuffer.data, length);
sock->receiveMessageLength = 0;
ret = 1;
break;
}
std::memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length);
sock->receiveMessageLength += length;
continue;
}
}
if (sock->sendNext)
SendMessageNext(sock);
return ret;
}
static void PrintStats(qsocket_t *s) {
console::info("canSend = %4u \n", s->canSend);
console::info("sendSeq = %4u ", s->sendSequence);
console::info("recvSeq = %4u \n", s->receiveSequence);
console::info("\n");
}
static void NET_Stats_f(void) {
qsocket_t *s;
if (command::argc() == 1) {
console::info("unreliable messages sent = %i\n", unreliableMessagesSent);
console::info("unreliable messages recv = %i\n", unreliableMessagesReceived);
console::info("reliable messages sent = %i\n", messagesSent);
console::info("reliable messages received = %i\n", messagesReceived);
console::info("packetsSent = %i\n", packetsSent);
console::info("packetsReSent = %i\n", packetsReSent);
console::info("packetsReceived = %i\n", packetsReceived);
console::info("receivedDuplicateCount = %i\n", receivedDuplicateCount);
console::info("shortPacketCount = %i\n", shortPacketCount);
console::info("droppedDatagrams = %i\n", droppedDatagrams);
} else if (std::strcmp(command::argv(1)->c_str(), "*") == 0) {
for (s = net_activeSockets; s; s = s->next)
PrintStats(s);
for (s = net_freeSockets; s; s = s->next)
PrintStats(s);
} else {
for (s = net_activeSockets; s; s = s->next) {
if (q_strcasecmp(command::argv(1)->c_str(), s->address) == 0)
break;
}
if (s == NULL) {
for (s = net_freeSockets; s; s = s->next) {
if (q_strcasecmp(command::argv(1)->c_str(), s->address) == 0)
break;
}
}
if (s == NULL)
return;
PrintStats(s);
}
}
// recognize ip:port (based on ProQuake)
static const char *Strip_Port(const char *host) {
static char noport[MAX_QPATH];
/* array size as in Host_Connect_f() */
char *p;
int port;
if (!host || !*host)
return host;
q_strlcpy(noport, host, sizeof(noport));
if ((p = std::strrchr(noport, ':')) == NULL)
return host;
*p++ = '\0';
port = std::atoi(p);
if (port > 0 && port < 65536 && port != net_hostport) {
net_hostport = port;
console::info("Port set to %d\n", net_hostport);
}
return noport;
}
static bool testInProgress = false;
static int testPollCount;
static int testDriver;
static sys_socket_t testSocket;
static void Test_Poll(void *);
static PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll};
static void Test_Poll(void *unused) {
struct qsockaddr clientaddr;
int control;
int len;
char name[32];
char address[64];
int colors;
int frags;
int connectTime;
net_landriverlevel = testDriver;
while (1) {
const auto buf = new std::uint8_t[MAX_DATAGRAM];
len = dfunc.Read(testSocket, &buf[0], MAX_DATAGRAM, &clientaddr);
if (len < (int) sizeof(int)) {
delete []buf;
break;
}
net_message.write(buf, len);
delete []buf;
net_message.begin_reading();
control = BigLong(*((int *) net_message.data()));
net_message.read_long();
if (control == -1)
break;
if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL)
break;
if ((control & NETFLAG_LENGTH_MASK) != len)
break;
if (net_message.read_byte().value() != CCREP_PLAYER_INFO)
Sys_Error("Unexpected response to Player Info request\n");
net_message.read_byte().value(); /* playerNumber */
std::strcpy(name, net_message.read_string().value().c_str());
colors = net_message.read_long().value();
frags = net_message.read_long().value();
connectTime = net_message.read_long().value();
std::strcpy(address, net_message.read_string().value().c_str());
console::info("%s\n frags:%3i colors:%d %d time:%d\n %s\n", name, frags, colors >> 4, colors & 0x0f,
connectTime / 60, address);
}
testPollCount--;
if (testPollCount) {
SchedulePollProcedure(&testPollProcedure, 0.1);
} else {
dfunc.Close_Socket(testSocket);
testInProgress = false;
}
}
static void Test_f(void) {
const char *host;
int n;
int maxusers = MAX_SCOREBOARD;
struct qsockaddr sendaddr;
if (testInProgress)
return;
host = Strip_Port(command::argv(1).value_or("").c_str());
if (host && hostCacheCount) {
for (n = 0; n < hostCacheCount; n++) {
if (q_strcasecmp(host, hostcache[n].name) == 0) {
if (hostcache[n].driver != myDriverLevel)
continue;
net_landriverlevel = hostcache[n].ldriver;
maxusers = hostcache[n].maxusers;
std::memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
break;
}
}
if (n < hostCacheCount)
goto JustDoIt;
}
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) {
if (!net_landrivers[net_landriverlevel].initialized)
continue;
// see if we can resolve the host name
if (dfunc.GetAddrFromName(host, &sendaddr) != -1)
break;
}
if (net_landriverlevel == net_numlandrivers) {
console::info("Could not resolve %s\n", host);
return;
}
JustDoIt:
testSocket = dfunc.Open_Socket(0);
if (testSocket == INVALID_SOCKET)
return;
testInProgress = true;
testPollCount = 20;
testDriver = net_landriverlevel;
for (n = 0; n < maxusers; n++) {
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREQ_PLAYER_INFO);
net_message.write_byte(n);
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(testSocket, net_message.data(), net_message.size(), &sendaddr);
}
net_message.clear();
SchedulePollProcedure(&testPollProcedure, 0.1);
}
static bool test2InProgress = false;
static int test2Driver;
static sys_socket_t test2Socket;
static void Test2_Poll(void *);
static PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll};
static void Test2_Poll(void *unused) {
struct qsockaddr clientaddr;
int control;
int len;
char name[256];
char value[256];
net_landriverlevel = test2Driver;
name[0] = 0;
auto buf = new std::uint8_t[MAX_DATAGRAM];
len = dfunc.Read(test2Socket, &buf[0], MAX_DATAGRAM, &clientaddr);
if (len < (int) sizeof(int)) {
delete []buf;
goto Reschedule;
}
net_message.write(buf, len);
delete []buf;
net_message.begin_reading();
control = BigLong(*((int *) net_message.data()));
net_message.read_long();
if (control == -1)
goto Error;
if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL)
goto Error;
if ((control & NETFLAG_LENGTH_MASK) != len)
goto Error;
if (net_message.read_byte().value() != CCREP_RULE_INFO)
goto Error;
std::strcpy(name, net_message.read_string().value().c_str());
if (name[0] == 0)
goto Done;
std::strcpy(value, net_message.read_string().value().c_str());
console::info("%-16.16s %-16.16s\n", name, value);
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREQ_RULE_INFO);
net_message.write_string(name);
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(test2Socket, net_message.data(), net_message.size(), &clientaddr);
net_message.clear();
Reschedule:
SchedulePollProcedure(&test2PollProcedure, 0.05);
return;
Error:
console::info("Unexpected response to Rule Info request\n");
Done:
dfunc.Close_Socket(test2Socket);
test2InProgress = false;
return;
}
static void Test2_f(void) {
const char *host;
int n;
struct qsockaddr sendaddr;
if (test2InProgress)
return;
host = Strip_Port(command::argv(1).value_or("").c_str());
if (host && hostCacheCount) {
for (n = 0; n < hostCacheCount; n++) {
if (q_strcasecmp(host, hostcache[n].name) == 0) {
if (hostcache[n].driver != myDriverLevel)
continue;
net_landriverlevel = hostcache[n].ldriver;
std::memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
break;
}
}
if (n < hostCacheCount)
goto JustDoIt;
}
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) {
if (!net_landrivers[net_landriverlevel].initialized)
continue;
// see if we can resolve the host name
if (dfunc.GetAddrFromName(host, &sendaddr) != -1)
break;
}
if (net_landriverlevel == net_numlandrivers) {
console::info("Could not resolve %s\n", host);
return;
}
JustDoIt:
test2Socket = dfunc.Open_Socket(0);
if (test2Socket == INVALID_SOCKET)
return;
test2InProgress = true;
test2Driver = net_landriverlevel;
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREQ_RULE_INFO);
net_message.write_string("");
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(test2Socket, net_message.data(), net_message.size(), &sendaddr);
net_message.clear();
SchedulePollProcedure(&test2PollProcedure, 0.05);
}
int Datagram_Init(void) {
int i, num_inited;
sys_socket_t csock;
#ifdef BAN_TEST
banAddr.s_addr = INADDR_ANY;
banMask.s_addr = INADDR_NONE;
#endif
myDriverLevel = net_driverlevel;
command::add("net_stats", NET_Stats_f);
if (safemode || common::check_param("-nolan").has_value())
return -1;
num_inited = 0;
for (i = 0; i < net_numlandrivers; i++) {
csock = net_landrivers[i].Init();
if (csock == INVALID_SOCKET)
continue;
net_landrivers[i].initialized = true;
net_landrivers[i].controlSock = csock;
num_inited++;
}
if (num_inited == 0)
return -1;
#ifdef BAN_TEST
command::add("ban", NET_Ban_f);
#endif
command::add("test", Test_f);
command::add("test2", Test2_f);
return 0;
}
void Datagram_Shutdown(void) {
int i;
//
// shutdown the lan drivers
//
for (i = 0; i < net_numlandrivers; i++) {
if (net_landrivers[i].initialized) {
net_landrivers[i].Shutdown();
net_landrivers[i].initialized = false;
}
}
}
void Datagram_Close(qsocket_t *sock) {
sfunc.Close_Socket(sock->socket);
}
void Datagram_Listen(bool state) {
int i;
for (i = 0; i < net_numlandrivers; i++) {
if (net_landrivers[i].initialized)
net_landrivers[i].Listen(state);
}
}
static qsocket_t *_Datagram_CheckNewConnections(void) {
struct qsockaddr clientaddr;
struct qsockaddr newaddr;
sys_socket_t newsock;
sys_socket_t acceptsock;
qsocket_t *sock;
qsocket_t *s;
int len;
int command;
int control;
int ret;
acceptsock = dfunc.CheckNewConnections();
if (acceptsock == INVALID_SOCKET)
return NULL;
net_message.clear();
auto buf = new byte[MAX_DATAGRAM];
len = dfunc.Read(acceptsock, &buf[0], MAX_DATAGRAM, &clientaddr);
if (len < (int) sizeof(int)) {
delete []buf;
return nullptr;
}
net_message.write(buf, len);
delete []buf;
net_message.begin_reading();
control = BigLong(*((int *) net_message.data()));
net_message.read_long();
if (control == -1)
return NULL;
if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL)
return NULL;
if ((control & NETFLAG_LENGTH_MASK) != len)
return NULL;
command = net_message.read_byte().value();
if (command == CCREQ_SERVER_INFO) {
if (std::strcmp(net_message.read_string().value().c_str(), "QUAKE") != 0)
return NULL;
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREP_SERVER_INFO);
dfunc.GetSocketAddr(acceptsock, &newaddr);
net_message.write_string(dfunc.AddrToString(&newaddr));
net_message.write_string(hostname.string);
net_message.write_string(sv.name);
net_message.write_byte(net_activeconnections);
net_message.write_byte(svs.maxclients);
net_message.write_byte(NET_PROTOCOL_VERSION);
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(acceptsock, net_message.data(), net_message.size(), &clientaddr);
net_message.clear();
return NULL;
}
if (command == CCREQ_PLAYER_INFO) {
int playerNumber;
int activeNumber;
int clientNumber;
client_t *client;
playerNumber = net_message.read_byte().value();
activeNumber = -1;
for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++) {
if (client->active) {
activeNumber++;
if (activeNumber == playerNumber)
break;
}
}
if (clientNumber == svs.maxclients)
return NULL;
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREP_PLAYER_INFO);
net_message.write_byte(playerNumber);
net_message.write_string(client->name);
net_message.write_long(client->colors);
net_message.write_long((int) client->edict->v.frags);
net_message.write_long((int) (net_time - client->netconnection->connecttime));
net_message.write_string(client->netconnection->address);
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(acceptsock, net_message.data(), net_message.size(), &clientaddr);
net_message.clear();
return NULL;
}
if (command == CCREQ_RULE_INFO) {
// find the search start location
auto prevCvarName = net_message.read_string().value();
auto var = convar::find_var_after(prevCvarName, std::nullopt);
// send the response
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREP_RULE_INFO);
if (var) {
net_message.write_string(var.value()->name.c_str());
net_message.write_string(var.value()->string);
}
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(acceptsock, net_message.data(), net_message.size(), &clientaddr);
net_message.clear();
return NULL;
}
if (command != CCREQ_CONNECT)
return NULL;
if (std::strcmp(net_message.read_string().value().c_str(), "QUAKE") != 0)
return NULL;
if (net_message.read_byte().value() != NET_PROTOCOL_VERSION) {
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREP_REJECT);
net_message.write_string("Incompatible version.\n");
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(acceptsock, net_message.data(), net_message.size(), &clientaddr);
net_message.clear();
return NULL;
}
#ifdef BAN_TEST
// check for a ban
if (clientaddr.qsa_family == AF_INET) {
in_addr_t testAddr;
testAddr = ((struct sockaddr_in *) &clientaddr)->sin_addr.s_addr;
if ((testAddr & banMask.s_addr) == banAddr.s_addr) {
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREP_REJECT);
net_message.write_string("You have been banned.\n");
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(acceptsock, net_message.data(), net_message.size(), &clientaddr);
net_message.clear();
return NULL;
}
}
#endif
// see if this guy is already connected
for (s = net_activeSockets; s; s = s->next) {
if (s->driver != net_driverlevel)
continue;
ret = dfunc.AddrCompare(&clientaddr, &s->addr);
if (ret >= 0) {
// is this a duplicate connection reqeust?
if (ret == 0 && net_time - s->connecttime < 2.0) {
// yes, so send a duplicate reply
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREP_ACCEPT);
dfunc.GetSocketAddr(s->socket, &newaddr);
net_message.write_long(dfunc.GetSocketPort(&newaddr));
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(acceptsock, net_message.data(), net_message.size(), &clientaddr);
net_message.clear();
return NULL;
}
// it's somebody coming back in from a crash/disconnect
// so close the old qsocket and let their retry get them back in
NET_Close(s);
return NULL;
}
}
// allocate a QSocket
sock = NET_NewQSocket();
if (sock == NULL) // no room; try to let him know
{
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREP_REJECT);
net_message.write_string("Server is full.\n");
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(acceptsock, net_message.data(), net_message.size(), &clientaddr);
net_message.clear();
return NULL;
}
// allocate a network socket
newsock = dfunc.Open_Socket(0);
if (newsock == INVALID_SOCKET) {
NET_FreeQSocket(sock);
return NULL;
}
// connect to the client
if (dfunc.Connect(newsock, &clientaddr) == -1) {
dfunc.Close_Socket(newsock);
NET_FreeQSocket(sock);
return NULL;
}
// everything is allocated, just fill in the details
sock->socket = newsock;
sock->landriver = net_landriverlevel;
sock->addr = clientaddr;
std::strcpy(sock->address, dfunc.AddrToString(&clientaddr));
// send him back the info about the server connection he has been allocated
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREP_ACCEPT);
dfunc.GetSocketAddr(newsock, &newaddr);
net_message.write_long(dfunc.GetSocketPort(&newaddr));
// MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(acceptsock, net_message.data(), net_message.size(), &clientaddr);
net_message.clear();
return sock;
}
qsocket_t *Datagram_CheckNewConnections(void) {
qsocket_t *ret = NULL;
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) {
if (net_landrivers[net_landriverlevel].initialized) {
if ((ret = _Datagram_CheckNewConnections()) != NULL)
break;
}
}
return ret;
}
static void _Datagram_SearchForHosts(bool xmit) {
int ret;
int n;
int i;
struct qsockaddr readaddr;
struct qsockaddr myaddr;
int control;
dfunc.GetSocketAddr(dfunc.controlSock, &myaddr);
if (xmit) {
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREQ_SERVER_INFO);
net_message.write_string("QUAKE");
net_message.write_byte(NET_PROTOCOL_VERSION);
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Broadcast(dfunc.controlSock, net_message.data(), net_message.size());
net_message.clear();
}
auto buf = new std::uint8_t[MAX_DATAGRAM];
while ((ret = dfunc.Read(dfunc.controlSock, &buf[0], MAX_DATAGRAM, &readaddr)) > 0) {
if (ret < (int) sizeof(int))
continue;
net_message.write(buf, ret);
// don't answer our own query
if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0)
continue;
// is the cache full?
if (hostCacheCount == HOSTCACHESIZE)
continue;
net_message.begin_reading();
control = BigLong(*((int *) net_message.data()));
net_message.read_long();
if (control == -1)
continue;
if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL)
continue;
if ((control & NETFLAG_LENGTH_MASK) != ret)
continue;
if (net_message.read_byte().value() != CCREP_SERVER_INFO)
continue;
dfunc.GetAddrFromName(net_message.read_string().value().c_str(), &readaddr);
// search the cache for this server
for (n = 0; n < hostCacheCount; n++) {
if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0)
break;
}
// is it already there?
if (n < hostCacheCount)
continue;
// add it
hostCacheCount++;
std::strcpy(hostcache[n].name, net_message.read_string().value().c_str());
std::strcpy(hostcache[n].map, net_message.read_string().value().c_str());
hostcache[n].users = net_message.read_byte().value();
hostcache[n].maxusers = net_message.read_byte().value();
if (net_message.read_byte().value() != NET_PROTOCOL_VERSION) {
std::strcpy(hostcache[n].cname, hostcache[n].name);
hostcache[n].cname[14] = 0;
std::strcpy(hostcache[n].name, "*");
std::strcat(hostcache[n].name, hostcache[n].cname);
}
std::memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr));
hostcache[n].driver = net_driverlevel;
hostcache[n].ldriver = net_landriverlevel;
std::strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr));
// check for a name conflict
for (i = 0; i < hostCacheCount; i++) {
if (i == n)
continue;
if (q_strcasecmp(hostcache[n].name, hostcache[i].name) == 0) {
i = std::strlen(hostcache[n].name);
if (i < 15 && hostcache[n].name[i - 1] > '8') {
hostcache[n].name[i] = '0';
hostcache[n].name[i + 1] = 0;
} else
hostcache[n].name[i - 1]++;
i = -1;
}
}
}
delete []buf;
}
void Datagram_SearchForHosts(bool xmit) {
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) {
if (hostCacheCount == HOSTCACHESIZE)
break;
if (net_landrivers[net_landriverlevel].initialized)
_Datagram_SearchForHosts(xmit);
}
}
static qsocket_t *_Datagram_Connect(const char *host) {
struct qsockaddr sendaddr;
struct qsockaddr readaddr;
qsocket_t *sock;
sys_socket_t newsock;
int ret;
int reps;
double start_time;
int control;
std::string reason;
// see if we can resolve the host name
if (dfunc.GetAddrFromName(host, &sendaddr) == -1) {
console::info("Could not resolve %s\n", host);
return NULL;
}
newsock = dfunc.Open_Socket(0);
if (newsock == INVALID_SOCKET)
return NULL;
sock = NET_NewQSocket();
if (sock == NULL)
goto ErrorReturn2;
sock->socket = newsock;
sock->landriver = net_landriverlevel;
// connect to the host
if (dfunc.Connect(newsock, &sendaddr) == -1)
goto ErrorReturn;
// send the connection request
console::info("trying...\n");
SCR_UpdateScreen();
start_time = net_time;
for (reps = 0; reps < 3; reps++) {
net_message.clear();
// save space for the header, filled in later
net_message.write_long(0);
net_message.write_byte(CCREQ_CONNECT);
net_message.write_string("QUAKE");
net_message.write_byte(NET_PROTOCOL_VERSION);
*((int *) net_message.data()) = BigLong(NETFLAG_CTL | (net_message.size() & NETFLAG_LENGTH_MASK));
dfunc.Write(newsock, net_message.data(), net_message.size(), &sendaddr);
net_message.clear();
auto buf = new std::uint8_t[MAX_DATAGRAM];
do {
ret = dfunc.Read(newsock, &buf[0], MAX_DATAGRAM, &readaddr);
// if we got something, validate it
if (ret > 0) {
// is it from the right place?
if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) {
console::info("wrong reply address\n");
console::info("Expected: %s | %s\n", dfunc.AddrToString(&sendaddr), StrAddr(&sendaddr));
console::info("Received: %s | %s\n", dfunc.AddrToString(&readaddr), StrAddr(&readaddr));
SCR_UpdateScreen();
ret = 0;
continue;
}
if (ret < (int) sizeof(int)) {
ret = 0;
continue;
}
net_message.write(buf, ret);
net_message.begin_reading();
control = BigLong(*((int *) net_message.data()));
net_message.read_long();
if (control == -1) {
ret = 0;
continue;
}
if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL) {
ret = 0;
continue;
}
if ((control & NETFLAG_LENGTH_MASK) != ret) {
ret = 0;
continue;
}
}
} while (ret == 0 && (SetNetTime() - start_time) < 2.5);
delete []buf;
if (ret)
break;
console::info("still trying...\n");
SCR_UpdateScreen();
start_time = SetNetTime();
}
if (ret == 0) {
reason = "No Response";
console::info("%s\n", reason.c_str());
std::strcpy(m_return_reason, reason.c_str());
goto ErrorReturn;
}
if (ret == -1) {
reason = "Network Error";
console::info("%s\n", reason.c_str());
std::strcpy(m_return_reason, reason.c_str());
goto ErrorReturn;
}
ret = net_message.read_byte().value();
if (ret == CCREP_REJECT) {
reason = net_message.read_string().value();
console::info("%s\n", reason.c_str());
q_strlcpy(m_return_reason, reason.c_str(), sizeof(m_return_reason));
goto ErrorReturn;
}
if (ret == CCREP_ACCEPT) {
std::memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr));
dfunc.SetSocketPort(&sock->addr, net_message.read_long().value());
} else {
reason = "Bad Response";
console::info("%s\n", reason.c_str());
std::strcpy(m_return_reason, reason.c_str());
goto ErrorReturn;
}
dfunc.GetNameFromAddr(&sendaddr, sock->address);
console::info("Connection accepted\n");
sock->lastMessageTime = SetNetTime();
// switch the connection to the specified address
if (dfunc.Connect(newsock, &sock->addr) == -1) {
reason = "Connect to Game failed";
console::info("%s\n", reason.c_str());
std::strcpy(m_return_reason, reason.c_str());
goto ErrorReturn;
}
m_return_onerror = false;
return sock;
ErrorReturn:
NET_FreeQSocket(sock);
ErrorReturn2:
dfunc.Close_Socket(newsock);
if (m_return_onerror) {
IN_Deactivate(modestate == MS_WINDOWED);
key_dest = key_menu;
m_state = m_return_state;
m_return_onerror = false;
}
return NULL;
}
qsocket_t *Datagram_Connect(const char *host) {
qsocket_t *ret = NULL;
host = Strip_Port(host);
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) {
if (net_landrivers[net_landriverlevel].initialized) {
if ((ret = _Datagram_Connect(host)) != NULL)
break;
}
}
return ret;
}