2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Ashley Rose Hale (LadyHavoc)
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // for secure rcon authentication
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
35 // note this defaults on for dedicated servers, off for listen servers
36 cvar_t sv_public = {CF_SERVER, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
37 cvar_t sv_public_rejectreason = {CF_SERVER, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CF_SERVER | CF_ARCHIVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
39 extern cvar_t sv_status_privacy;
41 static cvar_t sv_masters [] =
43 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master1", "", "user-chosen master server 1"},
44 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master2", "", "user-chosen master server 2"},
45 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master3", "", "user-chosen master server 3"},
46 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master4", "", "user-chosen master server 4"},
47 {CF_CLIENT | CF_SERVER, "sv_masterextra1", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 1 (admin: Willis)"}, // admin: Willis
48 {CF_CLIENT | CF_SERVER, "sv_masterextra2", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 2 (admin: tChr)"}, // admin: tChr
53 static cvar_t sv_qwmasters [] =
55 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
56 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
57 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
58 {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
59 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
60 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
61 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
62 {CF_CLIENT | CF_SERVER, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
67 static double nextheartbeattime = 0;
71 static unsigned char cl_message_buf[NET_MAXMESSAGE];
72 static unsigned char sv_message_buf[NET_MAXMESSAGE];
73 char cl_readstring[MAX_INPUTLINE];
74 char sv_readstring[MAX_INPUTLINE];
76 cvar_t net_test = {CF_CLIENT | CF_SERVER, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
77 cvar_t net_usesizelimit = {CF_SERVER, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
78 cvar_t net_burstreserve = {CF_SERVER, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
79 cvar_t net_messagetimeout = {CF_CLIENT | CF_SERVER, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
80 cvar_t net_connecttimeout = {CF_CLIENT | CF_SERVER, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
81 cvar_t net_connectfloodblockingtimeout = {CF_SERVER, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
82 cvar_t net_challengefloodblockingtimeout = {CF_SERVER, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
83 cvar_t net_getstatusfloodblockingtimeout = {CF_SERVER, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
84 cvar_t net_sourceaddresscheck = {CF_CLIENT, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
85 cvar_t hostname = {CF_SERVER | CF_ARCHIVE, "hostname", "UNNAMED", "server message to show in server browser"};
86 cvar_t developer_networking = {CF_CLIENT | CF_SERVER, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
88 cvar_t net_fakelag = {CF_CLIENT, "net_fakelag","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
89 static cvar_t net_fakeloss_send = {CF_CLIENT, "net_fakeloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
90 static cvar_t net_fakeloss_receive = {CF_CLIENT, "net_fakeloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
91 static cvar_t net_slist_queriespersecond = {CF_CLIENT, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
92 static cvar_t net_slist_queriesperframe = {CF_CLIENT, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
93 static cvar_t net_slist_timeout = {CF_CLIENT, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
94 static cvar_t net_slist_pause = {CF_CLIENT, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
95 static cvar_t net_slist_maxtries = {CF_CLIENT, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
96 static cvar_t net_slist_favorites = {CF_CLIENT | CF_ARCHIVE, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
97 static cvar_t net_tos_dscp = {CF_CLIENT | CF_ARCHIVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
98 static cvar_t gameversion = {CF_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
99 static cvar_t gameversion_min = {CF_CLIENT | CF_SERVER, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
100 static cvar_t gameversion_max = {CF_CLIENT | CF_SERVER, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
101 static cvar_t rcon_restricted_password = {CF_SERVER | CF_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
102 static cvar_t rcon_restricted_commands = {CF_SERVER, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
103 static cvar_t rcon_secure_maxdiff = {CF_SERVER, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
104 extern cvar_t rcon_secure;
105 extern cvar_t rcon_secure_challengetimeout;
107 double masterquerytime = -1000;
108 int masterquerycount = 0;
109 int masterreplycount = 0;
110 int serverquerycount = 0;
111 int serverreplycount = 0;
113 challenge_t challenges[MAX_CHALLENGES];
116 /// this is only false if there are still servers left to query
117 static qbool serverlist_querysleep = true;
118 static qbool serverlist_paused = false;
119 /// this is pushed a second or two ahead of realtime whenever a master server
120 /// reply is received, to avoid issuing queries while master replies are still
121 /// flooding in (which would make a mess of the ping times)
122 static double serverlist_querywaittime = 0;
125 static int cl_numsockets;
126 static lhnetsocket_t *cl_sockets[16];
127 static int sv_numsockets;
128 static lhnetsocket_t *sv_sockets[16];
130 netconn_t *netconn_list = NULL;
131 mempool_t *netconn_mempool = NULL;
132 void *netconn_mutex = NULL;
134 cvar_t cl_netport = {CF_CLIENT, "cl_port", "0", "forces client to use chosen port number if not 0"};
135 cvar_t sv_netport = {CF_SERVER, "port", "26000", "server port for players to connect to"};
136 cvar_t net_address = {CF_CLIENT | CF_SERVER, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
137 cvar_t net_address_ipv6 = {CF_CLIENT | CF_SERVER, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
139 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
140 int cl_net_extresponse_count = 0;
141 int cl_net_extresponse_last = 0;
143 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
144 int sv_net_extresponse_count = 0;
145 int sv_net_extresponse_last = 0;
148 // ServerList interface
149 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
150 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
152 serverlist_infofield_t serverlist_sortbyfield;
153 int serverlist_sortflags;
155 int serverlist_viewcount = 0;
156 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
158 int serverlist_maxcachecount = 0;
159 int serverlist_cachecount = 0;
160 serverlist_entry_t *serverlist_cache = NULL;
162 qbool serverlist_consoleoutput;
164 static int nFavorites = 0;
165 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
166 static int nFavorites_idfp = 0;
167 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
169 void NetConn_UpdateFavorites_c(cvar_t *var)
175 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
177 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
178 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
179 // (if v6 address contains port, it must start with '[')
181 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
186 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
192 /// helper function to insert a value into the viewset
193 /// spare entries will be removed
194 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
197 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
198 i = serverlist_viewcount++;
200 i = SERVERLIST_VIEWLISTSIZE - 1;
203 for( ; i > index ; i-- )
204 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
206 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
209 /// we suppose serverlist_viewcount to be valid, ie > 0
210 static void _ServerList_ViewList_Helper_Remove( int index )
212 serverlist_viewcount--;
213 for( ; index < serverlist_viewcount ; index++ )
214 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
217 /// \returns true if A should be inserted before B
218 static qbool _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
220 int result = 0; // > 0 if for numbers A > B and for text if A < B
222 if( serverlist_sortflags & SLSF_CATEGORIES )
224 result = A->info.category - B->info.category;
229 if( serverlist_sortflags & SLSF_FAVORITES )
231 if(A->info.isfavorite != B->info.isfavorite)
232 return A->info.isfavorite;
235 switch( serverlist_sortbyfield ) {
237 result = A->info.ping - B->info.ping;
239 case SLIF_MAXPLAYERS:
240 result = A->info.maxplayers - B->info.maxplayers;
242 case SLIF_NUMPLAYERS:
243 result = A->info.numplayers - B->info.numplayers;
246 result = A->info.numbots - B->info.numbots;
249 result = A->info.numhumans - B->info.numhumans;
252 result = A->info.freeslots - B->info.freeslots;
255 result = A->info.protocol - B->info.protocol;
258 result = strcmp( B->info.cname, A->info.cname );
261 result = strcasecmp( B->info.game, A->info.game );
264 result = strcasecmp( B->info.map, A->info.map );
267 result = strcasecmp( B->info.mod, A->info.mod );
270 result = strcasecmp( B->info.name, A->info.name );
273 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
276 result = A->info.category - B->info.category;
278 case SLIF_ISFAVORITE:
279 result = !!B->info.isfavorite - !!A->info.isfavorite;
282 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
288 if( serverlist_sortflags & SLSF_DESCENDING )
294 // if the chosen sort key is identical, sort by index
295 // (makes this a stable sort, so that later replies from servers won't
296 // shuffle the servers around when they have the same ping)
300 static qbool _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
302 // This should actually be done with some intermediate and end-of-function return
314 case SLMO_GREATEREQUAL:
316 case SLMO_NOTCONTAIN:
317 case SLMO_STARTSWITH:
318 case SLMO_NOTSTARTSWITH:
321 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
326 static qbool _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
329 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
330 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
331 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
332 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
334 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
335 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
338 // Same here, also using an intermediate & final return would be more appropriate
342 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
343 case SLMO_NOTCONTAIN:
344 return !*bufferB || !strstr( bufferA, bufferB );
345 case SLMO_STARTSWITH:
346 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
347 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
348 case SLMO_NOTSTARTSWITH:
349 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
351 return strcmp( bufferA, bufferB ) < 0;
353 return strcmp( bufferA, bufferB ) <= 0;
355 return strcmp( bufferA, bufferB ) == 0;
357 return strcmp( bufferA, bufferB ) > 0;
359 return strcmp( bufferA, bufferB ) != 0;
360 case SLMO_GREATEREQUAL:
361 return strcmp( bufferA, bufferB ) >= 0;
363 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
368 static qbool _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
370 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
372 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
374 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
376 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
378 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
380 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
382 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
384 if( *mask->info.cname
385 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
388 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
391 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
394 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
397 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
399 if( *mask->info.qcstatus
400 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
402 if( *mask->info.players
403 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
405 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
407 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
412 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
414 int start, end, mid, i;
417 // reject incompatible servers
419 entry->info.gameversion != gameversion.integer
422 gameversion_min.integer >= 0 // min/max range set by user/mod?
423 && gameversion_max.integer >= 0
424 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
425 && gameversion_max.integer >= entry->info.gameversion
430 // refresh the "favorite" status
431 entry->info.isfavorite = false;
432 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
434 char idfp[FP64_SIZE+1];
435 for(i = 0; i < nFavorites; ++i)
437 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
439 entry->info.isfavorite = true;
443 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
445 for(i = 0; i < nFavorites_idfp; ++i)
447 if(!strcmp(idfp, favorites_idfp[i]))
449 entry->info.isfavorite = true;
456 // refresh the "category"
457 entry->info.category = MR_GetServerListEntryCategory(entry);
459 // FIXME: change this to be more readable (...)
460 // now check whether it passes through the masks
461 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
462 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
465 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
466 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
468 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
471 if( !serverlist_viewcount ) {
472 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
475 // ok, insert it, we just need to find out where exactly:
478 // check whether to insert it as new first item
479 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
480 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
482 } // check whether to insert it as new last item
483 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
484 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
488 end = serverlist_viewcount - 1;
489 while( end > start + 1 )
491 mid = (start + end) / 2;
492 // test the item that lies in the middle between start and end
493 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
494 // the item has to be in the upper half
497 // the item has to be in the lower half
500 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
503 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
506 for( i = 0; i < serverlist_viewcount; i++ )
508 if (ServerList_GetViewEntry(i) == entry)
510 _ServerList_ViewList_Helper_Remove(i);
516 void ServerList_RebuildViewList(void)
520 serverlist_viewcount = 0;
521 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
522 serverlist_entry_t *entry = &serverlist_cache[i];
523 // also display entries that are currently being refreshed [11/8/2007 Black]
524 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
525 ServerList_ViewList_Insert( entry );
529 void ServerList_ResetMasks(void)
533 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
534 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
535 // numbots needs to be compared to -1 to always succeed
536 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
537 serverlist_andmasks[i].info.numbots = -1;
538 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
539 serverlist_ormasks[i].info.numbots = -1;
542 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
545 int numplayers = 0, maxplayers = 0;
546 for (i = 0;i < serverlist_cachecount;i++)
548 if (serverlist_cache[i].query == SQS_QUERIED)
550 numplayers += serverlist_cache[i].info.numhumans;
551 maxplayers += serverlist_cache[i].info.maxplayers;
554 *numplayerspointer = numplayers;
555 *maxplayerspointer = maxplayers;
559 static void _ServerList_Test(void)
562 if (serverlist_maxcachecount <= 1024)
564 serverlist_maxcachecount = 1024;
565 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
567 for( i = 0 ; i < 1024 ; i++ ) {
568 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
569 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
570 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
571 serverlist_cache[serverlist_cachecount].finished = true;
572 dpsnprintf( serverlist_cache[serverlist_cachecount].line1, sizeof(serverlist_cache[serverlist_cachecount].info.line1), "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
573 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
574 serverlist_cachecount++;
579 void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool consoleoutput)
581 masterquerytime = host.realtime;
582 masterquerycount = 0;
583 masterreplycount = 0;
585 serverquerycount = 0;
586 serverreplycount = 0;
587 serverlist_cachecount = 0;
588 serverlist_viewcount = 0;
589 serverlist_maxcachecount = 0;
590 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
592 // refresh all entries
594 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
595 serverlist_entry_t *entry = &serverlist_cache[ n ];
596 entry->query = SQS_REFRESHING;
597 entry->querycounter = 0;
600 serverlist_consoleoutput = consoleoutput;
602 //_ServerList_Test();
604 NetConn_QueryMasters(querydp, queryqw);
610 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
614 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
615 Thread_LockMutex(netconn_mutex);
616 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
617 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
618 Thread_UnlockMutex(netconn_mutex);
621 if (net_fakeloss_receive.integer)
622 for (i = 0;i < cl_numsockets;i++)
623 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_receive.integer)
625 if (developer_networking.integer)
627 char addressstring[128], addressstring2[128];
628 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
631 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
632 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
633 Com_HexDumpToConsole((unsigned char *)data, length);
636 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
641 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
645 if (net_fakeloss_send.integer)
646 for (i = 0;i < cl_numsockets;i++)
647 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_send.integer)
649 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
650 Thread_LockMutex(netconn_mutex);
651 ret = LHNET_Write(mysocket, data, length, peeraddress);
652 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
653 Thread_UnlockMutex(netconn_mutex);
654 if (developer_networking.integer)
656 char addressstring[128], addressstring2[128];
657 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
658 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
659 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", (void *)mysocket, addressstring, (void *)data, length, (void *)peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
660 Com_HexDumpToConsole((unsigned char *)data, length);
665 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
667 // note this does not include the trailing NULL because we add that in the parser
668 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
671 qbool NetConn_CanSend(netconn_t *conn)
673 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
674 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = host.realtime;
675 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
676 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
677 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
678 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
679 if (host.realtime > conn->cleartime)
683 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
688 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
690 double bursttime = burstsize / (double)rate;
692 // delay later packets to obey rate limit
693 if (*cleartime < host.realtime - bursttime)
694 *cleartime = host.realtime - bursttime;
695 *cleartime = *cleartime + len / (double)rate;
697 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
698 if (net_test.integer)
700 if (*cleartime < host.realtime)
701 *cleartime = host.realtime;
705 static int NetConn_AddCryptoFlag(crypto_t *crypto)
707 // HACK: if an encrypted connection is used, randomly set some unused
708 // flags. When AES encryption is enabled, that will make resends differ
709 // from the original, so that e.g. substring filters in a router/IPS
710 // are unlikely to match a second time. See also "startkeylogger".
712 if (crypto->authenticated)
714 // Let's always set at least one of the bits.
715 int r = rand() % 7 + 1;
717 flag |= NETFLAG_CRYPTO0;
719 flag |= NETFLAG_CRYPTO1;
721 flag |= NETFLAG_CRYPTO2;
726 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qbool quakesignon_suppressreliables)
729 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
730 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
732 // if this packet was supposedly choked, but we find ourselves sending one
733 // anyway, make sure the size counting starts at zero
734 // (this mostly happens on level changes and disconnects and such)
735 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
736 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
738 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
740 if (protocol == PROTOCOL_QUAKEWORLD)
745 // note that it is ok to send empty messages to the qw server,
746 // otherwise it won't respond to us at all
748 sendreliable = false;
749 // if the remote side dropped the last reliable message, resend it
750 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
752 // if the reliable transmit buffer is empty, copy the current message out
753 if (!conn->sendMessageLength && conn->message.cursize)
755 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
756 conn->sendMessageLength = conn->message.cursize;
757 SZ_Clear(&conn->message); // clear the message buffer
758 conn->qw.reliable_sequence ^= 1;
761 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
762 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
763 // last received unreliable packet number, and last received reliable packet number (0 or 1)
764 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
766 conn->outgoing_unreliable_sequence++;
767 // client sends qport in every packet
768 if (conn == cls.netcon)
770 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
772 // also update cls.qw_outgoing_sequence
773 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
775 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
777 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
781 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
783 // add the reliable message if there is one
786 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
787 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
788 packetLen += conn->sendMessageLength;
789 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
792 // add the unreliable message if possible
793 if (packetLen + data->cursize <= 1400)
795 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
796 memcpy(sendbuffer + packetLen, data->data, data->cursize);
797 packetLen += data->cursize;
800 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
803 conn->unreliableMessagesSent++;
805 totallen += packetLen + 28;
809 unsigned int packetLen;
810 unsigned int dataLen;
815 // if a reliable message fragment has been lost, send it again
816 if (conn->sendMessageLength && (host.realtime - conn->lastSendTime) > 1.0)
818 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
820 dataLen = conn->sendMessageLength;
825 dataLen = MAX_PACKETFRAGMENT;
829 packetLen = NET_HEADERSIZE + dataLen;
831 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
832 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
833 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
835 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
837 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
838 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
840 conn->lastSendTime = host.realtime;
841 conn->packetsReSent++;
844 totallen += (int)sendmelen + 28;
847 // if we have a new reliable message to send, do so
848 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
850 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
852 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
853 conn->message.overflowed = true;
857 if (developer_networking.integer && conn == cls.netcon)
859 Con_Print("client sending reliable message to server:\n");
860 SZ_HexDumpToConsole(&conn->message);
863 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
864 conn->sendMessageLength = conn->message.cursize;
865 SZ_Clear(&conn->message);
867 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
869 dataLen = conn->sendMessageLength;
874 dataLen = MAX_PACKETFRAGMENT;
878 packetLen = NET_HEADERSIZE + dataLen;
880 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
881 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
882 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
884 conn->nq.sendSequence++;
886 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
888 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
890 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
892 conn->lastSendTime = host.realtime;
894 conn->reliableMessagesSent++;
896 totallen += (int)sendmelen + 28;
899 // if we have an unreliable message to send, do so
902 packetLen = NET_HEADERSIZE + data->cursize;
904 if (packetLen > (int)sizeof(sendbuffer))
906 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
910 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
911 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
912 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
914 conn->outgoing_unreliable_sequence++;
916 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
918 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
920 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
923 conn->unreliableMessagesSent++;
925 totallen += (int)sendmelen + 28;
929 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
934 qbool NetConn_HaveClientPorts(void)
936 return !!cl_numsockets;
939 qbool NetConn_HaveServerPorts(void)
941 return !!sv_numsockets;
944 void NetConn_CloseClientPorts(void)
946 for (;cl_numsockets > 0;cl_numsockets--)
947 if (cl_sockets[cl_numsockets - 1])
948 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
951 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
953 lhnetaddress_t address;
956 char addressstring2[1024];
957 if (addressstring && addressstring[0])
958 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
960 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
963 if ((s = LHNET_OpenSocket_Connectionless(&address)))
965 cl_sockets[cl_numsockets++] = s;
966 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
967 if (addresstype != LHNETADDRESSTYPE_LOOP)
968 Con_Printf("Client opened a socket on address %s\n", addressstring2);
972 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
973 Con_Printf(CON_ERROR "Client failed to open a socket on address %s\n", addressstring2);
977 Con_Printf(CON_ERROR "Client unable to parse address %s\n", addressstring);
980 void NetConn_OpenClientPorts(void)
983 NetConn_CloseClientPorts();
985 SV_LockThreadMutex(); // FIXME recursive?
986 Crypto_LoadKeys(); // client sockets
987 SV_UnlockThreadMutex();
989 port = bound(0, cl_netport.integer, 65535);
990 if (cl_netport.integer != port)
991 Cvar_SetValueQuick(&cl_netport, port);
993 Con_Printf("Client using an automatically assigned port\n");
995 Con_Printf("Client using port %i\n", port);
996 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
997 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
998 #ifndef NOSUPPORTIPV6
999 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1003 void NetConn_CloseServerPorts(void)
1005 for (;sv_numsockets > 0;sv_numsockets--)
1006 if (sv_sockets[sv_numsockets - 1])
1007 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1010 static qbool NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1012 lhnetaddress_t address;
1015 char addressstring2[1024];
1018 for (port = defaultport; port <= defaultport + range; port++)
1020 if (addressstring && addressstring[0])
1021 success = LHNETADDRESS_FromString(&address, addressstring, port);
1023 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1026 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1028 sv_sockets[sv_numsockets++] = s;
1029 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1030 if (addresstype != LHNETADDRESSTYPE_LOOP)
1031 Con_Printf("Server listening on address %s\n", addressstring2);
1036 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1037 Con_Printf(CON_ERROR "Server failed to open socket on address %s\n", addressstring2);
1042 Con_Printf(CON_ERROR "Server unable to parse address %s\n", addressstring);
1043 // if it cant parse one address, it wont be able to parse another for sure
1050 void NetConn_OpenServerPorts(int opennetports)
1053 NetConn_CloseServerPorts();
1055 SV_LockThreadMutex(); // FIXME recursive?
1056 Crypto_LoadKeys(); // server sockets
1057 SV_UnlockThreadMutex();
1059 NetConn_UpdateSockets();
1060 port = bound(0, sv_netport.integer, 65535);
1063 if (sv_netport.integer != port)
1064 Cvar_SetValueQuick(&sv_netport, port);
1065 if (cls.state != ca_dedicated)
1066 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1069 #ifndef NOSUPPORTIPV6
1070 qbool ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1071 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1073 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1076 if (sv_numsockets == 0)
1077 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1080 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1082 int i, a = LHNETADDRESS_GetAddressType(address);
1083 for (i = 0;i < cl_numsockets;i++)
1084 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1085 return cl_sockets[i];
1089 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1091 int i, a = LHNETADDRESS_GetAddressType(address);
1092 for (i = 0;i < sv_numsockets;i++)
1093 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1094 return sv_sockets[i];
1098 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1101 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1102 conn->mysocket = mysocket;
1103 conn->peeraddress = *peeraddress;
1104 conn->lastMessageTime = host.realtime;
1105 conn->message.data = conn->messagedata;
1106 conn->message.maxsize = sizeof(conn->messagedata);
1107 conn->message.cursize = 0;
1108 // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
1109 // reduce effectiveness of connection request floods
1110 conn->timeout = host.realtime + net_connecttimeout.value;
1111 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1112 conn->next = netconn_list;
1113 netconn_list = conn;
1117 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1118 void NetConn_Close(netconn_t *conn)
1121 // remove connection from list
1123 // allow the client to reconnect immediately
1124 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1126 if (conn == netconn_list)
1127 netconn_list = conn->next;
1130 for (c = netconn_list;c;c = c->next)
1132 if (c->next == conn)
1134 c->next = conn->next;
1138 // not found in list, we'll avoid crashing here...
1146 static int clientport = -1;
1147 static int clientport2 = -1;
1148 static int hostport = -1;
1150 // Call on disconnect, during startup, or if cl_netport is changed
1151 void NetConn_UpdateSockets_Client(void)
1153 if (cls.state == ca_disconnected && clientport != clientport2)
1155 clientport = clientport2;
1156 NetConn_CloseClientPorts();
1158 if (cl_numsockets == 0)
1159 NetConn_OpenClientPorts();
1162 // Call when cl_port is changed
1163 static void NetConn_cl_netport_Callback(cvar_t *var)
1165 if(cls.state != ca_dedicated)
1167 if (clientport2 != var->integer)
1169 clientport2 = var->integer;
1170 if (cls.state == ca_connected)
1171 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1173 NetConn_UpdateSockets_Client();
1177 // Call when port is changed
1178 static void NetConn_sv_netport_Callback(cvar_t *var)
1180 if (hostport != var->integer)
1182 hostport = var->integer;
1184 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1188 void NetConn_UpdateSockets(void)
1192 // TODO add logic to automatically close sockets if needed
1193 LHNET_DefaultDSCP(net_tos_dscp.integer);
1195 for (j = 0;j < MAX_RCONS;j++)
1197 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1198 if(cls.rcon_commands[i][0])
1200 if(host.realtime > cls.rcon_timeout[i])
1203 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1204 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1205 cls.rcon_commands[i][0] = 0;
1213 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1215 int originallength = (int)length;
1216 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1217 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1218 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1222 if (protocol == PROTOCOL_QUAKEWORLD)
1224 unsigned int sequence, sequence_ack;
1225 qbool reliable_ack, reliable_message;
1229 sequence = LittleLong(*((int *)(data + 0)));
1230 sequence_ack = LittleLong(*((int *)(data + 4)));
1234 if (conn != cls.netcon)
1239 // TODO: use qport to identify that this client really is who they say they are? (and elsewhere in the code to identify the connection without a port match?)
1240 //qport = LittleShort(*((int *)(data + 8)));
1245 conn->packetsReceived++;
1246 reliable_message = (sequence >> 31) != 0;
1247 reliable_ack = (sequence_ack >> 31) != 0;
1248 sequence &= ~(1<<31);
1249 sequence_ack &= ~(1<<31);
1250 if (sequence <= conn->qw.incoming_sequence)
1252 //Con_DPrint("Got a stale datagram\n");
1255 count = sequence - (conn->qw.incoming_sequence + 1);
1258 conn->droppedDatagrams += count;
1259 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1260 // If too may packets have been dropped, only write the
1261 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1262 // Because there's no point in writing more than
1263 // these as the netgraph is going to be full anyway.
1264 if (count > NETGRAPH_PACKETS)
1265 count = NETGRAPH_PACKETS;
1268 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1269 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1270 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1271 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1272 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1273 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1276 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1277 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1278 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1279 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1280 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1281 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1282 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1284 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1285 if (net_test.integer)
1287 if (conn->cleartime < host.realtime)
1288 conn->cleartime = host.realtime;
1291 if (reliable_ack == conn->qw.reliable_sequence)
1293 // received, now we will be able to send another reliable message
1294 conn->sendMessageLength = 0;
1295 conn->reliableMessagesReceived++;
1297 conn->qw.incoming_sequence = sequence;
1298 if (conn == cls.netcon)
1299 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1300 conn->qw.incoming_acknowledged = sequence_ack;
1301 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1302 if (reliable_message)
1303 conn->qw.incoming_reliable_sequence ^= 1;
1304 conn->lastMessageTime = host.realtime;
1305 conn->timeout = host.realtime + newtimeout;
1306 conn->unreliableMessagesReceived++;
1307 if (conn == cls.netcon)
1309 SZ_Clear(&cl_message);
1310 SZ_Write(&cl_message, data, (int)length);
1311 MSG_BeginReading(&cl_message);
1315 SZ_Clear(&sv_message);
1316 SZ_Write(&sv_message, data, (int)length);
1317 MSG_BeginReading(&sv_message);
1325 unsigned int sequence;
1330 originallength = (int)length;
1331 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1337 qlength = (unsigned int)BuffBigLong(data);
1338 flags = qlength & ~NETFLAG_LENGTH_MASK;
1339 qlength &= NETFLAG_LENGTH_MASK;
1340 // control packets were already handled
1341 if (!(flags & NETFLAG_CTL) && qlength == length)
1343 sequence = BuffBigLong(data + 4);
1344 conn->packetsReceived++;
1347 if (flags & NETFLAG_UNRELIABLE)
1349 if (sequence >= conn->nq.unreliableReceiveSequence)
1351 if (sequence > conn->nq.unreliableReceiveSequence)
1353 count = sequence - conn->nq.unreliableReceiveSequence;
1354 conn->droppedDatagrams += count;
1355 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1356 // If too may packets have been dropped, only write the
1357 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1358 // Because there's no point in writing more than
1359 // these as the netgraph is going to be full anyway.
1360 if (count > NETGRAPH_PACKETS)
1361 count = NETGRAPH_PACKETS;
1364 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1365 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1366 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1367 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1368 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1369 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1372 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1373 conn->incoming_netgraph[conn->incoming_packetcounter].time = host.realtime;
1374 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1375 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1376 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1377 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1378 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1380 conn->nq.unreliableReceiveSequence = sequence + 1;
1381 conn->lastMessageTime = host.realtime;
1382 conn->timeout = host.realtime + newtimeout;
1383 conn->unreliableMessagesReceived++;
1386 if (conn == cls.netcon)
1388 SZ_Clear(&cl_message);
1389 SZ_Write(&cl_message, data, (int)length);
1390 MSG_BeginReading(&cl_message);
1394 SZ_Clear(&sv_message);
1395 SZ_Write(&sv_message, data, (int)length);
1396 MSG_BeginReading(&sv_message);
1402 // Con_DPrint("Got a stale datagram\n");
1405 else if (flags & NETFLAG_ACK)
1407 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1408 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1410 if (sequence == (conn->nq.sendSequence - 1))
1412 if (sequence == conn->nq.ackSequence)
1414 conn->nq.ackSequence++;
1415 if (conn->nq.ackSequence != conn->nq.sendSequence)
1416 Con_DPrint("ack sequencing error\n");
1417 conn->lastMessageTime = host.realtime;
1418 conn->timeout = host.realtime + newtimeout;
1419 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1421 unsigned int packetLen;
1422 unsigned int dataLen;
1425 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1426 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1428 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1430 dataLen = conn->sendMessageLength;
1435 dataLen = MAX_PACKETFRAGMENT;
1439 packetLen = NET_HEADERSIZE + dataLen;
1441 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1442 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1443 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1445 conn->nq.sendSequence++;
1447 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1448 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1450 conn->lastSendTime = host.realtime;
1451 conn->packetsSent++;
1455 conn->sendMessageLength = 0;
1458 // Con_DPrint("Duplicate ACK received\n");
1461 // Con_DPrint("Stale ACK received\n");
1464 else if (flags & NETFLAG_DATA)
1466 unsigned char temppacket[8];
1467 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1468 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1470 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1472 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1473 StoreBigLong(temppacket + 4, sequence);
1474 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1476 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1477 if (sequence == conn->nq.receiveSequence)
1479 conn->lastMessageTime = host.realtime;
1480 conn->timeout = host.realtime + newtimeout;
1481 conn->nq.receiveSequence++;
1482 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1483 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1484 conn->receiveMessageLength += (int)length;
1486 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1487 "Dropping the message!\n", sequence );
1488 conn->receiveMessageLength = 0;
1491 if (flags & NETFLAG_EOM)
1493 conn->reliableMessagesReceived++;
1494 length = conn->receiveMessageLength;
1495 conn->receiveMessageLength = 0;
1498 if (conn == cls.netcon)
1500 SZ_Clear(&cl_message);
1501 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1502 MSG_BeginReading(&cl_message);
1506 SZ_Clear(&sv_message);
1507 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1508 MSG_BeginReading(&sv_message);
1515 conn->receivedDuplicateCount++;
1523 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1526 cls.connect_trying = false;
1528 M_Update_Return_Reason("");
1530 // Disconnect from the current server or stop demo playback
1531 if(cls.state == ca_connected || cls.demoplayback)
1532 CL_Disconnect(false, NULL);
1533 // allocate a net connection to keep track of things
1534 cls.netcon = NetConn_Open(mysocket, peeraddress);
1535 crypto = &cls.netcon->crypto;
1536 if(cls.crypto.authenticated)
1538 Crypto_FinishInstance(crypto, &cls.crypto);
1539 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1540 crypto->use_aes ? "Encrypted" : "Authenticated",
1541 cls.netcon->address,
1542 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1543 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1544 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1545 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1546 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1547 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1550 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1551 key_dest = key_game;
1555 cls.demonum = -1; // not in the demo loop now
1556 cls.state = ca_connected;
1557 cls.signon = 0; // need all the signon messages before playing
1558 cls.protocol = initialprotocol;
1559 // reset move sequence numbering on this new connection
1560 cls.servermovesequence = 0;
1561 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1562 CL_ForwardToServer("new");
1563 if (cls.protocol == PROTOCOL_QUAKE)
1565 // write a keepalive (clc_nop) as it seems to greatly improve the
1566 // chances of connecting to a netquake server
1568 unsigned char buf[4];
1569 memset(&msg, 0, sizeof(msg));
1571 msg.maxsize = sizeof(buf);
1572 MSG_WriteChar(&msg, clc_nop);
1573 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1577 int NetConn_IsLocalGame(void)
1579 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1585 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1589 serverlist_entry_t *entry = NULL;
1591 // search the cache for this server and update it
1592 for (n = 0;n < serverlist_cachecount;n++) {
1593 entry = &serverlist_cache[ n ];
1594 if (!strcmp(addressstring, entry->info.cname))
1598 if (n == serverlist_cachecount)
1600 // LAN search doesnt require an answer from the master server so we wont
1601 // know the ping nor will it be initialized already...
1604 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1607 if (serverlist_maxcachecount <= serverlist_cachecount)
1609 serverlist_maxcachecount += 64;
1610 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1612 entry = &serverlist_cache[n];
1614 memset(entry, 0, sizeof(*entry));
1615 // store the data the engine cares about (address and ping)
1616 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1617 entry->info.ping = 100000;
1618 entry->querytime = host.realtime;
1619 // if not in the slist menu we should print the server to console
1620 if (serverlist_consoleoutput)
1621 Con_Printf("querying %s\n", addressstring);
1622 ++serverlist_cachecount;
1624 // if this is the first reply from this server, count it as having replied
1625 pingtime = (int)((host.realtime - entry->querytime) * 1000.0 + 0.5);
1626 pingtime = bound(0, pingtime, 9999);
1627 if (entry->query == SQS_REFRESHING) {
1628 entry->info.ping = pingtime;
1629 entry->query = SQS_QUERIED;
1631 // convert to unsigned to catch the -1
1632 // I still dont like this but its better than the old 10000 magic ping number - as in easier to type and read :( [11/8/2007 Black]
1633 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1637 // other server info is updated by the caller
1641 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1643 serverlist_entry_t *entry = &serverlist_cache[n];
1644 serverlist_info_t *info = &entry->info;
1645 // update description strings for engine menu and console output
1646 dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
1647 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1649 info->gameversion != gameversion.integer
1652 gameversion_min.integer >= 0 // min/max range set by user/mod?
1653 && gameversion_max.integer >= 0
1654 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1655 && gameversion_max.integer >= info->gameversion
1658 info->mod, info->map);
1659 if (entry->query == SQS_QUERIED)
1661 if(!serverlist_paused)
1662 ServerList_ViewList_Remove(entry);
1664 // if not in the slist menu we should print the server to console (if wanted)
1665 else if( serverlist_consoleoutput )
1666 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1667 // and finally, update the view set
1668 if(!serverlist_paused)
1669 ServerList_ViewList_Insert( entry );
1670 // update the entry's state
1671 serverlist_cache[n].query = SQS_QUERIED;
1674 // returns true, if it's sensible to continue the processing
1675 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qbool isfavorite ) {
1677 serverlist_entry_t *entry;
1679 // ignore the rest of the message if the serverlist is full
1680 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1682 // also ignore it if we have already queried it (other master server response)
1683 for( n = 0 ; n < serverlist_cachecount ; n++ )
1684 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1687 if( n < serverlist_cachecount ) {
1688 // the entry has already been queried once or
1692 if (serverlist_maxcachecount <= n)
1694 serverlist_maxcachecount += 64;
1695 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1698 entry = &serverlist_cache[n];
1700 memset(entry, 0, sizeof(*entry));
1701 entry->protocol = protocol;
1702 // store the data the engine cares about (address and ping)
1703 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1705 entry->info.isfavorite = isfavorite;
1707 // no, then reset the ping right away
1708 entry->info.ping = -1;
1709 // we also want to increase the serverlist_cachecount then
1710 serverlist_cachecount++;
1713 entry->query = SQS_QUERYING;
1718 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qbool isextended)
1721 if (serverlist_consoleoutput)
1722 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1725 char ipstring [128];
1728 if (data[0] == '\\')
1730 unsigned short port = data[5] * 256 + data[6];
1732 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1733 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1735 // move on to next address in packet
1740 else if (data[0] == '/' && isextended && length >= 19)
1742 unsigned short port = data[17] * 256 + data[18];
1750 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1752 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1755 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1756 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1757 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1763 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1764 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1765 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1770 // move on to next address in packet
1776 Con_Print("Error while parsing the server list\n");
1780 if (serverlist_consoleoutput && developer_networking.integer)
1781 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1783 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1789 // begin or resume serverlist queries
1790 serverlist_querysleep = false;
1791 serverlist_querywaittime = host.realtime + 3;
1795 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1799 char *string, addressstring2[128];
1800 char stringbuf[16384];
1801 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1804 char infostringvalue[MAX_INPUTLINE];
1809 // quakeworld ingame packet
1810 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1812 // convert the address to a string incase we need it
1813 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1815 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1817 // received a command string - strip off the packaging and put it
1818 // into our string buffer with NULL termination
1821 length = min(length, (int)sizeof(stringbuf) - 1);
1822 memcpy(stringbuf, data, length);
1823 stringbuf[length] = 0;
1826 if (developer_networking.integer)
1828 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1829 Com_HexDumpToConsole(data, length);
1832 sendlength = sizeof(senddata) - 4;
1833 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1835 case CRYPTO_NOMATCH:
1841 memcpy(senddata, "\377\377\377\377", 4);
1842 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1845 case CRYPTO_DISCARD:
1848 memcpy(senddata, "\377\377\377\377", 4);
1849 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1853 case CRYPTO_REPLACE:
1854 string = senddata+4;
1855 length = (int)sendlength;
1859 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1862 for (j = 0;j < MAX_RCONS;j++)
1864 // note: this value from i is used outside the loop too...
1865 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1866 if(cls.rcon_commands[i][0])
1867 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1876 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1877 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1879 e = strchr(rcon_password.string, ' ');
1880 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1882 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1886 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1887 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1888 cls.rcon_commands[i][0] = 0;
1891 for (k = 0;k < MAX_RCONS;k++)
1892 if(cls.rcon_commands[k][0])
1893 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1898 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1899 // extend the timeout on other requests as we asked for a challenge
1900 for (l = 0;l < MAX_RCONS;l++)
1901 if(cls.rcon_commands[l][0])
1902 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1903 cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
1906 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1910 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1912 // darkplaces or quake3
1913 char protocolnames[1400];
1914 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1915 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1916 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1919 Protocol_Names(protocolnames, sizeof(protocolnames));
1921 M_Update_Return_Reason("Got challenge response");
1923 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1924 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1925 // TODO: add userinfo stuff here instead of using NQ commands?
1926 memcpy(senddata, "\377\377\377\377", 4);
1927 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1928 NetConn_WriteString(mysocket, senddata, peeraddress);
1931 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1933 // darkplaces or quake3
1934 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1935 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1939 M_Update_Return_Reason("Accepted");
1941 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1944 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1946 char rejectreason[128];
1947 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1948 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1951 cls.connect_trying = false;
1953 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1954 memcpy(rejectreason, string, length);
1955 rejectreason[length] = 0;
1957 M_Update_Return_Reason(rejectreason);
1962 if(key_dest != key_game)
1964 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1966 serverlist_info_t *info;
1971 // search the cache for this server and update it
1972 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1976 info = &serverlist_cache[n].info;
1981 info->qcstatus[0] = 0;
1982 info->players[0] = 0;
1983 info->protocol = -1;
1984 info->numplayers = 0;
1986 info->maxplayers = 0;
1987 info->gameversion = 0;
1989 p = strchr(string, '\n');
1992 *p = 0; // cut off the string there
1996 Con_Printf("statusResponse without players block?\n");
1998 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1999 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2000 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2001 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2002 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2003 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2004 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2005 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2006 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2007 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2008 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2009 info->numhumans = info->numplayers - max(0, info->numbots);
2010 info->freeslots = info->maxplayers - info->numplayers;
2012 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2016 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2018 serverlist_info_t *info;
2022 // search the cache for this server and update it
2023 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2027 info = &serverlist_cache[n].info;
2032 info->qcstatus[0] = 0;
2033 info->players[0] = 0;
2034 info->protocol = -1;
2035 info->numplayers = 0;
2037 info->maxplayers = 0;
2038 info->gameversion = 0;
2040 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2041 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2042 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2043 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2044 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2045 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2046 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2047 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2048 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2049 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2050 info->numhumans = info->numplayers - max(0, info->numbots);
2051 info->freeslots = info->maxplayers - info->numplayers;
2053 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2057 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2059 // Extract the IP addresses
2062 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2065 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2067 // Extract the IP addresses
2070 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2073 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2075 // Extract the IP addresses
2079 if (serverlist_consoleoutput)
2080 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2081 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2083 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2084 if (serverlist_consoleoutput && developer_networking.integer)
2085 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2087 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2091 // move on to next address in packet
2095 // begin or resume serverlist queries
2096 serverlist_querysleep = false;
2097 serverlist_querywaittime = host.realtime + 3;
2102 if (!strncmp(string, "extResponse ", 12))
2104 ++cl_net_extresponse_count;
2105 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2106 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2107 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2108 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2111 if (!strncmp(string, "ping", 4))
2113 if (developer_extra.integer)
2114 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2115 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2118 if (!strncmp(string, "ack", 3))
2120 // QuakeWorld compatibility
2121 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2123 // challenge message
2124 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2125 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2128 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2130 M_Update_Return_Reason("Got QuakeWorld challenge response");
2132 cls.qw_qport = qport.integer;
2133 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2134 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2135 memcpy(senddata, "\377\377\377\377", 4);
2136 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo);
2137 NetConn_WriteString(mysocket, senddata, peeraddress);
2140 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2143 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2144 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2148 M_Update_Return_Reason("QuakeWorld Accepted");
2150 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2153 if (length > 2 && !memcmp(string, "n\\", 2))
2156 serverlist_info_t *info;
2160 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2161 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2164 // search the cache for this server and update it
2165 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2169 info = &serverlist_cache[n].info;
2170 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2171 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2172 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2173 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2175 info->numplayers = 0; // updated below
2176 info->numhumans = 0; // updated below
2177 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2178 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2180 // count active players on server
2181 // (we could gather more info, but we're just after the number)
2182 s = strchr(string, '\n');
2186 while (s < string + length)
2188 for (;s < string + length && *s != '\n';s++)
2190 if (s >= string + length)
2198 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2202 if (string[0] == 'n')
2204 // qw print command, used by rcon replies too
2205 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2206 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2209 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2211 // we may not have liked the packet, but it was a command packet, so
2212 // we're done processing this packet now
2215 // quakeworld ingame packet
2216 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2219 CL_ParseServerMessage();
2222 // netquake control packets, supported for compatibility only
2223 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2227 serverlist_info_t *info;
2232 SZ_Clear(&cl_message);
2233 SZ_Write(&cl_message, data, length);
2234 MSG_BeginReading(&cl_message);
2235 c = MSG_ReadByte(&cl_message);
2239 if (developer_extra.integer)
2240 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2241 if (cls.connect_trying)
2243 lhnetaddress_t clientportaddress;
2244 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2245 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2248 clientportaddress = *peeraddress;
2249 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2250 // extra ProQuake stuff
2252 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2254 cls.proquake_servermod = 0;
2256 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2258 cls.proquake_serverversion = 0;
2260 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2262 cls.proquake_serverflags = 0;
2263 if (cls.proquake_servermod == 1)
2264 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2265 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2266 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2268 M_Update_Return_Reason("Accepted");
2270 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2274 if (developer_extra.integer) {
2275 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2278 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2280 cls.connect_trying = false;
2282 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2285 case CCREP_SERVER_INFO:
2286 if (developer_extra.integer)
2287 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2289 // LadyHavoc: because the quake server may report weird addresses
2290 // we just ignore it and keep the real address
2291 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2292 // search the cache for this server and update it
2293 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2297 info = &serverlist_cache[n].info;
2298 strlcpy(info->game, "Quake", sizeof(info->game));
2299 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2300 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2301 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2302 info->numplayers = MSG_ReadByte(&cl_message);
2303 info->maxplayers = MSG_ReadByte(&cl_message);
2304 info->protocol = MSG_ReadByte(&cl_message);
2306 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2309 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2310 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2311 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2314 if (developer_extra.integer)
2315 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2317 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2319 case CCREP_PLAYER_INFO:
2320 // we got a CCREP_PLAYER_INFO??
2321 //if (developer_extra.integer)
2322 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2324 case CCREP_RULE_INFO:
2325 // we got a CCREP_RULE_INFO??
2326 //if (developer_extra.integer)
2327 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2332 SZ_Clear(&cl_message);
2333 // we may not have liked the packet, but it was a valid control
2334 // packet, so we're done processing this packet now
2338 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2339 CL_ParseServerMessage();
2344 void NetConn_QueryQueueFrame(void)
2350 static double querycounter = 0;
2352 if(!net_slist_pause.integer && serverlist_paused)
2353 ServerList_RebuildViewList();
2354 serverlist_paused = net_slist_pause.integer != 0;
2356 if (serverlist_querysleep)
2359 // apply a cool down time after master server replies,
2360 // to avoid messing up the ping times on the servers
2361 if (serverlist_querywaittime > host.realtime)
2364 // each time querycounter reaches 1.0 issue a query
2365 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2366 maxqueries = (int)querycounter;
2367 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2368 querycounter -= maxqueries;
2370 if( maxqueries == 0 ) {
2374 // scan serverlist and issue queries as needed
2375 serverlist_querysleep = true;
2377 timeouttime = host.realtime - net_slist_timeout.value;
2378 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2380 serverlist_entry_t *entry = &serverlist_cache[ index ];
2381 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2386 serverlist_querysleep = false;
2387 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2392 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2394 lhnetaddress_t address;
2397 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2398 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2400 for (socket = 0; socket < cl_numsockets ; socket++)
2401 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2405 for (socket = 0; socket < cl_numsockets ; socket++)
2406 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2409 // update the entry fields
2410 entry->querytime = host.realtime;
2411 entry->querycounter++;
2413 // if not in the slist menu we should print the server to console
2414 if (serverlist_consoleoutput)
2415 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2421 // have we tried to refresh this server?
2422 if( entry->query == SQS_REFRESHING ) {
2423 // yes, so update the reply count (since its not responding anymore)
2425 if(!serverlist_paused)
2426 ServerList_ViewList_Remove(entry);
2428 entry->query = SQS_TIMEDOUT;
2434 void NetConn_ClientFrame(void)
2437 lhnetaddress_t peeraddress;
2438 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2439 NetConn_UpdateSockets();
2440 if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2443 if (cls.connect_remainingtries == 0)
2444 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2446 cls.connect_nextsendtime = host.realtime + 1;
2447 cls.connect_remainingtries--;
2448 if (cls.connect_remainingtries <= -10)
2450 cls.connect_trying = false;
2452 M_Update_Return_Reason("Connect: Failed");
2456 // try challenge first (newer DP server or QW)
2457 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2458 // then try netquake as a fallback (old server, or netquake)
2459 SZ_Clear(&cl_message);
2460 // save space for the header, filled in later
2461 MSG_WriteLong(&cl_message, 0);
2462 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2463 MSG_WriteString(&cl_message, "QUAKE");
2464 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2465 // extended proquake stuff
2466 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2467 // this version matches ProQuake 3.40, the first version to support
2468 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2469 // higher clients, so we pretend we are that version...
2470 MSG_WriteByte(&cl_message, 34); // version * 10
2471 MSG_WriteByte(&cl_message, 0); // flags
2472 MSG_WriteLong(&cl_message, 0); // password
2473 // write the packetsize now...
2474 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2475 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2476 SZ_Clear(&cl_message);
2478 for (i = 0;i < cl_numsockets;i++)
2480 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2482 // R_TimeReport("clientreadnetwork");
2483 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2484 // R_TimeReport("clientparsepacket");
2488 NetConn_QueryQueueFrame();
2490 if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2491 CL_Disconnect(true, "Connection timed out");
2494 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2498 for (i = 0;i < bufferlength - 1;i++)
2502 c = rand () % (127 - 33) + 33;
2503 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2509 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2510 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2512 prvm_prog_t *prog = SVVM_prog;
2514 unsigned int nb_clients = 0, nb_bots = 0, i;
2517 const char *crypto_idstring;
2518 const char *worldstatusstr;
2520 // How many clients are there?
2521 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2523 if (svs.clients[i].active)
2526 if (!svs.clients[i].netconnection)
2532 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2533 if(worldstatusstr && *worldstatusstr)
2538 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2539 if(*q != '\\' && *q != '\n')
2544 /// \TODO: we should add more information for the full status string
2545 crypto_idstring = Crypto_GetInfoResponseDataString();
2546 length = dpsnprintf(out_msg, out_size,
2547 "\377\377\377\377%s\x0A"
2548 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2549 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2554 fullstatus ? "statusResponse" : "infoResponse",
2555 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2556 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2557 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2558 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2559 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2560 fullstatus ? "\n" : "");
2562 // Make sure it fits in the buffer
2572 savelength = length;
2574 ptr = out_msg + length;
2575 left = (int)out_size - length;
2577 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2579 client_t *client = &svs.clients[i];
2582 int nameind, cleanind, pingvalue;
2584 char cleanname [sizeof(client->name)];
2585 const char *statusstr;
2588 // Remove all characters '"' and '\' in the player name
2593 curchar = client->name[nameind++];
2594 if (curchar != '"' && curchar != '\\')
2596 cleanname[cleanind++] = curchar;
2597 if (cleanind == sizeof(cleanname) - 1)
2600 } while (curchar != '\0');
2601 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2603 pingvalue = (int)(client->ping * 1000.0f);
2604 if(client->netconnection)
2605 pingvalue = bound(1, pingvalue, 9999);
2610 ed = PRVM_EDICT_NUM(i + 1);
2611 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2612 if(statusstr && *statusstr)
2617 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2618 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2623 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2625 if(client->frags == -666) // spectator
2626 strlcpy(teambuf, " 0", sizeof(teambuf));
2627 else if(client->colors == 0x44) // red team
2628 strlcpy(teambuf, " 1", sizeof(teambuf));
2629 else if(client->colors == 0xDD) // blue team
2630 strlcpy(teambuf, " 2", sizeof(teambuf));
2631 else if(client->colors == 0xCC) // yellow team
2632 strlcpy(teambuf, " 3", sizeof(teambuf));
2633 else if(client->colors == 0x99) // pink team
2634 strlcpy(teambuf, " 4", sizeof(teambuf));
2636 strlcpy(teambuf, " 0", sizeof(teambuf));
2641 // note: team number is inserted according to SoF2 protocol
2643 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2649 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2658 // turn it into an infoResponse!
2659 out_msg[savelength] = 0;
2660 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2661 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2676 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2678 size_t floodslotnum, bestfloodslotnum;
2679 double bestfloodtime;
2680 lhnetaddress_t noportpeeraddress;
2681 // see if this is a connect flood
2682 noportpeeraddress = *peeraddress;
2683 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2684 bestfloodslotnum = 0;
2685 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2686 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2688 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2690 bestfloodtime = floodlist[floodslotnum].lasttime;
2691 bestfloodslotnum = floodslotnum;
2693 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2695 // this address matches an ongoing flood address
2696 if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2700 // renew the ban on this address so it does not expire
2701 // until the flood has subsided
2702 floodlist[floodslotnum].lasttime = host.realtime;
2704 //Con_Printf("Flood detected!\n");
2707 // the flood appears to have subsided, so allow this
2708 bestfloodslotnum = floodslotnum; // reuse the same slot
2712 // begin a new timeout on this address
2713 floodlist[bestfloodslotnum].address = noportpeeraddress;
2714 floodlist[bestfloodslotnum].lasttime = host.realtime;
2715 //Con_Printf("Flood detection initiated!\n");
2719 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2721 size_t floodslotnum;
2722 lhnetaddress_t noportpeeraddress;
2723 // see if this is a connect flood
2724 noportpeeraddress = *peeraddress;
2725 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2726 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2728 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2730 // this address matches an ongoing flood address
2732 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2733 floodlist[floodslotnum].lasttime = 0;
2734 //Con_Printf("Flood cleared!\n");
2739 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2741 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2747 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2751 t1 = (long) time(NULL);
2752 t2 = strtol(s, NULL, 0);
2753 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2756 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2759 return !memcmp(mdfourbuf, hash, 16);
2762 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2768 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2772 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2775 // validate the challenge
2776 for (i = 0;i < MAX_CHALLENGES;i++)
2777 if(challenges[i].time > 0)
2778 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2780 // if the challenge is not recognized, drop the packet
2781 if (i == MAX_CHALLENGES)
2784 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2787 if(memcmp(mdfourbuf, hash, 16))
2790 // unmark challenge to prevent replay attacks
2791 challenges[i].time = 0;
2796 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2799 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2803 return !strcmp(password, hash);
2806 /// returns a string describing the user level, or NULL for auth failure
2807 static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
2809 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2810 static char buf[MAX_INPUTLINE];
2812 qbool restricted = false;
2813 qbool have_usernames = false;
2814 static char vabuf[1024];
2816 userpass_start = rcon_password.string;
2817 while((userpass_end = strchr(userpass_start, ' ')))
2819 have_usernames = true;
2820 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2821 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2822 if(comparator(peeraddress, buf, password, cs, cslen))
2824 userpass_start = userpass_end + 1;
2826 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2828 userpass_end = userpass_start + strlen(userpass_start);
2829 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2834 have_usernames = false;
2835 userpass_start = rcon_restricted_password.string;
2836 while((userpass_end = strchr(userpass_start, ' ')))
2838 have_usernames = true;
2839 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2840 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2841 if(comparator(peeraddress, buf, password, cs, cslen))
2843 userpass_start = userpass_end + 1;
2845 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2847 userpass_end = userpass_start + strlen(userpass_start);
2848 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2852 return NULL; // DENIED
2855 for(text = s; text != endpos; ++text)
2856 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2857 return NULL; // block possible exploits against the parser/alias expansion
2861 size_t l = strlen(s);
2864 hasquotes = (strchr(s, '"') != NULL);
2865 // sorry, we can't allow these substrings in wildcard expressions,
2866 // as they can mess with the argument counts
2867 text = rcon_restricted_commands.string;
2868 while(COM_ParseToken_Console(&text))
2870 // com_token now contains a pattern to check for...
2871 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2874 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2877 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2879 if(!strcmp(com_token, s))
2882 else // single-arg expression? must match the beginning of the command
2884 if(!strcmp(com_token, s))
2886 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2890 // if we got here, nothing matched!
2898 userpass_startpass = strchr(userpass_start, ':');
2899 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2900 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2902 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2905 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qbool proquakeprotocol)
2909 // looks like a legitimate rcon command with the correct password
2910 const char *s_ptr = s;
2911 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2912 while(s_ptr != endpos)
2914 size_t l = strlen(s_ptr);
2916 Con_Printf(" %s;", s_ptr);
2921 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2922 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2925 size_t l = strlen(s);
2928 client_t *host_client_save = host_client;
2929 Cmd_ExecuteString(cmd_local, s, src_local, true);
2930 host_client = host_client_save;
2931 // in case it is a command that changes host_client (like restart)
2935 Con_Rcon_Redirect_End();
2939 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2943 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2945 int i, ret, clientnum, best;
2947 char *string, response[2800], addressstring2[128];
2948 static char stringbuf[16384]; // server only
2949 qbool islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2950 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2951 size_t sendlength, response_len;
2952 char infostringvalue[MAX_INPUTLINE];
2957 // convert the address to a string incase we need it
2958 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2960 // see if we can identify the sender as a local player
2961 // (this is necessary for rcon to send a reliable reply if the client is
2962 // actually on the server, not sending remotely)
2963 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2964 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2966 if (i == svs.maxclients)
2969 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2971 // received a command string - strip off the packaging and put it
2972 // into our string buffer with NULL termination
2975 length = min(length, (int)sizeof(stringbuf) - 1);
2976 memcpy(stringbuf, data, length);
2977 stringbuf[length] = 0;
2980 if (developer_extra.integer)
2982 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2983 Com_HexDumpToConsole(data, length);
2986 sendlength = sizeof(senddata) - 4;
2987 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2989 case CRYPTO_NOMATCH:
2995 memcpy(senddata, "\377\377\377\377", 4);
2996 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2999 case CRYPTO_DISCARD:
3002 memcpy(senddata, "\377\377\377\377", 4);
3003 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3007 case CRYPTO_REPLACE:
3008 string = senddata+4;
3009 length = (int)sendlength;
3013 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3015 for (i = 0, best = 0, besttime = host.realtime;i < MAX_CHALLENGES;i++)
3017 if(challenges[i].time > 0)
3018 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3020 if (besttime > challenges[i].time)
3021 besttime = challenges[best = i].time;
3023 // if we did not find an exact match, choose the oldest and
3024 // update address and string
3025 if (i == MAX_CHALLENGES)
3028 challenges[i].address = *peeraddress;
3029 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3033 // flood control: drop if requesting challenge too often
3034 if(challenges[i].time > host.realtime - net_challengefloodblockingtimeout.value)
3037 challenges[i].time = host.realtime;
3038 // send the challenge
3039 memcpy(response, "\377\377\377\377", 4);
3040 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3041 response_len = strlen(response) + 1;
3042 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3043 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3046 if (length > 8 && !memcmp(string, "connect\\", 8))
3050 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3054 if(crypto && crypto->authenticated)
3056 // no need to check challenge
3057 if(crypto_developer.integer)
3059 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3060 crypto->use_aes ? "Encrypted" : "Authenticated",
3062 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3063 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3064 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3065 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3066 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3067 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3073 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3075 // validate the challenge
3076 for (i = 0;i < MAX_CHALLENGES;i++)
3077 if(challenges[i].time > 0)
3078 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3080 // if the challenge is not recognized, drop the packet
3081 if (i == MAX_CHALLENGES)
3086 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3087 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3089 if(!(islocal || sv_public.integer > -2))
3091 if (developer_extra.integer)
3092 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3093 memcpy(response, "\377\377\377\377", 4);
3094 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3095 NetConn_WriteString(mysocket, response, peeraddress);
3099 // check engine protocol
3100 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3102 if (developer_extra.integer)
3103 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3104 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3108 // see if this is a duplicate connection request or a disconnected
3109 // client who is rejoining to the same client slot
3110 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3112 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3114 // this is a known client...
3115 if(crypto && crypto->authenticated)
3117 // reject if changing key!
3118 if(client->netconnection->crypto.authenticated)
3121 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3123 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3125 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3127 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3130 if (developer_extra.integer)
3131 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3132 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3139 // reject if downgrading!
3140 if(client->netconnection->crypto.authenticated)
3142 if (developer_extra.integer)
3143 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3144 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3150 // client crashed and is coming back,
3151 // keep their stuff intact
3152 if (developer_extra.integer)
3153 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3154 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3155 if(crypto && crypto->authenticated)
3156 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3157 SV_SendServerinfo(client);
3161 // client is still trying to connect,
3162 // so we send a duplicate reply
3163 if (developer_extra.integer)
3164 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3165 if(crypto && crypto->authenticated)
3166 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3167 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3173 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3176 // find an empty client slot for this new client
3177 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3180 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3182 // allocated connection
3183 if (developer_extra.integer)
3184 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3185 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3186 // now set up the client
3187 if(crypto && crypto->authenticated)
3188 Crypto_FinishInstance(&conn->crypto, crypto);
3189 SV_ConnectClient(clientnum, conn);
3190 NetConn_Heartbeat(1);
3195 // no empty slots found - server is full
3196 if (developer_extra.integer)
3197 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3198 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3202 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3204 const char *challenge = NULL;
3206 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3209 // If there was a challenge in the getinfo message
3210 if (length > 8 && string[7] == ' ')
3211 challenge = string + 8;
3213 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3215 if (developer_extra.integer)
3216 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3217 NetConn_WriteString(mysocket, response, peeraddress);
3221 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3223 const char *challenge = NULL;
3225 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3228 // If there was a challenge in the getinfo message
3229 if (length > 10 && string[9] == ' ')
3230 challenge = string + 10;
3232 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3234 if (developer_extra.integer)
3235 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3236 NetConn_WriteString(mysocket, response, peeraddress);
3240 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3242 char *password = string + 20;
3243 char *timeval = string + 37;
3244 char *s = strchr(timeval, ' ');
3245 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3246 const char *userlevel;
3248 if(rcon_secure.integer > 1)
3252 return true; // invalid packet
3255 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3256 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3259 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3261 char *password = string + 25;
3262 char *challenge = string + 42;
3263 char *s = strchr(challenge, ' ');
3264 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3265 const char *userlevel;
3267 return true; // invalid packet
3270 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3271 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3274 if (length >= 5 && !memcmp(string, "rcon ", 5))
3277 char *s = string + 5;
3278 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3281 if(rcon_secure.integer > 0)
3284 for (j = 0;!ISWHITESPACE(*s);s++)
3285 if (j < (int)sizeof(password) - 1)
3287 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3290 if (!ISWHITESPACE(password[0]))
3292 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3293 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3297 if (!strncmp(string, "extResponse ", 12))
3299 ++sv_net_extresponse_count;
3300 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3301 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3302 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3303 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3306 if (!strncmp(string, "ping", 4))
3308 if (developer_extra.integer)
3309 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3310 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3313 if (!strncmp(string, "ack", 3))
3315 // we may not have liked the packet, but it was a command packet, so
3316 // we're done processing this packet now
3319 // netquake control packets, supported for compatibility only, and only
3320 // when running game protocols that are normally served via this connection
3322 // (this protects more modern protocols against being used for
3323 // Quake packet flood Denial Of Service attacks)
3324 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)
3328 const char *protocolname;
3329 client_t *knownclient;
3330 client_t *newclient;
3333 SZ_Clear(&sv_message);
3334 SZ_Write(&sv_message, data, length);
3335 MSG_BeginReading(&sv_message);
3336 c = MSG_ReadByte(&sv_message);
3340 if (developer_extra.integer)
3341 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3342 if(!(islocal || sv_public.integer > -2))
3344 if (developer_extra.integer)
3345 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3346 SZ_Clear(&sv_message);
3347 // save space for the header, filled in later
3348 MSG_WriteLong(&sv_message, 0);
3349 MSG_WriteByte(&sv_message, CCREP_REJECT);
3350 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3351 MSG_WriteString(&sv_message, "\n");
3352 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3353 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3354 SZ_Clear(&sv_message);
3358 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3359 protocolnumber = MSG_ReadByte(&sv_message);
3360 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3362 if (developer_extra.integer)
3363 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3364 SZ_Clear(&sv_message);
3365 // save space for the header, filled in later
3366 MSG_WriteLong(&sv_message, 0);
3367 MSG_WriteByte(&sv_message, CCREP_REJECT);
3368 MSG_WriteString(&sv_message, "Incompatible version.\n");
3369 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3370 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3371 SZ_Clear(&sv_message);
3375 // see if this connect request comes from a known client
3376 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3378 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3380 // this is either a duplicate connection request
3381 // or coming back from a timeout
3382 // (if so, keep their stuff intact)
3384 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3385 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3387 if (developer_extra.integer)
3388 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3389 SZ_Clear(&sv_message);
3390 // save space for the header, filled in later
3391 MSG_WriteLong(&sv_message, 0);
3392 MSG_WriteByte(&sv_message, CCREP_REJECT);
3393 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3394 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3395 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3396 SZ_Clear(&sv_message);
3401 if (developer_extra.integer)
3402 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3403 SZ_Clear(&sv_message);
3404 // save space for the header, filled in later
3405 MSG_WriteLong(&sv_message, 0);
3406 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3407 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3408 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3409 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3410 SZ_Clear(&sv_message);
3412 // if client is already spawned, re-send the
3413 // serverinfo message as they'll need it to play
3414 if (knownclient->begun)
3415 SV_SendServerinfo(knownclient);
3420 // this is a new client, check for connection flood
3421 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3424 // find a slot for the new client
3425 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3428 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3430 // connect to the client
3431 // everything is allocated, just fill in the details
3432 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3433 if (developer_extra.integer)
3434 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3435 // send back the info about the server connection
3436 SZ_Clear(&sv_message);
3437 // save space for the header, filled in later
3438 MSG_WriteLong(&sv_message, 0);
3439 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3440 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3441 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3442 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3443 SZ_Clear(&sv_message);
3444 // now set up the client struct
3445 SV_ConnectClient(clientnum, conn);
3446 NetConn_Heartbeat(1);
3451 if (developer_extra.integer)
3452 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3453 // no room; try to let player know
3454 SZ_Clear(&sv_message);
3455 // save space for the header, filled in later
3456 MSG_WriteLong(&sv_message, 0);
3457 MSG_WriteByte(&sv_message, CCREP_REJECT);
3458 MSG_WriteString(&sv_message, "Server is full.\n");
3459 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3460 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3461 SZ_Clear(&sv_message);
3463 case CCREQ_SERVER_INFO:
3464 if (developer_extra.integer)
3465 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3466 if(!(islocal || sv_public.integer > -1))
3469 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3472 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3475 char myaddressstring[128];
3476 if (developer_extra.integer)
3477 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3478 SZ_Clear(&sv_message);
3479 // save space for the header, filled in later
3480 MSG_WriteLong(&sv_message, 0);
3481 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3482 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3483 MSG_WriteString(&sv_message, myaddressstring);
3484 MSG_WriteString(&sv_message, hostname.string);
3485 MSG_WriteString(&sv_message, sv.name);
3486 // How many clients are there?
3487 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3488 if (svs.clients[i].active)
3490 MSG_WriteByte(&sv_message, numclients);
3491 MSG_WriteByte(&sv_message, svs.maxclients);
3492 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3493 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3494 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3495 SZ_Clear(&sv_message);
3498 case CCREQ_PLAYER_INFO:
3499 if (developer_extra.integer)
3500 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3501 if(!(islocal || sv_public.integer > -1))
3504 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3509 int playerNumber, activeNumber, clientNumber;
3512 playerNumber = MSG_ReadByte(&sv_message);
3514 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3515 if (client->active && ++activeNumber == playerNumber)
3517 if (clientNumber != svs.maxclients)
3519 SZ_Clear(&sv_message);
3520 // save space for the header, filled in later
3521 MSG_WriteLong(&sv_message, 0);
3522 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3523 MSG_WriteByte(&sv_message, playerNumber);
3524 MSG_WriteString(&sv_message, client->name);
3525 MSG_WriteLong(&sv_message, client->colors);
3526 MSG_WriteLong(&sv_message, client->frags);
3527 MSG_WriteLong(&sv_message, (int)(host.realtime - client->connecttime));
3528 if(sv_status_privacy.integer)
3529 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3531 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3532 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3533 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3534 SZ_Clear(&sv_message);
3538 case CCREQ_RULE_INFO:
3539 if (developer_extra.integer)
3540 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3541 if(!(islocal || sv_public.integer > -1))
3544 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3551 // find the search start location
3552 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3553 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CF_NOTIFY);
3555 // send the response
3556 SZ_Clear(&sv_message);
3557 // save space for the header, filled in later
3558 MSG_WriteLong(&sv_message, 0);
3559 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3562 MSG_WriteString(&sv_message, var->name);
3563 MSG_WriteString(&sv_message, var->string);
3565 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3566 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3567 SZ_Clear(&sv_message);
3571 if (developer_extra.integer)
3572 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3573 if (sv.active && !rcon_secure.integer)
3575 char password[2048];
3579 const char *userlevel;
3580 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3581 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3583 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3584 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3585 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3592 SZ_Clear(&sv_message);
3593 // we may not have liked the packet, but it was a valid control
3594 // packet, so we're done processing this packet now
3599 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3601 SV_ReadClientMessage();
3608 void NetConn_ServerFrame(void)
3611 lhnetaddress_t peeraddress;
3612 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3613 for (i = 0;i < sv_numsockets;i++)
3614 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3615 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3618 void NetConn_SleepMicroseconds(int microseconds)
3620 LHNET_SleepUntilPacket_Microseconds(microseconds);
3624 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
3628 lhnetaddress_t masteraddress;
3629 lhnetaddress_t broadcastaddress;
3632 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3635 // 26000 is the default quake server port, servers on other ports will not
3637 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3638 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3642 for (i = 0;i < cl_numsockets;i++)
3646 const char *cmdname, *extraoptions;
3647 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3649 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3651 // search LAN for Quake servers
3652 SZ_Clear(&cl_message);
3653 // save space for the header, filled in later
3654 MSG_WriteLong(&cl_message, 0);
3655 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3656 MSG_WriteString(&cl_message, "QUAKE");
3657 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3658 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3659 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3660 SZ_Clear(&cl_message);
3662 // search LAN for DarkPlaces servers
3663 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3666 // build the getservers message to send to the dpmaster master servers
3667 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3669 cmdname = "getserversExt";
3670 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3674 cmdname = "getservers";
3677 memcpy(request, "\377\377\377\377", 4);
3678 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3681 for (masternum = 0;sv_masters[masternum].name;masternum++)
3683 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3686 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3690 // search favorite servers
3691 for(j = 0; j < nFavorites; ++j)
3693 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3695 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3696 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3703 // only query QuakeWorld servers when the user wants to
3706 for (i = 0;i < cl_numsockets;i++)
3710 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3712 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3714 // search LAN for QuakeWorld servers
3715 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3717 // build the getservers message to send to the qwmaster master servers
3718 // note this has no -1 prefix, and the trailing nul byte is sent
3719 dpsnprintf(request, sizeof(request), "c\n");
3723 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3725 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3727 if (m_state != m_slist)
3729 char lookupstring[128];
3730 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3731 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3734 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3738 // search favorite servers
3739 for(j = 0; j < nFavorites; ++j)
3741 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3743 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3745 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3746 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3753 if (!masterquerycount)
3755 Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
3756 M_Update_Return_Reason("No network");
3761 void NetConn_Heartbeat(int priority)
3763 lhnetaddress_t masteraddress;
3765 lhnetsocket_t *mysocket;
3767 // if it's a state change (client connected), limit next heartbeat to no
3768 // more than 30 sec in the future
3769 if (priority == 1 && nextheartbeattime > host.realtime + 30.0)
3770 nextheartbeattime = host.realtime + 30.0;
3772 // limit heartbeatperiod to 30 to 270 second range,
3773 // lower limit is to avoid abusing master servers with excess traffic,
3774 // upper limit is to avoid timing out on the master server (which uses
3776 if (sv_heartbeatperiod.value < 30)
3777 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3778 if (sv_heartbeatperiod.value > 270)
3779 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3781 // make advertising optional and don't advertise singleplayer games, and
3782 // only send a heartbeat as often as the admin wants
3783 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
3785 nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
3786 for (masternum = 0;sv_masters[masternum].name;masternum++)
3787 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3788 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3792 static void Net_Heartbeat_f(cmd_state_t *cmd)
3795 NetConn_Heartbeat(2);
3797 Con_Print("No server running, can not heartbeat to master server.\n");
3800 static void PrintStats(netconn_t *conn)
3802 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3803 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3805 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3806 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3807 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3808 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3809 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3810 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3811 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3812 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3813 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3814 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3817 void Net_Stats_f(cmd_state_t *cmd)
3820 Con_Print("connections =\n");
3821 for (conn = netconn_list;conn;conn = conn->next)
3826 void Net_Refresh_f(cmd_state_t *cmd)
3828 if (m_state != m_slist) {
3829 Con_Print("Sending new requests to master servers\n");
3830 ServerList_QueryList(false, true, false, true);
3831 Con_Print("Listening for replies...\n");
3833 ServerList_QueryList(false, true, false, false);
3836 void Net_Slist_f(cmd_state_t *cmd)
3838 ServerList_ResetMasks();
3839 serverlist_sortbyfield = SLIF_PING;
3840 serverlist_sortflags = 0;
3841 if (m_state != m_slist) {
3842 Con_Print("Sending requests to master servers\n");
3843 ServerList_QueryList(true, true, false, true);
3844 Con_Print("Listening for replies...\n");
3846 ServerList_QueryList(true, true, false, false);
3849 void Net_SlistQW_f(cmd_state_t *cmd)
3851 ServerList_ResetMasks();
3852 serverlist_sortbyfield = SLIF_PING;
3853 serverlist_sortflags = 0;
3854 if (m_state != m_slist) {
3855 Con_Print("Sending requests to master servers\n");
3856 ServerList_QueryList(true, false, true, true);
3857 serverlist_consoleoutput = true;
3858 Con_Print("Listening for replies...\n");
3860 ServerList_QueryList(true, false, true, false);
3864 void NetConn_Init(void)
3867 lhnetaddress_t tempaddress;
3868 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3869 Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
3871 Cmd_AddCommand(CF_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
3872 Cmd_AddCommand(CF_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3873 Cmd_AddCommand(CF_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3875 Cmd_AddCommand(CF_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3876 Cvar_RegisterVariable(&net_test);
3877 Cvar_RegisterVariable(&net_usesizelimit);
3878 Cvar_RegisterVariable(&net_burstreserve);
3879 Cvar_RegisterVariable(&rcon_restricted_password);
3880 Cvar_RegisterVariable(&rcon_restricted_commands);
3881 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3882 Cvar_RegisterVariable(&net_slist_queriespersecond);
3883 Cvar_RegisterVariable(&net_slist_queriesperframe);
3884 Cvar_RegisterVariable(&net_slist_timeout);
3885 Cvar_RegisterVariable(&net_slist_maxtries);
3886 Cvar_RegisterVariable(&net_slist_favorites);
3888 Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
3890 Cvar_RegisterVariable(&net_slist_pause);
3891 #ifdef IP_TOS // register cvar only if supported
3892 Cvar_RegisterVariable(&net_tos_dscp);
3894 Cvar_RegisterVariable(&net_messagetimeout);
3895 Cvar_RegisterVariable(&net_connecttimeout);
3896 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3897 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3898 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3899 Cvar_RegisterVariable(&net_sourceaddresscheck);
3900 Cvar_RegisterVariable(&net_fakelag);
3901 Cvar_RegisterVariable(&net_fakeloss_send);
3902 Cvar_RegisterVariable(&net_fakeloss_receive);
3903 Cvar_RegisterVirtual(&net_fakelag, "cl_netlocalping");
3904 Cvar_RegisterVirtual(&net_fakeloss_send, "cl_netpacketloss_send");
3905 Cvar_RegisterVirtual(&net_fakeloss_receive, "cl_netpacketloss_receive");
3906 Cvar_RegisterVariable(&hostname);
3907 Cvar_RegisterVariable(&developer_networking);
3908 Cvar_RegisterVariable(&cl_netport);
3909 Cvar_RegisterCallback(&cl_netport, NetConn_cl_netport_Callback);
3910 Cvar_RegisterVariable(&sv_netport);
3911 Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
3912 Cvar_RegisterVariable(&net_address);
3913 Cvar_RegisterVariable(&net_address_ipv6);
3914 Cvar_RegisterVariable(&sv_public);
3915 Cvar_RegisterVariable(&sv_public_rejectreason);
3916 Cvar_RegisterVariable(&sv_heartbeatperiod);
3917 for (i = 0;sv_masters[i].name;i++)
3918 Cvar_RegisterVariable(&sv_masters[i]);
3919 Cvar_RegisterVariable(&gameversion);
3920 Cvar_RegisterVariable(&gameversion_min);
3921 Cvar_RegisterVariable(&gameversion_max);
3922 // 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.
3923 if ((i = Sys_CheckParm("-ip")) && i + 1 < sys.argc)
3925 if (LHNETADDRESS_FromString(&tempaddress, sys.argv[i + 1], 0) == 1)
3927 Con_Printf("-ip option used, setting net_address to \"%s\"\n", sys.argv[i + 1]);
3928 Cvar_SetQuick(&net_address, sys.argv[i + 1]);
3931 Con_Printf(CON_ERROR "-ip option used, but unable to parse the address \"%s\"\n", sys.argv[i + 1]);
3933 // 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
3934 if (((i = Sys_CheckParm("-port")) || (i = Sys_CheckParm("-ipport")) || (i = Sys_CheckParm("-udpport"))) && i + 1 < sys.argc)
3936 i = atoi(sys.argv[i + 1]);
3937 if (i >= 0 && i < 65536)
3939 Con_Printf("-port option used, setting port cvar to %i\n", i);
3940 Cvar_SetValueQuick(&sv_netport, i);
3943 Con_Printf(CON_ERROR "-port option used, but %i is not a valid port number\n", i);
3947 cl_message.data = cl_message_buf;
3948 cl_message.maxsize = sizeof(cl_message_buf);
3949 cl_message.cursize = 0;
3950 sv_message.data = sv_message_buf;
3951 sv_message.maxsize = sizeof(sv_message_buf);
3952 sv_message.cursize = 0;
3954 if (Thread_HasThreads())
3955 netconn_mutex = Thread_CreateMutex();
3958 void NetConn_Shutdown(void)
3960 NetConn_CloseClientPorts();
3961 NetConn_CloseServerPorts();
3964 Thread_DestroyMutex(netconn_mutex);
3965 netconn_mutex = NULL;