2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Ashley Rose Hale (LadyHavoc)
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // for secure rcon authentication
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
35 // note this defaults on for dedicated servers, off for listen servers
36 cvar_t sv_public = {CF_SERVER, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
37 cvar_t sv_public_rejectreason = {CF_SERVER, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CF_SERVER | CF_ARCHIVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
39 extern cvar_t sv_status_privacy;
41 static cvar_t sv_masters [] =
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)"},
53 static cvar_t sv_qwmasters [] =
55 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
56 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
57 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
58 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
59 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
60 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
61 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
62 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
66 static double nextheartbeattime = 0;
70 static unsigned char cl_message_buf[NET_MAXMESSAGE];
71 static unsigned char sv_message_buf[NET_MAXMESSAGE];
72 char cl_readstring[MAX_INPUTLINE];
73 char sv_readstring[MAX_INPUTLINE];
75 cvar_t net_test = {CF_CLIENT | CF_SERVER, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
76 cvar_t net_usesizelimit = {CF_SERVER, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
77 cvar_t net_burstreserve = {CF_SERVER, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
78 cvar_t net_messagetimeout = {CF_CLIENT | CF_SERVER, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
79 cvar_t net_connecttimeout = {CF_CLIENT | CF_SERVER, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
80 cvar_t net_connectfloodblockingtimeout = {CF_SERVER, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
81 cvar_t net_challengefloodblockingtimeout = {CF_SERVER, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
82 cvar_t net_getstatusfloodblockingtimeout = {CF_SERVER, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 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."};
83 cvar_t net_sourceaddresscheck = {CF_CLIENT, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
84 cvar_t hostname = {CF_SERVER | CF_ARCHIVE, "hostname", "UNNAMED", "server message to show in server browser"};
85 cvar_t developer_networking = {CF_CLIENT | CF_SERVER, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
87 cvar_t net_fakelag = {CF_CLIENT, "net_fakelag","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
88 static cvar_t net_fakeloss_send = {CF_CLIENT, "net_fakeloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
89 static cvar_t net_fakeloss_receive = {CF_CLIENT, "net_fakeloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
92 static cvar_t net_slist_debug = {CF_CLIENT, "net_slist_debug", "0", "enables verbose messages for master server queries"};
93 static cvar_t net_slist_favorites = {CF_CLIENT | CF_ARCHIVE, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
94 static cvar_t net_slist_interval = {CF_CLIENT, "net_slist_interval", "1.125", "minimum number of seconds to wait between getstatus queries to the same DP server, must be >= server's net_getstatusfloodblockingtimeout"};
95 static 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"};
96 static cvar_t net_slist_maxtries = {CF_CLIENT, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
97 static cvar_t net_slist_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"};
98 static cvar_t net_slist_queriespersecond = {CF_CLIENT, "net_slist_queriespersecond", "128", "how many server information requests to send per second"};
99 static 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)"};
100 static 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"};
103 static cvar_t net_tos_dscp = {CF_CLIENT | CF_ARCHIVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
104 static cvar_t gameversion = {CF_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
105 static cvar_t gameversion_min = {CF_CLIENT | CF_SERVER, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
106 static cvar_t gameversion_max = {CF_CLIENT | CF_SERVER, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
107 static cvar_t rcon_restricted_password = {CF_SERVER | CF_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
108 static cvar_t rcon_restricted_commands = {CF_SERVER, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
109 static cvar_t rcon_secure_maxdiff = {CF_SERVER, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
110 extern cvar_t rcon_secure;
111 extern cvar_t rcon_secure_challengetimeout;
113 double masterquerytime = -1000;
114 unsigned masterquerycount = 0;
115 unsigned masterreplycount = 0;
116 unsigned serverquerycount = 0;
117 unsigned serverreplycount = 0;
119 challenge_t challenges[MAX_CHALLENGES];
121 #define DPMASTER_COUNT sizeof(sv_masters) / sizeof(cvar_t)
122 #define QWMASTER_COUNT sizeof(sv_qwmasters) / sizeof(cvar_t)
124 /// bitfield because in theory we could be doing QW & DP simultaneously
125 uint8_t serverlist_querystage = 0;
128 #define SLIST_QUERYSTAGE_DPMASTERS 1
129 #define SLIST_QUERYSTAGE_QWMASTERS 2
130 #define SLIST_QUERYSTAGE_SERVERS 4
132 static uint8_t dpmasterstatus[DPMASTER_COUNT] = {0};
133 static uint8_t qwmasterstatus[QWMASTER_COUNT] = {0};
134 #define MASTER_TX_QUERY 1 // we sent the query
135 #define MASTER_RX_RESPONSE 2 // we got at least 1 packet of the response
136 #define MASTER_RX_COMPLETE 3 // we saw the EOT marker (assumes dpmaster >= 2.0, see dpmaster/doc/techinfo.txt)
138 /// the hash password for timestamp verification
139 char serverlist_dpserverquerykey[12]; // challenge_t uses [12]
142 static unsigned cl_numsockets;
143 static lhnetsocket_t *cl_sockets[16];
144 static unsigned sv_numsockets;
145 static lhnetsocket_t *sv_sockets[16];
147 netconn_t *netconn_list = NULL;
148 mempool_t *netconn_mempool = NULL;
149 void *netconn_mutex = NULL;
151 cvar_t cl_netport = {CF_CLIENT, "cl_port", "0", "forces client to use chosen port number if not 0"};
152 cvar_t sv_netport = {CF_SERVER, "port", "26000", "server port for players to connect to"};
153 cvar_t net_address = {CF_CLIENT | CF_SERVER, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
154 cvar_t net_address_ipv6 = {CF_CLIENT | CF_SERVER, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
156 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
157 unsigned cl_net_extresponse_count = 0;
158 unsigned cl_net_extresponse_last = 0;
160 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
161 unsigned sv_net_extresponse_count = 0;
162 unsigned sv_net_extresponse_last = 0;
165 // ServerList interface
166 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
167 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
169 serverlist_infofield_t serverlist_sortbyfield;
170 unsigned serverlist_sortflags;
172 unsigned serverlist_viewcount = 0;
173 uint16_t serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
175 unsigned serverlist_maxcachecount = 0;
176 unsigned serverlist_cachecount = 0;
177 serverlist_entry_t *serverlist_cache = NULL;
179 static qbool serverlist_consoleoutput;
181 static unsigned nFavorites = 0;
182 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
183 static unsigned nFavorites_idfp = 0;
184 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
186 void NetConn_UpdateFavorites_c(cvar_t *var)
192 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
194 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
195 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
196 // (if v6 address contains port, it must start with '[')
198 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
203 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
209 /// helper function to insert a value into the viewset
210 /// spare entries will be removed
211 static void _ServerList_ViewList_Helper_InsertBefore(unsigned index, serverlist_entry_t *entry)
215 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
216 i = serverlist_viewcount++;
218 i = SERVERLIST_VIEWLISTSIZE - 1;
221 for( ; i > index ; i-- )
222 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
224 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
227 /// we suppose serverlist_viewcount to be valid, ie > 0
228 static inline void _ServerList_ViewList_Helper_Remove(unsigned index)
230 serverlist_viewcount--;
231 for( ; index < serverlist_viewcount ; index++ )
232 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
235 /// \returns true if A should be inserted before B
236 static qbool _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
238 int result = 0; // > 0 if for numbers A > B and for text if A < B
240 if( serverlist_sortflags & SLSF_CATEGORIES )
242 result = A->info.category - B->info.category;
247 if( serverlist_sortflags & SLSF_FAVORITES )
249 if(A->info.isfavorite != B->info.isfavorite)
250 return A->info.isfavorite;
253 switch( serverlist_sortbyfield ) {
255 result = A->info.ping - B->info.ping;
257 case SLIF_MAXPLAYERS:
258 result = A->info.maxplayers - B->info.maxplayers;
260 case SLIF_NUMPLAYERS:
261 result = A->info.numplayers - B->info.numplayers;
264 result = A->info.numbots - B->info.numbots;
267 result = A->info.numhumans - B->info.numhumans;
270 result = A->info.freeslots - B->info.freeslots;
273 result = A->info.protocol - B->info.protocol;
276 result = strcmp( B->info.cname, A->info.cname );
279 result = strcasecmp( B->info.game, A->info.game );
282 result = strcasecmp( B->info.map, A->info.map );
285 result = strcasecmp( B->info.mod, A->info.mod );
288 result = strcasecmp( B->info.name, A->info.name );
291 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
294 result = A->info.category - B->info.category;
296 case SLIF_ISFAVORITE:
297 result = !!B->info.isfavorite - !!A->info.isfavorite;
300 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
306 if( serverlist_sortflags & SLSF_DESCENDING )
312 // if the chosen sort key is identical, sort by index
313 // (makes this a stable sort, so that later replies from servers won't
314 // shuffle the servers around when they have the same ping)
318 static qbool _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
320 // This should actually be done with some intermediate and end-of-function return
332 case SLMO_GREATEREQUAL:
334 case SLMO_NOTCONTAIN:
335 case SLMO_STARTSWITH:
336 case SLMO_NOTSTARTSWITH:
339 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
344 static qbool _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
347 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
348 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
349 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
350 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
352 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
353 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
356 // Same here, also using an intermediate & final return would be more appropriate
360 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
361 case SLMO_NOTCONTAIN:
362 return !*bufferB || !strstr( bufferA, bufferB );
363 case SLMO_STARTSWITH:
364 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
365 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
366 case SLMO_NOTSTARTSWITH:
367 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
369 return strcmp( bufferA, bufferB ) < 0;
371 return strcmp( bufferA, bufferB ) <= 0;
373 return strcmp( bufferA, bufferB ) == 0;
375 return strcmp( bufferA, bufferB ) > 0;
377 return strcmp( bufferA, bufferB ) != 0;
378 case SLMO_GREATEREQUAL:
379 return strcmp( bufferA, bufferB ) >= 0;
381 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
386 static qbool _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
388 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
390 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
392 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
394 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
396 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
398 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
400 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
402 if( *mask->info.cname
403 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
406 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
409 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
412 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
415 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
417 if( *mask->info.qcstatus
418 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
420 if( *mask->info.players
421 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
423 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
425 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
430 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
432 unsigned start, end, mid, i;
435 // reject incompatible servers
437 entry->info.gameversion != gameversion.integer
440 gameversion_min.integer >= 0 // min/max range set by user/mod?
441 && gameversion_max.integer >= 0
442 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
443 && gameversion_max.integer >= entry->info.gameversion
448 // also display entries that are currently being refreshed [11/8/2007 Black]
449 // bones_was_here: if their previous ping was acceptable (unset if timeout occurs)
450 if (!entry->info.ping)
453 // refresh the "favorite" status
454 entry->info.isfavorite = false;
455 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
457 char idfp[FP64_SIZE+1];
458 for(i = 0; i < nFavorites; ++i)
460 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
462 entry->info.isfavorite = true;
466 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
468 for(i = 0; i < nFavorites_idfp; ++i)
470 if(!strcmp(idfp, favorites_idfp[i]))
472 entry->info.isfavorite = true;
479 // refresh the "category"
480 entry->info.category = MR_GetServerListEntryCategory(entry);
482 // FIXME: change this to be more readable (...)
483 // now check whether it passes through the masks
484 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
485 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
488 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
489 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
491 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
494 if( !serverlist_viewcount ) {
495 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
498 // ok, insert it, we just need to find out where exactly:
501 // check whether to insert it as new first item
502 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
503 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
505 } // check whether to insert it as new last item
506 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
507 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
511 end = serverlist_viewcount - 1;
512 while( end > start + 1 )
514 mid = (start + end) / 2;
515 // test the item that lies in the middle between start and end
516 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
517 // the item has to be in the upper half
520 // the item has to be in the lower half
523 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
526 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
530 for( i = 0; i < serverlist_viewcount; i++ )
532 if (ServerList_GetViewEntry(i) == entry)
534 _ServerList_ViewList_Helper_Remove(i);
540 void ServerList_RebuildViewList(cvar_t *var)
544 if (net_slist_pause.integer)
547 serverlist_viewcount = 0;
548 for (i = 0; i < serverlist_cachecount; ++i)
549 ServerList_ViewList_Insert(&serverlist_cache[i]);
552 void ServerList_ResetMasks(void)
556 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
557 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
558 // numbots needs to be compared to -1 to always succeed
559 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
560 serverlist_andmasks[i].info.numbots = -1;
561 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
562 serverlist_ormasks[i].info.numbots = -1;
565 void ServerList_GetPlayerStatistics(unsigned *numplayerspointer, unsigned *maxplayerspointer)
568 unsigned numplayers = 0, maxplayers = 0;
570 for (i = 0;i < serverlist_cachecount;i++)
572 if (serverlist_cache[i].info.ping)
574 numplayers += serverlist_cache[i].info.numhumans;
575 maxplayers += serverlist_cache[i].info.maxplayers;
578 *numplayerspointer = numplayers;
579 *maxplayerspointer = maxplayers;
583 static void _ServerList_Test(void)
586 if (serverlist_maxcachecount <= 1024)
588 serverlist_maxcachecount = 1024;
589 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
591 for( i = 0 ; i < 1024 ; i++ ) {
592 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
593 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
594 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
595 serverlist_cache[serverlist_cachecount].finished = true;
596 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 );
597 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
598 serverlist_cachecount++;
605 ServerList_BuildDPServerQuery
607 Generates the string for pinging DP servers with a hash-verified timestamp
608 to provide reliable pings while preventing ping cheating,
609 and discard spurious getstatus/getinfo packets.
611 14 bytes of header including the mandatory space,
612 22 bytes of base64-encoded hash,
613 up to 16 bytes of unsigned hexadecimal milliseconds (typically 4-5 bytes sent),
616 The maximum challenge length (after the space) for existing DP7 servers is 49.
619 static inline void ServerList_BuildDPServerQuery(char *buffer, size_t buffersize, double querytime)
621 unsigned char hash[24]; // 4*(16/3) rounded up to 4 byte multiple
622 uint64_t timestamp = querytime * 1000.0; // no rounding up as that could make small pings go <= 0
624 HMAC_MDFOUR_16BYTES(hash,
625 (unsigned char *)×tamp, sizeof(timestamp),
626 (unsigned char *)serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
627 base64_encode(hash, 16, sizeof(hash));
628 dpsnprintf(buffer, buffersize, "\377\377\377\377getstatus %.22s%" PRIx64, hash, timestamp);
631 static void NetConn_BuildChallengeString(char *buffer, int bufferlength);
632 void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool consoleoutput)
635 lhnetaddress_t broadcastaddress;
636 char dpquery[53]; // theoretical max: 14+22+16+1
638 if (net_slist_debug.integer)
639 Con_Printf("^2Querying master, favourite and LAN servers, reset=%u\n", resetcache);
640 serverlist_querystage = (querydp ? SLIST_QUERYSTAGE_DPMASTERS : 0) | (queryqw ? SLIST_QUERYSTAGE_QWMASTERS : 0);
641 masterquerycount = 0;
642 masterreplycount = 0;
645 serverquerycount = 0;
646 serverreplycount = 0;
647 serverlist_cachecount = 0;
648 serverlist_viewcount = 0;
649 serverlist_maxcachecount = 0;
650 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
654 // refresh all entries
655 for (i = 0; i < serverlist_cachecount; ++i)
657 serverlist_entry_t *entry = &serverlist_cache[i];
658 entry->responded = false;
659 entry->querycounter = 0;
662 serverlist_consoleoutput = consoleoutput;
664 //_ServerList_Test();
666 NetConn_QueryMasters(querydp, queryqw);
668 // Generate new DP server query key string
669 // Used to prevent ping cheating and discard spurious getstatus/getinfo packets
670 if (!serverlist_querystage) // don't change key while updating
671 NetConn_BuildChallengeString(serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
675 // Master and and/or favourite queries were likely delayed by DNS lag,
676 // for correct pings we need to know what host.realtime would be if it were updated now.
677 masterquerytime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
678 ServerList_BuildDPServerQuery(dpquery, sizeof(dpquery), masterquerytime);
680 // 26000 is the default quake server port, servers on other ports will not be found
681 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
682 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
684 for (i = 0; i < cl_numsockets; ++i)
688 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) != LHNETADDRESS_GetAddressType(&broadcastaddress))
693 // search LAN for Quake servers
694 SZ_Clear(&cl_message);
695 // save space for the header, filled in later
696 MSG_WriteLong(&cl_message, 0);
697 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
698 MSG_WriteString(&cl_message, "QUAKE");
699 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
700 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
701 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
702 SZ_Clear(&cl_message);
704 // search LAN for DarkPlaces servers
705 NetConn_WriteString(cl_sockets[i], dpquery, &broadcastaddress);
709 // search LAN for QuakeWorld servers
710 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
717 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
722 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
723 Thread_LockMutex(netconn_mutex);
724 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
725 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
726 Thread_UnlockMutex(netconn_mutex);
729 if (net_fakeloss_receive.integer)
730 for (i = 0;i < cl_numsockets;i++)
731 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_receive.integer)
733 if (developer_networking.integer)
735 char addressstring[128], addressstring2[128];
736 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
739 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
740 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
741 Com_HexDumpToConsole((unsigned char *)data, length);
744 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
749 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
754 if (net_fakeloss_send.integer)
755 for (i = 0;i < cl_numsockets;i++)
756 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_send.integer)
758 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
759 Thread_LockMutex(netconn_mutex);
760 ret = LHNET_Write(mysocket, data, length, peeraddress);
761 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
762 Thread_UnlockMutex(netconn_mutex);
763 if (developer_networking.integer)
765 char addressstring[128], addressstring2[128];
766 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
767 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
768 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)");
769 Com_HexDumpToConsole((unsigned char *)data, length);
774 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
776 // note this does not include the trailing NULL because we add that in the parser
777 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
780 qbool NetConn_CanSend(netconn_t *conn)
782 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
783 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = host.realtime;
784 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
785 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
786 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
787 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
788 if (host.realtime > conn->cleartime)
792 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
797 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
799 double bursttime = burstsize / (double)rate;
801 // delay later packets to obey rate limit
802 if (*cleartime < host.realtime - bursttime)
803 *cleartime = host.realtime - bursttime;
804 *cleartime = *cleartime + len / (double)rate;
806 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
807 if (net_test.integer)
809 if (*cleartime < host.realtime)
810 *cleartime = host.realtime;
814 static int NetConn_AddCryptoFlag(crypto_t *crypto)
816 // HACK: if an encrypted connection is used, randomly set some unused
817 // flags. When AES encryption is enabled, that will make resends differ
818 // from the original, so that e.g. substring filters in a router/IPS
819 // are unlikely to match a second time. See also "startkeylogger".
821 if (crypto->authenticated)
823 // Let's always set at least one of the bits.
824 int r = rand() % 7 + 1;
826 flag |= NETFLAG_CRYPTO0;
828 flag |= NETFLAG_CRYPTO1;
830 flag |= NETFLAG_CRYPTO2;
835 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qbool quakesignon_suppressreliables)
838 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
839 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
841 // if this packet was supposedly choked, but we find ourselves sending one
842 // anyway, make sure the size counting starts at zero
843 // (this mostly happens on level changes and disconnects and such)
844 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
845 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
847 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
849 if (protocol == PROTOCOL_QUAKEWORLD)
854 // note that it is ok to send empty messages to the qw server,
855 // otherwise it won't respond to us at all
857 sendreliable = false;
858 // if the remote side dropped the last reliable message, resend it
859 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
861 // if the reliable transmit buffer is empty, copy the current message out
862 if (!conn->sendMessageLength && conn->message.cursize)
864 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
865 conn->sendMessageLength = conn->message.cursize;
866 SZ_Clear(&conn->message); // clear the message buffer
867 conn->qw.reliable_sequence ^= 1;
870 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
871 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
872 // last received unreliable packet number, and last received reliable packet number (0 or 1)
873 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
875 conn->outgoing_unreliable_sequence++;
876 // client sends qport in every packet
877 if (conn == cls.netcon)
879 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
881 // also update cls.qw_outgoing_sequence
882 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
884 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
886 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
890 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
892 // add the reliable message if there is one
895 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
896 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
897 packetLen += conn->sendMessageLength;
898 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
901 // add the unreliable message if possible
902 if (packetLen + data->cursize <= 1400)
904 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
905 memcpy(sendbuffer + packetLen, data->data, data->cursize);
906 packetLen += data->cursize;
909 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
912 conn->unreliableMessagesSent++;
914 totallen += packetLen + 28;
918 unsigned int packetLen;
919 unsigned int dataLen;
924 // if a reliable message fragment has been lost, send it again
925 if (conn->sendMessageLength && (host.realtime - conn->lastSendTime) > 1.0)
927 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
929 dataLen = conn->sendMessageLength;
934 dataLen = MAX_PACKETFRAGMENT;
938 packetLen = NET_HEADERSIZE + dataLen;
940 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
941 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
942 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
944 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
946 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
947 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
949 conn->lastSendTime = host.realtime;
950 conn->packetsReSent++;
953 totallen += (int)sendmelen + 28;
956 // if we have a new reliable message to send, do so
957 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
959 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
961 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
962 conn->message.overflowed = true;
966 if (developer_networking.integer && conn == cls.netcon)
968 Con_Print("client sending reliable message to server:\n");
969 SZ_HexDumpToConsole(&conn->message);
972 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
973 conn->sendMessageLength = conn->message.cursize;
974 SZ_Clear(&conn->message);
976 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
978 dataLen = conn->sendMessageLength;
983 dataLen = MAX_PACKETFRAGMENT;
987 packetLen = NET_HEADERSIZE + dataLen;
989 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
990 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
991 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
993 conn->nq.sendSequence++;
995 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
997 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
999 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1001 conn->lastSendTime = host.realtime;
1002 conn->packetsSent++;
1003 conn->reliableMessagesSent++;
1005 totallen += (int)sendmelen + 28;
1008 // if we have an unreliable message to send, do so
1011 packetLen = NET_HEADERSIZE + data->cursize;
1013 if (packetLen > (int)sizeof(sendbuffer))
1015 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
1019 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
1020 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
1021 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
1023 conn->outgoing_unreliable_sequence++;
1025 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
1027 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1029 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1031 conn->packetsSent++;
1032 conn->unreliableMessagesSent++;
1034 totallen += (int)sendmelen + 28;
1038 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
1043 qbool NetConn_HaveClientPorts(void)
1045 return !!cl_numsockets;
1048 qbool NetConn_HaveServerPorts(void)
1050 return !!sv_numsockets;
1053 void NetConn_CloseClientPorts(void)
1055 for (;cl_numsockets > 0;cl_numsockets--)
1056 if (cl_sockets[cl_numsockets - 1])
1057 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
1060 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
1062 lhnetaddress_t address;
1065 char addressstring2[1024];
1066 if (addressstring && addressstring[0])
1067 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
1069 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
1072 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1074 cl_sockets[cl_numsockets++] = s;
1075 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1076 if (addresstype != LHNETADDRESSTYPE_LOOP)
1077 Con_Printf("Client opened a socket on address %s\n", addressstring2);
1081 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1082 Con_Printf(CON_ERROR "Client failed to open a socket on address %s\n", addressstring2);
1086 Con_Printf(CON_ERROR "Client unable to parse address %s\n", addressstring);
1089 void NetConn_OpenClientPorts(void)
1092 NetConn_CloseClientPorts();
1094 SV_LockThreadMutex(); // FIXME recursive?
1095 Crypto_LoadKeys(); // client sockets
1096 SV_UnlockThreadMutex();
1098 port = bound(0, cl_netport.integer, 65535);
1099 if (cl_netport.integer != port)
1100 Cvar_SetValueQuick(&cl_netport, port);
1102 Con_Printf("Client using an automatically assigned port\n");
1104 Con_Printf("Client using port %i\n", port);
1105 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
1106 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
1107 #ifndef NOSUPPORTIPV6
1108 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1112 void NetConn_CloseServerPorts(void)
1114 for (;sv_numsockets > 0;sv_numsockets--)
1115 if (sv_sockets[sv_numsockets - 1])
1116 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1119 static qbool NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1121 lhnetaddress_t address;
1124 char addressstring2[1024];
1127 for (port = defaultport; port <= defaultport + range; port++)
1129 if (addressstring && addressstring[0])
1130 success = LHNETADDRESS_FromString(&address, addressstring, port);
1132 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1135 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1137 sv_sockets[sv_numsockets++] = s;
1138 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1139 if (addresstype != LHNETADDRESSTYPE_LOOP)
1140 Con_Printf("Server listening on address %s\n", addressstring2);
1145 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1146 Con_Printf(CON_ERROR "Server failed to open socket on address %s\n", addressstring2);
1151 Con_Printf(CON_ERROR "Server unable to parse address %s\n", addressstring);
1152 // if it cant parse one address, it wont be able to parse another for sure
1159 void NetConn_OpenServerPorts(int opennetports)
1162 NetConn_CloseServerPorts();
1164 SV_LockThreadMutex(); // FIXME recursive?
1165 Crypto_LoadKeys(); // server sockets
1166 SV_UnlockThreadMutex();
1168 NetConn_UpdateSockets();
1169 port = bound(0, sv_netport.integer, 65535);
1172 if (sv_netport.integer != port)
1173 Cvar_SetValueQuick(&sv_netport, port);
1174 if (cls.state != ca_dedicated)
1175 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1178 #ifndef NOSUPPORTIPV6
1179 qbool ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1180 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1182 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1185 if (sv_numsockets == 0)
1186 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1189 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1192 lhnetaddresstype_t a = LHNETADDRESS_GetAddressType(address);
1194 for (i = 0;i < cl_numsockets;i++)
1195 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1196 return cl_sockets[i];
1200 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1203 lhnetaddresstype_t a = LHNETADDRESS_GetAddressType(address);
1205 for (i = 0;i < sv_numsockets;i++)
1206 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1207 return sv_sockets[i];
1211 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1214 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1215 conn->mysocket = mysocket;
1216 conn->peeraddress = *peeraddress;
1217 conn->lastMessageTime = host.realtime;
1218 conn->message.data = conn->messagedata;
1219 conn->message.maxsize = sizeof(conn->messagedata);
1220 conn->message.cursize = 0;
1221 // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
1222 // reduce effectiveness of connection request floods
1223 conn->timeout = host.realtime + net_connecttimeout.value;
1224 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1225 conn->next = netconn_list;
1226 netconn_list = conn;
1230 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1231 void NetConn_Close(netconn_t *conn)
1234 // remove connection from list
1236 // allow the client to reconnect immediately
1237 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1239 if (conn == netconn_list)
1240 netconn_list = conn->next;
1243 for (c = netconn_list;c;c = c->next)
1245 if (c->next == conn)
1247 c->next = conn->next;
1251 // not found in list, we'll avoid crashing here...
1259 static int clientport = -1;
1260 static int clientport2 = -1;
1262 // Call on disconnect, during startup, or if cl_port/cl_netport is changed
1263 static void NetConn_CL_UpdateSockets_Callback(cvar_t *var)
1265 if(cls.state != ca_dedicated)
1267 if (clientport2 != var->integer)
1269 clientport2 = var->integer;
1270 if (cls.state == ca_connected)
1271 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1274 if (cls.state == ca_disconnected && clientport != clientport2)
1276 clientport = clientport2;
1277 NetConn_CloseClientPorts();
1279 if (cl_numsockets == 0)
1280 NetConn_OpenClientPorts();
1284 static int hostport = -1;
1286 // Call when port/sv_netport is changed
1287 static void NetConn_sv_netport_Callback(cvar_t *var)
1289 if (hostport != var->integer)
1291 hostport = var->integer;
1293 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1297 void NetConn_UpdateSockets(void)
1301 // TODO add logic to automatically close sockets if needed
1302 LHNET_DefaultDSCP(net_tos_dscp.integer);
1304 for (j = 0;j < MAX_RCONS;j++)
1306 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1307 if(cls.rcon_commands[i][0])
1309 if(host.realtime > cls.rcon_timeout[i])
1312 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1313 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1314 cls.rcon_commands[i][0] = 0;
1322 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1324 int originallength = (int)length;
1325 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1326 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1327 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1331 if (protocol == PROTOCOL_QUAKEWORLD)
1333 unsigned int sequence, sequence_ack;
1334 qbool reliable_ack, reliable_message;
1338 sequence = LittleLong(*((int *)(data + 0)));
1339 sequence_ack = LittleLong(*((int *)(data + 4)));
1343 if (conn != cls.netcon)
1348 // 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?)
1349 //qport = LittleShort(*((int *)(data + 8)));
1354 conn->packetsReceived++;
1355 reliable_message = (sequence >> 31) != 0;
1356 reliable_ack = (sequence_ack >> 31) != 0;
1357 sequence &= ~(1<<31);
1358 sequence_ack &= ~(1<<31);
1359 if (sequence <= conn->qw.incoming_sequence)
1361 //Con_DPrint("Got a stale datagram\n");
1364 count = sequence - (conn->qw.incoming_sequence + 1);
1367 conn->droppedDatagrams += count;
1368 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1369 // If too may packets have been dropped, only write the
1370 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1371 // Because there's no point in writing more than
1372 // these as the netgraph is going to be full anyway.
1373 if (count > NETGRAPH_PACKETS)
1374 count = NETGRAPH_PACKETS;
1377 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1378 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1379 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1380 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1381 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1382 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1385 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1386 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1387 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1388 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1389 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1390 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1391 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1393 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1394 if (net_test.integer)
1396 if (conn->cleartime < host.realtime)
1397 conn->cleartime = host.realtime;
1400 if (reliable_ack == conn->qw.reliable_sequence)
1402 // received, now we will be able to send another reliable message
1403 conn->sendMessageLength = 0;
1404 conn->reliableMessagesReceived++;
1406 conn->qw.incoming_sequence = sequence;
1407 if (conn == cls.netcon)
1408 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1409 conn->qw.incoming_acknowledged = sequence_ack;
1410 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1411 if (reliable_message)
1412 conn->qw.incoming_reliable_sequence ^= 1;
1413 conn->lastMessageTime = host.realtime;
1414 conn->timeout = host.realtime + newtimeout;
1415 conn->unreliableMessagesReceived++;
1416 if (conn == cls.netcon)
1418 SZ_Clear(&cl_message);
1419 SZ_Write(&cl_message, data, (int)length);
1420 MSG_BeginReading(&cl_message);
1424 SZ_Clear(&sv_message);
1425 SZ_Write(&sv_message, data, (int)length);
1426 MSG_BeginReading(&sv_message);
1434 unsigned int sequence;
1439 originallength = (int)length;
1440 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1446 qlength = (unsigned int)BuffBigLong(data);
1447 flags = qlength & ~NETFLAG_LENGTH_MASK;
1448 qlength &= NETFLAG_LENGTH_MASK;
1449 // control packets were already handled
1450 if (!(flags & NETFLAG_CTL) && qlength == length)
1452 sequence = BuffBigLong(data + 4);
1453 conn->packetsReceived++;
1456 if (flags & NETFLAG_UNRELIABLE)
1458 if (sequence >= conn->nq.unreliableReceiveSequence)
1460 if (sequence > conn->nq.unreliableReceiveSequence)
1462 count = sequence - conn->nq.unreliableReceiveSequence;
1463 conn->droppedDatagrams += count;
1464 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1465 // If too may packets have been dropped, only write the
1466 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1467 // Because there's no point in writing more than
1468 // these as the netgraph is going to be full anyway.
1469 if (count > NETGRAPH_PACKETS)
1470 count = NETGRAPH_PACKETS;
1473 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1474 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1475 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1476 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1477 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1478 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1481 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1482 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1483 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1484 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1485 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1486 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1487 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1489 conn->nq.unreliableReceiveSequence = sequence + 1;
1490 conn->lastMessageTime = host.realtime;
1491 conn->timeout = host.realtime + newtimeout;
1492 conn->unreliableMessagesReceived++;
1495 if (conn == cls.netcon)
1497 SZ_Clear(&cl_message);
1498 SZ_Write(&cl_message, data, (int)length);
1499 MSG_BeginReading(&cl_message);
1503 SZ_Clear(&sv_message);
1504 SZ_Write(&sv_message, data, (int)length);
1505 MSG_BeginReading(&sv_message);
1511 // Con_DPrint("Got a stale datagram\n");
1514 else if (flags & NETFLAG_ACK)
1516 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1517 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1519 if (sequence == (conn->nq.sendSequence - 1))
1521 if (sequence == conn->nq.ackSequence)
1523 conn->nq.ackSequence++;
1524 if (conn->nq.ackSequence != conn->nq.sendSequence)
1525 Con_DPrint("ack sequencing error\n");
1526 conn->lastMessageTime = host.realtime;
1527 conn->timeout = host.realtime + newtimeout;
1528 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1530 unsigned int packetLen;
1531 unsigned int dataLen;
1534 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1535 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1537 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1539 dataLen = conn->sendMessageLength;
1544 dataLen = MAX_PACKETFRAGMENT;
1548 packetLen = NET_HEADERSIZE + dataLen;
1550 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1551 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1552 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1554 conn->nq.sendSequence++;
1556 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1557 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1559 conn->lastSendTime = host.realtime;
1560 conn->packetsSent++;
1564 conn->sendMessageLength = 0;
1567 // Con_DPrint("Duplicate ACK received\n");
1570 // Con_DPrint("Stale ACK received\n");
1573 else if (flags & NETFLAG_DATA)
1575 unsigned char temppacket[8];
1576 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1577 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1579 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1581 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1582 StoreBigLong(temppacket + 4, sequence);
1583 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1585 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1586 if (sequence == conn->nq.receiveSequence)
1588 conn->lastMessageTime = host.realtime;
1589 conn->timeout = host.realtime + newtimeout;
1590 conn->nq.receiveSequence++;
1591 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1592 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1593 conn->receiveMessageLength += (int)length;
1595 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1596 "Dropping the message!\n", sequence );
1597 conn->receiveMessageLength = 0;
1600 if (flags & NETFLAG_EOM)
1602 conn->reliableMessagesReceived++;
1603 length = conn->receiveMessageLength;
1604 conn->receiveMessageLength = 0;
1607 if (conn == cls.netcon)
1609 SZ_Clear(&cl_message);
1610 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1611 MSG_BeginReading(&cl_message);
1615 SZ_Clear(&sv_message);
1616 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1617 MSG_BeginReading(&sv_message);
1624 conn->receivedDuplicateCount++;
1632 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1635 cls.connect_trying = false;
1637 M_Update_Return_Reason("");
1639 // Disconnect from the current server or stop demo playback
1640 if(cls.state == ca_connected || cls.demoplayback)
1642 // allocate a net connection to keep track of things
1643 cls.netcon = NetConn_Open(mysocket, peeraddress);
1644 crypto = &cls.netcon->crypto;
1645 if(cls.crypto.authenticated)
1647 Crypto_FinishInstance(crypto, &cls.crypto);
1648 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1649 crypto->use_aes ? "Encrypted" : "Authenticated",
1650 cls.netcon->address,
1651 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1652 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1653 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1654 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1655 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1656 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1659 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1660 key_dest = key_game;
1664 cls.demonum = -1; // not in the demo loop now
1665 cls.state = ca_connected;
1666 cls.signon = 0; // need all the signon messages before playing
1667 cls.protocol = initialprotocol;
1668 // reset move sequence numbering on this new connection
1669 cls.servermovesequence = 0;
1670 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1671 CL_ForwardToServer("new");
1672 if (cls.protocol == PROTOCOL_QUAKE)
1674 // write a keepalive (clc_nop) as it seems to greatly improve the
1675 // chances of connecting to a netquake server
1677 unsigned char buf[4];
1678 memset(&msg, 0, sizeof(msg));
1680 msg.maxsize = sizeof(buf);
1681 MSG_WriteChar(&msg, clc_nop);
1682 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1686 int NetConn_IsLocalGame(void)
1688 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1694 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
1695 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring, const char *challenge)
1699 double currentrealtime;
1700 serverlist_entry_t *entry;
1702 // search the cache for this server and update it
1703 for (n = 0; n < serverlist_cachecount; ++n)
1705 entry = &serverlist_cache[n];
1706 if (!strcmp(addressstring, entry->info.cname))
1710 if (n == serverlist_cachecount)
1712 if (net_slist_debug.integer)
1713 Con_Printf("^6Received LAN broadcast response from %s\n", addressstring);
1716 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1719 if (serverlist_maxcachecount <= serverlist_cachecount)
1721 serverlist_maxcachecount += 64;
1722 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1724 ++serverlist_cachecount;
1725 entry = &serverlist_cache[n];
1727 memset(entry, 0, sizeof(*entry));
1728 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1730 // consider the broadcast to be the first query
1731 // NetConn_QueryQueueFrame() will perform more until net_slist_maxtries is reached
1732 entry->querycounter = 1;
1733 entry->querytime = masterquerytime;
1734 // protocol is one of these at all
1735 // NetConn_ClientParsePacket_ServerList_PrepareQuery() callsites
1736 entry->protocol = challenge ? PROTOCOL_DARKPLACES7 : PROTOCOL_QUAKEWORLD;
1738 if (serverlist_consoleoutput)
1739 Con_Printf("querying %s\n", addressstring);
1742 // If the client stalls partway through a frame (test command: `alias a a;a`)
1743 // for correct pings we need to know what host.realtime would be if it were updated now.
1744 currentrealtime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
1748 unsigned char hash[24]; // 4*(16/3) rounded up to 4 byte multiple
1749 uint64_t timestamp = strtoull(&challenge[22], NULL, 16);
1751 HMAC_MDFOUR_16BYTES(hash,
1752 (unsigned char *)×tamp, sizeof(timestamp),
1753 (unsigned char *)serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
1754 base64_encode(hash, 16, sizeof(hash));
1755 if (memcmp(hash, challenge, 22) != 0)
1758 ping = currentrealtime * 1000.0 - timestamp;
1761 ping = 1000 * (currentrealtime - entry->querytime);
1763 if (ping <= 0 || ping > net_slist_maxping.value
1764 || (entry->info.ping && ping > entry->info.ping + 100)) // server loading map, client stall, etc
1767 // never round down to 0, 0 latency is impossible, 0 means no data available
1771 if (entry->info.ping)
1772 entry->info.ping = (entry->info.ping + ping) * 0.5 + 0.5; // "average" biased toward most recent results
1775 entry->info.ping = ping + 0.5;
1778 entry->responded = true;
1780 // other server info is updated by the caller
1784 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1786 serverlist_entry_t *entry = &serverlist_cache[n];
1787 serverlist_info_t *info = &entry->info;
1789 // update description strings for engine menu and console output
1790 dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5.0f^7 ^%c%3u^7/%3u %-65.65s",
1791 info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'),
1792 info->ping ?: INFINITY, // display inf when a listed server times out and net_slist_pause blocks its removal
1793 ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'),
1797 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1799 info->gameversion != gameversion.integer
1802 gameversion_min.integer >= 0 // min/max range set by user/mod?
1803 && gameversion_max.integer >= 0
1804 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1805 && gameversion_max.integer >= info->gameversion
1808 info->mod, info->map);
1810 if(!net_slist_pause.integer)
1812 ServerList_ViewList_Remove(entry);
1813 ServerList_ViewList_Insert(entry);
1816 if (serverlist_consoleoutput)
1817 Con_Printf("%s\n%s\n", entry->line1, entry->line2);
1820 // returns true, if it's sensible to continue the processing
1821 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery(int protocol, const char *ipstring, qbool isfavorite)
1824 serverlist_entry_t *entry;
1826 // ignore the rest of the message if the serverlist is full
1827 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1830 for (n = 0; n < serverlist_cachecount; ++n)
1831 if (!strcmp(ipstring, serverlist_cache[n].info.cname))
1834 // also ignore it if we have already queried it (other master server response)
1835 if (n < serverlist_cachecount)
1838 if (serverlist_maxcachecount <= n)
1840 serverlist_maxcachecount += 64;
1841 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1844 entry = &serverlist_cache[n];
1845 memset(entry, 0, sizeof(*entry));
1846 entry->protocol = protocol;
1847 strlcpy(entry->info.cname, ipstring, sizeof(entry->info.cname));
1848 entry->info.isfavorite = isfavorite;
1850 serverlist_cachecount++;
1856 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *masteraddress, const char *masteraddressstring, const unsigned char *data, int length, qbool isextended)
1859 lhnetaddress_t testaddress;
1861 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
1862 if (sv_masters[masternum].string[0]
1863 && LHNETADDRESS_FromString(&testaddress, sv_masters[masternum].string, DPMASTER_PORT)
1864 && LHNETADDRESS_Compare(&testaddress, masteraddress) == 0)
1866 if (net_sourceaddresscheck.integer && masternum >= DPMASTER_COUNT)
1868 Con_Printf(CON_WARN "ignoring DarkPlaces %sserver list from unrecognised master %s\n", isextended ? "extended " : "", masteraddressstring);
1873 dpmasterstatus[masternum] = MASTER_RX_RESPONSE;
1874 if (serverlist_consoleoutput || net_slist_debug.integer)
1875 Con_Printf("^5Received DarkPlaces server list %sfrom %s\n", isextended ? "(extended) " : "", sv_masters[masternum].string);
1879 char ipstring [128];
1882 if (data[0] == '\\')
1884 unsigned short port = data[5] * 256 + data[6];
1886 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1887 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1888 else if (port == 0 && data[1] == 'E' && data[2] == 'O' && data[3] == 'T' && data[4] == '\0')
1890 dpmasterstatus[masternum] = MASTER_RX_COMPLETE;
1891 if (net_slist_debug.integer)
1892 Con_Printf("^4End Of Transmission %sfrom %s\n", isextended ? "(extended) " : "", sv_masters[masternum].string);
1896 // move on to next address in packet
1901 else if (data[0] == '/' && isextended && length >= 19)
1903 unsigned short port = data[17] * 256 + data[18];
1911 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1913 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1916 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1917 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1918 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1924 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1925 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1926 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1931 // move on to next address in packet
1937 Con_Print(CON_WARN "Error while parsing the server list\n");
1941 if (serverlist_consoleoutput && developer_networking.integer)
1942 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1944 if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, ipstring, false))
1948 if (serverlist_querystage & SLIST_QUERYSTAGE_QWMASTERS)
1949 return; // we must wait if we're also querying QW as it has no EOT marker
1950 // begin or resume serverlist queries
1951 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
1952 if (dpmasterstatus[masternum] && dpmasterstatus[masternum] < MASTER_RX_COMPLETE)
1953 break; // was queried but no EOT marker received yet
1954 if (masternum >= DPMASTER_COUNT)
1956 serverlist_querystage = SLIST_QUERYSTAGE_SERVERS;
1957 if (net_slist_debug.integer)
1958 Con_Print("^2Starting to query servers early (got EOT from all masters)\n");
1962 static void NetConn_ClientParsePacket_ServerList_ParseQWList(lhnetaddress_t *masteraddress, const char *masteraddressstring, const unsigned char *data, int length)
1965 lhnetaddress_t testaddress;
1967 for (masternum = 0; masternum < QWMASTER_COUNT; ++masternum)
1968 if (sv_qwmasters[masternum].string[0]
1969 && LHNETADDRESS_FromString(&testaddress, sv_qwmasters[masternum].string, QWMASTER_PORT)
1970 && LHNETADDRESS_Compare(&testaddress, masteraddress) == 0)
1972 if (net_sourceaddresscheck.integer && masternum >= QWMASTER_COUNT)
1974 Con_Printf(CON_WARN "ignoring QuakeWorld server list from unrecognised master %s\n", masteraddressstring);
1979 qwmasterstatus[masternum] = MASTER_RX_RESPONSE;
1980 if (serverlist_consoleoutput || net_slist_debug.integer)
1981 Con_Printf("^5Received QuakeWorld server list from %s\n", sv_qwmasters[masternum].string);
1983 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
1987 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
1988 if (serverlist_consoleoutput && developer_networking.integer)
1989 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
1991 if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_QUAKEWORLD, ipstring, false))
1994 // move on to next address in packet
1999 // Unlike in NetConn_ClientParsePacket_ServerList_ParseDPList()
2000 // we can't start to query servers early here because QW has no EOT marker.
2004 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2008 char *string, addressstring2[128];
2009 char stringbuf[16384];
2010 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2013 char infostringvalue[MAX_INPUTLINE];
2017 // quakeworld ingame packet
2018 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
2020 // convert the address to a string incase we need it
2021 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2023 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2025 // received a command string - strip off the packaging and put it
2026 // into our string buffer with NULL termination
2029 length = min(length, (int)sizeof(stringbuf) - 1);
2030 memcpy(stringbuf, data, length);
2031 stringbuf[length] = 0;
2034 if (developer_networking.integer)
2036 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
2037 Com_HexDumpToConsole(data, length);
2040 sendlength = sizeof(senddata) - 4;
2041 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2043 case CRYPTO_NOMATCH:
2049 memcpy(senddata, "\377\377\377\377", 4);
2050 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2053 case CRYPTO_DISCARD:
2056 memcpy(senddata, "\377\377\377\377", 4);
2057 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2061 case CRYPTO_REPLACE:
2062 string = senddata+4;
2063 length = (int)sendlength;
2067 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
2070 for (j = 0;j < MAX_RCONS;j++)
2072 // note: this value from i is used outside the loop too...
2073 i = (cls.rcon_ringpos + j) % MAX_RCONS;
2074 if(cls.rcon_commands[i][0])
2075 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
2084 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
2085 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
2087 e = strchr(rcon_password.string, ' ');
2088 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2090 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
2094 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
2095 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
2096 cls.rcon_commands[i][0] = 0;
2099 for (k = 0;k < MAX_RCONS;k++)
2100 if(cls.rcon_commands[k][0])
2101 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
2106 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
2107 // extend the timeout on other requests as we asked for a challenge
2108 for (l = 0;l < MAX_RCONS;l++)
2109 if(cls.rcon_commands[l][0])
2110 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
2111 cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
2114 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
2118 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
2120 // darkplaces or quake3
2121 char protocolnames[1400];
2122 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
2123 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2124 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
2127 Protocol_Names(protocolnames, sizeof(protocolnames));
2129 M_Update_Return_Reason("Got challenge response");
2131 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2132 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2133 // TODO: add userinfo stuff here instead of using NQ commands?
2134 memcpy(senddata, "\377\377\377\377", 4);
2135 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
2136 NetConn_WriteString(mysocket, senddata, peeraddress);
2139 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
2141 // darkplaces or quake3
2142 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2143 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
2147 M_Update_Return_Reason("Accepted");
2149 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
2152 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
2154 char rejectreason[128];
2155 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2156 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
2159 cls.connect_trying = false;
2161 length = min(length - 7, (int)sizeof(rejectreason) - 1);
2162 memcpy(rejectreason, string, length);
2163 rejectreason[length] = 0;
2165 M_Update_Return_Reason(rejectreason);
2170 if(key_dest != key_game)
2172 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
2174 serverlist_info_t *info;
2179 // search the cache for this server and update it
2180 // the challenge is (ab)used to return the query time
2181 s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue));
2182 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, s);
2186 info = &serverlist_cache[n].info;
2191 info->qcstatus[0] = 0;
2192 info->players[0] = 0;
2193 info->protocol = -1;
2194 info->numplayers = 0;
2196 info->maxplayers = 0;
2197 info->gameversion = 0;
2199 p = strchr(string, '\n');
2202 *p = 0; // cut off the string there
2206 Con_Printf("statusResponse without players block?\n");
2208 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2209 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2210 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2211 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2212 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2213 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2214 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2215 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2216 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2217 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2218 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2219 info->numhumans = info->numplayers - max(0, info->numbots);
2220 info->freeslots = info->maxplayers - info->numplayers;
2222 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2226 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2228 serverlist_info_t *info;
2232 // search the cache for this server and update it
2233 // the challenge is (ab)used to return the query time
2234 s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue));
2235 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, s);
2239 info = &serverlist_cache[n].info;
2244 info->qcstatus[0] = 0;
2245 info->players[0] = 0;
2246 info->protocol = -1;
2247 info->numplayers = 0;
2249 info->maxplayers = 0;
2250 info->gameversion = 0;
2252 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2253 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2254 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2255 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2256 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2257 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2258 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2259 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2260 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2261 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2262 info->numhumans = info->numplayers - max(0, info->numbots);
2263 info->freeslots = info->maxplayers - info->numplayers;
2265 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2269 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2271 // Extract the IP addresses
2274 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, addressstring2, data, length, false);
2277 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2279 // Extract the IP addresses
2282 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, addressstring2, data, length, true);
2285 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2287 // Extract the IP addresses
2290 NetConn_ClientParsePacket_ServerList_ParseQWList(peeraddress, addressstring2, data, length);
2295 if (!strncmp(string, "extResponse ", 12))
2297 ++cl_net_extresponse_count;
2298 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2299 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2300 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2301 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2304 if (!strncmp(string, "ping", 4))
2306 if (developer_extra.integer)
2307 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2308 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2311 if (!strncmp(string, "ack", 3))
2313 // QuakeWorld compatibility
2314 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2316 // challenge message
2317 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2318 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2321 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2323 M_Update_Return_Reason("Got QuakeWorld challenge response");
2325 cls.qw_qport = qport.integer;
2326 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2327 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2328 memcpy(senddata, "\377\377\377\377", 4);
2329 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);
2330 NetConn_WriteString(mysocket, senddata, peeraddress);
2333 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2336 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2337 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2341 M_Update_Return_Reason("QuakeWorld Accepted");
2343 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2346 if (length > 2 && !memcmp(string, "n\\", 2))
2349 serverlist_info_t *info;
2353 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2354 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2357 // search the cache for this server and update it
2358 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, NULL);
2362 info = &serverlist_cache[n].info;
2363 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2364 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2365 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2366 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2368 info->numplayers = 0; // updated below
2369 info->numhumans = 0; // updated below
2370 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2371 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2373 // count active players on server
2374 // (we could gather more info, but we're just after the number)
2375 s = strchr(string, '\n');
2379 while (s < string + length)
2381 for (;s < string + length && *s != '\n';s++)
2383 if (s >= string + length)
2391 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2395 if (string[0] == 'n')
2397 // qw print command, used by rcon replies too
2398 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2399 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2402 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2404 // we may not have liked the packet, but it was a command packet, so
2405 // we're done processing this packet now
2408 // quakeworld ingame packet
2409 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2412 CL_ParseServerMessage();
2415 // netquake control packets, supported for compatibility only
2416 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2420 serverlist_info_t *info;
2425 SZ_Clear(&cl_message);
2426 SZ_Write(&cl_message, data, length);
2427 MSG_BeginReading(&cl_message);
2428 c = MSG_ReadByte(&cl_message);
2432 if (developer_extra.integer)
2433 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2434 if (cls.connect_trying)
2436 lhnetaddress_t clientportaddress;
2437 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2438 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2441 clientportaddress = *peeraddress;
2442 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2443 // extra ProQuake stuff
2445 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2447 cls.proquake_servermod = 0;
2449 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2451 cls.proquake_serverversion = 0;
2453 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2455 cls.proquake_serverflags = 0;
2456 if (cls.proquake_servermod == 1)
2457 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2458 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2459 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2461 M_Update_Return_Reason("Accepted");
2463 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2467 if (developer_extra.integer) {
2468 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2471 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2473 cls.connect_trying = false;
2475 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2478 case CCREP_SERVER_INFO:
2479 if (developer_extra.integer)
2480 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2482 // LadyHavoc: because the quake server may report weird addresses
2483 // we just ignore it and keep the real address
2484 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2485 // search the cache for this server and update it
2486 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, NULL);
2490 info = &serverlist_cache[n].info;
2491 strlcpy(info->game, "Quake", sizeof(info->game));
2492 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2493 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2494 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2495 info->numplayers = MSG_ReadByte(&cl_message);
2496 info->maxplayers = MSG_ReadByte(&cl_message);
2497 info->protocol = MSG_ReadByte(&cl_message);
2499 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2502 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2503 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2504 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2507 if (developer_extra.integer)
2508 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2510 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2512 case CCREP_PLAYER_INFO:
2513 // we got a CCREP_PLAYER_INFO??
2514 //if (developer_extra.integer)
2515 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2517 case CCREP_RULE_INFO:
2518 // we got a CCREP_RULE_INFO??
2519 //if (developer_extra.integer)
2520 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2525 SZ_Clear(&cl_message);
2526 // we may not have liked the packet, but it was a valid control
2527 // packet, so we're done processing this packet now
2531 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2532 CL_ParseServerMessage();
2537 void NetConn_QueryQueueFrame(void)
2540 unsigned queries, maxqueries;
2541 char dpquery[53]; // theoretical max: 14+22+16+1
2542 double currentrealtime;
2543 static double querycounter = 0;
2544 qbool pending = false;
2546 if (!serverlist_querystage)
2549 // If the client stalls partway through a frame (test command: `alias a a;a`)
2550 // for correct pings we need to know what host.realtime would be if it were updated now.
2551 currentrealtime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
2553 // apply a cool down time after master server replies,
2554 // to avoid messing up the ping times on the servers
2555 if (serverlist_querystage < SLIST_QUERYSTAGE_SERVERS)
2557 if (currentrealtime < masterquerytime + net_slist_timeout.value)
2560 // Report the masters that timed out or whose response was incomplete.
2561 for (index = 0; index < DPMASTER_COUNT; ++index)
2562 if (dpmasterstatus[index] && dpmasterstatus[index] < MASTER_RX_COMPLETE)
2563 Con_Printf(CON_WARN "WARNING: dpmaster %s %s\n", sv_masters[index].string, dpmasterstatus[index] == MASTER_TX_QUERY ? "timed out" : "response incomplete");
2564 for (index = 0; index < QWMASTER_COUNT; ++index)
2565 if (qwmasterstatus[index] && qwmasterstatus[index] < MASTER_RX_RESPONSE) // no EOT marker in QW lists
2566 Con_Printf(CON_WARN "WARNING: qwmaster %s timed out\n", sv_qwmasters[index].string);
2568 serverlist_querystage = SLIST_QUERYSTAGE_SERVERS;
2571 // each time querycounter reaches 1.0 issue a query
2572 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2573 maxqueries = bound(0, (int)querycounter, net_slist_queriesperframe.integer);
2574 querycounter -= maxqueries;
2575 if (maxqueries == 0)
2578 // QW depends on waiting "long enough" between queries that responses "definitely" refer to the most recent querytime
2579 // DP servers can echo back a timestamp for reliable (and more frequent, see net_slist_interval) pings
2580 ServerList_BuildDPServerQuery(dpquery, sizeof(dpquery), currentrealtime);
2582 for (index = 0, queries = 0; index < serverlist_cachecount && queries < maxqueries; ++index)
2584 serverlist_entry_t *entry = &serverlist_cache[index];
2586 if (entry->querycounter < min(8, (unsigned)net_slist_maxtries.integer))
2588 lhnetaddress_t address;
2591 // only check this when there are tries remaining so we finish querying sooner
2592 if (currentrealtime < entry->querytime + (entry->protocol == PROTOCOL_QUAKEWORLD ? net_slist_timeout : net_slist_interval).value)
2598 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2599 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2601 for (socket = 0; socket < cl_numsockets; ++socket)
2602 if (cl_sockets[socket])
2603 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2607 for (socket = 0; socket < cl_numsockets; ++socket)
2608 if (cl_sockets[socket])
2609 NetConn_WriteString(cl_sockets[socket], dpquery, &address);
2612 entry->querytime = currentrealtime;
2613 entry->querycounter++;
2616 if (serverlist_consoleoutput)
2617 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2619 else // reached net_slist_maxtries
2620 if (!entry->responded // no acceptable response during this refresh cycle
2621 && (entry->info.ping)) // visible in the list (has old ping from previous refresh cycle)
2623 if (currentrealtime >= entry->querytime + net_slist_maxping.integer/1000)
2625 // you have no chance to survive make your timeout
2627 if(!net_slist_pause.integer)
2628 ServerList_ViewList_Remove(entry);
2629 entry->info.ping = 0; // removed later if net_slist_pause
2631 else // still has time
2636 // If we got to the end of the list (didn't hit maxqueries)
2637 // and no servers remain to be queried or checked for timeout,
2638 // there's nothing else to do here until the next refresh cycle.
2639 if (index >= serverlist_cachecount && !pending)
2641 if (net_slist_debug.integer)
2642 Con_Printf("^2Finished querying masters and servers in %f\n", currentrealtime - masterquerytime);
2643 serverlist_querystage = 0;
2648 void NetConn_ClientFrame(void)
2652 lhnetaddress_t peeraddress;
2653 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2655 NetConn_UpdateSockets();
2656 if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2659 if (cls.connect_remainingtries == 0)
2660 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2662 cls.connect_nextsendtime = host.realtime + 1;
2663 cls.connect_remainingtries--;
2664 if (cls.connect_remainingtries <= -10)
2666 cls.connect_trying = false;
2668 M_Update_Return_Reason("Connect: Failed");
2672 // try challenge first (newer DP server or QW)
2673 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2674 // then try netquake as a fallback (old server, or netquake)
2675 SZ_Clear(&cl_message);
2676 // save space for the header, filled in later
2677 MSG_WriteLong(&cl_message, 0);
2678 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2679 MSG_WriteString(&cl_message, "QUAKE");
2680 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2681 // extended proquake stuff
2682 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2683 // this version matches ProQuake 3.40, the first version to support
2684 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2685 // higher clients, so we pretend we are that version...
2686 MSG_WriteByte(&cl_message, 34); // version * 10
2687 MSG_WriteByte(&cl_message, 0); // flags
2688 MSG_WriteLong(&cl_message, 0); // password
2689 // write the packetsize now...
2690 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2691 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2692 SZ_Clear(&cl_message);
2694 for (i = 0;i < cl_numsockets;i++)
2696 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2698 // R_TimeReport("clientreadnetwork");
2699 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2700 // R_TimeReport("clientparsepacket");
2704 NetConn_QueryQueueFrame();
2706 if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2707 CL_DisconnectEx(true, "Connection timed out");
2710 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2714 for (i = 0;i < bufferlength - 1;i++)
2718 c = rand () % (127 - 33) + 33;
2719 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2725 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2726 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2728 prvm_prog_t *prog = SVVM_prog;
2730 unsigned int nb_clients = 0, nb_bots = 0, i;
2733 const char *crypto_idstring;
2734 const char *worldstatusstr;
2736 // How many clients are there?
2737 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2739 if (svs.clients[i].active)
2742 if (!svs.clients[i].netconnection)
2748 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2749 if(worldstatusstr && *worldstatusstr)
2754 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2755 if(*q != '\\' && *q != '\n')
2760 /// \TODO: we should add more information for the full status string
2761 crypto_idstring = Crypto_GetInfoResponseDataString();
2762 length = dpsnprintf(out_msg, out_size,
2763 "\377\377\377\377%s\x0A"
2764 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2765 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2770 fullstatus ? "statusResponse" : "infoResponse",
2771 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2772 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2773 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2774 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2775 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2776 fullstatus ? "\n" : "");
2778 // Make sure it fits in the buffer
2788 savelength = length;
2790 ptr = out_msg + length;
2791 left = (int)out_size - length;
2793 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2795 client_t *client = &svs.clients[i];
2798 int nameind, cleanind, pingvalue;
2800 char cleanname [sizeof(client->name)];
2801 const char *statusstr;
2804 // Remove all characters '"' and '\' in the player name
2809 curchar = client->name[nameind++];
2810 if (curchar != '"' && curchar != '\\')
2812 cleanname[cleanind++] = curchar;
2813 if (cleanind == sizeof(cleanname) - 1)
2816 } while (curchar != '\0');
2817 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2819 pingvalue = (int)(client->ping * 1000.0f);
2820 if(client->netconnection)
2821 pingvalue = bound(1, pingvalue, 9999);
2826 ed = PRVM_EDICT_NUM(i + 1);
2827 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2828 if(statusstr && *statusstr)
2833 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2834 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2839 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2841 if(client->frags == -666) // spectator
2842 strlcpy(teambuf, " 0", sizeof(teambuf));
2843 else if(client->colors == 0x44) // red team
2844 strlcpy(teambuf, " 1", sizeof(teambuf));
2845 else if(client->colors == 0xDD) // blue team
2846 strlcpy(teambuf, " 2", sizeof(teambuf));
2847 else if(client->colors == 0xCC) // yellow team
2848 strlcpy(teambuf, " 3", sizeof(teambuf));
2849 else if(client->colors == 0x99) // pink team
2850 strlcpy(teambuf, " 4", sizeof(teambuf));
2852 strlcpy(teambuf, " 0", sizeof(teambuf));
2857 // note: team number is inserted according to SoF2 protocol
2859 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2865 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2874 // turn it into an infoResponse!
2875 out_msg[savelength] = 0;
2876 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2877 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2892 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2894 size_t floodslotnum, bestfloodslotnum;
2895 double bestfloodtime;
2896 lhnetaddress_t noportpeeraddress;
2897 // see if this is a connect flood
2898 noportpeeraddress = *peeraddress;
2899 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2900 bestfloodslotnum = 0;
2901 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2902 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2904 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2906 bestfloodtime = floodlist[floodslotnum].lasttime;
2907 bestfloodslotnum = floodslotnum;
2909 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2911 // this address matches an ongoing flood address
2912 if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2916 // renew the ban on this address so it does not expire
2917 // until the flood has subsided
2918 floodlist[floodslotnum].lasttime = host.realtime;
2920 //Con_Printf("Flood detected!\n");
2923 // the flood appears to have subsided, so allow this
2924 bestfloodslotnum = floodslotnum; // reuse the same slot
2928 // begin a new timeout on this address
2929 floodlist[bestfloodslotnum].address = noportpeeraddress;
2930 floodlist[bestfloodslotnum].lasttime = host.realtime;
2931 //Con_Printf("Flood detection initiated!\n");
2935 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2937 size_t floodslotnum;
2938 lhnetaddress_t noportpeeraddress;
2939 // see if this is a connect flood
2940 noportpeeraddress = *peeraddress;
2941 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2942 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2944 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2946 // this address matches an ongoing flood address
2948 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2949 floodlist[floodslotnum].lasttime = 0;
2950 //Con_Printf("Flood cleared!\n");
2955 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2957 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2963 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2967 t1 = (long) time(NULL);
2968 t2 = strtol(s, NULL, 0);
2969 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2972 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2975 return !memcmp(mdfourbuf, hash, 16);
2978 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2984 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2988 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2991 // validate the challenge
2992 for (i = 0;i < MAX_CHALLENGES;i++)
2993 if(challenges[i].time > 0)
2994 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2996 // if the challenge is not recognized, drop the packet
2997 if (i == MAX_CHALLENGES)
3000 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
3003 if(memcmp(mdfourbuf, hash, 16))
3006 // unmark challenge to prevent replay attacks
3007 challenges[i].time = 0;
3012 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
3015 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
3019 return !strcmp(password, hash);
3022 /// returns a string describing the user level, or NULL for auth failure
3023 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)
3025 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
3026 static char buf[MAX_INPUTLINE];
3028 qbool restricted = false;
3029 qbool have_usernames = false;
3030 static char vabuf[1024];
3032 userpass_start = rcon_password.string;
3033 while((userpass_end = strchr(userpass_start, ' ')))
3035 have_usernames = true;
3036 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
3037 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
3038 if(comparator(peeraddress, buf, password, cs, cslen))
3040 userpass_start = userpass_end + 1;
3042 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
3044 userpass_end = userpass_start + strlen(userpass_start);
3045 if(comparator(peeraddress, userpass_start, password, cs, cslen))
3050 have_usernames = false;
3051 userpass_start = rcon_restricted_password.string;
3052 while((userpass_end = strchr(userpass_start, ' ')))
3054 have_usernames = true;
3055 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
3056 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
3057 if(comparator(peeraddress, buf, password, cs, cslen))
3059 userpass_start = userpass_end + 1;
3061 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
3063 userpass_end = userpass_start + strlen(userpass_start);
3064 if(comparator(peeraddress, userpass_start, password, cs, cslen))
3068 return NULL; // DENIED
3071 for(text = s; text != endpos; ++text)
3072 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
3073 return NULL; // block possible exploits against the parser/alias expansion
3077 size_t l = strlen(s);
3080 hasquotes = (strchr(s, '"') != NULL);
3081 // sorry, we can't allow these substrings in wildcard expressions,
3082 // as they can mess with the argument counts
3083 text = rcon_restricted_commands.string;
3084 while(COM_ParseToken_Console(&text))
3086 // com_token now contains a pattern to check for...
3087 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
3090 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
3093 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
3095 if(!strcmp(com_token, s))
3098 else // single-arg expression? must match the beginning of the command
3100 if(!strcmp(com_token, s))
3102 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
3106 // if we got here, nothing matched!
3114 userpass_startpass = strchr(userpass_start, ':');
3115 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
3116 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
3118 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
3121 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qbool proquakeprotocol)
3125 // looks like a legitimate rcon command with the correct password
3126 const char *s_ptr = s;
3127 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
3128 while(s_ptr != endpos)
3130 size_t l = strlen(s_ptr);
3132 Con_Printf(" %s;", s_ptr);
3137 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3138 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
3141 size_t l = strlen(s);
3144 client_t *host_client_save = host_client;
3145 Cmd_ExecuteString(cmd_local, s, src_local, true);
3146 host_client = host_client_save;
3147 // in case it is a command that changes host_client (like restart)
3151 Con_Rcon_Redirect_End();
3155 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
3159 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
3161 int i, ret, clientnum, best;
3163 char *string, response[2800], addressstring2[128];
3164 static char stringbuf[16384]; // server only
3165 qbool islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
3166 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
3167 size_t sendlength, response_len;
3168 char infostringvalue[MAX_INPUTLINE];
3173 // convert the address to a string incase we need it
3174 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
3176 // see if we can identify the sender as a local player
3177 // (this is necessary for rcon to send a reliable reply if the client is
3178 // actually on the server, not sending remotely)
3179 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3180 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
3182 if (i == svs.maxclients)
3185 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
3187 // received a command string - strip off the packaging and put it
3188 // into our string buffer with NULL termination
3191 length = min(length, (int)sizeof(stringbuf) - 1);
3192 memcpy(stringbuf, data, length);
3193 stringbuf[length] = 0;
3196 if (developer_extra.integer)
3198 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
3199 Com_HexDumpToConsole(data, length);
3202 sendlength = sizeof(senddata) - 4;
3203 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
3205 case CRYPTO_NOMATCH:
3211 memcpy(senddata, "\377\377\377\377", 4);
3212 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3215 case CRYPTO_DISCARD:
3218 memcpy(senddata, "\377\377\377\377", 4);
3219 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3223 case CRYPTO_REPLACE:
3224 string = senddata+4;
3225 length = (int)sendlength;
3229 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3231 for (i = 0, best = 0, besttime = host.realtime;i < MAX_CHALLENGES;i++)
3233 if(challenges[i].time > 0)
3234 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3236 if (besttime > challenges[i].time)
3237 besttime = challenges[best = i].time;
3239 // if we did not find an exact match, choose the oldest and
3240 // update address and string
3241 if (i == MAX_CHALLENGES)
3244 challenges[i].address = *peeraddress;
3245 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3249 // flood control: drop if requesting challenge too often
3250 if(challenges[i].time > host.realtime - net_challengefloodblockingtimeout.value)
3253 challenges[i].time = host.realtime;
3254 // send the challenge
3255 memcpy(response, "\377\377\377\377", 4);
3256 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3257 response_len = strlen(response) + 1;
3258 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3259 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3262 if (length > 8 && !memcmp(string, "connect\\", 8))
3266 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3270 if(crypto && crypto->authenticated)
3272 // no need to check challenge
3273 if(crypto_developer.integer)
3275 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3276 crypto->use_aes ? "Encrypted" : "Authenticated",
3278 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3279 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3280 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3281 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3282 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3283 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3289 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3291 // validate the challenge
3292 for (i = 0;i < MAX_CHALLENGES;i++)
3293 if(challenges[i].time > 0)
3294 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3296 // if the challenge is not recognized, drop the packet
3297 if (i == MAX_CHALLENGES)
3302 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3303 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3305 if(!(islocal || sv_public.integer > -2))
3307 if (developer_extra.integer)
3308 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3309 memcpy(response, "\377\377\377\377", 4);
3310 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3311 NetConn_WriteString(mysocket, response, peeraddress);
3315 // check engine protocol
3316 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3318 if (developer_extra.integer)
3319 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3320 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3324 // see if this is a duplicate connection request or a disconnected
3325 // client who is rejoining to the same client slot
3326 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3328 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3330 // this is a known client...
3331 if(crypto && crypto->authenticated)
3333 // reject if changing key!
3334 if(client->netconnection->crypto.authenticated)
3337 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3339 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3341 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3343 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3346 if (developer_extra.integer)
3347 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3348 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3355 // reject if downgrading!
3356 if(client->netconnection->crypto.authenticated)
3358 if (developer_extra.integer)
3359 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3360 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3366 // client crashed and is coming back,
3367 // keep their stuff intact
3368 if (developer_extra.integer)
3369 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3370 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3371 if(crypto && crypto->authenticated)
3372 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3373 SV_SendServerinfo(client);
3377 // client is still trying to connect,
3378 // so we send a duplicate reply
3379 if (developer_extra.integer)
3380 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3381 if(crypto && crypto->authenticated)
3382 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3383 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3389 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3392 // find an empty client slot for this new client
3393 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3396 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3398 // allocated connection
3399 if (developer_extra.integer)
3400 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3401 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3402 // now set up the client
3403 if(crypto && crypto->authenticated)
3404 Crypto_FinishInstance(&conn->crypto, crypto);
3405 SV_ConnectClient(clientnum, conn);
3406 NetConn_Heartbeat(1);
3411 // no empty slots found - server is full
3412 if (developer_extra.integer)
3413 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3414 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3418 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3420 const char *challenge = NULL;
3422 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3425 // If there was a challenge in the getinfo message
3426 if (length > 8 && string[7] == ' ')
3427 challenge = string + 8;
3429 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3431 if (developer_extra.integer)
3432 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3433 NetConn_WriteString(mysocket, response, peeraddress);
3437 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3439 const char *challenge = NULL;
3441 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3444 // If there was a challenge in the getinfo message
3445 if (length > 10 && string[9] == ' ')
3446 challenge = string + 10;
3448 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3450 if (developer_extra.integer)
3451 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3452 NetConn_WriteString(mysocket, response, peeraddress);
3456 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3458 char *password = string + 20;
3459 char *timeval = string + 37;
3460 char *s = strchr(timeval, ' ');
3461 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3462 const char *userlevel;
3464 if(rcon_secure.integer > 1)
3468 return true; // invalid packet
3471 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3472 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3475 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3477 char *password = string + 25;
3478 char *challenge = string + 42;
3479 char *s = strchr(challenge, ' ');
3480 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3481 const char *userlevel;
3483 return true; // invalid packet
3486 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3487 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3490 if (length >= 5 && !memcmp(string, "rcon ", 5))
3493 char *s = string + 5;
3494 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3497 if(rcon_secure.integer > 0)
3500 for (j = 0;!ISWHITESPACE(*s);s++)
3501 if (j < (int)sizeof(password) - 1)
3503 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3506 if (!ISWHITESPACE(password[0]))
3508 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3509 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3513 if (!strncmp(string, "extResponse ", 12))
3515 ++sv_net_extresponse_count;
3516 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3517 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3518 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3519 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3522 if (!strncmp(string, "ping", 4))
3524 if (developer_extra.integer)
3525 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3526 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3529 if (!strncmp(string, "ack", 3))
3531 // we may not have liked the packet, but it was a command packet, so
3532 // we're done processing this packet now
3535 // netquake control packets, supported for compatibility only, and only
3536 // when running game protocols that are normally served via this connection
3538 // (this protects more modern protocols against being used for
3539 // Quake packet flood Denial Of Service attacks)
3540 if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) && !ENCRYPTION_REQUIRED)
3544 const char *protocolname;
3545 client_t *knownclient;
3546 client_t *newclient;
3549 SZ_Clear(&sv_message);
3550 SZ_Write(&sv_message, data, length);
3551 MSG_BeginReading(&sv_message);
3552 c = MSG_ReadByte(&sv_message);
3556 if (developer_extra.integer)
3557 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3558 if(!(islocal || sv_public.integer > -2))
3560 if (developer_extra.integer)
3561 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3562 SZ_Clear(&sv_message);
3563 // save space for the header, filled in later
3564 MSG_WriteLong(&sv_message, 0);
3565 MSG_WriteByte(&sv_message, CCREP_REJECT);
3566 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3567 MSG_WriteString(&sv_message, "\n");
3568 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3569 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3570 SZ_Clear(&sv_message);
3574 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3575 protocolnumber = MSG_ReadByte(&sv_message);
3576 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3578 if (developer_extra.integer)
3579 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3580 SZ_Clear(&sv_message);
3581 // save space for the header, filled in later
3582 MSG_WriteLong(&sv_message, 0);
3583 MSG_WriteByte(&sv_message, CCREP_REJECT);
3584 MSG_WriteString(&sv_message, "Incompatible version.\n");
3585 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3586 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3587 SZ_Clear(&sv_message);
3591 // see if this connect request comes from a known client
3592 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3594 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3596 // this is either a duplicate connection request
3597 // or coming back from a timeout
3598 // (if so, keep their stuff intact)
3600 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3601 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3603 if (developer_extra.integer)
3604 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3605 SZ_Clear(&sv_message);
3606 // save space for the header, filled in later
3607 MSG_WriteLong(&sv_message, 0);
3608 MSG_WriteByte(&sv_message, CCREP_REJECT);
3609 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3610 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3611 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3612 SZ_Clear(&sv_message);
3617 if (developer_extra.integer)
3618 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3619 SZ_Clear(&sv_message);
3620 // save space for the header, filled in later
3621 MSG_WriteLong(&sv_message, 0);
3622 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3623 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3624 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3625 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3626 SZ_Clear(&sv_message);
3628 // if client is already spawned, re-send the
3629 // serverinfo message as they'll need it to play
3630 if (knownclient->begun)
3631 SV_SendServerinfo(knownclient);
3636 // this is a new client, check for connection flood
3637 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3640 // find a slot for the new client
3641 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3644 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3646 // connect to the client
3647 // everything is allocated, just fill in the details
3648 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3649 if (developer_extra.integer)
3650 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3651 // send back the info about the server connection
3652 SZ_Clear(&sv_message);
3653 // save space for the header, filled in later
3654 MSG_WriteLong(&sv_message, 0);
3655 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3656 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3657 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3658 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3659 SZ_Clear(&sv_message);
3660 // now set up the client struct
3661 SV_ConnectClient(clientnum, conn);
3662 NetConn_Heartbeat(1);
3667 if (developer_extra.integer)
3668 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3669 // no room; try to let player know
3670 SZ_Clear(&sv_message);
3671 // save space for the header, filled in later
3672 MSG_WriteLong(&sv_message, 0);
3673 MSG_WriteByte(&sv_message, CCREP_REJECT);
3674 MSG_WriteString(&sv_message, "Server is full.\n");
3675 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3676 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3677 SZ_Clear(&sv_message);
3679 case CCREQ_SERVER_INFO:
3680 if (developer_extra.integer)
3681 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3682 if(!(islocal || sv_public.integer > -1))
3685 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3688 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3691 char myaddressstring[128];
3692 if (developer_extra.integer)
3693 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3694 SZ_Clear(&sv_message);
3695 // save space for the header, filled in later
3696 MSG_WriteLong(&sv_message, 0);
3697 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3698 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3699 MSG_WriteString(&sv_message, myaddressstring);
3700 MSG_WriteString(&sv_message, hostname.string);
3701 MSG_WriteString(&sv_message, sv.name);
3702 // How many clients are there?
3703 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3704 if (svs.clients[i].active)
3706 MSG_WriteByte(&sv_message, numclients);
3707 MSG_WriteByte(&sv_message, svs.maxclients);
3708 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3709 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3710 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3711 SZ_Clear(&sv_message);
3714 case CCREQ_PLAYER_INFO:
3715 if (developer_extra.integer)
3716 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3717 if(!(islocal || sv_public.integer > -1))
3720 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3725 int playerNumber, activeNumber, clientNumber;
3728 playerNumber = MSG_ReadByte(&sv_message);
3730 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3731 if (client->active && ++activeNumber == playerNumber)
3733 if (clientNumber != svs.maxclients)
3735 SZ_Clear(&sv_message);
3736 // save space for the header, filled in later
3737 MSG_WriteLong(&sv_message, 0);
3738 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3739 MSG_WriteByte(&sv_message, playerNumber);
3740 MSG_WriteString(&sv_message, client->name);
3741 MSG_WriteLong(&sv_message, client->colors);
3742 MSG_WriteLong(&sv_message, client->frags);
3743 MSG_WriteLong(&sv_message, (int)(host.realtime - client->connecttime));
3744 if(sv_status_privacy.integer)
3745 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3747 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3748 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3749 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3750 SZ_Clear(&sv_message);
3754 case CCREQ_RULE_INFO:
3755 if (developer_extra.integer)
3756 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3757 if(!(islocal || sv_public.integer > -1))
3760 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3767 // find the search start location
3768 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3769 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CF_NOTIFY);
3771 // send the response
3772 SZ_Clear(&sv_message);
3773 // save space for the header, filled in later
3774 MSG_WriteLong(&sv_message, 0);
3775 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3778 MSG_WriteString(&sv_message, var->name);
3779 MSG_WriteString(&sv_message, var->string);
3781 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3782 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3783 SZ_Clear(&sv_message);
3787 if (developer_extra.integer)
3788 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3789 if (sv.active && !rcon_secure.integer)
3791 char password[2048];
3795 const char *userlevel;
3796 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3797 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3799 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3800 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3801 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3808 SZ_Clear(&sv_message);
3809 // we may not have liked the packet, but it was a valid control
3810 // packet, so we're done processing this packet now
3815 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3817 SV_ReadClientMessage();
3824 void NetConn_ServerFrame(void)
3828 lhnetaddress_t peeraddress;
3829 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3831 for (i = 0;i < sv_numsockets;i++)
3832 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3833 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3836 void NetConn_SleepMicroseconds(int microseconds)
3838 LHNET_SleepUntilPacket_Microseconds(microseconds);
3842 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
3846 lhnetaddress_t masteraddress;
3848 char lookupstring[128];
3850 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3853 memset(dpmasterstatus, 0, sizeof(*dpmasterstatus));
3854 memset(qwmasterstatus, 0, sizeof(*qwmasterstatus));
3858 for (i = 0;i < cl_numsockets;i++)
3862 const char *cmdname, *extraoptions;
3863 lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3865 // build the getservers message to send to the dpmaster master servers
3866 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3868 cmdname = "getserversExt";
3869 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3873 cmdname = "getservers";
3876 memcpy(request, "\377\377\377\377", 4);
3877 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3880 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
3882 if(sv_masters[masternum].string[0]
3883 && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT)
3884 && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3886 if (serverlist_consoleoutput || net_slist_debug.integer)
3888 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3889 Con_Printf("Querying DP master %s (resolved from %s)\n", lookupstring, sv_masters[masternum].string);
3892 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3893 dpmasterstatus[masternum] = MASTER_TX_QUERY;
3897 // search favorite servers
3898 for(j = 0; j < nFavorites; ++j)
3899 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af
3900 && LHNETADDRESS_ToString(&favorites[j], lookupstring, sizeof(lookupstring), true))
3901 NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, lookupstring, true);
3906 // only query QuakeWorld servers when the user wants to
3909 dpsnprintf(request, sizeof(request), "c\n");
3911 for (i = 0;i < cl_numsockets;i++)
3915 lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3918 for (masternum = 0; masternum < QWMASTER_COUNT; ++masternum)
3920 if(sv_qwmasters[masternum].string[0]
3921 && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT)
3922 && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3924 if (serverlist_consoleoutput || net_slist_debug.integer)
3926 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3927 Con_Printf("Querying QW master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3930 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3931 qwmasterstatus[masternum] = MASTER_TX_QUERY;
3935 // search favorite servers
3936 for(j = 0; j < nFavorites; ++j)
3937 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af
3938 && LHNETADDRESS_ToString(&favorites[j], lookupstring, sizeof(lookupstring), true))
3939 NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_QUAKEWORLD, lookupstring, true);
3944 if (!masterquerycount)
3946 Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
3947 M_Update_Return_Reason("No network");
3952 void NetConn_Heartbeat(int priority)
3954 lhnetaddress_t masteraddress;
3956 lhnetsocket_t *mysocket;
3958 // if it's a state change (client connected), limit next heartbeat to no
3959 // more than 30 sec in the future
3960 if (priority == 1 && nextheartbeattime > host.realtime + 30.0)
3961 nextheartbeattime = host.realtime + 30.0;
3963 // limit heartbeatperiod to 30 to 270 second range,
3964 // lower limit is to avoid abusing master servers with excess traffic,
3965 // upper limit is to avoid timing out on the master server (which uses
3967 if (sv_heartbeatperiod.value < 30)
3968 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3969 if (sv_heartbeatperiod.value > 270)
3970 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3972 // make advertising optional and don't advertise singleplayer games, and
3973 // only send a heartbeat as often as the admin wants
3974 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
3976 nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
3977 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
3978 if (sv_masters[masternum].string[0]
3979 && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT)
3980 && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3981 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3985 static void Net_Heartbeat_f(cmd_state_t *cmd)
3988 NetConn_Heartbeat(2);
3990 Con_Print("No server running, can not heartbeat to master server.\n");
3993 static void PrintStats(netconn_t *conn)
3995 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3996 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3998 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3999 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
4000 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
4001 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
4002 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
4003 Con_Printf("packetsSent = %i\n", conn->packetsSent);
4004 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
4005 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
4006 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
4007 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
4010 void Net_Stats_f(cmd_state_t *cmd)
4013 Con_Print("connections =\n");
4014 for (conn = netconn_list;conn;conn = conn->next)
4019 void Net_Refresh_f(cmd_state_t *cmd)
4021 if (m_state != m_slist)
4023 Con_Print("Sending new requests to DP master servers\n");
4024 ServerList_QueryList(false, true, false, true);
4025 Con_Print("Listening for replies...\n");
4028 ServerList_QueryList(false, true, false, false);
4031 void Net_Slist_f(cmd_state_t *cmd)
4033 ServerList_ResetMasks();
4034 serverlist_sortbyfield = SLIF_PING;
4035 serverlist_sortflags = 0;
4036 if (m_state != m_slist)
4038 Con_Print("Sending requests to DP master servers\n");
4039 ServerList_QueryList(true, true, false, true);
4040 Con_Print("Listening for replies...\n");
4043 ServerList_QueryList(true, true, false, false);
4046 void Net_SlistQW_f(cmd_state_t *cmd)
4048 ServerList_ResetMasks();
4049 serverlist_sortbyfield = SLIF_PING;
4050 serverlist_sortflags = 0;
4051 if (m_state != m_slist)
4053 Con_Print("Sending requests to QW master servers\n");
4054 ServerList_QueryList(true, false, true, true);
4055 Con_Print("Listening for replies...\n");
4058 ServerList_QueryList(true, false, true, false);
4062 void NetConn_Init(void)
4065 lhnetaddress_t tempaddress;
4066 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
4067 Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
4069 Cmd_AddCommand(CF_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
4070 Cmd_AddCommand(CF_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
4071 Cmd_AddCommand(CF_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
4073 Cmd_AddCommand(CF_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
4074 Cvar_RegisterVariable(&net_test);
4075 Cvar_RegisterVariable(&net_usesizelimit);
4076 Cvar_RegisterVariable(&net_burstreserve);
4077 Cvar_RegisterVariable(&rcon_restricted_password);
4078 Cvar_RegisterVariable(&rcon_restricted_commands);
4079 Cvar_RegisterVariable(&rcon_secure_maxdiff);
4082 Cvar_RegisterVariable(&net_slist_debug);
4083 Cvar_RegisterVariable(&net_slist_favorites);
4084 Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
4085 Cvar_RegisterVariable(&net_slist_interval);
4086 Cvar_RegisterVariable(&net_slist_maxping);
4087 Cvar_RegisterVariable(&net_slist_maxtries);
4088 Cvar_RegisterVariable(&net_slist_pause);
4089 Cvar_RegisterCallback(&net_slist_pause, ServerList_RebuildViewList);
4090 Cvar_RegisterVariable(&net_slist_queriespersecond);
4091 Cvar_RegisterVariable(&net_slist_queriesperframe);
4092 Cvar_RegisterVariable(&net_slist_timeout);
4095 #ifdef IP_TOS // register cvar only if supported
4096 Cvar_RegisterVariable(&net_tos_dscp);
4098 Cvar_RegisterVariable(&net_messagetimeout);
4099 Cvar_RegisterVariable(&net_connecttimeout);
4100 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
4101 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
4102 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
4103 Cvar_RegisterVariable(&net_sourceaddresscheck);
4104 Cvar_RegisterVariable(&net_fakelag);
4105 Cvar_RegisterVariable(&net_fakeloss_send);
4106 Cvar_RegisterVariable(&net_fakeloss_receive);
4107 Cvar_RegisterVirtual(&net_fakelag, "cl_netlocalping");
4108 Cvar_RegisterVirtual(&net_fakeloss_send, "cl_netpacketloss_send");
4109 Cvar_RegisterVirtual(&net_fakeloss_receive, "cl_netpacketloss_receive");
4110 Cvar_RegisterVariable(&hostname);
4111 Cvar_RegisterVariable(&developer_networking);
4112 Cvar_RegisterVariable(&cl_netport);
4113 Cvar_RegisterCallback(&cl_netport, NetConn_CL_UpdateSockets_Callback);
4114 Cvar_RegisterVariable(&sv_netport);
4115 Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
4116 Cvar_RegisterVariable(&net_address);
4117 Cvar_RegisterVariable(&net_address_ipv6);
4118 Cvar_RegisterVariable(&sv_public);
4119 Cvar_RegisterVariable(&sv_public_rejectreason);
4120 Cvar_RegisterVariable(&sv_heartbeatperiod);
4121 for (uint8_t masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
4122 Cvar_RegisterVariable(&sv_masters[masternum]);
4123 Cvar_RegisterVariable(&gameversion);
4124 Cvar_RegisterVariable(&gameversion_min);
4125 Cvar_RegisterVariable(&gameversion_max);
4126 // 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.
4127 if ((i = Sys_CheckParm("-ip")) && i + 1 < sys.argc)
4129 if (LHNETADDRESS_FromString(&tempaddress, sys.argv[i + 1], 0) == 1)
4131 Con_Printf("-ip option used, setting net_address to \"%s\"\n", sys.argv[i + 1]);
4132 Cvar_SetQuick(&net_address, sys.argv[i + 1]);
4135 Con_Printf(CON_ERROR "-ip option used, but unable to parse the address \"%s\"\n", sys.argv[i + 1]);
4137 // 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
4138 if (((i = Sys_CheckParm("-port")) || (i = Sys_CheckParm("-ipport")) || (i = Sys_CheckParm("-udpport"))) && i + 1 < sys.argc)
4140 i = atoi(sys.argv[i + 1]);
4141 if (i >= 0 && i < 65536)
4143 Con_Printf("-port option used, setting port cvar to %i\n", i);
4144 Cvar_SetValueQuick(&sv_netport, i);
4147 Con_Printf(CON_ERROR "-port option used, but %i is not a valid port number\n", i);
4151 cl_message.data = cl_message_buf;
4152 cl_message.maxsize = sizeof(cl_message_buf);
4153 cl_message.cursize = 0;
4154 sv_message.data = sv_message_buf;
4155 sv_message.maxsize = sizeof(sv_message_buf);
4156 sv_message.cursize = 0;
4158 if (Thread_HasThreads())
4159 netconn_mutex = Thread_CreateMutex();
4162 void NetConn_Shutdown(void)
4164 NetConn_CloseClientPorts();
4165 NetConn_CloseServerPorts();
4168 Thread_DestroyMutex(netconn_mutex);
4169 netconn_mutex = NULL;