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