mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-27 06:34:11 +00:00
0bd05c71ac
I don't know why they were ever signed (oversight at id and just propagated?). Anyway, this resulted in "unsigned" spreading a bit, but all to reasonable places.
1145 lines
28 KiB
C
1145 lines
28 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/keys.h"
|
|
#include "QF/qendian.h"
|
|
#include "QF/msg.h"
|
|
#include "QF/qargs.h"
|
|
#include "QF/screen.h"
|
|
|
|
#include "netmain.h"
|
|
|
|
#include "../nq/include/client.h"
|
|
#include "../nq/include/server.h"
|
|
#include "../nq/include/game.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 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->string);
|
|
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;
|
|
cvar_t *var;
|
|
|
|
// find the search start location
|
|
prevCvarName = MSG_ReadString (net_message);
|
|
if (*prevCvarName) {
|
|
var = Cvar_FindVar (prevCvarName);
|
|
if (!var)
|
|
return NULL;
|
|
var = var->next;
|
|
} else {
|
|
var = cvar_vars;
|
|
}
|
|
|
|
// search for the next server cvar
|
|
while (var) {
|
|
if (var->flags & CVAR_SERVERINFO) {
|
|
break;
|
|
}
|
|
var = var->next;
|
|
}
|
|
|
|
// 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, var->string);
|
|
}
|
|
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;
|
|
}
|