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)
1636 cls.connect_trying = false;
1637 // Disconnect from the current server or stop demo playback
1638 if(cls.state == ca_connected || cls.demoplayback)
1640 // allocate a net connection to keep track of things
1641 cls.netcon = NetConn_Open(mysocket, peeraddress);
1642 strlcpy(cl_connect_status, "Connection established", sizeof(cl_connect_status));
1643 crypto = &cls.netcon->crypto;
1644 if(cls.crypto.authenticated)
1646 Crypto_FinishInstance(crypto, &cls.crypto);
1647 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1648 crypto->use_aes ? "Encrypted" : "Authenticated",
1649 cls.netcon->address,
1650 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1651 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1652 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1653 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1654 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1655 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1659 Con_Printf("%s to %s\n", cl_connect_status, cls.netcon->address);
1661 key_dest = key_game;
1665 cls.demonum = -1; // not in the demo loop now
1666 cls.state = ca_connected;
1667 cls.signon = 0; // need all the signon messages before playing
1668 cls.protocol = initialprotocol;
1669 // reset move sequence numbering on this new connection
1670 cls.servermovesequence = 0;
1671 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1672 CL_ForwardToServer("new");
1673 if (cls.protocol == PROTOCOL_QUAKE)
1675 // write a keepalive (clc_nop) as it seems to greatly improve the
1676 // chances of connecting to a netquake server
1678 unsigned char buf[4];
1679 memset(&msg, 0, sizeof(msg));
1681 msg.maxsize = sizeof(buf);
1682 MSG_WriteChar(&msg, clc_nop);
1683 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1687 int NetConn_IsLocalGame(void)
1689 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1695 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
1696 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring, const char *challenge)
1700 double currentrealtime;
1701 serverlist_entry_t *entry;
1703 // search the cache for this server and update it
1704 for (n = 0; n < serverlist_cachecount; ++n)
1706 entry = &serverlist_cache[n];
1707 if (!strcmp(addressstring, entry->info.cname))
1711 if (n == serverlist_cachecount)
1713 if (net_slist_debug.integer)
1714 Con_Printf("^6Received LAN broadcast response from %s\n", addressstring);
1717 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1720 if (serverlist_maxcachecount <= serverlist_cachecount)
1722 serverlist_maxcachecount += 64;
1723 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1725 ++serverlist_cachecount;
1726 entry = &serverlist_cache[n];
1728 memset(entry, 0, sizeof(*entry));
1729 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1731 // consider the broadcast to be the first query
1732 // NetConn_QueryQueueFrame() will perform more until net_slist_maxtries is reached
1733 entry->querycounter = 1;
1734 entry->querytime = masterquerytime;
1735 // protocol is one of these at all
1736 // NetConn_ClientParsePacket_ServerList_PrepareQuery() callsites
1737 entry->protocol = challenge ? PROTOCOL_DARKPLACES7 : PROTOCOL_QUAKEWORLD;
1739 if (serverlist_consoleoutput)
1740 Con_Printf("querying %s\n", addressstring);
1743 // If the client stalls partway through a frame (test command: `alias a a;a`)
1744 // for correct pings we need to know what host.realtime would be if it were updated now.
1745 currentrealtime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
1749 unsigned char hash[24]; // 4*(16/3) rounded up to 4 byte multiple
1750 uint64_t timestamp = strtoull(&challenge[22], NULL, 16);
1752 HMAC_MDFOUR_16BYTES(hash,
1753 (unsigned char *)×tamp, sizeof(timestamp),
1754 (unsigned char *)serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
1755 base64_encode(hash, 16, sizeof(hash));
1756 if (memcmp(hash, challenge, 22) != 0)
1759 ping = currentrealtime * 1000.0 - timestamp;
1762 ping = 1000 * (currentrealtime - entry->querytime);
1764 if (ping <= 0 || ping > net_slist_maxping.value
1765 || (entry->info.ping && ping > entry->info.ping + 100)) // server loading map, client stall, etc
1768 // never round down to 0, 0 latency is impossible, 0 means no data available
1772 if (entry->info.ping)
1773 entry->info.ping = (entry->info.ping + ping) * 0.5 + 0.5; // "average" biased toward most recent results
1776 entry->info.ping = ping + 0.5;
1779 entry->responded = true;
1781 // other server info is updated by the caller
1785 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1787 serverlist_entry_t *entry = &serverlist_cache[n];
1788 serverlist_info_t *info = &entry->info;
1790 // update description strings for engine menu and console output
1791 dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5.0f^7 ^%c%3u^7/%3u %-65.65s",
1792 info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'),
1793 info->ping ?: INFINITY, // display inf when a listed server times out and net_slist_pause blocks its removal
1794 ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'),
1798 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1800 info->gameversion != gameversion.integer
1803 gameversion_min.integer >= 0 // min/max range set by user/mod?
1804 && gameversion_max.integer >= 0
1805 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1806 && gameversion_max.integer >= info->gameversion
1809 info->mod, info->map);
1811 if(!net_slist_pause.integer)
1813 ServerList_ViewList_Remove(entry);
1814 ServerList_ViewList_Insert(entry);
1817 if (serverlist_consoleoutput)
1818 Con_Printf("%s\n%s\n", entry->line1, entry->line2);
1821 // returns true, if it's sensible to continue the processing
1822 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery(int protocol, const char *ipstring, qbool isfavorite)
1825 serverlist_entry_t *entry;
1827 // ignore the rest of the message if the serverlist is full
1828 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1831 for (n = 0; n < serverlist_cachecount; ++n)
1832 if (!strcmp(ipstring, serverlist_cache[n].info.cname))
1835 // also ignore it if we have already queried it (other master server response)
1836 if (n < serverlist_cachecount)
1839 if (serverlist_maxcachecount <= n)
1841 serverlist_maxcachecount += 64;
1842 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1845 entry = &serverlist_cache[n];
1846 memset(entry, 0, sizeof(*entry));
1847 entry->protocol = protocol;
1848 strlcpy(entry->info.cname, ipstring, sizeof(entry->info.cname));
1849 entry->info.isfavorite = isfavorite;
1851 serverlist_cachecount++;
1857 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *masteraddress, const char *masteraddressstring, const unsigned char *data, int length, qbool isextended)
1860 lhnetaddress_t testaddress;
1862 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
1863 if (sv_masters[masternum].string[0]
1864 && LHNETADDRESS_FromString(&testaddress, sv_masters[masternum].string, DPMASTER_PORT)
1865 && LHNETADDRESS_Compare(&testaddress, masteraddress) == 0)
1867 if (net_sourceaddresscheck.integer && masternum >= DPMASTER_COUNT)
1869 Con_Printf(CON_WARN "ignoring DarkPlaces %sserver list from unrecognised master %s\n", isextended ? "extended " : "", masteraddressstring);
1874 dpmasterstatus[masternum] = MASTER_RX_RESPONSE;
1875 if (serverlist_consoleoutput || net_slist_debug.integer)
1876 Con_Printf("^5Received DarkPlaces server list %sfrom %s\n", isextended ? "(extended) " : "", sv_masters[masternum].string);
1880 char ipstring [128];
1883 if (data[0] == '\\')
1885 unsigned short port = data[5] * 256 + data[6];
1887 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1888 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1889 else if (port == 0 && data[1] == 'E' && data[2] == 'O' && data[3] == 'T' && data[4] == '\0')
1891 dpmasterstatus[masternum] = MASTER_RX_COMPLETE;
1892 if (net_slist_debug.integer)
1893 Con_Printf("^4End Of Transmission %sfrom %s\n", isextended ? "(extended) " : "", sv_masters[masternum].string);
1897 // move on to next address in packet
1902 else if (data[0] == '/' && isextended && length >= 19)
1904 unsigned short port = data[17] * 256 + data[18];
1912 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1914 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1917 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1918 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1919 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1925 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1926 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1927 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1932 // move on to next address in packet
1938 Con_Print(CON_WARN "Error while parsing the server list\n");
1942 if (serverlist_consoleoutput && developer_networking.integer)
1943 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1945 if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, ipstring, false))
1949 if (serverlist_querystage & SLIST_QUERYSTAGE_QWMASTERS)
1950 return; // we must wait if we're also querying QW as it has no EOT marker
1951 // begin or resume serverlist queries
1952 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
1953 if (dpmasterstatus[masternum] && dpmasterstatus[masternum] < MASTER_RX_COMPLETE)
1954 break; // was queried but no EOT marker received yet
1955 if (masternum >= DPMASTER_COUNT)
1957 serverlist_querystage = SLIST_QUERYSTAGE_SERVERS;
1958 if (net_slist_debug.integer)
1959 Con_Print("^2Starting to query servers early (got EOT from all masters)\n");
1963 static void NetConn_ClientParsePacket_ServerList_ParseQWList(lhnetaddress_t *masteraddress, const char *masteraddressstring, const unsigned char *data, int length)
1966 lhnetaddress_t testaddress;
1968 for (masternum = 0; masternum < QWMASTER_COUNT; ++masternum)
1969 if (sv_qwmasters[masternum].string[0]
1970 && LHNETADDRESS_FromString(&testaddress, sv_qwmasters[masternum].string, QWMASTER_PORT)
1971 && LHNETADDRESS_Compare(&testaddress, masteraddress) == 0)
1973 if (net_sourceaddresscheck.integer && masternum >= QWMASTER_COUNT)
1975 Con_Printf(CON_WARN "ignoring QuakeWorld server list from unrecognised master %s\n", masteraddressstring);
1980 qwmasterstatus[masternum] = MASTER_RX_RESPONSE;
1981 if (serverlist_consoleoutput || net_slist_debug.integer)
1982 Con_Printf("^5Received QuakeWorld server list from %s\n", sv_qwmasters[masternum].string);
1984 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
1988 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
1989 if (serverlist_consoleoutput && developer_networking.integer)
1990 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
1992 if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_QUAKEWORLD, ipstring, false))
1995 // move on to next address in packet
2000 // Unlike in NetConn_ClientParsePacket_ServerList_ParseDPList()
2001 // we can't start to query servers early here because QW has no EOT marker.
2005 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2009 char *string, addressstring2[128];
2010 char stringbuf[16384];
2011 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2014 char infostringvalue[MAX_INPUTLINE];
2018 // quakeworld ingame packet
2019 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
2021 // convert the address to a string incase we need it
2022 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2024 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2026 // received a command string - strip off the packaging and put it
2027 // into our string buffer with NULL termination
2030 length = min(length, (int)sizeof(stringbuf) - 1);
2031 memcpy(stringbuf, data, length);
2032 stringbuf[length] = 0;
2035 if (developer_networking.integer)
2037 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
2038 Com_HexDumpToConsole(data, length);
2041 sendlength = sizeof(senddata) - 4;
2042 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress, addressstring2))
2044 case CRYPTO_NOMATCH:
2050 memcpy(senddata, "\377\377\377\377", 4);
2051 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2054 case CRYPTO_DISCARD:
2057 memcpy(senddata, "\377\377\377\377", 4);
2058 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2062 case CRYPTO_REPLACE:
2063 string = senddata+4;
2064 length = (int)sendlength;
2068 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
2071 for (j = 0;j < MAX_RCONS;j++)
2073 // note: this value from i is used outside the loop too...
2074 i = (cls.rcon_ringpos + j) % MAX_RCONS;
2075 if(cls.rcon_commands[i][0])
2076 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
2085 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
2086 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
2088 e = strchr(rcon_password.string, ' ');
2089 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2091 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
2095 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
2096 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
2097 cls.rcon_commands[i][0] = 0;
2100 for (k = 0;k < MAX_RCONS;k++)
2101 if(cls.rcon_commands[k][0])
2102 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
2107 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
2108 // extend the timeout on other requests as we asked for a challenge
2109 for (l = 0;l < MAX_RCONS;l++)
2110 if(cls.rcon_commands[l][0])
2111 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
2112 cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
2115 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
2119 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
2121 // darkplaces or quake3
2122 char protocolnames[1400];
2124 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2125 Con_Printf(CON_WARN "ignoring challenge message from wrong server %s\n", addressstring2);
2128 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
2129 strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status));
2131 Protocol_Names(protocolnames, sizeof(protocolnames));
2132 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2133 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2134 // TODO: add userinfo stuff here instead of using NQ commands?
2135 memcpy(senddata, "\377\377\377\377", 4);
2136 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
2137 NetConn_WriteString(mysocket, senddata, peeraddress);
2140 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
2142 // darkplaces or quake3
2143 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2144 Con_Printf(CON_WARN "ignoring accept message from wrong server %s\n", addressstring2);
2147 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
2150 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
2152 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2153 Con_Printf(CON_WARN "ignoring reject message from wrong server %s\n", addressstring2);
2156 cls.connect_trying = false;
2158 length = min(length - 7, (int)sizeof(cl_connect_status) - 1);
2159 dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: rejected, %.*s", length, string);
2160 Con_Printf(CON_ERROR "Connect: rejected by %s\n" CON_ERROR "%.*s\n", addressstring2, length, string);
2164 if(key_dest != key_game)
2166 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
2168 serverlist_info_t *info;
2173 // search the cache for this server and update it
2174 // the challenge is (ab)used to return the query time
2175 s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue));
2176 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, s);
2180 info = &serverlist_cache[n].info;
2185 info->qcstatus[0] = 0;
2186 info->players[0] = 0;
2187 info->protocol = -1;
2188 info->numplayers = 0;
2190 info->maxplayers = 0;
2191 info->gameversion = 0;
2193 p = strchr(string, '\n');
2196 *p = 0; // cut off the string there
2200 Con_Printf("statusResponse without players block?\n");
2202 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2203 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2204 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2205 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2206 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2207 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2208 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2209 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2210 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2211 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2212 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2213 info->numhumans = info->numplayers - max(0, info->numbots);
2214 info->freeslots = info->maxplayers - info->numplayers;
2216 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2220 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2222 serverlist_info_t *info;
2226 // search the cache for this server and update it
2227 // the challenge is (ab)used to return the query time
2228 s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue));
2229 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, s);
2233 info = &serverlist_cache[n].info;
2238 info->qcstatus[0] = 0;
2239 info->players[0] = 0;
2240 info->protocol = -1;
2241 info->numplayers = 0;
2243 info->maxplayers = 0;
2244 info->gameversion = 0;
2246 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2247 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2248 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2249 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2250 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2251 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2252 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2253 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2254 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2255 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2256 info->numhumans = info->numplayers - max(0, info->numbots);
2257 info->freeslots = info->maxplayers - info->numplayers;
2259 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2263 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2265 // Extract the IP addresses
2268 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, addressstring2, data, length, false);
2271 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2273 // Extract the IP addresses
2276 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, addressstring2, data, length, true);
2279 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2281 // Extract the IP addresses
2284 NetConn_ClientParsePacket_ServerList_ParseQWList(peeraddress, addressstring2, data, length);
2289 if (!strncmp(string, "extResponse ", 12))
2291 ++cl_net_extresponse_count;
2292 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2293 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2294 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2295 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2298 if (!strncmp(string, "ping", 4))
2300 if (developer_extra.integer)
2301 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2302 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2305 if (!strncmp(string, "ack", 3))
2307 // QuakeWorld compatibility
2308 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2310 // challenge message
2311 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2312 Con_Printf(CON_WARN "ignoring c message from wrong server %s\n", addressstring2);
2315 Con_DPrintf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2316 strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status));
2318 cls.qw_qport = qport.integer;
2319 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2320 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2321 memcpy(senddata, "\377\377\377\377", 4);
2322 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);
2323 NetConn_WriteString(mysocket, senddata, peeraddress);
2326 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2329 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2330 Con_Printf(CON_WARN "ignoring j message from wrong server %s\n", addressstring2);
2333 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2336 if (length > 2 && !memcmp(string, "n\\", 2))
2339 serverlist_info_t *info;
2343 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2344 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2347 // search the cache for this server and update it
2348 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, NULL);
2352 info = &serverlist_cache[n].info;
2353 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2354 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2355 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2356 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2358 info->numplayers = 0; // updated below
2359 info->numhumans = 0; // updated below
2360 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2361 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2363 // count active players on server
2364 // (we could gather more info, but we're just after the number)
2365 s = strchr(string, '\n');
2369 while (s < string + length)
2371 for (;s < string + length && *s != '\n';s++)
2373 if (s >= string + length)
2381 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2385 if (string[0] == 'n')
2387 // qw print command, used by rcon replies too
2388 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2389 Con_Printf(CON_WARN "ignoring n message from wrong server %s\n", addressstring2);
2392 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2394 // we may not have liked the packet, but it was a command packet, so
2395 // we're done processing this packet now
2398 // quakeworld ingame packet
2399 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2402 CL_ParseServerMessage();
2405 // netquake control packets, supported for compatibility only
2406 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2410 serverlist_info_t *info;
2415 SZ_Clear(&cl_message);
2416 SZ_Write(&cl_message, data, length);
2417 MSG_BeginReading(&cl_message);
2418 c = MSG_ReadByte(&cl_message);
2422 if (developer_extra.integer)
2423 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2424 if (cls.connect_trying)
2426 lhnetaddress_t clientportaddress;
2427 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2428 Con_Printf(CON_WARN "ignoring CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2431 clientportaddress = *peeraddress;
2432 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2433 // extra ProQuake stuff
2435 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2437 cls.proquake_servermod = 0;
2439 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2441 cls.proquake_serverversion = 0;
2443 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2445 cls.proquake_serverflags = 0;
2446 if (cls.proquake_servermod == 1)
2447 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2448 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2449 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2450 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2454 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2456 Con_Printf(CON_WARN "ignoring CCREP_REJECT message from wrong server %s\n", addressstring2);
2459 cls.connect_trying = false;
2460 dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: rejected, %s", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2461 Con_Printf(CON_ERROR "Connect: rejected by %s\n%s\n", addressstring2, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2463 case CCREP_SERVER_INFO:
2464 if (developer_extra.integer)
2465 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2467 // LadyHavoc: because the quake server may report weird addresses
2468 // we just ignore it and keep the real address
2469 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2470 // search the cache for this server and update it
2471 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, NULL);
2475 info = &serverlist_cache[n].info;
2476 strlcpy(info->game, "Quake", sizeof(info->game));
2477 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2478 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2479 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2480 info->numplayers = MSG_ReadByte(&cl_message);
2481 info->maxplayers = MSG_ReadByte(&cl_message);
2482 info->protocol = MSG_ReadByte(&cl_message);
2484 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2487 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2488 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2489 Con_Printf(CON_WARN "ignoring CCREP_RCON message from wrong server %s\n", addressstring2);
2492 if (developer_extra.integer)
2493 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2495 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2497 case CCREP_PLAYER_INFO:
2498 // we got a CCREP_PLAYER_INFO??
2499 //if (developer_extra.integer)
2500 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2502 case CCREP_RULE_INFO:
2503 // we got a CCREP_RULE_INFO??
2504 //if (developer_extra.integer)
2505 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2510 SZ_Clear(&cl_message);
2511 // we may not have liked the packet, but it was a valid control
2512 // packet, so we're done processing this packet now
2516 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2517 CL_ParseServerMessage();
2522 void NetConn_QueryQueueFrame(void)
2525 unsigned queries, maxqueries;
2526 char dpquery[53]; // theoretical max: 14+22+16+1
2527 double currentrealtime;
2528 static double querycounter = 0;
2529 qbool pending = false;
2531 if (!serverlist_querystage)
2534 // If the client stalls partway through a frame (test command: `alias a a;a`)
2535 // for correct pings we need to know what host.realtime would be if it were updated now.
2536 currentrealtime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
2538 // apply a cool down time after master server replies,
2539 // to avoid messing up the ping times on the servers
2540 if (serverlist_querystage < SLIST_QUERYSTAGE_SERVERS)
2542 if (currentrealtime < masterquerytime + net_slist_timeout.value)
2545 // Report the masters that timed out or whose response was incomplete.
2546 for (index = 0; index < DPMASTER_COUNT; ++index)
2547 if (dpmasterstatus[index] && dpmasterstatus[index] < MASTER_RX_COMPLETE)
2548 Con_Printf(CON_WARN "WARNING: dpmaster %s %s\n", sv_masters[index].string, dpmasterstatus[index] == MASTER_TX_QUERY ? "timed out" : "response incomplete");
2549 for (index = 0; index < QWMASTER_COUNT; ++index)
2550 if (qwmasterstatus[index] && qwmasterstatus[index] < MASTER_RX_RESPONSE) // no EOT marker in QW lists
2551 Con_Printf(CON_WARN "WARNING: qwmaster %s timed out\n", sv_qwmasters[index].string);
2553 serverlist_querystage = SLIST_QUERYSTAGE_SERVERS;
2556 // each time querycounter reaches 1.0 issue a query
2557 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2558 maxqueries = bound(0, (int)querycounter, net_slist_queriesperframe.integer);
2559 querycounter -= maxqueries;
2560 if (maxqueries == 0)
2563 // QW depends on waiting "long enough" between queries that responses "definitely" refer to the most recent querytime
2564 // DP servers can echo back a timestamp for reliable (and more frequent, see net_slist_interval) pings
2565 ServerList_BuildDPServerQuery(dpquery, sizeof(dpquery), currentrealtime);
2567 for (index = 0, queries = 0; index < serverlist_cachecount && queries < maxqueries; ++index)
2569 serverlist_entry_t *entry = &serverlist_cache[index];
2571 if (entry->querycounter < min(8, (unsigned)net_slist_maxtries.integer))
2573 lhnetaddress_t address;
2576 // only check this when there are tries remaining so we finish querying sooner
2577 if (currentrealtime < entry->querytime + (entry->protocol == PROTOCOL_QUAKEWORLD ? net_slist_timeout : net_slist_interval).value)
2583 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2584 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2586 for (socket = 0; socket < cl_numsockets; ++socket)
2587 if (cl_sockets[socket])
2588 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2592 for (socket = 0; socket < cl_numsockets; ++socket)
2593 if (cl_sockets[socket])
2594 NetConn_WriteString(cl_sockets[socket], dpquery, &address);
2597 entry->querytime = currentrealtime;
2598 entry->querycounter++;
2601 if (serverlist_consoleoutput)
2602 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2604 else // reached net_slist_maxtries
2605 if (!entry->responded // no acceptable response during this refresh cycle
2606 && (entry->info.ping)) // visible in the list (has old ping from previous refresh cycle)
2608 if (currentrealtime >= entry->querytime + net_slist_maxping.integer/1000)
2610 // you have no chance to survive make your timeout
2612 if(!net_slist_pause.integer)
2613 ServerList_ViewList_Remove(entry);
2614 entry->info.ping = 0; // removed later if net_slist_pause
2616 else // still has time
2621 // If we got to the end of the list (didn't hit maxqueries)
2622 // and no servers remain to be queried or checked for timeout,
2623 // there's nothing else to do here until the next refresh cycle.
2624 if (index >= serverlist_cachecount && !pending)
2626 if (net_slist_debug.integer)
2627 Con_Printf("^2Finished querying masters and servers in %f\n", currentrealtime - masterquerytime);
2628 serverlist_querystage = 0;
2633 void NetConn_ClientFrame(void)
2637 lhnetaddress_t peeraddress;
2638 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2640 NetConn_UpdateSockets();
2642 if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2644 if (cls.connect_remainingtries > 0)
2646 cls.connect_remainingtries--;
2647 dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: sending initial request, %i %s left...", cls.connect_remainingtries, cls.connect_remainingtries == 1 ? "retry" : "retries");
2653 cls.connect_trying = false;
2654 LHNETADDRESS_ToString(&cls.connect_address, address, sizeof(address), true);
2655 strlcpy(cl_connect_status, "Connect: failed, no reply", sizeof(cl_connect_status));
2656 Con_Printf(CON_ERROR "%s from %s\n", cl_connect_status, address);
2659 cls.connect_nextsendtime = host.realtime + 1;
2661 // try challenge first (newer DP server or QW)
2662 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2663 // then try netquake as a fallback (old server, or netquake)
2664 SZ_Clear(&cl_message);
2665 // save space for the header, filled in later
2666 MSG_WriteLong(&cl_message, 0);
2667 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2668 MSG_WriteString(&cl_message, "QUAKE");
2669 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2670 // extended proquake stuff
2671 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2672 // this version matches ProQuake 3.40, the first version to support
2673 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2674 // higher clients, so we pretend we are that version...
2675 MSG_WriteByte(&cl_message, 34); // version * 10
2676 MSG_WriteByte(&cl_message, 0); // flags
2677 MSG_WriteLong(&cl_message, 0); // password
2678 // write the packetsize now...
2679 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2680 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2681 SZ_Clear(&cl_message);
2684 for (i = 0;i < cl_numsockets;i++)
2686 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2688 // R_TimeReport("clientreadnetwork");
2689 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2690 // R_TimeReport("clientparsepacket");
2694 NetConn_QueryQueueFrame();
2696 if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2697 CL_DisconnectEx(true, "Connection timed out");
2700 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2704 for (i = 0;i < bufferlength - 1;i++)
2708 c = rand () % (127 - 33) + 33;
2709 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2715 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2716 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2718 prvm_prog_t *prog = SVVM_prog;
2720 unsigned int nb_clients = 0, nb_bots = 0, i;
2723 const char *crypto_idstring;
2724 const char *worldstatusstr;
2726 // How many clients are there?
2727 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2729 if (svs.clients[i].active)
2732 if (!svs.clients[i].netconnection)
2738 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2739 if(worldstatusstr && *worldstatusstr)
2744 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2745 if(*q != '\\' && *q != '\n')
2750 /// \TODO: we should add more information for the full status string
2751 crypto_idstring = Crypto_GetInfoResponseDataString();
2752 length = dpsnprintf(out_msg, out_size,
2753 "\377\377\377\377%s\x0A"
2754 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2755 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2760 fullstatus ? "statusResponse" : "infoResponse",
2761 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2762 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2763 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2764 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2765 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2766 fullstatus ? "\n" : "");
2768 // Make sure it fits in the buffer
2778 savelength = length;
2780 ptr = out_msg + length;
2781 left = (int)out_size - length;
2783 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2785 client_t *client = &svs.clients[i];
2788 int nameind, cleanind, pingvalue;
2790 char cleanname [sizeof(client->name)];
2791 const char *statusstr;
2794 // Remove all characters '"' and '\' in the player name
2799 curchar = client->name[nameind++];
2800 if (curchar != '"' && curchar != '\\')
2802 cleanname[cleanind++] = curchar;
2803 if (cleanind == sizeof(cleanname) - 1)
2806 } while (curchar != '\0');
2807 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2809 pingvalue = (int)(client->ping * 1000.0f);
2810 if(client->netconnection)
2811 pingvalue = bound(1, pingvalue, 9999);
2816 ed = PRVM_EDICT_NUM(i + 1);
2817 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2818 if(statusstr && *statusstr)
2823 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2824 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2829 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2831 if(client->frags == -666) // spectator
2832 strlcpy(teambuf, " 0", sizeof(teambuf));
2833 else if(client->colors == 0x44) // red team
2834 strlcpy(teambuf, " 1", sizeof(teambuf));
2835 else if(client->colors == 0xDD) // blue team
2836 strlcpy(teambuf, " 2", sizeof(teambuf));
2837 else if(client->colors == 0xCC) // yellow team
2838 strlcpy(teambuf, " 3", sizeof(teambuf));
2839 else if(client->colors == 0x99) // pink team
2840 strlcpy(teambuf, " 4", sizeof(teambuf));
2842 strlcpy(teambuf, " 0", sizeof(teambuf));
2847 // note: team number is inserted according to SoF2 protocol
2849 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2855 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2864 // turn it into an infoResponse!
2865 out_msg[savelength] = 0;
2866 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2867 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2882 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2884 size_t floodslotnum, bestfloodslotnum;
2885 double bestfloodtime;
2886 lhnetaddress_t noportpeeraddress;
2887 // see if this is a connect flood
2888 noportpeeraddress = *peeraddress;
2889 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2890 bestfloodslotnum = 0;
2891 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2892 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2894 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2896 bestfloodtime = floodlist[floodslotnum].lasttime;
2897 bestfloodslotnum = floodslotnum;
2899 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2901 // this address matches an ongoing flood address
2902 if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2906 // renew the ban on this address so it does not expire
2907 // until the flood has subsided
2908 floodlist[floodslotnum].lasttime = host.realtime;
2910 //Con_Printf("Flood detected!\n");
2913 // the flood appears to have subsided, so allow this
2914 bestfloodslotnum = floodslotnum; // reuse the same slot
2918 // begin a new timeout on this address
2919 floodlist[bestfloodslotnum].address = noportpeeraddress;
2920 floodlist[bestfloodslotnum].lasttime = host.realtime;
2921 //Con_Printf("Flood detection initiated!\n");
2925 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2927 size_t floodslotnum;
2928 lhnetaddress_t noportpeeraddress;
2929 // see if this is a connect flood
2930 noportpeeraddress = *peeraddress;
2931 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2932 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2934 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2936 // this address matches an ongoing flood address
2938 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2939 floodlist[floodslotnum].lasttime = 0;
2940 //Con_Printf("Flood cleared!\n");
2945 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2947 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2953 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2957 t1 = (long) time(NULL);
2958 t2 = strtol(s, NULL, 0);
2959 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2962 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2965 return !memcmp(mdfourbuf, hash, 16);
2968 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2974 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2978 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2981 // validate the challenge
2982 for (i = 0;i < MAX_CHALLENGES;i++)
2983 if(challenges[i].time > 0)
2984 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2986 // if the challenge is not recognized, drop the packet
2987 if (i == MAX_CHALLENGES)
2990 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2993 if(memcmp(mdfourbuf, hash, 16))
2996 // unmark challenge to prevent replay attacks
2997 challenges[i].time = 0;
3002 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
3005 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
3009 return !strcmp(password, hash);
3012 /// returns a string describing the user level, or NULL for auth failure
3013 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)
3015 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
3016 static char buf[MAX_INPUTLINE];
3018 qbool restricted = false;
3019 qbool have_usernames = false;
3020 static char vabuf[1024];
3022 userpass_start = rcon_password.string;
3023 while((userpass_end = strchr(userpass_start, ' ')))
3025 have_usernames = true;
3026 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
3027 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
3028 if(comparator(peeraddress, buf, password, cs, cslen))
3030 userpass_start = userpass_end + 1;
3032 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
3034 userpass_end = userpass_start + strlen(userpass_start);
3035 if(comparator(peeraddress, userpass_start, password, cs, cslen))
3040 have_usernames = false;
3041 userpass_start = rcon_restricted_password.string;
3042 while((userpass_end = strchr(userpass_start, ' ')))
3044 have_usernames = true;
3045 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
3046 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
3047 if(comparator(peeraddress, buf, password, cs, cslen))
3049 userpass_start = userpass_end + 1;
3051 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
3053 userpass_end = userpass_start + strlen(userpass_start);
3054 if(comparator(peeraddress, userpass_start, password, cs, cslen))
3058 return NULL; // DENIED
3061 for(text = s; text != endpos; ++text)
3062 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
3063 return NULL; // block possible exploits against the parser/alias expansion
3067 size_t l = strlen(s);
3070 hasquotes = (strchr(s, '"') != NULL);
3071 // sorry, we can't allow these substrings in wildcard expressions,
3072 // as they can mess with the argument counts
3073 text = rcon_restricted_commands.string;
3074 while(COM_ParseToken_Console(&text))
3076 // com_token now contains a pattern to check for...
3077 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
3080 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
3083 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
3085 if(!strcmp(com_token, s))
3088 else // single-arg expression? must match the beginning of the command
3090 if(!strcmp(com_token, s))
3092 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
3096 // if we got here, nothing matched!
3104 userpass_startpass = strchr(userpass_start, ':');
3105 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
3106 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
3108 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
3111 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qbool proquakeprotocol)
3115 // looks like a legitimate rcon command with the correct password
3116 const char *s_ptr = s;
3117 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
3118 while(s_ptr != endpos)
3120 size_t l = strlen(s_ptr);
3122 Con_Printf(" %s;", s_ptr);
3127 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3128 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
3131 size_t l = strlen(s);
3134 client_t *host_client_save = host_client;
3135 Cmd_ExecuteString(cmd_local, s, src_local, true);
3136 host_client = host_client_save;
3137 // in case it is a command that changes host_client (like restart)
3141 Con_Rcon_Redirect_End();
3145 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
3149 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
3151 int i, ret, clientnum, best;
3153 char *string, response[2800], addressstring2[128];
3154 static char stringbuf[16384]; // server only
3155 qbool islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
3156 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
3157 size_t sendlength, response_len;
3158 char infostringvalue[MAX_INPUTLINE];
3163 // convert the address to a string incase we need it
3164 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
3166 // see if we can identify the sender as a local player
3167 // (this is necessary for rcon to send a reliable reply if the client is
3168 // actually on the server, not sending remotely)
3169 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3170 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
3172 if (i == svs.maxclients)
3175 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
3177 // received a command string - strip off the packaging and put it
3178 // into our string buffer with NULL termination
3181 length = min(length, (int)sizeof(stringbuf) - 1);
3182 memcpy(stringbuf, data, length);
3183 stringbuf[length] = 0;
3186 if (developer_extra.integer)
3188 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
3189 Com_HexDumpToConsole(data, length);
3192 sendlength = sizeof(senddata) - 4;
3193 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
3195 case CRYPTO_NOMATCH:
3201 memcpy(senddata, "\377\377\377\377", 4);
3202 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3205 case CRYPTO_DISCARD:
3208 memcpy(senddata, "\377\377\377\377", 4);
3209 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3213 case CRYPTO_REPLACE:
3214 string = senddata+4;
3215 length = (int)sendlength;
3219 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3221 for (i = 0, best = 0, besttime = host.realtime;i < MAX_CHALLENGES;i++)
3223 if(challenges[i].time > 0)
3224 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3226 if (besttime > challenges[i].time)
3227 besttime = challenges[best = i].time;
3229 // if we did not find an exact match, choose the oldest and
3230 // update address and string
3231 if (i == MAX_CHALLENGES)
3234 challenges[i].address = *peeraddress;
3235 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3239 // flood control: drop if requesting challenge too often
3240 if(challenges[i].time > host.realtime - net_challengefloodblockingtimeout.value)
3243 challenges[i].time = host.realtime;
3244 // send the challenge
3245 memcpy(response, "\377\377\377\377", 4);
3246 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3247 response_len = strlen(response) + 1;
3248 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3249 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3252 if (length > 8 && !memcmp(string, "connect\\", 8))
3256 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3260 if(crypto && crypto->authenticated)
3262 // no need to check challenge
3263 if(crypto_developer.integer)
3265 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3266 crypto->use_aes ? "Encrypted" : "Authenticated",
3268 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3269 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3270 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3271 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3272 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3273 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3279 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3281 // validate the challenge
3282 for (i = 0;i < MAX_CHALLENGES;i++)
3283 if(challenges[i].time > 0)
3284 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3286 // if the challenge is not recognized, drop the packet
3287 if (i == MAX_CHALLENGES)
3292 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3293 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3295 if(!(islocal || sv_public.integer > -2))
3297 if (developer_extra.integer)
3298 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3299 memcpy(response, "\377\377\377\377", 4);
3300 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3301 NetConn_WriteString(mysocket, response, peeraddress);
3305 // check engine protocol
3306 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3308 if (developer_extra.integer)
3309 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3310 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3314 // see if this is a duplicate connection request or a disconnected
3315 // client who is rejoining to the same client slot
3316 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3318 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3320 // this is a known client...
3321 if(crypto && crypto->authenticated)
3323 // reject if changing key!
3324 if(client->netconnection->crypto.authenticated)
3327 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3329 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3331 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3333 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3336 if (developer_extra.integer)
3337 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3338 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3345 // reject if downgrading!
3346 if(client->netconnection->crypto.authenticated)
3348 if (developer_extra.integer)
3349 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3350 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3356 // client crashed and is coming back,
3357 // keep their stuff intact
3358 if (developer_extra.integer)
3359 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3360 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3361 if(crypto && crypto->authenticated)
3362 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3363 SV_SendServerinfo(client);
3367 // client is still trying to connect,
3368 // so we send a duplicate reply
3369 if (developer_extra.integer)
3370 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3371 if(crypto && crypto->authenticated)
3372 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3373 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3379 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3382 // find an empty client slot for this new client
3383 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3386 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3388 // allocated connection
3389 if (developer_extra.integer)
3390 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3391 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3392 // now set up the client
3393 if(crypto && crypto->authenticated)
3394 Crypto_FinishInstance(&conn->crypto, crypto);
3395 SV_ConnectClient(clientnum, conn);
3396 NetConn_Heartbeat(1);
3401 // no empty slots found - server is full
3402 if (developer_extra.integer)
3403 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3404 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3408 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3410 const char *challenge = NULL;
3412 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3415 // If there was a challenge in the getinfo message
3416 if (length > 8 && string[7] == ' ')
3417 challenge = string + 8;
3419 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3421 if (developer_extra.integer)
3422 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3423 NetConn_WriteString(mysocket, response, peeraddress);
3427 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3429 const char *challenge = NULL;
3431 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3434 // If there was a challenge in the getinfo message
3435 if (length > 10 && string[9] == ' ')
3436 challenge = string + 10;
3438 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3440 if (developer_extra.integer)
3441 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3442 NetConn_WriteString(mysocket, response, peeraddress);
3446 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3448 char *password = string + 20;
3449 char *timeval = string + 37;
3450 char *s = strchr(timeval, ' ');
3451 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3452 const char *userlevel;
3454 if(rcon_secure.integer > 1)
3458 return true; // invalid packet
3461 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3462 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3465 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3467 char *password = string + 25;
3468 char *challenge = string + 42;
3469 char *s = strchr(challenge, ' ');
3470 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3471 const char *userlevel;
3473 return true; // invalid packet
3476 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3477 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3480 if (length >= 5 && !memcmp(string, "rcon ", 5))
3483 char *s = string + 5;
3484 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3487 if(rcon_secure.integer > 0)
3490 for (j = 0;!ISWHITESPACE(*s);s++)
3491 if (j < (int)sizeof(password) - 1)
3493 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3496 if (!ISWHITESPACE(password[0]))
3498 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3499 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3503 if (!strncmp(string, "extResponse ", 12))
3505 ++sv_net_extresponse_count;
3506 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3507 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3508 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3509 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3512 if (!strncmp(string, "ping", 4))
3514 if (developer_extra.integer)
3515 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3516 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3519 if (!strncmp(string, "ack", 3))
3521 // we may not have liked the packet, but it was a command packet, so
3522 // we're done processing this packet now
3525 // netquake control packets, supported for compatibility only, and only
3526 // when running game protocols that are normally served via this connection
3528 // (this protects more modern protocols against being used for
3529 // Quake packet flood Denial Of Service attacks)
3530 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)
3534 const char *protocolname;
3535 client_t *knownclient;
3536 client_t *newclient;
3539 SZ_Clear(&sv_message);
3540 SZ_Write(&sv_message, data, length);
3541 MSG_BeginReading(&sv_message);
3542 c = MSG_ReadByte(&sv_message);
3546 if (developer_extra.integer)
3547 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3548 if(!(islocal || sv_public.integer > -2))
3550 if (developer_extra.integer)
3551 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3552 SZ_Clear(&sv_message);
3553 // save space for the header, filled in later
3554 MSG_WriteLong(&sv_message, 0);
3555 MSG_WriteByte(&sv_message, CCREP_REJECT);
3556 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3557 MSG_WriteString(&sv_message, "\n");
3558 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3559 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3560 SZ_Clear(&sv_message);
3564 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3565 protocolnumber = MSG_ReadByte(&sv_message);
3566 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3568 if (developer_extra.integer)
3569 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3570 SZ_Clear(&sv_message);
3571 // save space for the header, filled in later
3572 MSG_WriteLong(&sv_message, 0);
3573 MSG_WriteByte(&sv_message, CCREP_REJECT);
3574 MSG_WriteString(&sv_message, "Incompatible version.\n");
3575 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3576 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3577 SZ_Clear(&sv_message);
3581 // see if this connect request comes from a known client
3582 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3584 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3586 // this is either a duplicate connection request
3587 // or coming back from a timeout
3588 // (if so, keep their stuff intact)
3590 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3591 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3593 if (developer_extra.integer)
3594 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3595 SZ_Clear(&sv_message);
3596 // save space for the header, filled in later
3597 MSG_WriteLong(&sv_message, 0);
3598 MSG_WriteByte(&sv_message, CCREP_REJECT);
3599 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3600 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3601 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3602 SZ_Clear(&sv_message);
3607 if (developer_extra.integer)
3608 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3609 SZ_Clear(&sv_message);
3610 // save space for the header, filled in later
3611 MSG_WriteLong(&sv_message, 0);
3612 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3613 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3614 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3615 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3616 SZ_Clear(&sv_message);
3618 // if client is already spawned, re-send the
3619 // serverinfo message as they'll need it to play
3620 if (knownclient->begun)
3621 SV_SendServerinfo(knownclient);
3626 // this is a new client, check for connection flood
3627 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3630 // find a slot for the new client
3631 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3634 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3636 // connect to the client
3637 // everything is allocated, just fill in the details
3638 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3639 if (developer_extra.integer)
3640 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3641 // send back the info about the server connection
3642 SZ_Clear(&sv_message);
3643 // save space for the header, filled in later
3644 MSG_WriteLong(&sv_message, 0);
3645 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3646 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3647 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3648 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3649 SZ_Clear(&sv_message);
3650 // now set up the client struct
3651 SV_ConnectClient(clientnum, conn);
3652 NetConn_Heartbeat(1);
3657 if (developer_extra.integer)
3658 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3659 // no room; try to let player know
3660 SZ_Clear(&sv_message);
3661 // save space for the header, filled in later
3662 MSG_WriteLong(&sv_message, 0);
3663 MSG_WriteByte(&sv_message, CCREP_REJECT);
3664 MSG_WriteString(&sv_message, "Server is full.\n");
3665 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3666 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3667 SZ_Clear(&sv_message);
3669 case CCREQ_SERVER_INFO:
3670 if (developer_extra.integer)
3671 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3672 if(!(islocal || sv_public.integer > -1))
3675 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3678 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3681 char myaddressstring[128];
3682 if (developer_extra.integer)
3683 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3684 SZ_Clear(&sv_message);
3685 // save space for the header, filled in later
3686 MSG_WriteLong(&sv_message, 0);
3687 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3688 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3689 MSG_WriteString(&sv_message, myaddressstring);
3690 MSG_WriteString(&sv_message, hostname.string);
3691 MSG_WriteString(&sv_message, sv.name);
3692 // How many clients are there?
3693 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3694 if (svs.clients[i].active)
3696 MSG_WriteByte(&sv_message, numclients);
3697 MSG_WriteByte(&sv_message, svs.maxclients);
3698 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3699 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3700 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3701 SZ_Clear(&sv_message);
3704 case CCREQ_PLAYER_INFO:
3705 if (developer_extra.integer)
3706 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3707 if(!(islocal || sv_public.integer > -1))
3710 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3715 int playerNumber, activeNumber, clientNumber;
3718 playerNumber = MSG_ReadByte(&sv_message);
3720 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3721 if (client->active && ++activeNumber == playerNumber)
3723 if (clientNumber != svs.maxclients)
3725 SZ_Clear(&sv_message);
3726 // save space for the header, filled in later
3727 MSG_WriteLong(&sv_message, 0);
3728 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3729 MSG_WriteByte(&sv_message, playerNumber);
3730 MSG_WriteString(&sv_message, client->name);
3731 MSG_WriteLong(&sv_message, client->colors);
3732 MSG_WriteLong(&sv_message, client->frags);
3733 MSG_WriteLong(&sv_message, (int)(host.realtime - client->connecttime));
3734 if(sv_status_privacy.integer)
3735 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3737 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3738 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3739 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3740 SZ_Clear(&sv_message);
3744 case CCREQ_RULE_INFO:
3745 if (developer_extra.integer)
3746 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3747 if(!(islocal || sv_public.integer > -1))
3750 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3757 // find the search start location
3758 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3759 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CF_NOTIFY);
3761 // send the response
3762 SZ_Clear(&sv_message);
3763 // save space for the header, filled in later
3764 MSG_WriteLong(&sv_message, 0);
3765 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3768 MSG_WriteString(&sv_message, var->name);
3769 MSG_WriteString(&sv_message, var->string);
3771 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3772 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3773 SZ_Clear(&sv_message);
3777 if (developer_extra.integer)
3778 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3779 if (sv.active && !rcon_secure.integer)
3781 char password[2048];
3785 const char *userlevel;
3786 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3787 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3789 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3790 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3791 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3798 SZ_Clear(&sv_message);
3799 // we may not have liked the packet, but it was a valid control
3800 // packet, so we're done processing this packet now
3805 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3807 SV_ReadClientMessage();
3814 void NetConn_ServerFrame(void)
3818 lhnetaddress_t peeraddress;
3819 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3821 for (i = 0;i < sv_numsockets;i++)
3822 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3823 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3827 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
3831 lhnetaddress_t masteraddress;
3833 char lookupstring[128];
3835 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3838 memset(dpmasterstatus, 0, sizeof(*dpmasterstatus));
3839 memset(qwmasterstatus, 0, sizeof(*qwmasterstatus));
3843 for (i = 0;i < cl_numsockets;i++)
3847 const char *cmdname, *extraoptions;
3848 lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3850 // build the getservers message to send to the dpmaster master servers
3851 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3853 cmdname = "getserversExt";
3854 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3858 cmdname = "getservers";
3861 memcpy(request, "\377\377\377\377", 4);
3862 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3865 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
3867 if(sv_masters[masternum].string[0]
3868 && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT)
3869 && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3871 if (serverlist_consoleoutput || net_slist_debug.integer)
3873 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3874 Con_Printf("Querying DP master %s (resolved from %s)\n", lookupstring, sv_masters[masternum].string);
3877 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3878 dpmasterstatus[masternum] = MASTER_TX_QUERY;
3882 // search favorite servers
3883 for(j = 0; j < nFavorites; ++j)
3884 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af
3885 && LHNETADDRESS_ToString(&favorites[j], lookupstring, sizeof(lookupstring), true))
3886 NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, lookupstring, true);
3891 // only query QuakeWorld servers when the user wants to
3894 dpsnprintf(request, sizeof(request), "c\n");
3896 for (i = 0;i < cl_numsockets;i++)
3900 lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3903 for (masternum = 0; masternum < QWMASTER_COUNT; ++masternum)
3905 if(sv_qwmasters[masternum].string[0]
3906 && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT)
3907 && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3909 if (serverlist_consoleoutput || net_slist_debug.integer)
3911 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3912 Con_Printf("Querying QW master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3915 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3916 qwmasterstatus[masternum] = MASTER_TX_QUERY;
3920 // search favorite servers
3921 for(j = 0; j < nFavorites; ++j)
3922 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af
3923 && LHNETADDRESS_ToString(&favorites[j], lookupstring, sizeof(lookupstring), true))
3924 NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_QUAKEWORLD, lookupstring, true);
3929 if (!masterquerycount)
3931 Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
3932 strlcpy(cl_connect_status, "No network", sizeof(cl_connect_status));
3937 void NetConn_Heartbeat(int priority)
3939 lhnetaddress_t masteraddress;
3941 lhnetsocket_t *mysocket;
3943 // if it's a state change (client connected), limit next heartbeat to no
3944 // more than 30 sec in the future
3945 if (priority == 1 && nextheartbeattime > host.realtime + 30.0)
3946 nextheartbeattime = host.realtime + 30.0;
3948 // limit heartbeatperiod to 30 to 270 second range,
3949 // lower limit is to avoid abusing master servers with excess traffic,
3950 // upper limit is to avoid timing out on the master server (which uses
3952 if (sv_heartbeatperiod.value < 30)
3953 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3954 if (sv_heartbeatperiod.value > 270)
3955 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3957 // make advertising optional and don't advertise singleplayer games, and
3958 // only send a heartbeat as often as the admin wants
3959 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
3961 nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
3962 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
3963 if (sv_masters[masternum].string[0]
3964 && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT)
3965 && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3966 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3970 static void Net_Heartbeat_f(cmd_state_t *cmd)
3973 NetConn_Heartbeat(2);
3975 Con_Print("No server running, can not heartbeat to master server.\n");
3978 static void PrintStats(netconn_t *conn)
3980 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3981 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3983 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3984 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3985 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3986 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3987 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3988 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3989 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3990 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3991 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3992 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3995 void Net_Stats_f(cmd_state_t *cmd)
3998 Con_Print("connections =\n");
3999 for (conn = netconn_list;conn;conn = conn->next)
4004 void Net_Refresh_f(cmd_state_t *cmd)
4006 if (m_state != m_slist)
4008 Con_Print("Sending new requests to DP master servers\n");
4009 ServerList_QueryList(false, true, false, true);
4010 Con_Print("Listening for replies...\n");
4013 ServerList_QueryList(false, true, false, false);
4016 void Net_Slist_f(cmd_state_t *cmd)
4018 ServerList_ResetMasks();
4019 serverlist_sortbyfield = SLIF_PING;
4020 serverlist_sortflags = 0;
4021 if (m_state != m_slist)
4023 Con_Print("Sending requests to DP master servers\n");
4024 ServerList_QueryList(true, true, false, true);
4025 Con_Print("Listening for replies...\n");
4028 ServerList_QueryList(true, true, false, false);
4031 void Net_SlistQW_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 QW master servers\n");
4039 ServerList_QueryList(true, false, true, true);
4040 Con_Print("Listening for replies...\n");
4043 ServerList_QueryList(true, false, true, false);
4047 void NetConn_Init(void)
4050 lhnetaddress_t tempaddress;
4051 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
4052 Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
4054 Cmd_AddCommand(CF_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
4055 Cmd_AddCommand(CF_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
4056 Cmd_AddCommand(CF_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
4058 Cmd_AddCommand(CF_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
4059 Cvar_RegisterVariable(&net_test);
4060 Cvar_RegisterVariable(&net_usesizelimit);
4061 Cvar_RegisterVariable(&net_burstreserve);
4062 Cvar_RegisterVariable(&rcon_restricted_password);
4063 Cvar_RegisterVariable(&rcon_restricted_commands);
4064 Cvar_RegisterVariable(&rcon_secure_maxdiff);
4067 Cvar_RegisterVariable(&net_slist_debug);
4068 Cvar_RegisterVariable(&net_slist_favorites);
4069 Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
4070 Cvar_RegisterVariable(&net_slist_interval);
4071 Cvar_RegisterVariable(&net_slist_maxping);
4072 Cvar_RegisterVariable(&net_slist_maxtries);
4073 Cvar_RegisterVariable(&net_slist_pause);
4074 Cvar_RegisterCallback(&net_slist_pause, ServerList_RebuildViewList);
4075 Cvar_RegisterVariable(&net_slist_queriespersecond);
4076 Cvar_RegisterVariable(&net_slist_queriesperframe);
4077 Cvar_RegisterVariable(&net_slist_timeout);
4080 #ifdef IP_TOS // register cvar only if supported
4081 Cvar_RegisterVariable(&net_tos_dscp);
4083 Cvar_RegisterVariable(&net_messagetimeout);
4084 Cvar_RegisterVariable(&net_connecttimeout);
4085 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
4086 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
4087 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
4088 Cvar_RegisterVariable(&net_sourceaddresscheck);
4089 Cvar_RegisterVariable(&net_fakelag);
4090 Cvar_RegisterVariable(&net_fakeloss_send);
4091 Cvar_RegisterVariable(&net_fakeloss_receive);
4092 Cvar_RegisterVirtual(&net_fakelag, "cl_netlocalping");
4093 Cvar_RegisterVirtual(&net_fakeloss_send, "cl_netpacketloss_send");
4094 Cvar_RegisterVirtual(&net_fakeloss_receive, "cl_netpacketloss_receive");
4095 Cvar_RegisterVariable(&hostname);
4096 Cvar_RegisterVariable(&developer_networking);
4097 Cvar_RegisterVariable(&cl_netport);
4098 Cvar_RegisterCallback(&cl_netport, NetConn_CL_UpdateSockets_Callback);
4099 Cvar_RegisterVariable(&sv_netport);
4100 Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
4101 Cvar_RegisterVariable(&net_address);
4102 Cvar_RegisterVariable(&net_address_ipv6);
4103 Cvar_RegisterVariable(&sv_public);
4104 Cvar_RegisterVariable(&sv_public_rejectreason);
4105 Cvar_RegisterVariable(&sv_heartbeatperiod);
4106 for (uint8_t masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
4107 Cvar_RegisterVariable(&sv_masters[masternum]);
4108 Cvar_RegisterVariable(&gameversion);
4109 Cvar_RegisterVariable(&gameversion_min);
4110 Cvar_RegisterVariable(&gameversion_max);
4111 // 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.
4112 if ((i = Sys_CheckParm("-ip")) && i + 1 < sys.argc)
4114 if (LHNETADDRESS_FromString(&tempaddress, sys.argv[i + 1], 0) == 1)
4116 Con_Printf("-ip option used, setting net_address to \"%s\"\n", sys.argv[i + 1]);
4117 Cvar_SetQuick(&net_address, sys.argv[i + 1]);
4120 Con_Printf(CON_ERROR "-ip option used, but unable to parse the address \"%s\"\n", sys.argv[i + 1]);
4122 // 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
4123 if (((i = Sys_CheckParm("-port")) || (i = Sys_CheckParm("-ipport")) || (i = Sys_CheckParm("-udpport"))) && i + 1 < sys.argc)
4125 i = atoi(sys.argv[i + 1]);
4126 if (i >= 0 && i < 65536)
4128 Con_Printf("-port option used, setting port cvar to %i\n", i);
4129 Cvar_SetValueQuick(&sv_netport, i);
4132 Con_Printf(CON_ERROR "-port option used, but %i is not a valid port number\n", i);
4136 cl_message.data = cl_message_buf;
4137 cl_message.maxsize = sizeof(cl_message_buf);
4138 cl_message.cursize = 0;
4139 sv_message.data = sv_message_buf;
4140 sv_message.maxsize = sizeof(sv_message_buf);
4141 sv_message.cursize = 0;
4143 if (Thread_HasThreads())
4144 netconn_mutex = Thread_CreateMutex();
4147 void NetConn_Shutdown(void)
4149 NetConn_CloseClientPorts();
4150 NetConn_CloseServerPorts();
4153 Thread_DestroyMutex(netconn_mutex);
4154 netconn_mutex = NULL;