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