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