quakeforge/libs/net/nm/net_dgrm.c
Bill Currie f6baa16084 [cvar] Remove the cvar lists
They're really redundant, and removing the next pointer makes for a
slightly smaller cvar struct. Cvar_Select was added to allow finding
lists of matching cvars.

The tab-completion and config saving code was reworked to use the hash
table DO functions. Comments removed since the code was completely
rewritten, but still many thanks to EvilTypeGuy and Fett.
2022-04-25 00:26:45 +09:00

1164 lines
29 KiB
C

/*
net_dgrm.c
Datagram network driver.
Copyright (C) 1996-1997 Id Software, 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 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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "QF/cmd.h"
#include "QF/cvar.h"
#include "QF/sys.h"
#include "QF/heapsort.h"
#include "QF/keys.h"
#include "QF/qendian.h"
#include "QF/msg.h"
#include "QF/qargs.h"
#include "QF/screen.h"
#include "netmain.h"
#include "../nq/include/server.h"
// This is enables a simple IP banning mechanism
#define BAN_TEST
#ifdef BAN_TEST
#if defined(_WIN32)
#include "winquake.h"
typedef uint32_t in_addr_t;
#else
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
#endif
#endif // BAN_TEST
#include "net_dgrm.h"
// 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 */
int packetsSent = 0;
int packetsReSent = 0;
int packetsReceived = 0;
int receivedDuplicateCount = 0;
int shortPacketCount = 0;
int droppedDatagrams;
static int myDriverLevel;
static byte dgrm_send_packet_data[MAX_DATAGRAM + NET_HEADERSIZE];
static sizebuf_t dgrm_send_packet = {
.data = dgrm_send_packet_data,
.maxsize = sizeof (dgrm_send_packet_data),
};
static byte dgrm_receive_packet_data[MAX_DATAGRAM + NET_HEADERSIZE];
static sizebuf_t dgrm_receive_packet = {
.data = dgrm_receive_packet_data,
.maxsize = sizeof (dgrm_receive_packet_data),
};
static qmsg_t dgrm_message = {
.message = &dgrm_receive_packet,
};
// FIXME: MENUCODE
//extern int m_return_state;
//extern int m_state;
//extern qboolean m_return_onerror;
//extern char m_return_reason[32];
#ifdef BAN_TEST
in_addr_t banAddr = 0x00000000;
in_addr_t banMask = 0xffffffff;
static void
NET_Ban_f (void)
{
char addrStr[32]; //FIXME: overflow
char maskStr[32]; //FIXME: overflow
__attribute__((format(PRINTF, 1, 2))) void (*print) (const char *fmt, ...);
if (cmd_source == src_command) {
if (!sv.active) {
CL_Cmd_ForwardToServer ();
return;
}
print = Sys_Printf;
} else {
if (*sv_globals.deathmatch && !host_client->privileged) {
return;
}
print = SV_ClientPrintf;
}
switch (Cmd_Argc ()) {
case 1:
if (banAddr) {
struct in_addr t;
t.s_addr = banAddr;
strcpy (addrStr, inet_ntoa (t));
t.s_addr = banMask;
strcpy (maskStr, inet_ntoa (t));
print ("Banning %s [%s]\n", addrStr, maskStr);
} else {
print ("Banning not active\n");
}
break;
case 2:
if (strcasecmp (Cmd_Argv (1), "off") == 0) {
banAddr = 0x00000000;
} else {
banAddr = inet_addr (Cmd_Argv (1));
}
banMask = 0xffffffff;
break;
case 3:
banAddr = inet_addr (Cmd_Argv (1));
banMask = inet_addr (Cmd_Argv (2));
break;
default:
print ("BAN ip_address [mask]\n");
break;
}
}
#endif
int
Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data)
{
unsigned packetLen;
unsigned dataLen;
unsigned eom;
memcpy (sock->sendMessage, data->data, data->cursize);
sock->sendMessageLength = data->cursize;
if (data->cursize <= MAX_DATAGRAM) {
dataLen = data->cursize;
eom = NETFLAG_EOM;
} else {
dataLen = MAX_DATAGRAM;
eom = 0;
}
packetLen = NET_HEADERSIZE + dataLen;
SZ_Clear (&dgrm_send_packet);
MSG_WriteLongBE (&dgrm_send_packet, packetLen | (NETFLAG_DATA | eom));
MSG_WriteLongBE (&dgrm_send_packet, sock->sendSequence++);
MSG_WriteBytes (&dgrm_send_packet, sock->sendMessage, dataLen);
sock->canSend = false;
if (sfunc.Write (sock->socket, dgrm_send_packet.data,
dgrm_send_packet.cursize, &sock->addr) == -1) {
return -1;
}
sock->lastSendTime = net_time;
packetsSent++;
return 1;
}
static int
SendMessageNext (qsocket_t *sock)
{
unsigned packetLen;
unsigned dataLen;
unsigned eom;
if (sock->sendMessageLength <= MAX_DATAGRAM) {
dataLen = sock->sendMessageLength;
eom = NETFLAG_EOM;
} else {
dataLen = MAX_DATAGRAM;
eom = 0;
}
packetLen = NET_HEADERSIZE + dataLen;
SZ_Clear (&dgrm_send_packet);
MSG_WriteLongBE (&dgrm_send_packet, packetLen | (NETFLAG_DATA | eom));
MSG_WriteLongBE (&dgrm_send_packet, sock->sendSequence++);
MSG_WriteBytes (&dgrm_send_packet, sock->sendMessage, dataLen);
sock->sendNext = false;
if (sfunc.Write (sock->socket, dgrm_send_packet.data,
dgrm_send_packet.cursize, &sock->addr) == -1) {
return -1;
}
sock->lastSendTime = net_time;
packetsSent++;
return 1;
}
static int
ReSendMessage (qsocket_t *sock)
{
unsigned packetLen;
unsigned dataLen;
unsigned eom;
if (sock->sendMessageLength <= MAX_DATAGRAM) {
dataLen = sock->sendMessageLength;
eom = NETFLAG_EOM;
} else {
dataLen = MAX_DATAGRAM;
eom = 0;
}
packetLen = NET_HEADERSIZE + dataLen;
SZ_Clear (&dgrm_send_packet);
MSG_WriteLongBE (&dgrm_send_packet, packetLen | (NETFLAG_DATA | eom));
MSG_WriteLongBE (&dgrm_send_packet, sock->sendSequence - 1);
MSG_WriteBytes (&dgrm_send_packet, sock->sendMessage, dataLen);
sock->sendNext = false;
if (sfunc.Write (sock->socket, dgrm_send_packet.data,
dgrm_send_packet.cursize, &sock->addr) == -1) {
return -1;
}
sock->lastSendTime = net_time;
packetsReSent++;
return 1;
}
qboolean
Datagram_CanSendMessage (qsocket_t *sock)
{
if (sock->sendNext) {
SendMessageNext (sock);
}
return sock->canSend;
}
__attribute__((const)) qboolean
Datagram_CanSendUnreliableMessage (qsocket_t *sock)
{
return true;
}
int
Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
{
int packetLen;
packetLen = NET_HEADERSIZE + data->cursize;
SZ_Clear (&dgrm_send_packet);
MSG_WriteLongBE (&dgrm_send_packet, packetLen | NETFLAG_UNRELIABLE);
MSG_WriteLongBE (&dgrm_send_packet, sock->unreliableSendSequence++);
MSG_WriteBytes (&dgrm_send_packet, data->data, data->cursize);
if (sfunc.Write (sock->socket, dgrm_send_packet.data,
dgrm_send_packet.cursize, &sock->addr) == -1) {
return -1;
}
packetsSent++;
return 1;
}
int
Datagram_GetMessage (qsocket_t *sock)
{
unsigned length;
unsigned flags;
int ret = 0;
netadr_t readaddr;
unsigned sequence;
unsigned count;
/// If there is an outstanding reliable packet and more than 1 second has
/// passed, resend the packet.
if (!sock->canSend) {
if ((net_time - sock->lastSendTime) > 1.0) {
ReSendMessage (sock);
}
}
while (1) {
SZ_Clear (&dgrm_receive_packet);
length = sfunc.Read (sock->socket, dgrm_receive_packet.data,
dgrm_receive_packet.maxsize, &readaddr);
dgrm_receive_packet.cursize = length;
// if ((rand() & 255) > 220)
// continue;
if (length == 0) {
break;
}
if ((int) length == -1) {
Sys_Printf ("Read error\n");
return -1;
}
if (sfunc.AddrCompare (&readaddr, &sock->addr) != 0) {
continue;
}
if (length < NET_HEADERSIZE) {
shortPacketCount++;
continue;
}
MSG_BeginReading (&dgrm_message);
length = MSG_ReadLongBE (&dgrm_message);
flags = length & (~NETFLAG_LENGTH_MASK);
length &= NETFLAG_LENGTH_MASK;
if (flags & NETFLAG_CTL) {
continue;
}
sequence = MSG_ReadLongBE (&dgrm_message);
packetsReceived++;
if (flags & NETFLAG_UNRELIABLE) {
if (sequence < sock->unreliableReceiveSequence) {
Sys_MaskPrintf (SYS_net, "Got a stale datagram\n");
ret = 0;
break;
}
if (sequence != sock->unreliableReceiveSequence) {
count = sequence - sock->unreliableReceiveSequence;
droppedDatagrams += count;
Sys_MaskPrintf (SYS_net, "Dropped %u datagram(s)\n", count);
}
sock->unreliableReceiveSequence = sequence + 1;
length -= NET_HEADERSIZE;
/// Copy unreliable data to net_message
SZ_Clear (net_message->message);
byte *buf = SZ_GetSpace (net_message->message, length);
MSG_ReadBytes (&dgrm_message, buf, length);
ret = 2;
break;
}
if (flags & NETFLAG_ACK) {
if (sequence != (sock->sendSequence - 1)) {
Sys_MaskPrintf (SYS_net, "Stale ACK received\n");
continue;
}
if (sequence == sock->ackSequence) {
sock->ackSequence++;
if (sock->ackSequence != sock->sendSequence) {
Sys_MaskPrintf (SYS_net, "ack sequencing error\n");
}
} else {
Sys_MaskPrintf (SYS_net, "Duplicate ACK received\n");
continue;
}
sock->sendMessageLength -= MAX_DATAGRAM;
if (sock->sendMessageLength > 0) {
memcpy (sock->sendMessage, sock->sendMessage + MAX_DATAGRAM,
sock->sendMessageLength);
sock->sendNext = true;
} else {
sock->sendMessageLength = 0;
sock->canSend = true;
}
continue;
}
if (flags & NETFLAG_DATA) {
SZ_Clear (&dgrm_send_packet);
MSG_WriteLongBE (&dgrm_send_packet, NET_HEADERSIZE | NETFLAG_ACK);
MSG_WriteLongBE (&dgrm_send_packet, sequence);
sfunc.Write (sock->socket, dgrm_send_packet.data,
dgrm_send_packet.cursize, &readaddr);
if (sequence != sock->receiveSequence) {
receivedDuplicateCount++;
continue;
}
sock->receiveSequence++;
length -= NET_HEADERSIZE;
if (flags & NETFLAG_EOM) {
SZ_Clear (net_message->message);
SZ_Write (net_message->message, sock->receiveMessage,
sock->receiveMessageLength);
byte *buf = SZ_GetSpace (net_message->message, length);
MSG_ReadBytes (&dgrm_message, buf, length);
sock->receiveMessageLength = 0;
ret = 1;
break;
}
/// Append reliable data to sock->receiveMessage.
MSG_ReadBytes (&dgrm_message,
sock->receiveMessage + sock->receiveMessageLength,
length);
sock->receiveMessageLength += length;
continue;
}
}
if (sock->sendNext) {
SendMessageNext (sock);
}
return ret;
}
static void
PrintStats (qsocket_t *s)
{
Sys_Printf ("canSend = %4u \n", s->canSend);
Sys_Printf ("sendSeq = %4u ", s->sendSequence);
Sys_Printf ("recvSeq = %4u \n", s->receiveSequence);
Sys_Printf ("\n");
}
static void
NET_Stats_f (void)
{
qsocket_t *s;
if (Cmd_Argc () == 1) {
Sys_Printf ("unreliable messages sent = %i\n",
unreliableMessagesSent);
Sys_Printf ("unreliable messages recv = %i\n",
unreliableMessagesReceived);
Sys_Printf ("reliable messages sent = %i\n", messagesSent);
Sys_Printf ("reliable messages received = %i\n", messagesReceived);
Sys_Printf ("packetsSent = %i\n", packetsSent);
Sys_Printf ("packetsReSent = %i\n", packetsReSent);
Sys_Printf ("packetsReceived = %i\n", packetsReceived);
Sys_Printf ("receivedDuplicateCount = %i\n",
receivedDuplicateCount);
Sys_Printf ("shortPacketCount = %i\n", shortPacketCount);
Sys_Printf ("droppedDatagrams = %i\n", droppedDatagrams);
} else if (strcmp (Cmd_Argv (1), "*") == 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 (strcasecmp (Cmd_Argv (1), s->address) == 0) {
break;
}
}
if (s == NULL) {
for (s = net_freeSockets; s; s = s->next) {
if (strcasecmp (Cmd_Argv (1), s->address) == 0) {
break;
}
}
}
if (s == NULL) {
return;
}
PrintStats (s);
}
}
int
Datagram_Init (void)
{
int i;
int csock;
myDriverLevel = net_driverlevel;
Cmd_AddCommand ("net_stats", NET_Stats_f, "No Description");
if (COM_CheckParm ("-nolan")) {
return -1;
}
for (i = 0; i < net_numlandrivers; i++) {
csock = net_landrivers[i].Init ();
if (csock == -1) {
continue;
}
net_landrivers[i].initialized = true;
net_landrivers[i].controlSock = csock;
}
#ifdef BAN_TEST
Cmd_AddCommand ("ban", NET_Ban_f, "No Description");
#endif
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.CloseSocket (sock->socket);
}
void
Datagram_Listen (qboolean state)
{
int i;
for (i = 0; i < net_numlandrivers; i++) {
if (net_landrivers[i].initialized) {
net_landrivers[i].Listen (state);
}
}
}
static const cvar_t **cvar_list;
static int num_cvars;
static int
dg_cvar_cmp (const void *_a, const void *_b)
{
const cvar_t * const *a = _a;
const cvar_t * const *b = _b;
return strcmp ((*a)->name, (*b)->name);
}
static int
dg_cvar_select (const cvar_t *cvar, void *data)
{
if (cvar->flags & CVAR_SERVERINFO) {
*(int *)data += 1;
return 1;
}
return 0;
}
static qsocket_t *
_Datagram_CheckNewConnections (void)
{
netadr_t clientaddr;
netadr_t newaddr;
int newsock;
int acceptsock;
qsocket_t *sock;
qsocket_t *s;
int len;
int command;
int control;
int ret;
acceptsock = dfunc.CheckNewConnections ();
if (acceptsock == -1) {
return NULL;
}
SZ_Clear (net_message->message);
len = dfunc.Read (acceptsock, net_message->message->data,
net_message->message->maxsize, &clientaddr);
if (len < (int) sizeof (int)) {
return NULL;
}
net_message->message->cursize = len;
MSG_BeginReading (net_message);
control = MSG_ReadLongBE (net_message);
if (control == -1) {
return NULL;
}
if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL) {
return NULL;
}
if ((control & NETFLAG_LENGTH_MASK) != len) {
return NULL;
}
command = MSG_ReadByte (net_message);
if (command == CCREQ_SERVER_INFO) {
if (strcmp (MSG_ReadString (net_message), "QUAKE") != 0) {
return NULL;
}
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREP_SERVER_INFO);
dfunc.GetSocketAddr (acceptsock, &newaddr);
MSG_WriteString (net_message->message, dfunc.AddrToString (&newaddr));
MSG_WriteString (net_message->message, hostname);
MSG_WriteString (net_message->message, sv.name);
MSG_WriteByte (net_message->message, net_activeconnections);
MSG_WriteByte (net_message->message, svs.maxclients);
MSG_WriteByte (net_message->message, NET_PROTOCOL_VERSION);
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Write (acceptsock, net_message->message->data,
net_message->message->cursize, &clientaddr);
SZ_Clear (net_message->message);
return NULL;
}
if (command == CCREQ_PLAYER_INFO) {
int playerNumber;
int activeNumber;
unsigned clientNumber;
client_t *client;
playerNumber = MSG_ReadByte (net_message);
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;
}
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREP_PLAYER_INFO);
MSG_WriteByte (net_message->message, playerNumber);
MSG_WriteString (net_message->message, client->name);
MSG_WriteLong (net_message->message, client->colors);
MSG_WriteLong (net_message->message, SVfloat (client->edict, frags));
MSG_WriteLong (net_message->message,
(int) (net_time - client->netconnection->connecttime));
MSG_WriteString (net_message->message, client->netconnection->address);
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Write (acceptsock, net_message->message->data,
net_message->message->cursize, &clientaddr);
SZ_Clear (net_message->message);
return NULL;
}
if (command == CCREQ_RULE_INFO) {
const char *prevCvarName;
const cvar_t *var;
// find the search start location
prevCvarName = MSG_ReadString (net_message);
if (*prevCvarName) {
cvar_t key = { .name = prevCvarName };
var = bsearch (&key, cvar_list, num_cvars, sizeof (cvar_t *),
dg_cvar_cmp);
if (!var)
return 0;
var++;
} else {
if (cvar_list) {
free (cvar_list);
}
num_cvars = 0;
cvar_list = Cvar_Select (dg_cvar_select, &num_cvars);
heapsort (cvar_list, num_cvars, sizeof (cvar_t *), dg_cvar_cmp);
var = cvar_list[0];
}
// send the response
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREP_RULE_INFO);
if (var) {
MSG_WriteString (net_message->message, var->name);
MSG_WriteString (net_message->message, Cvar_VarString (var));
}
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Write (acceptsock, net_message->message->data,
net_message->message->cursize, &clientaddr);
SZ_Clear (net_message->message);
return NULL;
}
if (command != CCREQ_CONNECT) {
return NULL;
}
if (strcmp (MSG_ReadString (net_message), "QUAKE") != 0) {
return NULL;
}
if (MSG_ReadByte (net_message) != NET_PROTOCOL_VERSION) {
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREP_REJECT);
MSG_WriteString (net_message->message, "Incompatible version.\n");
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Write (acceptsock, net_message->message->data,
net_message->message->cursize, &clientaddr);
SZ_Clear (net_message->message);
return NULL;
}
#ifdef BAN_TEST
// check for a ban
if (clientaddr.family == AF_INET) {
unsigned testAddr;
memcpy (&testAddr, clientaddr.ip, 4);
if ((testAddr & banMask) == banAddr) {
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREP_REJECT);
MSG_WriteString (net_message->message, "You have been banned.\n");
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Write (acceptsock, net_message->message->data,
net_message->message->cursize, &clientaddr);
SZ_Clear (net_message->message);
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
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREP_ACCEPT);
dfunc.GetSocketAddr (s->socket, &newaddr);
MSG_WriteLong (net_message->message,
dfunc.GetSocketPort (&newaddr));
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Write (acceptsock, net_message->message->data,
net_message->message->cursize, &clientaddr);
SZ_Clear (net_message->message);
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
Sys_MaskPrintf (SYS_net, "closing stale socket %d %g\n", ret,
net_time - s->connecttime);
NET_Close (s);
return NULL;
}
}
// allocate a QSocket
sock = NET_NewQSocket ();
if (sock == NULL) {
// no room; try to let him know
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREP_REJECT);
MSG_WriteString (net_message->message, "Server is full.\n");
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Write (acceptsock, net_message->message->data,
net_message->message->cursize, &clientaddr);
SZ_Clear (net_message->message);
return NULL;
}
// allocate a network socket
newsock = dfunc.OpenSocket (0);
if (newsock == -1) {
Sys_MaskPrintf (SYS_net, "failed to open socket");
NET_FreeQSocket (sock);
return NULL;
}
// connect to the client
if (dfunc.Connect (newsock, &clientaddr) == -1) {
Sys_MaskPrintf (SYS_net, "failed to connect client");
dfunc.CloseSocket (newsock);
NET_FreeQSocket (sock);
return NULL;
}
// everything is allocated, just fill in the details
sock->socket = newsock;
sock->landriver = net_landriverlevel;
sock->addr = clientaddr;
strcpy (sock->address, dfunc.AddrToString (&clientaddr));
// send him back the info about the server connection he has been
// allocated
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREP_ACCEPT);
dfunc.GetSocketAddr (newsock, &newaddr);
MSG_WriteLong (net_message->message, dfunc.GetSocketPort (&newaddr));
// MSG_WriteString(net_message->message, dfunc.AddrToString(&newaddr));
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Write (acceptsock, net_message->message->data,
net_message->message->cursize, &clientaddr);
SZ_Clear (net_message->message);
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 (qboolean xmit)
{
int ret;
netadr_t readaddr;
netadr_t myaddr;
int control;
dfunc.GetSocketAddr (dfunc.controlSock, &myaddr);
if (xmit) {
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREQ_SERVER_INFO);
MSG_WriteString (net_message->message, "QUAKE");
MSG_WriteByte (net_message->message, NET_PROTOCOL_VERSION);
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Broadcast (dfunc.controlSock, net_message->message->data,
net_message->message->cursize);
SZ_Clear (net_message->message);
}
while ((ret = dfunc.Read (dfunc.controlSock, net_message->message->data,
net_message->message->maxsize, &readaddr)) > 0) {
if (ret < (int) sizeof (int)) {
continue;
}
net_message->message->cursize = ret;
// don't answer our own query
if (dfunc.AddrCompare (&readaddr, &myaddr) >= 0) {
continue;
}
MSG_BeginReading (net_message);
control = MSG_ReadLongBE (net_message);
if (control == -1) {
continue;
}
if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL) {
continue;
}
if ((control & NETFLAG_LENGTH_MASK) != ret) {
continue;
}
if (MSG_ReadByte (net_message) != CCREP_SERVER_INFO) {
continue;
}
const char *addrstr = MSG_ReadString (net_message);
dfunc.GetAddrFromName (addrstr, &readaddr);
const char *name = MSG_ReadString (net_message);
const char *map = MSG_ReadString (net_message);
int users = MSG_ReadByte (net_message);
int maxusers = MSG_ReadByte (net_message);
int protocol = MSG_ReadByte (net_message);
const char *cname = dfunc.AddrToString (&readaddr);
if (protocol != NET_PROTOCOL_VERSION) {
char *new_name = alloca (strlen (name) + 2);
new_name[0] = '*';
strcpy (new_name + 1, name);
name = new_name;
}
NET_AddCachedHost (name, map, cname, users, maxusers, net_driverlevel,
net_landriverlevel, &readaddr);
}
}
void
Datagram_SearchForHosts (qboolean xmit)
{
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers;
net_landriverlevel++) {
if (net_landrivers[net_landriverlevel].initialized) {
_Datagram_SearchForHosts (xmit);
}
}
}
static qsocket_t *
_Datagram_Connect (const char *host)
{
netadr_t sendaddr;
netadr_t readaddr;
qsocket_t *sock;
int newsock;
int ret;
int reps;
double start_time;
int control;
const char *reason;
// see if we can resolve the host name
if (dfunc.GetAddrFromName (host, &sendaddr) == -1) {
return NULL;
}
newsock = dfunc.OpenSocket (0);
if (newsock == -1) {
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
Sys_Printf ("trying...\n");
CL_UpdateScreen (cl.time);
start_time = net_time;
for (reps = 0; reps < 3; reps++) {
SZ_Clear (net_message->message);
// save space for the header, filled in later
MSG_WriteLong (net_message->message, 0);
MSG_WriteByte (net_message->message, CCREQ_CONNECT);
MSG_WriteString (net_message->message, "QUAKE");
MSG_WriteByte (net_message->message, NET_PROTOCOL_VERSION);
MSG_PokeLongBE (net_message->message, 0,
NETFLAG_CTL | net_message->message->cursize);
dfunc.Write (newsock, net_message->message->data,
net_message->message->cursize, &sendaddr);
SZ_Clear (net_message->message);
do {
ret =
dfunc.Read (newsock, net_message->message->data,
net_message->message->maxsize, &readaddr);
// if we got something, validate it
if (ret > 0) {
// is it from the right place?
if (sfunc.AddrCompare (&readaddr, &sendaddr) != 0) {
Sys_MaskPrintf (SYS_net, "%2d ",
sfunc.AddrCompare (&readaddr, &sendaddr));
Sys_MaskPrintf (SYS_net, "%d %s ", readaddr.family,
sfunc.AddrToString (&readaddr));
Sys_MaskPrintf (SYS_net, "%d %s\n", sendaddr.family,
sfunc.AddrToString (&sendaddr));
ret = 0;
continue;
}
if (ret < (int) sizeof (int)) {
ret = 0;
continue;
}
net_message->message->cursize = ret;
MSG_BeginReading (net_message);
control = MSG_ReadLongBE (net_message);
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);
if (ret) {
break;
}
Sys_Printf ("still trying...\n");
CL_UpdateScreen (cl.time);
start_time = SetNetTime ();
}
if (ret == 0) {
reason = "No Response";
Sys_Printf ("%s\n", reason);
// strcpy (m_return_reason, reason);
goto ErrorReturn;
}
if (ret == -1) {
reason = "Network Error";
Sys_Printf ("%s\n", reason);
// strcpy (m_return_reason, reason);
goto ErrorReturn;
}
ret = MSG_ReadByte (net_message);
if (ret == CCREP_REJECT) {
reason = MSG_ReadString (net_message);
Sys_Printf ("%s\n", reason);
// strncpy (m_return_reason, reason, 31);
goto ErrorReturn;
}
if (ret == CCREP_ACCEPT) {
memcpy (&sock->addr, &sendaddr, sizeof (netadr_t));
dfunc.SetSocketPort (&sock->addr, MSG_ReadLong (net_message));
} else {
reason = "Bad Response";
Sys_Printf ("%s\n", reason);
// strcpy (m_return_reason, reason);
goto ErrorReturn;
}
dfunc.GetNameFromAddr (&sendaddr, sock->address);
Sys_Printf ("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";
Sys_Printf ("%s\n", reason);
// strcpy (m_return_reason, reason);
goto ErrorReturn;
}
// FIXME: MENUCODE
// m_return_onerror = false;
return sock;
ErrorReturn:
// FIXME: MENUCODE - do something with reason
Sys_MaskPrintf (SYS_net, "FIXME: MENUCODE - do something with reason\n");
NET_FreeQSocket (sock);
ErrorReturn2:
dfunc.CloseSocket (newsock);
// FIXME: MENUCODE
// if (m_return_onerror) {
// 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;
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;
}