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;
586 serverquerycount = 0;
587 serverreplycount = 0;
588 serverlist_cachecount = 0;
589 serverlist_viewcount = 0;
590 serverlist_maxcachecount = 0;
591 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
593 // refresh all entries
595 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
596 serverlist_entry_t *entry = &serverlist_cache[ n ];
597 entry->query = SQS_REFRESHING;
598 entry->querycounter = 0;
601 serverlist_consoleoutput = consoleoutput;
603 //_ServerList_Test();
605 NetConn_QueryMasters(querydp, queryqw);
611 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
615 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
616 Thread_LockMutex(netconn_mutex);
617 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
618 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
619 Thread_UnlockMutex(netconn_mutex);
622 if (net_fakeloss_receive.integer)
623 for (i = 0;i < cl_numsockets;i++)
624 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_receive.integer)
626 if (developer_networking.integer)
628 char addressstring[128], addressstring2[128];
629 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
632 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
633 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
634 Com_HexDumpToConsole((unsigned char *)data, length);
637 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
642 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
646 if (net_fakeloss_send.integer)
647 for (i = 0;i < cl_numsockets;i++)
648 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_send.integer)
650 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
651 Thread_LockMutex(netconn_mutex);
652 ret = LHNET_Write(mysocket, data, length, peeraddress);
653 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
654 Thread_UnlockMutex(netconn_mutex);
655 if (developer_networking.integer)
657 char addressstring[128], addressstring2[128];
658 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
659 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
660 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)");
661 Com_HexDumpToConsole((unsigned char *)data, length);
666 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
668 // note this does not include the trailing NULL because we add that in the parser
669 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
672 qbool NetConn_CanSend(netconn_t *conn)
674 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
675 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = host.realtime;
676 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
677 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
678 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
679 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
680 if (host.realtime > conn->cleartime)
684 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
689 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
691 double bursttime = burstsize / (double)rate;
693 // delay later packets to obey rate limit
694 if (*cleartime < host.realtime - bursttime)
695 *cleartime = host.realtime - bursttime;
696 *cleartime = *cleartime + len / (double)rate;
698 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
699 if (net_test.integer)
701 if (*cleartime < host.realtime)
702 *cleartime = host.realtime;
706 static int NetConn_AddCryptoFlag(crypto_t *crypto)
708 // HACK: if an encrypted connection is used, randomly set some unused
709 // flags. When AES encryption is enabled, that will make resends differ
710 // from the original, so that e.g. substring filters in a router/IPS
711 // are unlikely to match a second time. See also "startkeylogger".
713 if (crypto->authenticated)
715 // Let's always set at least one of the bits.
716 int r = rand() % 7 + 1;
718 flag |= NETFLAG_CRYPTO0;
720 flag |= NETFLAG_CRYPTO1;
722 flag |= NETFLAG_CRYPTO2;
727 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qbool quakesignon_suppressreliables)
730 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
731 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
733 // if this packet was supposedly choked, but we find ourselves sending one
734 // anyway, make sure the size counting starts at zero
735 // (this mostly happens on level changes and disconnects and such)
736 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
737 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
739 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
741 if (protocol == PROTOCOL_QUAKEWORLD)
746 // note that it is ok to send empty messages to the qw server,
747 // otherwise it won't respond to us at all
749 sendreliable = false;
750 // if the remote side dropped the last reliable message, resend it
751 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
753 // if the reliable transmit buffer is empty, copy the current message out
754 if (!conn->sendMessageLength && conn->message.cursize)
756 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
757 conn->sendMessageLength = conn->message.cursize;
758 SZ_Clear(&conn->message); // clear the message buffer
759 conn->qw.reliable_sequence ^= 1;
762 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
763 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
764 // last received unreliable packet number, and last received reliable packet number (0 or 1)
765 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
767 conn->outgoing_unreliable_sequence++;
768 // client sends qport in every packet
769 if (conn == cls.netcon)
771 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
773 // also update cls.qw_outgoing_sequence
774 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
776 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
778 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
782 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
784 // add the reliable message if there is one
787 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
788 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
789 packetLen += conn->sendMessageLength;
790 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
793 // add the unreliable message if possible
794 if (packetLen + data->cursize <= 1400)
796 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
797 memcpy(sendbuffer + packetLen, data->data, data->cursize);
798 packetLen += data->cursize;
801 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
804 conn->unreliableMessagesSent++;
806 totallen += packetLen + 28;
810 unsigned int packetLen;
811 unsigned int dataLen;
816 // if a reliable message fragment has been lost, send it again
817 if (conn->sendMessageLength && (host.realtime - conn->lastSendTime) > 1.0)
819 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
821 dataLen = conn->sendMessageLength;
826 dataLen = MAX_PACKETFRAGMENT;
830 packetLen = NET_HEADERSIZE + dataLen;
832 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
833 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
834 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
836 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
838 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
839 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
841 conn->lastSendTime = host.realtime;
842 conn->packetsReSent++;
845 totallen += (int)sendmelen + 28;
848 // if we have a new reliable message to send, do so
849 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
851 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
853 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
854 conn->message.overflowed = true;
858 if (developer_networking.integer && conn == cls.netcon)
860 Con_Print("client sending reliable message to server:\n");
861 SZ_HexDumpToConsole(&conn->message);
864 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
865 conn->sendMessageLength = conn->message.cursize;
866 SZ_Clear(&conn->message);
868 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
870 dataLen = conn->sendMessageLength;
875 dataLen = MAX_PACKETFRAGMENT;
879 packetLen = NET_HEADERSIZE + dataLen;
881 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
882 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
883 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
885 conn->nq.sendSequence++;
887 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
889 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
891 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
893 conn->lastSendTime = host.realtime;
895 conn->reliableMessagesSent++;
897 totallen += (int)sendmelen + 28;
900 // if we have an unreliable message to send, do so
903 packetLen = NET_HEADERSIZE + data->cursize;
905 if (packetLen > (int)sizeof(sendbuffer))
907 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
911 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
912 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
913 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
915 conn->outgoing_unreliable_sequence++;
917 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
919 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
921 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
924 conn->unreliableMessagesSent++;
926 totallen += (int)sendmelen + 28;
930 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
935 qbool NetConn_HaveClientPorts(void)
937 return !!cl_numsockets;
940 qbool NetConn_HaveServerPorts(void)
942 return !!sv_numsockets;
945 void NetConn_CloseClientPorts(void)
947 for (;cl_numsockets > 0;cl_numsockets--)
948 if (cl_sockets[cl_numsockets - 1])
949 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
952 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
954 lhnetaddress_t address;
957 char addressstring2[1024];
958 if (addressstring && addressstring[0])
959 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
961 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
964 if ((s = LHNET_OpenSocket_Connectionless(&address)))
966 cl_sockets[cl_numsockets++] = s;
967 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
968 if (addresstype != LHNETADDRESSTYPE_LOOP)
969 Con_Printf("Client opened a socket on address %s\n", addressstring2);
973 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
974 Con_Printf(CON_ERROR "Client failed to open a socket on address %s\n", addressstring2);
978 Con_Printf(CON_ERROR "Client unable to parse address %s\n", addressstring);
981 void NetConn_OpenClientPorts(void)
984 NetConn_CloseClientPorts();
986 SV_LockThreadMutex(); // FIXME recursive?
987 Crypto_LoadKeys(); // client sockets
988 SV_UnlockThreadMutex();
990 port = bound(0, cl_netport.integer, 65535);
991 if (cl_netport.integer != port)
992 Cvar_SetValueQuick(&cl_netport, port);
994 Con_Printf("Client using an automatically assigned port\n");
996 Con_Printf("Client using port %i\n", port);
997 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
998 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
999 #ifndef NOSUPPORTIPV6
1000 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1004 void NetConn_CloseServerPorts(void)
1006 for (;sv_numsockets > 0;sv_numsockets--)
1007 if (sv_sockets[sv_numsockets - 1])
1008 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1011 static qbool NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1013 lhnetaddress_t address;
1016 char addressstring2[1024];
1019 for (port = defaultport; port <= defaultport + range; port++)
1021 if (addressstring && addressstring[0])
1022 success = LHNETADDRESS_FromString(&address, addressstring, port);
1024 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1027 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1029 sv_sockets[sv_numsockets++] = s;
1030 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1031 if (addresstype != LHNETADDRESSTYPE_LOOP)
1032 Con_Printf("Server listening on address %s\n", addressstring2);
1037 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1038 Con_Printf(CON_ERROR "Server failed to open socket on address %s\n", addressstring2);
1043 Con_Printf(CON_ERROR "Server unable to parse address %s\n", addressstring);
1044 // if it cant parse one address, it wont be able to parse another for sure
1051 void NetConn_OpenServerPorts(int opennetports)
1054 NetConn_CloseServerPorts();
1056 SV_LockThreadMutex(); // FIXME recursive?
1057 Crypto_LoadKeys(); // server sockets
1058 SV_UnlockThreadMutex();
1060 NetConn_UpdateSockets();
1061 port = bound(0, sv_netport.integer, 65535);
1064 if (sv_netport.integer != port)
1065 Cvar_SetValueQuick(&sv_netport, port);
1066 if (cls.state != ca_dedicated)
1067 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1070 #ifndef NOSUPPORTIPV6
1071 qbool ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1072 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1074 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1077 if (sv_numsockets == 0)
1078 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1081 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1083 int i, a = LHNETADDRESS_GetAddressType(address);
1084 for (i = 0;i < cl_numsockets;i++)
1085 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1086 return cl_sockets[i];
1090 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1092 int i, a = LHNETADDRESS_GetAddressType(address);
1093 for (i = 0;i < sv_numsockets;i++)
1094 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1095 return sv_sockets[i];
1099 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1102 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1103 conn->mysocket = mysocket;
1104 conn->peeraddress = *peeraddress;
1105 conn->lastMessageTime = host.realtime;
1106 conn->message.data = conn->messagedata;
1107 conn->message.maxsize = sizeof(conn->messagedata);
1108 conn->message.cursize = 0;
1109 // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
1110 // reduce effectiveness of connection request floods
1111 conn->timeout = host.realtime + net_connecttimeout.value;
1112 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1113 conn->next = netconn_list;
1114 netconn_list = conn;
1118 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1119 void NetConn_Close(netconn_t *conn)
1122 // remove connection from list
1124 // allow the client to reconnect immediately
1125 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1127 if (conn == netconn_list)
1128 netconn_list = conn->next;
1131 for (c = netconn_list;c;c = c->next)
1133 if (c->next == conn)
1135 c->next = conn->next;
1139 // not found in list, we'll avoid crashing here...
1147 static int clientport = -1;
1148 static int clientport2 = -1;
1150 // Call on disconnect, during startup, or if cl_port/cl_netport is changed
1151 static void NetConn_CL_UpdateSockets_Callback(cvar_t *var)
1153 if(cls.state != ca_dedicated)
1155 if (clientport2 != var->integer)
1157 clientport2 = var->integer;
1158 if (cls.state == ca_connected)
1159 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1162 if (cls.state == ca_disconnected && clientport != clientport2)
1164 clientport = clientport2;
1165 NetConn_CloseClientPorts();
1167 if (cl_numsockets == 0)
1168 NetConn_OpenClientPorts();
1172 static int hostport = -1;
1174 // Call when port/sv_netport is changed
1175 static void NetConn_sv_netport_Callback(cvar_t *var)
1177 if (hostport != var->integer)
1179 hostport = var->integer;
1181 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1185 void NetConn_UpdateSockets(void)
1189 // TODO add logic to automatically close sockets if needed
1190 LHNET_DefaultDSCP(net_tos_dscp.integer);
1192 for (j = 0;j < MAX_RCONS;j++)
1194 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1195 if(cls.rcon_commands[i][0])
1197 if(host.realtime > cls.rcon_timeout[i])
1200 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1201 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1202 cls.rcon_commands[i][0] = 0;
1210 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1212 int originallength = (int)length;
1213 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1214 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1215 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1219 if (protocol == PROTOCOL_QUAKEWORLD)
1221 unsigned int sequence, sequence_ack;
1222 qbool reliable_ack, reliable_message;
1226 sequence = LittleLong(*((int *)(data + 0)));
1227 sequence_ack = LittleLong(*((int *)(data + 4)));
1231 if (conn != cls.netcon)
1236 // 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?)
1237 //qport = LittleShort(*((int *)(data + 8)));
1242 conn->packetsReceived++;
1243 reliable_message = (sequence >> 31) != 0;
1244 reliable_ack = (sequence_ack >> 31) != 0;
1245 sequence &= ~(1<<31);
1246 sequence_ack &= ~(1<<31);
1247 if (sequence <= conn->qw.incoming_sequence)
1249 //Con_DPrint("Got a stale datagram\n");
1252 count = sequence - (conn->qw.incoming_sequence + 1);
1255 conn->droppedDatagrams += count;
1256 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1257 // If too may packets have been dropped, only write the
1258 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1259 // Because there's no point in writing more than
1260 // these as the netgraph is going to be full anyway.
1261 if (count > NETGRAPH_PACKETS)
1262 count = NETGRAPH_PACKETS;
1265 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1266 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1267 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1268 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1269 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1270 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1273 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1274 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1275 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1276 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1277 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1278 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1279 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1281 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1282 if (net_test.integer)
1284 if (conn->cleartime < host.realtime)
1285 conn->cleartime = host.realtime;
1288 if (reliable_ack == conn->qw.reliable_sequence)
1290 // received, now we will be able to send another reliable message
1291 conn->sendMessageLength = 0;
1292 conn->reliableMessagesReceived++;
1294 conn->qw.incoming_sequence = sequence;
1295 if (conn == cls.netcon)
1296 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1297 conn->qw.incoming_acknowledged = sequence_ack;
1298 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1299 if (reliable_message)
1300 conn->qw.incoming_reliable_sequence ^= 1;
1301 conn->lastMessageTime = host.realtime;
1302 conn->timeout = host.realtime + newtimeout;
1303 conn->unreliableMessagesReceived++;
1304 if (conn == cls.netcon)
1306 SZ_Clear(&cl_message);
1307 SZ_Write(&cl_message, data, (int)length);
1308 MSG_BeginReading(&cl_message);
1312 SZ_Clear(&sv_message);
1313 SZ_Write(&sv_message, data, (int)length);
1314 MSG_BeginReading(&sv_message);
1322 unsigned int sequence;
1327 originallength = (int)length;
1328 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1334 qlength = (unsigned int)BuffBigLong(data);
1335 flags = qlength & ~NETFLAG_LENGTH_MASK;
1336 qlength &= NETFLAG_LENGTH_MASK;
1337 // control packets were already handled
1338 if (!(flags & NETFLAG_CTL) && qlength == length)
1340 sequence = BuffBigLong(data + 4);
1341 conn->packetsReceived++;
1344 if (flags & NETFLAG_UNRELIABLE)
1346 if (sequence >= conn->nq.unreliableReceiveSequence)
1348 if (sequence > conn->nq.unreliableReceiveSequence)
1350 count = sequence - conn->nq.unreliableReceiveSequence;
1351 conn->droppedDatagrams += count;
1352 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1353 // If too may packets have been dropped, only write the
1354 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1355 // Because there's no point in writing more than
1356 // these as the netgraph is going to be full anyway.
1357 if (count > NETGRAPH_PACKETS)
1358 count = NETGRAPH_PACKETS;
1361 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1362 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1363 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1364 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1365 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1366 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1369 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1370 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1371 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1372 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1373 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1374 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1375 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1377 conn->nq.unreliableReceiveSequence = sequence + 1;
1378 conn->lastMessageTime = host.realtime;
1379 conn->timeout = host.realtime + newtimeout;
1380 conn->unreliableMessagesReceived++;
1383 if (conn == cls.netcon)
1385 SZ_Clear(&cl_message);
1386 SZ_Write(&cl_message, data, (int)length);
1387 MSG_BeginReading(&cl_message);
1391 SZ_Clear(&sv_message);
1392 SZ_Write(&sv_message, data, (int)length);
1393 MSG_BeginReading(&sv_message);
1399 // Con_DPrint("Got a stale datagram\n");
1402 else if (flags & NETFLAG_ACK)
1404 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1405 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1407 if (sequence == (conn->nq.sendSequence - 1))
1409 if (sequence == conn->nq.ackSequence)
1411 conn->nq.ackSequence++;
1412 if (conn->nq.ackSequence != conn->nq.sendSequence)
1413 Con_DPrint("ack sequencing error\n");
1414 conn->lastMessageTime = host.realtime;
1415 conn->timeout = host.realtime + newtimeout;
1416 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1418 unsigned int packetLen;
1419 unsigned int dataLen;
1422 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1423 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1425 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1427 dataLen = conn->sendMessageLength;
1432 dataLen = MAX_PACKETFRAGMENT;
1436 packetLen = NET_HEADERSIZE + dataLen;
1438 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1439 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1440 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1442 conn->nq.sendSequence++;
1444 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1445 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1447 conn->lastSendTime = host.realtime;
1448 conn->packetsSent++;
1452 conn->sendMessageLength = 0;
1455 // Con_DPrint("Duplicate ACK received\n");
1458 // Con_DPrint("Stale ACK received\n");
1461 else if (flags & NETFLAG_DATA)
1463 unsigned char temppacket[8];
1464 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1465 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1467 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1469 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1470 StoreBigLong(temppacket + 4, sequence);
1471 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1473 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1474 if (sequence == conn->nq.receiveSequence)
1476 conn->lastMessageTime = host.realtime;
1477 conn->timeout = host.realtime + newtimeout;
1478 conn->nq.receiveSequence++;
1479 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1480 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1481 conn->receiveMessageLength += (int)length;
1483 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1484 "Dropping the message!\n", sequence );
1485 conn->receiveMessageLength = 0;
1488 if (flags & NETFLAG_EOM)
1490 conn->reliableMessagesReceived++;
1491 length = conn->receiveMessageLength;
1492 conn->receiveMessageLength = 0;
1495 if (conn == cls.netcon)
1497 SZ_Clear(&cl_message);
1498 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1499 MSG_BeginReading(&cl_message);
1503 SZ_Clear(&sv_message);
1504 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1505 MSG_BeginReading(&sv_message);
1512 conn->receivedDuplicateCount++;
1520 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1523 cls.connect_trying = false;
1525 M_Update_Return_Reason("");
1527 // Disconnect from the current server or stop demo playback
1528 if(cls.state == ca_connected || cls.demoplayback)
1530 // allocate a net connection to keep track of things
1531 cls.netcon = NetConn_Open(mysocket, peeraddress);
1532 crypto = &cls.netcon->crypto;
1533 if(cls.crypto.authenticated)
1535 Crypto_FinishInstance(crypto, &cls.crypto);
1536 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1537 crypto->use_aes ? "Encrypted" : "Authenticated",
1538 cls.netcon->address,
1539 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1540 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1541 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1542 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1543 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1544 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1547 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1548 key_dest = key_game;
1552 cls.demonum = -1; // not in the demo loop now
1553 cls.state = ca_connected;
1554 cls.signon = 0; // need all the signon messages before playing
1555 cls.protocol = initialprotocol;
1556 // reset move sequence numbering on this new connection
1557 cls.servermovesequence = 0;
1558 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1559 CL_ForwardToServer("new");
1560 if (cls.protocol == PROTOCOL_QUAKE)
1562 // write a keepalive (clc_nop) as it seems to greatly improve the
1563 // chances of connecting to a netquake server
1565 unsigned char buf[4];
1566 memset(&msg, 0, sizeof(msg));
1568 msg.maxsize = sizeof(buf);
1569 MSG_WriteChar(&msg, clc_nop);
1570 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1574 int NetConn_IsLocalGame(void)
1576 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1582 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1586 serverlist_entry_t *entry = NULL;
1588 // search the cache for this server and update it
1589 for (n = 0;n < serverlist_cachecount;n++) {
1590 entry = &serverlist_cache[ n ];
1591 if (!strcmp(addressstring, entry->info.cname))
1595 if (n == serverlist_cachecount)
1597 // LAN search doesnt require an answer from the master server so we wont
1598 // know the ping nor will it be initialized already...
1601 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1604 if (serverlist_maxcachecount <= serverlist_cachecount)
1606 serverlist_maxcachecount += 64;
1607 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1609 entry = &serverlist_cache[n];
1611 memset(entry, 0, sizeof(*entry));
1612 // store the data the engine cares about (address and ping)
1613 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1614 entry->info.ping = 100000;
1615 entry->querytime = host.realtime;
1616 // if not in the slist menu we should print the server to console
1617 if (serverlist_consoleoutput)
1618 Con_Printf("querying %s\n", addressstring);
1619 ++serverlist_cachecount;
1621 // if this is the first reply from this server, count it as having replied
1622 pingtime = (int)((host.realtime - entry->querytime) * 1000.0 + 0.5);
1623 pingtime = bound(0, pingtime, 9999);
1624 if (entry->query == SQS_REFRESHING) {
1625 entry->info.ping = pingtime;
1626 entry->query = SQS_QUERIED;
1628 // convert to unsigned to catch the -1
1629 // 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]
1630 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1634 // other server info is updated by the caller
1638 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1640 serverlist_entry_t *entry = &serverlist_cache[n];
1641 serverlist_info_t *info = &entry->info;
1642 // update description strings for engine menu and console output
1643 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);
1644 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1646 info->gameversion != gameversion.integer
1649 gameversion_min.integer >= 0 // min/max range set by user/mod?
1650 && gameversion_max.integer >= 0
1651 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1652 && gameversion_max.integer >= info->gameversion
1655 info->mod, info->map);
1656 if (entry->query == SQS_QUERIED)
1658 if(!serverlist_paused)
1659 ServerList_ViewList_Remove(entry);
1661 // if not in the slist menu we should print the server to console (if wanted)
1662 else if( serverlist_consoleoutput )
1663 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1664 // and finally, update the view set
1665 if(!serverlist_paused)
1666 ServerList_ViewList_Insert( entry );
1667 // update the entry's state
1668 serverlist_cache[n].query = SQS_QUERIED;
1671 // returns true, if it's sensible to continue the processing
1672 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qbool isfavorite ) {
1674 serverlist_entry_t *entry;
1676 // ignore the rest of the message if the serverlist is full
1677 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1679 // also ignore it if we have already queried it (other master server response)
1680 for( n = 0 ; n < serverlist_cachecount ; n++ )
1681 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1684 if( n < serverlist_cachecount ) {
1685 // the entry has already been queried once or
1689 if (serverlist_maxcachecount <= n)
1691 serverlist_maxcachecount += 64;
1692 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1695 entry = &serverlist_cache[n];
1697 memset(entry, 0, sizeof(*entry));
1698 entry->protocol = protocol;
1699 // store the data the engine cares about (address and ping)
1700 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1702 entry->info.isfavorite = isfavorite;
1704 // no, then reset the ping right away
1705 entry->info.ping = -1;
1706 // we also want to increase the serverlist_cachecount then
1707 serverlist_cachecount++;
1710 entry->query = SQS_QUERYING;
1715 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qbool isextended)
1718 if (serverlist_consoleoutput)
1719 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1722 char ipstring [128];
1725 if (data[0] == '\\')
1727 unsigned short port = data[5] * 256 + data[6];
1729 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1730 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1732 // move on to next address in packet
1737 else if (data[0] == '/' && isextended && length >= 19)
1739 unsigned short port = data[17] * 256 + data[18];
1747 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1749 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1752 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1753 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1754 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1760 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1761 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1762 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1767 // move on to next address in packet
1773 Con_Print("Error while parsing the server list\n");
1777 if (serverlist_consoleoutput && developer_networking.integer)
1778 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1780 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1786 // begin or resume serverlist queries
1787 serverlist_querysleep = false;
1788 serverlist_querywaittime = host.realtime + 3;
1792 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1796 char *string, addressstring2[128];
1797 char stringbuf[16384];
1798 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1801 char infostringvalue[MAX_INPUTLINE];
1806 // quakeworld ingame packet
1807 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1809 // convert the address to a string incase we need it
1810 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1812 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1814 // received a command string - strip off the packaging and put it
1815 // into our string buffer with NULL termination
1818 length = min(length, (int)sizeof(stringbuf) - 1);
1819 memcpy(stringbuf, data, length);
1820 stringbuf[length] = 0;
1823 if (developer_networking.integer)
1825 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1826 Com_HexDumpToConsole(data, length);
1829 sendlength = sizeof(senddata) - 4;
1830 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1832 case CRYPTO_NOMATCH:
1838 memcpy(senddata, "\377\377\377\377", 4);
1839 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1842 case CRYPTO_DISCARD:
1845 memcpy(senddata, "\377\377\377\377", 4);
1846 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1850 case CRYPTO_REPLACE:
1851 string = senddata+4;
1852 length = (int)sendlength;
1856 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1859 for (j = 0;j < MAX_RCONS;j++)
1861 // note: this value from i is used outside the loop too...
1862 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1863 if(cls.rcon_commands[i][0])
1864 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1873 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1874 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1876 e = strchr(rcon_password.string, ' ');
1877 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1879 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1883 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1884 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1885 cls.rcon_commands[i][0] = 0;
1888 for (k = 0;k < MAX_RCONS;k++)
1889 if(cls.rcon_commands[k][0])
1890 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1895 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1896 // extend the timeout on other requests as we asked for a challenge
1897 for (l = 0;l < MAX_RCONS;l++)
1898 if(cls.rcon_commands[l][0])
1899 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1900 cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
1903 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1907 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1909 // darkplaces or quake3
1910 char protocolnames[1400];
1911 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1912 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1913 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1916 Protocol_Names(protocolnames, sizeof(protocolnames));
1918 M_Update_Return_Reason("Got challenge response");
1920 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1921 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1922 // TODO: add userinfo stuff here instead of using NQ commands?
1923 memcpy(senddata, "\377\377\377\377", 4);
1924 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1925 NetConn_WriteString(mysocket, senddata, peeraddress);
1928 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1930 // darkplaces or quake3
1931 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1932 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1936 M_Update_Return_Reason("Accepted");
1938 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1941 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1943 char rejectreason[128];
1944 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1945 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1948 cls.connect_trying = false;
1950 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1951 memcpy(rejectreason, string, length);
1952 rejectreason[length] = 0;
1954 M_Update_Return_Reason(rejectreason);
1959 if(key_dest != key_game)
1961 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1963 serverlist_info_t *info;
1968 // search the cache for this server and update it
1969 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1973 info = &serverlist_cache[n].info;
1978 info->qcstatus[0] = 0;
1979 info->players[0] = 0;
1980 info->protocol = -1;
1981 info->numplayers = 0;
1983 info->maxplayers = 0;
1984 info->gameversion = 0;
1986 p = strchr(string, '\n');
1989 *p = 0; // cut off the string there
1993 Con_Printf("statusResponse without players block?\n");
1995 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1996 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1997 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1998 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1999 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2000 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2001 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2002 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2003 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2004 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2005 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2006 info->numhumans = info->numplayers - max(0, info->numbots);
2007 info->freeslots = info->maxplayers - info->numplayers;
2009 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2013 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2015 serverlist_info_t *info;
2019 // search the cache for this server and update it
2020 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2024 info = &serverlist_cache[n].info;
2029 info->qcstatus[0] = 0;
2030 info->players[0] = 0;
2031 info->protocol = -1;
2032 info->numplayers = 0;
2034 info->maxplayers = 0;
2035 info->gameversion = 0;
2037 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2038 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2039 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2040 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2041 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2042 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2043 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2044 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2045 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2046 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2047 info->numhumans = info->numplayers - max(0, info->numbots);
2048 info->freeslots = info->maxplayers - info->numplayers;
2050 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2054 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2056 // Extract the IP addresses
2059 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2062 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2064 // Extract the IP addresses
2067 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2070 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2072 // Extract the IP addresses
2076 if (serverlist_consoleoutput)
2077 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2078 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2080 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2081 if (serverlist_consoleoutput && developer_networking.integer)
2082 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2084 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2088 // move on to next address in packet
2092 // begin or resume serverlist queries
2093 serverlist_querysleep = false;
2094 serverlist_querywaittime = host.realtime + 3;
2099 if (!strncmp(string, "extResponse ", 12))
2101 ++cl_net_extresponse_count;
2102 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2103 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2104 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2105 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2108 if (!strncmp(string, "ping", 4))
2110 if (developer_extra.integer)
2111 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2112 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2115 if (!strncmp(string, "ack", 3))
2117 // QuakeWorld compatibility
2118 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2120 // challenge message
2121 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2122 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2125 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2127 M_Update_Return_Reason("Got QuakeWorld challenge response");
2129 cls.qw_qport = qport.integer;
2130 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2131 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2132 memcpy(senddata, "\377\377\377\377", 4);
2133 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);
2134 NetConn_WriteString(mysocket, senddata, peeraddress);
2137 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2140 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2141 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2145 M_Update_Return_Reason("QuakeWorld Accepted");
2147 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2150 if (length > 2 && !memcmp(string, "n\\", 2))
2153 serverlist_info_t *info;
2157 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2158 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2161 // search the cache for this server and update it
2162 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2166 info = &serverlist_cache[n].info;
2167 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2168 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2169 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2170 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2172 info->numplayers = 0; // updated below
2173 info->numhumans = 0; // updated below
2174 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2175 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2177 // count active players on server
2178 // (we could gather more info, but we're just after the number)
2179 s = strchr(string, '\n');
2183 while (s < string + length)
2185 for (;s < string + length && *s != '\n';s++)
2187 if (s >= string + length)
2195 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2199 if (string[0] == 'n')
2201 // qw print command, used by rcon replies too
2202 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2203 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2206 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2208 // we may not have liked the packet, but it was a command packet, so
2209 // we're done processing this packet now
2212 // quakeworld ingame packet
2213 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2216 CL_ParseServerMessage();
2219 // netquake control packets, supported for compatibility only
2220 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2224 serverlist_info_t *info;
2229 SZ_Clear(&cl_message);
2230 SZ_Write(&cl_message, data, length);
2231 MSG_BeginReading(&cl_message);
2232 c = MSG_ReadByte(&cl_message);
2236 if (developer_extra.integer)
2237 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2238 if (cls.connect_trying)
2240 lhnetaddress_t clientportaddress;
2241 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2242 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2245 clientportaddress = *peeraddress;
2246 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2247 // extra ProQuake stuff
2249 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2251 cls.proquake_servermod = 0;
2253 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2255 cls.proquake_serverversion = 0;
2257 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2259 cls.proquake_serverflags = 0;
2260 if (cls.proquake_servermod == 1)
2261 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2262 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2263 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2265 M_Update_Return_Reason("Accepted");
2267 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2271 if (developer_extra.integer) {
2272 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2275 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2277 cls.connect_trying = false;
2279 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2282 case CCREP_SERVER_INFO:
2283 if (developer_extra.integer)
2284 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2286 // LadyHavoc: because the quake server may report weird addresses
2287 // we just ignore it and keep the real address
2288 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2289 // search the cache for this server and update it
2290 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2294 info = &serverlist_cache[n].info;
2295 strlcpy(info->game, "Quake", sizeof(info->game));
2296 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2297 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2298 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2299 info->numplayers = MSG_ReadByte(&cl_message);
2300 info->maxplayers = MSG_ReadByte(&cl_message);
2301 info->protocol = MSG_ReadByte(&cl_message);
2303 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2306 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2307 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2308 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2311 if (developer_extra.integer)
2312 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2314 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2316 case CCREP_PLAYER_INFO:
2317 // we got a CCREP_PLAYER_INFO??
2318 //if (developer_extra.integer)
2319 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2321 case CCREP_RULE_INFO:
2322 // we got a CCREP_RULE_INFO??
2323 //if (developer_extra.integer)
2324 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2329 SZ_Clear(&cl_message);
2330 // we may not have liked the packet, but it was a valid control
2331 // packet, so we're done processing this packet now
2335 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2336 CL_ParseServerMessage();
2341 void NetConn_QueryQueueFrame(void)
2347 static double querycounter = 0;
2349 if(!net_slist_pause.integer && serverlist_paused)
2350 ServerList_RebuildViewList();
2351 serverlist_paused = net_slist_pause.integer != 0;
2353 if (serverlist_querysleep)
2356 // apply a cool down time after master server replies,
2357 // to avoid messing up the ping times on the servers
2358 if (serverlist_querywaittime > host.realtime)
2361 // each time querycounter reaches 1.0 issue a query
2362 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2363 maxqueries = (int)querycounter;
2364 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2365 querycounter -= maxqueries;
2367 if( maxqueries == 0 ) {
2371 // scan serverlist and issue queries as needed
2372 serverlist_querysleep = true;
2374 timeouttime = host.realtime - net_slist_timeout.value;
2375 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2377 serverlist_entry_t *entry = &serverlist_cache[ index ];
2378 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2383 serverlist_querysleep = false;
2384 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 ) {
2420 // yes, so update the reply count (since its not responding anymore)
2422 if(!serverlist_paused)
2423 ServerList_ViewList_Remove(entry);
2425 entry->query = SQS_TIMEDOUT;
2431 void NetConn_ClientFrame(void)
2434 lhnetaddress_t peeraddress;
2435 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2436 NetConn_UpdateSockets();
2437 if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2440 if (cls.connect_remainingtries == 0)
2441 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2443 cls.connect_nextsendtime = host.realtime + 1;
2444 cls.connect_remainingtries--;
2445 if (cls.connect_remainingtries <= -10)
2447 cls.connect_trying = false;
2449 M_Update_Return_Reason("Connect: Failed");
2453 // try challenge first (newer DP server or QW)
2454 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2455 // then try netquake as a fallback (old server, or netquake)
2456 SZ_Clear(&cl_message);
2457 // save space for the header, filled in later
2458 MSG_WriteLong(&cl_message, 0);
2459 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2460 MSG_WriteString(&cl_message, "QUAKE");
2461 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2462 // extended proquake stuff
2463 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2464 // this version matches ProQuake 3.40, the first version to support
2465 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2466 // higher clients, so we pretend we are that version...
2467 MSG_WriteByte(&cl_message, 34); // version * 10
2468 MSG_WriteByte(&cl_message, 0); // flags
2469 MSG_WriteLong(&cl_message, 0); // password
2470 // write the packetsize now...
2471 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2472 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2473 SZ_Clear(&cl_message);
2475 for (i = 0;i < cl_numsockets;i++)
2477 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2479 // R_TimeReport("clientreadnetwork");
2480 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2481 // R_TimeReport("clientparsepacket");
2485 NetConn_QueryQueueFrame();
2487 if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2488 CL_DisconnectEx(true, "Connection timed out");
2491 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2495 for (i = 0;i < bufferlength - 1;i++)
2499 c = rand () % (127 - 33) + 33;
2500 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2506 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2507 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2509 prvm_prog_t *prog = SVVM_prog;
2511 unsigned int nb_clients = 0, nb_bots = 0, i;
2514 const char *crypto_idstring;
2515 const char *worldstatusstr;
2517 // How many clients are there?
2518 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2520 if (svs.clients[i].active)
2523 if (!svs.clients[i].netconnection)
2529 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2530 if(worldstatusstr && *worldstatusstr)
2535 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2536 if(*q != '\\' && *q != '\n')
2541 /// \TODO: we should add more information for the full status string
2542 crypto_idstring = Crypto_GetInfoResponseDataString();
2543 length = dpsnprintf(out_msg, out_size,
2544 "\377\377\377\377%s\x0A"
2545 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2546 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2551 fullstatus ? "statusResponse" : "infoResponse",
2552 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2553 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2554 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2555 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2556 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2557 fullstatus ? "\n" : "");
2559 // Make sure it fits in the buffer
2569 savelength = length;
2571 ptr = out_msg + length;
2572 left = (int)out_size - length;
2574 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2576 client_t *client = &svs.clients[i];
2579 int nameind, cleanind, pingvalue;
2581 char cleanname [sizeof(client->name)];
2582 const char *statusstr;
2585 // Remove all characters '"' and '\' in the player name
2590 curchar = client->name[nameind++];
2591 if (curchar != '"' && curchar != '\\')
2593 cleanname[cleanind++] = curchar;
2594 if (cleanind == sizeof(cleanname) - 1)
2597 } while (curchar != '\0');
2598 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2600 pingvalue = (int)(client->ping * 1000.0f);
2601 if(client->netconnection)
2602 pingvalue = bound(1, pingvalue, 9999);
2607 ed = PRVM_EDICT_NUM(i + 1);
2608 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2609 if(statusstr && *statusstr)
2614 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2615 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2620 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2622 if(client->frags == -666) // spectator
2623 strlcpy(teambuf, " 0", sizeof(teambuf));
2624 else if(client->colors == 0x44) // red team
2625 strlcpy(teambuf, " 1", sizeof(teambuf));
2626 else if(client->colors == 0xDD) // blue team
2627 strlcpy(teambuf, " 2", sizeof(teambuf));
2628 else if(client->colors == 0xCC) // yellow team
2629 strlcpy(teambuf, " 3", sizeof(teambuf));
2630 else if(client->colors == 0x99) // pink team
2631 strlcpy(teambuf, " 4", sizeof(teambuf));
2633 strlcpy(teambuf, " 0", sizeof(teambuf));
2638 // note: team number is inserted according to SoF2 protocol
2640 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2646 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2655 // turn it into an infoResponse!
2656 out_msg[savelength] = 0;
2657 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2658 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2673 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2675 size_t floodslotnum, bestfloodslotnum;
2676 double bestfloodtime;
2677 lhnetaddress_t noportpeeraddress;
2678 // see if this is a connect flood
2679 noportpeeraddress = *peeraddress;
2680 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2681 bestfloodslotnum = 0;
2682 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2683 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2685 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2687 bestfloodtime = floodlist[floodslotnum].lasttime;
2688 bestfloodslotnum = floodslotnum;
2690 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2692 // this address matches an ongoing flood address
2693 if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2697 // renew the ban on this address so it does not expire
2698 // until the flood has subsided
2699 floodlist[floodslotnum].lasttime = host.realtime;
2701 //Con_Printf("Flood detected!\n");
2704 // the flood appears to have subsided, so allow this
2705 bestfloodslotnum = floodslotnum; // reuse the same slot
2709 // begin a new timeout on this address
2710 floodlist[bestfloodslotnum].address = noportpeeraddress;
2711 floodlist[bestfloodslotnum].lasttime = host.realtime;
2712 //Con_Printf("Flood detection initiated!\n");
2716 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2718 size_t floodslotnum;
2719 lhnetaddress_t noportpeeraddress;
2720 // see if this is a connect flood
2721 noportpeeraddress = *peeraddress;
2722 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2723 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2725 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2727 // this address matches an ongoing flood address
2729 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2730 floodlist[floodslotnum].lasttime = 0;
2731 //Con_Printf("Flood cleared!\n");
2736 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2738 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2744 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2748 t1 = (long) time(NULL);
2749 t2 = strtol(s, NULL, 0);
2750 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2753 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2756 return !memcmp(mdfourbuf, hash, 16);
2759 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2765 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2769 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2772 // validate the challenge
2773 for (i = 0;i < MAX_CHALLENGES;i++)
2774 if(challenges[i].time > 0)
2775 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2777 // if the challenge is not recognized, drop the packet
2778 if (i == MAX_CHALLENGES)
2781 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2784 if(memcmp(mdfourbuf, hash, 16))
2787 // unmark challenge to prevent replay attacks
2788 challenges[i].time = 0;
2793 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2796 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2800 return !strcmp(password, hash);
2803 /// returns a string describing the user level, or NULL for auth failure
2804 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)
2806 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2807 static char buf[MAX_INPUTLINE];
2809 qbool restricted = false;
2810 qbool have_usernames = false;
2811 static char vabuf[1024];
2813 userpass_start = rcon_password.string;
2814 while((userpass_end = strchr(userpass_start, ' ')))
2816 have_usernames = true;
2817 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2818 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2819 if(comparator(peeraddress, buf, password, cs, cslen))
2821 userpass_start = userpass_end + 1;
2823 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2825 userpass_end = userpass_start + strlen(userpass_start);
2826 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2831 have_usernames = false;
2832 userpass_start = rcon_restricted_password.string;
2833 while((userpass_end = strchr(userpass_start, ' ')))
2835 have_usernames = true;
2836 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2837 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2838 if(comparator(peeraddress, buf, password, cs, cslen))
2840 userpass_start = userpass_end + 1;
2842 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2844 userpass_end = userpass_start + strlen(userpass_start);
2845 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2849 return NULL; // DENIED
2852 for(text = s; text != endpos; ++text)
2853 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2854 return NULL; // block possible exploits against the parser/alias expansion
2858 size_t l = strlen(s);
2861 hasquotes = (strchr(s, '"') != NULL);
2862 // sorry, we can't allow these substrings in wildcard expressions,
2863 // as they can mess with the argument counts
2864 text = rcon_restricted_commands.string;
2865 while(COM_ParseToken_Console(&text))
2867 // com_token now contains a pattern to check for...
2868 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2871 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2874 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2876 if(!strcmp(com_token, s))
2879 else // single-arg expression? must match the beginning of the command
2881 if(!strcmp(com_token, s))
2883 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2887 // if we got here, nothing matched!
2895 userpass_startpass = strchr(userpass_start, ':');
2896 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2897 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2899 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2902 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qbool proquakeprotocol)
2906 // looks like a legitimate rcon command with the correct password
2907 const char *s_ptr = s;
2908 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2909 while(s_ptr != endpos)
2911 size_t l = strlen(s_ptr);
2913 Con_Printf(" %s;", s_ptr);
2918 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2919 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2922 size_t l = strlen(s);
2925 client_t *host_client_save = host_client;
2926 Cmd_ExecuteString(cmd_local, s, src_local, true);
2927 host_client = host_client_save;
2928 // in case it is a command that changes host_client (like restart)
2932 Con_Rcon_Redirect_End();
2936 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2940 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2942 int i, ret, clientnum, best;
2944 char *string, response[2800], addressstring2[128];
2945 static char stringbuf[16384]; // server only
2946 qbool islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2947 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2948 size_t sendlength, response_len;
2949 char infostringvalue[MAX_INPUTLINE];
2954 // convert the address to a string incase we need it
2955 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2957 // see if we can identify the sender as a local player
2958 // (this is necessary for rcon to send a reliable reply if the client is
2959 // actually on the server, not sending remotely)
2960 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2961 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2963 if (i == svs.maxclients)
2966 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2968 // received a command string - strip off the packaging and put it
2969 // into our string buffer with NULL termination
2972 length = min(length, (int)sizeof(stringbuf) - 1);
2973 memcpy(stringbuf, data, length);
2974 stringbuf[length] = 0;
2977 if (developer_extra.integer)
2979 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2980 Com_HexDumpToConsole(data, length);
2983 sendlength = sizeof(senddata) - 4;
2984 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2986 case CRYPTO_NOMATCH:
2992 memcpy(senddata, "\377\377\377\377", 4);
2993 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2996 case CRYPTO_DISCARD:
2999 memcpy(senddata, "\377\377\377\377", 4);
3000 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3004 case CRYPTO_REPLACE:
3005 string = senddata+4;
3006 length = (int)sendlength;
3010 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3012 for (i = 0, best = 0, besttime = host.realtime;i < MAX_CHALLENGES;i++)
3014 if(challenges[i].time > 0)
3015 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3017 if (besttime > challenges[i].time)
3018 besttime = challenges[best = i].time;
3020 // if we did not find an exact match, choose the oldest and
3021 // update address and string
3022 if (i == MAX_CHALLENGES)
3025 challenges[i].address = *peeraddress;
3026 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3030 // flood control: drop if requesting challenge too often
3031 if(challenges[i].time > host.realtime - net_challengefloodblockingtimeout.value)
3034 challenges[i].time = host.realtime;
3035 // send the challenge
3036 memcpy(response, "\377\377\377\377", 4);
3037 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3038 response_len = strlen(response) + 1;
3039 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3040 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3043 if (length > 8 && !memcmp(string, "connect\\", 8))
3047 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3051 if(crypto && crypto->authenticated)
3053 // no need to check challenge
3054 if(crypto_developer.integer)
3056 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3057 crypto->use_aes ? "Encrypted" : "Authenticated",
3059 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3060 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3061 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3062 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3063 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3064 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3070 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3072 // validate the challenge
3073 for (i = 0;i < MAX_CHALLENGES;i++)
3074 if(challenges[i].time > 0)
3075 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3077 // if the challenge is not recognized, drop the packet
3078 if (i == MAX_CHALLENGES)
3083 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3084 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3086 if(!(islocal || sv_public.integer > -2))
3088 if (developer_extra.integer)
3089 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3090 memcpy(response, "\377\377\377\377", 4);
3091 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3092 NetConn_WriteString(mysocket, response, peeraddress);
3096 // check engine protocol
3097 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3099 if (developer_extra.integer)
3100 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3101 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3105 // see if this is a duplicate connection request or a disconnected
3106 // client who is rejoining to the same client slot
3107 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3109 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3111 // this is a known client...
3112 if(crypto && crypto->authenticated)
3114 // reject if changing key!
3115 if(client->netconnection->crypto.authenticated)
3118 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3120 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3122 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3124 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3127 if (developer_extra.integer)
3128 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3129 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3136 // reject if downgrading!
3137 if(client->netconnection->crypto.authenticated)
3139 if (developer_extra.integer)
3140 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3141 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3147 // client crashed and is coming back,
3148 // keep their stuff intact
3149 if (developer_extra.integer)
3150 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3151 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3152 if(crypto && crypto->authenticated)
3153 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3154 SV_SendServerinfo(client);
3158 // client is still trying to connect,
3159 // so we send a duplicate reply
3160 if (developer_extra.integer)
3161 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3162 if(crypto && crypto->authenticated)
3163 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3164 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3170 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3173 // find an empty client slot for this new client
3174 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3177 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3179 // allocated connection
3180 if (developer_extra.integer)
3181 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3182 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3183 // now set up the client
3184 if(crypto && crypto->authenticated)
3185 Crypto_FinishInstance(&conn->crypto, crypto);
3186 SV_ConnectClient(clientnum, conn);
3187 NetConn_Heartbeat(1);
3192 // no empty slots found - server is full
3193 if (developer_extra.integer)
3194 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3195 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3199 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3201 const char *challenge = NULL;
3203 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3206 // If there was a challenge in the getinfo message
3207 if (length > 8 && string[7] == ' ')
3208 challenge = string + 8;
3210 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3212 if (developer_extra.integer)
3213 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3214 NetConn_WriteString(mysocket, response, peeraddress);
3218 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3220 const char *challenge = NULL;
3222 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3225 // If there was a challenge in the getinfo message
3226 if (length > 10 && string[9] == ' ')
3227 challenge = string + 10;
3229 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3231 if (developer_extra.integer)
3232 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3233 NetConn_WriteString(mysocket, response, peeraddress);
3237 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3239 char *password = string + 20;
3240 char *timeval = string + 37;
3241 char *s = strchr(timeval, ' ');
3242 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3243 const char *userlevel;
3245 if(rcon_secure.integer > 1)
3249 return true; // invalid packet
3252 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3253 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3256 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3258 char *password = string + 25;
3259 char *challenge = string + 42;
3260 char *s = strchr(challenge, ' ');
3261 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3262 const char *userlevel;
3264 return true; // invalid packet
3267 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3268 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3271 if (length >= 5 && !memcmp(string, "rcon ", 5))
3274 char *s = string + 5;
3275 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3278 if(rcon_secure.integer > 0)
3281 for (j = 0;!ISWHITESPACE(*s);s++)
3282 if (j < (int)sizeof(password) - 1)
3284 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3287 if (!ISWHITESPACE(password[0]))
3289 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3290 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3294 if (!strncmp(string, "extResponse ", 12))
3296 ++sv_net_extresponse_count;
3297 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3298 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3299 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3300 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3303 if (!strncmp(string, "ping", 4))
3305 if (developer_extra.integer)
3306 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3307 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3310 if (!strncmp(string, "ack", 3))
3312 // we may not have liked the packet, but it was a command packet, so
3313 // we're done processing this packet now
3316 // netquake control packets, supported for compatibility only, and only
3317 // when running game protocols that are normally served via this connection
3319 // (this protects more modern protocols against being used for
3320 // Quake packet flood Denial Of Service attacks)
3321 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)
3325 const char *protocolname;
3326 client_t *knownclient;
3327 client_t *newclient;
3330 SZ_Clear(&sv_message);
3331 SZ_Write(&sv_message, data, length);
3332 MSG_BeginReading(&sv_message);
3333 c = MSG_ReadByte(&sv_message);
3337 if (developer_extra.integer)
3338 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3339 if(!(islocal || sv_public.integer > -2))
3341 if (developer_extra.integer)
3342 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3343 SZ_Clear(&sv_message);
3344 // save space for the header, filled in later
3345 MSG_WriteLong(&sv_message, 0);
3346 MSG_WriteByte(&sv_message, CCREP_REJECT);
3347 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3348 MSG_WriteString(&sv_message, "\n");
3349 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3350 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3351 SZ_Clear(&sv_message);
3355 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3356 protocolnumber = MSG_ReadByte(&sv_message);
3357 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3359 if (developer_extra.integer)
3360 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3361 SZ_Clear(&sv_message);
3362 // save space for the header, filled in later
3363 MSG_WriteLong(&sv_message, 0);
3364 MSG_WriteByte(&sv_message, CCREP_REJECT);
3365 MSG_WriteString(&sv_message, "Incompatible version.\n");
3366 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3367 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3368 SZ_Clear(&sv_message);
3372 // see if this connect request comes from a known client
3373 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3375 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3377 // this is either a duplicate connection request
3378 // or coming back from a timeout
3379 // (if so, keep their stuff intact)
3381 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3382 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3384 if (developer_extra.integer)
3385 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3386 SZ_Clear(&sv_message);
3387 // save space for the header, filled in later
3388 MSG_WriteLong(&sv_message, 0);
3389 MSG_WriteByte(&sv_message, CCREP_REJECT);
3390 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3391 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3392 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3393 SZ_Clear(&sv_message);
3398 if (developer_extra.integer)
3399 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3400 SZ_Clear(&sv_message);
3401 // save space for the header, filled in later
3402 MSG_WriteLong(&sv_message, 0);
3403 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3404 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3405 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3406 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3407 SZ_Clear(&sv_message);
3409 // if client is already spawned, re-send the
3410 // serverinfo message as they'll need it to play
3411 if (knownclient->begun)
3412 SV_SendServerinfo(knownclient);
3417 // this is a new client, check for connection flood
3418 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3421 // find a slot for the new client
3422 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3425 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3427 // connect to the client
3428 // everything is allocated, just fill in the details
3429 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3430 if (developer_extra.integer)
3431 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3432 // send back the info about the server connection
3433 SZ_Clear(&sv_message);
3434 // save space for the header, filled in later
3435 MSG_WriteLong(&sv_message, 0);
3436 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3437 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3438 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3439 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3440 SZ_Clear(&sv_message);
3441 // now set up the client struct
3442 SV_ConnectClient(clientnum, conn);
3443 NetConn_Heartbeat(1);
3448 if (developer_extra.integer)
3449 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3450 // no room; try to let player know
3451 SZ_Clear(&sv_message);
3452 // save space for the header, filled in later
3453 MSG_WriteLong(&sv_message, 0);
3454 MSG_WriteByte(&sv_message, CCREP_REJECT);
3455 MSG_WriteString(&sv_message, "Server is full.\n");
3456 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3457 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3458 SZ_Clear(&sv_message);
3460 case CCREQ_SERVER_INFO:
3461 if (developer_extra.integer)
3462 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3463 if(!(islocal || sv_public.integer > -1))
3466 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3469 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3472 char myaddressstring[128];
3473 if (developer_extra.integer)
3474 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3475 SZ_Clear(&sv_message);
3476 // save space for the header, filled in later
3477 MSG_WriteLong(&sv_message, 0);
3478 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3479 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3480 MSG_WriteString(&sv_message, myaddressstring);
3481 MSG_WriteString(&sv_message, hostname.string);
3482 MSG_WriteString(&sv_message, sv.name);
3483 // How many clients are there?
3484 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3485 if (svs.clients[i].active)
3487 MSG_WriteByte(&sv_message, numclients);
3488 MSG_WriteByte(&sv_message, svs.maxclients);
3489 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3490 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3491 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3492 SZ_Clear(&sv_message);
3495 case CCREQ_PLAYER_INFO:
3496 if (developer_extra.integer)
3497 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3498 if(!(islocal || sv_public.integer > -1))
3501 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3506 int playerNumber, activeNumber, clientNumber;
3509 playerNumber = MSG_ReadByte(&sv_message);
3511 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3512 if (client->active && ++activeNumber == playerNumber)
3514 if (clientNumber != svs.maxclients)
3516 SZ_Clear(&sv_message);
3517 // save space for the header, filled in later
3518 MSG_WriteLong(&sv_message, 0);
3519 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3520 MSG_WriteByte(&sv_message, playerNumber);
3521 MSG_WriteString(&sv_message, client->name);
3522 MSG_WriteLong(&sv_message, client->colors);
3523 MSG_WriteLong(&sv_message, client->frags);
3524 MSG_WriteLong(&sv_message, (int)(host.realtime - client->connecttime));
3525 if(sv_status_privacy.integer)
3526 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3528 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3529 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3530 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3531 SZ_Clear(&sv_message);
3535 case CCREQ_RULE_INFO:
3536 if (developer_extra.integer)
3537 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3538 if(!(islocal || sv_public.integer > -1))
3541 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3548 // find the search start location
3549 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3550 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CF_NOTIFY);
3552 // send the response
3553 SZ_Clear(&sv_message);
3554 // save space for the header, filled in later
3555 MSG_WriteLong(&sv_message, 0);
3556 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3559 MSG_WriteString(&sv_message, var->name);
3560 MSG_WriteString(&sv_message, var->string);
3562 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3563 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3564 SZ_Clear(&sv_message);
3568 if (developer_extra.integer)
3569 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3570 if (sv.active && !rcon_secure.integer)
3572 char password[2048];
3576 const char *userlevel;
3577 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3578 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3580 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3581 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3582 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3589 SZ_Clear(&sv_message);
3590 // we may not have liked the packet, but it was a valid control
3591 // packet, so we're done processing this packet now
3596 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3598 SV_ReadClientMessage();
3605 void NetConn_ServerFrame(void)
3608 lhnetaddress_t peeraddress;
3609 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3610 for (i = 0;i < sv_numsockets;i++)
3611 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3612 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3615 void NetConn_SleepMicroseconds(int microseconds)
3617 LHNET_SleepUntilPacket_Microseconds(microseconds);
3621 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
3625 lhnetaddress_t masteraddress;
3626 lhnetaddress_t broadcastaddress;
3629 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3632 // 26000 is the default quake server port, servers on other ports will not
3634 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3635 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3639 for (i = 0;i < cl_numsockets;i++)
3643 const char *cmdname, *extraoptions;
3644 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3646 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3648 // search LAN for Quake servers
3649 SZ_Clear(&cl_message);
3650 // save space for the header, filled in later
3651 MSG_WriteLong(&cl_message, 0);
3652 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3653 MSG_WriteString(&cl_message, "QUAKE");
3654 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3655 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3656 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3657 SZ_Clear(&cl_message);
3659 // search LAN for DarkPlaces servers
3660 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3663 // build the getservers message to send to the dpmaster master servers
3664 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3666 cmdname = "getserversExt";
3667 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3671 cmdname = "getservers";
3674 memcpy(request, "\377\377\377\377", 4);
3675 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3678 for (masternum = 0;sv_masters[masternum].name;masternum++)
3680 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3683 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3687 // search favorite servers
3688 for(j = 0; j < nFavorites; ++j)
3690 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3692 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3693 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3700 // only query QuakeWorld servers when the user wants to
3703 for (i = 0;i < cl_numsockets;i++)
3707 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3709 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3711 // search LAN for QuakeWorld servers
3712 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3714 // build the getservers message to send to the qwmaster master servers
3715 // note this has no -1 prefix, and the trailing nul byte is sent
3716 dpsnprintf(request, sizeof(request), "c\n");
3720 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3722 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3724 if (m_state != m_slist)
3726 char lookupstring[128];
3727 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3728 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3731 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3735 // search favorite servers
3736 for(j = 0; j < nFavorites; ++j)
3738 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3740 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3742 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3743 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3750 if (!masterquerycount)
3752 Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
3753 M_Update_Return_Reason("No network");
3758 void NetConn_Heartbeat(int priority)
3760 lhnetaddress_t masteraddress;
3762 lhnetsocket_t *mysocket;
3764 // if it's a state change (client connected), limit next heartbeat to no
3765 // more than 30 sec in the future
3766 if (priority == 1 && nextheartbeattime > host.realtime + 30.0)
3767 nextheartbeattime = host.realtime + 30.0;
3769 // limit heartbeatperiod to 30 to 270 second range,
3770 // lower limit is to avoid abusing master servers with excess traffic,
3771 // upper limit is to avoid timing out on the master server (which uses
3773 if (sv_heartbeatperiod.value < 30)
3774 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3775 if (sv_heartbeatperiod.value > 270)
3776 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3778 // make advertising optional and don't advertise singleplayer games, and
3779 // only send a heartbeat as often as the admin wants
3780 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
3782 nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
3783 for (masternum = 0;sv_masters[masternum].name;masternum++)
3784 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3785 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3789 static void Net_Heartbeat_f(cmd_state_t *cmd)
3792 NetConn_Heartbeat(2);
3794 Con_Print("No server running, can not heartbeat to master server.\n");
3797 static void PrintStats(netconn_t *conn)
3799 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3800 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3802 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3803 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3804 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3805 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3806 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3807 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3808 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3809 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3810 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3811 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3814 void Net_Stats_f(cmd_state_t *cmd)
3817 Con_Print("connections =\n");
3818 for (conn = netconn_list;conn;conn = conn->next)
3823 void Net_Refresh_f(cmd_state_t *cmd)
3825 if (m_state != m_slist) {
3826 Con_Print("Sending new requests to master servers\n");
3827 ServerList_QueryList(false, true, false, true);
3828 Con_Print("Listening for replies...\n");
3830 ServerList_QueryList(false, true, false, false);
3833 void Net_Slist_f(cmd_state_t *cmd)
3835 ServerList_ResetMasks();
3836 serverlist_sortbyfield = SLIF_PING;
3837 serverlist_sortflags = 0;
3838 if (m_state != m_slist) {
3839 Con_Print("Sending requests to master servers\n");
3840 ServerList_QueryList(true, true, false, true);
3841 Con_Print("Listening for replies...\n");
3843 ServerList_QueryList(true, true, false, false);
3846 void Net_SlistQW_f(cmd_state_t *cmd)
3848 ServerList_ResetMasks();
3849 serverlist_sortbyfield = SLIF_PING;
3850 serverlist_sortflags = 0;
3851 if (m_state != m_slist) {
3852 Con_Print("Sending requests to master servers\n");
3853 ServerList_QueryList(true, false, true, true);
3854 serverlist_consoleoutput = true;
3855 Con_Print("Listening for replies...\n");
3857 ServerList_QueryList(true, false, true, false);
3861 void NetConn_Init(void)
3864 lhnetaddress_t tempaddress;
3865 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3866 Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
3868 Cmd_AddCommand(CF_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
3869 Cmd_AddCommand(CF_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3870 Cmd_AddCommand(CF_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3872 Cmd_AddCommand(CF_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3873 Cvar_RegisterVariable(&net_test);
3874 Cvar_RegisterVariable(&net_usesizelimit);
3875 Cvar_RegisterVariable(&net_burstreserve);
3876 Cvar_RegisterVariable(&rcon_restricted_password);
3877 Cvar_RegisterVariable(&rcon_restricted_commands);
3878 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3879 Cvar_RegisterVariable(&net_slist_queriespersecond);
3880 Cvar_RegisterVariable(&net_slist_queriesperframe);
3881 Cvar_RegisterVariable(&net_slist_timeout);
3882 Cvar_RegisterVariable(&net_slist_maxtries);
3883 Cvar_RegisterVariable(&net_slist_favorites);
3885 Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
3887 Cvar_RegisterVariable(&net_slist_pause);
3888 #ifdef IP_TOS // register cvar only if supported
3889 Cvar_RegisterVariable(&net_tos_dscp);
3891 Cvar_RegisterVariable(&net_messagetimeout);
3892 Cvar_RegisterVariable(&net_connecttimeout);
3893 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3894 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3895 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3896 Cvar_RegisterVariable(&net_sourceaddresscheck);
3897 Cvar_RegisterVariable(&net_fakelag);
3898 Cvar_RegisterVariable(&net_fakeloss_send);
3899 Cvar_RegisterVariable(&net_fakeloss_receive);
3900 Cvar_RegisterVirtual(&net_fakelag, "cl_netlocalping");
3901 Cvar_RegisterVirtual(&net_fakeloss_send, "cl_netpacketloss_send");
3902 Cvar_RegisterVirtual(&net_fakeloss_receive, "cl_netpacketloss_receive");
3903 Cvar_RegisterVariable(&hostname);
3904 Cvar_RegisterVariable(&developer_networking);
3905 Cvar_RegisterVariable(&cl_netport);
3906 Cvar_RegisterCallback(&cl_netport, NetConn_CL_UpdateSockets_Callback);
3907 Cvar_RegisterVariable(&sv_netport);
3908 Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
3909 Cvar_RegisterVariable(&net_address);
3910 Cvar_RegisterVariable(&net_address_ipv6);
3911 Cvar_RegisterVariable(&sv_public);
3912 Cvar_RegisterVariable(&sv_public_rejectreason);
3913 Cvar_RegisterVariable(&sv_heartbeatperiod);
3914 for (i = 0;sv_masters[i].name;i++)
3915 Cvar_RegisterVariable(&sv_masters[i]);
3916 Cvar_RegisterVariable(&gameversion);
3917 Cvar_RegisterVariable(&gameversion_min);
3918 Cvar_RegisterVariable(&gameversion_max);
3919 // 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.
3920 if ((i = Sys_CheckParm("-ip")) && i + 1 < sys.argc)
3922 if (LHNETADDRESS_FromString(&tempaddress, sys.argv[i + 1], 0) == 1)
3924 Con_Printf("-ip option used, setting net_address to \"%s\"\n", sys.argv[i + 1]);
3925 Cvar_SetQuick(&net_address, sys.argv[i + 1]);
3928 Con_Printf(CON_ERROR "-ip option used, but unable to parse the address \"%s\"\n", sys.argv[i + 1]);
3930 // 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
3931 if (((i = Sys_CheckParm("-port")) || (i = Sys_CheckParm("-ipport")) || (i = Sys_CheckParm("-udpport"))) && i + 1 < sys.argc)
3933 i = atoi(sys.argv[i + 1]);
3934 if (i >= 0 && i < 65536)
3936 Con_Printf("-port option used, setting port cvar to %i\n", i);
3937 Cvar_SetValueQuick(&sv_netport, i);
3940 Con_Printf(CON_ERROR "-port option used, but %i is not a valid port number\n", i);
3944 cl_message.data = cl_message_buf;
3945 cl_message.maxsize = sizeof(cl_message_buf);
3946 cl_message.cursize = 0;
3947 sv_message.data = sv_message_buf;
3948 sv_message.maxsize = sizeof(sv_message_buf);
3949 sv_message.cursize = 0;
3951 if (Thread_HasThreads())
3952 netconn_mutex = Thread_CreateMutex();
3955 void NetConn_Shutdown(void)
3957 NetConn_CloseClientPorts();
3958 NetConn_CloseServerPorts();
3961 Thread_DestroyMutex(netconn_mutex);
3962 netconn_mutex = NULL;