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