+static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
+{
+ serverlist_entry_t *entry = &serverlist_cache[n];
+ serverlist_info_t *info = &entry->info;
+ // update description strings for engine menu and console output
+ dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
+ dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
+ (
+ info->gameversion != gameversion.integer
+ &&
+ !(
+ gameversion_min.integer >= 0 // min/max range set by user/mod?
+ && gameversion_max.integer >= 0
+ && gameversion_min.integer <= info->gameversion // version of server in min/max range?
+ && gameversion_max.integer >= info->gameversion
+ )
+ ) ? '1' : '4',
+ info->mod, info->map);
+ if (entry->query == SQS_QUERIED)
+ {
+ if(!serverlist_paused)
+ ServerList_ViewList_Remove(entry);
+ }
+ // if not in the slist menu we should print the server to console (if wanted)
+ else if( serverlist_consoleoutput )
+ Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
+ // and finally, update the view set
+ if(!serverlist_paused)
+ ServerList_ViewList_Insert( entry );
+ // update the entry's state
+ serverlist_cache[n].query = SQS_QUERIED;
+}
+
+// returns true, if it's sensible to continue the processing
+static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qbool isfavorite ) {
+ int n;
+ serverlist_entry_t *entry;
+
+ // ignore the rest of the message if the serverlist is full
+ if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
+ return false;
+ // also ignore it if we have already queried it (other master server response)
+ for( n = 0 ; n < serverlist_cachecount ; n++ )
+ if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
+ break;
+
+ if( n < serverlist_cachecount ) {
+ // the entry has already been queried once or
+ return true;
+ }
+
+ if (serverlist_maxcachecount <= n)
+ {
+ serverlist_maxcachecount += 64;
+ serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
+ }
+
+ entry = &serverlist_cache[n];
+
+ memset(entry, 0, sizeof(*entry));
+ entry->protocol = protocol;
+ // store the data the engine cares about (address and ping)
+ strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
+
+ entry->info.isfavorite = isfavorite;
+
+ // no, then reset the ping right away
+ entry->info.ping = -1;
+ // we also want to increase the serverlist_cachecount then
+ serverlist_cachecount++;
+ serverquerycount++;
+
+ entry->query = SQS_QUERYING;
+
+ return true;
+}
+
+static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qbool isextended)
+{
+ masterreplycount++;
+ if (serverlist_consoleoutput)
+ Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
+ while (length >= 7)
+ {
+ char ipstring [128];
+
+ // IPv4 address
+ if (data[0] == '\\')
+ {
+ unsigned short port = data[5] * 256 + data[6];
+
+ if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
+ dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
+
+ // move on to next address in packet
+ data += 7;
+ length -= 7;
+ }
+ // IPv6 address
+ else if (data[0] == '/' && isextended && length >= 19)
+ {
+ unsigned short port = data[17] * 256 + data[18];
+
+ if (port != 0)
+ {
+#ifdef WHY_JUST_WHY
+ const char *ifname;
+ char ifnamebuf[16];
+
+ /// \TODO: make some basic checks of the IP address (broadcast, ...)
+
+ ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
+ if (ifname != NULL)
+ {
+ dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
+ (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
+ (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
+ ifname, port);
+ }
+ else
+#endif
+ {
+ dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
+ (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
+ (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
+ port);
+ }
+ }
+
+ // move on to next address in packet
+ data += 19;
+ length -= 19;
+ }
+ else
+ {
+ Con_Print("Error while parsing the server list\n");
+ break;
+ }
+
+ if (serverlist_consoleoutput && developer_networking.integer)
+ Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
+
+ if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
+ break;
+ }
+
+ }
+
+ // begin or resume serverlist queries
+ serverlist_querysleep = false;
+ serverlist_querywaittime = host.realtime + 3;
+}
+#endif
+
+static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
+{
+ qbool fromserver;
+ int ret, c;
+ char *string, addressstring2[128];
+ char stringbuf[16384];
+ char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+ size_t sendlength;
+#ifdef CONFIG_MENU
+ char infostringvalue[MAX_INPUTLINE];
+ char ipstring[32];
+ const char *s;
+#endif
+
+ // quakeworld ingame packet
+ fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
+
+ // convert the address to a string incase we need it
+ LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
+
+ if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
+ {
+ // received a command string - strip off the packaging and put it
+ // into our string buffer with NULL termination
+ data += 4;
+ length -= 4;
+ length = min(length, (int)sizeof(stringbuf) - 1);
+ memcpy(stringbuf, data, length);
+ stringbuf[length] = 0;
+ string = stringbuf;
+
+ if (developer_networking.integer)
+ {
+ Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
+ Com_HexDumpToConsole(data, length);
+ }
+
+ sendlength = sizeof(senddata) - 4;
+ switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
+ {
+ case CRYPTO_NOMATCH:
+ // nothing to do
+ break;
+ case CRYPTO_MATCH:
+ if(sendlength)
+ {
+ memcpy(senddata, "\377\377\377\377", 4);
+ NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
+ }
+ break;
+ case CRYPTO_DISCARD:
+ if(sendlength)
+ {
+ memcpy(senddata, "\377\377\377\377", 4);
+ NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
+ }
+ return true;
+ break;
+ case CRYPTO_REPLACE:
+ string = senddata+4;
+ length = (int)sendlength;
+ break;
+ }
+
+ if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
+ {
+ int i = 0, j;
+ for (j = 0;j < MAX_RCONS;j++)
+ {
+ // note: this value from i is used outside the loop too...
+ i = (cls.rcon_ringpos + j) % MAX_RCONS;
+ if(cls.rcon_commands[i][0])
+ if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
+ break;
+ }
+ if (j < MAX_RCONS)
+ {
+ char buf[1500];
+ char argbuf[1500];
+ const char *e;
+ int n;
+ dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
+ memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
+
+ e = strchr(rcon_password.string, ' ');
+ n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
+
+ if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
+ {
+ int k;
+ buf[45] = ' ';
+ strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
+ NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
+ cls.rcon_commands[i][0] = 0;
+ --cls.rcon_trying;
+
+ for (k = 0;k < MAX_RCONS;k++)
+ if(cls.rcon_commands[k][0])
+ if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
+ break;
+ if(k < MAX_RCONS)
+ {
+ int l;
+ NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
+ // extend the timeout on other requests as we asked for a challenge
+ for (l = 0;l < MAX_RCONS;l++)
+ if(cls.rcon_commands[l][0])
+ if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
+ cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
+ }
+
+ return true; // we used up the challenge, so we can't use this oen for connecting now anyway
+ }
+ }
+ }
+ if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
+ {
+ // darkplaces or quake3
+ char protocolnames[1400];
+ Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
+ if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+ Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
+ return true;
+ }
+ Protocol_Names(protocolnames, sizeof(protocolnames));
+#ifdef CONFIG_MENU
+ M_Update_Return_Reason("Got challenge response");
+#endif
+ // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
+ InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
+ // TODO: add userinfo stuff here instead of using NQ commands?
+ memcpy(senddata, "\377\377\377\377", 4);
+ dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
+ NetConn_WriteString(mysocket, senddata, peeraddress);
+ return true;
+ }
+ if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
+ {
+ // darkplaces or quake3
+ if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+ Con_DPrintf("accept message from wrong server %s\n", addressstring2);
+ return true;
+ }
+#ifdef CONFIG_MENU
+ M_Update_Return_Reason("Accepted");
+#endif
+ NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);