mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-27 06:02:16 +00:00
7ee6e8131b
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@21 fc73d0e0-1445-4013-8a0c-d673dee63da5
1668 lines
40 KiB
C
1668 lines
40 KiB
C
/*
|
|
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
|
|
|
|
#include "../client/quakedef.h"
|
|
|
|
|
|
#ifdef NQPROT
|
|
|
|
#define NET_GAMENAME_NQ "QUAKE"
|
|
#define NET_GAMENAME_QW "QUAKEWORLD"
|
|
|
|
// 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 "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;
|
|
qbyte data[MAX_OVERALLDATAGRAM];
|
|
} packetBuffer;
|
|
|
|
#ifndef SERVERONLY
|
|
extern int m_return_state;
|
|
extern qboolean m_return_onerror;
|
|
extern char m_return_reason[32];
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
char *StrAddr (struct qsockaddr *addr)
|
|
{
|
|
static char buf[34];
|
|
qbyte *p = (qbyte *)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];
|
|
|
|
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));
|
|
Con_Printf("Banning %s [%s]\n", addrStr, maskStr);
|
|
}
|
|
else
|
|
Con_Printf("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:
|
|
Con_Printf("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_NQDATAGRAM)
|
|
{
|
|
dataLen = data->cursize;
|
|
eom = NETFLAG_EOM;
|
|
}
|
|
else
|
|
{
|
|
dataLen = MAX_NQDATAGRAM;
|
|
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, (qbyte *)&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_NQDATAGRAM)
|
|
{
|
|
dataLen = sock->sendMessageLength;
|
|
eom = NETFLAG_EOM;
|
|
}
|
|
else
|
|
{
|
|
dataLen = MAX_NQDATAGRAM;
|
|
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, (qbyte *)&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_NQDATAGRAM)
|
|
{
|
|
dataLen = sock->sendMessageLength;
|
|
eom = NETFLAG_EOM;
|
|
}
|
|
else
|
|
{
|
|
dataLen = MAX_NQDATAGRAM;
|
|
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, (qbyte *)&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_NQDATAGRAM)
|
|
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, (qbyte *)&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 sockaddr_qstorage readaddr;
|
|
unsigned int sequence;
|
|
unsigned int count;
|
|
|
|
if (!sock->canSend)
|
|
if ((net_time - sock->lastSendTime) > 1.0)
|
|
ReSendMessage (sock);
|
|
|
|
for(;;)
|
|
{
|
|
length = sfunc.Read (sock->socket, (qbyte *)&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_NQDATAGRAM;
|
|
if (sock->sendMessageLength > 0)
|
|
{
|
|
Q_memcpy(sock->sendMessage, sock->sendMessage+MAX_NQDATAGRAM, 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, (qbyte *)&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 *arg);
|
|
PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll};
|
|
|
|
static void Test_Poll(void *arg)
|
|
{
|
|
struct qsockaddr clientaddr;
|
|
int control;
|
|
int len;
|
|
char name[32];
|
|
char address[64];
|
|
int colors;
|
|
int frags;
|
|
int connectTime;
|
|
qbyte 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 *arg);
|
|
PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll};
|
|
|
|
static void Test2_Poll(void *arg)
|
|
{
|
|
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)
|
|
{
|
|
#ifndef CLIENTONLY
|
|
int i;
|
|
|
|
for (i = 0; i < net_numlandrivers; i++)
|
|
if (net_landrivers[i].initialized)
|
|
net_landrivers[i].Listen (state);
|
|
#endif
|
|
}
|
|
|
|
#ifndef CLIENTONLY
|
|
static qsocket_t *_Datagram_CheckNewConnections (void)
|
|
{
|
|
struct sockaddr_qstorage clientaddr;
|
|
struct sockaddr_qstorage newaddr;
|
|
int newsock;
|
|
int acceptsock;
|
|
qsocket_t *sock;
|
|
qsocket_t *s;
|
|
int len;
|
|
int command;
|
|
int control;
|
|
int ret;
|
|
#ifdef NET_GAMENAME_QW
|
|
char *gname;
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
int numcl;
|
|
|
|
#ifdef NET_GAMENAME_QW
|
|
gname = MSG_ReadString();
|
|
if (Q_strcmp(gname, NET_GAMENAME_QW))
|
|
if (Q_strcmp(gname, NET_GAMENAME_NQ) )
|
|
return NULL;
|
|
#else
|
|
if (Q_strcmp(MSG_ReadString(), NET_GAMENAME_NQ) != 0)
|
|
return NULL;
|
|
#endif
|
|
|
|
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);
|
|
numcl = 0;
|
|
for (len = 0; len < MAX_CLIENTS; len++)
|
|
{
|
|
if(svs.clients[len].state>cs_zombie || *svs.clients[len].name) //client or bot.
|
|
numcl++;
|
|
}
|
|
MSG_WriteByte(&net_message, numcl);
|
|
MSG_WriteByte(&net_message, sv.allocated_client_slots);
|
|
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 < sv.allocated_client_slots; clientNumber++, client++)
|
|
{
|
|
if (client->state>cs_zombie || *svs.clients[len].name)
|
|
{
|
|
activeNumber++;
|
|
if (activeNumber == playerNumber)
|
|
break;
|
|
}
|
|
}
|
|
if (clientNumber == sv.allocated_client_slots)
|
|
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, 0);
|
|
MSG_WriteLong(&net_message, (int)client->old_frags);
|
|
MSG_WriteLong(&net_message, (int)(net_time - client->connection_started));
|
|
MSG_WriteString(&net_message, "WITHHELD");
|
|
*((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)
|
|
{
|
|
extern cvar_group_t cvargroup_serverinfo;
|
|
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 = cvargroup_serverinfo.cvars;
|
|
|
|
// search for the next server cvar
|
|
while (var)
|
|
{
|
|
if (var->flags&CVAR_SERVERINFO)
|
|
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;
|
|
|
|
#ifdef NET_GAMENAME_QW
|
|
gname = MSG_ReadString();
|
|
if (Q_strcmp(gname, NET_GAMENAME_QW))
|
|
if (Q_strcmp(gname, NET_GAMENAME_NQ) )
|
|
return NULL;
|
|
#else
|
|
if (Q_strcmp(MSG_ReadString(), NET_GAMENAME_NQ) != 0)
|
|
return NULL;
|
|
#endif
|
|
|
|
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));
|
|
MSG_WriteString(&net_message, gname);
|
|
*((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;
|
|
}
|
|
|
|
if (!Q_strcmp(gname, NET_GAMENAME_QW))
|
|
sock->qwprotocol = true;
|
|
|
|
// 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, gname);
|
|
*((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;
|
|
}
|
|
#else
|
|
qsocket_t *Datagram_CheckNewConnections (void)
|
|
{
|
|
return NULL; //client only can't.
|
|
}
|
|
#endif
|
|
|
|
static void _Datagram_SearchForHosts (qboolean xmit)
|
|
{
|
|
int ret;
|
|
int n;
|
|
int i;
|
|
struct sockaddr_qstorage readaddr;
|
|
struct sockaddr_qstorage 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, NET_GAMENAME_QW);
|
|
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);
|
|
|
|
// save space for the header, filled in later
|
|
MSG_WriteLong(&net_message, 0);
|
|
MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
|
|
MSG_WriteString(&net_message, NET_GAMENAME_NQ); //look for either sort of server
|
|
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_strncpyz(hostcache[n].name, MSG_ReadString(), sizeof(hostcache[n].name));
|
|
Q_strncpyz(hostcache[n].map, MSG_ReadString(), sizeof(hostcache[n].map));
|
|
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 sockaddr_qstorage));
|
|
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);
|
|
}
|
|
}
|
|
|
|
#ifndef SERVERONLY
|
|
static qsocket_t *_Datagram_Connect (char *host)
|
|
{
|
|
struct sockaddr_qstorage sendaddr;
|
|
struct sockaddr_qstorage readaddr;
|
|
qsocket_t *sock;
|
|
int newsock;
|
|
int ret=0;
|
|
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, NET_GAMENAME_QW);
|
|
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);
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
MSG_WriteByte(&net_message, CCREQ_CONNECT);
|
|
MSG_WriteString(&net_message, NET_GAMENAME_NQ); //eitehr will do
|
|
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);
|
|
|
|
/* {
|
|
int s = sock->landriver, d = net_landriverlevel;
|
|
SVNQ_CheckForNewClients();
|
|
net_landriverlevel = d;
|
|
sock->landriver = s;
|
|
}
|
|
*/
|
|
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_strncpyz(m_return_reason, reason, sizeof(m_return_reason));
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (ret == CCREP_ACCEPT)
|
|
{
|
|
Q_memcpy(&sock->addr, &sendaddr, sizeof(struct sockaddr_qstorage));
|
|
dfunc.SetSocketPort (&sock->addr, MSG_ReadLong());
|
|
if (!Q_strcmp(NET_GAMENAME_QW, MSG_ReadString()))
|
|
sock->qwprotocol = true;
|
|
}
|
|
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;
|
|
}
|
|
static int newsock;
|
|
static qsocket_t *sock;
|
|
static qsocket_t *_Datagram_FailConnect (char *host)
|
|
{
|
|
if (sock)
|
|
NET_Close(sock);
|
|
// NET_FreeQSocket(sock);
|
|
|
|
if (!newsock)
|
|
return NULL;
|
|
|
|
dfunc.CloseSocket(newsock);
|
|
if (m_return_onerror)
|
|
{
|
|
key_dest = key_menu;
|
|
m_state = m_return_state;
|
|
m_return_onerror = false;
|
|
}
|
|
|
|
sock = NULL;
|
|
|
|
return NULL;
|
|
}
|
|
static qsocket_t *_Datagram_ContinueConnect (char *host)
|
|
{
|
|
struct sockaddr_qstorage sendaddr;
|
|
struct sockaddr_qstorage readaddr;
|
|
int ret;
|
|
// int reps;
|
|
int control;
|
|
char *reason;
|
|
|
|
// see if we can resolve the host name
|
|
if (!sock || dfunc.GetAddrFromName(host, &sendaddr) == -1)
|
|
return NULL;
|
|
|
|
|
|
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;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
while (ret > 0);
|
|
|
|
|
|
if (ret == 0)
|
|
{
|
|
reason = "No Response";
|
|
// Con_Printf("%s\n", reason);
|
|
Q_strcpy(m_return_reason, reason);
|
|
return NULL;
|
|
}
|
|
|
|
if (ret == -1)
|
|
{
|
|
reason = "Network Error";
|
|
// Con_Printf("%s\n", reason);
|
|
Q_strcpy(m_return_reason, reason);
|
|
return _Datagram_FailConnect(host);
|
|
}
|
|
|
|
ret = MSG_ReadByte();
|
|
if (ret == CCREP_REJECT)
|
|
{
|
|
reason = MSG_ReadString();
|
|
Con_Printf(reason);
|
|
Q_strncpyz(m_return_reason, reason, sizeof(m_return_reason));
|
|
return _Datagram_FailConnect(host);
|
|
}
|
|
|
|
if (ret == CCREP_ACCEPT)
|
|
{
|
|
Q_memcpy(&sock->addr, &sendaddr, sizeof(struct sockaddr_qstorage));
|
|
dfunc.SetSocketPort (&sock->addr, MSG_ReadLong());
|
|
if (!Q_strcmp(NET_GAMENAME_QW, MSG_ReadString()))
|
|
sock->qwprotocol = true;
|
|
}
|
|
else
|
|
{
|
|
reason = "Bad Response";
|
|
Con_Printf("%s\n", reason);
|
|
Q_strcpy(m_return_reason, reason);
|
|
return _Datagram_FailConnect(host);
|
|
}
|
|
|
|
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);
|
|
return _Datagram_FailConnect(host);
|
|
}
|
|
|
|
m_return_onerror = false;
|
|
return sock;
|
|
}
|
|
static qsocket_t *_Datagram_BeginConnect (char *host)
|
|
{
|
|
struct sockaddr_qstorage sendaddr;
|
|
double start_time;
|
|
|
|
// see if we can resolve the host name
|
|
if (dfunc.GetAddrFromName(host, &sendaddr) == -1)
|
|
return NULL;
|
|
|
|
sock = _Datagram_ContinueConnect(host);
|
|
if (sock)
|
|
return sock;
|
|
|
|
newsock = dfunc.OpenSocket (0);
|
|
if (newsock == -1)
|
|
return _Datagram_FailConnect(host);
|
|
|
|
sock = NET_NewQSocket ();
|
|
if (sock == NULL)
|
|
return _Datagram_FailConnect(host);
|
|
sock->socket = newsock;
|
|
sock->landriver = net_landriverlevel;
|
|
|
|
// connect to the host
|
|
if (dfunc.Connect (newsock, &sendaddr) == -1)
|
|
{
|
|
return _Datagram_FailConnect(host);
|
|
}
|
|
|
|
// send the connection request
|
|
Con_Printf("trying...\n");
|
|
start_time = net_time;
|
|
|
|
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, NET_GAMENAME_QW);
|
|
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);
|
|
|
|
MSG_WriteLong(&net_message, 0);
|
|
MSG_WriteByte(&net_message, CCREQ_CONNECT);
|
|
MSG_WriteString(&net_message, NET_GAMENAME_NQ); //either will do
|
|
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);
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
qsocket_t *Datagram_BeginConnect (char *host)
|
|
{
|
|
qsocket_t *ret = NULL;
|
|
#ifndef SERVERONLY
|
|
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
|
|
if (net_landrivers[net_landriverlevel].initialized)
|
|
if ((ret = _Datagram_BeginConnect (host)) != NULL)
|
|
break;
|
|
#endif
|
|
return ret;
|
|
}
|
|
qsocket_t *Datagram_ContinueConnect (char *host)
|
|
{
|
|
qsocket_t *ret = NULL;
|
|
#ifndef SERVERONLY
|
|
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
|
|
if (net_landrivers[net_landriverlevel].initialized)
|
|
if ((ret = _Datagram_ContinueConnect (host)) != NULL)
|
|
break;
|
|
#endif
|
|
return ret;
|
|
}
|
|
qsocket_t *Datagram_Connect (char *host)
|
|
{
|
|
qsocket_t *ret = NULL;
|
|
#ifndef SERVERONLY
|
|
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
|
|
if (net_landrivers[net_landriverlevel].initialized)
|
|
if ((ret = _Datagram_Connect (host)) != NULL)
|
|
break;
|
|
#endif
|
|
return ret;
|
|
}
|
|
#endif
|
|
|