+ int i;
+ char *s = string + 5;
+ char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
+ char password[64];
+
+ if(rcon_secure.integer > 0)
+ return true;
+
+ for (i = 0;!ISWHITESPACE(*s);s++)
+ if (i < (int)sizeof(password) - 1)
+ password[i++] = *s;
+ if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
+ ++s;
+ password[i] = 0;
+ if (!ISWHITESPACE(password[0]))
+ {
+ const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
+ RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
+ }
+ return true;
+ }
+ if (!strncmp(string, "extResponse ", 12))
+ {
+ ++sv_net_extresponse_count;
+ if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
+ sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
+ sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
+ dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
+ return true;
+ }
+ if (!strncmp(string, "ping", 4))
+ {
+ if (developer_extra.integer)
+ Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
+ NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
+ return true;
+ }
+ if (!strncmp(string, "ack", 3))
+ return true;
+ // we may not have liked the packet, but it was a command packet, so
+ // we're done processing this packet now
+ return true;
+ }
+ // netquake control packets, supported for compatibility only, and only
+ // when running game protocols that are normally served via this connection
+ // protocol
+ // (this protects more modern protocols against being used for
+ // Quake packet flood Denial Of Service attacks)
+ if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) && !ENCRYPTION_REQUIRED)
+ {
+ int c;
+ int protocolnumber;
+ const char *protocolname;
+ data += 4;
+ length -= 4;
+ SZ_Clear(&sv_message);
+ SZ_Write(&sv_message, data, length);
+ MSG_BeginReading(&sv_message);
+ c = MSG_ReadByte(&sv_message);
+ switch (c)
+ {
+ case CCREQ_CONNECT:
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
+ if(!(islocal || sv_public.integer > -2))
+ {
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_REJECT);
+ MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+ break;
+ }
+
+ protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
+ protocolnumber = MSG_ReadByte(&sv_message);
+ if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
+ {
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_REJECT);
+ MSG_WriteString(&sv_message, "Incompatible version.\n");
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+ break;
+ }
+
+ // see if this connect request comes from a known client
+ for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+ {
+ if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
+ {
+ // this is either a duplicate connection request
+ // or coming back from a timeout
+ // (if so, keep their stuff intact)
+
+ crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
+ if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
+ {
+ if (developer_extra.integer)
+ Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_REJECT);
+ MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+ return true;
+ }
+
+ // send a reply
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_ACCEPT);
+ MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+
+ // if client is already spawned, re-send the
+ // serverinfo message as they'll need it to play
+ if (client->begun)
+ SV_SendServerinfo(client);
+ return true;
+ }
+ }
+
+ // this is a new client, check for connection flood
+ if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
+ break;
+
+ // find a slot for the new client
+ for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+ {
+ netconn_t *conn;
+ if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
+ {
+ // connect to the client
+ // everything is allocated, just fill in the details
+ strlcpy (conn->address, addressstring2, sizeof (conn->address));
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
+ // send back the info about the server connection
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_ACCEPT);
+ MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+ // now set up the client struct
+ SV_ConnectClient(clientnum, conn);
+ NetConn_Heartbeat(1);
+ return true;
+ }
+ }
+
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
+ // no room; try to let player know
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_REJECT);
+ MSG_WriteString(&sv_message, "Server is full.\n");
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+ break;
+ case CCREQ_SERVER_INFO:
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
+ if(!(islocal || sv_public.integer > -1))
+ break;
+
+ if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
+ break;
+
+ if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
+ {
+ int numclients;
+ char myaddressstring[128];
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
+ LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
+ MSG_WriteString(&sv_message, myaddressstring);
+ MSG_WriteString(&sv_message, hostname.string);
+ MSG_WriteString(&sv_message, sv.name);
+ // How many clients are there?
+ for (i = 0, numclients = 0;i < svs.maxclients;i++)
+ if (svs.clients[i].active)
+ numclients++;
+ MSG_WriteByte(&sv_message, numclients);
+ MSG_WriteByte(&sv_message, svs.maxclients);
+ MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+ }
+ break;
+ case CCREQ_PLAYER_INFO:
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
+ if(!(islocal || sv_public.integer > -1))
+ break;
+
+ if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
+ break;
+
+ if (sv.active)
+ {
+ int playerNumber, activeNumber, clientNumber;
+ client_t *client;
+
+ playerNumber = MSG_ReadByte(&sv_message);
+ activeNumber = -1;
+ for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
+ if (client->active && ++activeNumber == playerNumber)
+ break;
+ if (clientNumber != svs.maxclients)
+ {
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
+ MSG_WriteByte(&sv_message, playerNumber);
+ MSG_WriteString(&sv_message, client->name);
+ MSG_WriteLong(&sv_message, client->colors);
+ MSG_WriteLong(&sv_message, client->frags);
+ MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
+ if(sv_status_privacy.integer)
+ MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
+ else
+ MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+ }
+ }
+ break;
+ case CCREQ_RULE_INFO:
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
+ if(!(islocal || sv_public.integer > -1))
+ break;
+
+ // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
+
+ if (sv.active)
+ {
+ char *prevCvarName;
+ cvar_t *var;
+
+ // find the search start location
+ prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
+ var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
+
+ // send the response
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
+ if (var)
+ {
+ MSG_WriteString(&sv_message, var->name);
+ MSG_WriteString(&sv_message, var->string);
+ }
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+ }
+ break;
+ case CCREQ_RCON:
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
+ if (sv.active && !rcon_secure.integer)