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;
1149 static int hostport = -1;
1151 // Call on disconnect, during startup, or if cl_netport is changed
1152 void NetConn_UpdateSockets_Client(void)
1154 if (cls.state == ca_disconnected && clientport != clientport2)
1156 clientport = clientport2;
1157 NetConn_CloseClientPorts();
1159 if (cl_numsockets == 0)
1160 NetConn_OpenClientPorts();
1163 // Call when cl_port is changed
1164 static void NetConn_cl_netport_Callback(cvar_t *var)
1166 if(cls.state != ca_dedicated)
1168 if (clientport2 != var->integer)
1170 clientport2 = var->integer;
1171 if (cls.state == ca_connected)
1172 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1174 NetConn_UpdateSockets_Client();
1178 // Call when port is changed
1179 static void NetConn_sv_netport_Callback(cvar_t *var)
1181 if (hostport != var->integer)
1183 hostport = var->integer;
1185 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1189 void NetConn_UpdateSockets(void)
1193 // TODO add logic to automatically close sockets if needed
1194 LHNET_DefaultDSCP(net_tos_dscp.integer);
1196 for (j = 0;j < MAX_RCONS;j++)
1198 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1199 if(cls.rcon_commands[i][0])
1201 if(host.realtime > cls.rcon_timeout[i])
1204 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1205 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1206 cls.rcon_commands[i][0] = 0;
1214 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1216 int originallength = (int)length;
1217 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1218 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1219 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1223 if (protocol == PROTOCOL_QUAKEWORLD)
1225 unsigned int sequence, sequence_ack;
1226 qbool reliable_ack, reliable_message;
1230 sequence = LittleLong(*((int *)(data + 0)));
1231 sequence_ack = LittleLong(*((int *)(data + 4)));
1235 if (conn != cls.netcon)
1240 // TODO: use qport to identify that this client really is who they say they are? (and elsewhere in the code to identify the connection without a port match?)
1241 //qport = LittleShort(*((int *)(data + 8)));
1246 conn->packetsReceived++;
1247 reliable_message = (sequence >> 31) != 0;
1248 reliable_ack = (sequence_ack >> 31) != 0;
1249 sequence &= ~(1<<31);
1250 sequence_ack &= ~(1<<31);
1251 if (sequence <= conn->qw.incoming_sequence)
1253 //Con_DPrint("Got a stale datagram\n");
1256 count = sequence - (conn->qw.incoming_sequence + 1);
1259 conn->droppedDatagrams += count;
1260 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1261 // If too may packets have been dropped, only write the
1262 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1263 // Because there's no point in writing more than
1264 // these as the netgraph is going to be full anyway.
1265 if (count > NETGRAPH_PACKETS)
1266 count = NETGRAPH_PACKETS;
1269 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1270 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1271 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1272 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1273 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1274 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1277 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1278 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1279 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1280 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1281 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1282 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1283 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1285 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1286 if (net_test.integer)
1288 if (conn->cleartime < host.realtime)
1289 conn->cleartime = host.realtime;
1292 if (reliable_ack == conn->qw.reliable_sequence)
1294 // received, now we will be able to send another reliable message
1295 conn->sendMessageLength = 0;
1296 conn->reliableMessagesReceived++;
1298 conn->qw.incoming_sequence = sequence;
1299 if (conn == cls.netcon)
1300 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1301 conn->qw.incoming_acknowledged = sequence_ack;
1302 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1303 if (reliable_message)
1304 conn->qw.incoming_reliable_sequence ^= 1;
1305 conn->lastMessageTime = host.realtime;
1306 conn->timeout = host.realtime + newtimeout;
1307 conn->unreliableMessagesReceived++;
1308 if (conn == cls.netcon)
1310 SZ_Clear(&cl_message);
1311 SZ_Write(&cl_message, data, (int)length);
1312 MSG_BeginReading(&cl_message);
1316 SZ_Clear(&sv_message);
1317 SZ_Write(&sv_message, data, (int)length);
1318 MSG_BeginReading(&sv_message);
1326 unsigned int sequence;
1331 originallength = (int)length;
1332 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1338 qlength = (unsigned int)BuffBigLong(data);
1339 flags = qlength & ~NETFLAG_LENGTH_MASK;
1340 qlength &= NETFLAG_LENGTH_MASK;
1341 // control packets were already handled
1342 if (!(flags & NETFLAG_CTL) && qlength == length)
1344 sequence = BuffBigLong(data + 4);
1345 conn->packetsReceived++;
1348 if (flags & NETFLAG_UNRELIABLE)
1350 if (sequence >= conn->nq.unreliableReceiveSequence)
1352 if (sequence > conn->nq.unreliableReceiveSequence)
1354 count = sequence - conn->nq.unreliableReceiveSequence;
1355 conn->droppedDatagrams += count;
1356 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1357 // If too may packets have been dropped, only write the
1358 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1359 // Because there's no point in writing more than
1360 // these as the netgraph is going to be full anyway.
1361 if (count > NETGRAPH_PACKETS)
1362 count = NETGRAPH_PACKETS;
1365 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1366 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1367 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1368 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1369 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1370 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1373 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1374 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1375 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1376 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1377 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1378 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1379 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1381 conn->nq.unreliableReceiveSequence = sequence + 1;
1382 conn->lastMessageTime = host.realtime;
1383 conn->timeout = host.realtime + newtimeout;
1384 conn->unreliableMessagesReceived++;
1387 if (conn == cls.netcon)
1389 SZ_Clear(&cl_message);
1390 SZ_Write(&cl_message, data, (int)length);
1391 MSG_BeginReading(&cl_message);
1395 SZ_Clear(&sv_message);
1396 SZ_Write(&sv_message, data, (int)length);
1397 MSG_BeginReading(&sv_message);
1403 // Con_DPrint("Got a stale datagram\n");
1406 else if (flags & NETFLAG_ACK)
1408 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1409 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1411 if (sequence == (conn->nq.sendSequence - 1))
1413 if (sequence == conn->nq.ackSequence)
1415 conn->nq.ackSequence++;
1416 if (conn->nq.ackSequence != conn->nq.sendSequence)
1417 Con_DPrint("ack sequencing error\n");
1418 conn->lastMessageTime = host.realtime;
1419 conn->timeout = host.realtime + newtimeout;
1420 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1422 unsigned int packetLen;
1423 unsigned int dataLen;
1426 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1427 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1429 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1431 dataLen = conn->sendMessageLength;
1436 dataLen = MAX_PACKETFRAGMENT;
1440 packetLen = NET_HEADERSIZE + dataLen;
1442 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1443 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1444 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1446 conn->nq.sendSequence++;
1448 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1449 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1451 conn->lastSendTime = host.realtime;
1452 conn->packetsSent++;
1456 conn->sendMessageLength = 0;
1459 // Con_DPrint("Duplicate ACK received\n");
1462 // Con_DPrint("Stale ACK received\n");
1465 else if (flags & NETFLAG_DATA)
1467 unsigned char temppacket[8];
1468 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1469 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1471 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1473 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1474 StoreBigLong(temppacket + 4, sequence);
1475 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1477 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1478 if (sequence == conn->nq.receiveSequence)
1480 conn->lastMessageTime = host.realtime;
1481 conn->timeout = host.realtime + newtimeout;
1482 conn->nq.receiveSequence++;
1483 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1484 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1485 conn->receiveMessageLength += (int)length;
1487 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1488 "Dropping the message!\n", sequence );
1489 conn->receiveMessageLength = 0;
1492 if (flags & NETFLAG_EOM)
1494 conn->reliableMessagesReceived++;
1495 length = conn->receiveMessageLength;
1496 conn->receiveMessageLength = 0;
1499 if (conn == cls.netcon)
1501 SZ_Clear(&cl_message);
1502 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1503 MSG_BeginReading(&cl_message);
1507 SZ_Clear(&sv_message);
1508 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1509 MSG_BeginReading(&sv_message);
1516 conn->receivedDuplicateCount++;
1524 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1527 cls.connect_trying = false;
1529 M_Update_Return_Reason("");
1531 // Disconnect from the current server or stop demo playback
1532 if(cls.state == ca_connected || cls.demoplayback)
1534 // allocate a net connection to keep track of things
1535 cls.netcon = NetConn_Open(mysocket, peeraddress);
1536 crypto = &cls.netcon->crypto;
1537 if(cls.crypto.authenticated)
1539 Crypto_FinishInstance(crypto, &cls.crypto);
1540 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1541 crypto->use_aes ? "Encrypted" : "Authenticated",
1542 cls.netcon->address,
1543 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1544 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1545 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1546 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1547 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1548 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1551 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1552 key_dest = key_game;
1556 cls.demonum = -1; // not in the demo loop now
1557 cls.state = ca_connected;
1558 cls.signon = 0; // need all the signon messages before playing
1559 cls.protocol = initialprotocol;
1560 // reset move sequence numbering on this new connection
1561 cls.servermovesequence = 0;
1562 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1563 CL_ForwardToServer("new");
1564 if (cls.protocol == PROTOCOL_QUAKE)
1566 // write a keepalive (clc_nop) as it seems to greatly improve the
1567 // chances of connecting to a netquake server
1569 unsigned char buf[4];
1570 memset(&msg, 0, sizeof(msg));
1572 msg.maxsize = sizeof(buf);
1573 MSG_WriteChar(&msg, clc_nop);
1574 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1578 int NetConn_IsLocalGame(void)
1580 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1586 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1590 serverlist_entry_t *entry = NULL;
1592 // search the cache for this server and update it
1593 for (n = 0;n < serverlist_cachecount;n++) {
1594 entry = &serverlist_cache[ n ];
1595 if (!strcmp(addressstring, entry->info.cname))
1599 if (n == serverlist_cachecount)
1601 // LAN search doesnt require an answer from the master server so we wont
1602 // know the ping nor will it be initialized already...
1605 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1608 if (serverlist_maxcachecount <= serverlist_cachecount)
1610 serverlist_maxcachecount += 64;
1611 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1613 entry = &serverlist_cache[n];
1615 memset(entry, 0, sizeof(*entry));
1616 // store the data the engine cares about (address and ping)
1617 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1618 entry->info.ping = 100000;
1619 entry->querytime = host.realtime;
1620 // if not in the slist menu we should print the server to console
1621 if (serverlist_consoleoutput)
1622 Con_Printf("querying %s\n", addressstring);
1623 ++serverlist_cachecount;
1625 // if this is the first reply from this server, count it as having replied
1626 pingtime = (int)((host.realtime - entry->querytime) * 1000.0 + 0.5);
1627 pingtime = bound(0, pingtime, 9999);
1628 if (entry->query == SQS_REFRESHING) {
1629 entry->info.ping = pingtime;
1630 entry->query = SQS_QUERIED;
1632 // convert to unsigned to catch the -1
1633 // 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]
1634 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1638 // other server info is updated by the caller
1642 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1644 serverlist_entry_t *entry = &serverlist_cache[n];
1645 serverlist_info_t *info = &entry->info;
1646 // update description strings for engine menu and console output
1647 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);
1648 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1650 info->gameversion != gameversion.integer
1653 gameversion_min.integer >= 0 // min/max range set by user/mod?
1654 && gameversion_max.integer >= 0
1655 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1656 && gameversion_max.integer >= info->gameversion
1659 info->mod, info->map);
1660 if (entry->query == SQS_QUERIED)
1662 if(!serverlist_paused)
1663 ServerList_ViewList_Remove(entry);
1665 // if not in the slist menu we should print the server to console (if wanted)
1666 else if( serverlist_consoleoutput )
1667 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1668 // and finally, update the view set
1669 if(!serverlist_paused)
1670 ServerList_ViewList_Insert( entry );
1671 // update the entry's state
1672 serverlist_cache[n].query = SQS_QUERIED;
1675 // returns true, if it's sensible to continue the processing
1676 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qbool isfavorite ) {
1678 serverlist_entry_t *entry;
1680 // ignore the rest of the message if the serverlist is full
1681 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1683 // also ignore it if we have already queried it (other master server response)
1684 for( n = 0 ; n < serverlist_cachecount ; n++ )
1685 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1688 if( n < serverlist_cachecount ) {
1689 // the entry has already been queried once or
1693 if (serverlist_maxcachecount <= n)
1695 serverlist_maxcachecount += 64;
1696 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1699 entry = &serverlist_cache[n];
1701 memset(entry, 0, sizeof(*entry));
1702 entry->protocol = protocol;
1703 // store the data the engine cares about (address and ping)
1704 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1706 entry->info.isfavorite = isfavorite;
1708 // no, then reset the ping right away
1709 entry->info.ping = -1;
1710 // we also want to increase the serverlist_cachecount then
1711 serverlist_cachecount++;
1714 entry->query = SQS_QUERYING;
1719 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qbool isextended)
1722 if (serverlist_consoleoutput)
1723 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1726 char ipstring [128];
1729 if (data[0] == '\\')
1731 unsigned short port = data[5] * 256 + data[6];
1733 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1734 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1736 // move on to next address in packet
1741 else if (data[0] == '/' && isextended && length >= 19)
1743 unsigned short port = data[17] * 256 + data[18];
1751 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1753 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1756 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1757 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1758 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1764 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1765 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1766 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1771 // move on to next address in packet
1777 Con_Print("Error while parsing the server list\n");
1781 if (serverlist_consoleoutput && developer_networking.integer)
1782 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1784 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1790 // begin or resume serverlist queries
1791 serverlist_querysleep = false;
1792 serverlist_querywaittime = host.realtime + 3;
1796 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1800 char *string, addressstring2[128];
1801 char stringbuf[16384];
1802 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1805 char infostringvalue[MAX_INPUTLINE];
1810 // quakeworld ingame packet
1811 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1813 // convert the address to a string incase we need it
1814 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1816 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1818 // received a command string - strip off the packaging and put it
1819 // into our string buffer with NULL termination
1822 length = min(length, (int)sizeof(stringbuf) - 1);
1823 memcpy(stringbuf, data, length);
1824 stringbuf[length] = 0;
1827 if (developer_networking.integer)
1829 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1830 Com_HexDumpToConsole(data, length);
1833 sendlength = sizeof(senddata) - 4;
1834 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1836 case CRYPTO_NOMATCH:
1842 memcpy(senddata, "\377\377\377\377", 4);
1843 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1846 case CRYPTO_DISCARD:
1849 memcpy(senddata, "\377\377\377\377", 4);
1850 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1854 case CRYPTO_REPLACE:
1855 string = senddata+4;
1856 length = (int)sendlength;
1860 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1863 for (j = 0;j < MAX_RCONS;j++)
1865 // note: this value from i is used outside the loop too...
1866 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1867 if(cls.rcon_commands[i][0])
1868 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1877 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1878 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1880 e = strchr(rcon_password.string, ' ');
1881 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1883 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1887 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1888 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1889 cls.rcon_commands[i][0] = 0;
1892 for (k = 0;k < MAX_RCONS;k++)
1893 if(cls.rcon_commands[k][0])
1894 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1899 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1900 // extend the timeout on other requests as we asked for a challenge
1901 for (l = 0;l < MAX_RCONS;l++)
1902 if(cls.rcon_commands[l][0])
1903 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1904 cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
1907 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1911 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1913 // darkplaces or quake3
1914 char protocolnames[1400];
1915 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1916 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1917 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1920 Protocol_Names(protocolnames, sizeof(protocolnames));
1922 M_Update_Return_Reason("Got challenge response");
1924 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1925 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1926 // TODO: add userinfo stuff here instead of using NQ commands?
1927 memcpy(senddata, "\377\377\377\377", 4);
1928 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1929 NetConn_WriteString(mysocket, senddata, peeraddress);
1932 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1934 // darkplaces or quake3
1935 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1936 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1940 M_Update_Return_Reason("Accepted");
1942 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1945 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1947 char rejectreason[128];
1948 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1949 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1952 cls.connect_trying = false;
1954 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1955 memcpy(rejectreason, string, length);
1956 rejectreason[length] = 0;
1958 M_Update_Return_Reason(rejectreason);
1963 if(key_dest != key_game)
1965 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1967 serverlist_info_t *info;
1972 // search the cache for this server and update it
1973 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1977 info = &serverlist_cache[n].info;
1982 info->qcstatus[0] = 0;
1983 info->players[0] = 0;
1984 info->protocol = -1;
1985 info->numplayers = 0;
1987 info->maxplayers = 0;
1988 info->gameversion = 0;
1990 p = strchr(string, '\n');
1993 *p = 0; // cut off the string there
1997 Con_Printf("statusResponse without players block?\n");
1999 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2000 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2001 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2002 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2003 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2004 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2005 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2006 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2007 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2008 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2009 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2010 info->numhumans = info->numplayers - max(0, info->numbots);
2011 info->freeslots = info->maxplayers - info->numplayers;
2013 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2017 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2019 serverlist_info_t *info;
2023 // search the cache for this server and update it
2024 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2028 info = &serverlist_cache[n].info;
2033 info->qcstatus[0] = 0;
2034 info->players[0] = 0;
2035 info->protocol = -1;
2036 info->numplayers = 0;
2038 info->maxplayers = 0;
2039 info->gameversion = 0;
2041 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2042 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2043 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2044 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2045 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2046 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2047 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2048 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2049 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2050 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2051 info->numhumans = info->numplayers - max(0, info->numbots);
2052 info->freeslots = info->maxplayers - info->numplayers;
2054 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2058 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2060 // Extract the IP addresses
2063 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2066 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2068 // Extract the IP addresses
2071 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2074 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2076 // Extract the IP addresses
2080 if (serverlist_consoleoutput)
2081 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2082 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2084 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2085 if (serverlist_consoleoutput && developer_networking.integer)
2086 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2088 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2092 // move on to next address in packet
2096 // begin or resume serverlist queries
2097 serverlist_querysleep = false;
2098 serverlist_querywaittime = host.realtime + 3;
2103 if (!strncmp(string, "extResponse ", 12))
2105 ++cl_net_extresponse_count;
2106 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2107 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2108 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2109 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2112 if (!strncmp(string, "ping", 4))
2114 if (developer_extra.integer)
2115 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2116 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2119 if (!strncmp(string, "ack", 3))
2121 // QuakeWorld compatibility
2122 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2124 // challenge message
2125 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2126 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2129 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2131 M_Update_Return_Reason("Got QuakeWorld challenge response");
2133 cls.qw_qport = qport.integer;
2134 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2135 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2136 memcpy(senddata, "\377\377\377\377", 4);
2137 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);
2138 NetConn_WriteString(mysocket, senddata, peeraddress);
2141 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2144 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2145 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2149 M_Update_Return_Reason("QuakeWorld Accepted");
2151 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2154 if (length > 2 && !memcmp(string, "n\\", 2))
2157 serverlist_info_t *info;
2161 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2162 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2165 // search the cache for this server and update it
2166 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2170 info = &serverlist_cache[n].info;
2171 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2172 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2173 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2174 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2176 info->numplayers = 0; // updated below
2177 info->numhumans = 0; // updated below
2178 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2179 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2181 // count active players on server
2182 // (we could gather more info, but we're just after the number)
2183 s = strchr(string, '\n');
2187 while (s < string + length)
2189 for (;s < string + length && *s != '\n';s++)
2191 if (s >= string + length)
2199 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2203 if (string[0] == 'n')
2205 // qw print command, used by rcon replies too
2206 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2207 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2210 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2212 // we may not have liked the packet, but it was a command packet, so
2213 // we're done processing this packet now
2216 // quakeworld ingame packet
2217 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2220 CL_ParseServerMessage();
2223 // netquake control packets, supported for compatibility only
2224 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2228 serverlist_info_t *info;
2233 SZ_Clear(&cl_message);
2234 SZ_Write(&cl_message, data, length);
2235 MSG_BeginReading(&cl_message);
2236 c = MSG_ReadByte(&cl_message);
2240 if (developer_extra.integer)
2241 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2242 if (cls.connect_trying)
2244 lhnetaddress_t clientportaddress;
2245 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2246 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2249 clientportaddress = *peeraddress;
2250 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2251 // extra ProQuake stuff
2253 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2255 cls.proquake_servermod = 0;
2257 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2259 cls.proquake_serverversion = 0;
2261 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2263 cls.proquake_serverflags = 0;
2264 if (cls.proquake_servermod == 1)
2265 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2266 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2267 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2269 M_Update_Return_Reason("Accepted");
2271 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2275 if (developer_extra.integer) {
2276 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2279 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2281 cls.connect_trying = false;
2283 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2286 case CCREP_SERVER_INFO:
2287 if (developer_extra.integer)
2288 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2290 // LadyHavoc: because the quake server may report weird addresses
2291 // we just ignore it and keep the real address
2292 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2293 // search the cache for this server and update it
2294 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2298 info = &serverlist_cache[n].info;
2299 strlcpy(info->game, "Quake", sizeof(info->game));
2300 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2301 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2302 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2303 info->numplayers = MSG_ReadByte(&cl_message);
2304 info->maxplayers = MSG_ReadByte(&cl_message);
2305 info->protocol = MSG_ReadByte(&cl_message);
2307 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2310 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2311 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2312 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2315 if (developer_extra.integer)
2316 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2318 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2320 case CCREP_PLAYER_INFO:
2321 // we got a CCREP_PLAYER_INFO??
2322 //if (developer_extra.integer)
2323 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2325 case CCREP_RULE_INFO:
2326 // we got a CCREP_RULE_INFO??
2327 //if (developer_extra.integer)
2328 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2333 SZ_Clear(&cl_message);
2334 // we may not have liked the packet, but it was a valid control
2335 // packet, so we're done processing this packet now
2339 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2340 CL_ParseServerMessage();
2345 void NetConn_QueryQueueFrame(void)
2351 static double querycounter = 0;
2353 if(!net_slist_pause.integer && serverlist_paused)
2354 ServerList_RebuildViewList();
2355 serverlist_paused = net_slist_pause.integer != 0;
2357 if (serverlist_querysleep)
2360 // apply a cool down time after master server replies,
2361 // to avoid messing up the ping times on the servers
2362 if (serverlist_querywaittime > host.realtime)
2365 // each time querycounter reaches 1.0 issue a query
2366 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2367 maxqueries = (int)querycounter;
2368 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2369 querycounter -= maxqueries;
2371 if( maxqueries == 0 ) {
2375 // scan serverlist and issue queries as needed
2376 serverlist_querysleep = true;
2378 timeouttime = host.realtime - net_slist_timeout.value;
2379 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2381 serverlist_entry_t *entry = &serverlist_cache[ index ];
2382 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2387 serverlist_querysleep = false;
2388 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2393 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2395 lhnetaddress_t address;
2398 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2399 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2401 for (socket = 0; socket < cl_numsockets ; socket++)
2402 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2406 for (socket = 0; socket < cl_numsockets ; socket++)
2407 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2410 // update the entry fields
2411 entry->querytime = host.realtime;
2412 entry->querycounter++;
2414 // if not in the slist menu we should print the server to console
2415 if (serverlist_consoleoutput)
2416 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2422 // have we tried to refresh this server?
2423 if( entry->query == SQS_REFRESHING ) {
2424 // yes, so update the reply count (since its not responding anymore)
2426 if(!serverlist_paused)
2427 ServerList_ViewList_Remove(entry);
2429 entry->query = SQS_TIMEDOUT;
2435 void NetConn_ClientFrame(void)
2438 lhnetaddress_t peeraddress;
2439 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2440 NetConn_UpdateSockets();
2441 if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2444 if (cls.connect_remainingtries == 0)
2445 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2447 cls.connect_nextsendtime = host.realtime + 1;
2448 cls.connect_remainingtries--;
2449 if (cls.connect_remainingtries <= -10)
2451 cls.connect_trying = false;
2453 M_Update_Return_Reason("Connect: Failed");
2457 // try challenge first (newer DP server or QW)
2458 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2459 // then try netquake as a fallback (old server, or netquake)
2460 SZ_Clear(&cl_message);
2461 // save space for the header, filled in later
2462 MSG_WriteLong(&cl_message, 0);
2463 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2464 MSG_WriteString(&cl_message, "QUAKE");
2465 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2466 // extended proquake stuff
2467 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2468 // this version matches ProQuake 3.40, the first version to support
2469 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2470 // higher clients, so we pretend we are that version...
2471 MSG_WriteByte(&cl_message, 34); // version * 10
2472 MSG_WriteByte(&cl_message, 0); // flags
2473 MSG_WriteLong(&cl_message, 0); // password
2474 // write the packetsize now...
2475 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2476 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2477 SZ_Clear(&cl_message);
2479 for (i = 0;i < cl_numsockets;i++)
2481 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2483 // R_TimeReport("clientreadnetwork");
2484 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2485 // R_TimeReport("clientparsepacket");
2489 NetConn_QueryQueueFrame();
2491 if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2492 CL_DisconnectEx(true, "Connection timed out");
2495 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2499 for (i = 0;i < bufferlength - 1;i++)
2503 c = rand () % (127 - 33) + 33;
2504 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2510 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2511 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2513 prvm_prog_t *prog = SVVM_prog;
2515 unsigned int nb_clients = 0, nb_bots = 0, i;
2518 const char *crypto_idstring;
2519 const char *worldstatusstr;
2521 // How many clients are there?
2522 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2524 if (svs.clients[i].active)
2527 if (!svs.clients[i].netconnection)
2533 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2534 if(worldstatusstr && *worldstatusstr)
2539 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2540 if(*q != '\\' && *q != '\n')
2545 /// \TODO: we should add more information for the full status string
2546 crypto_idstring = Crypto_GetInfoResponseDataString();
2547 length = dpsnprintf(out_msg, out_size,
2548 "\377\377\377\377%s\x0A"
2549 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2550 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2555 fullstatus ? "statusResponse" : "infoResponse",
2556 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2557 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2558 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2559 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2560 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2561 fullstatus ? "\n" : "");
2563 // Make sure it fits in the buffer
2573 savelength = length;
2575 ptr = out_msg + length;
2576 left = (int)out_size - length;
2578 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2580 client_t *client = &svs.clients[i];
2583 int nameind, cleanind, pingvalue;
2585 char cleanname [sizeof(client->name)];
2586 const char *statusstr;
2589 // Remove all characters '"' and '\' in the player name
2594 curchar = client->name[nameind++];
2595 if (curchar != '"' && curchar != '\\')
2597 cleanname[cleanind++] = curchar;
2598 if (cleanind == sizeof(cleanname) - 1)
2601 } while (curchar != '\0');
2602 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2604 pingvalue = (int)(client->ping * 1000.0f);
2605 if(client->netconnection)
2606 pingvalue = bound(1, pingvalue, 9999);
2611 ed = PRVM_EDICT_NUM(i + 1);
2612 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2613 if(statusstr && *statusstr)
2618 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2619 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2624 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2626 if(client->frags == -666) // spectator
2627 strlcpy(teambuf, " 0", sizeof(teambuf));
2628 else if(client->colors == 0x44) // red team
2629 strlcpy(teambuf, " 1", sizeof(teambuf));
2630 else if(client->colors == 0xDD) // blue team
2631 strlcpy(teambuf, " 2", sizeof(teambuf));
2632 else if(client->colors == 0xCC) // yellow team
2633 strlcpy(teambuf, " 3", sizeof(teambuf));
2634 else if(client->colors == 0x99) // pink team
2635 strlcpy(teambuf, " 4", sizeof(teambuf));
2637 strlcpy(teambuf, " 0", sizeof(teambuf));
2642 // note: team number is inserted according to SoF2 protocol
2644 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2650 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2659 // turn it into an infoResponse!
2660 out_msg[savelength] = 0;
2661 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2662 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2677 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2679 size_t floodslotnum, bestfloodslotnum;
2680 double bestfloodtime;
2681 lhnetaddress_t noportpeeraddress;
2682 // see if this is a connect flood
2683 noportpeeraddress = *peeraddress;
2684 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2685 bestfloodslotnum = 0;
2686 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2687 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2689 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2691 bestfloodtime = floodlist[floodslotnum].lasttime;
2692 bestfloodslotnum = floodslotnum;
2694 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2696 // this address matches an ongoing flood address
2697 if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2701 // renew the ban on this address so it does not expire
2702 // until the flood has subsided
2703 floodlist[floodslotnum].lasttime = host.realtime;
2705 //Con_Printf("Flood detected!\n");
2708 // the flood appears to have subsided, so allow this
2709 bestfloodslotnum = floodslotnum; // reuse the same slot
2713 // begin a new timeout on this address
2714 floodlist[bestfloodslotnum].address = noportpeeraddress;
2715 floodlist[bestfloodslotnum].lasttime = host.realtime;
2716 //Con_Printf("Flood detection initiated!\n");
2720 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2722 size_t floodslotnum;
2723 lhnetaddress_t noportpeeraddress;
2724 // see if this is a connect flood
2725 noportpeeraddress = *peeraddress;
2726 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2727 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2729 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2731 // this address matches an ongoing flood address
2733 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2734 floodlist[floodslotnum].lasttime = 0;
2735 //Con_Printf("Flood cleared!\n");
2740 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2742 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2748 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2752 t1 = (long) time(NULL);
2753 t2 = strtol(s, NULL, 0);
2754 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2757 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2760 return !memcmp(mdfourbuf, hash, 16);
2763 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2769 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2773 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2776 // validate the challenge
2777 for (i = 0;i < MAX_CHALLENGES;i++)
2778 if(challenges[i].time > 0)
2779 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2781 // if the challenge is not recognized, drop the packet
2782 if (i == MAX_CHALLENGES)
2785 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2788 if(memcmp(mdfourbuf, hash, 16))
2791 // unmark challenge to prevent replay attacks
2792 challenges[i].time = 0;
2797 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2800 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2804 return !strcmp(password, hash);
2807 /// returns a string describing the user level, or NULL for auth failure
2808 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)
2810 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2811 static char buf[MAX_INPUTLINE];
2813 qbool restricted = false;
2814 qbool have_usernames = false;
2815 static char vabuf[1024];
2817 userpass_start = rcon_password.string;
2818 while((userpass_end = strchr(userpass_start, ' ')))
2820 have_usernames = true;
2821 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2822 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2823 if(comparator(peeraddress, buf, password, cs, cslen))
2825 userpass_start = userpass_end + 1;
2827 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2829 userpass_end = userpass_start + strlen(userpass_start);
2830 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2835 have_usernames = false;
2836 userpass_start = rcon_restricted_password.string;
2837 while((userpass_end = strchr(userpass_start, ' ')))
2839 have_usernames = true;
2840 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2841 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2842 if(comparator(peeraddress, buf, password, cs, cslen))
2844 userpass_start = userpass_end + 1;
2846 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2848 userpass_end = userpass_start + strlen(userpass_start);
2849 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2853 return NULL; // DENIED
2856 for(text = s; text != endpos; ++text)
2857 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2858 return NULL; // block possible exploits against the parser/alias expansion
2862 size_t l = strlen(s);
2865 hasquotes = (strchr(s, '"') != NULL);
2866 // sorry, we can't allow these substrings in wildcard expressions,
2867 // as they can mess with the argument counts
2868 text = rcon_restricted_commands.string;
2869 while(COM_ParseToken_Console(&text))
2871 // com_token now contains a pattern to check for...
2872 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2875 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2878 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2880 if(!strcmp(com_token, s))
2883 else // single-arg expression? must match the beginning of the command
2885 if(!strcmp(com_token, s))
2887 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2891 // if we got here, nothing matched!
2899 userpass_startpass = strchr(userpass_start, ':');
2900 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2901 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2903 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2906 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qbool proquakeprotocol)
2910 // looks like a legitimate rcon command with the correct password
2911 const char *s_ptr = s;
2912 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2913 while(s_ptr != endpos)
2915 size_t l = strlen(s_ptr);
2917 Con_Printf(" %s;", s_ptr);
2922 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2923 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2926 size_t l = strlen(s);
2929 client_t *host_client_save = host_client;
2930 Cmd_ExecuteString(cmd_local, s, src_local, true);
2931 host_client = host_client_save;
2932 // in case it is a command that changes host_client (like restart)
2936 Con_Rcon_Redirect_End();
2940 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2944 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2946 int i, ret, clientnum, best;
2948 char *string, response[2800], addressstring2[128];
2949 static char stringbuf[16384]; // server only
2950 qbool islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2951 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2952 size_t sendlength, response_len;
2953 char infostringvalue[MAX_INPUTLINE];
2958 // convert the address to a string incase we need it
2959 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2961 // see if we can identify the sender as a local player
2962 // (this is necessary for rcon to send a reliable reply if the client is
2963 // actually on the server, not sending remotely)
2964 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2965 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2967 if (i == svs.maxclients)
2970 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2972 // received a command string - strip off the packaging and put it
2973 // into our string buffer with NULL termination
2976 length = min(length, (int)sizeof(stringbuf) - 1);
2977 memcpy(stringbuf, data, length);
2978 stringbuf[length] = 0;
2981 if (developer_extra.integer)
2983 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2984 Com_HexDumpToConsole(data, length);
2987 sendlength = sizeof(senddata) - 4;
2988 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2990 case CRYPTO_NOMATCH:
2996 memcpy(senddata, "\377\377\377\377", 4);
2997 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3000 case CRYPTO_DISCARD:
3003 memcpy(senddata, "\377\377\377\377", 4);
3004 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3008 case CRYPTO_REPLACE:
3009 string = senddata+4;
3010 length = (int)sendlength;
3014 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3016 for (i = 0, best = 0, besttime = host.realtime;i < MAX_CHALLENGES;i++)
3018 if(challenges[i].time > 0)
3019 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3021 if (besttime > challenges[i].time)
3022 besttime = challenges[best = i].time;
3024 // if we did not find an exact match, choose the oldest and
3025 // update address and string
3026 if (i == MAX_CHALLENGES)
3029 challenges[i].address = *peeraddress;
3030 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3034 // flood control: drop if requesting challenge too often
3035 if(challenges[i].time > host.realtime - net_challengefloodblockingtimeout.value)
3038 challenges[i].time = host.realtime;
3039 // send the challenge
3040 memcpy(response, "\377\377\377\377", 4);
3041 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3042 response_len = strlen(response) + 1;
3043 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3044 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3047 if (length > 8 && !memcmp(string, "connect\\", 8))
3051 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3055 if(crypto && crypto->authenticated)
3057 // no need to check challenge
3058 if(crypto_developer.integer)
3060 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3061 crypto->use_aes ? "Encrypted" : "Authenticated",
3063 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3064 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3065 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3066 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3067 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3068 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3074 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3076 // validate the challenge
3077 for (i = 0;i < MAX_CHALLENGES;i++)
3078 if(challenges[i].time > 0)
3079 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3081 // if the challenge is not recognized, drop the packet
3082 if (i == MAX_CHALLENGES)
3087 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3088 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3090 if(!(islocal || sv_public.integer > -2))
3092 if (developer_extra.integer)
3093 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3094 memcpy(response, "\377\377\377\377", 4);
3095 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3096 NetConn_WriteString(mysocket, response, peeraddress);
3100 // check engine protocol
3101 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3103 if (developer_extra.integer)
3104 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3105 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3109 // see if this is a duplicate connection request or a disconnected
3110 // client who is rejoining to the same client slot
3111 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3113 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3115 // this is a known client...
3116 if(crypto && crypto->authenticated)
3118 // reject if changing key!
3119 if(client->netconnection->crypto.authenticated)
3122 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3124 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3126 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3128 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3131 if (developer_extra.integer)
3132 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3133 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3140 // reject if downgrading!
3141 if(client->netconnection->crypto.authenticated)
3143 if (developer_extra.integer)
3144 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3145 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3151 // client crashed and is coming back,
3152 // keep their stuff intact
3153 if (developer_extra.integer)
3154 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3155 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3156 if(crypto && crypto->authenticated)
3157 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3158 SV_SendServerinfo(client);
3162 // client is still trying to connect,
3163 // so we send a duplicate reply
3164 if (developer_extra.integer)
3165 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3166 if(crypto && crypto->authenticated)
3167 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3168 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3174 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3177 // find an empty client slot for this new client
3178 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3181 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3183 // allocated connection
3184 if (developer_extra.integer)
3185 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3186 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3187 // now set up the client
3188 if(crypto && crypto->authenticated)
3189 Crypto_FinishInstance(&conn->crypto, crypto);
3190 SV_ConnectClient(clientnum, conn);
3191 NetConn_Heartbeat(1);
3196 // no empty slots found - server is full
3197 if (developer_extra.integer)
3198 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3199 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3203 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3205 const char *challenge = NULL;
3207 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3210 // If there was a challenge in the getinfo message
3211 if (length > 8 && string[7] == ' ')
3212 challenge = string + 8;
3214 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3216 if (developer_extra.integer)
3217 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3218 NetConn_WriteString(mysocket, response, peeraddress);
3222 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3224 const char *challenge = NULL;
3226 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3229 // If there was a challenge in the getinfo message
3230 if (length > 10 && string[9] == ' ')
3231 challenge = string + 10;
3233 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3235 if (developer_extra.integer)
3236 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3237 NetConn_WriteString(mysocket, response, peeraddress);
3241 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3243 char *password = string + 20;
3244 char *timeval = string + 37;
3245 char *s = strchr(timeval, ' ');
3246 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3247 const char *userlevel;
3249 if(rcon_secure.integer > 1)
3253 return true; // invalid packet
3256 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3257 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3260 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3262 char *password = string + 25;
3263 char *challenge = string + 42;
3264 char *s = strchr(challenge, ' ');
3265 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3266 const char *userlevel;
3268 return true; // invalid packet
3271 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3272 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3275 if (length >= 5 && !memcmp(string, "rcon ", 5))
3278 char *s = string + 5;
3279 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3282 if(rcon_secure.integer > 0)
3285 for (j = 0;!ISWHITESPACE(*s);s++)
3286 if (j < (int)sizeof(password) - 1)
3288 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3291 if (!ISWHITESPACE(password[0]))
3293 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3294 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3298 if (!strncmp(string, "extResponse ", 12))
3300 ++sv_net_extresponse_count;
3301 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3302 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3303 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3304 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3307 if (!strncmp(string, "ping", 4))
3309 if (developer_extra.integer)
3310 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3311 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3314 if (!strncmp(string, "ack", 3))
3316 // we may not have liked the packet, but it was a command packet, so
3317 // we're done processing this packet now
3320 // netquake control packets, supported for compatibility only, and only
3321 // when running game protocols that are normally served via this connection
3323 // (this protects more modern protocols against being used for
3324 // Quake packet flood Denial Of Service attacks)
3325 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)
3329 const char *protocolname;
3330 client_t *knownclient;
3331 client_t *newclient;
3334 SZ_Clear(&sv_message);
3335 SZ_Write(&sv_message, data, length);
3336 MSG_BeginReading(&sv_message);
3337 c = MSG_ReadByte(&sv_message);
3341 if (developer_extra.integer)
3342 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3343 if(!(islocal || sv_public.integer > -2))
3345 if (developer_extra.integer)
3346 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3347 SZ_Clear(&sv_message);
3348 // save space for the header, filled in later
3349 MSG_WriteLong(&sv_message, 0);
3350 MSG_WriteByte(&sv_message, CCREP_REJECT);
3351 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3352 MSG_WriteString(&sv_message, "\n");
3353 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3354 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3355 SZ_Clear(&sv_message);
3359 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3360 protocolnumber = MSG_ReadByte(&sv_message);
3361 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3363 if (developer_extra.integer)
3364 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3365 SZ_Clear(&sv_message);
3366 // save space for the header, filled in later
3367 MSG_WriteLong(&sv_message, 0);
3368 MSG_WriteByte(&sv_message, CCREP_REJECT);
3369 MSG_WriteString(&sv_message, "Incompatible version.\n");
3370 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3371 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3372 SZ_Clear(&sv_message);
3376 // see if this connect request comes from a known client
3377 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3379 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3381 // this is either a duplicate connection request
3382 // or coming back from a timeout
3383 // (if so, keep their stuff intact)
3385 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3386 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3388 if (developer_extra.integer)
3389 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3390 SZ_Clear(&sv_message);
3391 // save space for the header, filled in later
3392 MSG_WriteLong(&sv_message, 0);
3393 MSG_WriteByte(&sv_message, CCREP_REJECT);
3394 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3395 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3396 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3397 SZ_Clear(&sv_message);
3402 if (developer_extra.integer)
3403 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3404 SZ_Clear(&sv_message);
3405 // save space for the header, filled in later
3406 MSG_WriteLong(&sv_message, 0);
3407 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3408 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3409 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3410 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3411 SZ_Clear(&sv_message);
3413 // if client is already spawned, re-send the
3414 // serverinfo message as they'll need it to play
3415 if (knownclient->begun)
3416 SV_SendServerinfo(knownclient);
3421 // this is a new client, check for connection flood
3422 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3425 // find a slot for the new client
3426 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3429 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3431 // connect to the client
3432 // everything is allocated, just fill in the details
3433 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3434 if (developer_extra.integer)
3435 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3436 // send back the info about the server connection
3437 SZ_Clear(&sv_message);
3438 // save space for the header, filled in later
3439 MSG_WriteLong(&sv_message, 0);
3440 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3441 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3442 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3443 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3444 SZ_Clear(&sv_message);
3445 // now set up the client struct
3446 SV_ConnectClient(clientnum, conn);
3447 NetConn_Heartbeat(1);
3452 if (developer_extra.integer)
3453 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3454 // no room; try to let player know
3455 SZ_Clear(&sv_message);
3456 // save space for the header, filled in later
3457 MSG_WriteLong(&sv_message, 0);
3458 MSG_WriteByte(&sv_message, CCREP_REJECT);
3459 MSG_WriteString(&sv_message, "Server is full.\n");
3460 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3461 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3462 SZ_Clear(&sv_message);
3464 case CCREQ_SERVER_INFO:
3465 if (developer_extra.integer)
3466 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3467 if(!(islocal || sv_public.integer > -1))
3470 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3473 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3476 char myaddressstring[128];
3477 if (developer_extra.integer)
3478 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3479 SZ_Clear(&sv_message);
3480 // save space for the header, filled in later
3481 MSG_WriteLong(&sv_message, 0);
3482 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3483 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3484 MSG_WriteString(&sv_message, myaddressstring);
3485 MSG_WriteString(&sv_message, hostname.string);
3486 MSG_WriteString(&sv_message, sv.name);
3487 // How many clients are there?
3488 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3489 if (svs.clients[i].active)
3491 MSG_WriteByte(&sv_message, numclients);
3492 MSG_WriteByte(&sv_message, svs.maxclients);
3493 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3494 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3495 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3496 SZ_Clear(&sv_message);
3499 case CCREQ_PLAYER_INFO:
3500 if (developer_extra.integer)
3501 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3502 if(!(islocal || sv_public.integer > -1))
3505 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3510 int playerNumber, activeNumber, clientNumber;
3513 playerNumber = MSG_ReadByte(&sv_message);
3515 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3516 if (client->active && ++activeNumber == playerNumber)
3518 if (clientNumber != svs.maxclients)
3520 SZ_Clear(&sv_message);
3521 // save space for the header, filled in later
3522 MSG_WriteLong(&sv_message, 0);
3523 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3524 MSG_WriteByte(&sv_message, playerNumber);
3525 MSG_WriteString(&sv_message, client->name);
3526 MSG_WriteLong(&sv_message, client->colors);
3527 MSG_WriteLong(&sv_message, client->frags);
3528 MSG_WriteLong(&sv_message, (int)(host.realtime - client->connecttime));
3529 if(sv_status_privacy.integer)
3530 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3532 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3533 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3534 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3535 SZ_Clear(&sv_message);
3539 case CCREQ_RULE_INFO:
3540 if (developer_extra.integer)
3541 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3542 if(!(islocal || sv_public.integer > -1))
3545 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3552 // find the search start location
3553 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3554 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CF_NOTIFY);
3556 // send the response
3557 SZ_Clear(&sv_message);
3558 // save space for the header, filled in later
3559 MSG_WriteLong(&sv_message, 0);
3560 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3563 MSG_WriteString(&sv_message, var->name);
3564 MSG_WriteString(&sv_message, var->string);
3566 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3567 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3568 SZ_Clear(&sv_message);
3572 if (developer_extra.integer)
3573 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3574 if (sv.active && !rcon_secure.integer)
3576 char password[2048];
3580 const char *userlevel;
3581 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3582 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3584 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3585 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3586 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3593 SZ_Clear(&sv_message);
3594 // we may not have liked the packet, but it was a valid control
3595 // packet, so we're done processing this packet now
3600 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3602 SV_ReadClientMessage();
3609 void NetConn_ServerFrame(void)
3612 lhnetaddress_t peeraddress;
3613 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3614 for (i = 0;i < sv_numsockets;i++)
3615 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3616 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3619 void NetConn_SleepMicroseconds(int microseconds)
3621 LHNET_SleepUntilPacket_Microseconds(microseconds);
3625 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
3629 lhnetaddress_t masteraddress;
3630 lhnetaddress_t broadcastaddress;
3633 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3636 // 26000 is the default quake server port, servers on other ports will not
3638 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3639 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3643 for (i = 0;i < cl_numsockets;i++)
3647 const char *cmdname, *extraoptions;
3648 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3650 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3652 // search LAN for Quake servers
3653 SZ_Clear(&cl_message);
3654 // save space for the header, filled in later
3655 MSG_WriteLong(&cl_message, 0);
3656 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3657 MSG_WriteString(&cl_message, "QUAKE");
3658 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3659 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3660 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3661 SZ_Clear(&cl_message);
3663 // search LAN for DarkPlaces servers
3664 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3667 // build the getservers message to send to the dpmaster master servers
3668 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3670 cmdname = "getserversExt";
3671 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3675 cmdname = "getservers";
3678 memcpy(request, "\377\377\377\377", 4);
3679 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3682 for (masternum = 0;sv_masters[masternum].name;masternum++)
3684 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3687 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3691 // search favorite servers
3692 for(j = 0; j < nFavorites; ++j)
3694 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3696 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3697 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3704 // only query QuakeWorld servers when the user wants to
3707 for (i = 0;i < cl_numsockets;i++)
3711 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3713 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3715 // search LAN for QuakeWorld servers
3716 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3718 // build the getservers message to send to the qwmaster master servers
3719 // note this has no -1 prefix, and the trailing nul byte is sent
3720 dpsnprintf(request, sizeof(request), "c\n");
3724 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3726 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3728 if (m_state != m_slist)
3730 char lookupstring[128];
3731 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3732 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3735 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3739 // search favorite servers
3740 for(j = 0; j < nFavorites; ++j)
3742 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3744 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3746 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3747 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3754 if (!masterquerycount)
3756 Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
3757 M_Update_Return_Reason("No network");
3762 void NetConn_Heartbeat(int priority)
3764 lhnetaddress_t masteraddress;
3766 lhnetsocket_t *mysocket;
3768 // if it's a state change (client connected), limit next heartbeat to no
3769 // more than 30 sec in the future
3770 if (priority == 1 && nextheartbeattime > host.realtime + 30.0)
3771 nextheartbeattime = host.realtime + 30.0;
3773 // limit heartbeatperiod to 30 to 270 second range,
3774 // lower limit is to avoid abusing master servers with excess traffic,
3775 // upper limit is to avoid timing out on the master server (which uses
3777 if (sv_heartbeatperiod.value < 30)
3778 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3779 if (sv_heartbeatperiod.value > 270)
3780 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3782 // make advertising optional and don't advertise singleplayer games, and
3783 // only send a heartbeat as often as the admin wants
3784 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
3786 nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
3787 for (masternum = 0;sv_masters[masternum].name;masternum++)
3788 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3789 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3793 static void Net_Heartbeat_f(cmd_state_t *cmd)
3796 NetConn_Heartbeat(2);
3798 Con_Print("No server running, can not heartbeat to master server.\n");
3801 static void PrintStats(netconn_t *conn)
3803 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3804 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3806 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3807 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3808 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3809 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3810 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3811 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3812 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3813 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3814 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3815 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3818 void Net_Stats_f(cmd_state_t *cmd)
3821 Con_Print("connections =\n");
3822 for (conn = netconn_list;conn;conn = conn->next)
3827 void Net_Refresh_f(cmd_state_t *cmd)
3829 if (m_state != m_slist) {
3830 Con_Print("Sending new requests to master servers\n");
3831 ServerList_QueryList(false, true, false, true);
3832 Con_Print("Listening for replies...\n");
3834 ServerList_QueryList(false, true, false, false);
3837 void Net_Slist_f(cmd_state_t *cmd)
3839 ServerList_ResetMasks();
3840 serverlist_sortbyfield = SLIF_PING;
3841 serverlist_sortflags = 0;
3842 if (m_state != m_slist) {
3843 Con_Print("Sending requests to master servers\n");
3844 ServerList_QueryList(true, true, false, true);
3845 Con_Print("Listening for replies...\n");
3847 ServerList_QueryList(true, true, false, false);
3850 void Net_SlistQW_f(cmd_state_t *cmd)
3852 ServerList_ResetMasks();
3853 serverlist_sortbyfield = SLIF_PING;
3854 serverlist_sortflags = 0;
3855 if (m_state != m_slist) {
3856 Con_Print("Sending requests to master servers\n");
3857 ServerList_QueryList(true, false, true, true);
3858 serverlist_consoleoutput = true;
3859 Con_Print("Listening for replies...\n");
3861 ServerList_QueryList(true, false, true, false);
3865 void NetConn_Init(void)
3868 lhnetaddress_t tempaddress;
3869 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3870 Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
3872 Cmd_AddCommand(CF_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
3873 Cmd_AddCommand(CF_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3874 Cmd_AddCommand(CF_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3876 Cmd_AddCommand(CF_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3877 Cvar_RegisterVariable(&net_test);
3878 Cvar_RegisterVariable(&net_usesizelimit);
3879 Cvar_RegisterVariable(&net_burstreserve);
3880 Cvar_RegisterVariable(&rcon_restricted_password);
3881 Cvar_RegisterVariable(&rcon_restricted_commands);
3882 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3883 Cvar_RegisterVariable(&net_slist_queriespersecond);
3884 Cvar_RegisterVariable(&net_slist_queriesperframe);
3885 Cvar_RegisterVariable(&net_slist_timeout);
3886 Cvar_RegisterVariable(&net_slist_maxtries);
3887 Cvar_RegisterVariable(&net_slist_favorites);
3889 Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
3891 Cvar_RegisterVariable(&net_slist_pause);
3892 #ifdef IP_TOS // register cvar only if supported
3893 Cvar_RegisterVariable(&net_tos_dscp);
3895 Cvar_RegisterVariable(&net_messagetimeout);
3896 Cvar_RegisterVariable(&net_connecttimeout);
3897 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3898 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3899 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3900 Cvar_RegisterVariable(&net_sourceaddresscheck);
3901 Cvar_RegisterVariable(&net_fakelag);
3902 Cvar_RegisterVariable(&net_fakeloss_send);
3903 Cvar_RegisterVariable(&net_fakeloss_receive);
3904 Cvar_RegisterVirtual(&net_fakelag, "cl_netlocalping");
3905 Cvar_RegisterVirtual(&net_fakeloss_send, "cl_netpacketloss_send");
3906 Cvar_RegisterVirtual(&net_fakeloss_receive, "cl_netpacketloss_receive");
3907 Cvar_RegisterVariable(&hostname);
3908 Cvar_RegisterVariable(&developer_networking);
3909 Cvar_RegisterVariable(&cl_netport);
3910 Cvar_RegisterCallback(&cl_netport, NetConn_cl_netport_Callback);
3911 Cvar_RegisterVariable(&sv_netport);
3912 Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
3913 Cvar_RegisterVariable(&net_address);
3914 Cvar_RegisterVariable(&net_address_ipv6);
3915 Cvar_RegisterVariable(&sv_public);
3916 Cvar_RegisterVariable(&sv_public_rejectreason);
3917 Cvar_RegisterVariable(&sv_heartbeatperiod);
3918 for (i = 0;sv_masters[i].name;i++)
3919 Cvar_RegisterVariable(&sv_masters[i]);
3920 Cvar_RegisterVariable(&gameversion);
3921 Cvar_RegisterVariable(&gameversion_min);
3922 Cvar_RegisterVariable(&gameversion_max);
3923 // 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.
3924 if ((i = Sys_CheckParm("-ip")) && i + 1 < sys.argc)
3926 if (LHNETADDRESS_FromString(&tempaddress, sys.argv[i + 1], 0) == 1)
3928 Con_Printf("-ip option used, setting net_address to \"%s\"\n", sys.argv[i + 1]);
3929 Cvar_SetQuick(&net_address, sys.argv[i + 1]);
3932 Con_Printf(CON_ERROR "-ip option used, but unable to parse the address \"%s\"\n", sys.argv[i + 1]);
3934 // 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
3935 if (((i = Sys_CheckParm("-port")) || (i = Sys_CheckParm("-ipport")) || (i = Sys_CheckParm("-udpport"))) && i + 1 < sys.argc)
3937 i = atoi(sys.argv[i + 1]);
3938 if (i >= 0 && i < 65536)
3940 Con_Printf("-port option used, setting port cvar to %i\n", i);
3941 Cvar_SetValueQuick(&sv_netport, i);
3944 Con_Printf(CON_ERROR "-port option used, but %i is not a valid port number\n", i);
3948 cl_message.data = cl_message_buf;
3949 cl_message.maxsize = sizeof(cl_message_buf);
3950 cl_message.cursize = 0;
3951 sv_message.data = sv_message_buf;
3952 sv_message.maxsize = sizeof(sv_message_buf);
3953 sv_message.cursize = 0;
3955 if (Thread_HasThreads())
3956 netconn_mutex = Thread_CreateMutex();
3959 void NetConn_Shutdown(void)
3961 NetConn_CloseClientPorts();
3962 NetConn_CloseServerPorts();
3965 Thread_DestroyMutex(netconn_mutex);
3966 netconn_mutex = NULL;