summaryrefslogtreecommitdiff
path: root/apps/plugins/sdl/progs/quake/net_dgrm.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/sdl/progs/quake/net_dgrm.c')
-rw-r--r--apps/plugins/sdl/progs/quake/net_dgrm.c1390
1 files changed, 1390 insertions, 0 deletions
diff --git a/apps/plugins/sdl/progs/quake/net_dgrm.c b/apps/plugins/sdl/progs/quake/net_dgrm.c
new file mode 100644
index 0000000..a293b77
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_dgrm.c
@@ -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;
+}