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)"},
54 static cvar_t sv_qwmasters [] =
56 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
57 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
58 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
59 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
60 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
61 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
62 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
63 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
68 static double nextheartbeattime = 0;
72 static unsigned char cl_message_buf[NET_MAXMESSAGE];
73 static unsigned char sv_message_buf[NET_MAXMESSAGE];
74 char cl_readstring[MAX_INPUTLINE];
75 char sv_readstring[MAX_INPUTLINE];
77 cvar_t net_test = {CF_CLIENT | CF_SERVER, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
78 cvar_t net_usesizelimit = {CF_SERVER, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
79 cvar_t net_burstreserve = {CF_SERVER, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
80 cvar_t net_messagetimeout = {CF_CLIENT | CF_SERVER, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
81 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."};
82 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."};
83 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."};
84 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 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
85 cvar_t net_sourceaddresscheck = {CF_CLIENT, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
86 cvar_t hostname = {CF_SERVER | CF_ARCHIVE, "hostname", "UNNAMED", "server message to show in server browser"};
87 cvar_t developer_networking = {CF_CLIENT | CF_SERVER, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
89 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)"};
90 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)"};
91 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_queriespersecond = {CF_CLIENT, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
93 static cvar_t net_slist_queriesperframe = {CF_CLIENT, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
94 static cvar_t net_slist_timeout = {CF_CLIENT, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
95 static cvar_t net_slist_pause = {CF_CLIENT, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
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_favorites = {CF_CLIENT | CF_ARCHIVE, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
98 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)"};
99 static cvar_t gameversion = {CF_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
100 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"};
101 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"};
102 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"};
103 static cvar_t rcon_restricted_commands = {CF_SERVER, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
104 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)"};
105 extern cvar_t rcon_secure;
106 extern cvar_t rcon_secure_challengetimeout;
108 double masterquerytime = -1000;
109 unsigned masterquerycount = 0;
110 unsigned masterreplycount = 0;
111 unsigned serverquerycount = 0;
112 unsigned serverreplycount = 0;
114 challenge_t challenges[MAX_CHALLENGES];
117 /// this is only false if there are still servers left to query
118 static qbool serverlist_querysleep = true;
119 static qbool serverlist_paused = false;
120 /// this is pushed a second or two ahead of realtime whenever a master server
121 /// reply is received, to avoid issuing queries while master replies are still
122 /// flooding in (which would make a mess of the ping times)
123 static double serverlist_querywaittime = 0;
126 static unsigned cl_numsockets;
127 static lhnetsocket_t *cl_sockets[16];
128 static unsigned sv_numsockets;
129 static lhnetsocket_t *sv_sockets[16];
131 netconn_t *netconn_list = NULL;
132 mempool_t *netconn_mempool = NULL;
133 void *netconn_mutex = NULL;
135 cvar_t cl_netport = {CF_CLIENT, "cl_port", "0", "forces client to use chosen port number if not 0"};
136 cvar_t sv_netport = {CF_SERVER, "port", "26000", "server port for players to connect to"};
137 cvar_t net_address = {CF_CLIENT | CF_SERVER, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
138 cvar_t net_address_ipv6 = {CF_CLIENT | CF_SERVER, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
140 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
141 unsigned cl_net_extresponse_count = 0;
142 unsigned cl_net_extresponse_last = 0;
144 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
145 unsigned sv_net_extresponse_count = 0;
146 unsigned sv_net_extresponse_last = 0;
149 // ServerList interface
150 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
151 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
153 serverlist_infofield_t serverlist_sortbyfield;
154 unsigned serverlist_sortflags;
156 unsigned serverlist_viewcount = 0;
157 uint16_t serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
159 unsigned serverlist_maxcachecount = 0;
160 unsigned serverlist_cachecount = 0;
161 serverlist_entry_t *serverlist_cache = NULL;
163 qbool serverlist_consoleoutput;
165 static unsigned nFavorites = 0;
166 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
167 static unsigned nFavorites_idfp = 0;
168 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
170 void NetConn_UpdateFavorites_c(cvar_t *var)
176 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
178 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
179 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
180 // (if v6 address contains port, it must start with '[')
182 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
187 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
193 /// helper function to insert a value into the viewset
194 /// spare entries will be removed
195 static void _ServerList_ViewList_Helper_InsertBefore(unsigned index, serverlist_entry_t *entry)
199 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
200 i = serverlist_viewcount++;
202 i = SERVERLIST_VIEWLISTSIZE - 1;
205 for( ; i > index ; i-- )
206 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
208 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
211 /// we suppose serverlist_viewcount to be valid, ie > 0
212 static inline void _ServerList_ViewList_Helper_Remove(unsigned index)
214 serverlist_viewcount--;
215 for( ; index < serverlist_viewcount ; index++ )
216 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
219 /// \returns true if A should be inserted before B
220 static qbool _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
222 int result = 0; // > 0 if for numbers A > B and for text if A < B
224 if( serverlist_sortflags & SLSF_CATEGORIES )
226 result = A->info.category - B->info.category;
231 if( serverlist_sortflags & SLSF_FAVORITES )
233 if(A->info.isfavorite != B->info.isfavorite)
234 return A->info.isfavorite;
237 switch( serverlist_sortbyfield ) {
239 result = A->info.ping - B->info.ping;
241 case SLIF_MAXPLAYERS:
242 result = A->info.maxplayers - B->info.maxplayers;
244 case SLIF_NUMPLAYERS:
245 result = A->info.numplayers - B->info.numplayers;
248 result = A->info.numbots - B->info.numbots;
251 result = A->info.numhumans - B->info.numhumans;
254 result = A->info.freeslots - B->info.freeslots;
257 result = A->info.protocol - B->info.protocol;
260 result = strcmp( B->info.cname, A->info.cname );
263 result = strcasecmp( B->info.game, A->info.game );
266 result = strcasecmp( B->info.map, A->info.map );
269 result = strcasecmp( B->info.mod, A->info.mod );
272 result = strcasecmp( B->info.name, A->info.name );
275 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
278 result = A->info.category - B->info.category;
280 case SLIF_ISFAVORITE:
281 result = !!B->info.isfavorite - !!A->info.isfavorite;
284 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
290 if( serverlist_sortflags & SLSF_DESCENDING )
296 // if the chosen sort key is identical, sort by index
297 // (makes this a stable sort, so that later replies from servers won't
298 // shuffle the servers around when they have the same ping)
302 static qbool _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
304 // This should actually be done with some intermediate and end-of-function return
316 case SLMO_GREATEREQUAL:
318 case SLMO_NOTCONTAIN:
319 case SLMO_STARTSWITH:
320 case SLMO_NOTSTARTSWITH:
323 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
328 static qbool _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
331 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
332 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
333 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
334 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
336 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
337 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
340 // Same here, also using an intermediate & final return would be more appropriate
344 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
345 case SLMO_NOTCONTAIN:
346 return !*bufferB || !strstr( bufferA, bufferB );
347 case SLMO_STARTSWITH:
348 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
349 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
350 case SLMO_NOTSTARTSWITH:
351 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
353 return strcmp( bufferA, bufferB ) < 0;
355 return strcmp( bufferA, bufferB ) <= 0;
357 return strcmp( bufferA, bufferB ) == 0;
359 return strcmp( bufferA, bufferB ) > 0;
361 return strcmp( bufferA, bufferB ) != 0;
362 case SLMO_GREATEREQUAL:
363 return strcmp( bufferA, bufferB ) >= 0;
365 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
370 static qbool _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
372 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
374 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
376 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
378 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
380 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
382 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
384 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
386 if( *mask->info.cname
387 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
390 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
393 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
396 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
399 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
401 if( *mask->info.qcstatus
402 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
404 if( *mask->info.players
405 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
407 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
409 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
414 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
416 unsigned start, end, mid, i;
419 // reject incompatible servers
421 entry->info.gameversion != gameversion.integer
424 gameversion_min.integer >= 0 // min/max range set by user/mod?
425 && gameversion_max.integer >= 0
426 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
427 && gameversion_max.integer >= entry->info.gameversion
432 // refresh the "favorite" status
433 entry->info.isfavorite = false;
434 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
436 char idfp[FP64_SIZE+1];
437 for(i = 0; i < nFavorites; ++i)
439 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
441 entry->info.isfavorite = true;
445 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
447 for(i = 0; i < nFavorites_idfp; ++i)
449 if(!strcmp(idfp, favorites_idfp[i]))
451 entry->info.isfavorite = true;
458 // refresh the "category"
459 entry->info.category = MR_GetServerListEntryCategory(entry);
461 // FIXME: change this to be more readable (...)
462 // now check whether it passes through the masks
463 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
464 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
467 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
468 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
470 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
473 if( !serverlist_viewcount ) {
474 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
477 // ok, insert it, we just need to find out where exactly:
480 // check whether to insert it as new first item
481 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
482 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
484 } // check whether to insert it as new last item
485 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
486 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
490 end = serverlist_viewcount - 1;
491 while( end > start + 1 )
493 mid = (start + end) / 2;
494 // test the item that lies in the middle between start and end
495 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
496 // the item has to be in the upper half
499 // the item has to be in the lower half
502 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
505 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
509 for( i = 0; i < serverlist_viewcount; i++ )
511 if (ServerList_GetViewEntry(i) == entry)
513 _ServerList_ViewList_Helper_Remove(i);
519 void ServerList_RebuildViewList(void)
523 serverlist_viewcount = 0;
524 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
525 serverlist_entry_t *entry = &serverlist_cache[i];
526 // also display entries that are currently being refreshed [11/8/2007 Black]
527 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
528 ServerList_ViewList_Insert( entry );
532 void ServerList_ResetMasks(void)
536 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
537 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
538 // numbots needs to be compared to -1 to always succeed
539 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
540 serverlist_andmasks[i].info.numbots = -1;
541 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
542 serverlist_ormasks[i].info.numbots = -1;
545 void ServerList_GetPlayerStatistics(unsigned *numplayerspointer, unsigned *maxplayerspointer)
548 unsigned numplayers = 0, maxplayers = 0;
550 for (i = 0;i < serverlist_cachecount;i++)
552 if (serverlist_cache[i].query == SQS_QUERIED)
554 numplayers += serverlist_cache[i].info.numhumans;
555 maxplayers += serverlist_cache[i].info.maxplayers;
558 *numplayerspointer = numplayers;
559 *maxplayerspointer = maxplayers;
563 static void _ServerList_Test(void)
566 if (serverlist_maxcachecount <= 1024)
568 serverlist_maxcachecount = 1024;
569 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
571 for( i = 0 ; i < 1024 ; i++ ) {
572 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
573 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
574 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
575 serverlist_cache[serverlist_cachecount].finished = true;
576 dpsnprintf( serverlist_cache[serverlist_cachecount].line1, sizeof(serverlist_cache[serverlist_cachecount].info.line1), "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
577 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
578 serverlist_cachecount++;
583 void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool consoleoutput)
587 masterquerytime = host.realtime;
588 masterquerycount = 0;
589 masterreplycount = 0;
592 serverquerycount = 0;
593 serverreplycount = 0;
594 serverlist_cachecount = 0;
595 serverlist_viewcount = 0;
596 serverlist_maxcachecount = 0;
597 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
601 // refresh all entries
602 for (i = 0; i < serverlist_cachecount; ++i)
604 serverlist_entry_t *entry = &serverlist_cache[i];
605 entry->query = SQS_REFRESHING;
606 entry->querycounter = 0;
609 serverlist_consoleoutput = consoleoutput;
611 //_ServerList_Test();
613 NetConn_QueryMasters(querydp, queryqw);
619 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
624 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
625 Thread_LockMutex(netconn_mutex);
626 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
627 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
628 Thread_UnlockMutex(netconn_mutex);
631 if (net_fakeloss_receive.integer)
632 for (i = 0;i < cl_numsockets;i++)
633 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_receive.integer)
635 if (developer_networking.integer)
637 char addressstring[128], addressstring2[128];
638 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
641 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
642 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
643 Com_HexDumpToConsole((unsigned char *)data, length);
646 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
651 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
656 if (net_fakeloss_send.integer)
657 for (i = 0;i < cl_numsockets;i++)
658 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_send.integer)
660 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
661 Thread_LockMutex(netconn_mutex);
662 ret = LHNET_Write(mysocket, data, length, peeraddress);
663 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
664 Thread_UnlockMutex(netconn_mutex);
665 if (developer_networking.integer)
667 char addressstring[128], addressstring2[128];
668 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
669 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
670 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)");
671 Com_HexDumpToConsole((unsigned char *)data, length);
676 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
678 // note this does not include the trailing NULL because we add that in the parser
679 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
682 qbool NetConn_CanSend(netconn_t *conn)
684 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
685 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = host.realtime;
686 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
687 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
688 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
689 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
690 if (host.realtime > conn->cleartime)
694 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
699 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
701 double bursttime = burstsize / (double)rate;
703 // delay later packets to obey rate limit
704 if (*cleartime < host.realtime - bursttime)
705 *cleartime = host.realtime - bursttime;
706 *cleartime = *cleartime + len / (double)rate;
708 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
709 if (net_test.integer)
711 if (*cleartime < host.realtime)
712 *cleartime = host.realtime;
716 static int NetConn_AddCryptoFlag(crypto_t *crypto)
718 // HACK: if an encrypted connection is used, randomly set some unused
719 // flags. When AES encryption is enabled, that will make resends differ
720 // from the original, so that e.g. substring filters in a router/IPS
721 // are unlikely to match a second time. See also "startkeylogger".
723 if (crypto->authenticated)
725 // Let's always set at least one of the bits.
726 int r = rand() % 7 + 1;
728 flag |= NETFLAG_CRYPTO0;
730 flag |= NETFLAG_CRYPTO1;
732 flag |= NETFLAG_CRYPTO2;
737 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qbool quakesignon_suppressreliables)
740 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
741 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
743 // if this packet was supposedly choked, but we find ourselves sending one
744 // anyway, make sure the size counting starts at zero
745 // (this mostly happens on level changes and disconnects and such)
746 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
747 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
749 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
751 if (protocol == PROTOCOL_QUAKEWORLD)
756 // note that it is ok to send empty messages to the qw server,
757 // otherwise it won't respond to us at all
759 sendreliable = false;
760 // if the remote side dropped the last reliable message, resend it
761 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
763 // if the reliable transmit buffer is empty, copy the current message out
764 if (!conn->sendMessageLength && conn->message.cursize)
766 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
767 conn->sendMessageLength = conn->message.cursize;
768 SZ_Clear(&conn->message); // clear the message buffer
769 conn->qw.reliable_sequence ^= 1;
772 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
773 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
774 // last received unreliable packet number, and last received reliable packet number (0 or 1)
775 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
777 conn->outgoing_unreliable_sequence++;
778 // client sends qport in every packet
779 if (conn == cls.netcon)
781 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
783 // also update cls.qw_outgoing_sequence
784 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
786 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
788 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
792 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
794 // add the reliable message if there is one
797 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
798 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
799 packetLen += conn->sendMessageLength;
800 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
803 // add the unreliable message if possible
804 if (packetLen + data->cursize <= 1400)
806 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
807 memcpy(sendbuffer + packetLen, data->data, data->cursize);
808 packetLen += data->cursize;
811 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
814 conn->unreliableMessagesSent++;
816 totallen += packetLen + 28;
820 unsigned int packetLen;
821 unsigned int dataLen;
826 // if a reliable message fragment has been lost, send it again
827 if (conn->sendMessageLength && (host.realtime - conn->lastSendTime) > 1.0)
829 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
831 dataLen = conn->sendMessageLength;
836 dataLen = MAX_PACKETFRAGMENT;
840 packetLen = NET_HEADERSIZE + dataLen;
842 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
843 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
844 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
846 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
848 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
849 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
851 conn->lastSendTime = host.realtime;
852 conn->packetsReSent++;
855 totallen += (int)sendmelen + 28;
858 // if we have a new reliable message to send, do so
859 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
861 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
863 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
864 conn->message.overflowed = true;
868 if (developer_networking.integer && conn == cls.netcon)
870 Con_Print("client sending reliable message to server:\n");
871 SZ_HexDumpToConsole(&conn->message);
874 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
875 conn->sendMessageLength = conn->message.cursize;
876 SZ_Clear(&conn->message);
878 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
880 dataLen = conn->sendMessageLength;
885 dataLen = MAX_PACKETFRAGMENT;
889 packetLen = NET_HEADERSIZE + dataLen;
891 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
892 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
893 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
895 conn->nq.sendSequence++;
897 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
899 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
901 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
903 conn->lastSendTime = host.realtime;
905 conn->reliableMessagesSent++;
907 totallen += (int)sendmelen + 28;
910 // if we have an unreliable message to send, do so
913 packetLen = NET_HEADERSIZE + data->cursize;
915 if (packetLen > (int)sizeof(sendbuffer))
917 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
921 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
922 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
923 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
925 conn->outgoing_unreliable_sequence++;
927 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
929 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
931 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
934 conn->unreliableMessagesSent++;
936 totallen += (int)sendmelen + 28;
940 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
945 qbool NetConn_HaveClientPorts(void)
947 return !!cl_numsockets;
950 qbool NetConn_HaveServerPorts(void)
952 return !!sv_numsockets;
955 void NetConn_CloseClientPorts(void)
957 for (;cl_numsockets > 0;cl_numsockets--)
958 if (cl_sockets[cl_numsockets - 1])
959 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
962 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
964 lhnetaddress_t address;
967 char addressstring2[1024];
968 if (addressstring && addressstring[0])
969 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
971 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
974 if ((s = LHNET_OpenSocket_Connectionless(&address)))
976 cl_sockets[cl_numsockets++] = s;
977 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
978 if (addresstype != LHNETADDRESSTYPE_LOOP)
979 Con_Printf("Client opened a socket on address %s\n", addressstring2);
983 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
984 Con_Printf(CON_ERROR "Client failed to open a socket on address %s\n", addressstring2);
988 Con_Printf(CON_ERROR "Client unable to parse address %s\n", addressstring);
991 void NetConn_OpenClientPorts(void)
994 NetConn_CloseClientPorts();
996 SV_LockThreadMutex(); // FIXME recursive?
997 Crypto_LoadKeys(); // client sockets
998 SV_UnlockThreadMutex();
1000 port = bound(0, cl_netport.integer, 65535);
1001 if (cl_netport.integer != port)
1002 Cvar_SetValueQuick(&cl_netport, port);
1004 Con_Printf("Client using an automatically assigned port\n");
1006 Con_Printf("Client using port %i\n", port);
1007 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
1008 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
1009 #ifndef NOSUPPORTIPV6
1010 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1014 void NetConn_CloseServerPorts(void)
1016 for (;sv_numsockets > 0;sv_numsockets--)
1017 if (sv_sockets[sv_numsockets - 1])
1018 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1021 static qbool NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1023 lhnetaddress_t address;
1026 char addressstring2[1024];
1029 for (port = defaultport; port <= defaultport + range; port++)
1031 if (addressstring && addressstring[0])
1032 success = LHNETADDRESS_FromString(&address, addressstring, port);
1034 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1037 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1039 sv_sockets[sv_numsockets++] = s;
1040 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1041 if (addresstype != LHNETADDRESSTYPE_LOOP)
1042 Con_Printf("Server listening on address %s\n", addressstring2);
1047 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1048 Con_Printf(CON_ERROR "Server failed to open socket on address %s\n", addressstring2);
1053 Con_Printf(CON_ERROR "Server unable to parse address %s\n", addressstring);
1054 // if it cant parse one address, it wont be able to parse another for sure
1061 void NetConn_OpenServerPorts(int opennetports)
1064 NetConn_CloseServerPorts();
1066 SV_LockThreadMutex(); // FIXME recursive?
1067 Crypto_LoadKeys(); // server sockets
1068 SV_UnlockThreadMutex();
1070 NetConn_UpdateSockets();
1071 port = bound(0, sv_netport.integer, 65535);
1074 if (sv_netport.integer != port)
1075 Cvar_SetValueQuick(&sv_netport, port);
1076 if (cls.state != ca_dedicated)
1077 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1080 #ifndef NOSUPPORTIPV6
1081 qbool ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1082 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1084 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1087 if (sv_numsockets == 0)
1088 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1091 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1094 lhnetaddresstype_t a = LHNETADDRESS_GetAddressType(address);
1096 for (i = 0;i < cl_numsockets;i++)
1097 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1098 return cl_sockets[i];
1102 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1105 lhnetaddresstype_t a = LHNETADDRESS_GetAddressType(address);
1107 for (i = 0;i < sv_numsockets;i++)
1108 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1109 return sv_sockets[i];
1113 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1116 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1117 conn->mysocket = mysocket;
1118 conn->peeraddress = *peeraddress;
1119 conn->lastMessageTime = host.realtime;
1120 conn->message.data = conn->messagedata;
1121 conn->message.maxsize = sizeof(conn->messagedata);
1122 conn->message.cursize = 0;
1123 // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
1124 // reduce effectiveness of connection request floods
1125 conn->timeout = host.realtime + net_connecttimeout.value;
1126 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1127 conn->next = netconn_list;
1128 netconn_list = conn;
1132 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1133 void NetConn_Close(netconn_t *conn)
1136 // remove connection from list
1138 // allow the client to reconnect immediately
1139 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1141 if (conn == netconn_list)
1142 netconn_list = conn->next;
1145 for (c = netconn_list;c;c = c->next)
1147 if (c->next == conn)
1149 c->next = conn->next;
1153 // not found in list, we'll avoid crashing here...
1161 static int clientport = -1;
1162 static int clientport2 = -1;
1164 // Call on disconnect, during startup, or if cl_port/cl_netport is changed
1165 static void NetConn_CL_UpdateSockets_Callback(cvar_t *var)
1167 if(cls.state != ca_dedicated)
1169 if (clientport2 != var->integer)
1171 clientport2 = var->integer;
1172 if (cls.state == ca_connected)
1173 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1176 if (cls.state == ca_disconnected && clientport != clientport2)
1178 clientport = clientport2;
1179 NetConn_CloseClientPorts();
1181 if (cl_numsockets == 0)
1182 NetConn_OpenClientPorts();
1186 static int hostport = -1;
1188 // Call when port/sv_netport is changed
1189 static void NetConn_sv_netport_Callback(cvar_t *var)
1191 if (hostport != var->integer)
1193 hostport = var->integer;
1195 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1199 void NetConn_UpdateSockets(void)
1203 // TODO add logic to automatically close sockets if needed
1204 LHNET_DefaultDSCP(net_tos_dscp.integer);
1206 for (j = 0;j < MAX_RCONS;j++)
1208 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1209 if(cls.rcon_commands[i][0])
1211 if(host.realtime > cls.rcon_timeout[i])
1214 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1215 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1216 cls.rcon_commands[i][0] = 0;
1224 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1226 int originallength = (int)length;
1227 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1228 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1229 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1233 if (protocol == PROTOCOL_QUAKEWORLD)
1235 unsigned int sequence, sequence_ack;
1236 qbool reliable_ack, reliable_message;
1240 sequence = LittleLong(*((int *)(data + 0)));
1241 sequence_ack = LittleLong(*((int *)(data + 4)));
1245 if (conn != cls.netcon)
1250 // 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?)
1251 //qport = LittleShort(*((int *)(data + 8)));
1256 conn->packetsReceived++;
1257 reliable_message = (sequence >> 31) != 0;
1258 reliable_ack = (sequence_ack >> 31) != 0;
1259 sequence &= ~(1<<31);
1260 sequence_ack &= ~(1<<31);
1261 if (sequence <= conn->qw.incoming_sequence)
1263 //Con_DPrint("Got a stale datagram\n");
1266 count = sequence - (conn->qw.incoming_sequence + 1);
1269 conn->droppedDatagrams += count;
1270 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1271 // If too may packets have been dropped, only write the
1272 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1273 // Because there's no point in writing more than
1274 // these as the netgraph is going to be full anyway.
1275 if (count > NETGRAPH_PACKETS)
1276 count = NETGRAPH_PACKETS;
1279 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1280 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1281 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1282 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1283 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1284 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1287 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1288 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1289 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1290 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1291 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1292 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1293 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1295 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1296 if (net_test.integer)
1298 if (conn->cleartime < host.realtime)
1299 conn->cleartime = host.realtime;
1302 if (reliable_ack == conn->qw.reliable_sequence)
1304 // received, now we will be able to send another reliable message
1305 conn->sendMessageLength = 0;
1306 conn->reliableMessagesReceived++;
1308 conn->qw.incoming_sequence = sequence;
1309 if (conn == cls.netcon)
1310 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1311 conn->qw.incoming_acknowledged = sequence_ack;
1312 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1313 if (reliable_message)
1314 conn->qw.incoming_reliable_sequence ^= 1;
1315 conn->lastMessageTime = host.realtime;
1316 conn->timeout = host.realtime + newtimeout;
1317 conn->unreliableMessagesReceived++;
1318 if (conn == cls.netcon)
1320 SZ_Clear(&cl_message);
1321 SZ_Write(&cl_message, data, (int)length);
1322 MSG_BeginReading(&cl_message);
1326 SZ_Clear(&sv_message);
1327 SZ_Write(&sv_message, data, (int)length);
1328 MSG_BeginReading(&sv_message);
1336 unsigned int sequence;
1341 originallength = (int)length;
1342 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1348 qlength = (unsigned int)BuffBigLong(data);
1349 flags = qlength & ~NETFLAG_LENGTH_MASK;
1350 qlength &= NETFLAG_LENGTH_MASK;
1351 // control packets were already handled
1352 if (!(flags & NETFLAG_CTL) && qlength == length)
1354 sequence = BuffBigLong(data + 4);
1355 conn->packetsReceived++;
1358 if (flags & NETFLAG_UNRELIABLE)
1360 if (sequence >= conn->nq.unreliableReceiveSequence)
1362 if (sequence > conn->nq.unreliableReceiveSequence)
1364 count = sequence - conn->nq.unreliableReceiveSequence;
1365 conn->droppedDatagrams += count;
1366 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1367 // If too may packets have been dropped, only write the
1368 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1369 // Because there's no point in writing more than
1370 // these as the netgraph is going to be full anyway.
1371 if (count > NETGRAPH_PACKETS)
1372 count = NETGRAPH_PACKETS;
1375 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1376 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1377 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1378 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1379 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1380 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1383 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1384 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1385 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1386 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1387 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1388 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1389 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1391 conn->nq.unreliableReceiveSequence = sequence + 1;
1392 conn->lastMessageTime = host.realtime;
1393 conn->timeout = host.realtime + newtimeout;
1394 conn->unreliableMessagesReceived++;
1397 if (conn == cls.netcon)
1399 SZ_Clear(&cl_message);
1400 SZ_Write(&cl_message, data, (int)length);
1401 MSG_BeginReading(&cl_message);
1405 SZ_Clear(&sv_message);
1406 SZ_Write(&sv_message, data, (int)length);
1407 MSG_BeginReading(&sv_message);
1413 // Con_DPrint("Got a stale datagram\n");
1416 else if (flags & NETFLAG_ACK)
1418 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1419 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1421 if (sequence == (conn->nq.sendSequence - 1))
1423 if (sequence == conn->nq.ackSequence)
1425 conn->nq.ackSequence++;
1426 if (conn->nq.ackSequence != conn->nq.sendSequence)
1427 Con_DPrint("ack sequencing error\n");
1428 conn->lastMessageTime = host.realtime;
1429 conn->timeout = host.realtime + newtimeout;
1430 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1432 unsigned int packetLen;
1433 unsigned int dataLen;
1436 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1437 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1439 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1441 dataLen = conn->sendMessageLength;
1446 dataLen = MAX_PACKETFRAGMENT;
1450 packetLen = NET_HEADERSIZE + dataLen;
1452 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1453 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1454 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1456 conn->nq.sendSequence++;
1458 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1459 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1461 conn->lastSendTime = host.realtime;
1462 conn->packetsSent++;
1466 conn->sendMessageLength = 0;
1469 // Con_DPrint("Duplicate ACK received\n");
1472 // Con_DPrint("Stale ACK received\n");
1475 else if (flags & NETFLAG_DATA)
1477 unsigned char temppacket[8];
1478 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1479 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1481 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1483 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1484 StoreBigLong(temppacket + 4, sequence);
1485 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1487 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1488 if (sequence == conn->nq.receiveSequence)
1490 conn->lastMessageTime = host.realtime;
1491 conn->timeout = host.realtime + newtimeout;
1492 conn->nq.receiveSequence++;
1493 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1494 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1495 conn->receiveMessageLength += (int)length;
1497 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1498 "Dropping the message!\n", sequence );
1499 conn->receiveMessageLength = 0;
1502 if (flags & NETFLAG_EOM)
1504 conn->reliableMessagesReceived++;
1505 length = conn->receiveMessageLength;
1506 conn->receiveMessageLength = 0;
1509 if (conn == cls.netcon)
1511 SZ_Clear(&cl_message);
1512 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1513 MSG_BeginReading(&cl_message);
1517 SZ_Clear(&sv_message);
1518 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1519 MSG_BeginReading(&sv_message);
1526 conn->receivedDuplicateCount++;
1534 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1537 cls.connect_trying = false;
1539 M_Update_Return_Reason("");
1541 // Disconnect from the current server or stop demo playback
1542 if(cls.state == ca_connected || cls.demoplayback)
1544 // allocate a net connection to keep track of things
1545 cls.netcon = NetConn_Open(mysocket, peeraddress);
1546 crypto = &cls.netcon->crypto;
1547 if(cls.crypto.authenticated)
1549 Crypto_FinishInstance(crypto, &cls.crypto);
1550 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1551 crypto->use_aes ? "Encrypted" : "Authenticated",
1552 cls.netcon->address,
1553 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1554 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1555 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1556 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1557 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1558 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1561 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1562 key_dest = key_game;
1566 cls.demonum = -1; // not in the demo loop now
1567 cls.state = ca_connected;
1568 cls.signon = 0; // need all the signon messages before playing
1569 cls.protocol = initialprotocol;
1570 // reset move sequence numbering on this new connection
1571 cls.servermovesequence = 0;
1572 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1573 CL_ForwardToServer("new");
1574 if (cls.protocol == PROTOCOL_QUAKE)
1576 // write a keepalive (clc_nop) as it seems to greatly improve the
1577 // chances of connecting to a netquake server
1579 unsigned char buf[4];
1580 memset(&msg, 0, sizeof(msg));
1582 msg.maxsize = sizeof(buf);
1583 MSG_WriteChar(&msg, clc_nop);
1584 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1588 int NetConn_IsLocalGame(void)
1590 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1596 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1600 serverlist_entry_t *entry = NULL;
1602 // search the cache for this server and update it
1603 for (n = 0; n < serverlist_cachecount; ++n)
1605 entry = &serverlist_cache[n];
1606 if (!strcmp(addressstring, entry->info.cname))
1610 if (n == serverlist_cachecount)
1612 // LAN search doesnt require an answer from the master server so we wont
1613 // know the ping nor will it be initialized already...
1616 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1619 if (serverlist_maxcachecount <= serverlist_cachecount)
1621 serverlist_maxcachecount += 64;
1622 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1624 entry = &serverlist_cache[n];
1626 memset(entry, 0, sizeof(*entry));
1627 // store the data the engine cares about (address and ping)
1628 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1629 entry->info.ping = 100000;
1630 entry->querytime = host.realtime;
1631 // if not in the slist menu we should print the server to console
1632 if (serverlist_consoleoutput)
1633 Con_Printf("querying %s\n", addressstring);
1634 ++serverlist_cachecount;
1636 // if this is the first reply from this server, count it as having replied
1637 pingtime = (int)((host.realtime - entry->querytime) * 1000.0 + 0.5);
1638 pingtime = bound(0, pingtime, 9999);
1639 if (entry->query == SQS_REFRESHING) {
1640 entry->info.ping = pingtime;
1641 entry->query = SQS_QUERIED;
1643 // convert to unsigned to catch the -1
1644 // I still dont like this but its better than the old 10000 magic ping number - as in easier to type and read :( [11/8/2007 Black]
1645 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1649 // other server info is updated by the caller
1653 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1655 serverlist_entry_t *entry = &serverlist_cache[n];
1656 serverlist_info_t *info = &entry->info;
1657 // update description strings for engine menu and console output
1658 dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
1659 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1661 info->gameversion != gameversion.integer
1664 gameversion_min.integer >= 0 // min/max range set by user/mod?
1665 && gameversion_max.integer >= 0
1666 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1667 && gameversion_max.integer >= info->gameversion
1670 info->mod, info->map);
1671 if (entry->query == SQS_QUERIED)
1673 if(!serverlist_paused)
1674 ServerList_ViewList_Remove(entry);
1676 // if not in the slist menu we should print the server to console (if wanted)
1677 else if( serverlist_consoleoutput )
1678 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1679 // and finally, update the view set
1680 if(!serverlist_paused)
1681 ServerList_ViewList_Insert( entry );
1682 // update the entry's state
1683 serverlist_cache[n].query = SQS_QUERIED;
1686 // returns true, if it's sensible to continue the processing
1687 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery(int protocol, const char *ipstring, qbool isfavorite)
1690 serverlist_entry_t *entry;
1692 // ignore the rest of the message if the serverlist is full
1693 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1695 // also ignore it if we have already queried it (other master server response)
1696 for (n = 0; n < serverlist_cachecount; ++n)
1697 if (!strcmp(ipstring, serverlist_cache[n].info.cname))
1700 if (n < serverlist_cachecount)
1702 // the entry has already been queried once
1706 if (serverlist_maxcachecount <= n)
1708 serverlist_maxcachecount += 64;
1709 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1712 entry = &serverlist_cache[n];
1714 memset(entry, 0, sizeof(*entry));
1715 entry->protocol = protocol;
1716 // store the data the engine cares about (address and ping)
1717 strlcpy(entry->info.cname, ipstring, sizeof(entry->info.cname));
1719 entry->info.isfavorite = isfavorite;
1721 // no, then reset the ping right away
1722 entry->info.ping = -1;
1723 // we also want to increase the serverlist_cachecount then
1724 serverlist_cachecount++;
1727 entry->query = SQS_QUERYING;
1732 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qbool isextended)
1735 if (serverlist_consoleoutput)
1736 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1739 char ipstring [128];
1742 if (data[0] == '\\')
1744 unsigned short port = data[5] * 256 + data[6];
1746 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1747 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1749 // move on to next address in packet
1754 else if (data[0] == '/' && isextended && length >= 19)
1756 unsigned short port = data[17] * 256 + data[18];
1764 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1766 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1769 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1770 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1771 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1777 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1778 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1779 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1784 // move on to next address in packet
1790 Con_Print("Error while parsing the server list\n");
1794 if (serverlist_consoleoutput && developer_networking.integer)
1795 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1797 if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, ipstring, false))
1801 // begin or resume serverlist queries
1802 serverlist_querysleep = false;
1803 serverlist_querywaittime = host.realtime + 3;
1807 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1811 char *string, addressstring2[128];
1812 char stringbuf[16384];
1813 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1816 char infostringvalue[MAX_INPUTLINE];
1821 // quakeworld ingame packet
1822 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1824 // convert the address to a string incase we need it
1825 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1827 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1829 // received a command string - strip off the packaging and put it
1830 // into our string buffer with NULL termination
1833 length = min(length, (int)sizeof(stringbuf) - 1);
1834 memcpy(stringbuf, data, length);
1835 stringbuf[length] = 0;
1838 if (developer_networking.integer)
1840 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1841 Com_HexDumpToConsole(data, length);
1844 sendlength = sizeof(senddata) - 4;
1845 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1847 case CRYPTO_NOMATCH:
1853 memcpy(senddata, "\377\377\377\377", 4);
1854 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1857 case CRYPTO_DISCARD:
1860 memcpy(senddata, "\377\377\377\377", 4);
1861 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1865 case CRYPTO_REPLACE:
1866 string = senddata+4;
1867 length = (int)sendlength;
1871 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1874 for (j = 0;j < MAX_RCONS;j++)
1876 // note: this value from i is used outside the loop too...
1877 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1878 if(cls.rcon_commands[i][0])
1879 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1888 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1889 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1891 e = strchr(rcon_password.string, ' ');
1892 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1894 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1898 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1899 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1900 cls.rcon_commands[i][0] = 0;
1903 for (k = 0;k < MAX_RCONS;k++)
1904 if(cls.rcon_commands[k][0])
1905 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1910 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1911 // extend the timeout on other requests as we asked for a challenge
1912 for (l = 0;l < MAX_RCONS;l++)
1913 if(cls.rcon_commands[l][0])
1914 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1915 cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
1918 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1922 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1924 // darkplaces or quake3
1925 char protocolnames[1400];
1926 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1927 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1928 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1931 Protocol_Names(protocolnames, sizeof(protocolnames));
1933 M_Update_Return_Reason("Got challenge response");
1935 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1936 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1937 // TODO: add userinfo stuff here instead of using NQ commands?
1938 memcpy(senddata, "\377\377\377\377", 4);
1939 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1940 NetConn_WriteString(mysocket, senddata, peeraddress);
1943 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1945 // darkplaces or quake3
1946 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1947 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1951 M_Update_Return_Reason("Accepted");
1953 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1956 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1958 char rejectreason[128];
1959 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1960 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1963 cls.connect_trying = false;
1965 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1966 memcpy(rejectreason, string, length);
1967 rejectreason[length] = 0;
1969 M_Update_Return_Reason(rejectreason);
1974 if(key_dest != key_game)
1976 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1978 serverlist_info_t *info;
1983 // search the cache for this server and update it
1984 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1988 info = &serverlist_cache[n].info;
1993 info->qcstatus[0] = 0;
1994 info->players[0] = 0;
1995 info->protocol = -1;
1996 info->numplayers = 0;
1998 info->maxplayers = 0;
1999 info->gameversion = 0;
2001 p = strchr(string, '\n');
2004 *p = 0; // cut off the string there
2008 Con_Printf("statusResponse without players block?\n");
2010 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2011 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2012 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2013 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2014 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2015 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2016 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2017 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2018 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2019 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2020 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2021 info->numhumans = info->numplayers - max(0, info->numbots);
2022 info->freeslots = info->maxplayers - info->numplayers;
2024 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2028 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2030 serverlist_info_t *info;
2034 // search the cache for this server and update it
2035 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2039 info = &serverlist_cache[n].info;
2044 info->qcstatus[0] = 0;
2045 info->players[0] = 0;
2046 info->protocol = -1;
2047 info->numplayers = 0;
2049 info->maxplayers = 0;
2050 info->gameversion = 0;
2052 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2053 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2054 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2055 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2056 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2057 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2058 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2059 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2060 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2061 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2062 info->numhumans = info->numplayers - max(0, info->numbots);
2063 info->freeslots = info->maxplayers - info->numplayers;
2065 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2069 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2071 // Extract the IP addresses
2074 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2077 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2079 // Extract the IP addresses
2082 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2085 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2087 // Extract the IP addresses
2091 if (serverlist_consoleoutput)
2092 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2093 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2095 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2096 if (serverlist_consoleoutput && developer_networking.integer)
2097 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2099 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2103 // move on to next address in packet
2107 // begin or resume serverlist queries
2108 serverlist_querysleep = false;
2109 serverlist_querywaittime = host.realtime + 3;
2114 if (!strncmp(string, "extResponse ", 12))
2116 ++cl_net_extresponse_count;
2117 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2118 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2119 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2120 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2123 if (!strncmp(string, "ping", 4))
2125 if (developer_extra.integer)
2126 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2127 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2130 if (!strncmp(string, "ack", 3))
2132 // QuakeWorld compatibility
2133 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2135 // challenge message
2136 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2137 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2140 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2142 M_Update_Return_Reason("Got QuakeWorld challenge response");
2144 cls.qw_qport = qport.integer;
2145 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2146 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2147 memcpy(senddata, "\377\377\377\377", 4);
2148 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);
2149 NetConn_WriteString(mysocket, senddata, peeraddress);
2152 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2155 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2156 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2160 M_Update_Return_Reason("QuakeWorld Accepted");
2162 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2165 if (length > 2 && !memcmp(string, "n\\", 2))
2168 serverlist_info_t *info;
2172 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2173 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2176 // search the cache for this server and update it
2177 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2181 info = &serverlist_cache[n].info;
2182 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2183 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2184 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2185 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2187 info->numplayers = 0; // updated below
2188 info->numhumans = 0; // updated below
2189 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2190 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2192 // count active players on server
2193 // (we could gather more info, but we're just after the number)
2194 s = strchr(string, '\n');
2198 while (s < string + length)
2200 for (;s < string + length && *s != '\n';s++)
2202 if (s >= string + length)
2210 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2214 if (string[0] == 'n')
2216 // qw print command, used by rcon replies too
2217 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2218 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2221 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2223 // we may not have liked the packet, but it was a command packet, so
2224 // we're done processing this packet now
2227 // quakeworld ingame packet
2228 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2231 CL_ParseServerMessage();
2234 // netquake control packets, supported for compatibility only
2235 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2239 serverlist_info_t *info;
2244 SZ_Clear(&cl_message);
2245 SZ_Write(&cl_message, data, length);
2246 MSG_BeginReading(&cl_message);
2247 c = MSG_ReadByte(&cl_message);
2251 if (developer_extra.integer)
2252 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2253 if (cls.connect_trying)
2255 lhnetaddress_t clientportaddress;
2256 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2257 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2260 clientportaddress = *peeraddress;
2261 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2262 // extra ProQuake stuff
2264 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2266 cls.proquake_servermod = 0;
2268 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2270 cls.proquake_serverversion = 0;
2272 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2274 cls.proquake_serverflags = 0;
2275 if (cls.proquake_servermod == 1)
2276 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2277 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2278 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2280 M_Update_Return_Reason("Accepted");
2282 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2286 if (developer_extra.integer) {
2287 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2290 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2292 cls.connect_trying = false;
2294 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2297 case CCREP_SERVER_INFO:
2298 if (developer_extra.integer)
2299 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2301 // LadyHavoc: because the quake server may report weird addresses
2302 // we just ignore it and keep the real address
2303 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2304 // search the cache for this server and update it
2305 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2309 info = &serverlist_cache[n].info;
2310 strlcpy(info->game, "Quake", sizeof(info->game));
2311 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2312 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2313 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2314 info->numplayers = MSG_ReadByte(&cl_message);
2315 info->maxplayers = MSG_ReadByte(&cl_message);
2316 info->protocol = MSG_ReadByte(&cl_message);
2318 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2321 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2322 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2323 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2326 if (developer_extra.integer)
2327 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2329 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2331 case CCREP_PLAYER_INFO:
2332 // we got a CCREP_PLAYER_INFO??
2333 //if (developer_extra.integer)
2334 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2336 case CCREP_RULE_INFO:
2337 // we got a CCREP_RULE_INFO??
2338 //if (developer_extra.integer)
2339 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2344 SZ_Clear(&cl_message);
2345 // we may not have liked the packet, but it was a valid control
2346 // packet, so we're done processing this packet now
2350 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2351 CL_ParseServerMessage();
2356 void NetConn_QueryQueueFrame(void)
2359 unsigned queries, maxqueries;
2361 static double querycounter = 0;
2363 if (!net_slist_pause.integer && serverlist_paused)
2364 ServerList_RebuildViewList();
2365 serverlist_paused = net_slist_pause.integer != 0;
2367 if (serverlist_querysleep)
2370 // apply a cool down time after master server replies,
2371 // to avoid messing up the ping times on the servers
2372 if (serverlist_querywaittime > host.realtime)
2375 // each time querycounter reaches 1.0 issue a query
2376 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2377 maxqueries = bound(0, (int)querycounter, net_slist_queriesperframe.integer);
2378 querycounter -= maxqueries;
2380 if (maxqueries == 0)
2383 // scan serverlist and issue queries as needed
2384 serverlist_querysleep = true;
2386 timeouttime = host.realtime - net_slist_timeout.value;
2387 for (index = 0, queries = 0; index < serverlist_cachecount && queries < maxqueries; ++index)
2389 serverlist_entry_t *entry = &serverlist_cache[index];
2390 if (entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING)
2393 serverlist_querysleep = false;
2394 if (entry->querycounter != 0 && entry->querytime > timeouttime)
2397 if (entry->querycounter != (unsigned)net_slist_maxtries.integer)
2399 lhnetaddress_t address;
2402 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2403 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2405 for (socket = 0; socket < cl_numsockets; ++socket)
2406 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2410 for (socket = 0; socket < cl_numsockets; ++socket)
2411 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2414 // update the entry fields
2415 entry->querytime = host.realtime;
2416 entry->querycounter++;
2418 // if not in the slist menu we should print the server to console
2419 if (serverlist_consoleoutput)
2420 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2426 // have we tried to refresh this server?
2427 if (entry->query == SQS_REFRESHING)
2429 // yes, so update the reply count (since its not responding anymore)
2431 if (!serverlist_paused)
2432 ServerList_ViewList_Remove(entry);
2434 entry->query = SQS_TIMEDOUT;
2440 void NetConn_ClientFrame(void)
2444 lhnetaddress_t peeraddress;
2445 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2447 NetConn_UpdateSockets();
2448 if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2451 if (cls.connect_remainingtries == 0)
2452 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2454 cls.connect_nextsendtime = host.realtime + 1;
2455 cls.connect_remainingtries--;
2456 if (cls.connect_remainingtries <= -10)
2458 cls.connect_trying = false;
2460 M_Update_Return_Reason("Connect: Failed");
2464 // try challenge first (newer DP server or QW)
2465 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2466 // then try netquake as a fallback (old server, or netquake)
2467 SZ_Clear(&cl_message);
2468 // save space for the header, filled in later
2469 MSG_WriteLong(&cl_message, 0);
2470 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2471 MSG_WriteString(&cl_message, "QUAKE");
2472 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2473 // extended proquake stuff
2474 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2475 // this version matches ProQuake 3.40, the first version to support
2476 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2477 // higher clients, so we pretend we are that version...
2478 MSG_WriteByte(&cl_message, 34); // version * 10
2479 MSG_WriteByte(&cl_message, 0); // flags
2480 MSG_WriteLong(&cl_message, 0); // password
2481 // write the packetsize now...
2482 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2483 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2484 SZ_Clear(&cl_message);
2486 for (i = 0;i < cl_numsockets;i++)
2488 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2490 // R_TimeReport("clientreadnetwork");
2491 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2492 // R_TimeReport("clientparsepacket");
2496 NetConn_QueryQueueFrame();
2498 if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2499 CL_DisconnectEx(true, "Connection timed out");
2502 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2506 for (i = 0;i < bufferlength - 1;i++)
2510 c = rand () % (127 - 33) + 33;
2511 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2517 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2518 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2520 prvm_prog_t *prog = SVVM_prog;
2522 unsigned int nb_clients = 0, nb_bots = 0, i;
2525 const char *crypto_idstring;
2526 const char *worldstatusstr;
2528 // How many clients are there?
2529 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2531 if (svs.clients[i].active)
2534 if (!svs.clients[i].netconnection)
2540 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2541 if(worldstatusstr && *worldstatusstr)
2546 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2547 if(*q != '\\' && *q != '\n')
2552 /// \TODO: we should add more information for the full status string
2553 crypto_idstring = Crypto_GetInfoResponseDataString();
2554 length = dpsnprintf(out_msg, out_size,
2555 "\377\377\377\377%s\x0A"
2556 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2557 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2562 fullstatus ? "statusResponse" : "infoResponse",
2563 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2564 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2565 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2566 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2567 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2568 fullstatus ? "\n" : "");
2570 // Make sure it fits in the buffer
2580 savelength = length;
2582 ptr = out_msg + length;
2583 left = (int)out_size - length;
2585 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2587 client_t *client = &svs.clients[i];
2590 int nameind, cleanind, pingvalue;
2592 char cleanname [sizeof(client->name)];
2593 const char *statusstr;
2596 // Remove all characters '"' and '\' in the player name
2601 curchar = client->name[nameind++];
2602 if (curchar != '"' && curchar != '\\')
2604 cleanname[cleanind++] = curchar;
2605 if (cleanind == sizeof(cleanname) - 1)
2608 } while (curchar != '\0');
2609 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2611 pingvalue = (int)(client->ping * 1000.0f);
2612 if(client->netconnection)
2613 pingvalue = bound(1, pingvalue, 9999);
2618 ed = PRVM_EDICT_NUM(i + 1);
2619 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2620 if(statusstr && *statusstr)
2625 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2626 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2631 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2633 if(client->frags == -666) // spectator
2634 strlcpy(teambuf, " 0", sizeof(teambuf));
2635 else if(client->colors == 0x44) // red team
2636 strlcpy(teambuf, " 1", sizeof(teambuf));
2637 else if(client->colors == 0xDD) // blue team
2638 strlcpy(teambuf, " 2", sizeof(teambuf));
2639 else if(client->colors == 0xCC) // yellow team
2640 strlcpy(teambuf, " 3", sizeof(teambuf));
2641 else if(client->colors == 0x99) // pink team
2642 strlcpy(teambuf, " 4", sizeof(teambuf));
2644 strlcpy(teambuf, " 0", sizeof(teambuf));
2649 // note: team number is inserted according to SoF2 protocol
2651 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2657 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2666 // turn it into an infoResponse!
2667 out_msg[savelength] = 0;
2668 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2669 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2684 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2686 size_t floodslotnum, bestfloodslotnum;
2687 double bestfloodtime;
2688 lhnetaddress_t noportpeeraddress;
2689 // see if this is a connect flood
2690 noportpeeraddress = *peeraddress;
2691 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2692 bestfloodslotnum = 0;
2693 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2694 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2696 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2698 bestfloodtime = floodlist[floodslotnum].lasttime;
2699 bestfloodslotnum = floodslotnum;
2701 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2703 // this address matches an ongoing flood address
2704 if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2708 // renew the ban on this address so it does not expire
2709 // until the flood has subsided
2710 floodlist[floodslotnum].lasttime = host.realtime;
2712 //Con_Printf("Flood detected!\n");
2715 // the flood appears to have subsided, so allow this
2716 bestfloodslotnum = floodslotnum; // reuse the same slot
2720 // begin a new timeout on this address
2721 floodlist[bestfloodslotnum].address = noportpeeraddress;
2722 floodlist[bestfloodslotnum].lasttime = host.realtime;
2723 //Con_Printf("Flood detection initiated!\n");
2727 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2729 size_t floodslotnum;
2730 lhnetaddress_t noportpeeraddress;
2731 // see if this is a connect flood
2732 noportpeeraddress = *peeraddress;
2733 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2734 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2736 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2738 // this address matches an ongoing flood address
2740 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2741 floodlist[floodslotnum].lasttime = 0;
2742 //Con_Printf("Flood cleared!\n");
2747 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2749 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2755 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2759 t1 = (long) time(NULL);
2760 t2 = strtol(s, NULL, 0);
2761 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2764 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2767 return !memcmp(mdfourbuf, hash, 16);
2770 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2776 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2780 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2783 // validate the challenge
2784 for (i = 0;i < MAX_CHALLENGES;i++)
2785 if(challenges[i].time > 0)
2786 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2788 // if the challenge is not recognized, drop the packet
2789 if (i == MAX_CHALLENGES)
2792 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2795 if(memcmp(mdfourbuf, hash, 16))
2798 // unmark challenge to prevent replay attacks
2799 challenges[i].time = 0;
2804 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2807 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2811 return !strcmp(password, hash);
2814 /// returns a string describing the user level, or NULL for auth failure
2815 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)
2817 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2818 static char buf[MAX_INPUTLINE];
2820 qbool restricted = false;
2821 qbool have_usernames = false;
2822 static char vabuf[1024];
2824 userpass_start = rcon_password.string;
2825 while((userpass_end = strchr(userpass_start, ' ')))
2827 have_usernames = true;
2828 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2829 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2830 if(comparator(peeraddress, buf, password, cs, cslen))
2832 userpass_start = userpass_end + 1;
2834 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2836 userpass_end = userpass_start + strlen(userpass_start);
2837 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2842 have_usernames = false;
2843 userpass_start = rcon_restricted_password.string;
2844 while((userpass_end = strchr(userpass_start, ' ')))
2846 have_usernames = true;
2847 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2848 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2849 if(comparator(peeraddress, buf, password, cs, cslen))
2851 userpass_start = userpass_end + 1;
2853 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2855 userpass_end = userpass_start + strlen(userpass_start);
2856 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2860 return NULL; // DENIED
2863 for(text = s; text != endpos; ++text)
2864 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2865 return NULL; // block possible exploits against the parser/alias expansion
2869 size_t l = strlen(s);
2872 hasquotes = (strchr(s, '"') != NULL);
2873 // sorry, we can't allow these substrings in wildcard expressions,
2874 // as they can mess with the argument counts
2875 text = rcon_restricted_commands.string;
2876 while(COM_ParseToken_Console(&text))
2878 // com_token now contains a pattern to check for...
2879 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2882 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2885 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2887 if(!strcmp(com_token, s))
2890 else // single-arg expression? must match the beginning of the command
2892 if(!strcmp(com_token, s))
2894 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2898 // if we got here, nothing matched!
2906 userpass_startpass = strchr(userpass_start, ':');
2907 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2908 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2910 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2913 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qbool proquakeprotocol)
2917 // looks like a legitimate rcon command with the correct password
2918 const char *s_ptr = s;
2919 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2920 while(s_ptr != endpos)
2922 size_t l = strlen(s_ptr);
2924 Con_Printf(" %s;", s_ptr);
2929 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2930 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2933 size_t l = strlen(s);
2936 client_t *host_client_save = host_client;
2937 Cmd_ExecuteString(cmd_local, s, src_local, true);
2938 host_client = host_client_save;
2939 // in case it is a command that changes host_client (like restart)
2943 Con_Rcon_Redirect_End();
2947 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2951 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2953 int i, ret, clientnum, best;
2955 char *string, response[2800], addressstring2[128];
2956 static char stringbuf[16384]; // server only
2957 qbool islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2958 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2959 size_t sendlength, response_len;
2960 char infostringvalue[MAX_INPUTLINE];
2965 // convert the address to a string incase we need it
2966 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2968 // see if we can identify the sender as a local player
2969 // (this is necessary for rcon to send a reliable reply if the client is
2970 // actually on the server, not sending remotely)
2971 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2972 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2974 if (i == svs.maxclients)
2977 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2979 // received a command string - strip off the packaging and put it
2980 // into our string buffer with NULL termination
2983 length = min(length, (int)sizeof(stringbuf) - 1);
2984 memcpy(stringbuf, data, length);
2985 stringbuf[length] = 0;
2988 if (developer_extra.integer)
2990 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2991 Com_HexDumpToConsole(data, length);
2994 sendlength = sizeof(senddata) - 4;
2995 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2997 case CRYPTO_NOMATCH:
3003 memcpy(senddata, "\377\377\377\377", 4);
3004 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3007 case CRYPTO_DISCARD:
3010 memcpy(senddata, "\377\377\377\377", 4);
3011 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3015 case CRYPTO_REPLACE:
3016 string = senddata+4;
3017 length = (int)sendlength;
3021 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3023 for (i = 0, best = 0, besttime = host.realtime;i < MAX_CHALLENGES;i++)
3025 if(challenges[i].time > 0)
3026 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3028 if (besttime > challenges[i].time)
3029 besttime = challenges[best = i].time;
3031 // if we did not find an exact match, choose the oldest and
3032 // update address and string
3033 if (i == MAX_CHALLENGES)
3036 challenges[i].address = *peeraddress;
3037 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3041 // flood control: drop if requesting challenge too often
3042 if(challenges[i].time > host.realtime - net_challengefloodblockingtimeout.value)
3045 challenges[i].time = host.realtime;
3046 // send the challenge
3047 memcpy(response, "\377\377\377\377", 4);
3048 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3049 response_len = strlen(response) + 1;
3050 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3051 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3054 if (length > 8 && !memcmp(string, "connect\\", 8))
3058 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3062 if(crypto && crypto->authenticated)
3064 // no need to check challenge
3065 if(crypto_developer.integer)
3067 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3068 crypto->use_aes ? "Encrypted" : "Authenticated",
3070 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3071 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3072 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3073 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3074 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3075 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3081 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3083 // validate the challenge
3084 for (i = 0;i < MAX_CHALLENGES;i++)
3085 if(challenges[i].time > 0)
3086 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3088 // if the challenge is not recognized, drop the packet
3089 if (i == MAX_CHALLENGES)
3094 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3095 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3097 if(!(islocal || sv_public.integer > -2))
3099 if (developer_extra.integer)
3100 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3101 memcpy(response, "\377\377\377\377", 4);
3102 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3103 NetConn_WriteString(mysocket, response, peeraddress);
3107 // check engine protocol
3108 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3110 if (developer_extra.integer)
3111 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3112 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3116 // see if this is a duplicate connection request or a disconnected
3117 // client who is rejoining to the same client slot
3118 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3120 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3122 // this is a known client...
3123 if(crypto && crypto->authenticated)
3125 // reject if changing key!
3126 if(client->netconnection->crypto.authenticated)
3129 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3131 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3133 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3135 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3138 if (developer_extra.integer)
3139 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3140 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3147 // reject if downgrading!
3148 if(client->netconnection->crypto.authenticated)
3150 if (developer_extra.integer)
3151 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3152 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3158 // client crashed and is coming back,
3159 // keep their stuff intact
3160 if (developer_extra.integer)
3161 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3162 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3163 if(crypto && crypto->authenticated)
3164 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3165 SV_SendServerinfo(client);
3169 // client is still trying to connect,
3170 // so we send a duplicate reply
3171 if (developer_extra.integer)
3172 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3173 if(crypto && crypto->authenticated)
3174 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3175 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3181 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3184 // find an empty client slot for this new client
3185 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3188 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3190 // allocated connection
3191 if (developer_extra.integer)
3192 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3193 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3194 // now set up the client
3195 if(crypto && crypto->authenticated)
3196 Crypto_FinishInstance(&conn->crypto, crypto);
3197 SV_ConnectClient(clientnum, conn);
3198 NetConn_Heartbeat(1);
3203 // no empty slots found - server is full
3204 if (developer_extra.integer)
3205 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3206 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3210 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3212 const char *challenge = NULL;
3214 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3217 // If there was a challenge in the getinfo message
3218 if (length > 8 && string[7] == ' ')
3219 challenge = string + 8;
3221 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3223 if (developer_extra.integer)
3224 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3225 NetConn_WriteString(mysocket, response, peeraddress);
3229 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3231 const char *challenge = NULL;
3233 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3236 // If there was a challenge in the getinfo message
3237 if (length > 10 && string[9] == ' ')
3238 challenge = string + 10;
3240 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3242 if (developer_extra.integer)
3243 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3244 NetConn_WriteString(mysocket, response, peeraddress);
3248 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3250 char *password = string + 20;
3251 char *timeval = string + 37;
3252 char *s = strchr(timeval, ' ');
3253 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3254 const char *userlevel;
3256 if(rcon_secure.integer > 1)
3260 return true; // invalid packet
3263 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3264 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3267 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3269 char *password = string + 25;
3270 char *challenge = string + 42;
3271 char *s = strchr(challenge, ' ');
3272 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3273 const char *userlevel;
3275 return true; // invalid packet
3278 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3279 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3282 if (length >= 5 && !memcmp(string, "rcon ", 5))
3285 char *s = string + 5;
3286 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3289 if(rcon_secure.integer > 0)
3292 for (j = 0;!ISWHITESPACE(*s);s++)
3293 if (j < (int)sizeof(password) - 1)
3295 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3298 if (!ISWHITESPACE(password[0]))
3300 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3301 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3305 if (!strncmp(string, "extResponse ", 12))
3307 ++sv_net_extresponse_count;
3308 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3309 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3310 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3311 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3314 if (!strncmp(string, "ping", 4))
3316 if (developer_extra.integer)
3317 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3318 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3321 if (!strncmp(string, "ack", 3))
3323 // we may not have liked the packet, but it was a command packet, so
3324 // we're done processing this packet now
3327 // netquake control packets, supported for compatibility only, and only
3328 // when running game protocols that are normally served via this connection
3330 // (this protects more modern protocols against being used for
3331 // Quake packet flood Denial Of Service attacks)
3332 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)
3336 const char *protocolname;
3337 client_t *knownclient;
3338 client_t *newclient;
3341 SZ_Clear(&sv_message);
3342 SZ_Write(&sv_message, data, length);
3343 MSG_BeginReading(&sv_message);
3344 c = MSG_ReadByte(&sv_message);
3348 if (developer_extra.integer)
3349 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3350 if(!(islocal || sv_public.integer > -2))
3352 if (developer_extra.integer)
3353 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3354 SZ_Clear(&sv_message);
3355 // save space for the header, filled in later
3356 MSG_WriteLong(&sv_message, 0);
3357 MSG_WriteByte(&sv_message, CCREP_REJECT);
3358 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3359 MSG_WriteString(&sv_message, "\n");
3360 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3361 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3362 SZ_Clear(&sv_message);
3366 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3367 protocolnumber = MSG_ReadByte(&sv_message);
3368 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3370 if (developer_extra.integer)
3371 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3372 SZ_Clear(&sv_message);
3373 // save space for the header, filled in later
3374 MSG_WriteLong(&sv_message, 0);
3375 MSG_WriteByte(&sv_message, CCREP_REJECT);
3376 MSG_WriteString(&sv_message, "Incompatible version.\n");
3377 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3378 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3379 SZ_Clear(&sv_message);
3383 // see if this connect request comes from a known client
3384 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3386 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3388 // this is either a duplicate connection request
3389 // or coming back from a timeout
3390 // (if so, keep their stuff intact)
3392 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3393 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3395 if (developer_extra.integer)
3396 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3397 SZ_Clear(&sv_message);
3398 // save space for the header, filled in later
3399 MSG_WriteLong(&sv_message, 0);
3400 MSG_WriteByte(&sv_message, CCREP_REJECT);
3401 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3402 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3403 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3404 SZ_Clear(&sv_message);
3409 if (developer_extra.integer)
3410 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3411 SZ_Clear(&sv_message);
3412 // save space for the header, filled in later
3413 MSG_WriteLong(&sv_message, 0);
3414 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3415 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3416 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3417 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3418 SZ_Clear(&sv_message);
3420 // if client is already spawned, re-send the
3421 // serverinfo message as they'll need it to play
3422 if (knownclient->begun)
3423 SV_SendServerinfo(knownclient);
3428 // this is a new client, check for connection flood
3429 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3432 // find a slot for the new client
3433 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3436 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3438 // connect to the client
3439 // everything is allocated, just fill in the details
3440 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3441 if (developer_extra.integer)
3442 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3443 // send back the info about the server connection
3444 SZ_Clear(&sv_message);
3445 // save space for the header, filled in later
3446 MSG_WriteLong(&sv_message, 0);
3447 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3448 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3449 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3450 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3451 SZ_Clear(&sv_message);
3452 // now set up the client struct
3453 SV_ConnectClient(clientnum, conn);
3454 NetConn_Heartbeat(1);
3459 if (developer_extra.integer)
3460 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3461 // no room; try to let player know
3462 SZ_Clear(&sv_message);
3463 // save space for the header, filled in later
3464 MSG_WriteLong(&sv_message, 0);
3465 MSG_WriteByte(&sv_message, CCREP_REJECT);
3466 MSG_WriteString(&sv_message, "Server is full.\n");
3467 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3468 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3469 SZ_Clear(&sv_message);
3471 case CCREQ_SERVER_INFO:
3472 if (developer_extra.integer)
3473 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3474 if(!(islocal || sv_public.integer > -1))
3477 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3480 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3483 char myaddressstring[128];
3484 if (developer_extra.integer)
3485 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3486 SZ_Clear(&sv_message);
3487 // save space for the header, filled in later
3488 MSG_WriteLong(&sv_message, 0);
3489 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3490 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3491 MSG_WriteString(&sv_message, myaddressstring);
3492 MSG_WriteString(&sv_message, hostname.string);
3493 MSG_WriteString(&sv_message, sv.name);
3494 // How many clients are there?
3495 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3496 if (svs.clients[i].active)
3498 MSG_WriteByte(&sv_message, numclients);
3499 MSG_WriteByte(&sv_message, svs.maxclients);
3500 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3501 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3502 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3503 SZ_Clear(&sv_message);
3506 case CCREQ_PLAYER_INFO:
3507 if (developer_extra.integer)
3508 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3509 if(!(islocal || sv_public.integer > -1))
3512 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3517 int playerNumber, activeNumber, clientNumber;
3520 playerNumber = MSG_ReadByte(&sv_message);
3522 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3523 if (client->active && ++activeNumber == playerNumber)
3525 if (clientNumber != svs.maxclients)
3527 SZ_Clear(&sv_message);
3528 // save space for the header, filled in later
3529 MSG_WriteLong(&sv_message, 0);
3530 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3531 MSG_WriteByte(&sv_message, playerNumber);
3532 MSG_WriteString(&sv_message, client->name);
3533 MSG_WriteLong(&sv_message, client->colors);
3534 MSG_WriteLong(&sv_message, client->frags);
3535 MSG_WriteLong(&sv_message, (int)(host.realtime - client->connecttime));
3536 if(sv_status_privacy.integer)
3537 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3539 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3540 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3541 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3542 SZ_Clear(&sv_message);
3546 case CCREQ_RULE_INFO:
3547 if (developer_extra.integer)
3548 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3549 if(!(islocal || sv_public.integer > -1))
3552 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3559 // find the search start location
3560 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3561 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CF_NOTIFY);
3563 // send the response
3564 SZ_Clear(&sv_message);
3565 // save space for the header, filled in later
3566 MSG_WriteLong(&sv_message, 0);
3567 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3570 MSG_WriteString(&sv_message, var->name);
3571 MSG_WriteString(&sv_message, var->string);
3573 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3574 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3575 SZ_Clear(&sv_message);
3579 if (developer_extra.integer)
3580 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3581 if (sv.active && !rcon_secure.integer)
3583 char password[2048];
3587 const char *userlevel;
3588 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3589 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3591 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3592 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3593 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3600 SZ_Clear(&sv_message);
3601 // we may not have liked the packet, but it was a valid control
3602 // packet, so we're done processing this packet now
3607 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3609 SV_ReadClientMessage();
3616 void NetConn_ServerFrame(void)
3620 lhnetaddress_t peeraddress;
3621 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3623 for (i = 0;i < sv_numsockets;i++)
3624 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3625 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3628 void NetConn_SleepMicroseconds(int microseconds)
3630 LHNET_SleepUntilPacket_Microseconds(microseconds);
3634 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
3638 lhnetaddress_t masteraddress;
3639 lhnetaddress_t broadcastaddress;
3642 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3645 // 26000 is the default quake server port, servers on other ports will not
3647 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3648 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3652 for (i = 0;i < cl_numsockets;i++)
3656 const char *cmdname, *extraoptions;
3657 lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3659 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3661 // search LAN for Quake servers
3662 SZ_Clear(&cl_message);
3663 // save space for the header, filled in later
3664 MSG_WriteLong(&cl_message, 0);
3665 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3666 MSG_WriteString(&cl_message, "QUAKE");
3667 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3668 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3669 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3670 SZ_Clear(&cl_message);
3672 // search LAN for DarkPlaces servers
3673 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3676 // build the getservers message to send to the dpmaster master servers
3677 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3679 cmdname = "getserversExt";
3680 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3684 cmdname = "getservers";
3687 memcpy(request, "\377\377\377\377", 4);
3688 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3691 for (masternum = 0;sv_masters[masternum].name;masternum++)
3693 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3696 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3700 // search favorite servers
3701 for(j = 0; j < nFavorites; ++j)
3703 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3705 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3706 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3713 // only query QuakeWorld servers when the user wants to
3716 for (i = 0;i < cl_numsockets;i++)
3720 lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3722 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3724 // search LAN for QuakeWorld servers
3725 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3727 // build the getservers message to send to the qwmaster master servers
3728 // note this has no -1 prefix, and the trailing nul byte is sent
3729 dpsnprintf(request, sizeof(request), "c\n");
3733 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3735 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3737 if (m_state != m_slist)
3739 char lookupstring[128];
3740 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3741 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3744 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3748 // search favorite servers
3749 for(j = 0; j < nFavorites; ++j)
3751 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3753 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3755 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3756 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3763 if (!masterquerycount)
3765 Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
3766 M_Update_Return_Reason("No network");
3771 void NetConn_Heartbeat(int priority)
3773 lhnetaddress_t masteraddress;
3775 lhnetsocket_t *mysocket;
3777 // if it's a state change (client connected), limit next heartbeat to no
3778 // more than 30 sec in the future
3779 if (priority == 1 && nextheartbeattime > host.realtime + 30.0)
3780 nextheartbeattime = host.realtime + 30.0;
3782 // limit heartbeatperiod to 30 to 270 second range,
3783 // lower limit is to avoid abusing master servers with excess traffic,
3784 // upper limit is to avoid timing out on the master server (which uses
3786 if (sv_heartbeatperiod.value < 30)
3787 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3788 if (sv_heartbeatperiod.value > 270)
3789 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3791 // make advertising optional and don't advertise singleplayer games, and
3792 // only send a heartbeat as often as the admin wants
3793 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
3795 nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
3796 for (masternum = 0;sv_masters[masternum].name;masternum++)
3797 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3798 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3802 static void Net_Heartbeat_f(cmd_state_t *cmd)
3805 NetConn_Heartbeat(2);
3807 Con_Print("No server running, can not heartbeat to master server.\n");
3810 static void PrintStats(netconn_t *conn)
3812 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3813 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3815 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3816 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3817 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3818 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3819 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3820 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3821 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3822 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3823 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3824 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3827 void Net_Stats_f(cmd_state_t *cmd)
3830 Con_Print("connections =\n");
3831 for (conn = netconn_list;conn;conn = conn->next)
3836 void Net_Refresh_f(cmd_state_t *cmd)
3838 if (m_state != m_slist)
3840 Con_Print("Sending new requests to master servers\n");
3841 ServerList_QueryList(false, true, false, true);
3842 Con_Print("Listening for replies...\n");
3845 ServerList_QueryList(false, true, false, false);
3848 void Net_Slist_f(cmd_state_t *cmd)
3850 ServerList_ResetMasks();
3851 serverlist_sortbyfield = SLIF_PING;
3852 serverlist_sortflags = 0;
3853 if (m_state != m_slist)
3855 Con_Print("Sending requests to master servers\n");
3856 ServerList_QueryList(true, true, false, true);
3857 Con_Print("Listening for replies...\n");
3860 ServerList_QueryList(true, true, false, false);
3863 void Net_SlistQW_f(cmd_state_t *cmd)
3865 ServerList_ResetMasks();
3866 serverlist_sortbyfield = SLIF_PING;
3867 serverlist_sortflags = 0;
3868 if (m_state != m_slist)
3870 Con_Print("Sending requests to master servers\n");
3871 ServerList_QueryList(true, false, true, true);
3872 serverlist_consoleoutput = true;
3873 Con_Print("Listening for replies...\n");
3876 ServerList_QueryList(true, false, true, false);
3880 void NetConn_Init(void)
3883 lhnetaddress_t tempaddress;
3884 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3885 Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
3887 Cmd_AddCommand(CF_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
3888 Cmd_AddCommand(CF_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3889 Cmd_AddCommand(CF_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3891 Cmd_AddCommand(CF_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3892 Cvar_RegisterVariable(&net_test);
3893 Cvar_RegisterVariable(&net_usesizelimit);
3894 Cvar_RegisterVariable(&net_burstreserve);
3895 Cvar_RegisterVariable(&rcon_restricted_password);
3896 Cvar_RegisterVariable(&rcon_restricted_commands);
3897 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3898 Cvar_RegisterVariable(&net_slist_queriespersecond);
3899 Cvar_RegisterVariable(&net_slist_queriesperframe);
3900 Cvar_RegisterVariable(&net_slist_timeout);
3901 Cvar_RegisterVariable(&net_slist_maxtries);
3902 Cvar_RegisterVariable(&net_slist_favorites);
3904 Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
3906 Cvar_RegisterVariable(&net_slist_pause);
3907 #ifdef IP_TOS // register cvar only if supported
3908 Cvar_RegisterVariable(&net_tos_dscp);
3910 Cvar_RegisterVariable(&net_messagetimeout);
3911 Cvar_RegisterVariable(&net_connecttimeout);
3912 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3913 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3914 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3915 Cvar_RegisterVariable(&net_sourceaddresscheck);
3916 Cvar_RegisterVariable(&net_fakelag);
3917 Cvar_RegisterVariable(&net_fakeloss_send);
3918 Cvar_RegisterVariable(&net_fakeloss_receive);
3919 Cvar_RegisterVirtual(&net_fakelag, "cl_netlocalping");
3920 Cvar_RegisterVirtual(&net_fakeloss_send, "cl_netpacketloss_send");
3921 Cvar_RegisterVirtual(&net_fakeloss_receive, "cl_netpacketloss_receive");
3922 Cvar_RegisterVariable(&hostname);
3923 Cvar_RegisterVariable(&developer_networking);
3924 Cvar_RegisterVariable(&cl_netport);
3925 Cvar_RegisterCallback(&cl_netport, NetConn_CL_UpdateSockets_Callback);
3926 Cvar_RegisterVariable(&sv_netport);
3927 Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
3928 Cvar_RegisterVariable(&net_address);
3929 Cvar_RegisterVariable(&net_address_ipv6);
3930 Cvar_RegisterVariable(&sv_public);
3931 Cvar_RegisterVariable(&sv_public_rejectreason);
3932 Cvar_RegisterVariable(&sv_heartbeatperiod);
3933 for (i = 0;sv_masters[i].name;i++)
3934 Cvar_RegisterVariable(&sv_masters[i]);
3935 Cvar_RegisterVariable(&gameversion);
3936 Cvar_RegisterVariable(&gameversion_min);
3937 Cvar_RegisterVariable(&gameversion_max);
3938 // 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.
3939 if ((i = Sys_CheckParm("-ip")) && i + 1 < sys.argc)
3941 if (LHNETADDRESS_FromString(&tempaddress, sys.argv[i + 1], 0) == 1)
3943 Con_Printf("-ip option used, setting net_address to \"%s\"\n", sys.argv[i + 1]);
3944 Cvar_SetQuick(&net_address, sys.argv[i + 1]);
3947 Con_Printf(CON_ERROR "-ip option used, but unable to parse the address \"%s\"\n", sys.argv[i + 1]);
3949 // 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
3950 if (((i = Sys_CheckParm("-port")) || (i = Sys_CheckParm("-ipport")) || (i = Sys_CheckParm("-udpport"))) && i + 1 < sys.argc)
3952 i = atoi(sys.argv[i + 1]);
3953 if (i >= 0 && i < 65536)
3955 Con_Printf("-port option used, setting port cvar to %i\n", i);
3956 Cvar_SetValueQuick(&sv_netport, i);
3959 Con_Printf(CON_ERROR "-port option used, but %i is not a valid port number\n", i);
3963 cl_message.data = cl_message_buf;
3964 cl_message.maxsize = sizeof(cl_message_buf);
3965 cl_message.cursize = 0;
3966 sv_message.data = sv_message_buf;
3967 sv_message.maxsize = sizeof(sv_message_buf);
3968 sv_message.cursize = 0;
3970 if (Thread_HasThreads())
3971 netconn_mutex = Thread_CreateMutex();
3974 void NetConn_Shutdown(void)
3976 NetConn_CloseClientPorts();
3977 NetConn_CloseServerPorts();
3980 Thread_DestroyMutex(netconn_mutex);
3981 netconn_mutex = NULL;