0,0 → 1,1390 |
/* |
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 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++) |
sprintf (buf + n * 2, "%02x", *p++); |
return buf; |
} |
#endif |
|
|
#ifdef BAN_TEST |
unsigned long banAddr = 0x00000000; |
unsigned long banMask = 0xffffffff; |
|
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; |
} |