Created new subdir: uquake. Pick your favorite U word for the meaning --
Unchained, Ultimate, Ultra, Up Yours, Underworld, Underground, Unified,
Unity, etc. You know the drill.
This takes care of the "standalone" problem with the wrong name, and the
recent snafu with multiple developers working on the same files
simultaneously...expect me (and probably others) to start locking dirs when
updates are taking place.
And yes, this update is really as large as it looks. Software only at the
moment, but I will have the makefile updated to build the GL builds as
well.
1999-12-26 13:51:52 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
2000-01-08 04:50:26 +00:00
|
|
|
Portions Copyright (C) 1999,2000 Nelson Rush.
|
Created new subdir: uquake. Pick your favorite U word for the meaning --
Unchained, Ultimate, Ultra, Up Yours, Underworld, Underground, Unified,
Unity, etc. You know the drill.
This takes care of the "standalone" problem with the wrong name, and the
recent snafu with multiple developers working on the same files
simultaneously...expect me (and probably others) to start locking dirs when
updates are taking place.
And yes, this update is really as large as it looks. Software only at the
moment, but I will have the makefile updated to build the GL builds as
well.
1999-12-26 13:51:52 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
// net_dgrm.c
|
|
|
|
|
|
|
|
// This is enables a simple IP banning mechanism
|
|
|
|
#define BAN_TEST
|
|
|
|
|
|
|
|
#ifdef BAN_TEST
|
|
|
|
#if defined(_WIN32)
|
|
|
|
#include <windows.h>
|
|
|
|
#elif defined (NeXT)
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#else
|
|
|
|
#define AF_INET 2 /* internet */
|
|
|
|
struct in_addr
|
|
|
|
{
|
|
|
|
union
|
|
|
|
{
|
|
|
|
struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b;
|
|
|
|
struct { unsigned short s_w1,s_w2; } S_un_w;
|
|
|
|
unsigned long S_addr;
|
|
|
|
} S_un;
|
|
|
|
};
|
|
|
|
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
|
|
|
|
struct sockaddr_in
|
|
|
|
{
|
|
|
|
short sin_family;
|
|
|
|
unsigned short sin_port;
|
|
|
|
struct in_addr sin_addr;
|
|
|
|
char sin_zero[8];
|
|
|
|
};
|
|
|
|
char *inet_ntoa(struct in_addr in);
|
|
|
|
unsigned long inet_addr(const char *cp);
|
|
|
|
#endif
|
|
|
|
#endif // BAN_TEST
|
|
|
|
|
|
|
|
#include "quakedef.h"
|
|
|
|
#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;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
unsigned int length;
|
|
|
|
unsigned int sequence;
|
|
|
|
byte data[MAX_DATAGRAM];
|
|
|
|
} packetBuffer;
|
|
|
|
|
|
|
|
extern int m_return_state;
|
|
|
|
extern int m_state;
|
|
|
|
extern qboolean m_return_onerror;
|
|
|
|
extern char m_return_reason[32];
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
char *StrAddr (struct qsockaddr *addr)
|
|
|
|
{
|
|
|
|
static char buf[34];
|
|
|
|
byte *p = (byte *)addr;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
for (n = 0; n < 16; n++)
|
2000-01-05 00:05:48 +00:00
|
|
|
snprintf(buf + n * 2, sizeof(buf), "%02x", *p++);
|
Created new subdir: uquake. Pick your favorite U word for the meaning --
Unchained, Ultimate, Ultra, Up Yours, Underworld, Underground, Unified,
Unity, etc. You know the drill.
This takes care of the "standalone" problem with the wrong name, and the
recent snafu with multiple developers working on the same files
simultaneously...expect me (and probably others) to start locking dirs when
updates are taking place.
And yes, this update is really as large as it looks. Software only at the
moment, but I will have the makefile updated to build the GL builds as
well.
1999-12-26 13:51:52 +00:00
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef BAN_TEST
|
2000-01-18 20:15:35 +00:00
|
|
|
unsigned long banAddr = ~0x0;
|
|
|
|
unsigned long banMask = 0x0;
|
Created new subdir: uquake. Pick your favorite U word for the meaning --
Unchained, Ultimate, Ultra, Up Yours, Underworld, Underground, Unified,
Unity, etc. You know the drill.
This takes care of the "standalone" problem with the wrong name, and the
recent snafu with multiple developers working on the same files
simultaneously...expect me (and probably others) to start locking dirs when
updates are taking place.
And yes, this update is really as large as it looks. Software only at the
moment, but I will have the makefile updated to build the GL builds as
well.
1999-12-26 13:51:52 +00:00
|
|
|
|
|
|
|
void NET_Ban_f (void)
|
|
|
|
{
|
|
|
|
char addrStr [32];
|
|
|
|
char maskStr [32];
|
|
|
|
void (*print) (char *fmt, ...);
|
|
|
|
|
|
|
|
if (cmd_source == src_command)
|
|
|
|
{
|
|
|
|
if (!sv.active)
|
|
|
|
{
|
|
|
|
Cmd_ForwardToServer ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
print = Con_Printf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pr_global_struct->deathmatch && !host_client->privileged)
|
|
|
|
return;
|
|
|
|
print = SV_ClientPrintf;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (Cmd_Argc ())
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
if (((struct in_addr *)&banAddr)->s_addr)
|
|
|
|
{
|
|
|
|
Q_strcpy(addrStr, inet_ntoa(*(struct in_addr *)&banAddr));
|
|
|
|
Q_strcpy(maskStr, inet_ntoa(*(struct in_addr *)&banMask));
|
|
|
|
print("Banning %s [%s]\n", addrStr, maskStr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
print("Banning not active\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
if (Q_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 int packetLen;
|
|
|
|
unsigned int dataLen;
|
|
|
|
unsigned int eom;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (data->cursize == 0)
|
|
|
|
Sys_Error("Datagram_SendMessage: zero length message\n");
|
|
|
|
|
|
|
|
if (data->cursize > NET_MAXMESSAGE)
|
|
|
|
Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize);
|
|
|
|
|
|
|
|
if (sock->canSend == false)
|
|
|
|
Sys_Error("SendMessage: called with canSend == false\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Q_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;
|
|
|
|
|
|
|
|
packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
|
|
|
|
packetBuffer.sequence = BigLong(sock->sendSequence++);
|
|
|
|
Q_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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++);
|
|
|
|
Q_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
Q_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qboolean Datagram_CanSendMessage (qsocket_t *sock)
|
|
|
|
{
|
|
|
|
if (sock->sendNext)
|
|
|
|
SendMessageNext (sock);
|
|
|
|
|
|
|
|
return sock->canSend;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
|
|
|
|
{
|
|
|
|
int packetLen;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (data->cursize == 0)
|
|
|
|
Sys_Error("Datagram_SendUnreliableMessage: zero length message\n");
|
|
|
|
|
|
|
|
if (data->cursize > MAX_DATAGRAM)
|
|
|
|
Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
packetLen = NET_HEADERSIZE + data->cursize;
|
|
|
|
|
|
|
|
packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE);
|
|
|
|
packetBuffer.sequence = BigLong(sock->unreliableSendSequence++);
|
|
|
|
Q_memcpy (packetBuffer.data, data->data, data->cursize);
|
|
|
|
|
|
|
|
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 = sfunc.Read (sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr);
|
|
|
|
|
|
|
|
// if ((rand() & 255) > 220)
|
|
|
|
// continue;
|
|
|
|
|
|
|
|
if (length == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (length == -1)
|
|
|
|
{
|
|
|
|
Con_Printf("Read error\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
Con_DPrintf("Forged packet received\n");
|
|
|
|
Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr));
|
|
|
|
Con_DPrintf("Received: %s\n", StrAddr (&readaddr));
|
|
|
|
#endif
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
Con_DPrintf("Got a stale datagram\n");
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (sequence != sock->unreliableReceiveSequence)
|
|
|
|
{
|
|
|
|
count = sequence - sock->unreliableReceiveSequence;
|
|
|
|
droppedDatagrams += count;
|
|
|
|
Con_DPrintf("Dropped %u datagram(s)\n", count);
|
|
|
|
}
|
|
|
|
sock->unreliableReceiveSequence = sequence + 1;
|
|
|
|
|
|
|
|
length -= NET_HEADERSIZE;
|
|
|
|
|
|
|
|
SZ_Clear (&net_message);
|
|
|
|
SZ_Write (&net_message, packetBuffer.data, length);
|
|
|
|
|
|
|
|
ret = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & NETFLAG_ACK)
|
|
|
|
{
|
|
|
|
if (sequence != (sock->sendSequence - 1))
|
|
|
|
{
|
|
|
|
Con_DPrintf("Stale ACK received\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (sequence == sock->ackSequence)
|
|
|
|
{
|
|
|
|
sock->ackSequence++;
|
|
|
|
if (sock->ackSequence != sock->sendSequence)
|
|
|
|
Con_DPrintf("ack sequencing error\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Con_DPrintf("Duplicate ACK received\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sock->sendMessageLength -= MAX_DATAGRAM;
|
|
|
|
if (sock->sendMessageLength > 0)
|
|
|
|
{
|
|
|
|
Q_memcpy(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)
|
|
|
|
{
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength);
|
|
|
|
SZ_Write(&net_message, packetBuffer.data, length);
|
|
|
|
sock->receiveMessageLength = 0;
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length);
|
|
|
|
sock->receiveMessageLength += length;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sock->sendNext)
|
|
|
|
SendMessageNext (sock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrintStats(qsocket_t *s)
|
|
|
|
{
|
|
|
|
Con_Printf("canSend = %4u \n", s->canSend);
|
|
|
|
Con_Printf("sendSeq = %4u ", s->sendSequence);
|
|
|
|
Con_Printf("recvSeq = %4u \n", s->receiveSequence);
|
|
|
|
Con_Printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void NET_Stats_f (void)
|
|
|
|
{
|
|
|
|
qsocket_t *s;
|
|
|
|
|
|
|
|
if (Cmd_Argc () == 1)
|
|
|
|
{
|
|
|
|
Con_Printf("unreliable messages sent = %i\n", unreliableMessagesSent);
|
|
|
|
Con_Printf("unreliable messages recv = %i\n", unreliableMessagesReceived);
|
|
|
|
Con_Printf("reliable messages sent = %i\n", messagesSent);
|
|
|
|
Con_Printf("reliable messages received = %i\n", messagesReceived);
|
|
|
|
Con_Printf("packetsSent = %i\n", packetsSent);
|
|
|
|
Con_Printf("packetsReSent = %i\n", packetsReSent);
|
|
|
|
Con_Printf("packetsReceived = %i\n", packetsReceived);
|
|
|
|
Con_Printf("receivedDuplicateCount = %i\n", receivedDuplicateCount);
|
|
|
|
Con_Printf("shortPacketCount = %i\n", shortPacketCount);
|
|
|
|
Con_Printf("droppedDatagrams = %i\n", droppedDatagrams);
|
|
|
|
}
|
|
|
|
else if (Q_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 (Q_strcasecmp(Cmd_Argv(1), s->address) == 0)
|
|
|
|
break;
|
|
|
|
if (s == NULL)
|
|
|
|
for (s = net_freeSockets; s; s = s->next)
|
|
|
|
if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0)
|
|
|
|
break;
|
|
|
|
if (s == NULL)
|
|
|
|
return;
|
|
|
|
PrintStats(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static qboolean testInProgress = false;
|
|
|
|
static int testPollCount;
|
|
|
|
static int testDriver;
|
|
|
|
static int testSocket;
|
|
|
|
|
|
|
|
static void Test_Poll(void);
|
|
|
|
PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll};
|
|
|
|
|
|
|
|
static void Test_Poll(void)
|
|
|
|
{
|
|
|
|
struct qsockaddr clientaddr;
|
|
|
|
int control;
|
|
|
|
int len;
|
|
|
|
char name[32];
|
|
|
|
char address[64];
|
|
|
|
int colors;
|
|
|
|
int frags;
|
|
|
|
int connectTime;
|
|
|
|
byte playerNumber;
|
|
|
|
|
|
|
|
net_landriverlevel = testDriver;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr);
|
|
|
|
if (len < sizeof(int))
|
|
|
|
break;
|
|
|
|
|
|
|
|
net_message.cursize = len;
|
|
|
|
|
|
|
|
MSG_BeginReading ();
|
|
|
|
control = BigLong(*((int *)net_message.data));
|
|
|
|
MSG_ReadLong();
|
|
|
|
if (control == -1)
|
|
|
|
break;
|
|
|
|
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
|
|
|
|
break;
|
|
|
|
if ((control & NETFLAG_LENGTH_MASK) != len)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (MSG_ReadByte() != CCREP_PLAYER_INFO)
|
|
|
|
Sys_Error("Unexpected repsonse to Player Info request\n");
|
|
|
|
|
|
|
|
playerNumber = MSG_ReadByte();
|
|
|
|
Q_strcpy(name, MSG_ReadString());
|
|
|
|
colors = MSG_ReadLong();
|
|
|
|
frags = MSG_ReadLong();
|
|
|
|
connectTime = MSG_ReadLong();
|
|
|
|
Q_strcpy(address, MSG_ReadString());
|
|
|
|
|
|
|
|
Con_Printf("%s\n frags:%3i colors:%u %u time:%u\n %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address);
|
|
|
|
}
|
|
|
|
|
|
|
|
testPollCount--;
|
|
|
|
if (testPollCount)
|
|
|
|
{
|
|
|
|
SchedulePollProcedure(&testPollProcedure, 0.1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dfunc.CloseSocket(testSocket);
|
|
|
|
testInProgress = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Test_f (void)
|
|
|
|
{
|
|
|
|
char *host;
|
|
|
|
int n;
|
|
|
|
int max = MAX_SCOREBOARD;
|
|
|
|
struct qsockaddr sendaddr;
|
|
|
|
|
|
|
|
if (testInProgress)
|
|
|
|
return;
|
|
|
|
|
|
|
|
host = Cmd_Argv (1);
|
|
|
|
|
|
|
|
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;
|
|
|
|
max = hostcache[n].maxusers;
|
|
|
|
Q_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)
|
|
|
|
return;
|
|
|
|
|
|
|
|
JustDoIt:
|
|
|
|
testSocket = dfunc.OpenSocket(0);
|
|
|
|
if (testSocket == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
testInProgress = true;
|
|
|
|
testPollCount = 20;
|
|
|
|
testDriver = net_landriverlevel;
|
|
|
|
|
|
|
|
for (n = 0; n < max; n++)
|
|
|
|
{
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO);
|
|
|
|
MSG_WriteByte(&net_message, n);
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr);
|
|
|
|
}
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
SchedulePollProcedure(&testPollProcedure, 0.1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static qboolean test2InProgress = false;
|
|
|
|
static int test2Driver;
|
|
|
|
static int test2Socket;
|
|
|
|
|
|
|
|
static void Test2_Poll(void);
|
|
|
|
PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll};
|
|
|
|
|
|
|
|
static void Test2_Poll(void)
|
|
|
|
{
|
|
|
|
struct qsockaddr clientaddr;
|
|
|
|
int control;
|
|
|
|
int len;
|
|
|
|
char name[256];
|
|
|
|
char value[256];
|
|
|
|
|
|
|
|
net_landriverlevel = test2Driver;
|
|
|
|
name[0] = 0;
|
|
|
|
|
|
|
|
len = dfunc.Read (test2Socket, net_message.data, net_message.maxsize, &clientaddr);
|
|
|
|
if (len < sizeof(int))
|
|
|
|
goto Reschedule;
|
|
|
|
|
|
|
|
net_message.cursize = len;
|
|
|
|
|
|
|
|
MSG_BeginReading ();
|
|
|
|
control = BigLong(*((int *)net_message.data));
|
|
|
|
MSG_ReadLong();
|
|
|
|
if (control == -1)
|
|
|
|
goto Error;
|
|
|
|
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
|
|
|
|
goto Error;
|
|
|
|
if ((control & NETFLAG_LENGTH_MASK) != len)
|
|
|
|
goto Error;
|
|
|
|
|
|
|
|
if (MSG_ReadByte() != CCREP_RULE_INFO)
|
|
|
|
goto Error;
|
|
|
|
|
|
|
|
Q_strcpy(name, MSG_ReadString());
|
|
|
|
if (name[0] == 0)
|
|
|
|
goto Done;
|
|
|
|
Q_strcpy(value, MSG_ReadString());
|
|
|
|
|
|
|
|
Con_Printf("%-16.16s %-16.16s\n", name, value);
|
|
|
|
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
|
|
|
|
MSG_WriteString(&net_message, name);
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (test2Socket, net_message.data, net_message.cursize, &clientaddr);
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
|
|
|
|
Reschedule:
|
|
|
|
SchedulePollProcedure(&test2PollProcedure, 0.05);
|
|
|
|
return;
|
|
|
|
|
|
|
|
Error:
|
|
|
|
Con_Printf("Unexpected repsonse to Rule Info request\n");
|
|
|
|
Done:
|
|
|
|
dfunc.CloseSocket(test2Socket);
|
|
|
|
test2InProgress = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Test2_f (void)
|
|
|
|
{
|
|
|
|
char *host;
|
|
|
|
int n;
|
|
|
|
struct qsockaddr sendaddr;
|
|
|
|
|
|
|
|
if (test2InProgress)
|
|
|
|
return;
|
|
|
|
|
|
|
|
host = Cmd_Argv (1);
|
|
|
|
|
|
|
|
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;
|
|
|
|
Q_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)
|
|
|
|
return;
|
|
|
|
|
|
|
|
JustDoIt:
|
|
|
|
test2Socket = dfunc.OpenSocket(0);
|
|
|
|
if (test2Socket == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
test2InProgress = true;
|
|
|
|
test2Driver = net_landriverlevel;
|
|
|
|
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
|
|
|
|
MSG_WriteString(&net_message, "");
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (test2Socket, net_message.data, net_message.cursize, &sendaddr);
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
SchedulePollProcedure(&test2PollProcedure, 0.05);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Datagram_Init (void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int csock;
|
|
|
|
|
|
|
|
myDriverLevel = net_driverlevel;
|
|
|
|
Cmd_AddCommand ("net_stats", NET_Stats_f);
|
|
|
|
|
|
|
|
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);
|
|
|
|
#endif
|
|
|
|
Cmd_AddCommand ("test", Test_f);
|
|
|
|
Cmd_AddCommand ("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.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)
|
|
|
|
{
|
|
|
|
struct qsockaddr clientaddr;
|
|
|
|
struct qsockaddr 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);
|
|
|
|
|
|
|
|
len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr);
|
|
|
|
if (len < sizeof(int))
|
|
|
|
return NULL;
|
|
|
|
net_message.cursize = len;
|
|
|
|
|
|
|
|
MSG_BeginReading ();
|
|
|
|
control = BigLong(*((int *)net_message.data));
|
|
|
|
MSG_ReadLong();
|
|
|
|
if (control == -1)
|
|
|
|
return NULL;
|
|
|
|
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
|
|
|
|
return NULL;
|
|
|
|
if ((control & NETFLAG_LENGTH_MASK) != len)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
command = MSG_ReadByte();
|
|
|
|
if (command == CCREQ_SERVER_INFO)
|
|
|
|
{
|
|
|
|
if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
|
|
|
|
dfunc.GetSocketAddr(acceptsock, &newaddr);
|
|
|
|
MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
|
|
|
|
MSG_WriteString(&net_message, hostname.string);
|
|
|
|
MSG_WriteString(&net_message, sv.name);
|
|
|
|
MSG_WriteByte(&net_message, net_activeconnections);
|
|
|
|
MSG_WriteByte(&net_message, svs.maxclients);
|
|
|
|
MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (command == CCREQ_PLAYER_INFO)
|
|
|
|
{
|
|
|
|
int playerNumber;
|
|
|
|
int activeNumber;
|
|
|
|
int clientNumber;
|
|
|
|
client_t *client;
|
|
|
|
|
|
|
|
playerNumber = MSG_ReadByte();
|
|
|
|
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);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
|
|
|
|
MSG_WriteByte(&net_message, playerNumber);
|
|
|
|
MSG_WriteString(&net_message, client->name);
|
|
|
|
MSG_WriteLong(&net_message, client->colors);
|
|
|
|
MSG_WriteLong(&net_message, (int)client->edict->v.frags);
|
|
|
|
MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime));
|
|
|
|
MSG_WriteString(&net_message, client->netconnection->address);
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (command == CCREQ_RULE_INFO)
|
|
|
|
{
|
|
|
|
char *prevCvarName;
|
|
|
|
cvar_t *var;
|
|
|
|
|
|
|
|
// find the search start location
|
|
|
|
prevCvarName = MSG_ReadString();
|
|
|
|
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->server)
|
|
|
|
break;
|
|
|
|
var = var->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// send the response
|
|
|
|
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREP_RULE_INFO);
|
|
|
|
if (var)
|
|
|
|
{
|
|
|
|
MSG_WriteString(&net_message, var->name);
|
|
|
|
MSG_WriteString(&net_message, var->string);
|
|
|
|
}
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (command != CCREQ_CONNECT)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
|
|
|
|
{
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREP_REJECT);
|
|
|
|
MSG_WriteString(&net_message, "Incompatible version.\n");
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef BAN_TEST
|
|
|
|
// check for a ban
|
|
|
|
if (clientaddr.sa_family == AF_INET)
|
|
|
|
{
|
|
|
|
unsigned long testAddr;
|
|
|
|
testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr;
|
|
|
|
if ((testAddr & banMask) == banAddr)
|
|
|
|
{
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREP_REJECT);
|
|
|
|
MSG_WriteString(&net_message, "You have been banned.\n");
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
|
|
|
|
SZ_Clear(&net_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);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREP_ACCEPT);
|
|
|
|
dfunc.GetSocketAddr(s->socket, &newaddr);
|
|
|
|
MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
|
|
|
|
SZ_Clear(&net_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
|
|
|
|
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);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREP_REJECT);
|
|
|
|
MSG_WriteString(&net_message, "Server is full.\n");
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// allocate a network socket
|
|
|
|
newsock = dfunc.OpenSocket(0);
|
|
|
|
if (newsock == -1)
|
|
|
|
{
|
|
|
|
NET_FreeQSocket(sock);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// connect to the client
|
|
|
|
if (dfunc.Connect (newsock, &clientaddr) == -1)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
Q_strcpy(sock->address, dfunc.AddrToString(&clientaddr));
|
|
|
|
|
|
|
|
// send him back the info about the server connection he has been allocated
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREP_ACCEPT);
|
|
|
|
dfunc.GetSocketAddr(newsock, &newaddr);
|
|
|
|
MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
|
|
|
|
// MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
|
|
|
|
SZ_Clear(&net_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;
|
|
|
|
int n;
|
|
|
|
int i;
|
|
|
|
struct qsockaddr readaddr;
|
|
|
|
struct qsockaddr myaddr;
|
|
|
|
int control;
|
|
|
|
|
|
|
|
dfunc.GetSocketAddr (dfunc.controlSock, &myaddr);
|
|
|
|
if (xmit)
|
|
|
|
{
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
|
|
|
|
MSG_WriteString(&net_message, "QUAKE");
|
|
|
|
MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize);
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0)
|
|
|
|
{
|
|
|
|
if (ret < sizeof(int))
|
|
|
|
continue;
|
|
|
|
net_message.cursize = ret;
|
|
|
|
|
|
|
|
// don't answer our own query
|
|
|
|
if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// is the cache full?
|
|
|
|
if (hostCacheCount == HOSTCACHESIZE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
MSG_BeginReading ();
|
|
|
|
control = BigLong(*((int *)net_message.data));
|
|
|
|
MSG_ReadLong();
|
|
|
|
if (control == -1)
|
|
|
|
continue;
|
|
|
|
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
|
|
|
|
continue;
|
|
|
|
if ((control & NETFLAG_LENGTH_MASK) != ret)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (MSG_ReadByte() != CCREP_SERVER_INFO)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dfunc.GetAddrFromName(MSG_ReadString(), &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++;
|
|
|
|
Q_strcpy(hostcache[n].name, MSG_ReadString());
|
|
|
|
Q_strcpy(hostcache[n].map, MSG_ReadString());
|
|
|
|
hostcache[n].users = MSG_ReadByte();
|
|
|
|
hostcache[n].maxusers = MSG_ReadByte();
|
|
|
|
if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
|
|
|
|
{
|
|
|
|
Q_strcpy(hostcache[n].cname, hostcache[n].name);
|
|
|
|
hostcache[n].cname[14] = 0;
|
|
|
|
Q_strcpy(hostcache[n].name, "*");
|
|
|
|
Q_strcat(hostcache[n].name, hostcache[n].cname);
|
|
|
|
}
|
|
|
|
Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr));
|
|
|
|
hostcache[n].driver = net_driverlevel;
|
|
|
|
hostcache[n].ldriver = net_landriverlevel;
|
|
|
|
Q_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 = Q_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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Datagram_SearchForHosts (qboolean 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 (char *host)
|
|
|
|
{
|
|
|
|
struct qsockaddr sendaddr;
|
|
|
|
struct qsockaddr readaddr;
|
|
|
|
qsocket_t *sock;
|
|
|
|
int newsock;
|
|
|
|
int ret;
|
|
|
|
int reps;
|
|
|
|
double start_time;
|
|
|
|
int control;
|
|
|
|
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
|
|
|
|
Con_Printf("trying...\n"); SCR_UpdateScreen ();
|
|
|
|
start_time = net_time;
|
|
|
|
|
|
|
|
for (reps = 0; reps < 3; reps++)
|
|
|
|
{
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
// save space for the header, filled in later
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
|
|
MSG_WriteByte(&net_message, CCREQ_CONNECT);
|
|
|
|
MSG_WriteString(&net_message, "QUAKE");
|
|
|
|
MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
|
|
|
|
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
|
|
|
|
dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr);
|
|
|
|
SZ_Clear(&net_message);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr);
|
|
|
|
// if we got something, validate it
|
|
|
|
if (ret > 0)
|
|
|
|
{
|
|
|
|
// is it from the right place?
|
|
|
|
if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
Con_Printf("wrong reply address\n");
|
|
|
|
Con_Printf("Expected: %s\n", StrAddr (&sendaddr));
|
|
|
|
Con_Printf("Received: %s\n", StrAddr (&readaddr));
|
|
|
|
SCR_UpdateScreen ();
|
|
|
|
#endif
|
|
|
|
ret = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < sizeof(int))
|
|
|
|
{
|
|
|
|
ret = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_message.cursize = ret;
|
|
|
|
MSG_BeginReading ();
|
|
|
|
|
|
|
|
control = BigLong(*((int *)net_message.data));
|
|
|
|
MSG_ReadLong();
|
|
|
|
if (control == -1)
|
|
|
|
{
|
|
|
|
ret = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((control & (~NETFLAG_LENGTH_MASK)) != 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;
|
|
|
|
Con_Printf("still trying...\n"); SCR_UpdateScreen ();
|
|
|
|
start_time = SetNetTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
reason = "No Response";
|
|
|
|
Con_Printf("%s\n", reason);
|
|
|
|
Q_strcpy(m_return_reason, reason);
|
|
|
|
goto ErrorReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == -1)
|
|
|
|
{
|
|
|
|
reason = "Network Error";
|
|
|
|
Con_Printf("%s\n", reason);
|
|
|
|
Q_strcpy(m_return_reason, reason);
|
|
|
|
goto ErrorReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = MSG_ReadByte();
|
|
|
|
if (ret == CCREP_REJECT)
|
|
|
|
{
|
|
|
|
reason = MSG_ReadString();
|
|
|
|
Con_Printf(reason);
|
|
|
|
Q_strncpy(m_return_reason, reason, 31);
|
|
|
|
goto ErrorReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == CCREP_ACCEPT)
|
|
|
|
{
|
|
|
|
Q_memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr));
|
|
|
|
dfunc.SetSocketPort (&sock->addr, MSG_ReadLong());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reason = "Bad Response";
|
|
|
|
Con_Printf("%s\n", reason);
|
|
|
|
Q_strcpy(m_return_reason, reason);
|
|
|
|
goto ErrorReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
dfunc.GetNameFromAddr (&sendaddr, sock->address);
|
|
|
|
|
|
|
|
Con_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";
|
|
|
|
Con_Printf("%s\n", reason);
|
|
|
|
Q_strcpy(m_return_reason, reason);
|
|
|
|
goto ErrorReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_return_onerror = false;
|
|
|
|
return sock;
|
|
|
|
|
|
|
|
ErrorReturn:
|
|
|
|
NET_FreeQSocket(sock);
|
|
|
|
ErrorReturn2:
|
|
|
|
dfunc.CloseSocket(newsock);
|
|
|
|
if (m_return_onerror)
|
|
|
|
{
|
|
|
|
key_dest = key_menu;
|
|
|
|
m_state = m_return_state;
|
|
|
|
m_return_onerror = false;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsocket_t *Datagram_Connect (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;
|
|
|
|
}
|