00adb61a7ca265edda56cc95b02ab019207d58cb
[xonotic/darkplaces.git] / netconn.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 */
22
23 #include "quakedef.h"
24 #include "thread.h"
25 #include "lhnet.h"
26
27 // for secure rcon authentication
28 #include "hmac.h"
29 #include "mdfour.h"
30 #include <time.h>
31
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
34
35 // note this defaults on for dedicated servers, off for listen servers
36 cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
37 cvar_t sv_public_rejectreason = {0, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
39 extern cvar_t sv_status_privacy;
40
41 static cvar_t sv_masters [] =
42 {
43         {CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
44         {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
45         {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
46         {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
47         {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
48         {0, "sv_masterextra2", "64.22.107.125", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
49         {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
50 #ifdef SUPPORTIPV6
51         {0, "sv_masterextra4", "[2a03:4000:2:225::51:334d]:27950", "dpmaster.sudo.rm-f.org - default master server 4 (admin: divVerent)"}, // admin: divVerent
52 #endif
53         {0, NULL, NULL, NULL}
54 };
55
56 static cvar_t sv_qwmasters [] =
57 {
58         {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
59         {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
60         {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
61         {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
62         {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
63         {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
64         {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
65         {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
66         {0, "sv_qwmasterextra5", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
67         {0, NULL, NULL, NULL}
68 };
69
70 static double nextheartbeattime = 0;
71
72 sizebuf_t cl_message;
73 sizebuf_t sv_message;
74 static unsigned char cl_message_buf[NET_MAXMESSAGE];
75 static unsigned char sv_message_buf[NET_MAXMESSAGE];
76 char cl_readstring[MAX_INPUTLINE];
77 char sv_readstring[MAX_INPUTLINE];
78
79 cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
80 cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
81 cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
82 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
83 cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
84 cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
85 cvar_t net_challengefloodblockingtimeout = {0, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
86 cvar_t net_getstatusfloodblockingtimeout = {0, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
87 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
88 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
89
90 cvar_t cl_netlocalping = {0, "cl_netlocalping","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
91 static cvar_t cl_netpacketloss_send = {0, "cl_netpacketloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
92 static cvar_t cl_netpacketloss_receive = {0, "cl_netpacketloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
93 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
94 static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
95 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
96 static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
97 static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
98 static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
99 static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
100 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
101 static cvar_t gameversion_min = {0, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
102 static cvar_t gameversion_max = {0, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
103 static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
104 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
105 static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
106 extern cvar_t rcon_secure;
107 extern cvar_t rcon_secure_challengetimeout;
108
109 double masterquerytime = -1000;
110 int masterquerycount = 0;
111 int masterreplycount = 0;
112 int serverquerycount = 0;
113 int serverreplycount = 0;
114
115 challenge_t challenge[MAX_CHALLENGES];
116
117 /// this is only false if there are still servers left to query
118 static qboolean serverlist_querysleep = true;
119 static qboolean serverlist_paused = false;
120 /// this is pushed a second or two ahead of realtime whenever a master server
121 /// reply is received, to avoid issuing queries while master replies are still
122 /// flooding in (which would make a mess of the ping times)
123 static double serverlist_querywaittime = 0;
124
125 static int cl_numsockets;
126 static lhnetsocket_t *cl_sockets[16];
127 static int sv_numsockets;
128 static lhnetsocket_t *sv_sockets[16];
129
130 netconn_t *netconn_list = NULL;
131 mempool_t *netconn_mempool = NULL;
132 void *netconn_mutex = NULL;
133
134 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
135 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
136 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
137 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
138
139 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
140 int cl_net_extresponse_count = 0;
141 int cl_net_extresponse_last = 0;
142
143 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
144 int sv_net_extresponse_count = 0;
145 int sv_net_extresponse_last = 0;
146
147 // ServerList interface
148 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
149 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
150
151 serverlist_infofield_t serverlist_sortbyfield;
152 int serverlist_sortflags;
153
154 int serverlist_viewcount = 0;
155 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
156
157 int serverlist_maxcachecount = 0;
158 int serverlist_cachecount = 0;
159 serverlist_entry_t *serverlist_cache = NULL;
160
161 qboolean serverlist_consoleoutput;
162
163 static int nFavorites = 0;
164 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
165 static int nFavorites_idfp = 0;
166 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
167
168 void NetConn_UpdateFavorites(void)
169 {
170         const char *p;
171         nFavorites = 0;
172         nFavorites_idfp = 0;
173         p = net_slist_favorites.string;
174         while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
175         {
176                 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
177                 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
178                 // (if v6 address contains port, it must start with '[')
179                 {
180                         strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
181                         ++nFavorites_idfp;
182                 }
183                 else 
184                 {
185                         if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
186                                 ++nFavorites;
187                 }
188         }
189 }
190
191 /// helper function to insert a value into the viewset
192 /// spare entries will be removed
193 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
194 {
195     int i;
196         if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
197                 i = serverlist_viewcount++;
198         } else {
199                 i = SERVERLIST_VIEWLISTSIZE - 1;
200         }
201
202         for( ; i > index ; i-- )
203                 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
204
205         serverlist_viewlist[index] = (int)(entry - serverlist_cache);
206 }
207
208 /// we suppose serverlist_viewcount to be valid, ie > 0
209 static void _ServerList_ViewList_Helper_Remove( int index )
210 {
211         serverlist_viewcount--;
212         for( ; index < serverlist_viewcount ; index++ )
213                 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
214 }
215
216 /// \returns true if A should be inserted before B
217 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
218 {
219         int result = 0; // > 0 if for numbers A > B and for text if A < B
220
221         if( serverlist_sortflags & SLSF_FAVORITESFIRST )
222         {
223                 if(A->info.isfavorite != B->info.isfavorite)
224                         return A->info.isfavorite;
225         }
226
227         switch( serverlist_sortbyfield ) {
228                 case SLIF_PING:
229                         result = A->info.ping - B->info.ping;
230                         break;
231                 case SLIF_MAXPLAYERS:
232                         result = A->info.maxplayers - B->info.maxplayers;
233                         break;
234                 case SLIF_NUMPLAYERS:
235                         result = A->info.numplayers - B->info.numplayers;
236                         break;
237                 case SLIF_NUMBOTS:
238                         result = A->info.numbots - B->info.numbots;
239                         break;
240                 case SLIF_NUMHUMANS:
241                         result = A->info.numhumans - B->info.numhumans;
242                         break;
243                 case SLIF_FREESLOTS:
244                         result = A->info.freeslots - B->info.freeslots;
245                         break;
246                 case SLIF_PROTOCOL:
247                         result = A->info.protocol - B->info.protocol;
248                         break;
249                 case SLIF_CNAME:
250                         result = strcmp( B->info.cname, A->info.cname );
251                         break;
252                 case SLIF_GAME:
253                         result = strcasecmp( B->info.game, A->info.game );
254                         break;
255                 case SLIF_MAP:
256                         result = strcasecmp( B->info.map, A->info.map );
257                         break;
258                 case SLIF_MOD:
259                         result = strcasecmp( B->info.mod, A->info.mod );
260                         break;
261                 case SLIF_NAME:
262                         result = strcasecmp( B->info.name, A->info.name );
263                         break;
264                 case SLIF_QCSTATUS:
265                         result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
266                         break;
267                 case SLIF_ISFAVORITE:
268                         result = !!B->info.isfavorite - !!A->info.isfavorite;
269                         break;
270                 default:
271                         Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
272                         break;
273         }
274
275         if (result != 0)
276         {
277                 if( serverlist_sortflags & SLSF_DESCENDING )
278                         return result > 0;
279                 else
280                         return result < 0;
281         }
282
283         // if the chosen sort key is identical, sort by index
284         // (makes this a stable sort, so that later replies from servers won't
285         //  shuffle the servers around when they have the same ping)
286         return A < B;
287 }
288
289 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
290 {
291         // This should actually be done with some intermediate and end-of-function return
292         switch( op ) {
293                 case SLMO_LESS:
294                         return A < B;
295                 case SLMO_LESSEQUAL:
296                         return A <= B;
297                 case SLMO_EQUAL:
298                         return A == B;
299                 case SLMO_GREATER:
300                         return A > B;
301                 case SLMO_NOTEQUAL:
302                         return A != B;
303                 case SLMO_GREATEREQUAL:
304                 case SLMO_CONTAINS:
305                 case SLMO_NOTCONTAIN:
306                 case SLMO_STARTSWITH:
307                 case SLMO_NOTSTARTSWITH:
308                         return A >= B;
309                 default:
310                         Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
311                         return false;
312         }
313 }
314
315 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
316 {
317         int i;
318         char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
319         COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
320         for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
321                 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
322         bufferA[i] = 0;
323         for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
324                 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
325         bufferB[i] = 0;
326
327         // Same here, also using an intermediate & final return would be more appropriate
328         // A info B mask
329         switch( op ) {
330                 case SLMO_CONTAINS:
331                         return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
332                 case SLMO_NOTCONTAIN:
333                         return !*bufferB || !strstr( bufferA, bufferB );
334                 case SLMO_STARTSWITH:
335                         //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
336                         return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
337                 case SLMO_NOTSTARTSWITH:
338                         return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
339                 case SLMO_LESS:
340                         return strcmp( bufferA, bufferB ) < 0;
341                 case SLMO_LESSEQUAL:
342                         return strcmp( bufferA, bufferB ) <= 0;
343                 case SLMO_EQUAL:
344                         return strcmp( bufferA, bufferB ) == 0;
345                 case SLMO_GREATER:
346                         return strcmp( bufferA, bufferB ) > 0;
347                 case SLMO_NOTEQUAL:
348                         return strcmp( bufferA, bufferB ) != 0;
349                 case SLMO_GREATEREQUAL:
350                         return strcmp( bufferA, bufferB ) >= 0;
351                 default:
352                         Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
353                         return false;
354         }
355 }
356
357 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
358 {
359         if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
360                 return false;
361         if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
362                 return false;
363         if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
364                 return false;
365         if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
366                 return false;
367         if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
368                 return false;
369         if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
370                 return false;
371         if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
372                 return false;
373         if( *mask->info.cname
374                 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
375                 return false;
376         if( *mask->info.game
377                 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
378                 return false;
379         if( *mask->info.mod
380                 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
381                 return false;
382         if( *mask->info.map
383                 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
384                 return false;
385         if( *mask->info.name
386                 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
387                 return false;
388         if( *mask->info.qcstatus
389                 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
390                 return false;
391         if( *mask->info.players
392                 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
393                 return false;
394         if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
395                 return false;
396         return true;
397 }
398
399 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
400 {
401         int start, end, mid, i;
402         lhnetaddress_t addr;
403
404         // reject incompatible servers
405         if(
406                 entry->info.gameversion != gameversion.integer
407                 &&
408                 !(
409                            gameversion_min.integer >= 0 // min/max range set by user/mod?
410                         && gameversion_max.integer >= 0
411                         && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
412                         && gameversion_max.integer >= entry->info.gameversion
413                  )
414         )
415                 return;
416
417         // refresh the "favorite" status
418         entry->info.isfavorite = false;
419         if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
420         {
421                 char idfp[FP64_SIZE+1];
422                 for(i = 0; i < nFavorites; ++i)
423                 {
424                         if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
425                         {
426                                 entry->info.isfavorite = true;
427                                 break;
428                         }
429                 }
430                 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
431                 {
432                         for(i = 0; i < nFavorites_idfp; ++i)
433                         {
434                                 if(!strcmp(idfp, favorites_idfp[i]))
435                                 {
436                                         entry->info.isfavorite = true;
437                                         break;
438                                 }
439                         }
440                 }
441         }
442
443         // FIXME: change this to be more readable (...)
444         // now check whether it passes through the masks
445         for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
446                 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
447                         return;
448
449         for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
450                 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
451                         break;
452         if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
453                 return;
454
455         if( !serverlist_viewcount ) {
456                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
457                 return;
458         }
459         // ok, insert it, we just need to find out where exactly:
460
461         // two special cases
462         // check whether to insert it as new first item
463         if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
464                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
465                 return;
466         } // check whether to insert it as new last item
467         else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
468                 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
469                 return;
470         }
471         start = 0;
472         end = serverlist_viewcount - 1;
473         while( end > start + 1 )
474         {
475                 mid = (start + end) / 2;
476                 // test the item that lies in the middle between start and end
477                 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
478                         // the item has to be in the upper half
479                         end = mid;
480                 else
481                         // the item has to be in the lower half
482                         start = mid;
483         }
484         _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
485 }
486
487 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
488 {
489         int i;
490         for( i = 0; i < serverlist_viewcount; i++ )
491         {
492                 if (ServerList_GetViewEntry(i) == entry)
493                 {
494                         _ServerList_ViewList_Helper_Remove(i);
495                         break;
496                 }
497         }
498 }
499
500 void ServerList_RebuildViewList(void)
501 {
502         int i;
503
504         serverlist_viewcount = 0;
505         for( i = 0 ; i < serverlist_cachecount ; i++ ) {
506                 serverlist_entry_t *entry = &serverlist_cache[i];
507                 // also display entries that are currently being refreshed [11/8/2007 Black]
508                 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
509                         ServerList_ViewList_Insert( entry );
510         }
511 }
512
513 void ServerList_ResetMasks(void)
514 {
515         int i;
516
517         memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
518         memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
519         // numbots needs to be compared to -1 to always succeed
520         for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
521                 serverlist_andmasks[i].info.numbots = -1;
522         for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
523                 serverlist_ormasks[i].info.numbots = -1;
524 }
525
526 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
527 {
528         int i;
529         int numplayers = 0, maxplayers = 0;
530         for (i = 0;i < serverlist_cachecount;i++)
531         {
532                 if (serverlist_cache[i].query == SQS_QUERIED)
533                 {
534                         numplayers += serverlist_cache[i].info.numhumans;
535                         maxplayers += serverlist_cache[i].info.maxplayers;
536                 }
537         }
538         *numplayerspointer = numplayers;
539         *maxplayerspointer = maxplayers;
540 }
541
542 #if 0
543 static void _ServerList_Test(void)
544 {
545         int i;
546         if (serverlist_maxcachecount <= 1024)
547         {
548                 serverlist_maxcachecount = 1024;
549                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
550         }
551         for( i = 0 ; i < 1024 ; i++ ) {
552                 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
553                 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
554                 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
555                 serverlist_cache[serverlist_cachecount].finished = true;
556                 dpsnprintf( serverlist_cache[serverlist_cachecount].line1, sizeof(serverlist_cache[serverlist_cachecount].info.line1), "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
557                 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
558                 serverlist_cachecount++;
559         }
560 }
561 #endif
562
563 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
564 {
565         masterquerytime = realtime;
566         masterquerycount = 0;
567         masterreplycount = 0;
568         if( resetcache ) {
569                 serverquerycount = 0;
570                 serverreplycount = 0;
571                 serverlist_cachecount = 0;
572                 serverlist_viewcount = 0;
573                 serverlist_maxcachecount = 0;
574                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
575         } else {
576                 // refresh all entries
577                 int n;
578                 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
579                         serverlist_entry_t *entry = &serverlist_cache[ n ];
580                         entry->query = SQS_REFRESHING;
581                         entry->querycounter = 0;
582                 }
583         }
584         serverlist_consoleoutput = consoleoutput;
585
586         //_ServerList_Test();
587
588         NetConn_QueryMasters(querydp, queryqw);
589 }
590
591 // rest
592
593 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
594 {
595         int length;
596         int i;
597         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
598                 Thread_LockMutex(netconn_mutex);
599         length = LHNET_Read(mysocket, data, maxlength, peeraddress);
600         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
601                 Thread_UnlockMutex(netconn_mutex);
602         if (length == 0)
603                 return 0;
604         if (cl_netpacketloss_receive.integer)
605                 for (i = 0;i < cl_numsockets;i++)
606                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
607                                 return 0;
608         if (developer_networking.integer)
609         {
610                 char addressstring[128], addressstring2[128];
611                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
612                 if (length > 0)
613                 {
614                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
615                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
616                         Com_HexDumpToConsole((unsigned char *)data, length);
617                 }
618                 else
619                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
620         }
621         return length;
622 }
623
624 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
625 {
626         int ret;
627         int i;
628         if (cl_netpacketloss_send.integer)
629                 for (i = 0;i < cl_numsockets;i++)
630                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
631                                 return length;
632         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
633                 Thread_LockMutex(netconn_mutex);
634         ret = LHNET_Write(mysocket, data, length, peeraddress);
635         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
636                 Thread_UnlockMutex(netconn_mutex);
637         if (developer_networking.integer)
638         {
639                 char addressstring[128], addressstring2[128];
640                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
641                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
642                 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", (void *)mysocket, addressstring, (void *)data, length, (void *)peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
643                 Com_HexDumpToConsole((unsigned char *)data, length);
644         }
645         return ret;
646 }
647
648 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
649 {
650         // note this does not include the trailing NULL because we add that in the parser
651         return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
652 }
653
654 qboolean NetConn_CanSend(netconn_t *conn)
655 {
656         conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
657         conn->outgoing_netgraph[conn->outgoing_packetcounter].time            = realtime;
658         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
659         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
660         conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
661         conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime       = conn->cleartime;
662         if (realtime > conn->cleartime)
663                 return true;
664         else
665         {
666                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
667                 return false;
668         }
669 }
670
671 void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
672 {
673         double bursttime = burstsize / (double)rate;
674
675         // delay later packets to obey rate limit
676         if (*cleartime < realtime - bursttime)
677                 *cleartime = realtime - bursttime;
678         *cleartime = *cleartime + len / (double)rate;
679
680         // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
681         if (net_test.integer)
682         {
683                 if (*cleartime < realtime)
684                         *cleartime = realtime;
685         }
686 }
687
688 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
689 {
690         int totallen = 0;
691         unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
692         unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
693
694         // if this packet was supposedly choked, but we find ourselves sending one
695         // anyway, make sure the size counting starts at zero
696         // (this mostly happens on level changes and disconnects and such)
697         if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
698                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
699
700         conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
701
702         if (protocol == PROTOCOL_QUAKEWORLD)
703         {
704                 int packetLen;
705                 qboolean sendreliable;
706
707                 // note that it is ok to send empty messages to the qw server,
708                 // otherwise it won't respond to us at all
709
710                 sendreliable = false;
711                 // if the remote side dropped the last reliable message, resend it
712                 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
713                         sendreliable = true;
714                 // if the reliable transmit buffer is empty, copy the current message out
715                 if (!conn->sendMessageLength && conn->message.cursize)
716                 {
717                         memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
718                         conn->sendMessageLength = conn->message.cursize;
719                         SZ_Clear(&conn->message); // clear the message buffer
720                         conn->qw.reliable_sequence ^= 1;
721                         sendreliable = true;
722                 }
723                 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
724                 StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
725                 // last received unreliable packet number, and last received reliable packet number (0 or 1)
726                 StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
727                 packetLen = 8;
728                 conn->outgoing_unreliable_sequence++;
729                 // client sends qport in every packet
730                 if (conn == cls.netcon)
731                 {
732                         *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
733                         packetLen += 2;
734                         // also update cls.qw_outgoing_sequence
735                         cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
736                 }
737                 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
738                 {
739                         Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
740                         return -1;
741                 }
742
743                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
744
745                 // add the reliable message if there is one
746                 if (sendreliable)
747                 {
748                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
749                         memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
750                         packetLen += conn->sendMessageLength;
751                         conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
752                 }
753
754                 // add the unreliable message if possible
755                 if (packetLen + data->cursize <= 1400)
756                 {
757                         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
758                         memcpy(sendbuffer + packetLen, data->data, data->cursize);
759                         packetLen += data->cursize;
760                 }
761
762                 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
763
764                 conn->packetsSent++;
765                 conn->unreliableMessagesSent++;
766
767                 totallen += packetLen + 28;
768         }
769         else
770         {
771                 unsigned int packetLen;
772                 unsigned int dataLen;
773                 unsigned int eom;
774                 const void *sendme;
775                 size_t sendmelen;
776
777                 // if a reliable message fragment has been lost, send it again
778                 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
779                 {
780                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
781                         {
782                                 dataLen = conn->sendMessageLength;
783                                 eom = NETFLAG_EOM;
784                         }
785                         else
786                         {
787                                 dataLen = MAX_PACKETFRAGMENT;
788                                 eom = 0;
789                         }
790
791                         packetLen = NET_HEADERSIZE + dataLen;
792
793                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
794                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
795                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
796
797                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
798
799                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
800                         if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
801                         {
802                                 conn->lastSendTime = realtime;
803                                 conn->packetsReSent++;
804                         }
805
806                         totallen += sendmelen + 28;
807                 }
808
809                 // if we have a new reliable message to send, do so
810                 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
811                 {
812                         if (conn->message.cursize > (int)sizeof(conn->sendMessage))
813                         {
814                                 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
815                                 conn->message.overflowed = true;
816                                 return -1;
817                         }
818
819                         if (developer_networking.integer && conn == cls.netcon)
820                         {
821                                 Con_Print("client sending reliable message to server:\n");
822                                 SZ_HexDumpToConsole(&conn->message);
823                         }
824
825                         memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
826                         conn->sendMessageLength = conn->message.cursize;
827                         SZ_Clear(&conn->message);
828
829                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
830                         {
831                                 dataLen = conn->sendMessageLength;
832                                 eom = NETFLAG_EOM;
833                         }
834                         else
835                         {
836                                 dataLen = MAX_PACKETFRAGMENT;
837                                 eom = 0;
838                         }
839
840                         packetLen = NET_HEADERSIZE + dataLen;
841
842                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
843                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
844                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
845
846                         conn->nq.sendSequence++;
847
848                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
849
850                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
851                         if(sendme)
852                                 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
853
854                         conn->lastSendTime = realtime;
855                         conn->packetsSent++;
856                         conn->reliableMessagesSent++;
857
858                         totallen += sendmelen + 28;
859                 }
860
861                 // if we have an unreliable message to send, do so
862                 if (data->cursize)
863                 {
864                         packetLen = NET_HEADERSIZE + data->cursize;
865
866                         if (packetLen > (int)sizeof(sendbuffer))
867                         {
868                                 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
869                                 return -1;
870                         }
871
872                         StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE);
873                         StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
874                         memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
875
876                         conn->outgoing_unreliable_sequence++;
877
878                         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
879
880                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
881                         if(sendme)
882                                 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
883
884                         conn->packetsSent++;
885                         conn->unreliableMessagesSent++;
886
887                         totallen += sendmelen + 28;
888                 }
889         }
890
891         NetConn_UpdateCleartime(&conn->cleartime, cl_rate.integer, cl_rate_burstsize.integer, totallen);
892
893         return 0;
894 }
895
896 qboolean NetConn_HaveClientPorts(void)
897 {
898         return !!cl_numsockets;
899 }
900
901 qboolean NetConn_HaveServerPorts(void)
902 {
903         return !!sv_numsockets;
904 }
905
906 void NetConn_CloseClientPorts(void)
907 {
908         for (;cl_numsockets > 0;cl_numsockets--)
909                 if (cl_sockets[cl_numsockets - 1])
910                         LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
911 }
912
913 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
914 {
915         lhnetaddress_t address;
916         lhnetsocket_t *s;
917         int success;
918         char addressstring2[1024];
919         if (addressstring && addressstring[0])
920                 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
921         else
922                 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
923         if (success)
924         {
925                 if ((s = LHNET_OpenSocket_Connectionless(&address)))
926                 {
927                         cl_sockets[cl_numsockets++] = s;
928                         LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
929                         if (addresstype != LHNETADDRESSTYPE_LOOP)
930                                 Con_Printf("Client opened a socket on address %s\n", addressstring2);
931                 }
932                 else
933                 {
934                         LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
935                         Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
936                 }
937         }
938         else
939                 Con_Printf("Client unable to parse address %s\n", addressstring);
940 }
941
942 void NetConn_OpenClientPorts(void)
943 {
944         int port;
945         NetConn_CloseClientPorts();
946
947         SV_LockThreadMutex(); // FIXME recursive?
948         Crypto_LoadKeys(); // client sockets
949         SV_UnlockThreadMutex();
950
951         port = bound(0, cl_netport.integer, 65535);
952         if (cl_netport.integer != port)
953                 Cvar_SetValueQuick(&cl_netport, port);
954         if(port == 0)
955                 Con_Printf("Client using an automatically assigned port\n");
956         else
957                 Con_Printf("Client using port %i\n", port);
958         NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
959         NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
960 #ifdef SUPPORTIPV6
961         NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
962 #endif
963 }
964
965 void NetConn_CloseServerPorts(void)
966 {
967         for (;sv_numsockets > 0;sv_numsockets--)
968                 if (sv_sockets[sv_numsockets - 1])
969                         LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
970 }
971
972 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
973 {
974         lhnetaddress_t address;
975         lhnetsocket_t *s;
976         int port;
977         char addressstring2[1024];
978         int success;
979
980         for (port = defaultport; port <= defaultport + range; port++)
981         {
982                 if (addressstring && addressstring[0])
983                         success = LHNETADDRESS_FromString(&address, addressstring, port);
984                 else
985                         success = LHNETADDRESS_FromPort(&address, addresstype, port);
986                 if (success)
987                 {
988                         if ((s = LHNET_OpenSocket_Connectionless(&address)))
989                         {
990                                 sv_sockets[sv_numsockets++] = s;
991                                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
992                                 if (addresstype != LHNETADDRESSTYPE_LOOP)
993                                         Con_Printf("Server listening on address %s\n", addressstring2);
994                                 return true;
995                         }
996                         else
997                         {
998                                 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
999                                 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
1000                         }
1001                 }
1002                 else
1003                 {
1004                         Con_Printf("Server unable to parse address %s\n", addressstring);
1005                         // if it cant parse one address, it wont be able to parse another for sure
1006                         return false;
1007                 }
1008         }
1009         return false;
1010 }
1011
1012 void NetConn_OpenServerPorts(int opennetports)
1013 {
1014         int port;
1015         NetConn_CloseServerPorts();
1016
1017         SV_LockThreadMutex(); // FIXME recursive?
1018         Crypto_LoadKeys(); // server sockets
1019         SV_UnlockThreadMutex();
1020
1021         NetConn_UpdateSockets();
1022         port = bound(0, sv_netport.integer, 65535);
1023         if (port == 0)
1024                 port = 26000;
1025         Con_Printf("Server using port %i\n", port);
1026         if (sv_netport.integer != port)
1027                 Cvar_SetValueQuick(&sv_netport, port);
1028         if (cls.state != ca_dedicated)
1029                 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1030         if (opennetports)
1031         {
1032 #ifdef SUPPORTIPV6
1033                 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1034                 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1035 #else
1036                 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1037 #endif
1038         }
1039         if (sv_numsockets == 0)
1040                 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1041 }
1042
1043 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1044 {
1045         int i, a = LHNETADDRESS_GetAddressType(address);
1046         for (i = 0;i < cl_numsockets;i++)
1047                 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1048                         return cl_sockets[i];
1049         return NULL;
1050 }
1051
1052 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1053 {
1054         int i, a = LHNETADDRESS_GetAddressType(address);
1055         for (i = 0;i < sv_numsockets;i++)
1056                 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1057                         return sv_sockets[i];
1058         return NULL;
1059 }
1060
1061 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1062 {
1063         netconn_t *conn;
1064         conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1065         conn->mysocket = mysocket;
1066         conn->peeraddress = *peeraddress;
1067         conn->lastMessageTime = realtime;
1068         conn->message.data = conn->messagedata;
1069         conn->message.maxsize = sizeof(conn->messagedata);
1070         conn->message.cursize = 0;
1071         // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1072         // reduce effectiveness of connection request floods
1073         conn->timeout = realtime + net_connecttimeout.value;
1074         LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1075         conn->next = netconn_list;
1076         netconn_list = conn;
1077         return conn;
1078 }
1079
1080 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1081 void NetConn_Close(netconn_t *conn)
1082 {
1083         netconn_t *c;
1084         // remove connection from list
1085
1086         // allow the client to reconnect immediately
1087         NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1088
1089         if (conn == netconn_list)
1090                 netconn_list = conn->next;
1091         else
1092         {
1093                 for (c = netconn_list;c;c = c->next)
1094                 {
1095                         if (c->next == conn)
1096                         {
1097                                 c->next = conn->next;
1098                                 break;
1099                         }
1100                 }
1101                 // not found in list, we'll avoid crashing here...
1102                 if (!c)
1103                         return;
1104         }
1105         // free connection
1106         Mem_Free(conn);
1107 }
1108
1109 static int clientport = -1;
1110 static int clientport2 = -1;
1111 static int hostport = -1;
1112 void NetConn_UpdateSockets(void)
1113 {
1114         int i, j;
1115
1116         // TODO add logic to automatically close sockets if needed
1117         LHNET_DefaultDSCP(net_tos_dscp.integer);
1118
1119         if (cls.state != ca_dedicated)
1120         {
1121                 if (clientport2 != cl_netport.integer)
1122                 {
1123                         clientport2 = cl_netport.integer;
1124                         if (cls.state == ca_connected)
1125                                 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1126                 }
1127                 if (cls.state == ca_disconnected && clientport != clientport2)
1128                 {
1129                         clientport = clientport2;
1130                         NetConn_CloseClientPorts();
1131                 }
1132                 if (cl_numsockets == 0)
1133                         NetConn_OpenClientPorts();
1134         }
1135
1136         if (hostport != sv_netport.integer)
1137         {
1138                 hostport = sv_netport.integer;
1139                 if (sv.active)
1140                         Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1141         }
1142
1143         for (j = 0;j < MAX_RCONS;j++)
1144         {
1145                 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1146                 if(cls.rcon_commands[i][0])
1147                 {
1148                         if(realtime > cls.rcon_timeout[i])
1149                         {
1150                                 char s[128];
1151                                 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1152                                 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1153                                 cls.rcon_commands[i][0] = 0;
1154                                 --cls.rcon_trying;
1155                                 break;
1156                         }
1157                 }
1158         }
1159 }
1160
1161 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1162 {
1163         int originallength = length;
1164         unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1165         unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1166         unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1167         if (length < 8)
1168                 return 0;
1169
1170         if (protocol == PROTOCOL_QUAKEWORLD)
1171         {
1172                 int sequence, sequence_ack;
1173                 int reliable_ack, reliable_message;
1174                 int count;
1175                 //int qport;
1176
1177                 sequence = LittleLong(*((int *)(data + 0)));
1178                 sequence_ack = LittleLong(*((int *)(data + 4)));
1179                 data += 8;
1180                 length -= 8;
1181
1182                 if (conn != cls.netcon)
1183                 {
1184                         // server only
1185                         if (length < 2)
1186                                 return 0;
1187                         // TODO: use qport to identify that this client really is who they say they are?  (and elsewhere in the code to identify the connection without a port match?)
1188                         //qport = LittleShort(*((int *)(data + 8)));
1189                         data += 2;
1190                         length -= 2;
1191                 }
1192
1193                 conn->packetsReceived++;
1194                 reliable_message = (sequence >> 31) & 1;
1195                 reliable_ack = (sequence_ack >> 31) & 1;
1196                 sequence &= ~(1<<31);
1197                 sequence_ack &= ~(1<<31);
1198                 if (sequence <= conn->qw.incoming_sequence)
1199                 {
1200                         //Con_DPrint("Got a stale datagram\n");
1201                         return 0;
1202                 }
1203                 count = sequence - (conn->qw.incoming_sequence + 1);
1204                 if (count > 0)
1205                 {
1206                         conn->droppedDatagrams += count;
1207                         //Con_DPrintf("Dropped %u datagram(s)\n", count);
1208                         while (count--)
1209                         {
1210                                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1211                                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1212                                 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1213                                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1214                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1215                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1216                         }
1217                 }
1218                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1219                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1220                 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1221                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1222                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1223                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1224                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1225
1226                 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1227                 if (net_test.integer)
1228                 {
1229                         if (conn->cleartime < realtime)
1230                                 conn->cleartime = realtime;
1231                 }
1232
1233                 if (reliable_ack == conn->qw.reliable_sequence)
1234                 {
1235                         // received, now we will be able to send another reliable message
1236                         conn->sendMessageLength = 0;
1237                         conn->reliableMessagesReceived++;
1238                 }
1239                 conn->qw.incoming_sequence = sequence;
1240                 if (conn == cls.netcon)
1241                         cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1242                 conn->qw.incoming_acknowledged = sequence_ack;
1243                 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1244                 if (reliable_message)
1245                         conn->qw.incoming_reliable_sequence ^= 1;
1246                 conn->lastMessageTime = realtime;
1247                 conn->timeout = realtime + newtimeout;
1248                 conn->unreliableMessagesReceived++;
1249                 if (conn == cls.netcon)
1250                 {
1251                         SZ_Clear(&cl_message);
1252                         SZ_Write(&cl_message, data, length);
1253                         MSG_BeginReading(&cl_message);
1254                 }
1255                 else
1256                 {
1257                         SZ_Clear(&sv_message);
1258                         SZ_Write(&sv_message, data, length);
1259                         MSG_BeginReading(&sv_message);
1260                 }
1261                 return 2;
1262         }
1263         else
1264         {
1265                 unsigned int count;
1266                 unsigned int flags;
1267                 unsigned int sequence;
1268                 size_t qlength;
1269                 const void *sendme;
1270                 size_t sendmelen;
1271
1272                 originallength = length;
1273                 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1274                 if(!data)
1275                         return 0;
1276                 if(length < 8)
1277                         return 0;
1278
1279                 qlength = (unsigned int)BuffBigLong(data);
1280                 flags = qlength & ~NETFLAG_LENGTH_MASK;
1281                 qlength &= NETFLAG_LENGTH_MASK;
1282                 // control packets were already handled
1283                 if (!(flags & NETFLAG_CTL) && qlength == length)
1284                 {
1285                         sequence = BuffBigLong(data + 4);
1286                         conn->packetsReceived++;
1287                         data += 8;
1288                         length -= 8;
1289                         if (flags & NETFLAG_UNRELIABLE)
1290                         {
1291                                 if (sequence >= conn->nq.unreliableReceiveSequence)
1292                                 {
1293                                         if (sequence > conn->nq.unreliableReceiveSequence)
1294                                         {
1295                                                 count = sequence - conn->nq.unreliableReceiveSequence;
1296                                                 conn->droppedDatagrams += count;
1297                                                 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1298                                                 while (count--)
1299                                                 {
1300                                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1301                                                         conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1302                                                         conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1303                                                         conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1304                                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1305                                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1306                                                 }
1307                                         }
1308                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1309                                         conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1310                                         conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1311                                         conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1312                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1313                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1314                                         NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1315
1316                                         conn->nq.unreliableReceiveSequence = sequence + 1;
1317                                         conn->lastMessageTime = realtime;
1318                                         conn->timeout = realtime + newtimeout;
1319                                         conn->unreliableMessagesReceived++;
1320                                         if (length > 0)
1321                                         {
1322                                                 if (conn == cls.netcon)
1323                                                 {
1324                                                         SZ_Clear(&cl_message);
1325                                                         SZ_Write(&cl_message, data, length);
1326                                                         MSG_BeginReading(&cl_message);
1327                                                 }
1328                                                 else
1329                                                 {
1330                                                         SZ_Clear(&sv_message);
1331                                                         SZ_Write(&sv_message, data, length);
1332                                                         MSG_BeginReading(&sv_message);
1333                                                 }
1334                                                 return 2;
1335                                         }
1336                                 }
1337                                 //else
1338                                 //      Con_DPrint("Got a stale datagram\n");
1339                                 return 1;
1340                         }
1341                         else if (flags & NETFLAG_ACK)
1342                         {
1343                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1344                                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1345
1346                                 if (sequence == (conn->nq.sendSequence - 1))
1347                                 {
1348                                         if (sequence == conn->nq.ackSequence)
1349                                         {
1350                                                 conn->nq.ackSequence++;
1351                                                 if (conn->nq.ackSequence != conn->nq.sendSequence)
1352                                                         Con_DPrint("ack sequencing error\n");
1353                                                 conn->lastMessageTime = realtime;
1354                                                 conn->timeout = realtime + newtimeout;
1355                                                 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1356                                                 {
1357                                                         unsigned int packetLen;
1358                                                         unsigned int dataLen;
1359                                                         unsigned int eom;
1360
1361                                                         conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1362                                                         memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1363
1364                                                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1365                                                         {
1366                                                                 dataLen = conn->sendMessageLength;
1367                                                                 eom = NETFLAG_EOM;
1368                                                         }
1369                                                         else
1370                                                         {
1371                                                                 dataLen = MAX_PACKETFRAGMENT;
1372                                                                 eom = 0;
1373                                                         }
1374
1375                                                         packetLen = NET_HEADERSIZE + dataLen;
1376
1377                                                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
1378                                                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1379                                                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1380
1381                                                         conn->nq.sendSequence++;
1382
1383                                                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1384                                                         if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
1385                                                         {
1386                                                                 conn->lastSendTime = realtime;
1387                                                                 conn->packetsSent++;
1388                                                         }
1389                                                 }
1390                                                 else
1391                                                         conn->sendMessageLength = 0;
1392                                         }
1393                                         //else
1394                                         //      Con_DPrint("Duplicate ACK received\n");
1395                                 }
1396                                 //else
1397                                 //      Con_DPrint("Stale ACK received\n");
1398                                 return 1;
1399                         }
1400                         else if (flags & NETFLAG_DATA)
1401                         {
1402                                 unsigned char temppacket[8];
1403                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   += originallength + 28;
1404                                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1405
1406                                 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        += 8 + 28;
1407
1408                                 StoreBigLong(temppacket, 8 | NETFLAG_ACK);
1409                                 StoreBigLong(temppacket + 4, sequence);
1410                                 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1411                                 if(sendme)
1412                                         NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
1413                                 if (sequence == conn->nq.receiveSequence)
1414                                 {
1415                                         conn->lastMessageTime = realtime;
1416                                         conn->timeout = realtime + newtimeout;
1417                                         conn->nq.receiveSequence++;
1418                                         if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1419                                                 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1420                                                 conn->receiveMessageLength += length;
1421                                         } else {
1422                                                 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1423                                                                         "Dropping the message!\n", sequence );
1424                                                 conn->receiveMessageLength = 0;
1425                                                 return 1;
1426                                         }
1427                                         if (flags & NETFLAG_EOM)
1428                                         {
1429                                                 conn->reliableMessagesReceived++;
1430                                                 length = conn->receiveMessageLength;
1431                                                 conn->receiveMessageLength = 0;
1432                                                 if (length > 0)
1433                                                 {
1434                                                         if (conn == cls.netcon)
1435                                                         {
1436                                                                 SZ_Clear(&cl_message);
1437                                                                 SZ_Write(&cl_message, conn->receiveMessage, length);
1438                                                                 MSG_BeginReading(&cl_message);
1439                                                         }
1440                                                         else
1441                                                         {
1442                                                                 SZ_Clear(&sv_message);
1443                                                                 SZ_Write(&sv_message, conn->receiveMessage, length);
1444                                                                 MSG_BeginReading(&sv_message);
1445                                                         }
1446                                                         return 2;
1447                                                 }
1448                                         }
1449                                 }
1450                                 else
1451                                         conn->receivedDuplicateCount++;
1452                                 return 1;
1453                         }
1454                 }
1455         }
1456         return 0;
1457 }
1458
1459 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1460 {
1461         crypto_t *crypto;
1462         cls.connect_trying = false;
1463 #ifdef CONFIG_MENU
1464         M_Update_Return_Reason("");
1465 #endif
1466         // the connection request succeeded, stop current connection and set up a new connection
1467         CL_Disconnect();
1468         // if we're connecting to a remote server, shut down any local server
1469         if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1470         {
1471                 SV_LockThreadMutex();
1472                 Host_ShutdownServer ();
1473                 SV_UnlockThreadMutex();
1474         }
1475         // allocate a net connection to keep track of things
1476         cls.netcon = NetConn_Open(mysocket, peeraddress);
1477         crypto = &cls.crypto;
1478         if(crypto && crypto->authenticated)
1479         {
1480                 Crypto_ServerFinishInstance(&cls.netcon->crypto, crypto);
1481                 Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
1482                                 crypto->use_aes ? "Encrypted" : "Authenticated",
1483                                 cls.netcon->address,
1484                                 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1485                                 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1486                                 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1487                                 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1488                                 );
1489         }
1490         Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1491         key_dest = key_game;
1492 #ifdef CONFIG_MENU
1493         m_state = m_none;
1494 #endif
1495         cls.demonum = -1;                       // not in the demo loop now
1496         cls.state = ca_connected;
1497         cls.signon = 0;                         // need all the signon messages before playing
1498         cls.protocol = initialprotocol;
1499         // reset move sequence numbering on this new connection
1500         cls.servermovesequence = 0;
1501         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1502                 Cmd_ForwardStringToServer("new");
1503         if (cls.protocol == PROTOCOL_QUAKE)
1504         {
1505                 // write a keepalive (clc_nop) as it seems to greatly improve the
1506                 // chances of connecting to a netquake server
1507                 sizebuf_t msg;
1508                 unsigned char buf[4];
1509                 memset(&msg, 0, sizeof(msg));
1510                 msg.data = buf;
1511                 msg.maxsize = sizeof(buf);
1512                 MSG_WriteChar(&msg, clc_nop);
1513                 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1514         }
1515 }
1516
1517 int NetConn_IsLocalGame(void)
1518 {
1519         if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1520                 return true;
1521         return false;
1522 }
1523
1524 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1525 {
1526         int n;
1527         int pingtime;
1528         serverlist_entry_t *entry = NULL;
1529
1530         // search the cache for this server and update it
1531         for (n = 0;n < serverlist_cachecount;n++) {
1532                 entry = &serverlist_cache[ n ];
1533                 if (!strcmp(addressstring, entry->info.cname))
1534                         break;
1535         }
1536
1537         if (n == serverlist_cachecount)
1538         {
1539                 // LAN search doesnt require an answer from the master server so we wont
1540                 // know the ping nor will it be initialized already...
1541
1542                 // find a slot
1543                 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1544                         return -1;
1545
1546                 if (serverlist_maxcachecount <= serverlist_cachecount)
1547                 {
1548                         serverlist_maxcachecount += 64;
1549                         serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1550                 }
1551                 entry = &serverlist_cache[n];
1552
1553                 memset(entry, 0, sizeof(*entry));
1554                 // store the data the engine cares about (address and ping)
1555                 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1556                 entry->info.ping = 100000;
1557                 entry->querytime = realtime;
1558                 // if not in the slist menu we should print the server to console
1559                 if (serverlist_consoleoutput)
1560                         Con_Printf("querying %s\n", addressstring);
1561                 ++serverlist_cachecount;
1562         }
1563         // if this is the first reply from this server, count it as having replied
1564         pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1565         pingtime = bound(0, pingtime, 9999);
1566         if (entry->query == SQS_REFRESHING) {
1567                 entry->info.ping = pingtime;
1568                 entry->query = SQS_QUERIED;
1569         } else {
1570                 // convert to unsigned to catch the -1
1571                 // I still dont like this but its better than the old 10000 magic ping number - as in easier to type and read :( [11/8/2007 Black]
1572                 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1573                 serverreplycount++;
1574         }
1575         
1576         // other server info is updated by the caller
1577         return n;
1578 }
1579
1580 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1581 {
1582         serverlist_entry_t *entry = &serverlist_cache[n];
1583         serverlist_info_t *info = &entry->info;
1584         // update description strings for engine menu and console output
1585         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);
1586         dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1587                         (
1588                          info->gameversion != gameversion.integer
1589                          &&
1590                          !(
1591                                     gameversion_min.integer >= 0 // min/max range set by user/mod?
1592                                  && gameversion_max.integer >= 0
1593                                  && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1594                                  && gameversion_max.integer >= info->gameversion
1595                           )
1596                         ) ? '1' : '4',
1597                         info->mod, info->map);
1598         if (entry->query == SQS_QUERIED)
1599         {
1600                 if(!serverlist_paused)
1601                         ServerList_ViewList_Remove(entry);
1602         }
1603         // if not in the slist menu we should print the server to console (if wanted)
1604         else if( serverlist_consoleoutput )
1605                 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1606         // and finally, update the view set
1607         if(!serverlist_paused)
1608                 ServerList_ViewList_Insert( entry );
1609         //      update the entry's state
1610         serverlist_cache[n].query = SQS_QUERIED;
1611 }
1612
1613 // returns true, if it's sensible to continue the processing
1614 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1615         int n;
1616         serverlist_entry_t *entry;
1617
1618         //      ignore the rest of the message if the serverlist is full
1619         if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1620                 return false;
1621         //      also ignore     it      if      we      have already queried    it      (other master server    response)
1622         for( n =        0 ; n   < serverlist_cachecount ; n++   )
1623                 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1624                         break;
1625
1626         if( n < serverlist_cachecount ) {
1627                 // the entry has already been queried once or 
1628                 return true;
1629         }
1630
1631         if (serverlist_maxcachecount <= n)
1632         {
1633                 serverlist_maxcachecount += 64;
1634                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1635         }
1636
1637         entry = &serverlist_cache[n];
1638
1639         memset(entry, 0, sizeof(*entry));
1640         entry->protocol =       protocol;
1641         //      store   the data        the engine cares about (address and     ping)
1642         strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1643
1644         entry->info.isfavorite = isfavorite;
1645         
1646         // no, then reset the ping right away
1647         entry->info.ping = -1;
1648         // we also want to increase the serverlist_cachecount then
1649         serverlist_cachecount++;
1650         serverquerycount++;
1651
1652         entry->query =  SQS_QUERYING;
1653
1654         return true;
1655 }
1656
1657 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1658 {
1659         masterreplycount++;
1660         if (serverlist_consoleoutput)
1661                 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1662         while (length >= 7)
1663         {
1664                 char ipstring [128];
1665
1666                 // IPv4 address
1667                 if (data[0] == '\\')
1668                 {
1669                         unsigned short port = data[5] * 256 + data[6];
1670
1671                         if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1672                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1673
1674                         // move on to next address in packet
1675                         data += 7;
1676                         length -= 7;
1677                 }
1678                 // IPv6 address
1679                 else if (data[0] == '/' && isextended && length >= 19)
1680                 {
1681                         unsigned short port = data[17] * 256 + data[18];
1682
1683                         if (port != 0)
1684                         {
1685 #ifdef WHY_JUST_WHY
1686                                 const char *ifname;
1687                                 char ifnamebuf[16];
1688
1689                                 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1690
1691                                 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1692                                 if (ifname != NULL)
1693                                 {
1694                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1695                                                                 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1696                                                                 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1697                                                                 ifname, port);
1698                                 }
1699                                 else
1700 #endif
1701                                 {
1702                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1703                                                                 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1704                                                                 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1705                                                                 port);
1706                                 }
1707                         }
1708
1709                         // move on to next address in packet
1710                         data += 19;
1711                         length -= 19;
1712                 }
1713                 else
1714                 {
1715                         Con_Print("Error while parsing the server list\n");
1716                         break;
1717                 }
1718
1719                 if (serverlist_consoleoutput && developer_networking.integer)
1720                         Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1721                 
1722                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1723                         break;
1724                 }
1725
1726         }
1727
1728         // begin or resume serverlist queries
1729         serverlist_querysleep = false;
1730         serverlist_querywaittime = realtime + 3;
1731 }
1732
1733 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1734 {
1735         qboolean fromserver;
1736         int ret, c;
1737         const char *s;
1738         char *string, addressstring2[128], ipstring[32];
1739         char stringbuf[16384];
1740         char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1741         size_t sendlength;
1742         char infostringvalue[MAX_INPUTLINE];
1743         char vabuf[1024];
1744
1745         // quakeworld ingame packet
1746         fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1747
1748         // convert the address to a string incase we need it
1749         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1750
1751         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1752         {
1753                 // received a command string - strip off the packaging and put it
1754                 // into our string buffer with NULL termination
1755                 data += 4;
1756                 length -= 4;
1757                 length = min(length, (int)sizeof(stringbuf) - 1);
1758                 memcpy(stringbuf, data, length);
1759                 stringbuf[length] = 0;
1760                 string = stringbuf;
1761
1762                 if (developer_networking.integer)
1763                 {
1764                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1765                         Com_HexDumpToConsole(data, length);
1766                 }
1767
1768                 sendlength = sizeof(senddata) - 4;
1769                 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1770                 {
1771                         case CRYPTO_NOMATCH:
1772                                 // nothing to do
1773                                 break;
1774                         case CRYPTO_MATCH:
1775                                 if(sendlength)
1776                                 {
1777                                         memcpy(senddata, "\377\377\377\377", 4);
1778                                         NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1779                                 }
1780                                 break;
1781                         case CRYPTO_DISCARD:
1782                                 if(sendlength)
1783                                 {
1784                                         memcpy(senddata, "\377\377\377\377", 4);
1785                                         NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1786                                 }
1787                                 return true;
1788                                 break;
1789                         case CRYPTO_REPLACE:
1790                                 string = senddata+4;
1791                                 length = sendlength;
1792                                 break;
1793                 }
1794
1795                 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1796                 {
1797                         int i = 0, j;
1798                         for (j = 0;j < MAX_RCONS;j++)
1799                         {
1800                                 // note: this value from i is used outside the loop too...
1801                                 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1802                                 if(cls.rcon_commands[i][0])
1803                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1804                                                 break;
1805                         }
1806                         if (j < MAX_RCONS)
1807                         {
1808                                 char buf[1500];
1809                                 char argbuf[1500];
1810                                 const char *e;
1811                                 int n;
1812                                 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1813                                 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1814
1815                                 e = strchr(rcon_password.string, ' ');
1816                                 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1817
1818                                 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
1819                                 {
1820                                         int k;
1821                                         buf[45] = ' ';
1822                                         strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1823                                         NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
1824                                         cls.rcon_commands[i][0] = 0;
1825                                         --cls.rcon_trying;
1826
1827                                         for (k = 0;k < MAX_RCONS;k++)
1828                                                 if(cls.rcon_commands[k][0])
1829                                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1830                                                                 break;
1831                                         if(k < MAX_RCONS)
1832                                         {
1833                                                 int l;
1834                                                 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1835                                                 // extend the timeout on other requests as we asked for a challenge
1836                                                 for (l = 0;l < MAX_RCONS;l++)
1837                                                         if(cls.rcon_commands[l][0])
1838                                                                 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1839                                                                         cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1840                                         }
1841
1842                                         return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1843                                 }
1844                         }
1845                 }
1846                 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1847                 {
1848                         // darkplaces or quake3
1849                         char protocolnames[1400];
1850                         Protocol_Names(protocolnames, sizeof(protocolnames));
1851                         Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1852 #ifdef CONFIG_MENU
1853                         M_Update_Return_Reason("Got challenge response");
1854 #endif
1855                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1856                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1857                         // TODO: add userinfo stuff here instead of using NQ commands?
1858                         NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10), peeraddress);
1859                         return true;
1860                 }
1861                 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1862                 {
1863                         // darkplaces or quake3
1864 #ifdef CONFIG_MENU
1865                         M_Update_Return_Reason("Accepted");
1866 #endif
1867                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1868                         return true;
1869                 }
1870                 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1871                 {
1872                         char rejectreason[128];
1873                         cls.connect_trying = false;
1874                         string += 7;
1875                         length = min(length - 7, (int)sizeof(rejectreason) - 1);
1876                         memcpy(rejectreason, string, length);
1877                         rejectreason[length] = 0;
1878 #ifdef CONFIG_MENU
1879                         M_Update_Return_Reason(rejectreason);
1880 #endif
1881                         return true;
1882                 }
1883                 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1884                 {
1885                         serverlist_info_t *info;
1886                         char *p;
1887                         int n;
1888
1889                         string += 15;
1890                         // search the cache for this server and update it
1891                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1892                         if (n < 0)
1893                                 return true;
1894
1895                         info = &serverlist_cache[n].info;
1896                         info->game[0] = 0;
1897                         info->mod[0]  = 0;
1898                         info->map[0]  = 0;
1899                         info->name[0] = 0;
1900                         info->qcstatus[0] = 0;
1901                         info->players[0] = 0;
1902                         info->protocol = -1;
1903                         info->numplayers = 0;
1904                         info->numbots = -1;
1905                         info->maxplayers  = 0;
1906                         info->gameversion = 0;
1907
1908                         p = strchr(string, '\n');
1909                         if(p)
1910                         {
1911                                 *p = 0; // cut off the string there
1912                                 ++p;
1913                         }
1914                         else
1915                                 Con_Printf("statusResponse without players block?\n");
1916
1917                         if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1918                         if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1919                         if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1920                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1921                         if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1922                         if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1923                         if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1924                         if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1925                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1926                         if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1927                         if (p                                                                                         != NULL) strlcpy(info->players, p, sizeof(info->players));
1928                         info->numhumans = info->numplayers - max(0, info->numbots);
1929                         info->freeslots = info->maxplayers - info->numplayers;
1930
1931                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1932
1933                         return true;
1934                 }
1935                 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1936                 {
1937                         serverlist_info_t *info;
1938                         int n;
1939
1940                         string += 13;
1941                         // search the cache for this server and update it
1942                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1943                         if (n < 0)
1944                                 return true;
1945
1946                         info = &serverlist_cache[n].info;
1947                         info->game[0] = 0;
1948                         info->mod[0]  = 0;
1949                         info->map[0]  = 0;
1950                         info->name[0] = 0;
1951                         info->qcstatus[0] = 0;
1952                         info->players[0] = 0;
1953                         info->protocol = -1;
1954                         info->numplayers = 0;
1955                         info->numbots = -1;
1956                         info->maxplayers  = 0;
1957                         info->gameversion = 0;
1958
1959                         if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1960                         if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1961                         if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1962                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1963                         if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1964                         if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1965                         if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1966                         if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1967                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1968                         if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1969                         info->numhumans = info->numplayers - max(0, info->numbots);
1970                         info->freeslots = info->maxplayers - info->numplayers;
1971
1972                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1973
1974                         return true;
1975                 }
1976                 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1977                 {
1978                         // Extract the IP addresses
1979                         data += 18;
1980                         length -= 18;
1981                         NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
1982                         return true;
1983                 }
1984                 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1985                 {
1986                         // Extract the IP addresses
1987                         data += 21;
1988                         length -= 21;
1989                         NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
1990                         return true;
1991                 }
1992                 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1993                 {
1994                         // Extract the IP addresses
1995                         data += 2;
1996                         length -= 2;
1997                         masterreplycount++;
1998                         if (serverlist_consoleoutput)
1999                                 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2000                         while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2001                         {
2002                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2003                                 if (serverlist_consoleoutput && developer_networking.integer)
2004                                         Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2005                                 
2006                                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2007                                         break;
2008                                 }
2009
2010                                 // move on to next address in packet
2011                                 data += 6;
2012                                 length -= 6;
2013                         }
2014                         // begin or resume serverlist queries
2015                         serverlist_querysleep = false;
2016                         serverlist_querywaittime = realtime + 3;
2017                         return true;
2018                 }
2019                 if (!strncmp(string, "extResponse ", 12))
2020                 {
2021                         ++cl_net_extresponse_count;
2022                         if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2023                                 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2024                         cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2025                         dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2026                         return true;
2027                 }
2028                 if (!strncmp(string, "ping", 4))
2029                 {
2030                         if (developer_extra.integer)
2031                                 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2032                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2033                         return true;
2034                 }
2035                 if (!strncmp(string, "ack", 3))
2036                         return true;
2037                 // QuakeWorld compatibility
2038                 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2039                 {
2040                         // challenge message
2041                         Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2042 #ifdef CONFIG_MENU
2043                         M_Update_Return_Reason("Got QuakeWorld challenge response");
2044 #endif
2045                         cls.qw_qport = qport.integer;
2046                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2047                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2048                         NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo), peeraddress);
2049                         return true;
2050                 }
2051                 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2052                 {
2053                         // accept message
2054 #ifdef CONFIG_MENU
2055                         M_Update_Return_Reason("QuakeWorld Accepted");
2056 #endif
2057                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2058                         return true;
2059                 }
2060                 if (length > 2 && !memcmp(string, "n\\", 2))
2061                 {
2062                         serverlist_info_t *info;
2063                         int n;
2064
2065                         // qw server status
2066                         if (serverlist_consoleoutput && developer_networking.integer >= 2)
2067                                 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2068
2069                         string += 1;
2070                         // search the cache for this server and update it
2071                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2072                         if (n < 0)
2073                                 return true;
2074
2075                         info = &serverlist_cache[n].info;
2076                         strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2077                         if ((s = InfoString_GetValue(string, "*gamedir"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
2078                         if ((s = InfoString_GetValue(string, "map"          , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
2079                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2080                         info->protocol = 0;
2081                         info->numplayers = 0; // updated below
2082                         info->numhumans = 0; // updated below
2083                         if ((s = InfoString_GetValue(string, "maxclients"   , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
2084                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2085
2086                         // count active players on server
2087                         // (we could gather more info, but we're just after the number)
2088                         s = strchr(string, '\n');
2089                         if (s)
2090                         {
2091                                 s++;
2092                                 while (s < string + length)
2093                                 {
2094                                         for (;s < string + length && *s != '\n';s++)
2095                                                 ;
2096                                         if (s >= string + length)
2097                                                 break;
2098                                         info->numplayers++;
2099                                         info->numhumans++;
2100                                         s++;
2101                                 }
2102                         }
2103
2104                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2105
2106                         return true;
2107                 }
2108                 if (string[0] == 'n')
2109                 {
2110                         // qw print command
2111                         Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2112                 }
2113                 // we may not have liked the packet, but it was a command packet, so
2114                 // we're done processing this packet now
2115                 return true;
2116         }
2117         // quakeworld ingame packet
2118         if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2119         {
2120                 ret = 0;
2121                 CL_ParseServerMessage();
2122                 return ret;
2123         }
2124         // netquake control packets, supported for compatibility only
2125         if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2126         {
2127                 int n;
2128                 serverlist_info_t *info;
2129
2130                 data += 4;
2131                 length -= 4;
2132                 SZ_Clear(&cl_message);
2133                 SZ_Write(&cl_message, data, length);
2134                 MSG_BeginReading(&cl_message);
2135                 c = MSG_ReadByte(&cl_message);
2136                 switch (c)
2137                 {
2138                 case CCREP_ACCEPT:
2139                         if (developer_extra.integer)
2140                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2141                         if (cls.connect_trying)
2142                         {
2143                                 lhnetaddress_t clientportaddress;
2144                                 clientportaddress = *peeraddress;
2145                                 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2146                                 // extra ProQuake stuff
2147                                 if (length >= 6)
2148                                         cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2149                                 else
2150                                         cls.proquake_servermod = 0;
2151                                 if (length >= 7)
2152                                         cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2153                                 else
2154                                         cls.proquake_serverversion = 0;
2155                                 if (length >= 8)
2156                                         cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2157                                 else
2158                                         cls.proquake_serverflags = 0;
2159                                 if (cls.proquake_servermod == 1)
2160                                         Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2161                                 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2162                                 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2163 #ifdef CONFIG_MENU
2164                                 M_Update_Return_Reason("Accepted");
2165 #endif
2166                                 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2167                         }
2168                         break;
2169                 case CCREP_REJECT:
2170                         if (developer_extra.integer)
2171                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2172                         cls.connect_trying = false;
2173 #ifdef CONFIG_MENU
2174                         M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2175 #endif
2176                         break;
2177                 case CCREP_SERVER_INFO:
2178                         if (developer_extra.integer)
2179                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2180                         // LordHavoc: because the quake server may report weird addresses
2181                         // we just ignore it and keep the real address
2182                         MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2183                         // search the cache for this server and update it
2184                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2185                         if (n < 0)
2186                                 break;
2187
2188                         info = &serverlist_cache[n].info;
2189                         strlcpy(info->game, "Quake", sizeof(info->game));
2190                         strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2191                         strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2192                         strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2193                         info->numplayers = MSG_ReadByte(&cl_message);
2194                         info->maxplayers = MSG_ReadByte(&cl_message);
2195                         info->protocol = MSG_ReadByte(&cl_message);
2196
2197                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2198
2199                         break;
2200                 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2201                         if (developer_extra.integer)
2202                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2203
2204                         Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2205                         break;
2206                 case CCREP_PLAYER_INFO:
2207                         // we got a CCREP_PLAYER_INFO??
2208                         //if (developer_extra.integer)
2209                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2210                         break;
2211                 case CCREP_RULE_INFO:
2212                         // we got a CCREP_RULE_INFO??
2213                         //if (developer_extra.integer)
2214                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2215                         break;
2216                 default:
2217                         break;
2218                 }
2219                 SZ_Clear(&cl_message);
2220                 // we may not have liked the packet, but it was a valid control
2221                 // packet, so we're done processing this packet now
2222                 return true;
2223         }
2224         ret = 0;
2225         if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2226                 CL_ParseServerMessage();
2227         return ret;
2228 }
2229
2230 void NetConn_QueryQueueFrame(void)
2231 {
2232         int index;
2233         int queries;
2234         int maxqueries;
2235         double timeouttime;
2236         static double querycounter = 0;
2237
2238         if(!net_slist_pause.integer && serverlist_paused)
2239                 ServerList_RebuildViewList();
2240         serverlist_paused = net_slist_pause.integer != 0;
2241
2242         if (serverlist_querysleep)
2243                 return;
2244
2245         // apply a cool down time after master server replies,
2246         // to avoid messing up the ping times on the servers
2247         if (serverlist_querywaittime > realtime)
2248                 return;
2249
2250         // each time querycounter reaches 1.0 issue a query
2251         querycounter += cl.realframetime * net_slist_queriespersecond.value;
2252         maxqueries = (int)querycounter;
2253         maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2254         querycounter -= maxqueries;
2255
2256         if( maxqueries == 0 ) {
2257                 return;
2258         }
2259
2260         //      scan serverlist and issue queries as needed
2261         serverlist_querysleep = true;
2262
2263         timeouttime     = realtime - net_slist_timeout.value;
2264         for( index = 0, queries = 0 ;   index   < serverlist_cachecount &&      queries < maxqueries    ; index++ )
2265         {
2266                 serverlist_entry_t *entry = &serverlist_cache[ index ];
2267                 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2268                 {
2269                         continue;
2270                 }
2271
2272                 serverlist_querysleep   = false;
2273                 if( entry->querycounter !=      0 && entry->querytime > timeouttime     )
2274                 {
2275                         continue;
2276                 }
2277
2278                 if( entry->querycounter !=      (unsigned) net_slist_maxtries.integer )
2279                 {
2280                         lhnetaddress_t  address;
2281                         int socket;
2282
2283                         LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2284                         if      (entry->protocol == PROTOCOL_QUAKEWORLD)
2285                         {
2286                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
2287                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2288                         }
2289                         else
2290                         {
2291                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
2292                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2293                         }
2294
2295                         //      update the entry fields
2296                         entry->querytime = realtime;
2297                         entry->querycounter++;
2298
2299                         // if not in the slist menu we should print the server to console
2300                         if (serverlist_consoleoutput)
2301                                 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2302
2303                         queries++;
2304                 }
2305                 else
2306                 {
2307                         // have we tried to refresh this server?
2308                         if( entry->query == SQS_REFRESHING ) {
2309                                 // yes, so update the reply count (since its not responding anymore)
2310                                 serverreplycount--;
2311                                 if(!serverlist_paused)
2312                                         ServerList_ViewList_Remove(entry);
2313                         }
2314                         entry->query = SQS_TIMEDOUT;
2315                 }
2316         }
2317 }
2318
2319 void NetConn_ClientFrame(void)
2320 {
2321         int i, length;
2322         lhnetaddress_t peeraddress;
2323         unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2324         NetConn_UpdateSockets();
2325         if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2326         {
2327 #ifdef CONFIG_MENU
2328                 if (cls.connect_remainingtries == 0)
2329                         M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2330 #endif
2331                 cls.connect_nextsendtime = realtime + 1;
2332                 cls.connect_remainingtries--;
2333                 if (cls.connect_remainingtries <= -10)
2334                 {
2335                         cls.connect_trying = false;
2336 #ifdef CONFIG_MENU
2337                         M_Update_Return_Reason("Connect: Failed");
2338 #endif
2339                         return;
2340                 }
2341                 // try challenge first (newer DP server or QW)
2342                 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2343                 // then try netquake as a fallback (old server, or netquake)
2344                 SZ_Clear(&cl_message);
2345                 // save space for the header, filled in later
2346                 MSG_WriteLong(&cl_message, 0);
2347                 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2348                 MSG_WriteString(&cl_message, "QUAKE");
2349                 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2350                 // extended proquake stuff
2351                 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2352                 // this version matches ProQuake 3.40, the first version to support
2353                 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2354                 // higher clients, so we pretend we are that version...
2355                 MSG_WriteByte(&cl_message, 34); // version * 10
2356                 MSG_WriteByte(&cl_message, 0); // flags
2357                 MSG_WriteLong(&cl_message, 0); // password
2358                 // write the packetsize now...
2359                 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2360                 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2361                 SZ_Clear(&cl_message);
2362         }
2363         for (i = 0;i < cl_numsockets;i++)
2364         {
2365                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2366                 {
2367 //                      R_TimeReport("clientreadnetwork");
2368                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2369 //                      R_TimeReport("clientparsepacket");
2370                 }
2371         }
2372         NetConn_QueryQueueFrame();
2373         if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2374         {
2375                 Con_Print("Connection timed out\n");
2376                 CL_Disconnect();
2377                 SV_LockThreadMutex();
2378                 Host_ShutdownServer ();
2379                 SV_UnlockThreadMutex();
2380         }
2381 }
2382
2383 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2384 {
2385         int i;
2386         char c;
2387         for (i = 0;i < bufferlength - 1;i++)
2388         {
2389                 do
2390                 {
2391                         c = rand () % (127 - 33) + 33;
2392                 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2393                 buffer[i] = c;
2394         }
2395         buffer[i] = 0;
2396 }
2397
2398 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2399 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2400 {
2401         prvm_prog_t *prog = SVVM_prog;
2402         char qcstatus[256];
2403         unsigned int nb_clients = 0, nb_bots = 0, i;
2404         int length;
2405         char teambuf[3];
2406         const char *crypto_idstring;
2407         const char *str;
2408
2409         // How many clients are there?
2410         for (i = 0;i < (unsigned int)svs.maxclients;i++)
2411         {
2412                 if (svs.clients[i].active)
2413                 {
2414                         nb_clients++;
2415                         if (!svs.clients[i].netconnection)
2416                                 nb_bots++;
2417                 }
2418         }
2419
2420         *qcstatus = 0;
2421         str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2422         if(str && *str)
2423         {
2424                 char *p;
2425                 const char *q;
2426                 p = qcstatus;
2427                 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2428                         if(*q != '\\' && *q != '\n')
2429                                 *p++ = *q;
2430                 *p = 0;
2431         }
2432
2433         /// \TODO: we should add more information for the full status string
2434         crypto_idstring = Crypto_GetInfoResponseDataString();
2435         length = dpsnprintf(out_msg, out_size,
2436                                                 "\377\377\377\377%s\x0A"
2437                                                 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2438                                                 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2439                                                 "%s%s"
2440                                                 "%s%s"
2441                                                 "%s%s"
2442                                                 "%s",
2443                                                 fullstatus ? "statusResponse" : "infoResponse",
2444                                                 gamename, com_modname, gameversion.integer, svs.maxclients,
2445                                                 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2446                                                 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2447                                                 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2448                                                 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2449                                                 fullstatus ? "\n" : "");
2450
2451         // Make sure it fits in the buffer
2452         if (length < 0)
2453                 goto bad;
2454
2455         if (fullstatus)
2456         {
2457                 char *ptr;
2458                 int left;
2459                 int savelength;
2460
2461                 savelength = length;
2462
2463                 ptr = out_msg + length;
2464                 left = (int)out_size - length;
2465
2466                 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2467                 {
2468                         client_t *cl = &svs.clients[i];
2469                         if (cl->active)
2470                         {
2471                                 int nameind, cleanind, pingvalue;
2472                                 char curchar;
2473                                 char cleanname [sizeof(cl->name)];
2474                                 const char *str;
2475                                 prvm_edict_t *ed;
2476
2477                                 // Remove all characters '"' and '\' in the player name
2478                                 nameind = 0;
2479                                 cleanind = 0;
2480                                 do
2481                                 {
2482                                         curchar = cl->name[nameind++];
2483                                         if (curchar != '"' && curchar != '\\')
2484                                         {
2485                                                 cleanname[cleanind++] = curchar;
2486                                                 if (cleanind == sizeof(cleanname) - 1)
2487                                                         break;
2488                                         }
2489                                 } while (curchar != '\0');
2490                                 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2491
2492                                 pingvalue = (int)(cl->ping * 1000.0f);
2493                                 if(cl->netconnection)
2494                                         pingvalue = bound(1, pingvalue, 9999);
2495                                 else
2496                                         pingvalue = 0;
2497
2498                                 *qcstatus = 0;
2499                                 ed = PRVM_EDICT_NUM(i + 1);
2500                                 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2501                                 if(str && *str)
2502                                 {
2503                                         char *p;
2504                                         const char *q;
2505                                         p = qcstatus;
2506                                         for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2507                                                 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2508                                                         *p++ = *q;
2509                                         *p = 0;
2510                                 }
2511
2512                                 if ((gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) && (teamplay.integer > 0))
2513                                 {
2514                                         if(cl->frags == -666) // spectator
2515                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2516                                         else if(cl->colors == 0x44) // red team
2517                                                 strlcpy(teambuf, " 1", sizeof(teambuf));
2518                                         else if(cl->colors == 0xDD) // blue team
2519                                                 strlcpy(teambuf, " 2", sizeof(teambuf));
2520                                         else if(cl->colors == 0xCC) // yellow team
2521                                                 strlcpy(teambuf, " 3", sizeof(teambuf));
2522                                         else if(cl->colors == 0x99) // pink team
2523                                                 strlcpy(teambuf, " 4", sizeof(teambuf));
2524                                         else
2525                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2526                                 }
2527                                 else
2528                                         *teambuf = 0;
2529
2530                                 // note: team number is inserted according to SoF2 protocol
2531                                 if(*qcstatus)
2532                                         length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2533                                                                                 qcstatus,
2534                                                                                 pingvalue,
2535                                                                                 teambuf,
2536                                                                                 cleanname);
2537                                 else
2538                                         length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2539                                                                                 cl->frags,
2540                                                                                 pingvalue,
2541                                                                                 teambuf,
2542                                                                                 cleanname);
2543
2544                                 if(length < 0)
2545                                 {
2546                                         // out of space?
2547                                         // turn it into an infoResponse!
2548                                         out_msg[savelength] = 0;
2549                                         memcpy(out_msg + 4, "infoResponse\x0A", 13);
2550                                         memmove(out_msg + 17, out_msg + 19, savelength - 19);
2551                                         break;
2552                                 }
2553                                 left -= length;
2554                                 ptr += length;
2555                         }
2556                 }
2557         }
2558
2559         return true;
2560
2561 bad:
2562         return false;
2563 }
2564
2565 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2566 {
2567         size_t floodslotnum, bestfloodslotnum;
2568         double bestfloodtime;
2569         lhnetaddress_t noportpeeraddress;
2570         // see if this is a connect flood
2571         noportpeeraddress = *peeraddress;
2572         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2573         bestfloodslotnum = 0;
2574         bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2575         for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2576         {
2577                 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2578                 {
2579                         bestfloodtime = floodlist[floodslotnum].lasttime;
2580                         bestfloodslotnum = floodslotnum;
2581                 }
2582                 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2583                 {
2584                         // this address matches an ongoing flood address
2585                         if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2586                         {
2587                                 if(renew)
2588                                 {
2589                                         // renew the ban on this address so it does not expire
2590                                         // until the flood has subsided
2591                                         floodlist[floodslotnum].lasttime = realtime;
2592                                 }
2593                                 //Con_Printf("Flood detected!\n");
2594                                 return true;
2595                         }
2596                         // the flood appears to have subsided, so allow this
2597                         bestfloodslotnum = floodslotnum; // reuse the same slot
2598                         break;
2599                 }
2600         }
2601         // begin a new timeout on this address
2602         floodlist[bestfloodslotnum].address = noportpeeraddress;
2603         floodlist[bestfloodslotnum].lasttime = realtime;
2604         //Con_Printf("Flood detection initiated!\n");
2605         return false;
2606 }
2607
2608 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2609 {
2610         size_t floodslotnum;
2611         lhnetaddress_t noportpeeraddress;
2612         // see if this is a connect flood
2613         noportpeeraddress = *peeraddress;
2614         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2615         for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2616         {
2617                 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2618                 {
2619                         // this address matches an ongoing flood address
2620                         // remove the ban
2621                         floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2622                         floodlist[floodslotnum].lasttime = 0;
2623                         //Con_Printf("Flood cleared!\n");
2624                 }
2625         }
2626 }
2627
2628 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2629
2630 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2631 {
2632         char mdfourbuf[16];
2633         long t1, t2;
2634
2635         t1 = (long) time(NULL);
2636         t2 = strtol(s, NULL, 0);
2637         if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2638                 return false;
2639
2640         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2641                 return false;
2642
2643         return !memcmp(mdfourbuf, hash, 16);
2644 }
2645
2646 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2647 {
2648         char mdfourbuf[16];
2649         int i;
2650
2651         if(slen < (int)(sizeof(challenge[0].string)) - 1)
2652                 return false;
2653
2654         // validate the challenge
2655         for (i = 0;i < MAX_CHALLENGES;i++)
2656                 if(challenge[i].time > 0)
2657                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2658                                 break;
2659         // if the challenge is not recognized, drop the packet
2660         if (i == MAX_CHALLENGES)
2661                 return false;
2662
2663         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2664                 return false;
2665
2666         if(memcmp(mdfourbuf, hash, 16))
2667                 return false;
2668
2669         // unmark challenge to prevent replay attacks
2670         challenge[i].time = 0;
2671
2672         return true;
2673 }
2674
2675 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2676 {
2677         return !strcmp(password, hash);
2678 }
2679
2680 /// returns a string describing the user level, or NULL for auth failure
2681 static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
2682 {
2683         const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2684         static char buf[MAX_INPUTLINE];
2685         qboolean hasquotes;
2686         qboolean restricted = false;
2687         qboolean have_usernames = false;
2688         char vabuf[1024];
2689
2690         userpass_start = rcon_password.string;
2691         while((userpass_end = strchr(userpass_start, ' ')))
2692         {
2693                 have_usernames = true;
2694                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2695                 if(buf[0])
2696                         if(comparator(peeraddress, buf, password, cs, cslen))
2697                                 goto allow;
2698                 userpass_start = userpass_end + 1;
2699         }
2700         if(userpass_start[0])
2701         {
2702                 userpass_end = userpass_start + strlen(userpass_start);
2703                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2704                         goto allow;
2705         }
2706
2707         restricted = true;
2708         have_usernames = false;
2709         userpass_start = rcon_restricted_password.string;
2710         while((userpass_end = strchr(userpass_start, ' ')))
2711         {
2712                 have_usernames = true;
2713                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2714                 if(buf[0])
2715                         if(comparator(peeraddress, buf, password, cs, cslen))
2716                                 goto check;
2717                 userpass_start = userpass_end + 1;
2718         }
2719         if(userpass_start[0])
2720         {
2721                 userpass_end = userpass_start + strlen(userpass_start);
2722                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2723                         goto check;
2724         }
2725         
2726         return NULL; // DENIED
2727
2728 check:
2729         for(text = s; text != endpos; ++text)
2730                 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2731                         return NULL; // block possible exploits against the parser/alias expansion
2732
2733         while(s != endpos)
2734         {
2735                 size_t l = strlen(s);
2736                 if(l)
2737                 {
2738                         hasquotes = (strchr(s, '"') != NULL);
2739                         // sorry, we can't allow these substrings in wildcard expressions,
2740                         // as they can mess with the argument counts
2741                         text = rcon_restricted_commands.string;
2742                         while(COM_ParseToken_Console(&text))
2743                         {
2744                                 // com_token now contains a pattern to check for...
2745                                 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2746                                 {
2747                                         if(!hasquotes)
2748                                                 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2749                                                         goto match;
2750                                 }
2751                                 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2752                                 {
2753                                         if(!strcmp(com_token, s))
2754                                                 goto match;
2755                                 }
2756                                 else // single-arg expression? must match the beginning of the command
2757                                 {
2758                                         if(!strcmp(com_token, s))
2759                                                 goto match;
2760                                         if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2761                                                 goto match;
2762                                 }
2763                         }
2764                         // if we got here, nothing matched!
2765                         return NULL;
2766                 }
2767 match:
2768                 s += l + 1;
2769         }
2770
2771 allow:
2772         userpass_startpass = strchr(userpass_start, ':');
2773         if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2774                 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2775
2776         return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2777 }
2778
2779 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2780 {
2781         if(userlevel)
2782         {
2783                 // looks like a legitimate rcon command with the correct password
2784                 const char *s_ptr = s;
2785                 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2786                 while(s_ptr != endpos)
2787                 {
2788                         size_t l = strlen(s_ptr);
2789                         if(l)
2790                                 Con_Printf(" %s;", s_ptr);
2791                         s_ptr += l + 1;
2792                 }
2793                 Con_Printf("\n");
2794
2795                 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2796                         Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2797                 while(s != endpos)
2798                 {
2799                         size_t l = strlen(s);
2800                         if(l)
2801                         {
2802                                 client_t *host_client_save = host_client;
2803                                 Cmd_ExecuteString(s, src_command, true);
2804                                 host_client = host_client_save;
2805                                 // in case it is a command that changes host_client (like restart)
2806                         }
2807                         s += l + 1;
2808                 }
2809                 Con_Rcon_Redirect_End();
2810         }
2811         else
2812         {
2813                 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2814         }
2815 }
2816
2817 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2818 {
2819         int i, ret, clientnum, best;
2820         double besttime;
2821         client_t *client;
2822         char *s, *string, response[1400], addressstring2[128];
2823         static char stringbuf[16384]; // server only
2824         qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2825         char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2826         size_t sendlength, response_len;
2827         char infostringvalue[MAX_INPUTLINE];
2828         char vabuf[1024];
2829
2830         if (!sv.active)
2831                 return false;
2832
2833         // convert the address to a string incase we need it
2834         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2835
2836         // see if we can identify the sender as a local player
2837         // (this is necessary for rcon to send a reliable reply if the client is
2838         //  actually on the server, not sending remotely)
2839         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2840                 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2841                         break;
2842         if (i == svs.maxclients)
2843                 host_client = NULL;
2844
2845         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2846         {
2847                 // received a command string - strip off the packaging and put it
2848                 // into our string buffer with NULL termination
2849                 data += 4;
2850                 length -= 4;
2851                 length = min(length, (int)sizeof(stringbuf) - 1);
2852                 memcpy(stringbuf, data, length);
2853                 stringbuf[length] = 0;
2854                 string = stringbuf;
2855
2856                 if (developer_extra.integer)
2857                 {
2858                         Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2859                         Com_HexDumpToConsole(data, length);
2860                 }
2861
2862                 sendlength = sizeof(senddata) - 4;
2863                 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2864                 {
2865                         case CRYPTO_NOMATCH:
2866                                 // nothing to do
2867                                 break;
2868                         case CRYPTO_MATCH:
2869                                 if(sendlength)
2870                                 {
2871                                         memcpy(senddata, "\377\377\377\377", 4);
2872                                         NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2873                                 }
2874                                 break;
2875                         case CRYPTO_DISCARD:
2876                                 if(sendlength)
2877                                 {
2878                                         memcpy(senddata, "\377\377\377\377", 4);
2879                                         NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2880                                 }
2881                                 return true;
2882                                 break;
2883                         case CRYPTO_REPLACE:
2884                                 string = senddata+4;
2885                                 length = sendlength;
2886                                 break;
2887                 }
2888
2889                 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2890                 {
2891                         for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2892                         {
2893                                 if(challenge[i].time > 0)
2894                                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2895                                                 break;
2896                                 if (besttime > challenge[i].time)
2897                                         besttime = challenge[best = i].time;
2898                         }
2899                         // if we did not find an exact match, choose the oldest and
2900                         // update address and string
2901                         if (i == MAX_CHALLENGES)
2902                         {
2903                                 i = best;
2904                                 challenge[i].address = *peeraddress;
2905                                 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2906                         }
2907                         else
2908                         {
2909                                 // flood control: drop if requesting challenge too often
2910                                 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2911                                         return true;
2912                         }
2913                         challenge[i].time = realtime;
2914                         // send the challenge
2915                         dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2916                         response_len = strlen(response) + 1;
2917                         Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2918                         NetConn_Write(mysocket, response, response_len, peeraddress);
2919                         return true;
2920                 }
2921                 if (length > 8 && !memcmp(string, "connect\\", 8))
2922                 {
2923                         crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2924                         string += 7;
2925                         length -= 7;
2926
2927                         if(crypto && crypto->authenticated)
2928                         {
2929                                 // no need to check challenge
2930                                 if(crypto_developer.integer)
2931                                 {
2932                                         Con_Printf("%s connection to %s is being established: client is %s@%.*s, I am %.*s@%.*s\n",
2933                                                         crypto->use_aes ? "Encrypted" : "Authenticated",
2934                                                         addressstring2,
2935                                                         crypto->client_idfp[0] ? crypto->client_idfp : "-",
2936                                                         crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
2937                                                         crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
2938                                                         crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
2939                                                   );
2940                                 }
2941                         }
2942                         else
2943                         {
2944                                 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
2945                                 {
2946                                         // validate the challenge
2947                                         for (i = 0;i < MAX_CHALLENGES;i++)
2948                                                 if(challenge[i].time > 0)
2949                                                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
2950                                                                 break;
2951                                         // if the challenge is not recognized, drop the packet
2952                                         if (i == MAX_CHALLENGES)
2953                                                 return true;
2954                                 }
2955                         }
2956
2957                         if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
2958                                 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
2959
2960                         if(!(islocal || sv_public.integer > -2))
2961                         {
2962                                 if (developer_extra.integer)
2963                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
2964                                 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
2965                                 return true;
2966                         }
2967
2968                         // check engine protocol
2969                         if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
2970                         {
2971                                 if (developer_extra.integer)
2972                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
2973                                 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
2974                                 return true;
2975                         }
2976
2977                         // see if this is a duplicate connection request or a disconnected
2978                         // client who is rejoining to the same client slot
2979                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2980                         {
2981                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
2982                                 {
2983                                         // this is a known client...
2984                                         if(crypto && crypto->authenticated)
2985                                         {
2986                                                 // reject if changing key!
2987                                                 if(client->netconnection->crypto.authenticated)
2988                                                 {
2989                                                         if(
2990                                                                         strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
2991                                                                         ||
2992                                                                         strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
2993                                                                         ||
2994                                                                         strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
2995                                                                         ||
2996                                                                         strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
2997                                                           )
2998                                                         {
2999                                                                 if (developer_extra.integer)
3000                                                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3001                                                                 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3002                                                                 return true;
3003                                                         }
3004                                                 }
3005                                         }
3006                                         else
3007                                         {
3008                                                 // reject if downgrading!
3009                                                 if(client->netconnection->crypto.authenticated)
3010                                                 {
3011                                                         if (developer_extra.integer)
3012                                                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3013                                                         NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3014                                                         return true;
3015                                                 }
3016                                         }
3017                                         if (client->begun)
3018                                         {
3019                                                 // client crashed and is coming back,
3020                                                 // keep their stuff intact
3021                                                 if (developer_extra.integer)
3022                                                         Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3023                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3024                                                 if(crypto && crypto->authenticated)
3025                                                         Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
3026                                                 SV_SendServerinfo(client);
3027                                         }
3028                                         else
3029                                         {
3030                                                 // client is still trying to connect,
3031                                                 // so we send a duplicate reply
3032                                                 if (developer_extra.integer)
3033                                                         Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3034                                                 if(crypto && crypto->authenticated)
3035                                                         Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
3036                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3037                                         }
3038                                         return true;
3039                                 }
3040                         }
3041
3042                         if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3043                                 return true;
3044
3045                         // find an empty client slot for this new client
3046                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3047                         {
3048                                 netconn_t *conn;
3049                                 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3050                                 {
3051                                         // allocated connection
3052                                         if (developer_extra.integer)
3053                                                 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3054                                         NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3055                                         // now set up the client
3056                                         if(crypto && crypto->authenticated)
3057                                                 Crypto_ServerFinishInstance(&conn->crypto, crypto);
3058                                         SV_ConnectClient(clientnum, conn);
3059                                         NetConn_Heartbeat(1);
3060                                         return true;
3061                                 }
3062                         }
3063
3064                         // no empty slots found - server is full
3065                         if (developer_extra.integer)
3066                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3067                         NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3068
3069                         return true;
3070                 }
3071                 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3072                 {
3073                         const char *challenge = NULL;
3074
3075                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3076                                 return true;
3077
3078                         // If there was a challenge in the getinfo message
3079                         if (length > 8 && string[7] == ' ')
3080                                 challenge = string + 8;
3081
3082                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3083                         {
3084                                 if (developer_extra.integer)
3085                                         Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3086                                 NetConn_WriteString(mysocket, response, peeraddress);
3087                         }
3088                         return true;
3089                 }
3090                 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3091                 {
3092                         const char *challenge = NULL;
3093
3094                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3095                                 return true;
3096
3097                         // If there was a challenge in the getinfo message
3098                         if (length > 10 && string[9] == ' ')
3099                                 challenge = string + 10;
3100
3101                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3102                         {
3103                                 if (developer_extra.integer)
3104                                         Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3105                                 NetConn_WriteString(mysocket, response, peeraddress);
3106                         }
3107                         return true;
3108                 }
3109                 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3110                 {
3111                         char *password = string + 20;
3112                         char *timeval = string + 37;
3113                         char *s = strchr(timeval, ' ');
3114                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3115                         const char *userlevel;
3116
3117                         if(rcon_secure.integer > 1)
3118                                 return true;
3119
3120                         if(!s)
3121                                 return true; // invalid packet
3122                         ++s;
3123
3124                         userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3125                         RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3126                         return true;
3127                 }
3128                 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3129                 {
3130                         char *password = string + 25;
3131                         char *challenge = string + 42;
3132                         char *s = strchr(challenge, ' ');
3133                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3134                         const char *userlevel;
3135                         if(!s)
3136                                 return true; // invalid packet
3137                         ++s;
3138
3139                         userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3140                         RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3141                         return true;
3142                 }
3143                 if (length >= 5 && !memcmp(string, "rcon ", 5))
3144                 {
3145                         int i;
3146                         char *s = string + 5;
3147                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3148                         char password[64];
3149
3150                         if(rcon_secure.integer > 0)
3151                                 return true;
3152
3153                         for (i = 0;!ISWHITESPACE(*s);s++)
3154                                 if (i < (int)sizeof(password) - 1)
3155                                         password[i++] = *s;
3156                         if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3157                                 ++s;
3158                         password[i] = 0;
3159                         if (!ISWHITESPACE(password[0]))
3160                         {
3161                                 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3162                                 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3163                         }
3164                         return true;
3165                 }
3166                 if (!strncmp(string, "extResponse ", 12))
3167                 {
3168                         ++sv_net_extresponse_count;
3169                         if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3170                                 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3171                         sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3172                         dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3173                         return true;
3174                 }
3175                 if (!strncmp(string, "ping", 4))
3176                 {
3177                         if (developer_extra.integer)
3178                                 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3179                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3180                         return true;
3181                 }
3182                 if (!strncmp(string, "ack", 3))
3183                         return true;
3184                 // we may not have liked the packet, but it was a command packet, so
3185                 // we're done processing this packet now
3186                 return true;
3187         }
3188         // netquake control packets, supported for compatibility only, and only
3189         // when running game protocols that are normally served via this connection
3190         // protocol
3191         // (this protects more modern protocols against being used for
3192         //  Quake packet flood Denial Of Service attacks)
3193         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)
3194         {
3195                 int c;
3196                 int protocolnumber;
3197                 const char *protocolname;
3198                 data += 4;
3199                 length -= 4;
3200                 SZ_Clear(&sv_message);
3201                 SZ_Write(&sv_message, data, length);
3202                 MSG_BeginReading(&sv_message);
3203                 c = MSG_ReadByte(&sv_message);
3204                 switch (c)
3205                 {
3206                 case CCREQ_CONNECT:
3207                         if (developer_extra.integer)
3208                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3209                         if(!(islocal || sv_public.integer > -2))
3210                         {
3211                                 if (developer_extra.integer)
3212                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3213                                 SZ_Clear(&sv_message);
3214                                 // save space for the header, filled in later
3215                                 MSG_WriteLong(&sv_message, 0);
3216                                 MSG_WriteByte(&sv_message, CCREP_REJECT);
3217                                 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3218                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3219                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3220                                 SZ_Clear(&sv_message);
3221                                 break;
3222                         }
3223
3224                         protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3225                         protocolnumber = MSG_ReadByte(&sv_message);
3226                         if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3227                         {
3228                                 if (developer_extra.integer)
3229                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3230                                 SZ_Clear(&sv_message);
3231                                 // save space for the header, filled in later
3232                                 MSG_WriteLong(&sv_message, 0);
3233                     &nb