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