/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers 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. */ // This is enables a simple IP banning mechanism #define BAN_TEST #include "q_stdinc.h" #include "net_sys.h" #include "q_defs.h" #include "net_defs.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 int32_t net_landriverlevel; /* statistic counters */ static int32_t packetsSent = 0; static int32_t packetsReSent = 0; static int32_t packetsReceived = 0; static int32_t receivedDuplicateCount = 0; static int32_t shortPacketCount = 0; static int32_t droppedDatagrams; static struct { uint32_t length; uint32_t sequence; byte data[MAX_DATAGRAM]; } packetBuffer; static int32_t myDriverLevel; extern bool m_return_onerror; extern char m_return_reason[32]; static char *StrAddr(struct qsockaddr *addr) { static char buf[34]; byte *p = (byte *)addr; int32_t n; for(n = 0; n < 16; n++) sprintf(buf + n * 2, "%02x", *p++); return buf; } #if defined(BAN_TEST) static struct in_addr banAddr; static struct in_addr banMask; static void NET_Ban_f(void) { char addrStr [32]; char maskStr [32]; void (*print_fn)(const char *fmt, ...) FUNCP_PRINTF(1, 2); if(cmd_source == src_command) { if(!sv.active) { Cmd_ForwardToServer(); return; } print_fn = Con_Printf; } else { if(G_Float(GBL_deathmatch)) return; print_fn = SV_ClientPrintf; } switch(Cmd_Argc()) { case 1: if(banAddr.s_addr != INADDR_ANY) { strcpy(addrStr, inet_ntoa(banAddr)); strcpy(maskStr, inet_ntoa(banMask)); print_fn("Banning %s [%s]\n", addrStr, maskStr); } else print_fn("Banning not active\n"); break; case 2: if(q_strcasecmp(Cmd_Argv(1), "off") == 0) banAddr.s_addr = INADDR_ANY; else banAddr.s_addr = inet_addr(Cmd_Argv(1)); banMask.s_addr = INADDR_NONE; break; case 3: banAddr.s_addr = inet_addr(Cmd_Argv(1)); banMask.s_addr = inet_addr(Cmd_Argv(2)); break; default: print_fn("BAN ip_address [mask]\n"); break; } } #endif // BAN_TEST int32_t Datagram_SendMessage(qsocket_t *sock, sizebuf_t *data) { uint32_t packetLen; uint32_t dataLen; uint32_t eom; #if defined(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 %" PRIu32 "\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, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsSent++; return 1; } static int32_t SendMessageNext(qsocket_t *sock) { uint32_t packetLen; uint32_t dataLen; uint32_t 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, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsSent++; return 1; } static int32_t ReSendMessage(qsocket_t *sock) { uint32_t packetLen; uint32_t dataLen; uint32_t 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, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsReSent++; return 1; } bool Datagram_CanSendMessage(qsocket_t *sock) { if(sock->sendNext) SendMessageNext(sock); return sock->canSend; } bool Datagram_CanSendUnreliableMessage(qsocket_t *sock) { (void)sock; return true; } int32_t Datagram_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data) { int32_t packetLen; #if defined(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 %" PRIu32 "\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, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; packetsSent++; return 1; } int32_t Datagram_GetMessage(qsocket_t *sock) { uint32_t length; uint32_t flags; int32_t ret = 0; struct qsockaddr readaddr; uint32_t sequence; uint32_t count; if(!sock->canSend) if((net_time - sock->lastSendTime) > 1.0) ReSendMessage(sock); while(1) { length = (uint32_t) sfunc.Read(sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); // if ((rand() & 255) > 220) // continue; if(length == 0) break; if(length == (uint32_t) -1) { Con_Printf("Read error\n"); return -1; } if(sfunc.AddrCompare(&readaddr, &sock->addr) != 0) { Con_Printf("Forged packet received\n"); Con_Printf("Expected: %s\n", StrAddr(&sock->addr)); Con_Printf("Received: %s\n", StrAddr(&readaddr)); 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 %" PRIu32 " 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) { memmove(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; } memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length); sock->receiveMessageLength += length; continue; } } if(sock->sendNext) SendMessageNext(sock); return ret; } static void PrintStats(qsocket_t *s) { Con_Printf("canSend = %4" PRIu32 " \n", s->canSend); Con_Printf("sendSeq = %4" PRIu32 " ", s->sendSequence); Con_Printf("recvSeq = %4" PRIu32 " \n", s->receiveSequence); Con_Printf("\n"); } static void NET_Stats_f(void) { qsocket_t *s; if(Cmd_Argc() == 1) { Con_Printf("unreliable messages sent = %" PRIi32 "\n", unreliableMessagesSent); Con_Printf("unreliable messages recv = %" PRIi32 "\n", unreliableMessagesReceived); Con_Printf("reliable messages sent = %" PRIi32 "\n", messagesSent); Con_Printf("reliable messages received = %" PRIi32 "\n", messagesReceived); Con_Printf("packetsSent = %" PRIi32 "\n", packetsSent); Con_Printf("packetsReSent = %" PRIi32 "\n", packetsReSent); Con_Printf("packetsReceived = %" PRIi32 "\n", packetsReceived); Con_Printf("receivedDuplicateCount = %" PRIi32 "\n", receivedDuplicateCount); Con_Printf("shortPacketCount = %" PRIi32 "\n", shortPacketCount); Con_Printf("droppedDatagrams = %" PRIi32 "\n", droppedDatagrams); } else if(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); } } // recognize ip:port (based on ProQuake) static const char *Strip_Port(const char *host) { static char noport[MAX_QPATH]; /* array size as in Host_Connect_f() */ char *p; int32_t port; if(!host || !*host) return host; q_strlcpy(noport, host, sizeof(noport)); if((p = strrchr(noport, ':')) == NULL) return host; *p++ = '\0'; port = atoi(p); if(port > 0 && port < 65536 && port != net_hostport) { net_hostport = port; Con_Printf("Port set to %" PRIi32 "\n", net_hostport); } return noport; } static bool testInProgress = false; static int32_t testPollCount; static int32_t testDriver; static sys_socket_t testSocket; static void Test_Poll(void *unused); static PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll}; static void Test_Poll(void *unused) { struct qsockaddr clientaddr; int32_t control; int32_t len; char name[32]; char address[64]; int32_t colors; int32_t frags; int32_t connectTime; (void)unused; net_landriverlevel = testDriver; while(1) { len = dfunc.Read(testSocket, net_message.data, net_message.maxsize, &clientaddr); if(len < (int32_t) sizeof(int32_t)) break; net_message.cursize = len; MSG_BeginReading(); control = BigLong(*((int32_t *)net_message.data)); MSG_ReadLong(); if(control == -1) break; if((control & (~NETFLAG_LENGTH_MASK)) != (int32_t)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"); MSG_ReadByte(); /* playerNumber */ strcpy(name, MSG_ReadString()); colors = MSG_ReadLong(); frags = MSG_ReadLong(); connectTime = MSG_ReadLong(); strcpy(address, MSG_ReadString()); Con_Printf("%s\n frags:%3" PRIi32 " colors:%" PRIi32 " %" PRIi32 " time:%" PRIi32 "\n %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address); } testPollCount--; if(testPollCount) { SchedulePollProcedure(&testPollProcedure, 0.1); } else { dfunc.Close_Socket(testSocket); testInProgress = false; } } static void Test_f(void) { const char *host; int32_t n; int32_t maxusers = MAX_SCOREBOARD; struct qsockaddr sendaddr; if(testInProgress) return; host = Strip_Port(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; maxusers = 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) { Con_Printf("Could not resolve %s\n", host); return; } justdoit: testSocket = dfunc.Open_Socket(0); if(testSocket == INVALID_SOCKET) return; testInProgress = true; testPollCount = 20; testDriver = net_landriverlevel; for(n = 0; n < maxusers; 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); *((int32_t *)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 bool test2InProgress = false; static int32_t test2Driver; static sys_socket_t test2Socket; static void Test2_Poll(void *unused); static PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll}; static void Test2_Poll(void *unused) { struct qsockaddr clientaddr; int32_t control; int32_t len; char name[256]; char value[256]; (void)unused; net_landriverlevel = test2Driver; name[0] = 0; len = dfunc.Read(test2Socket, net_message.data, net_message.maxsize, &clientaddr); if(len < (int32_t) sizeof(int32_t)) goto reschedule; net_message.cursize = len; MSG_BeginReading(); control = BigLong(*((int32_t *)net_message.data)); MSG_ReadLong(); if(control == -1) goto error; if((control & (~NETFLAG_LENGTH_MASK)) != (int32_t)NETFLAG_CTL) goto error; if((control & NETFLAG_LENGTH_MASK) != len) goto error; if(MSG_ReadByte() != 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); *((int32_t *)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.Close_Socket(test2Socket); test2InProgress = false; return; } static void Test2_f(void) { const char *host; int32_t n; struct qsockaddr sendaddr; if(test2InProgress) return; host = Strip_Port(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; 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) { Con_Printf("Could not resolve %s\n", host); return; } justdoit: test2Socket = dfunc.Open_Socket(0); if(test2Socket == INVALID_SOCKET) 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, ""); *((int32_t *)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); } int32_t Datagram_Init(void) { int32_t i, num_inited; sys_socket_t csock; #if defined(BAN_TEST) banAddr.s_addr = INADDR_ANY; banMask.s_addr = INADDR_NONE; #endif myDriverLevel = net_driverlevel; Cmd_AddCommand("net_stats", NET_Stats_f); if(safemode || COM_CheckParm("-nolan")) return -1; num_inited = 0; for(i = 0; i < net_numlandrivers; i++) { csock = net_landrivers[i].Init(); if(csock == INVALID_SOCKET) continue; net_landrivers[i].initialized = true; net_landrivers[i].controlSock = csock; num_inited++; } if(num_inited == 0) return -1; #if defined(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) { int32_t 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.Close_Socket(sock->socket); } void Datagram_Listen(bool state) { int32_t 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; sys_socket_t newsock; sys_socket_t acceptsock; qsocket_t *sock; qsocket_t *s; int32_t len; int32_t command; int32_t control; int32_t ret; acceptsock = dfunc.CheckNewConnections(); if(acceptsock == INVALID_SOCKET) return NULL; SZ_Clear(&net_message); len = dfunc.Read(acceptsock, net_message.data, net_message.maxsize, &clientaddr); if(len < (int32_t) sizeof(int32_t)) return NULL; net_message.cursize = len; MSG_BeginReading(); control = BigLong(*((int32_t *)net_message.data)); MSG_ReadLong(); if(control == -1) return NULL; if((control & (~NETFLAG_LENGTH_MASK)) != (int32_t)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; 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); *((int32_t *)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) { int32_t playerNumber; int32_t activeNumber; int32_t 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, (int32_t)ED_Float(client->edict, ED_frags)); MSG_WriteLong(&net_message, (int32_t)(net_time - client->netconnection->connecttime)); MSG_WriteString(&net_message, client->netconnection->address); *((int32_t *)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) { const char *prevCvarName; cvar_t *var; // find the search start location prevCvarName = MSG_ReadString(); var = Cvar_FindVarAfter(prevCvarName, CVAR_SERVERINFO); // 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); } *((int32_t *)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; 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"); *((int32_t *)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 defined(BAN_TEST) // check for a ban if(clientaddr.qsa_family == AF_INET) { in_addr_t testAddr; testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; if((testAddr & banMask.s_addr) == banAddr.s_addr) { 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"); *((int32_t *)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)); *((int32_t *)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"); *((int32_t *)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.Open_Socket(0); if(newsock == INVALID_SOCKET) { NET_FreeQSocket(sock); return NULL; } // connect to the client if(dfunc.Connect(newsock, &clientaddr) == -1) { dfunc.Close_Socket(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)); // MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); *((int32_t *)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_(bool xmit) { int32_t ret; int32_t n; int32_t i; struct qsockaddr readaddr; struct qsockaddr myaddr; int32_t 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); *((int32_t *)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 < (int32_t) sizeof(int32_t)) 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(*((int32_t *)net_message.data)); MSG_ReadLong(); if(control == -1) continue; if((control & (~NETFLAG_LENGTH_MASK)) != (int32_t)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++; strcpy(hostcache[n].name, MSG_ReadString()); strcpy(hostcache[n].map, MSG_ReadString()); hostcache[n].users = MSG_ReadByte(); hostcache[n].maxusers = MSG_ReadByte(); if(MSG_ReadByte() != NET_PROTOCOL_VERSION) { strcpy(hostcache[n].cname, hostcache[n].name); hostcache[n].cname[14] = 0; strcpy(hostcache[n].name, "*"); strcat(hostcache[n].name, hostcache[n].cname); } memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); hostcache[n].driver = net_driverlevel; hostcache[n].ldriver = net_landriverlevel; 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 = 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(bool 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_(const char *host) { struct qsockaddr sendaddr; struct qsockaddr readaddr; qsocket_t *sock; sys_socket_t newsock; int32_t ret; int32_t reps; double start_time; int32_t control; const char *reason; // see if we can resolve the host name if(dfunc.GetAddrFromName(host, &sendaddr) == -1) { Con_Printf("Could not resolve %s\n", host); return NULL; } newsock = dfunc.Open_Socket(0); if(newsock == INVALID_SOCKET) return NULL; sock = NET_NewQSocket(); if(sock == NULL) goto error2; sock->socket = newsock; sock->landriver = net_landriverlevel; // connect to the host if(dfunc.Connect(newsock, &sendaddr) == -1) goto error; // 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); *((int32_t *)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) { Con_Printf("wrong reply address\n"); Con_Printf("Expected: %s | %s\n", dfunc.AddrToString(&sendaddr), StrAddr(&sendaddr)); Con_Printf("Received: %s | %s\n", dfunc.AddrToString(&readaddr), StrAddr(&readaddr)); SCR_UpdateScreen(); ret = 0; continue; } if(ret < (int32_t) sizeof(int32_t)) { ret = 0; continue; } net_message.cursize = ret; MSG_BeginReading(); control = BigLong(*((int32_t *)net_message.data)); MSG_ReadLong(); if(control == -1) { ret = 0; continue; } if((control & (~NETFLAG_LENGTH_MASK)) != (int32_t)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); strcpy(m_return_reason, reason); goto error; } if(ret == -1) { reason = "Network error"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto error; } ret = MSG_ReadByte(); if(ret == CCREP_REJECT) { reason = MSG_ReadString(); Con_Printf("%s\n", reason); q_strlcpy(m_return_reason, reason, sizeof(m_return_reason)); goto error; } 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 error; } 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); strcpy(m_return_reason, reason); goto error; } m_return_onerror = false; return sock; error: NET_FreeQSocket(sock); error2: dfunc.Close_Socket(newsock); if(m_return_onerror) { IN_Deactivate(modestate == MS_WINDOWED); 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; host = Strip_Port(host); 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; }