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