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