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