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