2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Ashley Rose Hale (LadyHavoc)
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // for secure rcon authentication
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
35 // note this defaults on for dedicated servers, off for listen servers
36 cvar_t sv_public = {CF_SERVER, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
37 cvar_t sv_public_rejectreason = {CF_SERVER, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CF_SERVER | CF_ARCHIVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
39 extern cvar_t sv_status_privacy;
41 static cvar_t sv_masters [] =
43 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master1", "", "user-chosen master server 1"},
44 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master2", "", "user-chosen master server 2"},
45 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master3", "", "user-chosen master server 3"},
46 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master4", "", "user-chosen master server 4"},
47 {CF_CLIENT | CF_SERVER, "sv_masterextra1", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 1 (admin: Willis)"},
48 {CF_CLIENT | CF_SERVER, "sv_masterextra2", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 2 (admin: tChr)"},
49 {CF_CLIENT | CF_SERVER, "sv_masterextra3", "dpm.dpmaster.org:27777", "dpm.dpmaster.org - default master server 3 (admin: gazby/soylent_cow)"},
54 static cvar_t sv_qwmasters [] =
56 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
57 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
58 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
59 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
60 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
61 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
62 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
63 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
68 static double nextheartbeattime = 0;
72 static unsigned char cl_message_buf[NET_MAXMESSAGE];
73 static unsigned char sv_message_buf[NET_MAXMESSAGE];
74 char cl_readstring[MAX_INPUTLINE];
75 char sv_readstring[MAX_INPUTLINE];
77 cvar_t net_test = {CF_CLIENT | CF_SERVER, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
78 cvar_t net_usesizelimit = {CF_SERVER, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
79 cvar_t net_burstreserve = {CF_SERVER, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
80 cvar_t net_messagetimeout = {CF_CLIENT | CF_SERVER, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
81 cvar_t net_connecttimeout = {CF_CLIENT | CF_SERVER, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
82 cvar_t net_connectfloodblockingtimeout = {CF_SERVER, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
83 cvar_t net_challengefloodblockingtimeout = {CF_SERVER, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
84 cvar_t net_getstatusfloodblockingtimeout = {CF_SERVER, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
85 cvar_t net_sourceaddresscheck = {CF_CLIENT, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
86 cvar_t hostname = {CF_SERVER | CF_ARCHIVE, "hostname", "UNNAMED", "server message to show in server browser"};
87 cvar_t developer_networking = {CF_CLIENT | CF_SERVER, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
89 cvar_t net_fakelag = {CF_CLIENT, "net_fakelag","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
90 static cvar_t net_fakeloss_send = {CF_CLIENT, "net_fakeloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
91 static cvar_t net_fakeloss_receive = {CF_CLIENT, "net_fakeloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
92 static cvar_t net_slist_queriespersecond = {CF_CLIENT, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
93 static cvar_t net_slist_queriesperframe = {CF_CLIENT, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
94 static cvar_t net_slist_timeout = {CF_CLIENT, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
95 static cvar_t net_slist_pause = {CF_CLIENT, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
96 static cvar_t net_slist_maxtries = {CF_CLIENT, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
97 static cvar_t net_slist_favorites = {CF_CLIENT | CF_ARCHIVE, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
98 static cvar_t net_tos_dscp = {CF_CLIENT | CF_ARCHIVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
99 static cvar_t gameversion = {CF_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
100 static cvar_t gameversion_min = {CF_CLIENT | CF_SERVER, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
101 static cvar_t gameversion_max = {CF_CLIENT | CF_SERVER, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
102 static cvar_t rcon_restricted_password = {CF_SERVER | CF_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
103 static cvar_t rcon_restricted_commands = {CF_SERVER, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
104 static cvar_t rcon_secure_maxdiff = {CF_SERVER, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
105 extern cvar_t rcon_secure;
106 extern cvar_t rcon_secure_challengetimeout;
108 double masterquerytime = -1000;
109 int masterquerycount = 0;
110 int masterreplycount = 0;
111 int serverquerycount = 0;
112 int serverreplycount = 0;
114 challenge_t challenges[MAX_CHALLENGES];
117 /// this is only false if there are still servers left to query
118 static qbool serverlist_querysleep = true;
119 static qbool serverlist_paused = false;
120 /// this is pushed a second or two ahead of realtime whenever a master server
121 /// reply is received, to avoid issuing queries while master replies are still
122 /// flooding in (which would make a mess of the ping times)
123 static double serverlist_querywaittime = 0;
126 static int cl_numsockets;
127 static lhnetsocket_t *cl_sockets[16];
128 static int sv_numsockets;
129 static lhnetsocket_t *sv_sockets[16];
131 netconn_t *netconn_list = NULL;
132 mempool_t *netconn_mempool = NULL;
133 void *netconn_mutex = NULL;
135 cvar_t cl_netport = {CF_CLIENT, "cl_port", "0", "forces client to use chosen port number if not 0"};
136 cvar_t sv_netport = {CF_SERVER, "port", "26000", "server port for players to connect to"};
137 cvar_t net_address = {CF_CLIENT | CF_SERVER, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
138 cvar_t net_address_ipv6 = {CF_CLIENT | CF_SERVER, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
140 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
141 int cl_net_extresponse_count = 0;
142 int cl_net_extresponse_last = 0;
144 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
145 int sv_net_extresponse_count = 0;
146 int sv_net_extresponse_last = 0;
149 // ServerList interface
150 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
151 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
153 serverlist_infofield_t serverlist_sortbyfield;
154 int serverlist_sortflags;
156 int serverlist_viewcount = 0;
157 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
159 int serverlist_maxcachecount = 0;
160 int serverlist_cachecount = 0;
161 serverlist_entry_t *serverlist_cache = NULL;
163 qbool serverlist_consoleoutput;
165 static int nFavorites = 0;
166 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
167 static int nFavorites_idfp = 0;
168 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
170 void NetConn_UpdateFavorites_c(cvar_t *var)
176 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
178 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
179 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
180 // (if v6 address contains port, it must start with '[')
182 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
187 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
193 /// helper function to insert a value into the viewset
194 /// spare entries will be removed
195 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
198 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
199 i = serverlist_viewcount++;
201 i = SERVERLIST_VIEWLISTSIZE - 1;
204 for( ; i > index ; i-- )
205 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
207 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
210 /// we suppose serverlist_viewcount to be valid, ie > 0
211 static void _ServerList_ViewList_Helper_Remove( int index )
213 serverlist_viewcount--;
214 for( ; index < serverlist_viewcount ; index++ )
215 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
218 /// \returns true if A should be inserted before B
219 static qbool _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
221 int result = 0; // > 0 if for numbers A > B and for text if A < B
223 if( serverlist_sortflags & SLSF_CATEGORIES )
225 result = A->info.category - B->info.category;
230 if( serverlist_sortflags & SLSF_FAVORITES )
232 if(A->info.isfavorite != B->info.isfavorite)
233 return A->info.isfavorite;
236 switch( serverlist_sortbyfield ) {
238 result = A->info.ping - B->info.ping;
240 case SLIF_MAXPLAYERS:
241 result = A->info.maxplayers - B->info.maxplayers;
243 case SLIF_NUMPLAYERS:
244 result = A->info.numplayers - B->info.numplayers;
247 result = A->info.numbots - B->info.numbots;
250 result = A->info.numhumans - B->info.numhumans;
253 result = A->info.freeslots - B->info.freeslots;
256 result = A->info.protocol - B->info.protocol;
259 result = strcmp( B->info.cname, A->info.cname );
262 result = strcasecmp( B->info.game, A->info.game );
265 result = strcasecmp( B->info.map, A->info.map );
268 result = strcasecmp( B->info.mod, A->info.mod );
271 result = strcasecmp( B->info.name, A->info.name );
274 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
277 result = A->info.category - B->info.category;
279 case SLIF_ISFAVORITE:
280 result = !!B->info.isfavorite - !!A->info.isfavorite;
283 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
289 if( serverlist_sortflags & SLSF_DESCENDING )
295 // if the chosen sort key is identical, sort by index
296 // (makes this a stable sort, so that later replies from servers won't
297 // shuffle the servers around when they have the same ping)
301 static qbool _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
303 // This should actually be done with some intermediate and end-of-function return
315 case SLMO_GREATEREQUAL:
317 case SLMO_NOTCONTAIN:
318 case SLMO_STARTSWITH:
319 case SLMO_NOTSTARTSWITH:
322 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
327 static qbool _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
330 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
331 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
332 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
333 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
335 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
336 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
339 // Same here, also using an intermediate & final return would be more appropriate
343 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
344 case SLMO_NOTCONTAIN:
345 return !*bufferB || !strstr( bufferA, bufferB );
346 case SLMO_STARTSWITH:
347 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
348 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
349 case SLMO_NOTSTARTSWITH:
350 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
352 return strcmp( bufferA, bufferB ) < 0;
354 return strcmp( bufferA, bufferB ) <= 0;
356 return strcmp( bufferA, bufferB ) == 0;
358 return strcmp( bufferA, bufferB ) > 0;
360 return strcmp( bufferA, bufferB ) != 0;
361 case SLMO_GREATEREQUAL:
362 return strcmp( bufferA, bufferB ) >= 0;
364 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
369 static qbool _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
371 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
373 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
375 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
377 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
379 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
381 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
383 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
385 if( *mask->info.cname
386 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
389 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
392 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
395 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
398 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
400 if( *mask->info.qcstatus
401 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
403 if( *mask->info.players
404 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
406 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
408 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
413 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
415 int start, end, mid, i;
418 // reject incompatible servers
420 entry->info.gameversion != gameversion.integer
423 gameversion_min.integer >= 0 // min/max range set by user/mod?
424 && gameversion_max.integer >= 0
425 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
426 && gameversion_max.integer >= entry->info.gameversion
431 // refresh the "favorite" status
432 entry->info.isfavorite = false;
433 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
435 char idfp[FP64_SIZE+1];
436 for(i = 0; i < nFavorites; ++i)
438 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
440 entry->info.isfavorite = true;
444 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
446 for(i = 0; i < nFavorites_idfp; ++i)
448 if(!strcmp(idfp, favorites_idfp[i]))
450 entry->info.isfavorite = true;
457 // refresh the "category"
458 entry->info.category = MR_GetServerListEntryCategory(entry);
460 // FIXME: change this to be more readable (...)
461 // now check whether it passes through the masks
462 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
463 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
466 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
467 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
469 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
472 if( !serverlist_viewcount ) {
473 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
476 // ok, insert it, we just need to find out where exactly:
479 // check whether to insert it as new first item
480 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
481 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
483 } // check whether to insert it as new last item
484 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
485 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
489 end = serverlist_viewcount - 1;
490 while( end > start + 1 )
492 mid = (start + end) / 2;
493 // test the item that lies in the middle between start and end
494 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
495 // the item has to be in the upper half
498 // the item has to be in the lower half
501 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
504 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
507 for( i = 0; i < serverlist_viewcount; i++ )
509 if (ServerList_GetViewEntry(i) == entry)
511 _ServerList_ViewList_Helper_Remove(i);
517 void ServerList_RebuildViewList(void)
521 serverlist_viewcount = 0;
522 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
523 serverlist_entry_t *entry = &serverlist_cache[i];
524 // also display entries that are currently being refreshed [11/8/2007 Black]
525 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
526 ServerList_ViewList_Insert( entry );
530 void ServerList_ResetMasks(void)
534 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
535 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
536 // numbots needs to be compared to -1 to always succeed
537 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
538 serverlist_andmasks[i].info.numbots = -1;
539 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
540 serverlist_ormasks[i].info.numbots = -1;
543 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
546 int numplayers = 0, maxplayers = 0;
547 for (i = 0;i < serverlist_cachecount;i++)
549 if (serverlist_cache[i].query == SQS_QUERIED)
551 numplayers += serverlist_cache[i].info.numhumans;
552 maxplayers += serverlist_cache[i].info.maxplayers;
555 *numplayerspointer = numplayers;
556 *maxplayerspointer = maxplayers;
560 static void _ServerList_Test(void)
563 if (serverlist_maxcachecount <= 1024)
565 serverlist_maxcachecount = 1024;
566 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
568 for( i = 0 ; i < 1024 ; i++ ) {
569 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
570 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
571 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
572 serverlist_cache[serverlist_cachecount].finished = true;
573 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 );
574 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
575 serverlist_cachecount++;
580 void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool consoleoutput)
582 masterquerytime = host.realtime;
583 masterquerycount = 0;
584 masterreplycount = 0;
587 serverquerycount = 0;
588 serverreplycount = 0;
589 serverlist_cachecount = 0;
590 serverlist_viewcount = 0;
591 serverlist_maxcachecount = 0;
592 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
596 // refresh all entries
598 for (n = 0; n < serverlist_cachecount; ++n)
600 serverlist_entry_t *entry = &serverlist_cache[n];
601 entry->query = SQS_REFRESHING;
602 entry->querycounter = 0;
605 serverlist_consoleoutput = consoleoutput;
607 //_ServerList_Test();
609 NetConn_QueryMasters(querydp, queryqw);
615 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
619 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
620 Thread_LockMutex(netconn_mutex);
621 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
622 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
623 Thread_UnlockMutex(netconn_mutex);
626 if (net_fakeloss_receive.integer)
627 for (i = 0;i < cl_numsockets;i++)
628 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_receive.integer)
630 if (developer_networking.integer)
632 char addressstring[128], addressstring2[128];
633 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
636 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
637 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
638 Com_HexDumpToConsole((unsigned char *)data, length);
641 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
646 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
650 if (net_fakeloss_send.integer)
651 for (i = 0;i < cl_numsockets;i++)
652 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_send.integer)
654 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
655 Thread_LockMutex(netconn_mutex);
656 ret = LHNET_Write(mysocket, data, length, peeraddress);
657 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
658 Thread_UnlockMutex(netconn_mutex);
659 if (developer_networking.integer)
661 char addressstring[128], addressstring2[128];
662 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
663 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
664 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)");
665 Com_HexDumpToConsole((unsigned char *)data, length);
670 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
672 // note this does not include the trailing NULL because we add that in the parser
673 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
676 qbool NetConn_CanSend(netconn_t *conn)
678 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
679 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = host.realtime;
680 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
681 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
682 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
683 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
684 if (host.realtime > conn->cleartime)
688 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
693 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
695 double bursttime = burstsize / (double)rate;
697 // delay later packets to obey rate limit
698 if (*cleartime < host.realtime - bursttime)
699 *cleartime = host.realtime - bursttime;
700 *cleartime = *cleartime + len / (double)rate;
702 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
703 if (net_test.integer)
705 if (*cleartime < host.realtime)
706 *cleartime = host.realtime;
710 static int NetConn_AddCryptoFlag(crypto_t *crypto)
712 // HACK: if an encrypted connection is used, randomly set some unused
713 // flags. When AES encryption is enabled, that will make resends differ
714 // from the original, so that e.g. substring filters in a router/IPS
715 // are unlikely to match a second time. See also "startkeylogger".
717 if (crypto->authenticated)
719 // Let's always set at least one of the bits.
720 int r = rand() % 7 + 1;
722 flag |= NETFLAG_CRYPTO0;
724 flag |= NETFLAG_CRYPTO1;
726 flag |= NETFLAG_CRYPTO2;
731 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qbool quakesignon_suppressreliables)
734 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
735 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
737 // if this packet was supposedly choked, but we find ourselves sending one
738 // anyway, make sure the size counting starts at zero
739 // (this mostly happens on level changes and disconnects and such)
740 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
741 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
743 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
745 if (protocol == PROTOCOL_QUAKEWORLD)
750 // note that it is ok to send empty messages to the qw server,
751 // otherwise it won't respond to us at all
753 sendreliable = false;
754 // if the remote side dropped the last reliable message, resend it
755 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
757 // if the reliable transmit buffer is empty, copy the current message out
758 if (!conn->sendMessageLength && conn->message.cursize)
760 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
761 conn->sendMessageLength = conn->message.cursize;
762 SZ_Clear(&conn->message); // clear the message buffer
763 conn->qw.reliable_sequence ^= 1;
766 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
767 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
768 // last received unreliable packet number, and last received reliable packet number (0 or 1)
769 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
771 conn->outgoing_unreliable_sequence++;
772 // client sends qport in every packet
773 if (conn == cls.netcon)
775 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
777 // also update cls.qw_outgoing_sequence
778 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
780 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
782 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
786 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
788 // add the reliable message if there is one
791 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
792 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
793 packetLen += conn->sendMessageLength;
794 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
797 // add the unreliable message if possible
798 if (packetLen + data->cursize <= 1400)
800 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
801 memcpy(sendbuffer + packetLen, data->data, data->cursize);
802 packetLen += data->cursize;
805 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
808 conn->unreliableMessagesSent++;
810 totallen += packetLen + 28;
814 unsigned int packetLen;
815 unsigned int dataLen;
820 // if a reliable message fragment has been lost, send it again
821 if (conn->sendMessageLength && (host.realtime - conn->lastSendTime) > 1.0)
823 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
825 dataLen = conn->sendMessageLength;
830 dataLen = MAX_PACKETFRAGMENT;
834 packetLen = NET_HEADERSIZE + dataLen;
836 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
837 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
838 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
840 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
842 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
843 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
845 conn->lastSendTime = host.realtime;
846 conn->packetsReSent++;
849 totallen += (int)sendmelen + 28;
852 // if we have a new reliable message to send, do so
853 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
855 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
857 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
858 conn->message.overflowed = true;
862 if (developer_networking.integer && conn == cls.netcon)
864 Con_Print("client sending reliable message to server:\n");
865 SZ_HexDumpToConsole(&conn->message);
868 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
869 conn->sendMessageLength = conn->message.cursize;
870 SZ_Clear(&conn->message);
872 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
874 dataLen = conn->sendMessageLength;
879 dataLen = MAX_PACKETFRAGMENT;
883 packetLen = NET_HEADERSIZE + dataLen;
885 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
886 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
887 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
889 conn->nq.sendSequence++;
891 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
893 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
895 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
897 conn->lastSendTime = host.realtime;
899 conn->reliableMessagesSent++;
901 totallen += (int)sendmelen + 28;
904 // if we have an unreliable message to send, do so
907 packetLen = NET_HEADERSIZE + data->cursize;
909 if (packetLen > (int)sizeof(sendbuffer))
911 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
915 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
916 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
917 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
919 conn->outgoing_unreliable_sequence++;
921 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
923 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
925 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
928 conn->unreliableMessagesSent++;
930 totallen += (int)sendmelen + 28;
934 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
939 qbool NetConn_HaveClientPorts(void)
941 return !!cl_numsockets;
944 qbool NetConn_HaveServerPorts(void)
946 return !!sv_numsockets;
949 void NetConn_CloseClientPorts(void)
951 for (;cl_numsockets > 0;cl_numsockets--)
952 if (cl_sockets[cl_numsockets - 1])
953 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
956 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
958 lhnetaddress_t address;
961 char addressstring2[1024];
962 if (addressstring && addressstring[0])
963 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
965 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
968 if ((s = LHNET_OpenSocket_Connectionless(&address)))
970 cl_sockets[cl_numsockets++] = s;
971 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
972 if (addresstype != LHNETADDRESSTYPE_LOOP)
973 Con_Printf("Client opened a socket on address %s\n", addressstring2);
977 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
978 Con_Printf(CON_ERROR "Client failed to open a socket on address %s\n", addressstring2);
982 Con_Printf(CON_ERROR "Client unable to parse address %s\n", addressstring);
985 void NetConn_OpenClientPorts(void)
988 NetConn_CloseClientPorts();
990 SV_LockThreadMutex(); // FIXME recursive?
991 Crypto_LoadKeys(); // client sockets
992 SV_UnlockThreadMutex();
994 port = bound(0, cl_netport.integer, 65535);
995 if (cl_netport.integer != port)
996 Cvar_SetValueQuick(&cl_netport, port);
998 Con_Printf("Client using an automatically assigned port\n");
1000 Con_Printf("Client using port %i\n", port);
1001 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
1002 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
1003 #ifndef NOSUPPORTIPV6
1004 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1008 void NetConn_CloseServerPorts(void)
1010 for (;sv_numsockets > 0;sv_numsockets--)
1011 if (sv_sockets[sv_numsockets - 1])
1012 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1015 static qbool NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1017 lhnetaddress_t address;
1020 char addressstring2[1024];
1023 for (port = defaultport; port <= defaultport + range; port++)
1025 if (addressstring && addressstring[0])
1026 success = LHNETADDRESS_FromString(&address, addressstring, port);
1028 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1031 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1033 sv_sockets[sv_numsockets++] = s;
1034 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1035 if (addresstype != LHNETADDRESSTYPE_LOOP)
1036 Con_Printf("Server listening on address %s\n", addressstring2);
1041 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1042 Con_Printf(CON_ERROR "Server failed to open socket on address %s\n", addressstring2);
1047 Con_Printf(CON_ERROR "Server unable to parse address %s\n", addressstring);
1048 // if it cant parse one address, it wont be able to parse another for sure
1055 void NetConn_OpenServerPorts(int opennetports)
1058 NetConn_CloseServerPorts();
1060 SV_LockThreadMutex(); // FIXME recursive?
1061 Crypto_LoadKeys(); // server sockets
1062 SV_UnlockThreadMutex();
1064 NetConn_UpdateSockets();
1065 port = bound(0, sv_netport.integer, 65535);
1068 if (sv_netport.integer != port)
1069 Cvar_SetValueQuick(&sv_netport, port);
1070 if (cls.state != ca_dedicated)
1071 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1074 #ifndef NOSUPPORTIPV6
1075 qbool ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1076 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1078 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1081 if (sv_numsockets == 0)
1082 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1085 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1087 int i, a = LHNETADDRESS_GetAddressType(address);
1088 for (i = 0;i < cl_numsockets;i++)
1089 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1090 return cl_sockets[i];
1094 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1096 int i, a = LHNETADDRESS_GetAddressType(address);
1097 for (i = 0;i < sv_numsockets;i++)
1098 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1099 return sv_sockets[i];
1103 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1106 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1107 conn->mysocket = mysocket;
1108 conn->peeraddress = *peeraddress;
1109 conn->lastMessageTime = host.realtime;
1110 conn->message.data = conn->messagedata;
1111 conn->message.maxsize = sizeof(conn->messagedata);
1112 conn->message.cursize = 0;
1113 // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
1114 // reduce effectiveness of connection request floods
1115 conn->timeout = host.realtime + net_connecttimeout.value;
1116 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1117 conn->next = netconn_list;
1118 netconn_list = conn;
1122 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1123 void NetConn_Close(netconn_t *conn)
1126 // remove connection from list
1128 // allow the client to reconnect immediately
1129 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1131 if (conn == netconn_list)
1132 netconn_list = conn->next;
1135 for (c = netconn_list;c;c = c->next)
1137 if (c->next == conn)
1139 c->next = conn->next;
1143 // not found in list, we'll avoid crashing here...
1151 static int clientport = -1;
1152 static int clientport2 = -1;
1154 // Call on disconnect, during startup, or if cl_port/cl_netport is changed
1155 static void NetConn_CL_UpdateSockets_Callback(cvar_t *var)
1157 if(cls.state != ca_dedicated)
1159 if (clientport2 != var->integer)
1161 clientport2 = var->integer;
1162 if (cls.state == ca_connected)
1163 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1166 if (cls.state == ca_disconnected && clientport != clientport2)
1168 clientport = clientport2;
1169 NetConn_CloseClientPorts();
1171 if (cl_numsockets == 0)
1172 NetConn_OpenClientPorts();
1176 static int hostport = -1;
1178 // Call when port/sv_netport is changed
1179 static void NetConn_sv_netport_Callback(cvar_t *var)
1181 if (hostport != var->integer)
1183 hostport = var->integer;
1185 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1189 void NetConn_UpdateSockets(void)
1193 // TODO add logic to automatically close sockets if needed
1194 LHNET_DefaultDSCP(net_tos_dscp.integer);
1196 for (j = 0;j < MAX_RCONS;j++)
1198 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1199 if(cls.rcon_commands[i][0])
1201 if(host.realtime > cls.rcon_timeout[i])
1204 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1205 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1206 cls.rcon_commands[i][0] = 0;
1214 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1216 int originallength = (int)length;
1217 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1218 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1219 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1223 if (protocol == PROTOCOL_QUAKEWORLD)
1225 unsigned int sequence, sequence_ack;
1226 qbool reliable_ack, reliable_message;
1230 sequence = LittleLong(*((int *)(data + 0)));
1231 sequence_ack = LittleLong(*((int *)(data + 4)));
1235 if (conn != cls.netcon)
1240 // 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?)
1241 //qport = LittleShort(*((int *)(data + 8)));
1246 conn->packetsReceived++;
1247 reliable_message = (sequence >> 31) != 0;
1248 reliable_ack = (sequence_ack >> 31) != 0;
1249 sequence &= ~(1<<31);
1250 sequence_ack &= ~(1<<31);
1251 if (sequence <= conn->qw.incoming_sequence)
1253 //Con_DPrint("Got a stale datagram\n");
1256 count = sequence - (conn->qw.incoming_sequence + 1);
1259 conn->droppedDatagrams += count;
1260 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1261 // If too may packets have been dropped, only write the
1262 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1263 // Because there's no point in writing more than
1264 // these as the netgraph is going to be full anyway.
1265 if (count > NETGRAPH_PACKETS)
1266 count = NETGRAPH_PACKETS;
1269 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1270 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1271 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1272 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1273 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1274 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1277 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1278 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1279 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1280 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1281 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1282 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1283 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1285 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1286 if (net_test.integer)
1288 if (conn->cleartime < host.realtime)
1289 conn->cleartime = host.realtime;
1292 if (reliable_ack == conn->qw.reliable_sequence)
1294 // received, now we will be able to send another reliable message
1295 conn->sendMessageLength = 0;
1296 conn->reliableMessagesReceived++;
1298 conn->qw.incoming_sequence = sequence;
1299 if (conn == cls.netcon)
1300 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1301 conn->qw.incoming_acknowledged = sequence_ack;
1302 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1303 if (reliable_message)
1304 conn->qw.incoming_reliable_sequence ^= 1;
1305 conn->lastMessageTime = host.realtime;
1306 conn->timeout = host.realtime + newtimeout;
1307 conn->unreliableMessagesReceived++;
1308 if (conn == cls.netcon)
1310 SZ_Clear(&cl_message);
1311 SZ_Write(&cl_message, data, (int)length);
1312 MSG_BeginReading(&cl_message);
1316 SZ_Clear(&sv_message);
1317 SZ_Write(&sv_message, data, (int)length);
1318 MSG_BeginReading(&sv_message);
1326 unsigned int sequence;
1331 originallength = (int)length;
1332 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1338 qlength = (unsigned int)BuffBigLong(data);
1339 flags = qlength & ~NETFLAG_LENGTH_MASK;
1340 qlength &= NETFLAG_LENGTH_MASK;
1341 // control packets were already handled
1342 if (!(flags & NETFLAG_CTL) && qlength == length)
1344 sequence = BuffBigLong(data + 4);
1345 conn->packetsReceived++;
1348 if (flags & NETFLAG_UNRELIABLE)
1350 if (sequence >= conn->nq.unreliableReceiveSequence)
1352 if (sequence > conn->nq.unreliableReceiveSequence)
1354 count = sequence - conn->nq.unreliableReceiveSequence;
1355 conn->droppedDatagrams += count;
1356 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1357 // If too may packets have been dropped, only write the
1358 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1359 // Because there's no point in writing more than
1360 // these as the netgraph is going to be full anyway.
1361 if (count > NETGRAPH_PACKETS)
1362 count = NETGRAPH_PACKETS;
1365 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1366 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1367 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1368 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1369 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1370 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1373 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1374 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1375 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1376 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1377 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1378 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1379 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1381 conn->nq.unreliableReceiveSequence = sequence + 1;
1382 conn->lastMessageTime = host.realtime;
1383 conn->timeout = host.realtime + newtimeout;
1384 conn->unreliableMessagesReceived++;
1387 if (conn == cls.netcon)
1389 SZ_Clear(&cl_message);
1390 SZ_Write(&cl_message, data, (int)length);
1391 MSG_BeginReading(&cl_message);
1395 SZ_Clear(&sv_message);
1396 SZ_Write(&sv_message, data, (int)length);
1397 MSG_BeginReading(&sv_message);
1403 // Con_DPrint("Got a stale datagram\n");
1406 else if (flags & NETFLAG_ACK)
1408 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1409 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1411 if (sequence == (conn->nq.sendSequence - 1))
1413 if (sequence == conn->nq.ackSequence)
1415 conn->nq.ackSequence++;
1416 if (conn->nq.ackSequence != conn->nq.sendSequence)
1417 Con_DPrint("ack sequencing error\n");
1418 conn->lastMessageTime = host.realtime;
1419 conn->timeout = host.realtime + newtimeout;
1420 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1422 unsigned int packetLen;
1423 unsigned int dataLen;
1426 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1427 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1429 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1431 dataLen = conn->sendMessageLength;
1436 dataLen = MAX_PACKETFRAGMENT;
1440 packetLen = NET_HEADERSIZE + dataLen;
1442 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1443 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1444 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1446 conn->nq.sendSequence++;
1448 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1449 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1451 conn->lastSendTime = host.realtime;
1452 conn->packetsSent++;
1456 conn->sendMessageLength = 0;
1459 // Con_DPrint("Duplicate ACK received\n");
1462 // Con_DPrint("Stale ACK received\n");
1465 else if (flags & NETFLAG_DATA)
1467 unsigned char temppacket[8];
1468 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1469 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1471 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1473 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1474 StoreBigLong(temppacket + 4, sequence);
1475 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1477 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1478 if (sequence == conn->nq.receiveSequence)
1480 conn->lastMessageTime = host.realtime;
1481 conn->timeout = host.realtime + newtimeout;
1482 conn->nq.receiveSequence++;
1483 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1484 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1485 conn->receiveMessageLength += (int)length;
1487 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1488 "Dropping the message!\n", sequence );
1489 conn->receiveMessageLength = 0;
1492 if (flags & NETFLAG_EOM)
1494 conn->reliableMessagesReceived++;
1495 length = conn->receiveMessageLength;
1496 conn->receiveMessageLength = 0;
1499 if (conn == cls.netcon)
1501 SZ_Clear(&cl_message);
1502 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1503 MSG_BeginReading(&cl_message);
1507 SZ_Clear(&sv_message);
1508 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1509 MSG_BeginReading(&sv_message);
1516 conn->receivedDuplicateCount++;
1524 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1527 cls.connect_trying = false;
1529 M_Update_Return_Reason("");
1531 // Disconnect from the current server or stop demo playback
1532 if(cls.state == ca_connected || cls.demoplayback)
1534 // allocate a net connection to keep track of things
1535 cls.netcon = NetConn_Open(mysocket, peeraddress);
1536 crypto = &cls.netcon->crypto;
1537 if(cls.crypto.authenticated)
1539 Crypto_FinishInstance(crypto, &cls.crypto);
1540 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1541 crypto->use_aes ? "Encrypted" : "Authenticated",
1542 cls.netcon->address,
1543 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1544 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1545 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1546 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1547 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1548 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1551 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1552 key_dest = key_game;
1556 cls.demonum = -1; // not in the demo loop now
1557 cls.state = ca_connected;
1558 cls.signon = 0; // need all the signon messages before playing
1559 cls.protocol = initialprotocol;
1560 // reset move sequence numbering on this new connection
1561 cls.servermovesequence = 0;
1562 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1563 CL_ForwardToServer("new");
1564 if (cls.protocol == PROTOCOL_QUAKE)
1566 // write a keepalive (clc_nop) as it seems to greatly improve the
1567 // chances of connecting to a netquake server
1569 unsigned char buf[4];
1570 memset(&msg, 0, sizeof(msg));
1572 msg.maxsize = sizeof(buf);
1573 MSG_WriteChar(&msg, clc_nop);
1574 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1578 int NetConn_IsLocalGame(void)
1580 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1586 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1590 serverlist_entry_t *entry = NULL;
1592 // search the cache for this server and update it
1593 for (n = 0; n < serverlist_cachecount; ++n)
1595 entry = &serverlist_cache[n];
1596 if (!strcmp(addressstring, entry->info.cname))
1600 if (n == serverlist_cachecount)
1602 // LAN search doesnt require an answer from the master server so we wont
1603 // know the ping nor will it be initialized already...
1606 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1609 if (serverlist_maxcachecount <= serverlist_cachecount)
1611 serverlist_maxcachecount += 64;
1612 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1614 entry = &serverlist_cache[n];
1616 memset(entry, 0, sizeof(*entry));
1617 // store the data the engine cares about (address and ping)
1618 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1619 entry->info.ping = 100000;
1620 entry->querytime = host.realtime;
1621 // if not in the slist menu we should print the server to console
1622 if (serverlist_consoleoutput)
1623 Con_Printf("querying %s\n", addressstring);
1624 ++serverlist_cachecount;
1626 // if this is the first reply from this server, count it as having replied
1627 pingtime = (int)((host.realtime - entry->querytime) * 1000.0 + 0.5);
1628 pingtime = bound(0, pingtime, 9999);
1629 if (entry->query == SQS_REFRESHING) {
1630 entry->info.ping = pingtime;
1631 entry->query = SQS_QUERIED;
1633 // convert to unsigned to catch the -1
1634 // 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]
1635 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1639 // other server info is updated by the caller
1643 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1645 serverlist_entry_t *entry = &serverlist_cache[n];
1646 serverlist_info_t *info = &entry->info;
1647 // update description strings for engine menu and console output
1648 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);
1649 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1651 info->gameversion != gameversion.integer
1654 gameversion_min.integer >= 0 // min/max range set by user/mod?
1655 && gameversion_max.integer >= 0
1656 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1657 && gameversion_max.integer >= info->gameversion
1660 info->mod, info->map);
1661 if (entry->query == SQS_QUERIED)
1663 if(!serverlist_paused)
1664 ServerList_ViewList_Remove(entry);
1666 // if not in the slist menu we should print the server to console (if wanted)
1667 else if( serverlist_consoleoutput )
1668 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1669 // and finally, update the view set
1670 if(!serverlist_paused)
1671 ServerList_ViewList_Insert( entry );
1672 // update the entry's state
1673 serverlist_cache[n].query = SQS_QUERIED;
1676 // returns true, if it's sensible to continue the processing
1677 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery(int protocol, const char *ipstring, qbool isfavorite)
1680 serverlist_entry_t *entry;
1682 // ignore the rest of the message if the serverlist is full
1683 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1685 // also ignore it if we have already queried it (other master server response)
1686 for (n = 0; n < serverlist_cachecount; ++n)
1687 if (!strcmp(ipstring, serverlist_cache[n].info.cname))
1690 if (n < serverlist_cachecount)
1692 // the entry has already been queried once
1696 if (serverlist_maxcachecount <= n)
1698 serverlist_maxcachecount += 64;
1699 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1702 entry = &serverlist_cache[n];
1704 memset(entry, 0, sizeof(*entry));
1705 entry->protocol = protocol;
1706 // store the data the engine cares about (address and ping)
1707 strlcpy(entry->info.cname, ipstring, sizeof(entry->info.cname));
1709 entry->info.isfavorite = isfavorite;
1711 // no, then reset the ping right away
1712 entry->info.ping = -1;
1713 // we also want to increase the serverlist_cachecount then
1714 serverlist_cachecount++;
1717 entry->query = SQS_QUERYING;
1722 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qbool isextended)
1725 if (serverlist_consoleoutput)
1726 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1729 char ipstring [128];
1732 if (data[0] == '\\')
1734 unsigned short port = data[5] * 256 + data[6];
1736 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1737 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1739 // move on to next address in packet
1744 else if (data[0] == '/' && isextended && length >= 19)
1746 unsigned short port = data[17] * 256 + data[18];
1754 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1756 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1759 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1760 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1761 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1767 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1768 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1769 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1774 // move on to next address in packet
1780 Con_Print("Error while parsing the server list\n");
1784 if (serverlist_consoleoutput && developer_networking.integer)
1785 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1787 if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, ipstring, false))
1791 // begin or resume serverlist queries
1792 serverlist_querysleep = false;
1793 serverlist_querywaittime = host.realtime + 3;
1797 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1801 char *string, addressstring2[128];
1802 char stringbuf[16384];
1803 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1806 char infostringvalue[MAX_INPUTLINE];
1811 // quakeworld ingame packet
1812 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1814 // convert the address to a string incase we need it
1815 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1817 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1819 // received a command string - strip off the packaging and put it
1820 // into our string buffer with NULL termination
1823 length = min(length, (int)sizeof(stringbuf) - 1);
1824 memcpy(stringbuf, data, length);
1825 stringbuf[length] = 0;
1828 if (developer_networking.integer)
1830 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1831 Com_HexDumpToConsole(data, length);
1834 sendlength = sizeof(senddata) - 4;
1835 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1837 case CRYPTO_NOMATCH:
1843 memcpy(senddata, "\377\377\377\377", 4);
1844 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1847 case CRYPTO_DISCARD:
1850 memcpy(senddata, "\377\377\377\377", 4);
1851 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1855 case CRYPTO_REPLACE:
1856 string = senddata+4;
1857 length = (int)sendlength;
1861 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1864 for (j = 0;j < MAX_RCONS;j++)
1866 // note: this value from i is used outside the loop too...
1867 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1868 if(cls.rcon_commands[i][0])
1869 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1878 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1879 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1881 e = strchr(rcon_password.string, ' ');
1882 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1884 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1888 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1889 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1890 cls.rcon_commands[i][0] = 0;
1893 for (k = 0;k < MAX_RCONS;k++)
1894 if(cls.rcon_commands[k][0])
1895 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1900 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1901 // extend the timeout on other requests as we asked for a challenge
1902 for (l = 0;l < MAX_RCONS;l++)
1903 if(cls.rcon_commands[l][0])
1904 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1905 cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
1908 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1912 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1914 // darkplaces or quake3
1915 char protocolnames[1400];
1916 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1917 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1918 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1921 Protocol_Names(protocolnames, sizeof(protocolnames));
1923 M_Update_Return_Reason("Got challenge response");
1925 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1926 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1927 // TODO: add userinfo stuff here instead of using NQ commands?
1928 memcpy(senddata, "\377\377\377\377", 4);
1929 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1930 NetConn_WriteString(mysocket, senddata, peeraddress);
1933 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1935 // darkplaces or quake3
1936 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1937 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1941 M_Update_Return_Reason("Accepted");
1943 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1946 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1948 char rejectreason[128];
1949 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1950 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1953 cls.connect_trying = false;
1955 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1956 memcpy(rejectreason, string, length);
1957 rejectreason[length] = 0;
1959 M_Update_Return_Reason(rejectreason);
1964 if(key_dest != key_game)
1966 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1968 serverlist_info_t *info;
1973 // search the cache for this server and update it
1974 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1978 info = &serverlist_cache[n].info;
1983 info->qcstatus[0] = 0;
1984 info->players[0] = 0;
1985 info->protocol = -1;
1986 info->numplayers = 0;
1988 info->maxplayers = 0;
1989 info->gameversion = 0;
1991 p = strchr(string, '\n');
1994 *p = 0; // cut off the string there
1998 Con_Printf("statusResponse without players block?\n");
2000 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2001 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2002 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2003 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2004 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2005 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2006 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2007 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2008 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2009 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2010 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2011 info->numhumans = info->numplayers - max(0, info->numbots);
2012 info->freeslots = info->maxplayers - info->numplayers;
2014 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2018 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2020 serverlist_info_t *info;
2024 // search the cache for this server and update it
2025 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2029 info = &serverlist_cache[n].info;
2034 info->qcstatus[0] = 0;
2035 info->players[0] = 0;
2036 info->protocol = -1;
2037 info->numplayers = 0;
2039 info->maxplayers = 0;
2040 info->gameversion = 0;
2042 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2043 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2044 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2045 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2046 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2047 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2048 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2049 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2050 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2051 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2052 info->numhumans = info->numplayers - max(0, info->numbots);
2053 info->freeslots = info->maxplayers - info->numplayers;
2055 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2059 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2061 // Extract the IP addresses
2064 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2067 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2069 // Extract the IP addresses
2072 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2075 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2077 // Extract the IP addresses
2081 if (serverlist_consoleoutput)
2082 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2083 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2085 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2086 if (serverlist_consoleoutput && developer_networking.integer)
2087 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2089 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2093 // move on to next address in packet
2097 // begin or resume serverlist queries
2098 serverlist_querysleep = false;
2099 serverlist_querywaittime = host.realtime + 3;
2104 if (!strncmp(string, "extResponse ", 12))
2106 ++cl_net_extresponse_count;
2107 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2108 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2109 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2110 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2113 if (!strncmp(string, "ping", 4))
2115 if (developer_extra.integer)
2116 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2117 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2120 if (!strncmp(string, "ack", 3))
2122 // QuakeWorld compatibility
2123 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2125 // challenge message
2126 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2127 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2130 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2132 M_Update_Return_Reason("Got QuakeWorld challenge response");
2134 cls.qw_qport = qport.integer;
2135 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2136 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2137 memcpy(senddata, "\377\377\377\377", 4);
2138 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);
2139 NetConn_WriteString(mysocket, senddata, peeraddress);
2142 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2145 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2146 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2150 M_Update_Return_Reason("QuakeWorld Accepted");
2152 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2155 if (length > 2 && !memcmp(string, "n\\", 2))
2158 serverlist_info_t *info;
2162 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2163 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2166 // search the cache for this server and update it
2167 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2171 info = &serverlist_cache[n].info;
2172 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2173 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2174 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2175 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2177 info->numplayers = 0; // updated below
2178 info->numhumans = 0; // updated below
2179 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2180 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2182 // count active players on server
2183 // (we could gather more info, but we're just after the number)
2184 s = strchr(string, '\n');
2188 while (s < string + length)
2190 for (;s < string + length && *s != '\n';s++)
2192 if (s >= string + length)
2200 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2204 if (string[0] == 'n')
2206 // qw print command, used by rcon replies too
2207 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2208 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2211 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2213 // we may not have liked the packet, but it was a command packet, so
2214 // we're done processing this packet now
2217 // quakeworld ingame packet
2218 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2221 CL_ParseServerMessage();
2224 // netquake control packets, supported for compatibility only
2225 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2229 serverlist_info_t *info;
2234 SZ_Clear(&cl_message);
2235 SZ_Write(&cl_message, data, length);
2236 MSG_BeginReading(&cl_message);
2237 c = MSG_ReadByte(&cl_message);
2241 if (developer_extra.integer)
2242 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2243 if (cls.connect_trying)
2245 lhnetaddress_t clientportaddress;
2246 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2247 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2250 clientportaddress = *peeraddress;
2251 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2252 // extra ProQuake stuff
2254 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2256 cls.proquake_servermod = 0;
2258 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2260 cls.proquake_serverversion = 0;
2262 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2264 cls.proquake_serverflags = 0;
2265 if (cls.proquake_servermod == 1)
2266 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2267 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2268 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2270 M_Update_Return_Reason("Accepted");
2272 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2276 if (developer_extra.integer) {
2277 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2280 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2282 cls.connect_trying = false;
2284 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2287 case CCREP_SERVER_INFO:
2288 if (developer_extra.integer)
2289 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2291 // LadyHavoc: because the quake server may report weird addresses
2292 // we just ignore it and keep the real address
2293 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2294 // search the cache for this server and update it
2295 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2299 info = &serverlist_cache[n].info;
2300 strlcpy(info->game, "Quake", sizeof(info->game));
2301 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2302 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2303 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2304 info->numplayers = MSG_ReadByte(&cl_message);
2305 info->maxplayers = MSG_ReadByte(&cl_message);
2306 info->protocol = MSG_ReadByte(&cl_message);
2308 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2311 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2312 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2313 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2316 if (developer_extra.integer)
2317 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2319 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2321 case CCREP_PLAYER_INFO:
2322 // we got a CCREP_PLAYER_INFO??
2323 //if (developer_extra.integer)
2324 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2326 case CCREP_RULE_INFO:
2327 // we got a CCREP_RULE_INFO??
2328 //if (developer_extra.integer)
2329 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2334 SZ_Clear(&cl_message);
2335 // we may not have liked the packet, but it was a valid control
2336 // packet, so we're done processing this packet now
2340 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2341 CL_ParseServerMessage();
2346 void NetConn_QueryQueueFrame(void)
2352 static double querycounter = 0;
2354 if (!net_slist_pause.integer && serverlist_paused)
2355 ServerList_RebuildViewList();
2356 serverlist_paused = net_slist_pause.integer != 0;
2358 if (serverlist_querysleep)
2361 // apply a cool down time after master server replies,
2362 // to avoid messing up the ping times on the servers
2363 if (serverlist_querywaittime > host.realtime)
2366 // each time querycounter reaches 1.0 issue a query
2367 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2368 maxqueries = (int)querycounter;
2369 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2370 querycounter -= maxqueries;
2372 if (maxqueries == 0)
2375 // scan serverlist and issue queries as needed
2376 serverlist_querysleep = true;
2378 timeouttime = host.realtime - net_slist_timeout.value;
2379 for (index = 0, queries = 0; index < serverlist_cachecount && queries < maxqueries; ++index)
2381 serverlist_entry_t *entry = &serverlist_cache[index];
2382 if (entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING)
2385 serverlist_querysleep = false;
2386 if (entry->querycounter != 0 && entry->querytime > timeouttime)
2389 if (entry->querycounter != (unsigned)net_slist_maxtries.integer)
2391 lhnetaddress_t address;
2394 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2395 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2397 for (socket = 0; socket < cl_numsockets; ++socket)
2398 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2402 for (socket = 0; socket < cl_numsockets; ++socket)
2403 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2406 // update the entry fields
2407 entry->querytime = host.realtime;
2408 entry->querycounter++;
2410 // if not in the slist menu we should print the server to console
2411 if (serverlist_consoleoutput)
2412 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2418 // have we tried to refresh this server?
2419 if (entry->query == SQS_REFRESHING)
2421 // yes, so update the reply count (since its not responding anymore)
2423 if (!serverlist_paused)
2424 ServerList_ViewList_Remove(entry);
2426 entry->query = SQS_TIMEDOUT;
2432 void NetConn_ClientFrame(void)
2435 lhnetaddress_t peeraddress;
2436 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2437 NetConn_UpdateSockets();
2438 if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2441 if (cls.connect_remainingtries == 0)
2442 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2444 cls.connect_nextsendtime = host.realtime + 1;
2445 cls.connect_remainingtries--;
2446 if (cls.connect_remainingtries <= -10)
2448 cls.connect_trying = false;
2450 M_Update_Return_Reason("Connect: Failed");
2454 // try challenge first (newer DP server or QW)
2455 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2456 // then try netquake as a fallback (old server, or netquake)
2457 SZ_Clear(&cl_message);
2458 // save space for the header, filled in later
2459 MSG_WriteLong(&cl_message, 0);
2460 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2461 MSG_WriteString(&cl_message, "QUAKE");
2462 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2463 // extended proquake stuff
2464 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2465 // this version matches ProQuake 3.40, the first version to support
2466 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2467 // higher clients, so we pretend we are that version...
2468 MSG_WriteByte(&cl_message, 34); // version * 10
2469 MSG_WriteByte(&cl_message, 0); // flags
2470 MSG_WriteLong(&cl_message, 0); // password
2471 // write the packetsize now...
2472 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2473 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2474 SZ_Clear(&cl_message);
2476 for (i = 0;i < cl_numsockets;i++)
2478 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2480 // R_TimeReport("clientreadnetwork");
2481 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2482 // R_TimeReport("clientparsepacket");
2486 NetConn_QueryQueueFrame();
2488 if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2489 CL_DisconnectEx(true, "Connection timed out");
2492 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2496 for (i = 0;i < bufferlength - 1;i++)
2500 c = rand () % (127 - 33) + 33;
2501 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2507 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2508 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2510 prvm_prog_t *prog = SVVM_prog;
2512 unsigned int nb_clients = 0, nb_bots = 0, i;
2515 const char *crypto_idstring;
2516 const char *worldstatusstr;
2518 // How many clients are there?
2519 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2521 if (svs.clients[i].active)
2524 if (!svs.clients[i].netconnection)
2530 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2531 if(worldstatusstr && *worldstatusstr)
2536 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2537 if(*q != '\\' && *q != '\n')
2542 /// \TODO: we should add more information for the full status string
2543 crypto_idstring = Crypto_GetInfoResponseDataString();
2544 length = dpsnprintf(out_msg, out_size,
2545 "\377\377\377\377%s\x0A"
2546 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2547 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2552 fullstatus ? "statusResponse" : "infoResponse",
2553 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2554 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2555 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2556 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2557 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2558 fullstatus ? "\n" : "");
2560 // Make sure it fits in the buffer
2570 savelength = length;
2572 ptr = out_msg + length;
2573 left = (int)out_size - length;
2575 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2577 client_t *client = &svs.clients[i];
2580 int nameind, cleanind, pingvalue;
2582 char cleanname [sizeof(client->name)];
2583 const char *statusstr;
2586 // Remove all characters '"' and '\' in the player name
2591 curchar = client->name[nameind++];
2592 if (curchar != '"' && curchar != '\\')
2594 cleanname[cleanind++] = curchar;
2595 if (cleanind == sizeof(cleanname) - 1)
2598 } while (curchar != '\0');
2599 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2601 pingvalue = (int)(client->ping * 1000.0f);
2602 if(client->netconnection)
2603 pingvalue = bound(1, pingvalue, 9999);
2608 ed = PRVM_EDICT_NUM(i + 1);
2609 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2610 if(statusstr && *statusstr)
2615 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2616 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2621 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2623 if(client->frags == -666) // spectator
2624 strlcpy(teambuf, " 0", sizeof(teambuf));
2625 else if(client->colors == 0x44) // red team
2626 strlcpy(teambuf, " 1", sizeof(teambuf));
2627 else if(client->colors == 0xDD) // blue team
2628 strlcpy(teambuf, " 2", sizeof(teambuf));
2629 else if(client->colors == 0xCC) // yellow team
2630 strlcpy(teambuf, " 3", sizeof(teambuf));
2631 else if(client->colors == 0x99) // pink team
2632 strlcpy(teambuf, " 4", sizeof(teambuf));
2634 strlcpy(teambuf, " 0", sizeof(teambuf));
2639 // note: team number is inserted according to SoF2 protocol
2641 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2647 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2656 // turn it into an infoResponse!
2657 out_msg[savelength] = 0;
2658 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2659 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2674 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2676 size_t floodslotnum, bestfloodslotnum;
2677 double bestfloodtime;
2678 lhnetaddress_t noportpeeraddress;
2679 // see if this is a connect flood
2680 noportpeeraddress = *peeraddress;
2681 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2682 bestfloodslotnum = 0;
2683 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2684 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2686 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2688 bestfloodtime = floodlist[floodslotnum].lasttime;
2689 bestfloodslotnum = floodslotnum;
2691 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2693 // this address matches an ongoing flood address
2694 if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2698 // renew the ban on this address so it does not expire
2699 // until the flood has subsided
2700 floodlist[floodslotnum].lasttime = host.realtime;
2702 //Con_Printf("Flood detected!\n");
2705 // the flood appears to have subsided, so allow this
2706 bestfloodslotnum = floodslotnum; // reuse the same slot
2710 // begin a new timeout on this address
2711 floodlist[bestfloodslotnum].address = noportpeeraddress;
2712 floodlist[bestfloodslotnum].lasttime = host.realtime;
2713 //Con_Printf("Flood detection initiated!\n");
2717 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2719 size_t floodslotnum;
2720 lhnetaddress_t noportpeeraddress;
2721 // see if this is a connect flood
2722 noportpeeraddress = *peeraddress;
2723 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2724 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2726 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2728 // this address matches an ongoing flood address
2730 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2731 floodlist[floodslotnum].lasttime = 0;
2732 //Con_Printf("Flood cleared!\n");
2737 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2739 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2745 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2749 t1 = (long) time(NULL);
2750 t2 = strtol(s, NULL, 0);
2751 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2754 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2757 return !memcmp(mdfourbuf, hash, 16);
2760 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2766 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2770 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2773 // validate the challenge
2774 for (i = 0;i < MAX_CHALLENGES;i++)
2775 if(challenges[i].time > 0)
2776 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2778 // if the challenge is not recognized, drop the packet
2779 if (i == MAX_CHALLENGES)
2782 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2785 if(memcmp(mdfourbuf, hash, 16))
2788 // unmark challenge to prevent replay attacks
2789 challenges[i].time = 0;
2794 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2797 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2801 return !strcmp(password, hash);
2804 /// returns a string describing the user level, or NULL for auth failure
2805 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)
2807 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2808 static char buf[MAX_INPUTLINE];
2810 qbool restricted = false;
2811 qbool have_usernames = false;
2812 static char vabuf[1024];
2814 userpass_start = rcon_password.string;
2815 while((userpass_end = strchr(userpass_start, ' ')))
2817 have_usernames = true;
2818 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2819 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2820 if(comparator(peeraddress, buf, password, cs, cslen))
2822 userpass_start = userpass_end + 1;
2824 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2826 userpass_end = userpass_start + strlen(userpass_start);
2827 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2832 have_usernames = false;
2833 userpass_start = rcon_restricted_password.string;
2834 while((userpass_end = strchr(userpass_start, ' ')))
2836 have_usernames = true;
2837 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2838 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2839 if(comparator(peeraddress, buf, password, cs, cslen))
2841 userpass_start = userpass_end + 1;
2843 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2845 userpass_end = userpass_start + strlen(userpass_start);
2846 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2850 return NULL; // DENIED
2853 for(text = s; text != endpos; ++text)
2854 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2855 return NULL; // block possible exploits against the parser/alias expansion
2859 size_t l = strlen(s);
2862 hasquotes = (strchr(s, '"') != NULL);
2863 // sorry, we can't allow these substrings in wildcard expressions,
2864 // as they can mess with the argument counts
2865 text = rcon_restricted_commands.string;
2866 while(COM_ParseToken_Console(&text))
2868 // com_token now contains a pattern to check for...
2869 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2872 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2875 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2877 if(!strcmp(com_token, s))
2880 else // single-arg expression? must match the beginning of the command
2882 if(!strcmp(com_token, s))
2884 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2888 // if we got here, nothing matched!
2896 userpass_startpass = strchr(userpass_start, ':');
2897 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2898 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2900 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2903 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qbool proquakeprotocol)
2907 // looks like a legitimate rcon command with the correct password
2908 const char *s_ptr = s;
2909 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2910 while(s_ptr != endpos)
2912 size_t l = strlen(s_ptr);
2914 Con_Printf(" %s;", s_ptr);
2919 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2920 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2923 size_t l = strlen(s);
2926 client_t *host_client_save = host_client;
2927 Cmd_ExecuteString(cmd_local, s, src_local, true);
2928 host_client = host_client_save;
2929 // in case it is a command that changes host_client (like restart)
2933 Con_Rcon_Redirect_End();
2937 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2941 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2943 int i, ret, clientnum, best;
2945 char *string, response[2800], addressstring2[128];
2946 static char stringbuf[16384]; // server only
2947 qbool islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2948 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2949 size_t sendlength, response_len;
2950 char infostringvalue[MAX_INPUTLINE];
2955 // convert the address to a string incase we need it
2956 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2958 // see if we can identify the sender as a local player
2959 // (this is necessary for rcon to send a reliable reply if the client is
2960 // actually on the server, not sending remotely)
2961 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2962 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2964 if (i == svs.maxclients)
2967 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2969 // received a command string - strip off the packaging and put it
2970 // into our string buffer with NULL termination
2973 length = min(length, (int)sizeof(stringbuf) - 1);
2974 memcpy(stringbuf, data, length);
2975 stringbuf[length] = 0;
2978 if (developer_extra.integer)
2980 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2981 Com_HexDumpToConsole(data, length);
2984 sendlength = sizeof(senddata) - 4;
2985 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2987 case CRYPTO_NOMATCH:
2993 memcpy(senddata, "\377\377\377\377", 4);
2994 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2997 case CRYPTO_DISCARD:
3000 memcpy(senddata, "\377\377\377\377", 4);
3001 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3005 case CRYPTO_REPLACE:
3006 string = senddata+4;
3007 length = (int)sendlength;
3011 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3013 for (i = 0, best = 0, besttime = host.realtime;i < MAX_CHALLENGES;i++)
3015 if(challenges[i].time > 0)
3016 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3018 if (besttime > challenges[i].time)
3019 besttime = challenges[best = i].time;
3021 // if we did not find an exact match, choose the oldest and
3022 // update address and string
3023 if (i == MAX_CHALLENGES)
3026 challenges[i].address = *peeraddress;
3027 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3031 // flood control: drop if requesting challenge too often
3032 if(challenges[i].time > host.realtime - net_challengefloodblockingtimeout.value)
3035 challenges[i].time = host.realtime;
3036 // send the challenge
3037 memcpy(response, "\377\377\377\377", 4);
3038 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3039 response_len = strlen(response) + 1;
3040 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3041 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3044 if (length > 8 && !memcmp(string, "connect\\", 8))
3048 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3052 if(crypto && crypto->authenticated)
3054 // no need to check challenge
3055 if(crypto_developer.integer)
3057 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3058 crypto->use_aes ? "Encrypted" : "Authenticated",
3060 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3061 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3062 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3063 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3064 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3065 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3071 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3073 // validate the challenge
3074 for (i = 0;i < MAX_CHALLENGES;i++)
3075 if(challenges[i].time > 0)
3076 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3078 // if the challenge is not recognized, drop the packet
3079 if (i == MAX_CHALLENGES)
3084 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3085 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3087 if(!(islocal || sv_public.integer > -2))
3089 if (developer_extra.integer)
3090 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3091 memcpy(response, "\377\377\377\377", 4);
3092 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3093 NetConn_WriteString(mysocket, response, peeraddress);
3097 // check engine protocol
3098 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3100 if (developer_extra.integer)
3101 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3102 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3106 // see if this is a duplicate connection request or a disconnected
3107 // client who is rejoining to the same client slot
3108 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3110 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3112 // this is a known client...
3113 if(crypto && crypto->authenticated)
3115 // reject if changing key!
3116 if(client->netconnection->crypto.authenticated)
3119 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3121 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3123 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3125 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3128 if (developer_extra.integer)
3129 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3130 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3137 // reject if downgrading!
3138 if(client->netconnection->crypto.authenticated)
3140 if (developer_extra.integer)
3141 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3142 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3148 // client crashed and is coming back,
3149 // keep their stuff intact
3150 if (developer_extra.integer)
3151 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3152 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3153 if(crypto && crypto->authenticated)
3154 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3155 SV_SendServerinfo(client);
3159 // client is still trying to connect,
3160 // so we send a duplicate reply
3161 if (developer_extra.integer)
3162 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3163 if(crypto && crypto->authenticated)
3164 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3165 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3171 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3174 // find an empty client slot for this new client
3175 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3178 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3180 // allocated connection
3181 if (developer_extra.integer)
3182 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3183 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3184 // now set up the client
3185 if(crypto && crypto->authenticated)
3186 Crypto_FinishInstance(&conn->crypto, crypto);
3187 SV_ConnectClient(clientnum, conn);
3188 NetConn_Heartbeat(1);
3193 // no empty slots found - server is full
3194 if (developer_extra.integer)
3195 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3196 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3200 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3202 const char *challenge = NULL;
3204 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3207 // If there was a challenge in the getinfo message
3208 if (length > 8 && string[7] == ' ')
3209 challenge = string + 8;
3211 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3213 if (developer_extra.integer)
3214 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3215 NetConn_WriteString(mysocket, response, peeraddress);
3219 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3221 const char *challenge = NULL;
3223 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3226 // If there was a challenge in the getinfo message
3227 if (length > 10 && string[9] == ' ')
3228 challenge = string + 10;
3230 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3232 if (developer_extra.integer)
3233 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3234 NetConn_WriteString(mysocket, response, peeraddress);
3238 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3240 char *password = string + 20;
3241 char *timeval = string + 37;
3242 char *s = strchr(timeval, ' ');
3243 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3244 const char *userlevel;
3246 if(rcon_secure.integer > 1)
3250 return true; // invalid packet
3253 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3254 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3257 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3259 char *password = string + 25;
3260 char *challenge = string + 42;
3261 char *s = strchr(challenge, ' ');
3262 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3263 const char *userlevel;
3265 return true; // invalid packet
3268 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3269 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3272 if (length >= 5 && !memcmp(string, "rcon ", 5))
3275 char *s = string + 5;
3276 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3279 if(rcon_secure.integer > 0)
3282 for (j = 0;!ISWHITESPACE(*s);s++)
3283 if (j < (int)sizeof(password) - 1)
3285 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3288 if (!ISWHITESPACE(password[0]))
3290 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3291 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3295 if (!strncmp(string, "extResponse ", 12))
3297 ++sv_net_extresponse_count;
3298 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3299 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3300 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3301 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3304 if (!strncmp(string, "ping", 4))
3306 if (developer_extra.integer)
3307 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3308 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3311 if (!strncmp(string, "ack", 3))
3313 // we may not have liked the packet, but it was a command packet, so
3314 // we're done processing this packet now
3317 // netquake control packets, supported for compatibility only, and only
3318 // when running game protocols that are normally served via this connection
3320 // (this protects more modern protocols against being used for
3321 // Quake packet flood Denial Of Service attacks)
3322 if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) && !ENCRYPTION_REQUIRED)
3326 const char *protocolname;
3327 client_t *knownclient;
3328 client_t *newclient;
3331 SZ_Clear(&sv_message);
3332 SZ_Write(&sv_message, data, length);
3333 MSG_BeginReading(&sv_message);
3334 c = MSG_ReadByte(&sv_message);
3338 if (developer_extra.integer)
3339 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3340 if(!(islocal || sv_public.integer > -2))
3342 if (developer_extra.integer)
3343 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3344 SZ_Clear(&sv_message);
3345 // save space for the header, filled in later
3346 MSG_WriteLong(&sv_message, 0);
3347 MSG_WriteByte(&sv_message, CCREP_REJECT);
3348 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3349 MSG_WriteString(&sv_message, "\n");
3350 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3351 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3352 SZ_Clear(&sv_message);
3356 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3357 protocolnumber = MSG_ReadByte(&sv_message);
3358 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3360 if (developer_extra.integer)
3361 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3362 SZ_Clear(&sv_message);
3363 // save space for the header, filled in later
3364 MSG_WriteLong(&sv_message, 0);
3365 MSG_WriteByte(&sv_message, CCREP_REJECT);
3366 MSG_WriteString(&sv_message, "Incompatible version.\n");
3367 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3368 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3369 SZ_Clear(&sv_message);
3373 // see if this connect request comes from a known client
3374 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3376 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3378 // this is either a duplicate connection request
3379 // or coming back from a timeout
3380 // (if so, keep their stuff intact)
3382 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3383 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3385 if (developer_extra.integer)
3386 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3387 SZ_Clear(&sv_message);
3388 // save space for the header, filled in later
3389 MSG_WriteLong(&sv_message, 0);
3390 MSG_WriteByte(&sv_message, CCREP_REJECT);
3391 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3392 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3393 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3394 SZ_Clear(&sv_message);
3399 if (developer_extra.integer)
3400 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3401 SZ_Clear(&sv_message);
3402 // save space for the header, filled in later
3403 MSG_WriteLong(&sv_message, 0);
3404 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3405 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3406 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3407 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3408 SZ_Clear(&sv_message);
3410 // if client is already spawned, re-send the
3411 // serverinfo message as they'll need it to play
3412 if (knownclient->begun)
3413 SV_SendServerinfo(knownclient);
3418 // this is a new client, check for connection flood
3419 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3422 // find a slot for the new client
3423 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3426 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3428 // connect to the client
3429 // everything is allocated, just fill in the details
3430 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3431 if (developer_extra.integer)
3432 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3433 // send back the info about the server connection
3434 SZ_Clear(&sv_message);
3435 // save space for the header, filled in later
3436 MSG_WriteLong(&sv_message, 0);
3437 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3438 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3439 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3440 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3441 SZ_Clear(&sv_message);
3442 // now set up the client struct
3443 SV_ConnectClient(clientnum, conn);
3444 NetConn_Heartbeat(1);
3449 if (developer_extra.integer)
3450 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3451 // no room; try to let player know
3452 SZ_Clear(&sv_message);
3453 // save space for the header, filled in later
3454 MSG_WriteLong(&sv_message, 0);
3455 MSG_WriteByte(&sv_message, CCREP_REJECT);
3456 MSG_WriteString(&sv_message, "Server is full.\n");
3457 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3458 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3459 SZ_Clear(&sv_message);
3461 case CCREQ_SERVER_INFO:
3462 if (developer_extra.integer)
3463 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3464 if(!(islocal || sv_public.integer > -1))
3467 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3470 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3473 char myaddressstring[128];
3474 if (developer_extra.integer)
3475 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3476 SZ_Clear(&sv_message);
3477 // save space for the header, filled in later
3478 MSG_WriteLong(&sv_message, 0);
3479 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3480 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3481 MSG_WriteString(&sv_message, myaddressstring);
3482 MSG_WriteString(&sv_message, hostname.string);
3483 MSG_WriteString(&sv_message, sv.name);
3484 // How many clients are there?
3485 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3486 if (svs.clients[i].active)
3488 MSG_WriteByte(&sv_message, numclients);
3489 MSG_WriteByte(&sv_message, svs.maxclients);
3490 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3491 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3492 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3493 SZ_Clear(&sv_message);
3496 case CCREQ_PLAYER_INFO:
3497 if (developer_extra.integer)
3498 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3499 if(!(islocal || sv_public.integer > -1))
3502 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3507 int playerNumber, activeNumber, clientNumber;
3510 playerNumber = MSG_ReadByte(&sv_message);
3512 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3513 if (client->active && ++activeNumber == playerNumber)
3515 if (clientNumber != svs.maxclients)
3517 SZ_Clear(&sv_message);
3518 // save space for the header, filled in later
3519 MSG_WriteLong(&sv_message, 0);
3520 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3521 MSG_WriteByte(&sv_message, playerNumber);
3522 MSG_WriteString(&sv_message, client->name);
3523 MSG_WriteLong(&sv_message, client->colors);
3524 MSG_WriteLong(&sv_message, client->frags);
3525 MSG_WriteLong(&sv_message, (int)(host.realtime - client->connecttime));
3526 if(sv_status_privacy.integer)
3527 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3529 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3530 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3531 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3532 SZ_Clear(&sv_message);
3536 case CCREQ_RULE_INFO:
3537 if (developer_extra.integer)
3538 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3539 if(!(islocal || sv_public.integer > -1))
3542 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3549 // find the search start location
3550 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3551 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CF_NOTIFY);
3553 // send the response
3554 SZ_Clear(&sv_message);
3555 // save space for the header, filled in later
3556 MSG_WriteLong(&sv_message, 0);
3557 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3560 MSG_WriteString(&sv_message, var->name);
3561 MSG_WriteString(&sv_message, var->string);
3563 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3564 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3565 SZ_Clear(&sv_message);
3569 if (developer_extra.integer)
3570 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3571 if (sv.active && !rcon_secure.integer)
3573 char password[2048];
3577 const char *userlevel;
3578 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3579 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3581 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3582 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3583 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3590 SZ_Clear(&sv_message);
3591 // we may not have liked the packet, but it was a valid control
3592 // packet, so we're done processing this packet now
3597 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3599 SV_ReadClientMessage();
3606 void NetConn_ServerFrame(void)
3609 lhnetaddress_t peeraddress;
3610 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3611 for (i = 0;i < sv_numsockets;i++)
3612 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3613 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3616 void NetConn_SleepMicroseconds(int microseconds)
3618 LHNET_SleepUntilPacket_Microseconds(microseconds);
3622 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
3626 lhnetaddress_t masteraddress;
3627 lhnetaddress_t broadcastaddress;
3630 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3633 // 26000 is the default quake server port, servers on other ports will not
3635 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3636 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3640 for (i = 0;i < cl_numsockets;i++)
3644 const char *cmdname, *extraoptions;
3645 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3647 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3649 // search LAN for Quake servers
3650 SZ_Clear(&cl_message);
3651 // save space for the header, filled in later
3652 MSG_WriteLong(&cl_message, 0);
3653 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3654 MSG_WriteString(&cl_message, "QUAKE");
3655 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3656 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3657 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3658 SZ_Clear(&cl_message);
3660 // search LAN for DarkPlaces servers
3661 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3664 // build the getservers message to send to the dpmaster master servers
3665 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3667 cmdname = "getserversExt";
3668 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3672 cmdname = "getservers";
3675 memcpy(request, "\377\377\377\377", 4);
3676 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3679 for (masternum = 0;sv_masters[masternum].name;masternum++)
3681 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3684 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3688 // search favorite servers
3689 for(j = 0; j < nFavorites; ++j)
3691 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3693 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3694 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3701 // only query QuakeWorld servers when the user wants to
3704 for (i = 0;i < cl_numsockets;i++)
3708 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3710 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3712 // search LAN for QuakeWorld servers
3713 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3715 // build the getservers message to send to the qwmaster master servers
3716 // note this has no -1 prefix, and the trailing nul byte is sent
3717 dpsnprintf(request, sizeof(request), "c\n");
3721 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3723 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3725 if (m_state != m_slist)
3727 char lookupstring[128];
3728 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3729 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3732 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3736 // search favorite servers
3737 for(j = 0; j < nFavorites; ++j)
3739 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3741 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3743 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3744 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3751 if (!masterquerycount)
3753 Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
3754 M_Update_Return_Reason("No network");
3759 void NetConn_Heartbeat(int priority)
3761 lhnetaddress_t masteraddress;
3763 lhnetsocket_t *mysocket;
3765 // if it's a state change (client connected), limit next heartbeat to no
3766 // more than 30 sec in the future
3767 if (priority == 1 && nextheartbeattime > host.realtime + 30.0)
3768 nextheartbeattime = host.realtime + 30.0;
3770 // limit heartbeatperiod to 30 to 270 second range,
3771 // lower limit is to avoid abusing master servers with excess traffic,
3772 // upper limit is to avoid timing out on the master server (which uses
3774 if (sv_heartbeatperiod.value < 30)
3775 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3776 if (sv_heartbeatperiod.value > 270)
3777 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3779 // make advertising optional and don't advertise singleplayer games, and
3780 // only send a heartbeat as often as the admin wants
3781 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
3783 nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
3784 for (masternum = 0;sv_masters[masternum].name;masternum++)
3785 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3786 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3790 static void Net_Heartbeat_f(cmd_state_t *cmd)
3793 NetConn_Heartbeat(2);
3795 Con_Print("No server running, can not heartbeat to master server.\n");
3798 static void PrintStats(netconn_t *conn)
3800 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3801 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3803 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3804 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3805 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3806 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3807 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3808 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3809 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3810 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3811 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3812 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3815 void Net_Stats_f(cmd_state_t *cmd)
3818 Con_Print("connections =\n");
3819 for (conn = netconn_list;conn;conn = conn->next)
3824 void Net_Refresh_f(cmd_state_t *cmd)
3826 if (m_state != m_slist)
3828 Con_Print("Sending new requests to master servers\n");
3829 ServerList_QueryList(false, true, false, true);
3830 Con_Print("Listening for replies...\n");
3833 ServerList_QueryList(false, true, false, false);
3836 void Net_Slist_f(cmd_state_t *cmd)
3838 ServerList_ResetMasks();
3839 serverlist_sortbyfield = SLIF_PING;
3840 serverlist_sortflags = 0;
3841 if (m_state != m_slist)
3843 Con_Print("Sending requests to master servers\n");
3844 ServerList_QueryList(true, true, false, true);
3845 Con_Print("Listening for replies...\n");
3848 ServerList_QueryList(true, true, false, false);
3851 void Net_SlistQW_f(cmd_state_t *cmd)
3853 ServerList_ResetMasks();
3854 serverlist_sortbyfield = SLIF_PING;
3855 serverlist_sortflags = 0;
3856 if (m_state != m_slist)
3858 Con_Print("Sending requests to master servers\n");
3859 ServerList_QueryList(true, false, true, true);
3860 serverlist_consoleoutput = true;
3861 Con_Print("Listening for replies...\n");
3864 ServerList_QueryList(true, false, true, false);
3868 void NetConn_Init(void)
3871 lhnetaddress_t tempaddress;
3872 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3873 Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
3875 Cmd_AddCommand(CF_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
3876 Cmd_AddCommand(CF_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3877 Cmd_AddCommand(CF_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3879 Cmd_AddCommand(CF_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3880 Cvar_RegisterVariable(&net_test);
3881 Cvar_RegisterVariable(&net_usesizelimit);
3882 Cvar_RegisterVariable(&net_burstreserve);
3883 Cvar_RegisterVariable(&rcon_restricted_password);
3884 Cvar_RegisterVariable(&rcon_restricted_commands);
3885 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3886 Cvar_RegisterVariable(&net_slist_queriespersecond);
3887 Cvar_RegisterVariable(&net_slist_queriesperframe);
3888 Cvar_RegisterVariable(&net_slist_timeout);
3889 Cvar_RegisterVariable(&net_slist_maxtries);
3890 Cvar_RegisterVariable(&net_slist_favorites);
3892 Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
3894 Cvar_RegisterVariable(&net_slist_pause);
3895 #ifdef IP_TOS // register cvar only if supported
3896 Cvar_RegisterVariable(&net_tos_dscp);
3898 Cvar_RegisterVariable(&net_messagetimeout);
3899 Cvar_RegisterVariable(&net_connecttimeout);
3900 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3901 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3902 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3903 Cvar_RegisterVariable(&net_sourceaddresscheck);
3904 Cvar_RegisterVariable(&net_fakelag);
3905 Cvar_RegisterVariable(&net_fakeloss_send);
3906 Cvar_RegisterVariable(&net_fakeloss_receive);
3907 Cvar_RegisterVirtual(&net_fakelag, "cl_netlocalping");
3908 Cvar_RegisterVirtual(&net_fakeloss_send, "cl_netpacketloss_send");
3909 Cvar_RegisterVirtual(&net_fakeloss_receive, "cl_netpacketloss_receive");
3910 Cvar_RegisterVariable(&hostname);
3911 Cvar_RegisterVariable(&developer_networking);
3912 Cvar_RegisterVariable(&cl_netport);
3913 Cvar_RegisterCallback(&cl_netport, NetConn_CL_UpdateSockets_Callback);
3914 Cvar_RegisterVariable(&sv_netport);
3915 Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
3916 Cvar_RegisterVariable(&net_address);
3917 Cvar_RegisterVariable(&net_address_ipv6);
3918 Cvar_RegisterVariable(&sv_public);
3919 Cvar_RegisterVariable(&sv_public_rejectreason);
3920 Cvar_RegisterVariable(&sv_heartbeatperiod);
3921 for (i = 0;sv_masters[i].name;i++)
3922 Cvar_RegisterVariable(&sv_masters[i]);
3923 Cvar_RegisterVariable(&gameversion);
3924 Cvar_RegisterVariable(&gameversion_min);
3925 Cvar_RegisterVariable(&gameversion_max);
3926 // COMMANDLINEOPTION: Server: -ip <ipaddress> sets the ip address of this machine for purposes of networking (default 0.0.0.0 also known as INADDR_ANY), use only if you have multiple network adapters and need to choose one specifically.
3927 if ((i = Sys_CheckParm("-ip")) && i + 1 < sys.argc)
3929 if (LHNETADDRESS_FromString(&tempaddress, sys.argv[i + 1], 0) == 1)
3931 Con_Printf("-ip option used, setting net_address to \"%s\"\n", sys.argv[i + 1]);
3932 Cvar_SetQuick(&net_address, sys.argv[i + 1]);
3935 Con_Printf(CON_ERROR "-ip option used, but unable to parse the address \"%s\"\n", sys.argv[i + 1]);
3937 // COMMANDLINEOPTION: Server: -port <portnumber> sets the port to use for a server (default 26000, the same port as QUAKE itself), useful if you host multiple servers on your machine
3938 if (((i = Sys_CheckParm("-port")) || (i = Sys_CheckParm("-ipport")) || (i = Sys_CheckParm("-udpport"))) && i + 1 < sys.argc)
3940 i = atoi(sys.argv[i + 1]);
3941 if (i >= 0 && i < 65536)
3943 Con_Printf("-port option used, setting port cvar to %i\n", i);
3944 Cvar_SetValueQuick(&sv_netport, i);
3947 Con_Printf(CON_ERROR "-port option used, but %i is not a valid port number\n", i);
3951 cl_message.data = cl_message_buf;
3952 cl_message.maxsize = sizeof(cl_message_buf);
3953 cl_message.cursize = 0;
3954 sv_message.data = sv_message_buf;
3955 sv_message.maxsize = sizeof(sv_message_buf);
3956 sv_message.cursize = 0;
3958 if (Thread_HasThreads())
3959 netconn_mutex = Thread_CreateMutex();
3962 void NetConn_Shutdown(void)
3964 NetConn_CloseClientPorts();
3965 NetConn_CloseServerPorts();
3968 Thread_DestroyMutex(netconn_mutex);
3969 netconn_mutex = NULL;