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