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