/* 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 #elif defined (NeXT) #include #include #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" #include "net_master.h" cvar_t cl_port = {CVAR_SAVE, "cl_port", "0"}; // 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_DATAGRAM]; } packetBuffer; /* #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]; void (*print) (const char *fmt, ...); if (cmd_source == src_command) { if (!sv.active) { Cmd_ForwardToServer (); return; } print = Con_Printf; } else { if (pr_global_struct->deathmatch) return; print = SV_ClientPrintf; } switch (Cmd_Argc ()) { case 1: if (((struct in_addr *)&banAddr)->s_addr) { strcpy(addrStr, inet_ntoa(*(struct in_addr *)&banAddr)); 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 (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 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++); 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_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++); 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_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); 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_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++); 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 qsockaddr readaddr; unsigned int sequence; unsigned int count; int temp; if (!sock->canSend) if ((net_time - sock->lastSendTime) > 1.0) ReSendMessage (sock); while(1) { length = sfunc.Read (sock->socket, (qbyte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); if (length == 0) break; if ((int)length == -1) { Con_Printf("Read error\n"); return -1; } if ((temp = sfunc.AddrCompare(&readaddr, &sock->addr)) != 0) { char tempaddress1[64], tempaddress2[64]; if (temp == 1) { if (developer_networking.integer) { dfunc.GetNameFromAddr (&sock->addr, tempaddress1); dfunc.GetNameFromAddr (&readaddr, tempaddress2); Con_Printf("Packet from wrong port received but accepted (Expected: %s Received: %s)\n", tempaddress1, tempaddress2); } } else { dfunc.GetNameFromAddr (&sock->addr, tempaddress1); dfunc.GetNameFromAddr (&readaddr, tempaddress2); Con_Printf("Forged packet received (Expected: %s Received: %s)\n", tempaddress1, tempaddress2); 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) { 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, (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; } 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 (strcmp(Cmd_Argv(1), "*") == 0) { for (s = net_activeSockets; s; s = s->next) PrintStats(s); } else { for (s = net_activeSockets; s; s = s->next) if (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; qbyte playerNumber; int c; net_landriverlevel = testDriver; while (1) { len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr); if (len < (int)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)) != (int)NETFLAG_CTL) break; if ((control & NETFLAG_LENGTH_MASK) != len) break; c = MSG_ReadByte(); if (c != CCREP_PLAYER_INFO) Sys_Error("Unexpected repsonse to Player Info request\n"); playerNumber = MSG_ReadByte(); strcpy(name, MSG_ReadString()); colors = MSG_ReadLong(); frags = MSG_ReadLong(); connectTime = MSG_ReadLong(); 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) { const char *host; int n, max = MAX_SCOREBOARD; struct qsockaddr sendaddr; if (testInProgress) return; host = Cmd_Argv (1); if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) if (strcasecmp (host, hostcache[n].name) == 0) { if (hostcache[n].driver != myDriverLevel) continue; net_landriverlevel = hostcache[n].ldriver; max = hostcache[n].maxusers; 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; int c; 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 < (int)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)) != (int)NETFLAG_CTL) goto Error; if ((control & NETFLAG_LENGTH_MASK) != len) goto Error; c = MSG_ReadByte(); if (c != CCREP_RULE_INFO) goto Error; strcpy(name, MSG_ReadString()); if (name[0] == 0) goto Done; 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) { const char *host; int n; struct qsockaddr sendaddr; if (test2InProgress) return; host = Cmd_Argv (1); if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) if (strcasecmp (host, hostcache[n].name) == 0) { if (hostcache[n].driver != myDriverLevel) continue; net_landriverlevel = hostcache[n].ldriver; 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); Cvar_RegisterVariable (&cl_port); 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; int c; 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 < (int)sizeof(int)) return NULL; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); // Messages starting by 0xFFFFFFFF are master server messages if ((unsigned int)control == 0xFFFFFFFF) { int responsesize = Master_HandleMessage(); if (responsesize > 0) { dfunc.Write(acceptsock, net_message.data, responsesize, &clientaddr); SZ_Clear(&net_message); } return NULL; } if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) return NULL; if ((control & NETFLAG_LENGTH_MASK) != len) return NULL; command = MSG_ReadByte(); if (command == CCREQ_SERVER_INFO) { if (strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; Con_DPrintf("Datagram_CheckNewConnections: received CCREQ_SERVERINFO, replying.\n"); 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(); var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY); // 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 (strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; c = MSG_ReadByte(); if (c != 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 request? 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)); // LordHavoc: send from s->socket instead of acceptsock, this // way routers usually identify the connection correctly // (thanks to faded for provoking me to recite a lengthy // explanation of NAT nightmares, and realize this easy // workaround for quake) dfunc.Write (s->socket, net_message.data, net_message.cursize, &clientaddr); // LordHavoc: also send from acceptsock, for good measure 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; 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)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); // LordHavoc: send from sock->socket instead of acceptsock, this way routers // usually identify the connection correctly (thanks to faded for provoking // me to recite a lengthy explanation of NAT nightmares, and realize this // easy workaround for quake) dfunc.Write (sock->socket, net_message.data, net_message.cursize, &clientaddr); // LordHavoc: also send from acceptsock, for good measure 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 qboolean Datagram_HandleServerInfo (struct qsockaddr *readaddr) { //struct qsockaddr myaddr; int control; int c, n; char cname[256]; if (net_message.cursize < (int)sizeof(int)) return false; // don't answer our own query //dfunc.GetSocketAddr (dfunc.controlSock, &myaddr); //if (dfunc.AddrCompare(readaddr, &myaddr) >= 0) // return false; // is the cache full? if (hostCacheCount == HOSTCACHESIZE) return false; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) return false; if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) return false; if ((control & NETFLAG_LENGTH_MASK) != net_message.cursize) return false; c = MSG_ReadByte(); if (c != CCREP_SERVER_INFO) return false; // LordHavoc: because the UDP driver reports 0.0.0.0:26000 as the address // string we just ignore it and keep the real address MSG_ReadString(); // hostcache only uses text addresses strcpy(cname, dfunc.AddrToString(readaddr)); // search the cache for this server for (n = 0; n < hostCacheCount; n++) //if (dfunc.AddrCompare(readaddr, &hostcache[n].addr) == 0) if (!strcmp(cname, hostcache[n].cname)) return false; // add it hostCacheCount++; strcpy(hostcache[n].name, MSG_ReadString()); strcpy(hostcache[n].map, MSG_ReadString()); hostcache[n].users = MSG_ReadByte(); hostcache[n].maxusers = MSG_ReadByte(); c = MSG_ReadByte(); if (c != NET_PROTOCOL_VERSION) { strncpy(hostcache[n].cname, hostcache[n].name, sizeof(hostcache[n].cname) - 1); hostcache[n].cname[sizeof(hostcache[n].cname) - 1] = 0; strcpy(hostcache[n].name, "*"); strncat(hostcache[n].name, hostcache[n].cname, sizeof(hostcache[n].name) - 1); hostcache[n].name[sizeof(hostcache[n].name) - 1] = 0; } strcpy(hostcache[n].cname, cname); //memcpy(&hostcache[n].addr, readaddr, sizeof(struct qsockaddr)); //hostcache[n].driver = net_driverlevel; //hostcache[n].ldriver = net_landriverlevel; /* // check for a name conflict for (i = 0; i < hostCacheCount; i++) { if (i == n) continue; if (strcasecmp (hostcache[n].name, hostcache[i].name) == 0) { i = 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; } } */ return true; } static void _Datagram_SearchForHosts (qboolean xmit) { int ret; struct qsockaddr readaddr; 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) { net_message.cursize = ret; Datagram_HandleServerInfo (&readaddr); } } 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 qboolean _Datagram_SearchForInetHosts (const char *master) { qboolean result = false; struct qsockaddr masteraddr; struct qsockaddr readaddr; int ret; if (master) { if (dfunc.GetAddrFromName(master, &masteraddr) != -1) { int portnum = 0; const char* port = strrchr (master, ':'); if (port) portnum = atoi (port + 1); if (!portnum) portnum = MASTER_PORT; Con_DPrintf("Datagram_SearchForInetHosts: sending %d byte message to master %s port %i\n", net_message.cursize, master, portnum); dfunc.SetSocketPort (&masteraddr, portnum); dfunc.Write (dfunc.controlSock, net_message.data, net_message.cursize, &masteraddr); } } while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0) { net_message.cursize = ret; Con_DPrintf("Datagram_SearchForInetHosts: Read received %d byte message\n", net_message.cursize); if (Datagram_HandleServerInfo (&readaddr)) result = true; else Master_ParseServerList (&dfunc); } return result; } qboolean Datagram_SearchForInetHosts (const char *master) { qboolean result = false; for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (hostCacheCount == HOSTCACHESIZE) break; if (net_landrivers[net_landriverlevel].initialized) if (_Datagram_SearchForInetHosts (master)) result = true; } return result; } static qsocket_t *_Datagram_Connect (const 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 (cl_port.integer); 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");CL_UpdateScreen();CL_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? // we don't care if the port matches (this adds support for // the NAT fix in the server inspired by faded) if (sfunc.AddrCompare(&readaddr, &sendaddr) < 0) { char tempaddress1[64], tempaddress2[64]; dfunc.GetNameFromAddr (&sendaddr, tempaddress1); dfunc.GetNameFromAddr (&readaddr, tempaddress2); Con_Printf("wrong reply address (Expected: %s Received: %s)\n", tempaddress1, tempaddress2); CL_UpdateScreen (); CL_UpdateScreen (); ret = 0; continue; } if (ret < (int)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)) != (int)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");CL_UpdateScreen();CL_UpdateScreen(); start_time = SetNetTime(); } if (ret == 0) { reason = "No Response"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } if (ret == -1) { reason = "Network Error"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } ret = MSG_ReadByte(); if (ret == CCREP_REJECT) { reason = MSG_ReadString(); Con_Printf("%s", reason); strncpy(m_return_reason, reason, 31); goto ErrorReturn; } if (ret == CCREP_ACCEPT) { memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); } else { reason = "Bad Response"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } dfunc.GetNameFromAddr (&sendaddr, sock->address); Con_Printf ("Connection accepted to %s\n", sock->address); 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); 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 (const 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; } static void _Datagram_Heartbeat (const char *master) { struct qsockaddr masteraddr; int portnum; const char* port; if (dfunc.GetAddrFromName(master, &masteraddr) == -1) return; portnum = 0; port = strrchr (master, ':'); if (port) portnum = atoi (port + 1); if (!portnum) portnum = MASTER_PORT; dfunc.SetSocketPort (&masteraddr, portnum); // FIXME: this is the only use of UDP_Send in the entire engine, add a dfunc.acceptSock to get rid of this function! dfunc.Send (net_message.data, net_message.cursize, &masteraddr); } void Datagram_Heartbeat (const char *master) { for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) if (net_landrivers[net_landriverlevel].initialized) _Datagram_Heartbeat (master); }