| /* |
| 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 (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; |
| } |