]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - netconn.c
Compile with IPv6 support by default
[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 #ifndef NOSUPPORTIPV6
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 static 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, 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, 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 #ifndef NOSUPPORTIPV6
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 #ifndef NOSUPPORTIPV6
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                 unsigned int sequence, sequence_ack;
1216                 qboolean 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) != 0;
1238                 reliable_ack = (sequence_ack >> 31) != 0;
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                         // If too may packets have been dropped, only write the
1252                         // last NETGRAPH_PACKETS ones to the netgraph. Why?
1253                         // Because there's no point in writing more than
1254                         // these as the netgraph is going to be full anyway.
1255                         if (count > NETGRAPH_PACKETS)
1256                                 count = NETGRAPH_PACKETS;
1257                         while (count--)
1258                         {
1259                                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1260                                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1261                                 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1262                                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1263                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1264                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1265                         }
1266                 }
1267                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1268                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1269                 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1270                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1271                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1272                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1273                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1274
1275                 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1276                 if (net_test.integer)
1277                 {
1278                         if (conn->cleartime < realtime)
1279                                 conn->cleartime = realtime;
1280                 }
1281
1282                 if (reliable_ack == conn->qw.reliable_sequence)
1283                 {
1284                         // received, now we will be able to send another reliable message
1285                         conn->sendMessageLength = 0;
1286                         conn->reliableMessagesReceived++;
1287                 }
1288                 conn->qw.incoming_sequence = sequence;
1289                 if (conn == cls.netcon)
1290                         cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1291                 conn->qw.incoming_acknowledged = sequence_ack;
1292                 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1293                 if (reliable_message)
1294                         conn->qw.incoming_reliable_sequence ^= 1;
1295                 conn->lastMessageTime = realtime;
1296                 conn->timeout = realtime + newtimeout;
1297                 conn->unreliableMessagesReceived++;
1298                 if (conn == cls.netcon)
1299                 {
1300                         SZ_Clear(&cl_message);
1301                         SZ_Write(&cl_message, data, (int)length);
1302                         MSG_BeginReading(&cl_message);
1303                 }
1304                 else
1305                 {
1306                         SZ_Clear(&sv_message);
1307                         SZ_Write(&sv_message, data, (int)length);
1308                         MSG_BeginReading(&sv_message);
1309                 }
1310                 return 2;
1311         }
1312         else
1313         {
1314                 unsigned int count;
1315                 unsigned int flags;
1316                 unsigned int sequence;
1317                 size_t qlength;
1318                 const void *sendme;
1319                 size_t sendmelen;
1320
1321                 originallength = (int)length;
1322                 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1323                 if(!data)
1324                         return 0;
1325                 if(length < 8)
1326                         return 0;
1327
1328                 qlength = (unsigned int)BuffBigLong(data);
1329                 flags = qlength & ~NETFLAG_LENGTH_MASK;
1330                 qlength &= NETFLAG_LENGTH_MASK;
1331                 // control packets were already handled
1332                 if (!(flags & NETFLAG_CTL) && qlength == length)
1333                 {
1334                         sequence = BuffBigLong(data + 4);
1335                         conn->packetsReceived++;
1336                         data += 8;
1337                         length -= 8;
1338                         if (flags & NETFLAG_UNRELIABLE)
1339                         {
1340                                 if (sequence >= conn->nq.unreliableReceiveSequence)
1341                                 {
1342                                         if (sequence > conn->nq.unreliableReceiveSequence)
1343                                         {
1344                                                 count = sequence - conn->nq.unreliableReceiveSequence;
1345                                                 conn->droppedDatagrams += count;
1346                                                 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1347                                                 // If too may packets have been dropped, only write the
1348                                                 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1349                                                 // Because there's no point in writing more than
1350                                                 // these as the netgraph is going to be full anyway.
1351                                                 if (count > NETGRAPH_PACKETS)
1352                                                         count = NETGRAPH_PACKETS;
1353                                                 while (count--)
1354                                                 {
1355                                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1356                                                         conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1357                                                         conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1358                                                         conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1359                                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1360                                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1361                                                 }
1362                                         }
1363                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1364                                         conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1365                                         conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1366                                         conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1367                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1368                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1369                                         NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1370
1371                                         conn->nq.unreliableReceiveSequence = sequence + 1;
1372                                         conn->lastMessageTime = realtime;
1373                                         conn->timeout = realtime + newtimeout;
1374                                         conn->unreliableMessagesReceived++;
1375                                         if (length > 0)
1376                                         {
1377                                                 if (conn == cls.netcon)
1378                                                 {
1379                                                         SZ_Clear(&cl_message);
1380                                                         SZ_Write(&cl_message, data, (int)length);
1381                                                         MSG_BeginReading(&cl_message);
1382                                                 }
1383                                                 else
1384                                                 {
1385                                                         SZ_Clear(&sv_message);
1386                                                         SZ_Write(&sv_message, data, (int)length);
1387                                                         MSG_BeginReading(&sv_message);
1388                                                 }
1389                                                 return 2;
1390                                         }
1391                                 }
1392                                 //else
1393                                 //      Con_DPrint("Got a stale datagram\n");
1394                                 return 1;
1395                         }
1396                         else if (flags & NETFLAG_ACK)
1397                         {
1398                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1399                                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1400
1401                                 if (sequence == (conn->nq.sendSequence - 1))
1402                                 {
1403                                         if (sequence == conn->nq.ackSequence)
1404                                         {
1405                                                 conn->nq.ackSequence++;
1406                                                 if (conn->nq.ackSequence != conn->nq.sendSequence)
1407                                                         Con_DPrint("ack sequencing error\n");
1408                                                 conn->lastMessageTime = realtime;
1409                                                 conn->timeout = realtime + newtimeout;
1410                                                 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1411                                                 {
1412                                                         unsigned int packetLen;
1413                                                         unsigned int dataLen;
1414                                                         unsigned int eom;
1415
1416                                                         conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1417                                                         memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1418
1419                                                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1420                                                         {
1421                                                                 dataLen = conn->sendMessageLength;
1422                                                                 eom = NETFLAG_EOM;
1423                                                         }
1424                                                         else
1425                                                         {
1426                                                                 dataLen = MAX_PACKETFRAGMENT;
1427                                                                 eom = 0;
1428                                                         }
1429
1430                                                         packetLen = NET_HEADERSIZE + dataLen;
1431
1432                                                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1433                                                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1434                                                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1435
1436                                                         conn->nq.sendSequence++;
1437
1438                                                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1439                                                         if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1440                                                         {
1441                                                                 conn->lastSendTime = realtime;
1442                                                                 conn->packetsSent++;
1443                                                         }
1444                                                 }
1445                                                 else
1446                                                         conn->sendMessageLength = 0;
1447                                         }
1448                                         //else
1449                                         //      Con_DPrint("Duplicate ACK received\n");
1450                                 }
1451                                 //else
1452                                 //      Con_DPrint("Stale ACK received\n");
1453                                 return 1;
1454                         }
1455                         else if (flags & NETFLAG_DATA)
1456                         {
1457                                 unsigned char temppacket[8];
1458                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   += originallength + 28;
1459                                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1460
1461                                 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        += 8 + 28;
1462
1463                                 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1464                                 StoreBigLong(temppacket + 4, sequence);
1465                                 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1466                                 if(sendme)
1467                                         NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1468                                 if (sequence == conn->nq.receiveSequence)
1469                                 {
1470                                         conn->lastMessageTime = realtime;
1471                                         conn->timeout = realtime + newtimeout;
1472                                         conn->nq.receiveSequence++;
1473                                         if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1474                                                 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1475                                                 conn->receiveMessageLength += (int)length;
1476                                         } else {
1477                                                 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1478                                                                         "Dropping the message!\n", sequence );
1479                                                 conn->receiveMessageLength = 0;
1480                                                 return 1;
1481                                         }
1482                                         if (flags & NETFLAG_EOM)
1483                                         {
1484                                                 conn->reliableMessagesReceived++;
1485                                                 length = conn->receiveMessageLength;
1486                                                 conn->receiveMessageLength = 0;
1487                                                 if (length > 0)
1488                                                 {
1489                                                         if (conn == cls.netcon)
1490                                                         {
1491                                                                 SZ_Clear(&cl_message);
1492                                                                 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1493                                                                 MSG_BeginReading(&cl_message);
1494                                                         }
1495                                                         else
1496                                                         {
1497                                                                 SZ_Clear(&sv_message);
1498                                                                 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1499                                                                 MSG_BeginReading(&sv_message);
1500                                                         }
1501                                                         return 2;
1502                                                 }
1503                                         }
1504                                 }
1505                                 else
1506                                         conn->receivedDuplicateCount++;
1507                                 return 1;
1508                         }
1509                 }
1510         }
1511         return 0;
1512 }
1513
1514 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1515 {
1516         crypto_t *crypto;
1517         cls.connect_trying = false;
1518 #ifdef CONFIG_MENU
1519         M_Update_Return_Reason("");
1520 #endif
1521         // the connection request succeeded, stop current connection and set up a new connection
1522         CL_Disconnect();
1523         // if we're connecting to a remote server, shut down any local server
1524         if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1525         {
1526                 SV_LockThreadMutex();
1527                 Host_ShutdownServer ();
1528                 SV_UnlockThreadMutex();
1529         }
1530         // allocate a net connection to keep track of things
1531         cls.netcon = NetConn_Open(mysocket, peeraddress);
1532         crypto = &cls.netcon->crypto;
1533         if(cls.crypto.authenticated)
1534         {
1535                 Crypto_FinishInstance(crypto, &cls.crypto);
1536                 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1537                                 crypto->use_aes ? "Encrypted" : "Authenticated",
1538                                 cls.netcon->address,
1539                                 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1540                                 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1541                                 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1542                                 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1543                                 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1544                                 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1545                                 );
1546         }
1547         Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1548         key_dest = key_game;
1549 #ifdef CONFIG_MENU
1550         m_state = m_none;
1551 #endif
1552         cls.demonum = -1;                       // not in the demo loop now
1553         cls.state = ca_connected;
1554         cls.signon = 0;                         // need all the signon messages before playing
1555         cls.protocol = initialprotocol;
1556         // reset move sequence numbering on this new connection
1557         cls.servermovesequence = 0;
1558         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1559                 Cmd_ForwardStringToServer("new");
1560         if (cls.protocol == PROTOCOL_QUAKE)
1561         {
1562                 // write a keepalive (clc_nop) as it seems to greatly improve the
1563                 // chances of connecting to a netquake server
1564                 sizebuf_t msg;
1565                 unsigned char buf[4];
1566                 memset(&msg, 0, sizeof(msg));
1567                 msg.data = buf;
1568                 msg.maxsize = sizeof(buf);
1569                 MSG_WriteChar(&msg, clc_nop);
1570                 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1571         }
1572 }
1573
1574 int NetConn_IsLocalGame(void)
1575 {
1576         if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1577                 return true;
1578         return false;
1579 }
1580
1581 #ifdef CONFIG_MENU
1582 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1583 {
1584         int n;
1585         int pingtime;
1586         serverlist_entry_t *entry = NULL;
1587
1588         // search the cache for this server and update it
1589         for (n = 0;n < serverlist_cachecount;n++) {
1590                 entry = &serverlist_cache[ n ];
1591                 if (!strcmp(addressstring, entry->info.cname))
1592                         break;
1593         }
1594
1595         if (n == serverlist_cachecount)
1596         {
1597                 // LAN search doesnt require an answer from the master server so we wont
1598                 // know the ping nor will it be initialized already...
1599
1600                 // find a slot
1601                 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1602                         return -1;
1603
1604                 if (serverlist_maxcachecount <= serverlist_cachecount)
1605                 {
1606                         serverlist_maxcachecount += 64;
1607                         serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1608                 }
1609                 entry = &serverlist_cache[n];
1610
1611                 memset(entry, 0, sizeof(*entry));
1612                 // store the data the engine cares about (address and ping)
1613                 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1614                 entry->info.ping = 100000;
1615                 entry->querytime = realtime;
1616                 // if not in the slist menu we should print the server to console
1617                 if (serverlist_consoleoutput)
1618                         Con_Printf("querying %s\n", addressstring);
1619                 ++serverlist_cachecount;
1620         }
1621         // if this is the first reply from this server, count it as having replied
1622         pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1623         pingtime = bound(0, pingtime, 9999);
1624         if (entry->query == SQS_REFRESHING) {
1625                 entry->info.ping = pingtime;
1626                 entry->query = SQS_QUERIED;
1627         } else {
1628                 // convert to unsigned to catch the -1
1629                 // 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]
1630                 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1631                 serverreplycount++;
1632         }
1633         
1634         // other server info is updated by the caller
1635         return n;
1636 }
1637
1638 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1639 {
1640         serverlist_entry_t *entry = &serverlist_cache[n];
1641         serverlist_info_t *info = &entry->info;
1642         // update description strings for engine menu and console output
1643         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);
1644         dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1645                         (
1646                          info->gameversion != gameversion.integer
1647                          &&
1648                          !(
1649                                     gameversion_min.integer >= 0 // min/max range set by user/mod?
1650                                  && gameversion_max.integer >= 0
1651                                  && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1652                                  && gameversion_max.integer >= info->gameversion
1653                           )
1654                         ) ? '1' : '4',
1655                         info->mod, info->map);
1656         if (entry->query == SQS_QUERIED)
1657         {
1658                 if(!serverlist_paused)
1659                         ServerList_ViewList_Remove(entry);
1660         }
1661         // if not in the slist menu we should print the server to console (if wanted)
1662         else if( serverlist_consoleoutput )
1663                 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1664         // and finally, update the view set
1665         if(!serverlist_paused)
1666                 ServerList_ViewList_Insert( entry );
1667         //      update the entry's state
1668         serverlist_cache[n].query = SQS_QUERIED;
1669 }
1670
1671 // returns true, if it's sensible to continue the processing
1672 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1673         int n;
1674         serverlist_entry_t *entry;
1675
1676         //      ignore the rest of the message if the serverlist is full
1677         if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1678                 return false;
1679         //      also ignore     it      if      we      have already queried    it      (other master server    response)
1680         for( n =        0 ; n   < serverlist_cachecount ; n++   )
1681                 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1682                         break;
1683
1684         if( n < serverlist_cachecount ) {
1685                 // the entry has already been queried once or 
1686                 return true;
1687         }
1688
1689         if (serverlist_maxcachecount <= n)
1690         {
1691                 serverlist_maxcachecount += 64;
1692                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1693         }
1694
1695         entry = &serverlist_cache[n];
1696
1697         memset(entry, 0, sizeof(*entry));
1698         entry->protocol =       protocol;
1699         //      store   the data        the engine cares about (address and     ping)
1700         strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1701
1702         entry->info.isfavorite = isfavorite;
1703         
1704         // no, then reset the ping right away
1705         entry->info.ping = -1;
1706         // we also want to increase the serverlist_cachecount then
1707         serverlist_cachecount++;
1708         serverquerycount++;
1709
1710         entry->query =  SQS_QUERYING;
1711
1712         return true;
1713 }
1714
1715 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1716 {
1717         masterreplycount++;
1718         if (serverlist_consoleoutput)
1719                 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1720         while (length >= 7)
1721         {
1722                 char ipstring [128];
1723
1724                 // IPv4 address
1725                 if (data[0] == '\\')
1726                 {
1727                         unsigned short port = data[5] * 256 + data[6];
1728
1729                         if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1730                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1731
1732                         // move on to next address in packet
1733                         data += 7;
1734                         length -= 7;
1735                 }
1736                 // IPv6 address
1737                 else if (data[0] == '/' && isextended && length >= 19)
1738                 {
1739                         unsigned short port = data[17] * 256 + data[18];
1740
1741                         if (port != 0)
1742                         {
1743 #ifdef WHY_JUST_WHY
1744                                 const char *ifname;
1745                                 char ifnamebuf[16];
1746
1747                                 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1748
1749                                 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1750                                 if (ifname != NULL)
1751                                 {
1752                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1753                                                                 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1754                                                                 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1755                                                                 ifname, port);
1756                                 }
1757                                 else
1758 #endif
1759                                 {
1760                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1761                                                                 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1762                                                                 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1763                                                                 port);
1764                                 }
1765                         }
1766
1767                         // move on to next address in packet
1768                         data += 19;
1769                         length -= 19;
1770                 }
1771                 else
1772                 {
1773                         Con_Print("Error while parsing the server list\n");
1774                         break;
1775                 }
1776
1777                 if (serverlist_consoleoutput && developer_networking.integer)
1778                         Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1779                 
1780                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1781                         break;
1782                 }
1783
1784         }
1785
1786         // begin or resume serverlist queries
1787         serverlist_querysleep = false;
1788         serverlist_querywaittime = realtime + 3;
1789 }
1790 #endif
1791
1792 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1793 {
1794         qboolean fromserver;
1795         int ret, c;
1796         char *string, addressstring2[128];
1797         char stringbuf[16384];
1798         char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1799         size_t sendlength;
1800 #ifdef CONFIG_MENU
1801         char infostringvalue[MAX_INPUTLINE];
1802         char ipstring[32];
1803         const char *s;
1804 #endif
1805         char vabuf[1024];
1806
1807         // quakeworld ingame packet
1808         fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1809
1810         // convert the address to a string incase we need it
1811         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1812
1813         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1814         {
1815                 // received a command string - strip off the packaging and put it
1816                 // into our string buffer with NULL termination
1817                 data += 4;
1818                 length -= 4;
1819                 length = min(length, (int)sizeof(stringbuf) - 1);
1820                 memcpy(stringbuf, data, length);
1821                 stringbuf[length] = 0;
1822                 string = stringbuf;
1823
1824                 if (developer_networking.integer)
1825                 {
1826                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1827                         Com_HexDumpToConsole(data, length);
1828                 }
1829
1830                 sendlength = sizeof(senddata) - 4;
1831                 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1832                 {
1833                         case CRYPTO_NOMATCH:
1834                                 // nothing to do
1835                                 break;
1836                         case CRYPTO_MATCH:
1837                                 if(sendlength)
1838                                 {
1839                                         memcpy(senddata, "\377\377\377\377", 4);
1840                                         NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1841                                 }
1842                                 break;
1843                         case CRYPTO_DISCARD:
1844                                 if(sendlength)
1845                                 {
1846                                         memcpy(senddata, "\377\377\377\377", 4);
1847                                         NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1848                                 }
1849                                 return true;
1850                                 break;
1851                         case CRYPTO_REPLACE:
1852                                 string = senddata+4;
1853                                 length = (int)sendlength;
1854                                 break;
1855                 }
1856
1857                 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1858                 {
1859                         int i = 0, j;
1860                         for (j = 0;j < MAX_RCONS;j++)
1861                         {
1862                                 // note: this value from i is used outside the loop too...
1863                                 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1864                                 if(cls.rcon_commands[i][0])
1865                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1866                                                 break;
1867                         }
1868                         if (j < MAX_RCONS)
1869                         {
1870                                 char buf[1500];
1871                                 char argbuf[1500];
1872                                 const char *e;
1873                                 int n;
1874                                 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1875                                 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1876
1877                                 e = strchr(rcon_password.string, ' ');
1878                                 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1879
1880                                 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1881                                 {
1882                                         int k;
1883                                         buf[45] = ' ';
1884                                         strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1885                                         NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1886                                         cls.rcon_commands[i][0] = 0;
1887                                         --cls.rcon_trying;
1888
1889                                         for (k = 0;k < MAX_RCONS;k++)
1890                                                 if(cls.rcon_commands[k][0])
1891                                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1892                                                                 break;
1893                                         if(k < MAX_RCONS)
1894                                         {
1895                                                 int l;
1896                                                 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1897                                                 // extend the timeout on other requests as we asked for a challenge
1898                                                 for (l = 0;l < MAX_RCONS;l++)
1899                                                         if(cls.rcon_commands[l][0])
1900                                                                 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1901                                                                         cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1902                                         }
1903
1904                                         return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1905                                 }
1906                         }
1907                 }
1908                 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1909                 {
1910                         // darkplaces or quake3
1911                         char protocolnames[1400];
1912                         Protocol_Names(protocolnames, sizeof(protocolnames));
1913                         Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1914 #ifdef CONFIG_MENU
1915                         M_Update_Return_Reason("Got challenge response");
1916 #endif
1917                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1918                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1919                         // TODO: add userinfo stuff here instead of using NQ commands?
1920                         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);
1921                         return true;
1922                 }
1923                 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1924                 {
1925                         // darkplaces or quake3
1926 #ifdef CONFIG_MENU
1927                         M_Update_Return_Reason("Accepted");
1928 #endif
1929                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1930                         return true;
1931                 }
1932                 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1933                 {
1934                         char rejectreason[128];
1935                         cls.connect_trying = false;
1936                         string += 7;
1937                         length = min(length - 7, (int)sizeof(rejectreason) - 1);
1938                         memcpy(rejectreason, string, length);
1939                         rejectreason[length] = 0;
1940 #ifdef CONFIG_MENU
1941                         M_Update_Return_Reason(rejectreason);
1942 #endif
1943                         return true;
1944                 }
1945 #ifdef CONFIG_MENU
1946                 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1947                 {
1948                         serverlist_info_t *info;
1949                         char *p;
1950                         int n;
1951
1952                         string += 15;
1953                         // search the cache for this server and update it
1954                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1955                         if (n < 0)
1956                                 return true;
1957
1958                         info = &serverlist_cache[n].info;
1959                         info->game[0] = 0;
1960                         info->mod[0]  = 0;
1961                         info->map[0]  = 0;
1962                         info->name[0] = 0;
1963                         info->qcstatus[0] = 0;
1964                         info->players[0] = 0;
1965                         info->protocol = -1;
1966                         info->numplayers = 0;
1967                         info->numbots = -1;
1968                         info->maxplayers  = 0;
1969                         info->gameversion = 0;
1970
1971                         p = strchr(string, '\n');
1972                         if(p)
1973                         {
1974                                 *p = 0; // cut off the string there
1975                                 ++p;
1976                         }
1977                         else
1978                                 Con_Printf("statusResponse without players block?\n");
1979
1980                         if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1981                         if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1982                         if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1983                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1984                         if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1985                         if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1986                         if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1987                         if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1988                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1989                         if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1990                         if (p                                                                                         != NULL) strlcpy(info->players, p, sizeof(info->players));
1991                         info->numhumans = info->numplayers - max(0, info->numbots);
1992                         info->freeslots = info->maxplayers - info->numplayers;
1993
1994                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1995
1996                         return true;
1997                 }
1998                 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1999                 {
2000                         serverlist_info_t *info;
2001                         int n;
2002
2003                         string += 13;
2004                         // search the cache for this server and update it
2005                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2006                         if (n < 0)
2007                                 return true;
2008
2009                         info = &serverlist_cache[n].info;
2010                         info->game[0] = 0;
2011                         info->mod[0]  = 0;
2012                         info->map[0]  = 0;
2013                         info->name[0] = 0;
2014                         info->qcstatus[0] = 0;
2015                         info->players[0] = 0;
2016                         info->protocol = -1;
2017                         info->numplayers = 0;
2018                         info->numbots = -1;
2019                         info->maxplayers  = 0;
2020                         info->gameversion = 0;
2021
2022                         if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2023                         if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2024                         if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2025                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2026                         if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2027                         if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2028                         if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2029                         if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2030                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2031                         if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2032                         info->numhumans = info->numplayers - max(0, info->numbots);
2033                         info->freeslots = info->maxplayers - info->numplayers;
2034
2035                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2036
2037                         return true;
2038                 }
2039                 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2040                 {
2041                         // Extract the IP addresses
2042                         data += 18;
2043                         length -= 18;
2044                         NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2045                         return true;
2046                 }
2047                 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2048                 {
2049                         // Extract the IP addresses
2050                         data += 21;
2051                         length -= 21;
2052                         NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2053                         return true;
2054                 }
2055                 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2056                 {
2057                         // Extract the IP addresses
2058                         data += 2;
2059                         length -= 2;
2060                         masterreplycount++;
2061                         if (serverlist_consoleoutput)
2062                                 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2063                         while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2064                         {
2065                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2066                                 if (serverlist_consoleoutput && developer_networking.integer)
2067                                         Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2068                                 
2069                                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2070                                         break;
2071                                 }
2072
2073                                 // move on to next address in packet
2074                                 data += 6;
2075                                 length -= 6;
2076                         }
2077                         // begin or resume serverlist queries
2078                         serverlist_querysleep = false;
2079                         serverlist_querywaittime = realtime + 3;
2080                         return true;
2081                 }
2082 #endif
2083                 if (!strncmp(string, "extResponse ", 12))
2084                 {
2085                         ++cl_net_extresponse_count;
2086                         if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2087                                 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2088                         cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2089                         dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2090                         return true;
2091                 }
2092                 if (!strncmp(string, "ping", 4))
2093                 {
2094                         if (developer_extra.integer)
2095                                 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2096                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2097                         return true;
2098                 }
2099                 if (!strncmp(string, "ack", 3))
2100                         return true;
2101                 // QuakeWorld compatibility
2102                 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2103                 {
2104                         // challenge message
2105                         Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2106 #ifdef CONFIG_MENU
2107                         M_Update_Return_Reason("Got QuakeWorld challenge response");
2108 #endif
2109                         cls.qw_qport = qport.integer;
2110                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2111                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2112                         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);
2113                         return true;
2114                 }
2115                 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2116                 {
2117                         // accept message
2118 #ifdef CONFIG_MENU
2119                         M_Update_Return_Reason("QuakeWorld Accepted");
2120 #endif
2121                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2122                         return true;
2123                 }
2124                 if (length > 2 && !memcmp(string, "n\\", 2))
2125                 {
2126 #ifdef CONFIG_MENU
2127                         serverlist_info_t *info;
2128                         int n;
2129
2130                         // qw server status
2131                         if (serverlist_consoleoutput && developer_networking.integer >= 2)
2132                                 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2133
2134                         string += 1;
2135                         // search the cache for this server and update it
2136                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2137                         if (n < 0)
2138                                 return true;
2139
2140                         info = &serverlist_cache[n].info;
2141                         strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2142                         if ((s = InfoString_GetValue(string, "*gamedir"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
2143                         if ((s = InfoString_GetValue(string, "map"          , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
2144                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2145                         info->protocol = 0;
2146                         info->numplayers = 0; // updated below
2147                         info->numhumans = 0; // updated below
2148                         if ((s = InfoString_GetValue(string, "maxclients"   , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
2149                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2150
2151                         // count active players on server
2152                         // (we could gather more info, but we're just after the number)
2153                         s = strchr(string, '\n');
2154                         if (s)
2155                         {
2156                                 s++;
2157                                 while (s < string + length)
2158                                 {
2159                                         for (;s < string + length && *s != '\n';s++)
2160                                                 ;
2161                                         if (s >= string + length)
2162                                                 break;
2163                                         info->numplayers++;
2164                                         info->numhumans++;
2165                                         s++;
2166                                 }
2167                         }
2168
2169                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2170 #endif
2171                         return true;
2172                 }
2173                 if (string[0] == 'n')
2174                 {
2175                         // qw print command
2176                         Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2177                 }
2178                 // we may not have liked the packet, but it was a command packet, so
2179                 // we're done processing this packet now
2180                 return true;
2181         }
2182         // quakeworld ingame packet
2183         if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2184         {
2185                 ret = 0;
2186                 CL_ParseServerMessage();
2187                 return ret;
2188         }
2189         // netquake control packets, supported for compatibility only
2190         if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2191         {
2192 #ifdef CONFIG_MENU
2193                 int n;
2194                 serverlist_info_t *info;
2195 #endif
2196
2197                 data += 4;
2198                 length -= 4;
2199                 SZ_Clear(&cl_message);
2200                 SZ_Write(&cl_message, data, length);
2201                 MSG_BeginReading(&cl_message);
2202                 c = MSG_ReadByte(&cl_message);
2203                 switch (c)
2204                 {
2205                 case CCREP_ACCEPT:
2206                         if (developer_extra.integer)
2207                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2208                         if (cls.connect_trying)
2209                         {
2210                                 lhnetaddress_t clientportaddress;
2211                                 clientportaddress = *peeraddress;
2212                                 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2213                                 // extra ProQuake stuff
2214                                 if (length >= 6)
2215                                         cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2216                                 else
2217                                         cls.proquake_servermod = 0;
2218                                 if (length >= 7)
2219                                         cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2220                                 else
2221                                         cls.proquake_serverversion = 0;
2222                                 if (length >= 8)
2223                                         cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2224                                 else
2225                                         cls.proquake_serverflags = 0;
2226                                 if (cls.proquake_servermod == 1)
2227                                         Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2228                                 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2229                                 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2230 #ifdef CONFIG_MENU
2231                                 M_Update_Return_Reason("Accepted");
2232 #endif
2233                                 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2234                         }
2235                         break;
2236                 case CCREP_REJECT:
2237                         if (developer_extra.integer)
2238                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2239                         cls.connect_trying = false;
2240 #ifdef CONFIG_MENU
2241                         M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2242 #endif
2243                         break;
2244                 case CCREP_SERVER_INFO:
2245                         if (developer_extra.integer)
2246                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2247 #ifdef CONFIG_MENU
2248                         // LordHavoc: because the quake server may report weird addresses
2249                         // we just ignore it and keep the real address
2250                         MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2251                         // search the cache for this server and update it
2252                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2253                         if (n < 0)
2254                                 break;
2255
2256                         info = &serverlist_cache[n].info;
2257                         strlcpy(info->game, "Quake", sizeof(info->game));
2258                         strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2259                         strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2260                         strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2261                         info->numplayers = MSG_ReadByte(&cl_message);
2262                         info->maxplayers = MSG_ReadByte(&cl_message);
2263                         info->protocol = MSG_ReadByte(&cl_message);
2264
2265                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2266 #endif
2267                         break;
2268                 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2269                         if (developer_extra.integer)
2270                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2271
2272                         Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2273                         break;
2274                 case CCREP_PLAYER_INFO:
2275                         // we got a CCREP_PLAYER_INFO??
2276                         //if (developer_extra.integer)
2277                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2278                         break;
2279                 case CCREP_RULE_INFO:
2280                         // we got a CCREP_RULE_INFO??
2281                         //if (developer_extra.integer)
2282                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2283                         break;
2284                 default:
2285                         break;
2286                 }
2287                 SZ_Clear(&cl_message);
2288                 // we may not have liked the packet, but it was a valid control
2289                 // packet, so we're done processing this packet now
2290                 return true;
2291         }
2292         ret = 0;
2293         if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2294                 CL_ParseServerMessage();
2295         return ret;
2296 }
2297
2298 #ifdef CONFIG_MENU
2299 void NetConn_QueryQueueFrame(void)
2300 {
2301         int index;
2302         int queries;
2303         int maxqueries;
2304         double timeouttime;
2305         static double querycounter = 0;
2306
2307         if(!net_slist_pause.integer && serverlist_paused)
2308                 ServerList_RebuildViewList();
2309         serverlist_paused = net_slist_pause.integer != 0;
2310
2311         if (serverlist_querysleep)
2312                 return;
2313
2314         // apply a cool down time after master server replies,
2315         // to avoid messing up the ping times on the servers
2316         if (serverlist_querywaittime > realtime)
2317                 return;
2318
2319         // each time querycounter reaches 1.0 issue a query
2320         querycounter += cl.realframetime * net_slist_queriespersecond.value;
2321         maxqueries = (int)querycounter;
2322         maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2323         querycounter -= maxqueries;
2324
2325         if( maxqueries == 0 ) {
2326                 return;
2327         }
2328
2329         //      scan serverlist and issue queries as needed
2330         serverlist_querysleep = true;
2331
2332         timeouttime     = realtime - net_slist_timeout.value;
2333         for( index = 0, queries = 0 ;   index   < serverlist_cachecount &&      queries < maxqueries    ; index++ )
2334         {
2335                 serverlist_entry_t *entry = &serverlist_cache[ index ];
2336                 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2337                 {
2338                         continue;
2339                 }
2340
2341                 serverlist_querysleep   = false;
2342                 if( entry->querycounter !=      0 && entry->querytime > timeouttime     )
2343                 {
2344                         continue;
2345                 }
2346
2347                 if( entry->querycounter !=      (unsigned) net_slist_maxtries.integer )
2348                 {
2349                         lhnetaddress_t  address;
2350                         int socket;
2351
2352                         LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2353                         if      (entry->protocol == PROTOCOL_QUAKEWORLD)
2354                         {
2355                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
2356                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2357                         }
2358                         else
2359                         {
2360                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
2361                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2362                         }
2363
2364                         //      update the entry fields
2365                         entry->querytime = realtime;
2366                         entry->querycounter++;
2367
2368                         // if not in the slist menu we should print the server to console
2369                         if (serverlist_consoleoutput)
2370                                 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2371
2372                         queries++;
2373                 }
2374                 else
2375                 {
2376                         // have we tried to refresh this server?
2377                         if( entry->query == SQS_REFRESHING ) {
2378                                 // yes, so update the reply count (since its not responding anymore)
2379                                 serverreplycount--;
2380                                 if(!serverlist_paused)
2381                                         ServerList_ViewList_Remove(entry);
2382                         }
2383                         entry->query = SQS_TIMEDOUT;
2384                 }
2385         }
2386 }
2387 #endif
2388
2389 void NetConn_ClientFrame(void)
2390 {
2391         int i, length;
2392         lhnetaddress_t peeraddress;
2393         unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2394         NetConn_UpdateSockets();
2395         if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2396         {
2397 #ifdef CONFIG_MENU
2398                 if (cls.connect_remainingtries == 0)
2399                         M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2400 #endif
2401                 cls.connect_nextsendtime = realtime + 1;
2402                 cls.connect_remainingtries--;
2403                 if (cls.connect_remainingtries <= -10)
2404                 {
2405                         cls.connect_trying = false;
2406 #ifdef CONFIG_MENU
2407                         M_Update_Return_Reason("Connect: Failed");
2408 #endif
2409                         return;
2410                 }
2411                 // try challenge first (newer DP server or QW)
2412                 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2413                 // then try netquake as a fallback (old server, or netquake)
2414                 SZ_Clear(&cl_message);
2415                 // save space for the header, filled in later
2416                 MSG_WriteLong(&cl_message, 0);
2417                 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2418                 MSG_WriteString(&cl_message, "QUAKE");
2419                 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2420                 // extended proquake stuff
2421                 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2422                 // this version matches ProQuake 3.40, the first version to support
2423                 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2424                 // higher clients, so we pretend we are that version...
2425                 MSG_WriteByte(&cl_message, 34); // version * 10
2426                 MSG_WriteByte(&cl_message, 0); // flags
2427                 MSG_WriteLong(&cl_message, 0); // password
2428                 // write the packetsize now...
2429                 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2430                 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2431                 SZ_Clear(&cl_message);
2432         }
2433         for (i = 0;i < cl_numsockets;i++)
2434         {
2435                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2436                 {
2437 //                      R_TimeReport("clientreadnetwork");
2438                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2439 //                      R_TimeReport("clientparsepacket");
2440                 }
2441         }
2442 #ifdef CONFIG_MENU
2443         NetConn_QueryQueueFrame();
2444 #endif
2445         if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2446         {
2447                 Con_Print("Connection timed out\n");
2448                 CL_Disconnect();
2449                 SV_LockThreadMutex();
2450                 Host_ShutdownServer ();
2451                 SV_UnlockThreadMutex();
2452         }
2453 }
2454
2455 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2456 {
2457         int i;
2458         char c;
2459         for (i = 0;i < bufferlength - 1;i++)
2460         {
2461                 do
2462                 {
2463                         c = rand () % (127 - 33) + 33;
2464                 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2465                 buffer[i] = c;
2466         }
2467         buffer[i] = 0;
2468 }
2469
2470 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2471 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2472 {
2473         prvm_prog_t *prog = SVVM_prog;
2474         char qcstatus[256];
2475         unsigned int nb_clients = 0, nb_bots = 0, i;
2476         int length;
2477         char teambuf[3];
2478         const char *crypto_idstring;
2479         const char *str;
2480
2481         // How many clients are there?
2482         for (i = 0;i < (unsigned int)svs.maxclients;i++)
2483         {
2484                 if (svs.clients[i].active)
2485                 {
2486                         nb_clients++;
2487                         if (!svs.clients[i].netconnection)
2488                                 nb_bots++;
2489                 }
2490         }
2491
2492         *qcstatus = 0;
2493         str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2494         if(str && *str)
2495         {
2496                 char *p;
2497                 const char *q;
2498                 p = qcstatus;
2499                 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2500                         if(*q != '\\' && *q != '\n')
2501                                 *p++ = *q;
2502                 *p = 0;
2503         }
2504
2505         /// \TODO: we should add more information for the full status string
2506         crypto_idstring = Crypto_GetInfoResponseDataString();
2507         length = dpsnprintf(out_msg, out_size,
2508                                                 "\377\377\377\377%s\x0A"
2509                                                 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2510                                                 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2511                                                 "%s%s"
2512                                                 "%s%s"
2513                                                 "%s%s"
2514                                                 "%s",
2515                                                 fullstatus ? "statusResponse" : "infoResponse",
2516                                                 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2517                                                 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2518                                                 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2519                                                 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2520                                                 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2521                                                 fullstatus ? "\n" : "");
2522
2523         // Make sure it fits in the buffer
2524         if (length < 0)
2525                 goto bad;
2526
2527         if (fullstatus)
2528         {
2529                 char *ptr;
2530                 int left;
2531                 int savelength;
2532
2533                 savelength = length;
2534
2535                 ptr = out_msg + length;
2536                 left = (int)out_size - length;
2537
2538                 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2539                 {
2540                         client_t *cl = &svs.clients[i];
2541                         if (cl->active)
2542                         {
2543                                 int nameind, cleanind, pingvalue;
2544                                 char curchar;
2545                                 char cleanname [sizeof(cl->name)];
2546                                 const char *str;
2547                                 prvm_edict_t *ed;
2548
2549                                 // Remove all characters '"' and '\' in the player name
2550                                 nameind = 0;
2551                                 cleanind = 0;
2552                                 do
2553                                 {
2554                                         curchar = cl->name[nameind++];
2555                                         if (curchar != '"' && curchar != '\\')
2556                                         {
2557                                                 cleanname[cleanind++] = curchar;
2558                                                 if (cleanind == sizeof(cleanname) - 1)
2559                                                         break;
2560                                         }
2561                                 } while (curchar != '\0');
2562                                 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2563
2564                                 pingvalue = (int)(cl->ping * 1000.0f);
2565                                 if(cl->netconnection)
2566                                         pingvalue = bound(1, pingvalue, 9999);
2567                                 else
2568                                         pingvalue = 0;
2569
2570                                 *qcstatus = 0;
2571                                 ed = PRVM_EDICT_NUM(i + 1);
2572                                 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2573                                 if(str && *str)
2574                                 {
2575                                         char *p;
2576                                         const char *q;
2577                                         p = qcstatus;
2578                                         for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2579                                                 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2580                                                         *p++ = *q;
2581                                         *p = 0;
2582                                 }
2583
2584                                 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2585                                 {
2586                                         if(cl->frags == -666) // spectator
2587                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2588                                         else if(cl->colors == 0x44) // red team
2589                                                 strlcpy(teambuf, " 1", sizeof(teambuf));
2590                                         else if(cl->colors == 0xDD) // blue team
2591                                                 strlcpy(teambuf, " 2", sizeof(teambuf));
2592                                         else if(cl->colors == 0xCC) // yellow team
2593                                                 strlcpy(teambuf, " 3", sizeof(teambuf));
2594                                         else if(cl->colors == 0x99) // pink team
2595                                                 strlcpy(teambuf, " 4", sizeof(teambuf));
2596                                         else
2597                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2598                                 }
2599                                 else
2600                                         *teambuf = 0;
2601
2602                                 // note: team number is inserted according to SoF2 protocol
2603                                 if(*qcstatus)
2604                                         length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2605                                                                                 qcstatus,
2606                                                                                 pingvalue,
2607                                                                                 teambuf,
2608                                                                                 cleanname);
2609                                 else
2610                                         length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2611                                                                                 cl->frags,
2612                                                                                 pingvalue,
2613                                                                                 teambuf,
2614                                                                                 cleanname);
2615
2616                                 if(length < 0)
2617                                 {
2618                                         // out of space?
2619                                         // turn it into an infoResponse!
2620                                         out_msg[savelength] = 0;
2621                                         memcpy(out_msg + 4, "infoResponse\x0A", 13);
2622                                         memmove(out_msg + 17, out_msg + 19, savelength - 19);
2623                                         break;
2624                                 }
2625                                 left -= length;
2626                                 ptr += length;
2627                         }
2628                 }
2629         }
2630
2631         return true;
2632
2633 bad:
2634         return false;
2635 }
2636
2637 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2638 {
2639         size_t floodslotnum, bestfloodslotnum;
2640         double bestfloodtime;
2641         lhnetaddress_t noportpeeraddress;
2642         // see if this is a connect flood
2643         noportpeeraddress = *peeraddress;
2644         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2645         bestfloodslotnum = 0;
2646         bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2647         for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2648         {
2649                 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2650                 {
2651                         bestfloodtime = floodlist[floodslotnum].lasttime;
2652                         bestfloodslotnum = floodslotnum;
2653                 }
2654                 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2655                 {
2656                         // this address matches an ongoing flood address
2657                         if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2658                         {
2659                                 if(renew)
2660                                 {
2661                                         // renew the ban on this address so it does not expire
2662                                         // until the flood has subsided
2663                                         floodlist[floodslotnum].lasttime = realtime;
2664                                 }
2665                                 //Con_Printf("Flood detected!\n");
2666                                 return true;
2667                         }
2668                         // the flood appears to have subsided, so allow this
2669                         bestfloodslotnum = floodslotnum; // reuse the same slot
2670                         break;
2671                 }
2672         }
2673         // begin a new timeout on this address
2674         floodlist[bestfloodslotnum].address = noportpeeraddress;
2675         floodlist[bestfloodslotnum].lasttime = realtime;
2676         //Con_Printf("Flood detection initiated!\n");
2677         return false;
2678 }
2679
2680 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2681 {
2682         size_t floodslotnum;
2683         lhnetaddress_t noportpeeraddress;
2684         // see if this is a connect flood
2685         noportpeeraddress = *peeraddress;
2686         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2687         for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2688         {
2689                 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2690                 {
2691                         // this address matches an ongoing flood address
2692                         // remove the ban
2693                         floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2694                         floodlist[floodslotnum].lasttime = 0;
2695                         //Con_Printf("Flood cleared!\n");
2696                 }
2697         }
2698 }
2699
2700 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2701
2702 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2703 {
2704         char mdfourbuf[16];
2705         long t1, t2;
2706
2707         t1 = (long) time(NULL);
2708         t2 = strtol(s, NULL, 0);
2709         if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2710                 return false;
2711
2712         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2713                 return false;
2714
2715         return !memcmp(mdfourbuf, hash, 16);
2716 }
2717
2718 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2719 {
2720         char mdfourbuf[16];
2721         int i;
2722
2723         if(slen < (int)(sizeof(challenge[0].string)) - 1)
2724                 return false;
2725
2726         // validate the challenge
2727         for (i = 0;i < MAX_CHALLENGES;i++)
2728                 if(challenge[i].time > 0)
2729                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2730                                 break;
2731         // if the challenge is not recognized, drop the packet
2732         if (i == MAX_CHALLENGES)
2733                 return false;
2734
2735         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2736                 return false;
2737
2738         if(memcmp(mdfourbuf, hash, 16))
2739                 return false;
2740
2741         // unmark challenge to prevent replay attacks
2742         challenge[i].time = 0;
2743
2744         return true;
2745 }
2746
2747 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2748 {
2749         return !strcmp(password, hash);
2750 }
2751
2752 /// returns a string describing the user level, or NULL for auth failure
2753 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)
2754 {
2755         const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2756         static char buf[MAX_INPUTLINE];
2757         qboolean hasquotes;
2758         qboolean restricted = false;
2759         qboolean have_usernames = false;
2760         static char vabuf[1024];
2761
2762         userpass_start = rcon_password.string;
2763         while((userpass_end = strchr(userpass_start, ' ')))
2764         {
2765                 have_usernames = true;
2766                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2767                 if(buf[0])
2768                         if(comparator(peeraddress, buf, password, cs, cslen))
2769                                 goto allow;
2770                 userpass_start = userpass_end + 1;
2771         }
2772         if(userpass_start[0])
2773         {
2774                 userpass_end = userpass_start + strlen(userpass_start);
2775                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2776                         goto allow;
2777         }
2778
2779         restricted = true;
2780         have_usernames = false;
2781         userpass_start = rcon_restricted_password.string;
2782         while((userpass_end = strchr(userpass_start, ' ')))
2783         {
2784                 have_usernames = true;
2785                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2786                 if(buf[0])
2787                         if(comparator(peeraddress, buf, password, cs, cslen))
2788                                 goto check;
2789                 userpass_start = userpass_end + 1;
2790         }
2791         if(userpass_start[0])
2792         {
2793                 userpass_end = userpass_start + strlen(userpass_start);
2794                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2795                         goto check;
2796         }
2797         
2798         return NULL; // DENIED
2799
2800 check:
2801         for(text = s; text != endpos; ++text)
2802                 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2803                         return NULL; // block possible exploits against the parser/alias expansion
2804
2805         while(s != endpos)
2806         {
2807                 size_t l = strlen(s);
2808                 if(l)
2809                 {
2810                         hasquotes = (strchr(s, '"') != NULL);
2811                         // sorry, we can't allow these substrings in wildcard expressions,
2812                         // as they can mess with the argument counts
2813                         text = rcon_restricted_commands.string;
2814                         while(COM_ParseToken_Console(&text))
2815                         {
2816                                 // com_token now contains a pattern to check for...
2817                                 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2818                                 {
2819                                         if(!hasquotes)
2820                                                 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2821                                                         goto match;
2822                                 }
2823                                 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2824                                 {
2825                                         if(!strcmp(com_token, s))
2826                                                 goto match;
2827                                 }
2828                                 else // single-arg expression? must match the beginning of the command
2829                                 {
2830                                         if(!strcmp(com_token, s))
2831                                                 goto match;
2832                                         if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2833                                                 goto match;
2834                                 }
2835                         }
2836                         // if we got here, nothing matched!
2837                         return NULL;
2838                 }
2839 match:
2840                 s += l + 1;
2841         }
2842
2843 allow:
2844         userpass_startpass = strchr(userpass_start, ':');
2845         if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2846                 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2847
2848         return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2849 }
2850
2851 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2852 {
2853         if(userlevel)
2854         {
2855                 // looks like a legitimate rcon command with the correct password
2856                 const char *s_ptr = s;
2857                 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2858                 while(s_ptr != endpos)
2859                 {
2860                         size_t l = strlen(s_ptr);
2861                         if(l)
2862                                 Con_Printf(" %s;", s_ptr);
2863                         s_ptr += l + 1;
2864                 }
2865                 Con_Printf("\n");
2866
2867                 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2868                         Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2869                 while(s != endpos)
2870                 {
2871                         size_t l = strlen(s);
2872                         if(l)
2873                         {
2874                                 client_t *host_client_save = host_client;
2875                                 Cmd_ExecuteString(s, src_command, true);
2876                                 host_client = host_client_save;
2877                                 // in case it is a command that changes host_client (like restart)
2878                         }
2879                         s += l + 1;
2880                 }
2881                 Con_Rcon_Redirect_End();
2882         }
2883         else
2884         {
2885                 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2886         }
2887 }
2888
2889 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2890 {
2891         int i, ret, clientnum, best;
2892         double besttime;
2893         client_t *client;
2894         char *s, *string, response[1400], addressstring2[128];
2895         static char stringbuf[16384]; // server only
2896         qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2897         char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2898         size_t sendlength, response_len;
2899         char infostringvalue[MAX_INPUTLINE];
2900         char vabuf[1024];
2901
2902         if (!sv.active)
2903                 return false;
2904
2905         // convert the address to a string incase we need it
2906         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2907
2908         // see if we can identify the sender as a local player
2909         // (this is necessary for rcon to send a reliable reply if the client is
2910         //  actually on the server, not sending remotely)
2911         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2912                 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2913                         break;
2914         if (i == svs.maxclients)
2915                 host_client = NULL;
2916
2917         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2918         {
2919                 // received a command string - strip off the packaging and put it
2920                 // into our string buffer with NULL termination
2921                 data += 4;
2922                 length -= 4;
2923                 length = min(length, (int)sizeof(stringbuf) - 1);
2924                 memcpy(stringbuf, data, length);
2925                 stringbuf[length] = 0;
2926                 string = stringbuf;
2927
2928                 if (developer_extra.integer)
2929                 {
2930                         Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2931                         Com_HexDumpToConsole(data, length);
2932                 }
2933
2934                 sendlength = sizeof(senddata) - 4;
2935                 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2936                 {
2937                         case CRYPTO_NOMATCH:
2938                                 // nothing to do
2939                                 break;
2940                         case CRYPTO_MATCH:
2941                                 if(sendlength)
2942                                 {
2943                                         memcpy(senddata, "\377\377\377\377", 4);
2944                                         NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2945                                 }
2946                                 break;
2947                         case CRYPTO_DISCARD:
2948                                 if(sendlength)
2949                                 {
2950                                         memcpy(senddata, "\377\377\377\377", 4);
2951                                         NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2952                                 }
2953                                 return true;
2954                                 break;
2955                         case CRYPTO_REPLACE:
2956                                 string = senddata+4;
2957                                 length = (int)sendlength;
2958                                 break;
2959                 }
2960
2961                 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2962                 {
2963                         for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2964                         {
2965                                 if(challenge[i].time > 0)
2966                                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2967                                                 break;
2968                                 if (besttime > challenge[i].time)
2969                                         besttime = challenge[best = i].time;
2970                         }
2971                         // if we did not find an exact match, choose the oldest and
2972                         // update address and string
2973                         if (i == MAX_CHALLENGES)
2974                         {
2975                                 i = best;
2976                                 challenge[i].address = *peeraddress;
2977                                 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2978                         }
2979                         else
2980                         {
2981                                 // flood control: drop if requesting challenge too often
2982                                 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2983                                         return true;
2984                         }
2985                         challenge[i].time = realtime;
2986                         // send the challenge
2987                         dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2988                         response_len = strlen(response) + 1;
2989                         Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2990                         NetConn_Write(mysocket, response, (int)response_len, peeraddress);
2991                         return true;
2992                 }
2993                 if (length > 8 && !memcmp(string, "connect\\", 8))
2994                 {
2995                         crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2996                         string += 7;
2997                         length -= 7;
2998
2999                         if(crypto && crypto->authenticated)
3000                         {
3001                                 // no need to check challenge
3002                                 if(crypto_developer.integer)
3003                                 {
3004                                         Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3005                                                         crypto->use_aes ? "Encrypted" : "Authenticated",
3006                                                         addressstring2,
3007                                                         crypto->client_idfp[0] ? crypto->client_idfp : "-",
3008                                                         (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3009                                                         crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3010                                                         crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3011                                                         (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3012                                                         crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3013                                                   );
3014                                 }
3015                         }
3016                         else
3017                         {
3018                                 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3019                                 {
3020                                         // validate the challenge
3021                                         for (i = 0;i < MAX_CHALLENGES;i++)
3022                                                 if(challenge[i].time > 0)
3023                                                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
3024                                                                 break;
3025                                         // if the challenge is not recognized, drop the packet
3026                                         if (i == MAX_CHALLENGES)
3027                                                 return true;
3028                                 }
3029                         }
3030
3031                         if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3032                                 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3033
3034                         if(!(islocal || sv_public.integer > -2))
3035                         {
3036                                 if (developer_extra.integer)
3037                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3038                                 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
3039                                 return true;
3040                         }
3041
3042                         // check engine protocol
3043                         if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3044                         {
3045                                 if (developer_extra.integer)
3046                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3047                                 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3048                                 return true;
3049                         }
3050
3051                         // see if this is a duplicate connection request or a disconnected
3052                         // client who is rejoining to the same client slot
3053                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3054                         {
3055                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3056                                 {
3057                                         // this is a known client...
3058                                         if(crypto && crypto->authenticated)
3059                                         {
3060                                                 // reject if changing key!
3061                                                 if(client->netconnection->crypto.authenticated)
3062                                                 {
3063                                                         if(
3064                                                                         strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3065                                                                         ||
3066                                                                         strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3067                                                                         ||
3068                                                                         strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3069                                                                         ||
3070                                                                         strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3071                                                           )
3072                                                         {
3073                                                                 if (developer_extra.integer)
3074                                                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3075                                                                 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3076                                                                 return true;
3077                                                         }
3078                                                 }
3079                                         }
3080                                         else
3081                                         {
3082                                                 // reject if downgrading!
3083                                                 if(client->netconnection->crypto.authenticated)
3084                                                 {
3085                                                         if (developer_extra.integer)
3086                                                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3087                                                         NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3088                                                         return true;
3089                                                 }
3090                                         }
3091                                         if (client->begun)
3092                                         {
3093                                                 // client crashed and is coming back,
3094                                                 // keep their stuff intact
3095                                                 if (developer_extra.integer)
3096                                                         Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3097                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3098                                                 if(crypto && crypto->authenticated)
3099                                                         Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3100                                                 SV_SendServerinfo(client);
3101                                         }
3102                                         else
3103                                         {
3104                                                 // client is still trying to connect,
3105                                                 // so we send a duplicate reply
3106                                                 if (developer_extra.integer)
3107                                                         Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3108                                                 if(crypto && crypto->authenticated)
3109                                                         Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3110                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3111                                         }
3112                                         return true;
3113                                 }
3114                         }
3115
3116                         if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3117                                 return true;
3118
3119                         // find an empty client slot for this new client
3120                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3121                         {
3122                                 netconn_t *conn;
3123                                 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3124                                 {
3125                                         // allocated connection
3126                                         if (developer_extra.integer)
3127                                                 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3128                                         NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3129                                         // now set up the client
3130                                         if(crypto && crypto->authenticated)
3131                                                 Crypto_FinishInstance(&conn->crypto, crypto);
3132                                         SV_ConnectClient(clientnum, conn);
3133                                         NetConn_Heartbeat(1);
3134                                         return true;
3135                                 }
3136                         }
3137
3138                         // no empty slots found - server is full
3139                         if (developer_extra.integer)
3140                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3141                         NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3142
3143                         return true;
3144                 }
3145                 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3146                 {
3147                         const char *challenge = NULL;
3148
3149                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3150                                 return true;
3151
3152                         // If there was a challenge in the getinfo message
3153                         if (length > 8 && string[7] == ' ')
3154                                 challenge = string + 8;
3155
3156                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3157                         {
3158                                 if (developer_extra.integer)
3159                                         Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3160                                 NetConn_WriteString(mysocket, response, peeraddress);
3161                         }
3162                         return true;
3163                 }
3164                 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3165                 {
3166                         const char *challenge = NULL;
3167
3168                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3169                                 return true;
3170
3171                         // If there was a challenge in the getinfo message
3172                         if (length > 10 && string[9] == ' ')
3173                                 challenge = string + 10;
3174
3175                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3176                         {
3177                                 if (developer_extra.integer)
3178                                         Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3179                                 NetConn_WriteString(mysocket, response, peeraddress);
3180                         }
3181                         return true;
3182                 }
3183                 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3184                 {
3185                         char *password = string + 20;
3186                         char *timeval = string + 37;
3187                         char *s = strchr(timeval, ' ');
3188                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3189                         const char *userlevel;
3190
3191                         if(rcon_secure.integer > 1)
3192                                 return true;
3193
3194                         if(!s)
3195                                 return true; // invalid packet
3196                         ++s;
3197
3198                         userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3199                         RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3200                         return true;
3201                 }
3202                 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3203                 {
3204                         char *password = string + 25;
3205                         char *challenge = string + 42;
3206                         char *s = strchr(challenge, ' ');
3207                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3208                         const char *userlevel;
3209                         if(!s)
3210                                 return true; // invalid packet
3211                         ++s;
3212
3213                         userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3214                         RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3215                         return true;
3216                 }
3217                 if (length >= 5 && !memcmp(string, "rcon ", 5))
3218                 {
3219                         int i;
3220                         char *s = string + 5;
3221                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3222                         char password[64];
3223
3224                         if(rcon_secure.integer > 0)
3225                                 return true;
3226
3227                         for (i = 0;!ISWHITESPACE(*s);s++)
3228                                 if (i < (int)sizeof(password) - 1)
3229                                         password[i++] = *s;
3230                         if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3231                                 ++s;
3232                         password[i] = 0;
3233                         if (!ISWHITESPACE(password[0]))
3234                         {
3235                                 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3236                                 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3237                         }
3238                         return true;
3239                 }
3240                 if (!strncmp(string, "extResponse ", 12))
3241                 {
3242                         ++sv_net_extresponse_count;
3243                         if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3244                                 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3245                         sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3246                         dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3247                         return true;
3248                 }
3249                 if (!strncmp(string, "ping", 4))
3250                 {
3251                         if (developer_extra.integer)
3252                                 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3253                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3254                         return true;
3255                 }
3256                 if (!strncmp(string, "ack", 3))
3257                         return true;
3258                 // we may not have liked the packet, but it was a command packet, so
3259                 // we're done processing this packet now
3260                 return true;
3261         }
3262         // netquake control packets, supported for compatibility only, and only
3263         // when running game protocols that are normally served via this connection
3264         // protocol
3265         // (this protects more modern protocols against being used for
3266         //  Quake packet flood Denial Of Service attacks)
3267         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)
3268         {
3269                 int c;
3270                 int protocolnumber;
3271                 const char *protocolname;
3272                 data += 4;
3273                 length -= 4;
3274                 SZ_Clear(&sv_message);
3275                 SZ_Write(&sv_message, data, length);
3276                 MSG_BeginReading(&sv_message);
3277                 c = MSG_ReadByte(&sv_message);
3278                 switch (c)
3279                 {
3280                 case CCREQ_CONNECT:
3281                         if (developer_extra.integer)
3282                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3283                         if(!(islocal || sv_public.integer > -2))
3284                         {
3285                                 if (developer_extra.integer)
3286                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3287                                 SZ_Clear(&sv_message);
3288                                 // save space for the header, filled in later
3289                                 MSG_WriteLong(&sv_message, 0);
3290                                 MSG_WriteByte(&sv_message, CCREP_REJECT);
3291                                 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3292                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3293                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3294                                 SZ_Clear(&sv_message);
3295                                 break;
3296                         }
3297
3298                         protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3299                         protocolnumber = MSG_ReadByte(&sv_message);
3300                         if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3301                         {
3302                                 if (developer_extra.integer)
3303                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3304                                 SZ_Clear(&sv_message);
3305                                 // save space for the header, filled in later
3306                                 MSG_WriteLong(&sv_message, 0);
3307                                 MSG_WriteByte(&sv_message, CCREP_REJECT);
3308                                 MSG_WriteString(&sv_message, "Incompatible version.\n");
3309                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3310                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3311                                 SZ_Clear(&sv_message);
3312                                 break;
3313                         }
3314
3315                         // see if this connect request comes from a known client
3316                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3317                         {
3318                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3319                                 {
3320                                         // this is either a duplicate connection request
3321                                         // or coming back from a timeout
3322                                         // (if so, keep their stuff intact)
3323
3324                                         crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3325                                         if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3326                                         {
3327                                                 if (developer_extra.integer)
3328                                                         Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3329                                                 SZ_Clear(&sv_message);
3330                                                 // save space for the header, filled in later
3331                                                 MSG_WriteLong(&sv_message, 0);
3332                                                 MSG_WriteByte(&sv_message, CCREP_REJECT);
3333                                                 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3334                                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3335                                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3336                                                 SZ_Clear(&sv_message);
3337                                                 return true;
3338                                         }
3339
3340                                         // send a reply
3341                                         if (developer_extra.integer)
3342                                                 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3343                                         SZ_Clear(&sv_message);
3344                                         // save space for the header, filled in later
3345                                         MSG_WriteLong(&sv_message, 0);
3346                                         MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3347                                         MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3348                                         StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3349                                         NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3350                                         SZ_Clear(&sv_message);
3351
3352                                         // if client is already spawned, re-send the
3353                                         // serverinfo message as they'll need it to play
3354                                         if (client->begun)
3355                                                 SV_SendServerinfo(client);
3356                                         return true;
3357                                 }
3358                         }
3359
3360                         // this is a new client, check for connection flood
3361                         if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3362                                 break;
3363
3364                         // find a slot for the new client
3365                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3366                         {
3367                                 netconn_t *conn;
3368                                 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3369                                 {
3370                                         // connect to the client
3371                                         // everything is allocated, just fill in the details
3372                                         strlcpy (conn->address, addressstring2, sizeof (conn->address));
3373                                         if (developer_extra.integer)
3374                                                 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3375                                         // send back the info about the server connection
3376                                         SZ_Clear(&sv_message);
3377                                         // save space for the header, filled in later
3378                                         MSG_WriteLong(&sv_message, 0);
3379                                         MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3380                                         MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3381                                         StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3382                                         NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3383                                         SZ_Clear(&sv_message);
3384                                         // now set up the client struct
3385                                         SV_ConnectClient(clientnum, conn);
3386                                         NetConn_Heartbeat(1);
3387                                         return true;
3388                                 }
3389                         }
3390
3391                         if (developer_extra.integer)
3392                                 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3393                         // no room; try to let player know
3394                         SZ_Clear(&sv_message);
3395                         // save space for the header, filled in later
3396                         MSG_WriteLong(&sv_message, 0);
3397                         MSG_WriteByte(&sv_message, CCREP_REJECT);
3398                         MSG_WriteString(&sv_message, "Server is full.\n");
3399                         StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3400                         NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3401                         SZ_Clear(&sv_message);
3402                         break;
3403                 case CCREQ_SERVER_INFO:
3404                         if (developer_extra.integer)
3405                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3406                         if(!(islocal || sv_public.integer > -1))
3407                                 break;
3408
3409                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3410                                 break;
3411
3412                         if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3413                         {
3414                                 int numclients;
3415                                 char myaddressstring[128];
3416                                 if (developer_extra.integer)
3417                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3418                                 SZ_Clear(&sv_message);
3419                                 // save space for the header, filled in later
3420                                 MSG_WriteLong(&sv_message, 0);
3421                                 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3422                                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3423                                 MSG_WriteString(&sv_message, myaddressstring);
3424                                 MSG_WriteString(&sv_message, hostname.string);
3425                                 MSG_WriteString(&sv_message, sv.name);
3426                                 // How many clients are there?
3427                                 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3428                                         if (svs.clients[i].active)
3429                                                 numclients++;
3430                                 MSG_WriteByte(&sv_message, numclients);
3431                                 MSG_WriteByte(&sv_message, svs.maxclients);
3432                                 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3433                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3434                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3435                                 SZ_Clear(&sv_message);
3436                         }
3437                         break;
3438                 case CCREQ_PLAYER_INFO:
3439                         if (developer_extra.integer)
3440                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3441                         if(!(islocal || sv_public.integer > -1))
3442                                 break;
3443
3444                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3445                                 break;
3446
3447                         if (sv.active)
3448                         {
3449                                 int playerNumber, activeNumber, clientNumber;
3450                                 client_t *client;
3451
3452                                 playerNumber = MSG_ReadByte(&sv_message);
3453                                 activeNumber = -1;
3454                                 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3455                                         if (client->active && ++activeNumber == playerNumber)
3456                                                 break;
3457                                 if (clientNumber != svs.maxclients)
3458                                 {
3459                                         SZ_Clear(&sv_message);
3460                                         // save space for the header, filled in later
3461                                         MSG_WriteLong(&sv_message, 0);
3462                                         MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3463                                         MSG_WriteByte(&sv_message, playerNumber);
3464                                         MSG_WriteString(&sv_message, client->name);
3465                                         MSG_WriteLong(&sv_message, client->colors);
3466                                         MSG_WriteLong(&sv_message, client->frags);
3467                                         MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3468                                         if(sv_status_privacy.integer)
3469                                                 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3470                                         else
3471                                                 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3472                                         StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3473                                         NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3474                                         SZ_Clear(&sv_message);
3475                                 }
3476                         }
3477                         break;
3478                 case CCREQ_RULE_INFO:
3479                         if (developer_extra.integer)
3480                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3481                         if(!(islocal || sv_public.integer > -1))
3482                                 break;
3483
3484                         // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3485
3486                         if (sv.active)
3487                         {
3488                                 char *prevCvarName;
3489                                 cvar_t *var;
3490
3491                                 // find the search start location
3492                                 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3493                                 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3494
3495                                 // send the response
3496                                 SZ_Clear(&sv_message);
3497                                 // save space for the header, filled in later
3498                                 MSG_WriteLong(&sv_message, 0);
3499                                 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3500                                 if (var)
3501                                 {
3502                                         MSG_WriteString(&sv_message, var->name);
3503                                         MSG_WriteString(&sv_message, var->string);
3504                                 }
3505                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3506                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3507                                 SZ_Clear(&sv_message);
3508                         }
3509                         break;
3510                 case CCREQ_RCON:
3511                         if (developer_extra.integer)
3512                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3513                         if (sv.active && !rcon_secure.integer)
3514                         {
3515                                 char password[2048];
3516                                 char cmd[2048];
3517                                 char *s;
3518                                 char *endpos;
3519                                 const char *userlevel;
3520                                 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3521                                 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3522                                 s = cmd;
3523                                 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3524                                 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3525                                 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3526                                 return true;
3527                         }
3528                         break;
3529                 default:
3530                         break;
3531                 }
3532                 SZ_Clear(&sv_message);
3533                 // we may not have liked the packet, but it was a valid control
3534                 // packet, so we're done processing this packet now
3535                 return true;
3536         }
3537         if (host_client)
3538         {
3539                 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3540                 {
3541                         SV_ReadClientMessage();
3542                         return ret;
3543                 }
3544         }
3545         return 0;
3546 }
3547
3548 void NetConn_ServerFrame(void)
3549 {
3550         int i, length;
3551         lhnetaddress_t peeraddress;
3552         unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3553         for (i = 0;i < sv_numsockets;i++)
3554                 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3555                         NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3556         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3557         {
3558                 // never timeout loopback connections
3559                 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3560                 {
3561                         Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3562                         SV_DropClient(false);
3563                 }
3564         }
3565 }
3566
3567 void NetConn_SleepMicroseconds(int microseconds)
3568 {
3569         LHNET_SleepUntilPacket_Microseconds(microseconds);
3570 }
3571
3572 #ifdef CONFIG_MENU
3573 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3574 {
3575         int i, j;
3576         int masternum;
3577         lhnetaddress_t masteraddress;
3578         lhnetaddress_t broadcastaddress;
3579         char request[256];
3580
3581         if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3582                 return;
3583
3584         // 26000 is the default quake server port, servers on other ports will not
3585         // be found
3586         // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3587         LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3588
3589         if (querydp)
3590         {
3591                 for (i = 0;i < cl_numsockets;i++)
3592                 {
3593                         if (cl_sockets[i])
3594                         {
3595                                 const char *cmdname, *extraoptions;
3596                                 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3597
3598                                 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3599                                 {
3600                                         // search LAN for Quake servers
3601                                         SZ_Clear(&cl_message);
3602                                         // save space for the header, filled in later
3603                                         MSG_WriteLong(&cl_message, 0);
3604                                         MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3605                                         MSG_WriteString(&cl_message, "QUAKE");
3606                                         MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3607                                         StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3608                                         NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3609                                         SZ_Clear(&cl_message);
3610
3611                                         // search LAN for DarkPlaces servers
3612                                         NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3613                                 }
3614
3615                                 // build the getservers message to send to the dpmaster master servers
3616                                 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3617                                 {
3618                                         cmdname = "getserversExt";
3619                                         extraoptions = " ipv4 ipv6";  // ask for IPv4 and IPv6 servers
3620                                 }
3621                                 else
3622                                 {
3623                                         cmdname = "getservers";
3624                                         extraoptions = "";
3625                                 }
3626                                 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3627
3628                                 // search internet
3629                                 for (masternum = 0;sv_masters[masternum].name;masternum++)
3630                                 {
3631                                         if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3632                                         {
3633                                                 masterquerycount++;
3634                                                 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3635                                         }
3636                                 }
3637
3638                                 // search favorite servers
3639                                 for(j = 0; j < nFavorites; ++j)
3640                                 {
3641                                         if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3642                                         {
3643                                                 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3644                                                         NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3645                                         }
3646                                 }
3647                         }
3648                 }
3649         }
3650
3651         // only query QuakeWorld servers when the user wants to
3652         if (queryqw)
3653         {
3654                 for (i = 0;i < cl_numsockets;i++)
3655                 {
3656                         if (cl_sockets[i])
3657                         {
3658                                 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3659
3660                                 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3661                                 {
3662                                         // search LAN for QuakeWorld servers
3663                                         NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3664
3665                                         // build the getservers message to send to the qwmaster master servers
3666                                         // note this has no -1 prefix, and the trailing nul byte is sent
3667                                         dpsnprintf(request, sizeof(request), "c\n");
3668                                 }
3669
3670                                 // search internet
3671                                 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3672                                 {
3673                                         if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3674                                         {
3675                                                 if (m_state != m_slist)
3676                                                 {
3677                                                         char lookupstring[128];
3678                                                         LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3679                                                         Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3680                                                 }
3681                                                 masterquerycount++;
3682                                                 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3683                                         }
3684                                 }
3685
3686                                 // search favorite servers
3687                                 for(j = 0; j < nFavorites; ++j)
3688                                 {
3689                                         if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3690                                         {
3691                                                 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3692                                                 {
3693                                                         NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3694                                                         NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3695                                                 }
3696                                         }
3697                                 }
3698                         }
3699                 }
3700         }
3701         if (!masterquerycount)
3702         {
3703                 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3704                 M_Update_Return_Reason("No network");
3705         }
3706 }
3707 #endif
3708
3709 void NetConn_Heartbeat(int priority)
3710 {
3711         lhnetaddress_t masteraddress;
3712         int masternum;
3713         lhnetsocket_t *mysocket;
3714
3715         // if it's a state change (client connected), limit next heartbeat to no
3716         // more than 30 sec in the future
3717         if (priority == 1 && nextheartbeattime > realtime + 30.0)
3718                 nextheartbeattime = realtime + 30.0;
3719
3720         // limit heartbeatperiod to 30 to 270 second range,
3721         // lower limit is to avoid abusing master servers with excess traffic,
3722         // upper limit is to avoid timing out on the master server (which uses
3723         // 300 sec timeout)
3724         if (sv_heartbeatperiod.value < 30)
3725                 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3726         if (sv_heartbeatperiod.value > 270)
3727                 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3728
3729         // make advertising optional and don't advertise singleplayer games, and
3730         // only send a heartbeat as often as the admin wants
3731         if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3732         {
3733                 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3734                 for (masternum = 0;sv_masters[masternum].name;masternum++)
3735                         if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3736                                 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3737         }
3738 }
3739
3740 static void Net_Heartbeat_f(void)
3741 {
3742         if (sv.active)
3743                 NetConn_Heartbeat(2);
3744         else
3745                 Con_Print("No server running, can not heartbeat to master server.\n");
3746 }
3747
3748 static void PrintStats(netconn_t *conn)
3749 {
3750         if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3751                 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3752         else
3753                 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3754         Con_Printf("unreliable messages sent   = %i\n", conn->unreliableMessagesSent);
3755         Con_Printf("unreliable messages recv   = %i\n", conn->unreliableMessagesReceived);
3756         Con_Printf("reliable messages sent     = %i\n", conn->reliableMessagesSent);
3757         Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3758         Con_Printf("packetsSent                = %i\n", conn->packetsSent);
3759         Con_Printf("packetsReSent              = %i\n", conn->packetsReSent);
3760         Con_Printf("packetsReceived            = %i\n", conn->packetsReceived);
3761         Con_Printf("receivedDuplicateCount     = %i\n", conn->receivedDuplicateCount);
3762         Con_Printf("droppedDatagrams           = %i\n", conn->droppedDatagrams);
3763 }
3764
3765 void Net_Stats_f(void)
3766 {
3767         netconn_t *conn;
3768         Con_Print("connections                =\n");
3769         for (conn = netconn_list;conn;conn = conn->next)
3770                 PrintStats(conn);
3771 }
3772
3773 #ifdef CONFIG_MENU
3774 void Net_Refresh_f(void)
3775 {
3776         if (m_state != m_slist) {
3777                 Con_Print("Sending new requests to master servers\n");
3778                 ServerList_QueryList(false, true, false, true);
3779                 Con_Print("Listening for replies...\n");
3780         } else
3781                 ServerList_QueryList(false, true, false, false);
3782 }
3783
3784 void Net_Slist_f(void)
3785 {
3786         ServerList_ResetMasks();
3787         serverlist_sortbyfield = SLIF_PING;
3788         serverlist_sortflags = 0;
3789     if (m_state != m_slist) {
3790                 Con_Print("Sending requests to master servers\n");
3791                 ServerList_QueryList(true, true, false, true);
3792                 Con_Print("Listening for replies...\n");
3793         } else
3794                 ServerList_QueryList(true, true, false, false);
3795 }
3796
3797 void Net_SlistQW_f(void)
3798 {
3799         ServerList_ResetMasks();
3800         serverlist_sortbyfield = SLIF_PING;
3801         serverlist_sortflags = 0;
3802     if (m_state != m_slist) {
3803                 Con_Print("Sending requests to master servers\n");
3804                 ServerList_QueryList(true, false, true, true);
3805                 serverlist_consoleoutput = true;
3806                 Con_Print("Listening for replies...\n");
3807         } else
3808                 ServerList_QueryList(true, false, true, false);
3809 }
3810 #endif
3811
3812 void NetConn_Init(void)
3813 {
3814         int i;
3815         lhnetaddress_t tempaddress;
3816         netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3817         Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3818 #ifdef CONFIG_MENU
3819         Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3820         Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3821         Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3822 #endif
3823         Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3824         Cvar_RegisterVariable(&net_test);
3825         Cvar_RegisterVariable(&net_usesizelimit);
3826         Cvar_RegisterVariable(&net_burstreserve);
3827         Cvar_RegisterVariable(&rcon_restricted_password);
3828         Cvar_RegisterVariable(&rcon_restricted_commands);
3829         Cvar_RegisterVariable(&rcon_secure_maxdiff);
3830         Cvar_RegisterVariable(&net_slist_queriespersecond);
3831         Cvar_RegisterVariable(&net_slist_queriesperframe);
3832         Cvar_RegisterVariable(&net_slist_timeout);
3833         Cvar_RegisterVariable(&net_slist_maxtries);
3834         Cvar_RegisterVariable(&net_slist_favorites);
3835         Cvar_RegisterVariable(&net_slist_pause);
3836         if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3837                 Cvar_RegisterVariable(&net_tos_dscp);
3838         Cvar_RegisterVariable(&net_messagetimeout);
3839         Cvar_RegisterVariable(&net_connecttimeout);
3840         Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3841         Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3842         Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3843         Cvar_RegisterVariable(&cl_netlocalping);
3844         Cvar_RegisterVariable(&cl_netpacketloss_send);
3845         Cvar_RegisterVariable(&cl_netpacketloss_receive);
3846         Cvar_RegisterVariable(&hostname);
3847         Cvar_RegisterVariable(&developer_networking);
3848         Cvar_RegisterVariable(&cl_netport);
3849         Cvar_RegisterVariable(&sv_netport);
3850         Cvar_RegisterVariable(&net_address);
3851         Cvar_RegisterVariable(&net_address_ipv6);
3852         Cvar_RegisterVariable(&sv_public);
3853         Cvar_RegisterVariable(&sv_public_rejectreason);
3854         Cvar_RegisterVariable(&sv_heartbeatperiod);
3855         for (i = 0;sv_masters[i].name;i++)
3856                 Cvar_RegisterVariable(&sv_masters[i]);
3857         Cvar_RegisterVariable(&gameversion);
3858         Cvar_RegisterVariable(&gameversion_min);
3859         Cvar_RegisterVariable(&gameversion_max);
3860 // 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.
3861         if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3862         {
3863                 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3864                 {
3865                         Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3866                         Cvar_SetQuick(&net_address, com_argv[i + 1]);
3867                 }
3868                 else
3869                         Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3870         }
3871 // 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
3872         if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3873         {
3874                 i = atoi(com_argv[i + 1]);
3875                 if (i >= 0 && i < 65536)
3876                 {
3877                         Con_Printf("-port option used, setting port cvar to %i\n", i);
3878                         Cvar_SetValueQuick(&sv_netport, i);
3879                 }
3880                 else
3881                         Con_Printf("-port option used, but %i is not a valid port number\n", i);
3882         }
3883         cl_numsockets = 0;
3884         sv_numsockets = 0;
3885         cl_message.data = cl_message_buf;
3886         cl_message.maxsize = sizeof(cl_message_buf);
3887         cl_message.cursize = 0;
3888         sv_message.data = sv_message_buf;
3889         sv_message.maxsize = sizeof(sv_message_buf);
3890         sv_message.cursize = 0;
3891         LHNET_Init();
3892         if (Thread_HasThreads())
3893                 netconn_mutex = Thread_CreateMutex();
3894 }
3895
3896 void NetConn_Shutdown(void)
3897 {
3898         NetConn_CloseClientPorts();
3899         NetConn_CloseServerPorts();
3900         LHNET_Shutdown();
3901         if (netconn_mutex)
3902                 Thread_DestroyMutex(netconn_mutex);
3903         netconn_mutex = NULL;
3904 }
3905