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)"}, // admin: Willis
48 {CF_CLIENT | CF_SERVER, "sv_masterextra2", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 2 (admin: tChr)"}, // admin: tChr
53 static cvar_t sv_qwmasters [] =
55 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
56 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
57 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
58 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
59 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
60 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
61 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
62 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
67 static double nextheartbeattime = 0;
71 static unsigned char cl_message_buf[NET_MAXMESSAGE];
72 static unsigned char sv_message_buf[NET_MAXMESSAGE];
73 char cl_readstring[MAX_INPUTLINE];
74 char sv_readstring[MAX_INPUTLINE];
76 cvar_t net_test = {CF_CLIENT | CF_SERVER, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
77 cvar_t net_usesizelimit = {CF_SERVER, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
78 cvar_t net_burstreserve = {CF_SERVER, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
79 cvar_t net_messagetimeout = {CF_CLIENT | CF_SERVER, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
80 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."};
81 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."};
82 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."};
83 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."};
84 cvar_t net_sourceaddresscheck = {CF_CLIENT, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
85 cvar_t hostname = {CF_SERVER | CF_ARCHIVE, "hostname", "UNNAMED", "server message to show in server browser"};
86 cvar_t developer_networking = {CF_CLIENT | CF_SERVER, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
88 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)"};
89 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)"};
90 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)"};
91 static cvar_t net_slist_queriespersecond = {CF_CLIENT, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
92 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)"};
93 static cvar_t net_slist_timeout = {CF_CLIENT, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
94 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"};
95 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)"};
96 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"};
97 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)"};
98 static cvar_t gameversion = {CF_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
99 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"};
100 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"};
101 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"};
102 static cvar_t rcon_restricted_commands = {CF_SERVER, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
103 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)"};
104 extern cvar_t rcon_secure;
105 extern cvar_t rcon_secure_challengetimeout;
107 double masterquerytime = -1000;
108 int masterquerycount = 0;
109 int masterreplycount = 0;
110 int serverquerycount = 0;
111 int serverreplycount = 0;
113 challenge_t challenges[MAX_CHALLENGES];
116 /// this is only false if there are still servers left to query
117 static qbool serverlist_querysleep = true;
118 static qbool serverlist_paused = false;
119 /// this is pushed a second or two ahead of realtime whenever a master server
120 /// reply is received, to avoid issuing queries while master replies are still
121 /// flooding in (which would make a mess of the ping times)
122 static double serverlist_querywaittime = 0;
125 static int cl_numsockets;
126 static lhnetsocket_t *cl_sockets[16];
127 static int sv_numsockets;
128 static lhnetsocket_t *sv_sockets[16];
130 netconn_t *netconn_list = NULL;
131 mempool_t *netconn_mempool = NULL;
132 void *netconn_mutex = NULL;
134 cvar_t cl_netport = {CF_CLIENT, "cl_port", "0", "forces client to use chosen port number if not 0"};
135 cvar_t sv_netport = {CF_SERVER, "port", "26000", "server port for players to connect to"};
136 cvar_t net_address = {CF_CLIENT | CF_SERVER, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
137 cvar_t net_address_ipv6 = {CF_CLIENT | CF_SERVER, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
139 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
140 int cl_net_extresponse_count = 0;
141 int cl_net_extresponse_last = 0;
143 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
144 int sv_net_extresponse_count = 0;
145 int sv_net_extresponse_last = 0;
148 // ServerList interface
149 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
150 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
152 serverlist_infofield_t serverlist_sortbyfield;
153 int serverlist_sortflags;
155 int serverlist_viewcount = 0;
156 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
158 int serverlist_maxcachecount = 0;
159 int serverlist_cachecount = 0;
160 serverlist_entry_t *serverlist_cache = NULL;
162 qbool serverlist_consoleoutput;
164 static int nFavorites = 0;
165 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
166 static int nFavorites_idfp = 0;
167 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
169 void NetConn_UpdateFavorites_c(cvar_t *var)
175 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
177 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
178 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
179 // (if v6 address contains port, it must start with '[')
181 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
186 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
192 /// helper function to insert a value into the viewset
193 /// spare entries will be removed
194 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
197 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
198 i = serverlist_viewcount++;
200 i = SERVERLIST_VIEWLISTSIZE - 1;
203 for( ; i > index ; i-- )
204 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
206 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
209 /// we suppose serverlist_viewcount to be valid, ie > 0
210 static void _ServerList_ViewList_Helper_Remove( int index )
212 serverlist_viewcount--;
213 for( ; index < serverlist_viewcount ; index++ )
214 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
217 /// \returns true if A should be inserted before B
218 static qbool _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
220 int result = 0; // > 0 if for numbers A > B and for text if A < B
222 if( serverlist_sortflags & SLSF_CATEGORIES )
224 result = A->info.category - B->info.category;
229 if( serverlist_sortflags & SLSF_FAVORITES )
231 if(A->info.isfavorite != B->info.isfavorite)
232 return A->info.isfavorite;
235 switch( serverlist_sortbyfield ) {
237 result = A->info.ping - B->info.ping;
239 case SLIF_MAXPLAYERS:
240 result = A->info.maxplayers - B->info.maxplayers;
242 case SLIF_NUMPLAYERS:
243 result = A->info.numplayers - B->info.numplayers;
246 result = A->info.numbots - B->info.numbots;
249 result = A->info.numhumans - B->info.numhumans;
252 result = A->info.freeslots - B->info.freeslots;
255 result = A->info.protocol - B->info.protocol;
258 result = strcmp( B->info.cname, A->info.cname );
261 result = strcasecmp( B->info.game, A->info.game );
264 result = strcasecmp( B->info.map, A->info.map );
267 result = strcasecmp( B->info.mod, A->info.mod );
270 result = strcasecmp( B->info.name, A->info.name );
273 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
276 result = A->info.category - B->info.category;
278 case SLIF_ISFAVORITE:
279 result = !!B->info.isfavorite - !!A->info.isfavorite;
282 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
288 if( serverlist_sortflags & SLSF_DESCENDING )
294 // if the chosen sort key is identical, sort by index
295 // (makes this a stable sort, so that later replies from servers won't
296 // shuffle the servers around when they have the same ping)
300 static qbool _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
302 // This should actually be done with some intermediate and end-of-function return
314 case SLMO_GREATEREQUAL:
316 case SLMO_NOTCONTAIN:
317 case SLMO_STARTSWITH:
318 case SLMO_NOTSTARTSWITH:
321 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
326 static qbool _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
329 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
330 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
331 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
332 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
334 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
335 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
338 // Same here, also using an intermediate & final return would be more appropriate
342 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
343 case SLMO_NOTCONTAIN:
344 return !*bufferB || !strstr( bufferA, bufferB );
345 case SLMO_STARTSWITH:
346 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
347 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
348 case SLMO_NOTSTARTSWITH:
349 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
351 return strcmp( bufferA, bufferB ) < 0;
353 return strcmp( bufferA, bufferB ) <= 0;
355 return strcmp( bufferA, bufferB ) == 0;
357 return strcmp( bufferA, bufferB ) > 0;
359 return strcmp( bufferA, bufferB ) != 0;
360 case SLMO_GREATEREQUAL:
361 return strcmp( bufferA, bufferB ) >= 0;
363 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
368 static qbool _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
370 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
372 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
374 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
376 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
378 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
380 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
382 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
384 if( *mask->info.cname
385 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
388 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
391 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
394 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
397 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
399 if( *mask->info.qcstatus
400 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
402 if( *mask->info.players
403 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
405 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
407 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
412 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
414 int start, end, mid, i;
417 // reject incompatible servers
419 entry->info.gameversion != gameversion.integer
422 gameversion_min.integer >= 0 // min/max range set by user/mod?
423 && gameversion_max.integer >= 0
424 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
425 && gameversion_max.integer >= entry->info.gameversion
430 // refresh the "favorite" status
431 entry->info.isfavorite = false;
432 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
434 char idfp[FP64_SIZE+1];
435 for(i = 0; i < nFavorites; ++i)
437 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
439 entry->info.isfavorite = true;
443 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
445 for(i = 0; i < nFavorites_idfp; ++i)
447 if(!strcmp(idfp, favorites_idfp[i]))
449 entry->info.isfavorite = true;
456 // refresh the "category"
457 entry->info.category = MR_GetServerListEntryCategory(entry);
459 // FIXME: change this to be more readable (...)
460 // now check whether it passes through the masks
461 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
462 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
465 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
466 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
468 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
471 if( !serverlist_viewcount ) {
472 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
475 // ok, insert it, we just need to find out where exactly:
478 // check whether to insert it as new first item
479 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
480 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
482 } // check whether to insert it as new last item
483 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
484 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
488 end = serverlist_viewcount - 1;
489 while( end > start + 1 )
491 mid = (start + end) / 2;
492 // test the item that lies in the middle between start and end
493 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
494 // the item has to be in the upper half
497 // the item has to be in the lower half
500 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
503 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
506 for( i = 0; i < serverlist_viewcount; i++ )
508 if (ServerList_GetViewEntry(i) == entry)
510 _ServerList_ViewList_Helper_Remove(i);
516 void ServerList_RebuildViewList(void)
520 serverlist_viewcount = 0;
521 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
522 serverlist_entry_t *entry = &serverlist_cache[i];
523 // also display entries that are currently being refreshed [11/8/2007 Black]
524 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
525 ServerList_ViewList_Insert( entry );
529 void ServerList_ResetMasks(void)
533 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
534 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
535 // numbots needs to be compared to -1 to always succeed
536 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
537 serverlist_andmasks[i].info.numbots = -1;
538 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
539 serverlist_ormasks[i].info.numbots = -1;
542 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
545 int numplayers = 0, maxplayers = 0;
546 for (i = 0;i < serverlist_cachecount;i++)
548 if (serverlist_cache[i].query == SQS_QUERIED)
550 numplayers += serverlist_cache[i].info.numhumans;
551 maxplayers += serverlist_cache[i].info.maxplayers;
554 *numplayerspointer = numplayers;
555 *maxplayerspointer = maxplayers;
559 static void _ServerList_Test(void)
562 if (serverlist_maxcachecount <= 1024)
564 serverlist_maxcachecount = 1024;
565 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
567 for( i = 0 ; i < 1024 ; i++ ) {
568 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
569 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
570 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
571 serverlist_cache[serverlist_cachecount].finished = true;
572 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 );
573 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
574 serverlist_cachecount++;
579 void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool consoleoutput)
581 masterquerytime = host.realtime;
582 masterquerycount = 0;
583 masterreplycount = 0;
585 serverquerycount = 0;
586 serverreplycount = 0;
587 serverlist_cachecount = 0;
588 serverlist_viewcount = 0;
589 serverlist_maxcachecount = 0;
590 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
592 // refresh all entries
594 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
595 serverlist_entry_t *entry = &serverlist_cache[ n ];
596 entry->query = SQS_REFRESHING;
597 entry->querycounter = 0;
600 serverlist_consoleoutput = consoleoutput;
602 //_ServerList_Test();
604 NetConn_QueryMasters(querydp, queryqw);
610 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
614 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
615 Thread_LockMutex(netconn_mutex);
616 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
617 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
618 Thread_UnlockMutex(netconn_mutex);
621 if (net_fakeloss_receive.integer)
622 for (i = 0;i < cl_numsockets;i++)
623 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_receive.integer)
625 if (developer_networking.integer)
627 char addressstring[128], addressstring2[128];
628 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
631 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
632 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
633 Com_HexDumpToConsole((unsigned char *)data, length);
636 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
641 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
645 if (net_fakeloss_send.integer)
646 for (i = 0;i < cl_numsockets;i++)
647 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_send.integer)
649 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
650 Thread_LockMutex(netconn_mutex);
651 ret = LHNET_Write(mysocket, data, length, peeraddress);
652 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
653 Thread_UnlockMutex(netconn_mutex);
654 if (developer_networking.integer)
656 char addressstring[128], addressstring2[128];
657 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
658 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
659 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)");
660 Com_HexDumpToConsole((unsigned char *)data, length);
665 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
667 // note this does not include the trailing NULL because we add that in the parser
668 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
671 qbool NetConn_CanSend(netconn_t *conn)
673 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
674 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = host.realtime;
675 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
676 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
677 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
678 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
679 if (host.realtime > conn->cleartime)
683 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
688 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
690 double bursttime = burstsize / (double)rate;
692 // delay later packets to obey rate limit
693 if (*cleartime < host.realtime - bursttime)
694 *cleartime = host.realtime - bursttime;
695 *cleartime = *cleartime + len / (double)rate;
697 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
698 if (net_test.integer)
700 if (*cleartime < host.realtime)
701 *cleartime = host.realtime;
705 static int NetConn_AddCryptoFlag(crypto_t *crypto)
707 // HACK: if an encrypted connection is used, randomly set some unused
708 // flags. When AES encryption is enabled, that will make resends differ
709 // from the original, so that e.g. substring filters in a router/IPS
710 // are unlikely to match a second time. See also "startkeylogger".
712 if (crypto->authenticated)
714 // Let's always set at least one of the bits.
715 int r = rand() % 7 + 1;
717 flag |= NETFLAG_CRYPTO0;
719 flag |= NETFLAG_CRYPTO1;
721 flag |= NETFLAG_CRYPTO2;
726 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qbool quakesignon_suppressreliables)
729 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
730 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
732 // if this packet was supposedly choked, but we find ourselves sending one
733 // anyway, make sure the size counting starts at zero
734 // (this mostly happens on level changes and disconnects and such)
735 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
736 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
738 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
740 if (protocol == PROTOCOL_QUAKEWORLD)
745 // note that it is ok to send empty messages to the qw server,
746 // otherwise it won't respond to us at all
748 sendreliable = false;
749 // if the remote side dropped the last reliable message, resend it
750 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
752 // if the reliable transmit buffer is empty, copy the current message out
753 if (!conn->sendMessageLength && conn->message.cursize)
755 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
756 conn->sendMessageLength = conn->message.cursize;
757 SZ_Clear(&conn->message); // clear the message buffer
758 conn->qw.reliable_sequence ^= 1;
761 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
762 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
763 // last received unreliable packet number, and last received reliable packet number (0 or 1)
764 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
766 conn->outgoing_unreliable_sequence++;
767 // client sends qport in every packet
768 if (conn == cls.netcon)
770 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
772 // also update cls.qw_outgoing_sequence
773 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
775 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
777 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
781 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
783 // add the reliable message if there is one
786 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
787 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
788 packetLen += conn->sendMessageLength;
789 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
792 // add the unreliable message if possible
793 if (packetLen + data->cursize <= 1400)
795 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
796 memcpy(sendbuffer + packetLen, data->data, data->cursize);
797 packetLen += data->cursize;
800 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
803 conn->unreliableMessagesSent++;
805 totallen += packetLen + 28;
809 unsigned int packetLen;
810 unsigned int dataLen;
815 // if a reliable message fragment has been lost, send it again
816 if (conn->sendMessageLength && (host.realtime - conn->lastSendTime) > 1.0)
818 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
820 dataLen = conn->sendMessageLength;
825 dataLen = MAX_PACKETFRAGMENT;
829 packetLen = NET_HEADERSIZE + dataLen;
831 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
832 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
833 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
835 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
837 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
838 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
840 conn->lastSendTime = host.realtime;
841 conn->packetsReSent++;
844 totallen += (int)sendmelen + 28;
847 // if we have a new reliable message to send, do so
848 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
850 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
852 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
853 conn->message.overflowed = true;
857 if (developer_networking.integer && conn == cls.netcon)
859 Con_Print("client sending reliable message to server:\n");
860 SZ_HexDumpToConsole(&conn->message);
863 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
864 conn->sendMessageLength = conn->message.cursize;
865 SZ_Clear(&conn->message);
867 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
869 dataLen = conn->sendMessageLength;
874 dataLen = MAX_PACKETFRAGMENT;
878 packetLen = NET_HEADERSIZE + dataLen;
880 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
881 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
882 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
884 conn->nq.sendSequence++;
886 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
888 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
890 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
892 conn->lastSendTime = host.realtime;
894 conn->reliableMessagesSent++;
896 totallen += (int)sendmelen + 28;
899 // if we have an unreliable message to send, do so
902 packetLen = NET_HEADERSIZE + data->cursize;
904 if (packetLen > (int)sizeof(sendbuffer))
906 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
910 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
911 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
912 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
914 conn->outgoing_unreliable_sequence++;
916 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
918 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
920 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
923 conn->unreliableMessagesSent++;
925 totallen += (int)sendmelen + 28;
929 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
934 qbool NetConn_HaveClientPorts(void)
936 return !!cl_numsockets;
939 qbool NetConn_HaveServerPorts(void)
941 return !!sv_numsockets;
944 void NetConn_CloseClientPorts(void)
946 for (;cl_numsockets > 0;cl_numsockets--)
947 if (cl_sockets[cl_numsockets - 1])
948 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
951 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
953 lhnetaddress_t address;
956 char addressstring2[1024];
957 if (addressstring && addressstring[0])
958 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
960 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
963 if ((s = LHNET_OpenSocket_Connectionless(&address)))
965 cl_sockets[cl_numsockets++] = s;
966 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
967 if (addresstype != LHNETADDRESSTYPE_LOOP)
968 Con_Printf("Client opened a socket on address %s\n", addressstring2);
972 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
973 Con_Printf(CON_ERROR "Client failed to open a socket on address %s\n", addressstring2);
977 Con_Printf(CON_ERROR "Client unable to parse address %s\n", addressstring);
980 void NetConn_OpenClientPorts(void)
983 NetConn_CloseClientPorts();
985 SV_LockThreadMutex(); // FIXME recursive?
986 Crypto_LoadKeys(); // client sockets
987 SV_UnlockThreadMutex();
989 port = bound(0, cl_netport.integer, 65535);
990 if (cl_netport.integer != port)
991 Cvar_SetValueQuick(&cl_netport, port);
993 Con_Printf("Client using an automatically assigned port\n");
995 Con_Printf("Client using port %i\n", port);
996 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
997 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
998 #ifndef NOSUPPORTIPV6
999 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1003 void NetConn_CloseServerPorts(void)
1005 for (;sv_numsockets > 0;sv_numsockets--)
1006 if (sv_sockets[sv_numsockets - 1])
1007 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1010 static qbool NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1012 lhnetaddress_t address;
1015 char addressstring2[1024];
1018 for (port = defaultport; port <= defaultport + range; port++)
1020 if (addressstring && addressstring[0])
1021 success = LHNETADDRESS_FromString(&address, addressstring, port);
1023 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1026 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1028 sv_sockets[sv_numsockets++] = s;
1029 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1030 if (addresstype != LHNETADDRESSTYPE_LOOP)
1031 Con_Printf("Server listening on address %s\n", addressstring2);
1036 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1037 Con_Printf(CON_ERROR "Server failed to open socket on address %s\n", addressstring2);
1042 Con_Printf(CON_ERROR "Server unable to parse address %s\n", addressstring);
1043 // if it cant parse one address, it wont be able to parse another for sure
1050 void NetConn_OpenServerPorts(int opennetports)
1053 NetConn_CloseServerPorts();
1055 SV_LockThreadMutex(); // FIXME recursive?
1056 Crypto_LoadKeys(); // server sockets
1057 SV_UnlockThreadMutex();
1059 NetConn_UpdateSockets();
1060 port = bound(0, sv_netport.integer, 65535);
1063 if (sv_netport.integer != port)
1064 Cvar_SetValueQuick(&sv_netport, port);
1065 if (cls.state != ca_dedicated)
1066 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1069 #ifndef NOSUPPORTIPV6
1070 qbool ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1071 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1073 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1076 if (sv_numsockets == 0)
1077 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1080 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1082 int i, a = LHNETADDRESS_GetAddressType(address);
1083 for (i = 0;i < cl_numsockets;i++)
1084 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1085 return cl_sockets[i];
1089 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1091 int i, a = LHNETADDRESS_GetAddressType(address);
1092 for (i = 0;i < sv_numsockets;i++)
1093 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1094 return sv_sockets[i];
1098 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1101 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1102 conn->mysocket = mysocket;
1103 conn->peeraddress = *peeraddress;
1104 conn->lastMessageTime = host.realtime;
1105 conn->message.data = conn->messagedata;
1106 conn->message.maxsize = sizeof(conn->messagedata);
1107 conn->message.cursize = 0;
1108 // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
1109 // reduce effectiveness of connection request floods
1110 conn->timeout = host.realtime + net_connecttimeout.value;
1111 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1112 conn->next = netconn_list;
1113 netconn_list = conn;
1117 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1118 void NetConn_Close(netconn_t *conn)
1121 // remove connection from list
1123 // allow the client to reconnect immediately
1124 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1126 if (conn == netconn_list)
1127 netconn_list = conn->next;
1130 for (c = netconn_list;c;c = c->next)
1132 if (c->next == conn)
1134 c->next = conn->next;
1138 // not found in list, we'll avoid crashing here...
1146 static int clientport = -1;
1147 static int clientport2 = -1;
1148 static int hostport = -1;
1150 // Call on disconnect, during startup, or if cl_netport is changed
1151 void NetConn_UpdateSockets_Client(void)
1153 if (cls.state == ca_disconnected && clientport != clientport2)
1155 clientport = clientport2;
1156 NetConn_CloseClientPorts();
1158 if (cl_numsockets == 0)
1159 NetConn_OpenClientPorts();
1162 // Call when cl_port is changed
1163 static void NetConn_cl_netport_Callback(cvar_t *var)
1165 if(cls.state != ca_dedicated)
1167 if (clientport2 != var->integer)
1169 clientport2 = var->integer;
1170 if (cls.state == ca_connected)
1171 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1173 NetConn_UpdateSockets_Client();
1177 // Call when port is changed
1178 static void NetConn_sv_netport_Callback(cvar_t *var)
1180 if (hostport != var->integer)
1182 hostport = var->integer;
1184 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1188 void NetConn_UpdateSockets(void)
1192 // TODO add logic to automatically close sockets if needed
1193 LHNET_DefaultDSCP(net_tos_dscp.integer);
1195 for (j = 0;j < MAX_RCONS;j++)
1197 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1198 if(cls.rcon_commands[i][0])
1200 if(host.realtime > cls.rcon_timeout[i])
1203 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1204 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1205 cls.rcon_commands[i][0] = 0;
1213 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1215 int originallength = (int)length;
1216 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1217 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1218 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1222 if (protocol == PROTOCOL_QUAKEWORLD)
1224 unsigned int sequence, sequence_ack;
1225 qbool reliable_ack, reliable_message;
1229 sequence = LittleLong(*((int *)(data + 0)));
1230 sequence_ack = LittleLong(*((int *)(data + 4)));
1234 if (conn != cls.netcon)
1239 // 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?)
1240 //qport = LittleShort(*((int *)(data + 8)));
1245 conn->packetsReceived++;
1246 reliable_message = (sequence >> 31) != 0;
1247 reliable_ack = (sequence_ack >> 31) != 0;
1248 sequence &= ~(1<<31);
1249 sequence_ack &= ~(1<<31);
1250 if (sequence <= conn->qw.incoming_sequence)
1252 //Con_DPrint("Got a stale datagram\n");
1255 count = sequence - (conn->qw.incoming_sequence + 1);
1258 conn->droppedDatagrams += count;
1259 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1260 // If too may packets have been dropped, only write the
1261 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1262 // Because there's no point in writing more than
1263 // these as the netgraph is going to be full anyway.
1264 if (count > NETGRAPH_PACKETS)
1265 count = NETGRAPH_PACKETS;
1268 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1269 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1270 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1271 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1272 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1273 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1276 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1277 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1278 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1279 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1280 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1281 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1282 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1284 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1285 if (net_test.integer)
1287 if (conn->cleartime < host.realtime)
1288 conn->cleartime = host.realtime;
1291 if (reliable_ack == conn->qw.reliable_sequence)
1293 // received, now we will be able to send another reliable message
1294 conn->sendMessageLength = 0;
1295 conn->reliableMessagesReceived++;
1297 conn->qw.incoming_sequence = sequence;
1298 if (conn == cls.netcon)
1299 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1300 conn->qw.incoming_acknowledged = sequence_ack;
1301 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1302 if (reliable_message)
1303 conn->qw.incoming_reliable_sequence ^= 1;
1304 conn->lastMessageTime = host.realtime;
1305 conn->timeout = host.realtime + newtimeout;
1306 conn->unreliableMessagesReceived++;
1307 if (conn == cls.netcon)
1309 SZ_Clear(&cl_message);
1310 SZ_Write(&cl_message, data, (int)length);
1311 MSG_BeginReading(&cl_message);
1315 SZ_Clear(&sv_message);
1316 SZ_Write(&sv_message, data, (int)length);
1317 MSG_BeginReading(&sv_message);
1325 unsigned int sequence;
1330 originallength = (int)length;
1331 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1337 qlength = (unsigned int)BuffBigLong(data);
1338 flags = qlength & ~NETFLAG_LENGTH_MASK;
1339 qlength &= NETFLAG_LENGTH_MASK;
1340 // control packets were already handled
1341 if (!(flags & NETFLAG_CTL) && qlength == length)
1343 sequence = BuffBigLong(data + 4);
1344 conn->packetsReceived++;
1347 if (flags & NETFLAG_UNRELIABLE)
1349 if (sequence >= conn->nq.unreliableReceiveSequence)
1351 if (sequence > conn->nq.unreliableReceiveSequence)
1353 count = sequence - conn->nq.unreliableReceiveSequence;
1354 conn->droppedDatagrams += count;
1355 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1356 // If too may packets have been dropped, only write the
1357 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1358 // Because there's no point in writing more than
1359 // these as the netgraph is going to be full anyway.
1360 if (count > NETGRAPH_PACKETS)
1361 count = NETGRAPH_PACKETS;
1364 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1365 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1366 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1367 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1368 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1369 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1372 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1373 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1374 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1375 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1376 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1377 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1378 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1380 conn->nq.unreliableReceiveSequence = sequence + 1;
1381 conn->lastMessageTime = host.realtime;
1382 conn->timeout = host.realtime + newtimeout;
1383 conn->unreliableMessagesReceived++;
1386 if (conn == cls.netcon)
1388 SZ_Clear(&cl_message);
1389 SZ_Write(&cl_message, data, (int)length);
1390 MSG_BeginReading(&cl_message);
1394 SZ_Clear(&sv_message);
1395 SZ_Write(&sv_message, data, (int)length);
1396 MSG_BeginReading(&sv_message);
1402 // Con_DPrint("Got a stale datagram\n");
1405 else if (flags & NETFLAG_ACK)
1407 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1408 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1410 if (sequence == (conn->nq.sendSequence - 1))
1412 if (sequence == conn->nq.ackSequence)
1414 conn->nq.ackSequence++;
1415 if (conn->nq.ackSequence != conn->nq.sendSequence)
1416 Con_DPrint("ack sequencing error\n");
1417 conn->lastMessageTime = host.realtime;
1418 conn->timeout = host.realtime + newtimeout;
1419 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1421 unsigned int packetLen;
1422 unsigned int dataLen;
1425 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1426 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1428 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1430 dataLen = conn->sendMessageLength;
1435 dataLen = MAX_PACKETFRAGMENT;
1439 packetLen = NET_HEADERSIZE + dataLen;
1441 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1442 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1443 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1445 conn->nq.sendSequence++;
1447 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1448 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1450 conn->lastSendTime = host.realtime;
1451 conn->packetsSent++;
1455 conn->sendMessageLength = 0;
1458 // Con_DPrint("Duplicate ACK received\n");
1461 // Con_DPrint("Stale ACK received\n");
1464 else if (flags & NETFLAG_DATA)
1466 unsigned char temppacket[8];
1467 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1468 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1470 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1472 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1473 StoreBigLong(temppacket + 4, sequence);
1474 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1476 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1477 if (sequence == conn->nq.receiveSequence)
1479 conn->lastMessageTime = host.realtime;
1480 conn->timeout = host.realtime + newtimeout;
1481 conn->nq.receiveSequence++;
1482 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1483 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1484 conn->receiveMessageLength += (int)length;
1486 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1487 "Dropping the message!\n", sequence );
1488 conn->receiveMessageLength = 0;
1491 if (flags & NETFLAG_EOM)
1493 conn->reliableMessagesReceived++;
1494 length = conn->receiveMessageLength;
1495 conn->receiveMessageLength = 0;
1498 if (conn == cls.netcon)
1500 SZ_Clear(&cl_message);
1501 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1502 MSG_BeginReading(&cl_message);
1506 SZ_Clear(&sv_message);
1507 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1508 MSG_BeginReading(&sv_message);
1515 conn->receivedDuplicateCount++;
1523 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1526 cls.connect_trying = false;
1528 M_Update_Return_Reason("");
1530 // Disconnect from the current server or stop demo playback
1531 if(cls.state == ca_connected || cls.demoplayback)
1532 CL_Disconnect(false, NULL);
1533 // allocate a net connection to keep track of things
1534 cls.netcon = NetConn_Open(mysocket, peeraddress);
1535 crypto = &cls.netcon->crypto;
1536 if(cls.crypto.authenticated)
1538 Crypto_FinishInstance(crypto, &cls.crypto);
1539 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1540 crypto->use_aes ? "Encrypted" : "Authenticated",
1541 cls.netcon->address,
1542 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1543 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1544 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1545 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1546 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1547 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1550 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1551 key_dest = key_game;
1555 cls.demonum = -1; // not in the demo loop now
1556 cls.state = ca_connected;
1557 cls.signon = 0; // need all the signon messages before playing
1558 cls.protocol = initialprotocol;
1559 // reset move sequence numbering on this new connection
1560 cls.servermovesequence = 0;
1561 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1562 CL_ForwardToServer("new");
1563 if (cls.protocol == PROTOCOL_QUAKE)
1565 // write a keepalive (clc_nop) as it seems to greatly improve the
1566 // chances of connecting to a netquake server
1568 unsigned char buf[4];
1569 memset(&msg, 0, sizeof(msg));
1571 msg.maxsize = sizeof(buf);
1572 MSG_WriteChar(&msg, clc_nop);
1573 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1577 int NetConn_IsLocalGame(void)
1579 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1585 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1589 serverlist_entry_t *entry = NULL;
1591 // search the cache for this server and update it
1592 for (n = 0;n < serverlist_cachecount;n++) {
1593 entry = &serverlist_cache[ n ];
1594 if (!strcmp(addressstring, entry->info.cname))
1598 if (n == serverlist_cachecount)
1600 // LAN search doesnt require an answer from the master server so we wont
1601 // know the ping nor will it be initialized already...
1604 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1607 if (serverlist_maxcachecount <= serverlist_cachecount)
1609 serverlist_maxcachecount += 64;
1610 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1612 entry = &serverlist_cache[n];
1614 memset(entry, 0, sizeof(*entry));
1615 // store the data the engine cares about (address and ping)
1616 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1617 entry->info.ping = 100000;
1618 entry->querytime = host.realtime;
1619 // if not in the slist menu we should print the server to console
1620 if (serverlist_consoleoutput)
1621 Con_Printf("querying %s\n", addressstring);
1622 ++serverlist_cachecount;
1624 // if this is the first reply from this server, count it as having replied
1625 pingtime = (int)((host.realtime - entry->querytime) * 1000.0 + 0.5);
1626 pingtime = bound(0, pingtime, 9999);
1627 if (entry->query == SQS_REFRESHING) {
1628 entry->info.ping = pingtime;
1629 entry->query = SQS_QUERIED;
1631 // convert to unsigned to catch the -1
1632 // 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]
1633 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1637 // other server info is updated by the caller
1641 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1643 serverlist_entry_t *entry = &serverlist_cache[n];
1644 serverlist_info_t *info = &entry->info;
1645 // update description strings for engine menu and console output
1646 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);
1647 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1649 info->gameversion != gameversion.integer
1652 gameversion_min.integer >= 0 // min/max range set by user/mod?
1653 && gameversion_max.integer >= 0
1654 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1655 && gameversion_max.integer >= info->gameversion
1658 info->mod, info->map);
1659 if (entry->query == SQS_QUERIED)
1661 if(!serverlist_paused)
1662 ServerList_ViewList_Remove(entry);
1664 // if not in the slist menu we should print the server to console (if wanted)
1665 else if( serverlist_consoleoutput )
1666 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1667 // and finally, update the view set
1668 if(!serverlist_paused)
1669 ServerList_ViewList_Insert( entry );
1670 // update the entry's state
1671 serverlist_cache[n].query = SQS_QUERIED;
1674 // returns true, if it's sensible to continue the processing
1675 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qbool isfavorite ) {
1677 serverlist_entry_t *entry;
1679 // ignore the rest of the message if the serverlist is full
1680 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1682 // also ignore it if we have already queried it (other master server response)
1683 for( n = 0 ; n < serverlist_cachecount ; n++ )
1684 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1687 if( n < serverlist_cachecount ) {
1688 // the entry has already been queried once or
1692 if (serverlist_maxcachecount <= n)
1694 serverlist_maxcachecount += 64;
1695 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1698 entry = &serverlist_cache[n];
1700 memset(entry, 0, sizeof(*entry));
1701 entry->protocol = protocol;
1702 // store the data the engine cares about (address and ping)
1703 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1705 entry->info.isfavorite = isfavorite;
1707 // no, then reset the ping right away
1708 entry->info.ping = -1;
1709 // we also want to increase the serverlist_cachecount then
1710 serverlist_cachecount++;
1713 entry->query = SQS_QUERYING;
1718 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qbool isextended)
1721 if (serverlist_consoleoutput)
1722 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1725 char ipstring [128];
1728 if (data[0] == '\\')
1730 unsigned short port = data[5] * 256 + data[6];
1732 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1733 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1735 // move on to next address in packet
1740 else if (data[0] == '/' && isextended && length >= 19)
1742 unsigned short port = data[17] * 256 + data[18];
1750 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1752 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1755 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1756 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1757 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1763 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1764 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1765 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1770 // move on to next address in packet
1776 Con_Print("Error while parsing the server list\n");
1780 if (serverlist_consoleoutput && developer_networking.integer)
1781 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1783 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1789 // begin or resume serverlist queries
1790 serverlist_querysleep = false;
1791 serverlist_querywaittime = host.realtime + 3;
1795 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1799 char *string, addressstring2[128];
1800 char stringbuf[16384];
1801 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1804 char infostringvalue[MAX_INPUTLINE];
1809 // quakeworld ingame packet
1810 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1812 // convert the address to a string incase we need it
1813 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1815 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1817 // received a command string - strip off the packaging and put it
1818 // into our string buffer with NULL termination
1821 length = min(length, (int)sizeof(stringbuf) - 1);
1822 memcpy(stringbuf, data, length);
1823 stringbuf[length] = 0;
1826 if (developer_networking.integer)
1828 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1829 Com_HexDumpToConsole(data, length);
1832 sendlength = sizeof(senddata) - 4;
1833 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1835 case CRYPTO_NOMATCH:
1841 memcpy(senddata, "\377\377\377\377", 4);
1842 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1845 case CRYPTO_DISCARD:
1848 memcpy(senddata, "\377\377\377\377", 4);
1849 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1853 case CRYPTO_REPLACE:
1854 string = senddata+4;
1855 length = (int)sendlength;
1859 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1862 for (j = 0;j < MAX_RCONS;j++)
1864 // note: this value from i is used outside the loop too...
1865 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1866 if(cls.rcon_commands[i][0])
1867 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1876 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1877 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1879 e = strchr(rcon_password.string, ' ');
1880 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1882 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1886 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1887 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1888 cls.rcon_commands[i][0] = 0;
1891 for (k = 0;k < MAX_RCONS;k++)
1892 if(cls.rcon_commands[k][0])
1893 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1898 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1899 // extend the timeout on other requests as we asked for a challenge
1900 for (l = 0;l < MAX_RCONS;l++)
1901 if(cls.rcon_commands[l][0])
1902 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1903 cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
1906 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1910 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1912 // darkplaces or quake3
1913 char protocolnames[1400];
1914 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1915 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1916 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1919 Protocol_Names(protocolnames, sizeof(protocolnames));
1921 M_Update_Return_Reason("Got challenge response");
1923 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1924 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1925 // TODO: add userinfo stuff here instead of using NQ commands?
1926 memcpy(senddata, "\377\377\377\377", 4);
1927 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1928 NetConn_WriteString(mysocket, senddata, peeraddress);
1931 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1933 // darkplaces or quake3
1934 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1935 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1939 M_Update_Return_Reason("Accepted");
1941 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1944 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1946 char rejectreason[128];
1947 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1948 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1951 cls.connect_trying = false;
1953 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1954 memcpy(rejectreason, string, length);
1955 rejectreason[length] = 0;
1957 M_Update_Return_Reason(rejectreason);
1962 if(key_dest != key_game)
1964 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1966 serverlist_info_t *info;
1971 // search the cache for this server and update it
1972 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1976 info = &serverlist_cache[n].info;
1981 info->qcstatus[0] = 0;
1982 info->players[0] = 0;
1983 info->protocol = -1;
1984 info->numplayers = 0;
1986 info->maxplayers = 0;
1987 info->gameversion = 0;
1989 p = strchr(string, '\n');
1992 *p = 0; // cut off the string there
1996 Con_Printf("statusResponse without players block?\n");
1998 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1999 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2000 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2001 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2002 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2003 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2004 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2005 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2006 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2007 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2008 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2009 info->numhumans = info->numplayers - max(0, info->numbots);
2010 info->freeslots = info->maxplayers - info->numplayers;
2012 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2016 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2018 serverlist_info_t *info;
2022 // search the cache for this server and update it
2023 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2027 info = &serverlist_cache[n].info;
2032 info->qcstatus[0] = 0;
2033 info->players[0] = 0;
2034 info->protocol = -1;
2035 info->numplayers = 0;
2037 info->maxplayers = 0;
2038 info->gameversion = 0;
2040 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2041 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2042 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2043 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2044 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2045 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2046 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2047 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2048 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2049 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2050 info->numhumans = info->numplayers - max(0, info->numbots);
2051 info->freeslots = info->maxplayers - info->numplayers;
2053 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2057 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2059 // Extract the IP addresses
2062 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2065 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2067 // Extract the IP addresses
2070 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2073 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2075 // Extract the IP addresses
2079 if (serverlist_consoleoutput)
2080 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2081 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2083 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2084 if (serverlist_consoleoutput && developer_networking.integer)
2085 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2087 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2091 // move on to next address in packet
2095 // begin or resume serverlist queries
2096 serverlist_querysleep = false;
2097 serverlist_querywaittime = host.realtime + 3;
2102 if (!strncmp(string, "extResponse ", 12))
2104 ++cl_net_extresponse_count;
2105 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2106 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2107 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2108 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2111 if (!strncmp(string, "ping", 4))
2113 if (developer_extra.integer)
2114 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2115 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2118 if (!strncmp(string, "ack", 3))
2120 // QuakeWorld compatibility
2121 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2123 // challenge message
2124 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2125 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2128 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2130 M_Update_Return_Reason("Got QuakeWorld challenge response");
2132 cls.qw_qport = qport.integer;
2133 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2134 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2135 memcpy(senddata, "\377\377\377\377", 4);
2136 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);
2137 NetConn_WriteString(mysocket, senddata, peeraddress);
2140 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2143 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2144 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2148 M_Update_Return_Reason("QuakeWorld Accepted");
2150 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2153 if (length > 2 && !memcmp(string, "n\\", 2))
2156 serverlist_info_t *info;
2160 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2161 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2164 // search the cache for this server and update it
2165 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2169 info = &serverlist_cache[n].info;
2170 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2171 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2172 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2173 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2175 info->numplayers = 0; // updated below
2176 info->numhumans = 0; // updated below
2177 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2178 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2180 // count active players on server
2181 // (we could gather more info, but we're just after the number)
2182 s = strchr(string, '\n');
2186 while (s < string + length)
2188 for (;s < string + length && *s != '\n';s++)
2190 if (s >= string + length)
2198 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2202 if (string[0] == 'n')
2204 // qw print command, used by rcon replies too
2205 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2206 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2209 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2211 // we may not have liked the packet, but it was a command packet, so
2212 // we're done processing this packet now
2215 // quakeworld ingame packet
2216 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2219 CL_ParseServerMessage();
2222 // netquake control packets, supported for compatibility only
2223 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2227 serverlist_info_t *info;
2232 SZ_Clear(&cl_message);
2233 SZ_Write(&cl_message, data, length);
2234 MSG_BeginReading(&cl_message);
2235 c = MSG_ReadByte(&cl_message);
2239 if (developer_extra.integer)
2240 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2241 if (cls.connect_trying)
2243 lhnetaddress_t clientportaddress;
2244 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2245 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2248 clientportaddress = *peeraddress;
2249 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2250 // extra ProQuake stuff
2252 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2254 cls.proquake_servermod = 0;
2256 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2258 cls.proquake_serverversion = 0;
2260 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2262 cls.proquake_serverflags = 0;
2263 if (cls.proquake_servermod == 1)
2264 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2265 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2266 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2268 M_Update_Return_Reason("Accepted");
2270 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2274 if (developer_extra.integer) {
2275 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2278 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2280 cls.connect_trying = false;
2282 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2285 case CCREP_SERVER_INFO:
2286 if (developer_extra.integer)
2287 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2289 // LadyHavoc: because the quake server may report weird addresses
2290 // we just ignore it and keep the real address
2291 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2292 // search the cache for this server and update it
2293 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2297 info = &serverlist_cache[n].info;
2298 strlcpy(info->game, "Quake", sizeof(info->game));
2299 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2300 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2301 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2302 info->numplayers = MSG_ReadByte(&cl_message);
2303 info->maxplayers = MSG_ReadByte(&cl_message);
2304 info->protocol = MSG_ReadByte(&cl_message);
2306 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2309 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2310 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2311 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2314 if (developer_extra.integer)
2315 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2317 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2319 case CCREP_PLAYER_INFO:
2320 // we got a CCREP_PLAYER_INFO??
2321 //if (developer_extra.integer)
2322 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2324 case CCREP_RULE_INFO:
2325 // we got a CCREP_RULE_INFO??
2326 //if (developer_extra.integer)
2327 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2332 SZ_Clear(&cl_message);
2333 // we may not have liked the packet, but it was a valid control
2334 // packet, so we're done processing this packet now
2338 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2339 CL_ParseServerMessage();
2344 void NetConn_QueryQueueFrame(void)
2350 static double querycounter = 0;
2352 if(!net_slist_pause.integer && serverlist_paused)
2353 ServerList_RebuildViewList();
2354 serverlist_paused = net_slist_pause.integer != 0;
2356 if (serverlist_querysleep)
2359 // apply a cool down time after master server replies,
2360 // to avoid messing up the ping times on the servers
2361 if (serverlist_querywaittime > host.realtime)
2364 // each time querycounter reaches 1.0 issue a query
2365 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2366 maxqueries = (int)querycounter;
2367 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2368 querycounter -= maxqueries;
2370 if( maxqueries == 0 ) {
2374 // scan serverlist and issue queries as needed
2375 serverlist_querysleep = true;
2377 timeouttime = host.realtime - net_slist_timeout.value;
2378 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2380 serverlist_entry_t *entry = &serverlist_cache[ index ];
2381 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2386 serverlist_querysleep = false;
2387 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2392 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2394 lhnetaddress_t address;
2397 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2398 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2400 for (socket = 0; socket < cl_numsockets ; socket++)
2401 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2405 for (socket = 0; socket < cl_numsockets ; socket++)
2406 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2409 // update the entry fields
2410 entry->querytime = host.realtime;
2411 entry->querycounter++;
2413 // if not in the slist menu we should print the server to console
2414 if (serverlist_consoleoutput)
2415 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2421 // have we tried to refresh this server?
2422 if( entry->query == SQS_REFRESHING ) {
2423 // yes, so update the reply count (since its not responding anymore)
2425 if(!serverlist_paused)
2426 ServerList_ViewList_Remove(entry);
2428 entry->query = SQS_TIMEDOUT;
2434 void NetConn_ClientFrame(void)
2437 lhnetaddress_t peeraddress;
2438 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2439 NetConn_UpdateSockets();
2440 if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2443 if (cls.connect_remainingtries == 0)
2444 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2446 cls.connect_nextsendtime = host.realtime + 1;
2447 cls.connect_remainingtries--;
2448 if (cls.connect_remainingtries <= -10)
2450 cls.connect_trying = false;
2452 M_Update_Return_Reason("Connect: Failed");
2456 // try challenge first (newer DP server or QW)
2457 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2458 // then try netquake as a fallback (old server, or netquake)
2459 SZ_Clear(&cl_message);
2460 // save space for the header, filled in later
2461 MSG_WriteLong(&cl_message, 0);
2462 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2463 MSG_WriteString(&cl_message, "QUAKE");
2464 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2465 // extended proquake stuff
2466 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2467 // this version matches ProQuake 3.40, the first version to support
2468 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2469 // higher clients, so we pretend we are that version...
2470 MSG_WriteByte(&cl_message, 34); // version * 10
2471 MSG_WriteByte(&cl_message, 0); // flags
2472 MSG_WriteLong(&cl_message, 0); // password
2473 // write the packetsize now...
2474 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2475 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2476 SZ_Clear(&cl_message);
2478 for (i = 0;i < cl_numsockets;i++)
2480 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2482 // R_TimeReport("clientreadnetwork");
2483 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2484 // R_TimeReport("clientparsepacket");
2488 NetConn_QueryQueueFrame();
2490 if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2491 CL_Disconnect(true, "Connection timed out");
2494 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2498 for (i = 0;i < bufferlength - 1;i++)
2502 c = rand () % (127 - 33) + 33;
2503 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2509 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2510 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2512 prvm_prog_t *prog = SVVM_prog;
2514 unsigned int nb_clients = 0, nb_bots = 0, i;
2517 const char *crypto_idstring;
2518 const char *worldstatusstr;
2520 // How many clients are there?
2521 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2523 if (svs.clients[i].active)
2526 if (!svs.clients[i].netconnection)
2532 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2533 if(worldstatusstr && *worldstatusstr)
2538 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2539 if(*q != '\\' && *q != '\n')
2544 /// \TODO: we should add more information for the full status string
2545 crypto_idstring = Crypto_GetInfoResponseDataString();
2546 length = dpsnprintf(out_msg, out_size,
2547 "\377\377\377\377%s\x0A"
2548 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2549 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2554 fullstatus ? "statusResponse" : "infoResponse",
2555 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2556 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2557 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2558 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2559 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2560 fullstatus ? "\n" : "");
2562 // Make sure it fits in the buffer
2572 savelength = length;
2574 ptr = out_msg + length;
2575 left = (int)out_size - length;
2577 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2579 client_t *client = &svs.clients[i];
2582 int nameind, cleanind, pingvalue;
2584 char cleanname [sizeof(client->name)];
2585 const char *statusstr;
2588 // Remove all characters '"' and '\' in the player name
2593 curchar = client->name[nameind++];
2594 if (curchar != '"' && curchar != '\\')
2596 cleanname[cleanind++] = curchar;
2597 if (cleanind == sizeof(cleanname) - 1)
2600 } while (curchar != '\0');
2601 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2603 pingvalue = (int)(client->ping * 1000.0f);
2604 if(client->netconnection)
2605 pingvalue = bound(1, pingvalue, 9999);
2610 ed = PRVM_EDICT_NUM(i + 1);
2611 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2612 if(statusstr && *statusstr)
2617 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2618 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2623 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2625 if(client->frags == -666) // spectator
2626 strlcpy(teambuf, " 0", sizeof(teambuf));
2627 else if(client->colors == 0x44) // red team
2628 strlcpy(teambuf, " 1", sizeof(teambuf));
2629 else if(client->colors == 0xDD) // blue team
2630 strlcpy(teambuf, " 2", sizeof(teambuf));
2631 else if(client->colors == 0xCC) // yellow team
2632 strlcpy(teambuf, " 3", sizeof(teambuf));
2633 else if(client->colors == 0x99) // pink team
2634 strlcpy(teambuf, " 4", sizeof(teambuf));
2636 strlcpy(teambuf, " 0", sizeof(teambuf));
2641 // note: team number is inserted according to SoF2 protocol
2643 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2649 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2658 // turn it into an infoResponse!
2659 out_msg[savelength] = 0;
2660 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2661 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2676 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2678 size_t floodslotnum, bestfloodslotnum;
2679 double bestfloodtime;
2680 lhnetaddress_t noportpeeraddress;
2681 // see if this is a connect flood
2682 noportpeeraddress = *peeraddress;
2683 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2684 bestfloodslotnum = 0;
2685 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2686 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2688 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2690 bestfloodtime = floodlist[floodslotnum].lasttime;
2691 bestfloodslotnum = floodslotnum;
2693 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2695 // this address matches an ongoing flood address
2696 if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2700 // renew the ban on this address so it does not expire
2701 // until the flood has subsided
2702 floodlist[floodslotnum].lasttime = host.realtime;
2704 //Con_Printf("Flood detected!\n");
2707 // the flood appears to have subsided, so allow this
2708 bestfloodslotnum = floodslotnum; // reuse the same slot
2712 // begin a new timeout on this address
2713 floodlist[bestfloodslotnum].address = noportpeeraddress;
2714 floodlist[bestfloodslotnum].lasttime = host.realtime;
2715 //Con_Printf("Flood detection initiated!\n");
2719 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2721 size_t floodslotnum;
2722 lhnetaddress_t noportpeeraddress;
2723 // see if this is a connect flood
2724 noportpeeraddress = *peeraddress;
2725 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2726 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2728 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2730 // this address matches an ongoing flood address
2732 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2733 floodlist[floodslotnum].lasttime = 0;
2734 //Con_Printf("Flood cleared!\n");
2739 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2741 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2747 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2751 t1 = (long) time(NULL);
2752 t2 = strtol(s, NULL, 0);
2753 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2756 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2759 return !memcmp(mdfourbuf, hash, 16);
2762 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2768 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2772 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2775 // validate the challenge
2776 for (i = 0;i < MAX_CHALLENGES;i++)
2777 if(challenges[i].time > 0)
2778 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2780 // if the challenge is not recognized, drop the packet
2781 if (i == MAX_CHALLENGES)
2784 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2787 if(memcmp(mdfourbuf, hash, 16))
2790 // unmark challenge to prevent replay attacks
2791 challenges[i].time = 0;
2796 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2799 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2803 return !strcmp(password, hash);
2806 /// returns a string describing the user level, or NULL for auth failure
2807 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)
2809 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2810 static char buf[MAX_INPUTLINE];
2812 qbool restricted = false;
2813 qbool have_usernames = false;
2814 static char vabuf[1024];
2816 userpass_start = rcon_password.string;
2817 while((userpass_end = strchr(userpass_start, ' ')))
2819 have_usernames = true;
2820 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2821 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2822 if(comparator(peeraddress, buf, password, cs, cslen))
2824 userpass_start = userpass_end + 1;
2826 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2828 userpass_end = userpass_start + strlen(userpass_start);
2829 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2834 have_usernames = false;
2835 userpass_start = rcon_restricted_password.string;
2836 while((userpass_end = strchr(userpass_start, ' ')))
2838 have_usernames = true;
2839 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2840 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2841 if(comparator(peeraddress, buf, password, cs, cslen))
2843 userpass_start = userpass_end + 1;
2845 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2847 userpass_end = userpass_start + strlen(userpass_start);
2848 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2852 return NULL; // DENIED
2855 for(text = s; text != endpos; ++text)
2856 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2857 return NULL; // block possible exploits against the parser/alias expansion
2861 size_t l = strlen(s);
2864 hasquotes = (strchr(s, '"') != NULL);
2865 // sorry, we can't allow these substrings in wildcard expressions,
2866 // as they can mess with the argument counts
2867 text = rcon_restricted_commands.string;
2868 while(COM_ParseToken_Console(&text))
2870 // com_token now contains a pattern to check for...
2871 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2874 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2877 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2879 if(!strcmp(com_token, s))
2882 else // single-arg expression? must match the beginning of the command
2884 if(!strcmp(com_token, s))
2886 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))