Netconn: when an encrypted connection is used, randomly set one or more of three...
[xonotic/darkplaces.git] / netconn.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 */
22
23 #include "quakedef.h"
24 #include "thread.h"
25 #include "lhnet.h"
26
27 // for secure rcon authentication
28 #include "hmac.h"
29 #include "mdfour.h"
30 #include <time.h>
31
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
34
35 // note this defaults on for dedicated servers, off for listen servers
36 cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
37 cvar_t sv_public_rejectreason = {0, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
39 extern cvar_t sv_status_privacy;
40
41 static cvar_t sv_masters [] =
42 {
43         {CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
44         {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
45         {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
46         {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
47         {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
48         {0, "sv_masterextra2", "64.22.107.125", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
49         {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
50 #ifdef SUPPORTIPV6
51         {0, "sv_masterextra4", "[2a03:4000:2:225::51:334d]:27950", "dpmaster.sudo.rm-f.org - default master server 4 (admin: divVerent)"}, // admin: divVerent
52 #endif
53         {0, NULL, NULL, NULL}
54 };
55
56 #ifdef CONFIG_MENU
57 static cvar_t sv_qwmasters [] =
58 {
59         {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
60         {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
61         {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
62         {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
63         {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
64         {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
65         {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
66         {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
67         {0, "sv_qwmasterextra5", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
68         {0, NULL, NULL, NULL}
69 };
70 #endif
71
72 static double nextheartbeattime = 0;
73
74 sizebuf_t cl_message;
75 sizebuf_t sv_message;
76 static unsigned char cl_message_buf[NET_MAXMESSAGE];
77 static unsigned char sv_message_buf[NET_MAXMESSAGE];
78 char cl_readstring[MAX_INPUTLINE];
79 char sv_readstring[MAX_INPUTLINE];
80
81 cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
82 cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
83 cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
84 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
85 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."};
86 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."};
87 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."};
88 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."};
89 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
90 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
91
92 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)"};
93 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)"};
94 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)"};
95 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
96 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)"};
97 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
98 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"};
99 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)"};
100 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"};
101 static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
102 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
103 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"};
104 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"};
105 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"};
106 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
107 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)"};
108 extern cvar_t rcon_secure;
109 extern cvar_t rcon_secure_challengetimeout;
110
111 double masterquerytime = -1000;
112 int masterquerycount = 0;
113 int masterreplycount = 0;
114 int serverquerycount = 0;
115 int serverreplycount = 0;
116
117 challenge_t challenge[MAX_CHALLENGES];
118
119 #ifdef CONFIG_MENU
120 /// this is only false if there are still servers left to query
121 static qboolean serverlist_querysleep = true;
122 static qboolean serverlist_paused = false;
123 /// this is pushed a second or two ahead of realtime whenever a master server
124 /// reply is received, to avoid issuing queries while master replies are still
125 /// flooding in (which would make a mess of the ping times)
126 static double serverlist_querywaittime = 0;
127 #endif
128
129 static int cl_numsockets;
130 static lhnetsocket_t *cl_sockets[16];
131 static int sv_numsockets;
132 static lhnetsocket_t *sv_sockets[16];
133
134 netconn_t *netconn_list = NULL;
135 mempool_t *netconn_mempool = NULL;
136 void *netconn_mutex = NULL;
137
138 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
139 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
140 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
141 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
142
143 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
144 int cl_net_extresponse_count = 0;
145 int cl_net_extresponse_last = 0;
146
147 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
148 int sv_net_extresponse_count = 0;
149 int sv_net_extresponse_last = 0;
150
151 #ifdef CONFIG_MENU
152 // ServerList interface
153 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
154 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
155
156 serverlist_infofield_t serverlist_sortbyfield;
157 int serverlist_sortflags;
158
159 int serverlist_viewcount = 0;
160 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
161
162 int serverlist_maxcachecount = 0;
163 int serverlist_cachecount = 0;
164 serverlist_entry_t *serverlist_cache = NULL;
165
166 qboolean serverlist_consoleoutput;
167
168 static int nFavorites = 0;
169 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
170 static int nFavorites_idfp = 0;
171 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
172
173 void NetConn_UpdateFavorites(void)
174 {
175         const char *p;
176         nFavorites = 0;
177         nFavorites_idfp = 0;
178         p = net_slist_favorites.string;
179         while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
180         {
181                 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
182                 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
183                 // (if v6 address contains port, it must start with '[')
184                 {
185                         strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
186                         ++nFavorites_idfp;
187                 }
188                 else 
189                 {
190                         if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
191                                 ++nFavorites;
192                 }
193         }
194 }
195
196 /// helper function to insert a value into the viewset
197 /// spare entries will be removed
198 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
199 {
200     int i;
201         if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
202                 i = serverlist_viewcount++;
203         } else {
204                 i = SERVERLIST_VIEWLISTSIZE - 1;
205         }
206
207         for( ; i > index ; i-- )
208                 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
209
210         serverlist_viewlist[index] = (int)(entry - serverlist_cache);
211 }
212
213 /// we suppose serverlist_viewcount to be valid, ie > 0
214 static void _ServerList_ViewList_Helper_Remove( int index )
215 {
216         serverlist_viewcount--;
217         for( ; index < serverlist_viewcount ; index++ )
218                 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
219 }
220
221 /// \returns true if A should be inserted before B
222 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
223 {
224         int result = 0; // > 0 if for numbers A > B and for text if A < B
225
226         if( serverlist_sortflags & SLSF_CATEGORIES )
227         {
228                 result = A->info.category - B->info.category;
229                 if (result != 0)
230                         return result < 0;
231         }
232
233         if( serverlist_sortflags & SLSF_FAVORITES )
234         {
235                 if(A->info.isfavorite != B->info.isfavorite)
236                         return A->info.isfavorite;
237         }
238         
239         switch( serverlist_sortbyfield ) {
240                 case SLIF_PING:
241                         result = A->info.ping - B->info.ping;
242                         break;
243                 case SLIF_MAXPLAYERS:
244                         result = A->info.maxplayers - B->info.maxplayers;
245                         break;
246                 case SLIF_NUMPLAYERS:
247                         result = A->info.numplayers - B->info.numplayers;
248                         break;
249                 case SLIF_NUMBOTS:
250                         result = A->info.numbots - B->info.numbots;
251                         break;
252                 case SLIF_NUMHUMANS:
253                         result = A->info.numhumans - B->info.numhumans;
254                         break;
255                 case SLIF_FREESLOTS:
256                         result = A->info.freeslots - B->info.freeslots;
257                         break;
258                 case SLIF_PROTOCOL:
259                         result = A->info.protocol - B->info.protocol;
260                         break;
261                 case SLIF_CNAME:
262                         result = strcmp( B->info.cname, A->info.cname );
263                         break;
264                 case SLIF_GAME:
265                         result = strcasecmp( B->info.game, A->info.game );
266                         break;
267                 case SLIF_MAP:
268                         result = strcasecmp( B->info.map, A->info.map );
269                         break;
270                 case SLIF_MOD:
271                         result = strcasecmp( B->info.mod, A->info.mod );
272                         break;
273                 case SLIF_NAME:
274                         result = strcasecmp( B->info.name, A->info.name );
275                         break;
276                 case SLIF_QCSTATUS:
277                         result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
278                         break;
279                 case SLIF_CATEGORY:
280                         result = A->info.category - B->info.category;
281                         break;
282                 case SLIF_ISFAVORITE:
283                         result = !!B->info.isfavorite - !!A->info.isfavorite;
284                         break;
285                 default:
286                         Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
287                         break;
288         }
289
290         if (result != 0)
291         {
292                 if( serverlist_sortflags & SLSF_DESCENDING )
293                         return result > 0;
294                 else
295                         return result < 0;
296         }
297
298         // if the chosen sort key is identical, sort by index
299         // (makes this a stable sort, so that later replies from servers won't
300         //  shuffle the servers around when they have the same ping)
301         return A < B;
302 }
303
304 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
305 {
306         // This should actually be done with some intermediate and end-of-function return
307         switch( op ) {
308                 case SLMO_LESS:
309                         return A < B;
310                 case SLMO_LESSEQUAL:
311                         return A <= B;
312                 case SLMO_EQUAL:
313                         return A == B;
314                 case SLMO_GREATER:
315                         return A > B;
316                 case SLMO_NOTEQUAL:
317                         return A != B;
318                 case SLMO_GREATEREQUAL:
319                 case SLMO_CONTAINS:
320                 case SLMO_NOTCONTAIN:
321                 case SLMO_STARTSWITH:
322                 case SLMO_NOTSTARTSWITH:
323                         return A >= B;
324                 default:
325                         Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
326                         return false;
327         }
328 }
329
330 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
331 {
332         int i;
333         char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
334         COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
335         for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
336                 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
337         bufferA[i] = 0;
338         for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
339                 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
340         bufferB[i] = 0;
341
342         // Same here, also using an intermediate & final return would be more appropriate
343         // A info B mask
344         switch( op ) {
345                 case SLMO_CONTAINS:
346                         return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
347                 case SLMO_NOTCONTAIN:
348                         return !*bufferB || !strstr( bufferA, bufferB );
349                 case SLMO_STARTSWITH:
350                         //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
351                         return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
352                 case SLMO_NOTSTARTSWITH:
353                         return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
354                 case SLMO_LESS:
355                         return strcmp( bufferA, bufferB ) < 0;
356                 case SLMO_LESSEQUAL:
357                         return strcmp( bufferA, bufferB ) <= 0;
358                 case SLMO_EQUAL:
359                         return strcmp( bufferA, bufferB ) == 0;
360                 case SLMO_GREATER:
361                         return strcmp( bufferA, bufferB ) > 0;
362                 case SLMO_NOTEQUAL:
363                         return strcmp( bufferA, bufferB ) != 0;
364                 case SLMO_GREATEREQUAL:
365                         return strcmp( bufferA, bufferB ) >= 0;
366                 default:
367                         Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
368                         return false;
369         }
370 }
371
372 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
373 {
374         if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
375                 return false;
376         if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
377                 return false;
378         if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
379                 return false;
380         if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
381                 return false;
382         if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
383                 return false;
384         if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
385                 return false;
386         if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
387                 return false;
388         if( *mask->info.cname
389                 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
390                 return false;
391         if( *mask->info.game
392                 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
393                 return false;
394         if( *mask->info.mod
395                 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
396                 return false;
397         if( *mask->info.map
398                 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
399                 return false;
400         if( *mask->info.name
401                 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
402                 return false;
403         if( *mask->info.qcstatus
404                 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
405                 return false;
406         if( *mask->info.players
407                 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
408                 return false;
409         if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
410                 return false;
411         if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
412                 return false;
413         return true;
414 }
415
416 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
417 {
418         int start, end, mid, i;
419         lhnetaddress_t addr;
420
421         // reject incompatible servers
422         if(
423                 entry->info.gameversion != gameversion.integer
424                 &&
425                 !(
426                            gameversion_min.integer >= 0 // min/max range set by user/mod?
427                         && gameversion_max.integer >= 0
428                         && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
429                         && gameversion_max.integer >= entry->info.gameversion
430                  )
431         )
432                 return;
433
434         // refresh the "favorite" status
435         entry->info.isfavorite = false;
436         if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
437         {
438                 char idfp[FP64_SIZE+1];
439                 for(i = 0; i < nFavorites; ++i)
440                 {
441                         if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
442                         {
443                                 entry->info.isfavorite = true;
444                                 break;
445                         }
446                 }
447                 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
448                 {
449                         for(i = 0; i < nFavorites_idfp; ++i)
450                         {
451                                 if(!strcmp(idfp, favorites_idfp[i]))
452                                 {
453                                         entry->info.isfavorite = true;
454                                         break;
455                                 }
456                         }
457                 }
458         }
459
460         // refresh the "category"
461         entry->info.category = MR_GetServerListEntryCategory(entry);
462
463         // FIXME: change this to be more readable (...)
464         // now check whether it passes through the masks
465         for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
466                 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
467                         return;
468
469         for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
470                 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
471                         break;
472         if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
473                 return;
474
475         if( !serverlist_viewcount ) {
476                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
477                 return;
478         }
479         // ok, insert it, we just need to find out where exactly:
480
481         // two special cases
482         // check whether to insert it as new first item
483         if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
484                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
485                 return;
486         } // check whether to insert it as new last item
487         else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
488                 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
489                 return;
490         }
491         start = 0;
492         end = serverlist_viewcount - 1;
493         while( end > start + 1 )
494         {
495                 mid = (start + end) / 2;
496                 // test the item that lies in the middle between start and end
497                 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
498                         // the item has to be in the upper half
499                         end = mid;
500                 else
501                         // the item has to be in the lower half
502                         start = mid;
503         }
504         _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
505 }
506
507 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
508 {
509         int i;
510         for( i = 0; i < serverlist_viewcount; i++ )
511         {
512                 if (ServerList_GetViewEntry(i) == entry)
513                 {
514                         _ServerList_ViewList_Helper_Remove(i);
515                         break;
516                 }
517         }
518 }
519
520 void ServerList_RebuildViewList(void)
521 {
522         int i;
523
524         serverlist_viewcount = 0;
525         for( i = 0 ; i < serverlist_cachecount ; i++ ) {
526                 serverlist_entry_t *entry = &serverlist_cache[i];
527                 // also display entries that are currently being refreshed [11/8/2007 Black]
528                 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
529                         ServerList_ViewList_Insert( entry );
530         }
531 }
532
533 void ServerList_ResetMasks(void)
534 {
535         int i;
536
537         memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
538         memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
539         // numbots needs to be compared to -1 to always succeed
540         for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
541                 serverlist_andmasks[i].info.numbots = -1;
542         for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
543                 serverlist_ormasks[i].info.numbots = -1;
544 }
545
546 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
547 {
548         int i;
549         int numplayers = 0, maxplayers = 0;
550         for (i = 0;i < serverlist_cachecount;i++)
551         {
552                 if (serverlist_cache[i].query == SQS_QUERIED)
553                 {
554                         numplayers += serverlist_cache[i].info.numhumans;
555                         maxplayers += serverlist_cache[i].info.maxplayers;
556                 }
557         }
558         *numplayerspointer = numplayers;
559         *maxplayerspointer = maxplayers;
560 }
561
562 #if 0
563 static void _ServerList_Test(void)
564 {
565         int i;
566         if (serverlist_maxcachecount <= 1024)
567         {
568                 serverlist_maxcachecount = 1024;
569                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
570         }
571         for( i = 0 ; i < 1024 ; i++ ) {
572                 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
573                 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
574                 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
575                 serverlist_cache[serverlist_cachecount].finished = true;
576                 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 );
577                 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
578                 serverlist_cachecount++;
579         }
580 }
581 #endif
582
583 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
584 {
585         masterquerytime = realtime;
586         masterquerycount = 0;
587         masterreplycount = 0;
588         if( resetcache ) {
589                 serverquerycount = 0;
590                 serverreplycount = 0;
591                 serverlist_cachecount = 0;
592                 serverlist_viewcount = 0;
593                 serverlist_maxcachecount = 0;
594                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
595         } else {
596                 // refresh all entries
597                 int n;
598                 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
599                         serverlist_entry_t *entry = &serverlist_cache[ n ];
600                         entry->query = SQS_REFRESHING;
601                         entry->querycounter = 0;
602                 }
603         }
604         serverlist_consoleoutput = consoleoutput;
605
606         //_ServerList_Test();
607
608         NetConn_QueryMasters(querydp, queryqw);
609 }
610 #endif
611
612 // rest
613
614 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
615 {
616         int length;
617         int i;
618         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
619                 Thread_LockMutex(netconn_mutex);
620         length = LHNET_Read(mysocket, data, maxlength, peeraddress);
621         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
622                 Thread_UnlockMutex(netconn_mutex);
623         if (length == 0)
624                 return 0;
625         if (cl_netpacketloss_receive.integer)
626                 for (i = 0;i < cl_numsockets;i++)
627                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
628                                 return 0;
629         if (developer_networking.integer)
630         {
631                 char addressstring[128], addressstring2[128];
632                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
633                 if (length > 0)
634                 {
635                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
636                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
637                         Com_HexDumpToConsole((unsigned char *)data, length);
638                 }
639                 else
640                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
641         }
642         return length;
643 }
644
645 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
646 {
647         int ret;
648         int i;
649         if (cl_netpacketloss_send.integer)
650                 for (i = 0;i < cl_numsockets;i++)
651                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
652                                 return length;
653         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
654                 Thread_LockMutex(netconn_mutex);
655         ret = LHNET_Write(mysocket, data, length, peeraddress);
656         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
657                 Thread_UnlockMutex(netconn_mutex);
658         if (developer_networking.integer)
659         {
660                 char addressstring[128], addressstring2[128];
661                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
662                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
663                 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)");
664                 Com_HexDumpToConsole((unsigned char *)data, length);
665         }
666         return ret;
667 }
668
669 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
670 {
671         // note this does not include the trailing NULL because we add that in the parser
672         return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
673 }
674
675 qboolean NetConn_CanSend(netconn_t *conn)
676 {
677         conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
678         conn->outgoing_netgraph[conn->outgoing_packetcounter].time            = realtime;
679         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
680         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
681         conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
682         conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime       = conn->cleartime;
683         if (realtime > conn->cleartime)
684                 return true;
685         else
686         {
687                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
688                 return false;
689         }
690 }
691
692 void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
693 {
694         double bursttime = burstsize / (double)rate;
695
696         // delay later packets to obey rate limit
697         if (*cleartime < realtime - bursttime)
698                 *cleartime = realtime - bursttime;
699         *cleartime = *cleartime + len / (double)rate;
700
701         // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
702         if (net_test.integer)
703         {
704                 if (*cleartime < realtime)
705                         *cleartime = realtime;
706         }
707 }
708
709 static int NetConn_AddCryptoFlag(crypto_t *crypto)
710 {
711         // HACK: if an encrypted connection is used, randomly set some unused
712         // flags. When AES encryption is enabled, that will make resends differ
713         // from the original, so that e.g. substring filters in a router/IPS
714         // are unlikely to match a second time. See also "startkeylogger".
715         int flag = 0;
716         if (crypto->authenticated)
717         {
718                 // Let's always set at least one of the bits.
719                 int r = rand() % 7 + 1;
720                 if (r & 1)
721                         flag |= NETFLAG_CRYPTO0;
722                 if (r & 2)
723                         flag |= NETFLAG_CRYPTO1;
724                 if (r & 4)
725                         flag |= NETFLAG_CRYPTO2;
726         }
727         return flag;
728 }
729
730 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
731 {
732         int totallen = 0;
733         unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
734         unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
735
736         // if this packet was supposedly choked, but we find ourselves sending one
737         // anyway, make sure the size counting starts at zero
738         // (this mostly happens on level changes and disconnects and such)
739         if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
740                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
741
742         conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
743
744         if (protocol == PROTOCOL_QUAKEWORLD)
745         {
746                 int packetLen;
747                 qboolean sendreliable;
748
749                 // note that it is ok to send empty messages to the qw server,
750                 // otherwise it won't respond to us at all
751
752                 sendreliable = false;
753                 // if the remote side dropped the last reliable message, resend it
754                 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
755                         sendreliable = true;
756                 // if the reliable transmit buffer is empty, copy the current message out
757                 if (!conn->sendMessageLength && conn->message.cursize)
758                 {
759                         memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
760                         conn->sendMessageLength = conn->message.cursize;
761                         SZ_Clear(&conn->message); // clear the message buffer
762                         conn->qw.reliable_sequence ^= 1;
763                         sendreliable = true;
764                 }
765                 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
766                 StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
767                 // last received unreliable packet number, and last received reliable packet number (0 or 1)
768                 StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
769                 packetLen = 8;
770                 conn->outgoing_unreliable_sequence++;
771                 // client sends qport in every packet
772                 if (conn == cls.netcon)
773                 {
774                         *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
775                         packetLen += 2;
776                         // also update cls.qw_outgoing_sequence
777                         cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
778                 }
779                 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
780                 {
781                         Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
782                         return -1;
783                 }
784
785                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
786
787                 // add the reliable message if there is one
788                 if (sendreliable)
789                 {
790                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
791                         memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
792                         packetLen += conn->sendMessageLength;
793                         conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
794                 }
795
796                 // add the unreliable message if possible
797                 if (packetLen + data->cursize <= 1400)
798                 {
799                         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
800                         memcpy(sendbuffer + packetLen, data->data, data->cursize);
801                         packetLen += data->cursize;
802                 }
803
804                 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
805
806                 conn->packetsSent++;
807                 conn->unreliableMessagesSent++;
808
809                 totallen += packetLen + 28;
810         }
811         else
812         {
813                 unsigned int packetLen;
814                 unsigned int dataLen;
815                 unsigned int eom;
816                 const void *sendme;
817                 size_t sendmelen;
818
819                 // if a reliable message fragment has been lost, send it again
820                 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
821                 {
822                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
823                         {
824                                 dataLen = conn->sendMessageLength;
825                                 eom = NETFLAG_EOM;
826                         }
827                         else
828                         {
829                                 dataLen = MAX_PACKETFRAGMENT;
830                                 eom = 0;
831                         }
832
833                         packetLen = NET_HEADERSIZE + dataLen;
834
835                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
836                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
837                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
838
839                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
840
841                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
842                         if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
843                         {
844                                 conn->lastSendTime = realtime;
845                                 conn->packetsReSent++;
846                         }
847
848                         totallen += sendmelen + 28;
849                 }
850
851                 // if we have a new reliable message to send, do so
852                 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
853                 {
854                         if (conn->message.cursize > (int)sizeof(conn->sendMessage))
855                         {
856                                 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
857                                 conn->message.overflowed = true;
858                                 return -1;
859                         }
860
861                         if (developer_networking.integer && conn == cls.netcon)
862                         {
863                                 Con_Print("client sending reliable message to server:\n");
864                                 SZ_HexDumpToConsole(&conn->message);
865                         }
866
867                         memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
868                         conn->sendMessageLength = conn->message.cursize;
869                         SZ_Clear(&conn->message);
870
871                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
872                         {
873                                 dataLen = conn->sendMessageLength;
874                                 eom = NETFLAG_EOM;
875                         }
876                         else
877                         {
878                                 dataLen = MAX_PACKETFRAGMENT;
879                                 eom = 0;
880                         }
881
882                         packetLen = NET_HEADERSIZE + dataLen;
883
884                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
885                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
886                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
887
888                         conn->nq.sendSequence++;
889
890                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
891
892                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
893                         if(sendme)
894                                 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
895
896                         conn->lastSendTime = realtime;
897                         conn->packetsSent++;
898                         conn->reliableMessagesSent++;
899
900                         totallen += sendmelen + 28;
901                 }
902
903                 // if we have an unreliable message to send, do so
904                 if (data->cursize)
905                 {
906                         packetLen = NET_HEADERSIZE + data->cursize;
907
908                         if (packetLen > (int)sizeof(sendbuffer))
909                         {
910                                 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
911                                 return -1;
912                         }
913
914                         StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
915                         StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
916                         memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
917
918                         conn->outgoing_unreliable_sequence++;
919
920                         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
921
922                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
923                         if(sendme)
924                                 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
925
926                         conn->packetsSent++;
927                         conn->unreliableMessagesSent++;
928
929                         totallen += sendmelen + 28;
930                 }
931         }
932
933         NetConn_UpdateCleartime(&conn->cleartime, cl_rate.integer, cl_rate_burstsize.integer, totallen);
934
935         return 0;
936 }
937
938 qboolean NetConn_HaveClientPorts(void)
939 {
940         return !!cl_numsockets;
941 }
942
943 qboolean NetConn_HaveServerPorts(void)
944 {
945         return !!sv_numsockets;
946 }
947
948 void NetConn_CloseClientPorts(void)
949 {
950         for (;cl_numsockets > 0;cl_numsockets--)
951                 if (cl_sockets[cl_numsockets - 1])
952                         LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
953 }
954
955 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
956 {
957         lhnetaddress_t address;
958         lhnetsocket_t *s;
959         int success;
960         char addressstring2[1024];
961         if (addressstring && addressstring[0])
962                 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
963         else
964                 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
965         if (success)
966         {
967                 if ((s = LHNET_OpenSocket_Connectionless(&address)))
968                 {
969                         cl_sockets[cl_numsockets++] = s;
970                         LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
971                         if (addresstype != LHNETADDRESSTYPE_LOOP)
972                                 Con_Printf("Client opened a socket on address %s\n", addressstring2);
973                 }
974                 else
975                 {
976                         LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
977                         Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
978                 }
979         }
980         else
981                 Con_Printf("Client unable to parse address %s\n", addressstring);
982 }
983
984 void NetConn_OpenClientPorts(void)
985 {
986         int port;
987         NetConn_CloseClientPorts();
988
989         SV_LockThreadMutex(); // FIXME recursive?
990         Crypto_LoadKeys(); // client sockets
991         SV_UnlockThreadMutex();
992
993         port = bound(0, cl_netport.integer, 65535);
994         if (cl_netport.integer != port)
995                 Cvar_SetValueQuick(&cl_netport, port);
996         if(port == 0)
997                 Con_Printf("Client using an automatically assigned port\n");
998         else
999                 Con_Printf("Client using port %i\n", port);
1000         NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
1001         NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
1002 #ifdef SUPPORTIPV6
1003         NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1004 #endif
1005 }
1006
1007 void NetConn_CloseServerPorts(void)
1008 {
1009         for (;sv_numsockets > 0;sv_numsockets--)
1010                 if (sv_sockets[sv_numsockets - 1])
1011                         LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1012 }
1013
1014 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1015 {
1016         lhnetaddress_t address;
1017         lhnetsocket_t *s;
1018         int port;
1019         char addressstring2[1024];
1020         int success;
1021
1022         for (port = defaultport; port <= defaultport + range; port++)
1023         {
1024                 if (addressstring && addressstring[0])
1025                         success = LHNETADDRESS_FromString(&address, addressstring, port);
1026                 else
1027                         success = LHNETADDRESS_FromPort(&address, addresstype, port);
1028                 if (success)
1029                 {
1030                         if ((s = LHNET_OpenSocket_Connectionless(&address)))
1031                         {
1032                                 sv_sockets[sv_numsockets++] = s;
1033                                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1034                                 if (addresstype != LHNETADDRESSTYPE_LOOP)
1035                                         Con_Printf("Server listening on address %s\n", addressstring2);
1036                                 return true;
1037                         }
1038                         else
1039                         {
1040                                 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1041                                 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
1042                         }
1043                 }
1044                 else
1045                 {
1046                         Con_Printf("Server unable to parse address %s\n", addressstring);
1047                         // if it cant parse one address, it wont be able to parse another for sure
1048                         return false;
1049                 }
1050         }
1051         return false;
1052 }
1053
1054 void NetConn_OpenServerPorts(int opennetports)
1055 {
1056         int port;
1057         NetConn_CloseServerPorts();
1058
1059         SV_LockThreadMutex(); // FIXME recursive?
1060         Crypto_LoadKeys(); // server sockets
1061         SV_UnlockThreadMutex();
1062
1063         NetConn_UpdateSockets();
1064         port = bound(0, sv_netport.integer, 65535);
1065         if (port == 0)
1066                 port = 26000;
1067         Con_Printf("Server using port %i\n", port);
1068         if (sv_netport.integer != port)
1069                 Cvar_SetValueQuick(&sv_netport, port);
1070         if (cls.state != ca_dedicated)
1071                 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1072         if (opennetports)
1073         {
1074 #ifdef SUPPORTIPV6
1075                 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1076                 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1077 #else
1078                 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1079 #endif
1080         }
1081         if (sv_numsockets == 0)
1082                 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1083 }
1084
1085 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1086 {
1087         int i, a = LHNETADDRESS_GetAddressType(address);
1088         for (i = 0;i < cl_numsockets;i++)
1089                 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1090                         return cl_sockets[i];
1091         return NULL;
1092 }
1093
1094 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1095 {
1096         int i, a = LHNETADDRESS_GetAddressType(address);
1097         for (i = 0;i < sv_numsockets;i++)
1098                 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1099                         return sv_sockets[i];
1100         return NULL;
1101 }
1102
1103 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1104 {
1105         netconn_t *conn;
1106         conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1107         conn->mysocket = mysocket;
1108         conn->peeraddress = *peeraddress;
1109         conn->lastMessageTime = realtime;
1110         conn->message.data = conn->messagedata;
1111         conn->message.maxsize = sizeof(conn->messagedata);
1112         conn->message.cursize = 0;
1113         // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1114         // reduce effectiveness of connection request floods
1115         conn->timeout = realtime + net_connecttimeout.value;
1116         LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1117         conn->next = netconn_list;
1118         netconn_list = conn;
1119         return conn;
1120 }
1121
1122 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1123 void NetConn_Close(netconn_t *conn)
1124 {
1125         netconn_t *c;
1126         // remove connection from list
1127
1128         // allow the client to reconnect immediately
1129         NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1130
1131         if (conn == netconn_list)
1132                 netconn_list = conn->next;
1133         else
1134         {
1135                 for (c = netconn_list;c;c = c->next)
1136                 {
1137                         if (c->next == conn)
1138                         {
1139                                 c->next = conn->next;
1140                                 break;
1141                         }
1142                 }
1143                 // not found in list, we'll avoid crashing here...
1144                 if (!c)
1145                         return;
1146         }
1147         // free connection
1148         Mem_Free(conn);
1149 }
1150
1151 static int clientport = -1;
1152 static int clientport2 = -1;
1153 static int hostport = -1;
1154 void NetConn_UpdateSockets(void)
1155 {
1156         int i, j;
1157
1158         // TODO add logic to automatically close sockets if needed
1159         LHNET_DefaultDSCP(net_tos_dscp.integer);
1160
1161         if (cls.state != ca_dedicated)
1162         {
1163                 if (clientport2 != cl_netport.integer)
1164                 {
1165                         clientport2 = cl_netport.integer;
1166                         if (cls.state == ca_connected)
1167                                 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1168                 }
1169                 if (cls.state == ca_disconnected && clientport != clientport2)
1170                 {
1171                         clientport = clientport2;
1172                         NetConn_CloseClientPorts();
1173                 }
1174                 if (cl_numsockets == 0)
1175                         NetConn_OpenClientPorts();
1176         }
1177
1178         if (hostport != sv_netport.integer)
1179         {
1180                 hostport = sv_netport.integer;
1181                 if (sv.active)
1182                         Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1183         }
1184
1185         for (j = 0;j < MAX_RCONS;j++)
1186         {
1187                 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1188                 if(cls.rcon_commands[i][0])
1189                 {
1190                         if(realtime > cls.rcon_timeout[i])
1191                         {
1192                                 char s[128];
1193                                 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1194                                 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1195                                 cls.rcon_commands[i][0] = 0;
1196                                 --cls.rcon_trying;
1197                                 break;
1198                         }
1199                 }
1200         }
1201 }
1202
1203 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1204 {
1205         int originallength = length;
1206         unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1207         unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1208         unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1209         if (length < 8)
1210                 return 0;
1211
1212         if (protocol == PROTOCOL_QUAKEWORLD)
1213         {
1214                 int sequence, sequence_ack;
1215                 int reliable_ack, reliable_message;
1216                 int count;
1217                 //int qport;
1218
1219                 sequence = LittleLong(*((int *)(data + 0)));
1220                 sequence_ack = LittleLong(*((int *)(data + 4)));
1221                 data += 8;
1222                 length -= 8;
1223
1224                 if (conn != cls.netcon)
1225                 {
1226                         // server only
1227                         if (length < 2)
1228                                 return 0;
1229                         // 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?)
1230                         //qport = LittleShort(*((int *)(data + 8)));
1231                         data += 2;
1232                         length -= 2;
1233                 }
1234
1235                 conn->packetsReceived++;
1236                 reliable_message = (sequence >> 31) & 1;
1237                 reliable_ack = (sequence_ack >> 31) & 1;
1238                 sequence &= ~(1<<31);
1239                 sequence_ack &= ~(1<<31);
1240                 if (sequence <= conn->qw.incoming_sequence)
1241                 {
1242                         //Con_DPrint("Got a stale datagram\n");
1243                         return 0;
1244                 }
1245                 count = sequence - (conn->qw.incoming_sequence + 1);
1246                 if (count > 0)
1247                 {
1248                         conn->droppedDatagrams += count;
1249                         //Con_DPrintf("Dropped %u datagram(s)\n", count);
1250                         while (count--)
1251                         {
1252                                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1253                                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1254                                 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1255                                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1256                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1257                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1258                         }
1259                 }
1260                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1261                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1262                 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1263                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1264                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1265                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1266                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1267
1268                 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1269                 if (net_test.integer)
1270                 {
1271                         if (conn->cleartime < realtime)
1272                                 conn->cleartime = realtime;
1273                 }
1274
1275                 if (reliable_ack == conn->qw.reliable_sequence)
1276                 {
1277                         // received, now we will be able to send another reliable message
1278                         conn->sendMessageLength = 0;
1279                         conn->reliableMessagesReceived++;
1280                 }
1281                 conn->qw.incoming_sequence = sequence;
1282                 if (conn == cls.netcon)
1283                         cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1284                 conn->qw.incoming_acknowledged = sequence_ack;
1285                 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1286                 if (reliable_message)
1287                         conn->qw.incoming_reliable_sequence ^= 1;
1288                 conn->lastMessageTime = realtime;
1289                 conn->timeout = realtime + newtimeout;
1290                 conn->unreliableMessagesReceived++;
1291                 if (conn == cls.netcon)
1292                 {
1293                         SZ_Clear(&cl_message);
1294                         SZ_Write(&cl_message, data, length);
1295                         MSG_BeginReading(&cl_message);
1296                 }
1297                 else
1298                 {
1299                         SZ_Clear(&sv_message);
1300                         SZ_Write(&sv_message, data, length);
1301                         MSG_BeginReading(&sv_message);
1302                 }
1303                 return 2;
1304         }
1305         else
1306         {
1307                 unsigned int count;
1308                 unsigned int flags;
1309                 unsigned int sequence;
1310                 size_t qlength;
1311                 const void *sendme;
1312                 size_t sendmelen;
1313
1314                 originallength = length;
1315                 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1316                 if(!data)
1317                         return 0;
1318                 if(length < 8)
1319                         return 0;
1320
1321                 qlength = (unsigned int)BuffBigLong(data);
1322                 flags = qlength & ~NETFLAG_LENGTH_MASK;
1323                 qlength &= NETFLAG_LENGTH_MASK;
1324                 // control packets were already handled
1325                 if (!(flags & NETFLAG_CTL) && qlength == length)
1326                 {
1327                         sequence = BuffBigLong(data + 4);
1328                         conn->packetsReceived++;
1329                         data += 8;
1330                         length -= 8;
1331                         if (flags & NETFLAG_UNRELIABLE)
1332                         {
1333                                 if (sequence >= conn->nq.unreliableReceiveSequence)
1334                                 {
1335                                         if (sequence > conn->nq.unreliableReceiveSequence)
1336                                         {
1337                                                 count = sequence - conn->nq.unreliableReceiveSequence;
1338                                                 conn->droppedDatagrams += count;
1339                                                 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1340                                                 while (count--)
1341                                                 {
1342                                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1343                                                         conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1344                                                         conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1345                                                         conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1346                                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1347                                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1348                                                 }
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 = originallength + 28;
1354                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1355                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1356                                         NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1357
1358                                         conn->nq.unreliableReceiveSequence = sequence + 1;
1359                                         conn->lastMessageTime = realtime;
1360                                         conn->timeout = realtime + newtimeout;
1361                                         conn->unreliableMessagesReceived++;
1362                                         if (length > 0)
1363                                         {
1364                                                 if (conn == cls.netcon)
1365                                                 {
1366                                                         SZ_Clear(&cl_message);
1367                                                         SZ_Write(&cl_message, data, length);
1368                                                         MSG_BeginReading(&cl_message);
1369                                                 }
1370                                                 else
1371                                                 {
1372                                                         SZ_Clear(&sv_message);
1373                                                         SZ_Write(&sv_message, data, length);
1374                                                         MSG_BeginReading(&sv_message);
1375                                                 }
1376                                                 return 2;
1377                                         }
1378                                 }
1379                                 //else
1380                                 //      Con_DPrint("Got a stale datagram\n");
1381                                 return 1;
1382                         }
1383                         else if (flags & NETFLAG_ACK)
1384                         {
1385                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1386                                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1387
1388                                 if (sequence == (conn->nq.sendSequence - 1))
1389                                 {
1390                                         if (sequence == conn->nq.ackSequence)
1391                                         {
1392                                                 conn->nq.ackSequence++;
1393                                                 if (conn->nq.ackSequence != conn->nq.sendSequence)
1394                                                         Con_DPrint("ack sequencing error\n");
1395                                                 conn->lastMessageTime = realtime;
1396                                                 conn->timeout = realtime + newtimeout;
1397                                                 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1398                                                 {
1399                                                         unsigned int packetLen;
1400                                                         unsigned int dataLen;
1401                                                         unsigned int eom;
1402
1403                                                         conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1404                                                         memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1405
1406                                                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1407                                                         {
1408                                                                 dataLen = conn->sendMessageLength;
1409                                                                 eom = NETFLAG_EOM;
1410                                                         }
1411                                                         else
1412                                                         {
1413                                                                 dataLen = MAX_PACKETFRAGMENT;
1414                                                                 eom = 0;
1415                                                         }
1416
1417                                                         packetLen = NET_HEADERSIZE + dataLen;
1418
1419                                                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1420                                                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1421                                                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1422
1423                                                         conn->nq.sendSequence++;
1424
1425                                                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1426                                                         if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
1427                                                         {
1428                                                                 conn->lastSendTime = realtime;
1429                                                                 conn->packetsSent++;
1430                                                         }
1431                                                 }
1432                                                 else
1433                                                         conn->sendMessageLength = 0;
1434                                         }
1435                                         //else
1436                                         //      Con_DPrint("Duplicate ACK received\n");
1437                                 }
1438                                 //else
1439                                 //      Con_DPrint("Stale ACK received\n");
1440                                 return 1;
1441                         }
1442                         else if (flags & NETFLAG_DATA)
1443                         {
1444                                 unsigned char temppacket[8];
1445                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   += originallength + 28;
1446                                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1447
1448                                 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        += 8 + 28;
1449
1450                                 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1451                                 StoreBigLong(temppacket + 4, sequence);
1452                                 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1453                                 if(sendme)
1454                                         NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
1455                                 if (sequence == conn->nq.receiveSequence)
1456                                 {
1457                                         conn->lastMessageTime = realtime;
1458                                         conn->timeout = realtime + newtimeout;
1459                                         conn->nq.receiveSequence++;
1460                                         if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1461                                                 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1462                                                 conn->receiveMessageLength += length;
1463                                         } else {
1464                                                 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1465                                                                         "Dropping the message!\n", sequence );
1466                                                 conn->receiveMessageLength = 0;
1467                                                 return 1;
1468                                         }
1469                                         if (flags & NETFLAG_EOM)
1470                                         {
1471                                                 conn->reliableMessagesReceived++;
1472                                                 length = conn->receiveMessageLength;
1473                                                 conn->receiveMessageLength = 0;
1474                                                 if (length > 0)
1475                                                 {
1476                                                         if (conn == cls.netcon)
1477                                                         {
1478                                                                 SZ_Clear(&cl_message);
1479                                                                 SZ_Write(&cl_message, conn->receiveMessage, length);
1480                                                                 MSG_BeginReading(&cl_message);
1481                                                         }
1482                                                         else
1483                                                         {
1484                                                                 SZ_Clear(&sv_message);
1485                                                                 SZ_Write(&sv_message, conn->receiveMessage, length);
1486                                                                 MSG_BeginReading(&sv_message);
1487                                                         }
1488                                                         return 2;
1489                                                 }
1490                                         }
1491                                 }
1492                                 else
1493                                         conn->receivedDuplicateCount++;
1494                                 return 1;
1495                         }
1496                 }
1497         }
1498         return 0;
1499 }
1500
1501 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1502 {
1503         crypto_t *crypto;
1504         cls.connect_trying = false;
1505 #ifdef CONFIG_MENU
1506         M_Update_Return_Reason("");
1507 #endif
1508         // the connection request succeeded, stop current connection and set up a new connection
1509         CL_Disconnect();
1510         // if we're connecting to a remote server, shut down any local server
1511         if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1512         {
1513                 SV_LockThreadMutex();
1514                 Host_ShutdownServer ();
1515                 SV_UnlockThreadMutex();
1516         }
1517         // allocate a net connection to keep track of things
1518         cls.netcon = NetConn_Open(mysocket, peeraddress);
1519         crypto = &cls.crypto;
1520         if(crypto && crypto->authenticated)
1521         {
1522                 Crypto_ServerFinishInstance(&cls.netcon->crypto, crypto);
1523                 Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
1524                                 crypto->use_aes ? "Encrypted" : "Authenticated",
1525                                 cls.netcon->address,
1526                                 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1527                                 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1528                                 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1529                                 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1530                                 );
1531         }
1532         Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1533         key_dest = key_game;
1534 #ifdef CONFIG_MENU
1535         m_state = m_none;
1536 #endif
1537         cls.demonum = -1;                       // not in the demo loop now
1538         cls.state = ca_connected;
1539         cls.signon = 0;                         // need all the signon messages before playing
1540         cls.protocol = initialprotocol;
1541         // reset move sequence numbering on this new connection
1542         cls.servermovesequence = 0;
1543         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1544                 Cmd_ForwardStringToServer("new");
1545         if (cls.protocol == PROTOCOL_QUAKE)
1546         {
1547                 // write a keepalive (clc_nop) as it seems to greatly improve the
1548                 // chances of connecting to a netquake server
1549                 sizebuf_t msg;
1550                 unsigned char buf[4];
1551                 memset(&msg, 0, sizeof(msg));
1552                 msg.data = buf;
1553                 msg.maxsize = sizeof(buf);
1554                 MSG_WriteChar(&msg, clc_nop);
1555                 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1556         }
1557 }
1558
1559 int NetConn_IsLocalGame(void)
1560 {
1561         if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1562                 return true;
1563         return false;
1564 }
1565
1566 #ifdef CONFIG_MENU
1567 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1568 {
1569         int n;
1570         int pingtime;
1571         serverlist_entry_t *entry = NULL;
1572
1573         // search the cache for this server and update it
1574         for (n = 0;n < serverlist_cachecount;n++) {
1575                 entry = &serverlist_cache[ n ];
1576                 if (!strcmp(addressstring, entry->info.cname))
1577                         break;
1578         }
1579
1580         if (n == serverlist_cachecount)
1581         {
1582                 // LAN search doesnt require an answer from the master server so we wont
1583                 // know the ping nor will it be initialized already...
1584
1585                 // find a slot
1586                 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1587                         return -1;
1588
1589                 if (serverlist_maxcachecount <= serverlist_cachecount)
1590                 {
1591                         serverlist_maxcachecount += 64;
1592                         serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1593                 }
1594                 entry = &serverlist_cache[n];
1595
1596                 memset(entry, 0, sizeof(*entry));
1597                 // store the data the engine cares about (address and ping)
1598                 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1599                 entry->info.ping = 100000;
1600                 entry->querytime = realtime;
1601                 // if not in the slist menu we should print the server to console
1602                 if (serverlist_consoleoutput)
1603                         Con_Printf("querying %s\n", addressstring);
1604                 ++serverlist_cachecount;
1605         }
1606         // if this is the first reply from this server, count it as having replied
1607         pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1608         pingtime = bound(0, pingtime, 9999);
1609         if (entry->query == SQS_REFRESHING) {
1610                 entry->info.ping = pingtime;
1611                 entry->query = SQS_QUERIED;
1612         } else {
1613                 // convert to unsigned to catch the -1
1614                 // 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]
1615                 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1616                 serverreplycount++;
1617         }
1618         
1619         // other server info is updated by the caller
1620         return n;
1621 }
1622
1623 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1624 {
1625         serverlist_entry_t *entry = &serverlist_cache[n];
1626         serverlist_info_t *info = &entry->info;
1627         // update description strings for engine menu and console output
1628         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);
1629         dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1630                         (
1631                          info->gameversion != gameversion.integer
1632                          &&
1633                          !(
1634                                     gameversion_min.integer >= 0 // min/max range set by user/mod?
1635                                  && gameversion_max.integer >= 0
1636                                  && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1637                                  && gameversion_max.integer >= info->gameversion
1638                           )
1639                         ) ? '1' : '4',
1640                         info->mod, info->map);
1641         if (entry->query == SQS_QUERIED)
1642         {
1643                 if(!serverlist_paused)
1644                         ServerList_ViewList_Remove(entry);
1645         }
1646         // if not in the slist menu we should print the server to console (if wanted)
1647         else if( serverlist_consoleoutput )
1648                 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1649         // and finally, update the view set
1650         if(!serverlist_paused)
1651                 ServerList_ViewList_Insert( entry );
1652         //      update the entry's state
1653         serverlist_cache[n].query = SQS_QUERIED;
1654 }
1655
1656 // returns true, if it's sensible to continue the processing
1657 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1658         int n;
1659         serverlist_entry_t *entry;
1660
1661         //      ignore the rest of the message if the serverlist is full
1662         if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1663                 return false;
1664         //      also ignore     it      if      we      have already queried    it      (other master server    response)
1665         for( n =        0 ; n   < serverlist_cachecount ; n++   )
1666                 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1667                         break;
1668
1669         if( n < serverlist_cachecount ) {
1670                 // the entry has already been queried once or 
1671                 return true;
1672         }
1673
1674         if (serverlist_maxcachecount <= n)
1675         {
1676                 serverlist_maxcachecount += 64;
1677                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1678         }
1679
1680         entry = &serverlist_cache[n];
1681
1682         memset(entry, 0, sizeof(*entry));
1683         entry->protocol =       protocol;
1684         //      store   the data        the engine cares about (address and     ping)
1685         strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1686
1687         entry->info.isfavorite = isfavorite;
1688         
1689         // no, then reset the ping right away
1690         entry->info.ping = -1;
1691         // we also want to increase the serverlist_cachecount then
1692         serverlist_cachecount++;
1693         serverquerycount++;
1694
1695         entry->query =  SQS_QUERYING;
1696
1697         return true;
1698 }
1699
1700 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1701 {
1702         masterreplycount++;
1703         if (serverlist_consoleoutput)
1704                 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1705         while (length >= 7)
1706         {
1707                 char ipstring [128];
1708
1709                 // IPv4 address
1710                 if (data[0] == '\\')
1711                 {
1712                         unsigned short port = data[5] * 256 + data[6];
1713
1714                         if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1715                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1716
1717                         // move on to next address in packet
1718                         data += 7;
1719                         length -= 7;
1720                 }
1721                 // IPv6 address
1722                 else if (data[0] == '/' && isextended && length >= 19)
1723                 {
1724                         unsigned short port = data[17] * 256 + data[18];
1725
1726                         if (port != 0)
1727                         {
1728 #ifdef WHY_JUST_WHY
1729                                 const char *ifname;
1730                                 char ifnamebuf[16];
1731
1732                                 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1733
1734                                 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1735                                 if (ifname != NULL)
1736                                 {
1737                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1738                                                                 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1739                                                                 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1740                                                                 ifname, port);
1741                                 }
1742                                 else
1743 #endif
1744                                 {
1745                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1746                                                                 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1747                                                                 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1748                                                                 port);
1749                                 }
1750                         }
1751
1752                         // move on to next address in packet
1753                         data += 19;
1754                         length -= 19;
1755                 }
1756                 else
1757                 {
1758                         Con_Print("Error while parsing the server list\n");
1759                         break;
1760                 }
1761
1762                 if (serverlist_consoleoutput && developer_networking.integer)
1763                         Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1764                 
1765                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1766                         break;
1767                 }
1768
1769         }
1770
1771         // begin or resume serverlist queries
1772         serverlist_querysleep = false;
1773         serverlist_querywaittime = realtime + 3;
1774 }
1775 #endif
1776
1777 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1778 {
1779         qboolean fromserver;
1780         int ret, c;
1781         char *string, addressstring2[128];
1782         char stringbuf[16384];
1783         char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1784         size_t sendlength;
1785 #ifdef CONFIG_MENU
1786         char infostringvalue[MAX_INPUTLINE];
1787         char ipstring[32];
1788         const char *s;
1789 #endif
1790         char vabuf[1024];
1791
1792         // quakeworld ingame packet
1793         fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1794
1795         // convert the address to a string incase we need it
1796         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1797
1798         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1799         {
1800                 // received a command string - strip off the packaging and put it
1801                 // into our string buffer with NULL termination
1802                 data += 4;
1803                 length -= 4;
1804                 length = min(length, (int)sizeof(stringbuf) - 1);
1805                 memcpy(stringbuf, data, length);
1806                 stringbuf[length] = 0;
1807                 string = stringbuf;
1808
1809                 if (developer_networking.integer)
1810                 {
1811                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1812                         Com_HexDumpToConsole(data, length);
1813                 }
1814
1815                 sendlength = sizeof(senddata) - 4;
1816                 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1817                 {
1818                         case CRYPTO_NOMATCH:
1819                                 // nothing to do
1820                                 break;
1821                         case CRYPTO_MATCH:
1822                                 if(sendlength)
1823                                 {
1824                                         memcpy(senddata, "\377\377\377\377", 4);
1825                                         NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1826                                 }
1827                                 break;
1828                         case CRYPTO_DISCARD:
1829                                 if(sendlength)
1830                                 {
1831                                         memcpy(senddata, "\377\377\377\377", 4);
1832                                         NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1833                                 }
1834                                 return true;
1835                                 break;
1836                         case CRYPTO_REPLACE:
1837                                 string = senddata+4;
1838                                 length = sendlength;
1839                                 break;
1840                 }
1841
1842                 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1843                 {
1844                         int i = 0, j;
1845                         for (j = 0;j < MAX_RCONS;j++)
1846                         {
1847                                 // note: this value from i is used outside the loop too...
1848                                 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1849                                 if(cls.rcon_commands[i][0])
1850                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1851                                                 break;
1852                         }
1853                         if (j < MAX_RCONS)
1854                         {
1855                                 char buf[1500];
1856                                 char argbuf[1500];
1857                                 const char *e;
1858                                 int n;
1859                                 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1860                                 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1861
1862                                 e = strchr(rcon_password.string, ' ');
1863                                 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1864
1865                                 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
1866                                 {
1867                                         int k;
1868                                         buf[45] = ' ';
1869                                         strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1870                                         NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
1871                                         cls.rcon_commands[i][0] = 0;
1872                                         --cls.rcon_trying;
1873
1874                                         for (k = 0;k < MAX_RCONS;k++)
1875                                                 if(cls.rcon_commands[k][0])
1876                                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1877                                                                 break;
1878                                         if(k < MAX_RCONS)
1879                                         {
1880                                                 int l;
1881                                                 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1882                                                 // extend the timeout on other requests as we asked for a challenge
1883                                                 for (l = 0;l < MAX_RCONS;l++)
1884                                                         if(cls.rcon_commands[l][0])
1885                                                                 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1886                                                                         cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1887                                         }
1888
1889                                         return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1890                                 }
1891                         }
1892                 }
1893                 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1894                 {
1895                         // darkplaces or quake3
1896                         char protocolnames[1400];
1897                         Protocol_Names(protocolnames, sizeof(protocolnames));
1898                         Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1899 #ifdef CONFIG_MENU
1900                         M_Update_Return_Reason("Got challenge response");
1901 #endif
1902                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1903                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1904                         // TODO: add userinfo stuff here instead of using NQ commands?
1905                         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);
1906                         return true;
1907                 }
1908                 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1909                 {
1910                         // darkplaces or quake3
1911 #ifdef CONFIG_MENU
1912                         M_Update_Return_Reason("Accepted");
1913 #endif
1914                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1915                         return true;
1916                 }
1917                 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1918                 {
1919                         char rejectreason[128];
1920                         cls.connect_trying = false;
1921                         string += 7;
1922                         length = min(length - 7, (int)sizeof(rejectreason) - 1);
1923                         memcpy(rejectreason, string, length);
1924                         rejectreason[length] = 0;
1925 #ifdef CONFIG_MENU
1926                         M_Update_Return_Reason(rejectreason);
1927 #endif
1928                         return true;
1929                 }
1930 #ifdef CONFIG_MENU
1931                 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1932                 {
1933                         serverlist_info_t *info;
1934                         char *p;
1935                         int n;
1936
1937                         string += 15;
1938                         // search the cache for this server and update it
1939                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1940                         if (n < 0)
1941                                 return true;
1942
1943                         info = &serverlist_cache[n].info;
1944                         info->game[0] = 0;
1945                         info->mod[0]  = 0;
1946                         info->map[0]  = 0;
1947                         info->name[0] = 0;
1948                         info->qcstatus[0] = 0;
1949                         info->players[0] = 0;
1950                         info->protocol = -1;
1951                         info->numplayers = 0;
1952                         info->numbots = -1;
1953                         info->maxplayers  = 0;
1954                         info->gameversion = 0;
1955
1956                         p = strchr(string, '\n');
1957                         if(p)
1958                         {
1959                                 *p = 0; // cut off the string there
1960                                 ++p;
1961                         }
1962                         else
1963                                 Con_Printf("statusResponse without players block?\n");
1964
1965                         if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1966                         if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1967                         if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1968                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1969                         if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1970                         if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1971                         if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1972                         if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1973                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1974                         if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1975                         if (p                                                                                         != NULL) strlcpy(info->players, p, sizeof(info->players));
1976                         info->numhumans = info->numplayers - max(0, info->numbots);
1977                         info->freeslots = info->maxplayers - info->numplayers;
1978
1979                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1980
1981                         return true;
1982                 }
1983                 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1984                 {
1985                         serverlist_info_t *info;
1986                         int n;
1987
1988                         string += 13;
1989                         // search the cache for this server and update it
1990                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1991                         if (n < 0)
1992                                 return true;
1993
1994                         info = &serverlist_cache[n].info;
1995                         info->game[0] = 0;
1996                         info->mod[0]  = 0;
1997                         info->map[0]  = 0;
1998                         info->name[0] = 0;
1999                         info->qcstatus[0] = 0;
2000                         info->players[0] = 0;
2001                         info->protocol = -1;
2002                         info->numplayers = 0;
2003                         info->numbots = -1;
2004                         info->maxplayers  = 0;
2005                         info->gameversion = 0;
2006
2007                         if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2008                         if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2009                         if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2010                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2011                         if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2012                         if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2013                         if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2014                         if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2015                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2016                         if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2017                         info->numhumans = info->numplayers - max(0, info->numbots);
2018                         info->freeslots = info->maxplayers - info->numplayers;
2019
2020                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2021
2022                         return true;
2023                 }
2024                 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2025                 {
2026                         // Extract the IP addresses
2027                         data += 18;
2028                         length -= 18;
2029                         NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2030                         return true;
2031                 }
2032                 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2033                 {
2034                         // Extract the IP addresses
2035                         data += 21;
2036                         length -= 21;
2037                         NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2038                         return true;
2039                 }
2040                 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2041                 {
2042                         // Extract the IP addresses
2043                         data += 2;
2044                         length -= 2;
2045                         masterreplycount++;
2046                         if (serverlist_consoleoutput)
2047                                 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2048                         while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2049                         {
2050                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2051                                 if (serverlist_consoleoutput && developer_networking.integer)
2052                                         Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2053                                 
2054                                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2055                                         break;
2056                                 }
2057
2058                                 // move on to next address in packet
2059                                 data += 6;
2060                                 length -= 6;
2061                         }
2062                         // begin or resume serverlist queries
2063                         serverlist_querysleep = false;
2064                         serverlist_querywaittime = realtime + 3;
2065                         return true;
2066                 }
2067 #endif
2068                 if (!strncmp(string, "extResponse ", 12))
2069                 {
2070                         ++cl_net_extresponse_count;
2071                         if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2072                                 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2073                         cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2074                         dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2075                         return true;
2076                 }
2077                 if (!strncmp(string, "ping", 4))
2078                 {
2079                         if (developer_extra.integer)
2080                                 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2081                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2082                         return true;
2083                 }
2084                 if (!strncmp(string, "ack", 3))
2085                         return true;
2086                 // QuakeWorld compatibility
2087                 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2088                 {
2089                         // challenge message
2090                         Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2091 #ifdef CONFIG_MENU
2092                         M_Update_Return_Reason("Got QuakeWorld challenge response");
2093 #endif
2094                         cls.qw_qport = qport.integer;
2095                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2096                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2097                         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);
2098                         return true;
2099                 }
2100                 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2101                 {
2102                         // accept message
2103 #ifdef CONFIG_MENU
2104                         M_Update_Return_Reason("QuakeWorld Accepted");
2105 #endif
2106                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2107                         return true;
2108                 }
2109                 if (length > 2 && !memcmp(string, "n\\", 2))
2110                 {
2111 #ifdef CONFIG_MENU
2112                         serverlist_info_t *info;
2113                         int n;
2114
2115                         // qw server status
2116                         if (serverlist_consoleoutput && developer_networking.integer >= 2)
2117                                 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2118
2119                         string += 1;
2120                         // search the cache for this server and update it
2121                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2122                         if (n < 0)
2123                                 return true;
2124
2125                         info = &serverlist_cache[n].info;
2126                         strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2127                         if ((s = InfoString_GetValue(string, "*gamedir"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
2128                         if ((s = InfoString_GetValue(string, "map"          , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
2129                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2130                         info->protocol = 0;
2131                         info->numplayers = 0; // updated below
2132                         info->numhumans = 0; // updated below
2133                         if ((s = InfoString_GetValue(string, "maxclients"   , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
2134                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2135
2136                         // count active players on server
2137                         // (we could gather more info, but we're just after the number)
2138                         s = strchr(string, '\n');
2139                         if (s)
2140                         {
2141                                 s++;
2142                                 while (s < string + length)
2143                                 {
2144                                         for (;s < string + length && *s != '\n';s++)
2145                                                 ;
2146                                         if (s >= string + length)
2147                                                 break;
2148                                         info->numplayers++;
2149                                         info->numhumans++;
2150                                         s++;
2151                                 }
2152                         }
2153
2154                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2155 #endif
2156                         return true;
2157                 }
2158                 if (string[0] == 'n')
2159                 {
2160                         // qw print command
2161                         Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2162                 }
2163                 // we may not have liked the packet, but it was a command packet, so
2164                 // we're done processing this packet now
2165                 return true;
2166         }
2167         // quakeworld ingame packet
2168         if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2169         {
2170                 ret = 0;
2171                 CL_ParseServerMessage();
2172                 return ret;
2173         }
2174         // netquake control packets, supported for compatibility only
2175         if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2176         {
2177 #ifdef CONFIG_MENU
2178                 int n;
2179                 serverlist_info_t *info;
2180 #endif
2181
2182                 data += 4;
2183                 length -= 4;
2184                 SZ_Clear(&cl_message);
2185                 SZ_Write(&cl_message, data, length);
2186                 MSG_BeginReading(&cl_message);
2187                 c = MSG_ReadByte(&cl_message);
2188                 switch (c)
2189                 {
2190                 case CCREP_ACCEPT:
2191                         if (developer_extra.integer)
2192                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2193                         if (cls.connect_trying)
2194                         {
2195                                 lhnetaddress_t clientportaddress;
2196                                 clientportaddress = *peeraddress;
2197                                 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2198                                 // extra ProQuake stuff
2199                                 if (length >= 6)
2200                                         cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2201                                 else
2202                                         cls.proquake_servermod = 0;
2203                                 if (length >= 7)
2204                                         cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2205                                 else
2206                                         cls.proquake_serverversion = 0;
2207                                 if (length >= 8)
2208                                         cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2209                                 else
2210                                         cls.proquake_serverflags = 0;
2211                                 if (cls.proquake_servermod == 1)
2212                                         Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2213                                 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2214                                 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2215 #ifdef CONFIG_MENU
2216                                 M_Update_Return_Reason("Accepted");
2217 #endif
2218                                 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2219                         }
2220                         break;
2221                 case CCREP_REJECT:
2222                         if (developer_extra.integer)
2223                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2224                         cls.connect_trying = false;
2225 #ifdef CONFIG_MENU
2226                         M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2227 #endif
2228                         break;
2229                 case CCREP_SERVER_INFO:
2230                         if (developer_extra.integer)
2231                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2232 #ifdef CONFIG_MENU
2233                         // LordHavoc: because the quake server may report weird addresses
2234                         // we just ignore it and keep the real address
2235                         MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2236                         // search the cache for this server and update it
2237                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2238                         if (n < 0)
2239                                 break;
2240
2241                         info = &serverlist_cache[n].info;
2242                         strlcpy(info->game, "Quake", sizeof(info->game));
2243                         strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2244                         strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2245                         strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2246                         info->numplayers = MSG_ReadByte(&cl_message);
2247                         info->maxplayers = MSG_ReadByte(&cl_message);
2248                         info->protocol = MSG_ReadByte(&cl_message);
2249
2250                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2251 #endif
2252                         break;
2253                 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2254                         if (developer_extra.integer)
2255                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2256
2257                         Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2258                         break;
2259                 case CCREP_PLAYER_INFO:
2260                         // we got a CCREP_PLAYER_INFO??
2261                         //if (developer_extra.integer)
2262                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2263                         break;
2264                 case CCREP_RULE_INFO:
2265                         // we got a CCREP_RULE_INFO??
2266                         //if (developer_extra.integer)
2267                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2268                         break;
2269                 default:
2270                         break;
2271                 }
2272                 SZ_Clear(&cl_message);
2273                 // we may not have liked the packet, but it was a valid control
2274                 // packet, so we're done processing this packet now
2275                 return true;
2276         }
2277         ret = 0;
2278         if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2279                 CL_ParseServerMessage();
2280         return ret;
2281 }
2282
2283 #ifdef CONFIG_MENU
2284 void NetConn_QueryQueueFrame(void)
2285 {
2286         int index;
2287         int queries;
2288         int maxqueries;
2289         double timeouttime;
2290         static double querycounter = 0;
2291
2292         if(!net_slist_pause.integer && serverlist_paused)
2293                 ServerList_RebuildViewList();
2294         serverlist_paused = net_slist_pause.integer != 0;
2295
2296         if (serverlist_querysleep)
2297                 return;
2298
2299         // apply a cool down time after master server replies,
2300         // to avoid messing up the ping times on the servers
2301         if (serverlist_querywaittime > realtime)
2302                 return;
2303
2304         // each time querycounter reaches 1.0 issue a query
2305         querycounter += cl.realframetime * net_slist_queriespersecond.value;
2306         maxqueries = (int)querycounter;
2307         maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2308         querycounter -= maxqueries;
2309
2310         if( maxqueries == 0 ) {
2311                 return;
2312         }
2313
2314         //      scan serverlist and issue queries as needed
2315         serverlist_querysleep = true;
2316
2317         timeouttime     = realtime - net_slist_timeout.value;
2318         for( index = 0, queries = 0 ;   index   < serverlist_cachecount &&      queries < maxqueries    ; index++ )
2319         {
2320                 serverlist_entry_t *entry = &serverlist_cache[ index ];
2321                 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2322                 {
2323                         continue;
2324                 }
2325
2326                 serverlist_querysleep   = false;
2327                 if( entry->querycounter !=      0 && entry->querytime > timeouttime     )
2328                 {
2329                         continue;
2330                 }
2331
2332                 if( entry->querycounter !=      (unsigned) net_slist_maxtries.integer )
2333                 {
2334                         lhnetaddress_t  address;
2335                         int socket;
2336
2337                         LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2338                         if      (entry->protocol == PROTOCOL_QUAKEWORLD)
2339                         {
2340                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
2341                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2342                         }
2343                         else
2344                         {
2345                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
2346                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2347                         }
2348
2349                         //      update the entry fields
2350                         entry->querytime = realtime;
2351                         entry->querycounter++;
2352
2353                         // if not in the slist menu we should print the server to console
2354                         if (serverlist_consoleoutput)
2355                                 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2356
2357                         queries++;
2358                 }
2359                 else
2360                 {
2361                         // have we tried to refresh this server?
2362                         if( entry->query == SQS_REFRESHING ) {
2363                                 // yes, so update the reply count (since its not responding anymore)
2364                                 serverreplycount--;
2365                                 if(!serverlist_paused)
2366                                         ServerList_ViewList_Remove(entry);
2367                         }
2368                         entry->query = SQS_TIMEDOUT;
2369                 }
2370         }
2371 }
2372 #endif
2373
2374 void NetConn_ClientFrame(void)
2375 {
2376         int i, length;
2377         lhnetaddress_t peeraddress;
2378         unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2379         NetConn_UpdateSockets();
2380         if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2381         {
2382 #ifdef CONFIG_MENU
2383                 if (cls.connect_remainingtries == 0)
2384                         M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2385 #endif
2386                 cls.connect_nextsendtime = realtime + 1;
2387                 cls.connect_remainingtries--;
2388                 if (cls.connect_remainingtries <= -10)
2389                 {
2390                         cls.connect_trying = false;
2391 #ifdef CONFIG_MENU
2392                         M_Update_Return_Reason("Connect: Failed");
2393 #endif
2394                         return;
2395                 }
2396                 // try challenge first (newer DP server or QW)
2397                 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2398                 // then try netquake as a fallback (old server, or netquake)
2399                 SZ_Clear(&cl_message);
2400                 // save space for the header, filled in later
2401                 MSG_WriteLong(&cl_message, 0);
2402                 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2403                 MSG_WriteString(&cl_message, "QUAKE");
2404                 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2405                 // extended proquake stuff
2406                 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2407                 // this version matches ProQuake 3.40, the first version to support
2408                 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2409                 // higher clients, so we pretend we are that version...
2410                 MSG_WriteByte(&cl_message, 34); // version * 10
2411                 MSG_WriteByte(&cl_message, 0); // flags
2412                 MSG_WriteLong(&cl_message, 0); // password
2413                 // write the packetsize now...
2414                 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2415                 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2416                 SZ_Clear(&cl_message);
2417         }
2418         for (i = 0;i < cl_numsockets;i++)
2419         {
2420                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2421                 {
2422 //                      R_TimeReport("clientreadnetwork");
2423                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2424 //                      R_TimeReport("clientparsepacket");
2425                 }
2426         }
2427 #ifdef CONFIG_MENU
2428         NetConn_QueryQueueFrame();
2429 #endif
2430         if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2431         {
2432                 Con_Print("Connection timed out\n");
2433                 CL_Disconnect();
2434                 SV_LockThreadMutex();
2435                 Host_ShutdownServer ();
2436                 SV_UnlockThreadMutex();
2437         }
2438 }
2439
2440 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2441 {
2442         int i;
2443         char c;
2444         for (i = 0;i < bufferlength - 1;i++)
2445         {
2446                 do
2447                 {
2448                         c = rand () % (127 - 33) + 33;
2449                 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2450                 buffer[i] = c;
2451         }
2452         buffer[i] = 0;
2453 }
2454
2455 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2456 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2457 {
2458         prvm_prog_t *prog = SVVM_prog;
2459         char qcstatus[256];
2460         unsigned int nb_clients = 0, nb_bots = 0, i;
2461         int length;
2462         char teambuf[3];
2463         const char *crypto_idstring;
2464         const char *str;
2465
2466         // How many clients are there?
2467         for (i = 0;i < (unsigned int)svs.maxclients;i++)
2468         {
2469                 if (svs.clients[i].active)
2470                 {
2471                         nb_clients++;
2472                         if (!svs.clients[i].netconnection)
2473                                 nb_bots++;
2474                 }
2475         }
2476
2477         *qcstatus = 0;
2478         str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2479         if(str && *str)
2480         {
2481                 char *p;
2482                 const char *q;
2483                 p = qcstatus;
2484                 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2485                         if(*q != '\\' && *q != '\n')
2486                                 *p++ = *q;
2487                 *p = 0;
2488         }
2489
2490         /// \TODO: we should add more information for the full status string
2491         crypto_idstring = Crypto_GetInfoResponseDataString();
2492         length = dpsnprintf(out_msg, out_size,
2493                                                 "\377\377\377\377%s\x0A"
2494                                                 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2495                                                 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2496                                                 "%s%s"
2497                                                 "%s%s"
2498                                                 "%s%s"
2499                                                 "%s",
2500                                                 fullstatus ? "statusResponse" : "infoResponse",
2501                                                 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2502                                                 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2503                                                 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2504                                                 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2505                                                 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2506                                                 fullstatus ? "\n" : "");
2507
2508         // Make sure it fits in the buffer
2509         if (length < 0)
2510                 goto bad;
2511
2512         if (fullstatus)
2513         {
2514                 char *ptr;
2515                 int left;
2516                 int savelength;
2517
2518                 savelength = length;
2519
2520                 ptr = out_msg + length;
2521                 left = (int)out_size - length;
2522
2523                 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2524                 {
2525                         client_t *cl = &svs.clients[i];
2526                         if (cl->active)
2527                         {
2528                                 int nameind, cleanind, pingvalue;
2529                                 char curchar;
2530                                 char cleanname [sizeof(cl->name)];
2531                                 const char *str;
2532                                 prvm_edict_t *ed;
2533
2534                                 // Remove all characters '"' and '\' in the player name
2535                                 nameind = 0;
2536                                 cleanind = 0;
2537                                 do
2538                                 {
2539                                         curchar = cl->name[nameind++];
2540                                         if (curchar != '"' && curchar != '\\')
2541                                         {
2542                                                 cleanname[cleanind++] = curchar;
2543                                                 if (cleanind == sizeof(cleanname) - 1)
2544                                                         break;
2545                                         }
2546                                 } while (curchar != '\0');
2547                                 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2548
2549                                 pingvalue = (int)(cl->ping * 1000.0f);
2550                                 if(cl->netconnection)
2551                                         pingvalue = bound(1, pingvalue, 9999);
2552                                 else
2553                                         pingvalue = 0;
2554
2555                                 *qcstatus = 0;
2556                                 ed = PRVM_EDICT_NUM(i + 1);
2557                                 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2558                                 if(str && *str)
2559                                 {
2560                                         char *p;
2561                                         const char *q;
2562                                         p = qcstatus;
2563                                         for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2564                                                 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2565                                                         *p++ = *q;
2566                                         *p = 0;
2567                                 }
2568
2569                                 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2570                                 {
2571                                         if(cl->frags == -666) // spectator
2572                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2573                                         else if(cl->colors == 0x44) // red team
2574                                                 strlcpy(teambuf, " 1", sizeof(teambuf));
2575                                         else if(cl->colors == 0xDD) // blue team
2576                                                 strlcpy(teambuf, " 2", sizeof(teambuf));
2577                                         else if(cl->colors == 0xCC) // yellow team
2578                                                 strlcpy(teambuf, " 3", sizeof(teambuf));
2579                                         else if(cl->colors == 0x99) // pink team
2580                                                 strlcpy(teambuf, " 4", sizeof(teambuf));
2581                                         else
2582                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2583                                 }
2584                                 else
2585                                         *teambuf = 0;
2586
2587                                 // note: team number is inserted according to SoF2 protocol
2588                                 if(*qcstatus)
2589                                         length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2590                                                                                 qcstatus,
2591                                                                                 pingvalue,
2592                                                                                 teambuf,
2593                                                                                 cleanname);
2594                                 else
2595                                         length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2596                                                                                 cl->frags,
2597                                                                                 pingvalue,
2598                                                                                 teambuf,
2599                                                                                 cleanname);
2600
2601                                 if(length < 0)
2602                                 {
2603                                         // out of space?
2604                                         // turn it into an infoResponse!
2605                                         out_msg[savelength] = 0;
2606                                         memcpy(out_msg + 4, "infoResponse\x0A", 13);
2607                                         memmove(out_msg + 17, out_msg + 19, savelength - 19);
2608                                         break;
2609                                 }
2610                                 left -= length;
2611                                 ptr += length;
2612                         }
2613                 }
2614         }
2615
2616         return true;
2617
2618 bad:
2619         return false;
2620 }
2621
2622 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2623 {
2624         size_t floodslotnum, bestfloodslotnum;
2625         double bestfloodtime;
2626         lhnetaddress_t noportpeeraddress;
2627         // see if this is a connect flood
2628         noportpeeraddress = *peeraddress;
2629         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2630         bestfloodslotnum = 0;
2631         bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2632         for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2633         {
2634                 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2635                 {
2636                         bestfloodtime = floodlist[floodslotnum].lasttime;
2637                         bestfloodslotnum = floodslotnum;
2638                 }
2639                 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2640                 {
2641                         // this address matches an ongoing flood address
2642                         if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2643                         {
2644                                 if(renew)
2645                                 {
2646                                         // renew the ban on this address so it does not expire
2647                                         // until the flood has subsided
2648                                         floodlist[floodslotnum].lasttime = realtime;
2649                                 }
2650                                 //Con_Printf("Flood detected!\n");
2651                                 return true;
2652                         }
2653                         // the flood appears to have subsided, so allow this
2654                         bestfloodslotnum = floodslotnum; // reuse the same slot
2655                         break;
2656                 }
2657         }
2658         // begin a new timeout on this address
2659         floodlist[bestfloodslotnum].address = noportpeeraddress;
2660         floodlist[bestfloodslotnum].lasttime = realtime;
2661         //Con_Printf("Flood detection initiated!\n");
2662         return false;
2663 }
2664
2665 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2666 {
2667         size_t floodslotnum;
2668         lhnetaddress_t noportpeeraddress;
2669         // see if this is a connect flood
2670         noportpeeraddress = *peeraddress;
2671         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2672         for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2673         {
2674                 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2675                 {
2676                         // this address matches an ongoing flood address
2677                         // remove the ban
2678                         floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2679                         floodlist[floodslotnum].lasttime = 0;
2680                         //Con_Printf("Flood cleared!\n");
2681                 }
2682         }
2683 }
2684
2685 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2686
2687 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2688 {
2689         char mdfourbuf[16];
2690         long t1, t2;
2691
2692         t1 = (long) time(NULL);
2693         t2 = strtol(s, NULL, 0);
2694         if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2695                 return false;
2696
2697         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2698                 return false;
2699
2700         return !memcmp(mdfourbuf, hash, 16);
2701 }
2702
2703 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2704 {
2705         char mdfourbuf[16];
2706         int i;
2707
2708         if(slen < (int)(sizeof(challenge[0].string)) - 1)
2709                 return false;
2710
2711         // validate the challenge
2712         for (i = 0;i < MAX_CHALLENGES;i++)
2713                 if(challenge[i].time > 0)
2714                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2715                                 break;
2716         // if the challenge is not recognized, drop the packet
2717         if (i == MAX_CHALLENGES)
2718                 return false;
2719
2720         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2721                 return false;
2722
2723         if(memcmp(mdfourbuf, hash, 16))
2724                 return false;
2725
2726         // unmark challenge to prevent replay attacks
2727         challenge[i].time = 0;
2728
2729         return true;
2730 }
2731
2732 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2733 {
2734         return !strcmp(password, hash);
2735 }
2736
2737 /// returns a string describing the user level, or NULL for auth failure
2738 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)
2739 {
2740         const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2741         static char buf[MAX_INPUTLINE];
2742         qboolean hasquotes;
2743         qboolean restricted = false;
2744         qboolean have_usernames = false;
2745         char vabuf[1024];
2746
2747         userpass_start = rcon_password.string;
2748         while((userpass_end = strchr(userpass_start, ' ')))
2749         {
2750                 have_usernames = true;
2751                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2752                 if(buf[0])
2753                         if(comparator(peeraddress, buf, password, cs, cslen))
2754                                 goto allow;
2755                 userpass_start = userpass_end + 1;
2756         }
2757         if(userpass_start[0])
2758         {
2759                 userpass_end = userpass_start + strlen(userpass_start);
2760                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2761                         goto allow;
2762         }
2763
2764         restricted = true;
2765         have_usernames = false;
2766         userpass_start = rcon_restricted_password.string;
2767         while((userpass_end = strchr(userpass_start, ' ')))
2768         {
2769                 have_usernames = true;
2770                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2771                 if(buf[0])
2772                         if(comparator(peeraddress, buf, password, cs, cslen))
2773                                 goto check;
2774                 userpass_start = userpass_end + 1;
2775         }
2776         if(userpass_start[0])
2777         {
2778                 userpass_end = userpass_start + strlen(userpass_start);
2779                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2780                         goto check;
2781         }
2782         
2783         return NULL; // DENIED
2784
2785 check:
2786         for(text = s; text != endpos; ++text)
2787                 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2788                         return NULL; // block possible exploits against the parser/alias expansion
2789
2790         while(s != endpos)
2791         {
2792                 size_t l = strlen(s);
2793                 if(l)
2794                 {
2795                         hasquotes = (strchr(s, '"') != NULL);
2796                         // sorry, we can't allow these substrings in wildcard expressions,
2797                         // as they can mess with the argument counts
2798                         text = rcon_restricted_commands.string;
2799                         while(COM_ParseToken_Console(&text))
2800                         {
2801                                 // com_token now contains a pattern to check for...
2802                                 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2803                                 {
2804                                         if(!hasquotes)
2805                                                 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2806                                                         goto match;
2807                                 }
2808                                 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2809                                 {
2810                                         if(!strcmp(com_token, s))
2811                                                 goto match;
2812                                 }
2813                                 else // single-arg expression? must match the beginning of the command
2814                                 {
2815                                         if(!strcmp(com_token, s))
2816                                                 goto match;
2817                                         if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2818                                                 goto match;
2819                                 }
2820                         }
2821                         // if we got here, nothing matched!
2822                         return NULL;
2823                 }
2824 match:
2825                 s += l + 1;
2826         }
2827
2828 allow:
2829         userpass_startpass = strchr(userpass_start, ':');
2830         if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2831                 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2832
2833         return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2834 }
2835
2836 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2837 {
2838         if(userlevel)
2839         {
2840                 // looks like a legitimate rcon command with the correct password
2841                 const char *s_ptr = s;
2842                 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2843                 while(s_ptr != endpos)
2844                 {
2845                         size_t l = strlen(s_ptr);
2846                         if(l)
2847                                 Con_Printf(" %s;", s_ptr);
2848                         s_ptr += l + 1;
2849                 }
2850                 Con_Printf("\n");
2851
2852                 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2853                         Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2854                 while(s != endpos)
2855                 {
2856                         size_t l = strlen(s);
2857                         if(l)
2858                         {
2859                                 client_t *host_client_save = host_client;
2860                                 Cmd_ExecuteString(s, src_command, true);
2861                                 host_client = host_client_save;
2862                                 // in case it is a command that changes host_client (like restart)
2863                         }
2864                         s += l + 1;
2865                 }
2866                 Con_Rcon_Redirect_End();
2867         }
2868         else
2869         {
2870                 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2871         }
2872 }
2873
2874 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2875 {
2876         int i, ret, clientnum, best;
2877         double besttime;
2878         client_t *client;
2879         char *s, *string, response[1400], addressstring2[128];
2880         static char stringbuf[16384]; // server only
2881         qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2882         char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2883         size_t sendlength, response_len;
2884         char infostringvalue[MAX_INPUTLINE];
2885         char vabuf[1024];
2886
2887         if (!sv.active)
2888                 return false;
2889
2890         // convert the address to a string incase we need it
2891         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2892
2893         // see if we can identify the sender as a local player
2894         // (this is necessary for rcon to send a reliable reply if the client is
2895         //  actually on the server, not sending remotely)
2896         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2897                 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2898                         break;
2899         if (i == svs.maxclients)
2900                 host_client = NULL;
2901
2902         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2903         {
2904                 // received a command string - strip off the packaging and put it
2905                 // into our string buffer with NULL termination
2906                 data += 4;
2907                 length -= 4;
2908                 length = min(length, (int)sizeof(stringbuf) - 1);
2909                 memcpy(stringbuf, data, length);
2910                 stringbuf[length] = 0;
2911                 string = stringbuf;
2912
2913                 if (developer_extra.integer)
2914                 {
2915                         Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2916                         Com_HexDumpToConsole(data, length);
2917                 }
2918
2919                 sendlength = sizeof(senddata) - 4;
2920                 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2921                 {
2922                         case CRYPTO_NOMATCH:
2923                                 // nothing to do
2924                                 break;