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 // if we're connecting to a remote server, shut down any local server
1531 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1533 SV_LockThreadMutex();
1535 SV_UnlockThreadMutex();
1537 if(cls.state == ca_connected || cls.demoplayback)
1539 // allocate a net connection to keep track of things
1540 cls.netcon = NetConn_Open(mysocket, peeraddress);
1541 crypto = &cls.netcon->crypto;
1542 if(cls.crypto.authenticated)
1544 Crypto_FinishInstance(crypto, &cls.crypto);
1545 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1546 crypto->use_aes ? "Encrypted" : "Authenticated",
1547 cls.netcon->address,
1548 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1549 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1550 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1551 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1552 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1553 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1556 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1557 key_dest = key_game;
1561 cls.demonum = -1; // not in the demo loop now
1562 cls.state = ca_connected;
1563 cls.signon = 0; // need all the signon messages before playing
1564 cls.protocol = initialprotocol;
1565 // reset move sequence numbering on this new connection
1566 cls.servermovesequence = 0;
1567 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1568 CL_ForwardToServer("new");
1569 if (cls.protocol == PROTOCOL_QUAKE)
1571 // write a keepalive (clc_nop) as it seems to greatly improve the
1572 // chances of connecting to a netquake server
1574 unsigned char buf[4];
1575 memset(&msg, 0, sizeof(msg));
1577 msg.maxsize = sizeof(buf);
1578 MSG_WriteChar(&msg, clc_nop);
1579 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1583 int NetConn_IsLocalGame(void)
1585 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1591 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1595 serverlist_entry_t *entry = NULL;
1597 // search the cache for this server and update it
1598 for (n = 0;n < serverlist_cachecount;n++) {
1599 entry = &serverlist_cache[ n ];
1600 if (!strcmp(addressstring, entry->info.cname))
1604 if (n == serverlist_cachecount)
1606 // LAN search doesnt require an answer from the master server so we wont
1607 // know the ping nor will it be initialized already...
1610 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1613 if (serverlist_maxcachecount <= serverlist_cachecount)
1615 serverlist_maxcachecount += 64;
1616 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1618 entry = &serverlist_cache[n];
1620 memset(entry, 0, sizeof(*entry));
1621 // store the data the engine cares about (address and ping)
1622 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1623 entry->info.ping = 100000;
1624 entry->querytime = host.realtime;
1625 // if not in the slist menu we should print the server to console
1626 if (serverlist_consoleoutput)
1627 Con_Printf("querying %s\n", addressstring);
1628 ++serverlist_cachecount;
1630 // if this is the first reply from this server, count it as having replied
1631 pingtime = (int)((host.realtime - entry->querytime) * 1000.0 + 0.5);
1632 pingtime = bound(0, pingtime, 9999);
1633 if (entry->query == SQS_REFRESHING) {
1634 entry->info.ping = pingtime;
1635 entry->query = SQS_QUERIED;
1637 // convert to unsigned to catch the -1
1638 // 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]
1639 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1643 // other server info is updated by the caller
1647 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1649 serverlist_entry_t *entry = &serverlist_cache[n];
1650 serverlist_info_t *info = &entry->info;
1651 // update description strings for engine menu and console output
1652 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);
1653 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1655 info->gameversion != gameversion.integer
1658 gameversion_min.integer >= 0 // min/max range set by user/mod?
1659 && gameversion_max.integer >= 0
1660 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1661 && gameversion_max.integer >= info->gameversion
1664 info->mod, info->map);
1665 if (entry->query == SQS_QUERIED)
1667 if(!serverlist_paused)
1668 ServerList_ViewList_Remove(entry);
1670 // if not in the slist menu we should print the server to console (if wanted)
1671 else if( serverlist_consoleoutput )
1672 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1673 // and finally, update the view set
1674 if(!serverlist_paused)
1675 ServerList_ViewList_Insert( entry );
1676 // update the entry's state
1677 serverlist_cache[n].query = SQS_QUERIED;
1680 // returns true, if it's sensible to continue the processing
1681 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qbool isfavorite ) {
1683 serverlist_entry_t *entry;
1685 // ignore the rest of the message if the serverlist is full
1686 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1688 // also ignore it if we have already queried it (other master server response)
1689 for( n = 0 ; n < serverlist_cachecount ; n++ )
1690 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1693 if( n < serverlist_cachecount ) {
1694 // the entry has already been queried once or
1698 if (serverlist_maxcachecount <= n)
1700 serverlist_maxcachecount += 64;
1701 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1704 entry = &serverlist_cache[n];
1706 memset(entry, 0, sizeof(*entry));
1707 entry->protocol = protocol;
1708 // store the data the engine cares about (address and ping)
1709 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1711 entry->info.isfavorite = isfavorite;
1713 // no, then reset the ping right away
1714 entry->info.ping = -1;
1715 // we also want to increase the serverlist_cachecount then
1716 serverlist_cachecount++;
1719 entry->query = SQS_QUERYING;
1724 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qbool isextended)
1727 if (serverlist_consoleoutput)
1728 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1731 char ipstring [128];
1734 if (data[0] == '\\')
1736 unsigned short port = data[5] * 256 + data[6];
1738 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1739 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1741 // move on to next address in packet
1746 else if (data[0] == '/' && isextended && length >= 19)
1748 unsigned short port = data[17] * 256 + data[18];
1756 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1758 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1761 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1762 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1763 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1769 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1770 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1771 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1776 // move on to next address in packet
1782 Con_Print("Error while parsing the server list\n");
1786 if (serverlist_consoleoutput && developer_networking.integer)
1787 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1789 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1795 // begin or resume serverlist queries
1796 serverlist_querysleep = false;
1797 serverlist_querywaittime = host.realtime + 3;
1801 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1805 char *string, addressstring2[128];
1806 char stringbuf[16384];
1807 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1810 char infostringvalue[MAX_INPUTLINE];
1815 // quakeworld ingame packet
1816 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1818 // convert the address to a string incase we need it
1819 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1821 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1823 // received a command string - strip off the packaging and put it
1824 // into our string buffer with NULL termination
1827 length = min(length, (int)sizeof(stringbuf) - 1);
1828 memcpy(stringbuf, data, length);
1829 stringbuf[length] = 0;
1832 if (developer_networking.integer)
1834 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1835 Com_HexDumpToConsole(data, length);
1838 sendlength = sizeof(senddata) - 4;
1839 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1841 case CRYPTO_NOMATCH:
1847 memcpy(senddata, "\377\377\377\377", 4);
1848 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1851 case CRYPTO_DISCARD:
1854 memcpy(senddata, "\377\377\377\377", 4);
1855 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1859 case CRYPTO_REPLACE:
1860 string = senddata+4;
1861 length = (int)sendlength;
1865 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1868 for (j = 0;j < MAX_RCONS;j++)
1870 // note: this value from i is used outside the loop too...
1871 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1872 if(cls.rcon_commands[i][0])
1873 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1882 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1883 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1885 e = strchr(rcon_password.string, ' ');
1886 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1888 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1892 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1893 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1894 cls.rcon_commands[i][0] = 0;
1897 for (k = 0;k < MAX_RCONS;k++)
1898 if(cls.rcon_commands[k][0])
1899 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1904 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1905 // extend the timeout on other requests as we asked for a challenge
1906 for (l = 0;l < MAX_RCONS;l++)
1907 if(cls.rcon_commands[l][0])
1908 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1909 cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
1912 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1916 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1918 // darkplaces or quake3
1919 char protocolnames[1400];
1920 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1921 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1922 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1925 Protocol_Names(protocolnames, sizeof(protocolnames));
1927 M_Update_Return_Reason("Got challenge response");
1929 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1930 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1931 // TODO: add userinfo stuff here instead of using NQ commands?
1932 memcpy(senddata, "\377\377\377\377", 4);
1933 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1934 NetConn_WriteString(mysocket, senddata, peeraddress);
1937 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1939 // darkplaces or quake3
1940 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1941 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1945 M_Update_Return_Reason("Accepted");
1947 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1950 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1952 char rejectreason[128];
1953 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1954 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1957 cls.connect_trying = false;
1959 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1960 memcpy(rejectreason, string, length);
1961 rejectreason[length] = 0;
1963 M_Update_Return_Reason(rejectreason);
1968 if(key_dest != key_game)
1970 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1972 serverlist_info_t *info;
1977 // search the cache for this server and update it
1978 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1982 info = &serverlist_cache[n].info;
1987 info->qcstatus[0] = 0;
1988 info->players[0] = 0;
1989 info->protocol = -1;
1990 info->numplayers = 0;
1992 info->maxplayers = 0;
1993 info->gameversion = 0;
1995 p = strchr(string, '\n');
1998 *p = 0; // cut off the string there
2002 Con_Printf("statusResponse without players block?\n");
2004 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2005 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2006 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2007 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2008 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2009 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2010 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2011 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2012 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2013 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2014 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2015 info->numhumans = info->numplayers - max(0, info->numbots);
2016 info->freeslots = info->maxplayers - info->numplayers;
2018 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2022 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2024 serverlist_info_t *info;
2028 // search the cache for this server and update it
2029 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2033 info = &serverlist_cache[n].info;
2038 info->qcstatus[0] = 0;
2039 info->players[0] = 0;
2040 info->protocol = -1;
2041 info->numplayers = 0;
2043 info->maxplayers = 0;
2044 info->gameversion = 0;
2046 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2047 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2048 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2049 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2050 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2051 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2052 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2053 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2054 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2055 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2056 info->numhumans = info->numplayers - max(0, info->numbots);
2057 info->freeslots = info->maxplayers - info->numplayers;
2059 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2063 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2065 // Extract the IP addresses
2068 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2071 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2073 // Extract the IP addresses
2076 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2079 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2081 // Extract the IP addresses
2085 if (serverlist_consoleoutput)
2086 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2087 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2089 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2090 if (serverlist_consoleoutput && developer_networking.integer)
2091 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2093 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2097 // move on to next address in packet
2101 // begin or resume serverlist queries
2102 serverlist_querysleep = false;
2103 serverlist_querywaittime = host.realtime + 3;
2108 if (!strncmp(string, "extResponse ", 12))
2110 ++cl_net_extresponse_count;
2111 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2112 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2113 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2114 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2117 if (!strncmp(string, "ping", 4))
2119 if (developer_extra.integer)
2120 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2121 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2124 if (!strncmp(string, "ack", 3))
2126 // QuakeWorld compatibility
2127 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2129 // challenge message
2130 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2131 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2134 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2136 M_Update_Return_Reason("Got QuakeWorld challenge response");
2138 cls.qw_qport = qport.integer;
2139 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2140 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2141 memcpy(senddata, "\377\377\377\377", 4);
2142 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);
2143 NetConn_WriteString(mysocket, senddata, peeraddress);
2146 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2149 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2150 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2154 M_Update_Return_Reason("QuakeWorld Accepted");
2156 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2159 if (length > 2 && !memcmp(string, "n\\", 2))
2162 serverlist_info_t *info;
2166 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2167 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2170 // search the cache for this server and update it
2171 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2175 info = &serverlist_cache[n].info;
2176 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2177 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2178 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2179 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2181 info->numplayers = 0; // updated below
2182 info->numhumans = 0; // updated below
2183 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2184 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2186 // count active players on server
2187 // (we could gather more info, but we're just after the number)
2188 s = strchr(string, '\n');
2192 while (s < string + length)
2194 for (;s < string + length && *s != '\n';s++)
2196 if (s >= string + length)
2204 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2208 if (string[0] == 'n')
2210 // qw print command, used by rcon replies too
2211 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2212 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2215 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2217 // we may not have liked the packet, but it was a command packet, so
2218 // we're done processing this packet now
2221 // quakeworld ingame packet
2222 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2225 CL_ParseServerMessage();
2228 // netquake control packets, supported for compatibility only
2229 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2233 serverlist_info_t *info;
2238 SZ_Clear(&cl_message);
2239 SZ_Write(&cl_message, data, length);
2240 MSG_BeginReading(&cl_message);
2241 c = MSG_ReadByte(&cl_message);
2245 if (developer_extra.integer)
2246 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2247 if (cls.connect_trying)
2249 lhnetaddress_t clientportaddress;
2250 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2251 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2254 clientportaddress = *peeraddress;
2255 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2256 // extra ProQuake stuff
2258 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2260 cls.proquake_servermod = 0;
2262 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2264 cls.proquake_serverversion = 0;
2266 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2268 cls.proquake_serverflags = 0;
2269 if (cls.proquake_servermod == 1)
2270 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2271 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2272 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2274 M_Update_Return_Reason("Accepted");
2276 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2280 if (developer_extra.integer) {
2281 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2284 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2286 cls.connect_trying = false;
2288 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2291 case CCREP_SERVER_INFO:
2292 if (developer_extra.integer)
2293 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2295 // LadyHavoc: because the quake server may report weird addresses
2296 // we just ignore it and keep the real address
2297 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2298 // search the cache for this server and update it
2299 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2303 info = &serverlist_cache[n].info;
2304 strlcpy(info->game, "Quake", sizeof(info->game));
2305 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2306 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2307 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2308 info->numplayers = MSG_ReadByte(&cl_message);
2309 info->maxplayers = MSG_ReadByte(&cl_message);
2310 info->protocol = MSG_ReadByte(&cl_message);
2312 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2315 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2316 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2317 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2320 if (developer_extra.integer)
2321 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2323 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2325 case CCREP_PLAYER_INFO:
2326 // we got a CCREP_PLAYER_INFO??
2327 //if (developer_extra.integer)
2328 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2330 case CCREP_RULE_INFO:
2331 // we got a CCREP_RULE_INFO??
2332 //if (developer_extra.integer)
2333 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2338 SZ_Clear(&cl_message);
2339 // we may not have liked the packet, but it was a valid control
2340 // packet, so we're done processing this packet now
2344 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2345 CL_ParseServerMessage();
2350 void NetConn_QueryQueueFrame(void)
2356 static double querycounter = 0;
2358 if(!net_slist_pause.integer && serverlist_paused)
2359 ServerList_RebuildViewList();
2360 serverlist_paused = net_slist_pause.integer != 0;
2362 if (serverlist_querysleep)
2365 // apply a cool down time after master server replies,
2366 // to avoid messing up the ping times on the servers
2367 if (serverlist_querywaittime > host.realtime)
2370 // each time querycounter reaches 1.0 issue a query
2371 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2372 maxqueries = (int)querycounter;
2373 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2374 querycounter -= maxqueries;
2376 if( maxqueries == 0 ) {
2380 // scan serverlist and issue queries as needed
2381 serverlist_querysleep = true;
2383 timeouttime = host.realtime - net_slist_timeout.value;
2384 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2386 serverlist_entry_t *entry = &serverlist_cache[ index ];
2387 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2392 serverlist_querysleep = false;
2393 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2398 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2400 lhnetaddress_t address;
2403 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2404 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2406 for (socket = 0; socket < cl_numsockets ; socket++)
2407 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2411 for (socket = 0; socket < cl_numsockets ; socket++)
2412 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2415 // update the entry fields
2416 entry->querytime = host.realtime;
2417 entry->querycounter++;
2419 // if not in the slist menu we should print the server to console
2420 if (serverlist_consoleoutput)
2421 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2427 // have we tried to refresh this server?
2428 if( entry->query == SQS_REFRESHING ) {
2429 // yes, so update the reply count (since its not responding anymore)
2431 if(!serverlist_paused)
2432 ServerList_ViewList_Remove(entry);
2434 entry->query = SQS_TIMEDOUT;
2440 void NetConn_ClientFrame(void)
2443 lhnetaddress_t peeraddress;
2444 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2445 NetConn_UpdateSockets();
2446 if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2449 if (cls.connect_remainingtries == 0)
2450 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2452 cls.connect_nextsendtime = host.realtime + 1;
2453 cls.connect_remainingtries--;
2454 if (cls.connect_remainingtries <= -10)
2456 cls.connect_trying = false;
2458 M_Update_Return_Reason("Connect: Failed");
2462 // try challenge first (newer DP server or QW)
2463 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2464 // then try netquake as a fallback (old server, or netquake)
2465 SZ_Clear(&cl_message);
2466 // save space for the header, filled in later
2467 MSG_WriteLong(&cl_message, 0);
2468 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2469 MSG_WriteString(&cl_message, "QUAKE");
2470 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2471 // extended proquake stuff
2472 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2473 // this version matches ProQuake 3.40, the first version to support
2474 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2475 // higher clients, so we pretend we are that version...
2476 MSG_WriteByte(&cl_message, 34); // version * 10
2477 MSG_WriteByte(&cl_message, 0); // flags
2478 MSG_WriteLong(&cl_message, 0); // password
2479 // write the packetsize now...
2480 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2481 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2482 SZ_Clear(&cl_message);
2484 for (i = 0;i < cl_numsockets;i++)
2486 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2488 // R_TimeReport("clientreadnetwork");
2489 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2490 // R_TimeReport("clientparsepacket");
2494 NetConn_QueryQueueFrame();
2496 if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2498 Con_Print("Connection timed out\n");
2500 SV_LockThreadMutex();
2502 SV_UnlockThreadMutex();
2506 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2510 for (i = 0;i < bufferlength - 1;i++)
2514 c = rand () % (127 - 33) + 33;
2515 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2521 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2522 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2524 prvm_prog_t *prog = SVVM_prog;
2526 unsigned int nb_clients = 0, nb_bots = 0, i;
2529 const char *crypto_idstring;
2530 const char *worldstatusstr;
2532 // How many clients are there?
2533 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2535 if (svs.clients[i].active)
2538 if (!svs.clients[i].netconnection)
2544 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2545 if(worldstatusstr && *worldstatusstr)
2550 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2551 if(*q != '\\' && *q != '\n')
2556 /// \TODO: we should add more information for the full status string
2557 crypto_idstring = Crypto_GetInfoResponseDataString();
2558 length = dpsnprintf(out_msg, out_size,
2559 "\377\377\377\377%s\x0A"
2560 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2561 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2566 fullstatus ? "statusResponse" : "infoResponse",
2567 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2568 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2569 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2570 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2571 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2572 fullstatus ? "\n" : "");
2574 // Make sure it fits in the buffer
2584 savelength = length;
2586 ptr = out_msg + length;
2587 left = (int)out_size - length;
2589 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2591 client_t *client = &svs.clients[i];
2594 int nameind, cleanind, pingvalue;
2596 char cleanname [sizeof(client->name)];
2597 const char *statusstr;
2600 // Remove all characters '"' and '\' in the player name
2605 curchar = client->name[nameind++];
2606 if (curchar != '"' && curchar != '\\')
2608 cleanname[cleanind++] = curchar;
2609 if (cleanind == sizeof(cleanname) - 1)
2612 } while (curchar != '\0');
2613 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2615 pingvalue = (int)(client->ping * 1000.0f);
2616 if(client->netconnection)
2617 pingvalue = bound(1, pingvalue, 9999);
2622 ed = PRVM_EDICT_NUM(i + 1);
2623 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2624 if(statusstr && *statusstr)
2629 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2630 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2635 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2637 if(client->frags == -666) // spectator
2638 strlcpy(teambuf, " 0", sizeof(teambuf));
2639 else if(client->colors == 0x44) // red team
2640 strlcpy(teambuf, " 1", sizeof(teambuf));
2641 else if(client->colors == 0xDD) // blue team
2642 strlcpy(teambuf, " 2", sizeof(teambuf));
2643 else if(client->colors == 0xCC) // yellow team
2644 strlcpy(teambuf, " 3", sizeof(teambuf));
2645 else if(client->colors == 0x99) // pink team
2646 strlcpy(teambuf, " 4", sizeof(teambuf));
2648 strlcpy(teambuf, " 0", sizeof(teambuf));
2653 // note: team number is inserted according to SoF2 protocol
2655 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2661 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2670 // turn it into an infoResponse!
2671 out_msg[savelength] = 0;
2672 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2673 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2688 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2690 size_t floodslotnum, bestfloodslotnum;
2691 double bestfloodtime;
2692 lhnetaddress_t noportpeeraddress;
2693 // see if this is a connect flood
2694 noportpeeraddress = *peeraddress;
2695 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2696 bestfloodslotnum = 0;
2697 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2698 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2700 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2702 bestfloodtime = floodlist[floodslotnum].lasttime;
2703 bestfloodslotnum = floodslotnum;
2705 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2707 // this address matches an ongoing flood address
2708 if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2712 // renew the ban on this address so it does not expire
2713 // until the flood has subsided
2714 floodlist[floodslotnum].lasttime = host.realtime;
2716 //Con_Printf("Flood detected!\n");
2719 // the flood appears to have subsided, so allow this
2720 bestfloodslotnum = floodslotnum; // reuse the same slot
2724 // begin a new timeout on this address
2725 floodlist[bestfloodslotnum].address = noportpeeraddress;
2726 floodlist[bestfloodslotnum].lasttime = host.realtime;
2727 //Con_Printf("Flood detection initiated!\n");
2731 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2733 size_t floodslotnum;
2734 lhnetaddress_t noportpeeraddress;
2735 // see if this is a connect flood
2736 noportpeeraddress = *peeraddress;
2737 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2738 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2740 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2742 // this address matches an ongoing flood address
2744 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2745 floodlist[floodslotnum].lasttime = 0;
2746 //Con_Printf("Flood cleared!\n");
2751 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2753 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2759 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2763 t1 = (long) time(NULL);
2764 t2 = strtol(s, NULL, 0);
2765 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2768 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2771 return !memcmp(mdfourbuf, hash, 16);
2774 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2780 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2784 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2787 // validate the challenge
2788 for (i = 0;i < MAX_CHALLENGES;i++)
2789 if(challenges[i].time > 0)
2790 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2792 // if the challenge is not recognized, drop the packet
2793 if (i == MAX_CHALLENGES)
2796 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2799 if(memcmp(mdfourbuf, hash, 16))
2802 // unmark challenge to prevent replay attacks
2803 challenges[i].time = 0;
2808 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2811 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2815 return !strcmp(password, hash);
2818 /// returns a string describing the user level, or NULL for auth failure
2819 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)
2821 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2822 static char buf[MAX_INPUTLINE];
2824 qbool restricted = false;
2825 qbool have_usernames = false;
2826 static char vabuf[1024];
2828 userpass_start = rcon_password.string;
2829 while((userpass_end = strchr(userpass_start, ' ')))
2831 have_usernames = true;
2832 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2833 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2834 if(comparator(peeraddress, buf, password, cs, cslen))
2836 userpass_start = userpass_end + 1;
2838 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2840 userpass_end = userpass_start + strlen(userpass_start);
2841 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2846 have_usernames = false;
2847 userpass_start = rcon_restricted_password.string;
2848 while((userpass_end = strchr(userpass_start, ' ')))
2850 have_usernames = true;
2851 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2852 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2853 if(comparator(peeraddress, buf, password, cs, cslen))
2855 userpass_start = userpass_end + 1;
2857 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2859 userpass_end = userpass_start + strlen(userpass_start);
2860 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2864 return NULL; // DENIED
2867 for(text = s; text != endpos; ++text)
2868 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2869 return NULL; // block possible exploits against the parser/alias expansion
2873 size_t l = strlen(s);
2876 hasquotes = (strchr(s, '"') != NULL);
2877 // sorry, we can't allow these substrings in wildcard expressions,
2878 // as they can mess with the argument counts
2879 text = rcon_restricted_commands.string;
2880 while(COM_ParseToken_Console(&text))
2882 // com_token now contains a pattern to check for...
2883 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2886 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2889 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2891 if(!strcmp(com_token, s))
2894 else // single-arg expression? must match the beginning of the command
2896 if(!strcmp(com_token, s))
2898 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2902 // if we got here, nothing matched!
2910 userpass_startpass = strchr(userpass_start, ':');
2911 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2912 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2914 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2917 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qbool proquakeprotocol)
2921 // looks like a legitimate rcon command with the correct password
2922 const char *s_ptr = s;
2923 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2924 while(s_ptr != endpos)
2926 size_t l = strlen(s_ptr);
2928 Con_Printf(" %s;", s_ptr);
2933 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2934 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2937 size_t l = strlen(s);
2940 client_t *host_client_save = host_client;
2941 Cmd_ExecuteString(cmd_local, s, src_local, true);
2942 host_client = host_client_save;
2943 // in case it is a command that changes host_client (like restart)
2947 Con_Rcon_Redirect_End();
2951 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2955 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2957 int i, ret, clientnum, best;
2959 char *string, response[2800], addressstring2[128];
2960 static char stringbuf[16384]; // server only
2961 qbool islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2962 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2963 size_t sendlength, response_len;
2964 char infostringvalue[MAX_INPUTLINE];
2969 // convert the address to a string incase we need it
2970 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2972 // see if we can identify the sender as a local player
2973 // (this is necessary for rcon to send a reliable reply if the client is
2974 // actually on the server, not sending remotely)
2975 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2976 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2978 if (i == svs.maxclients)
2981 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2983 // received a command string - strip off the packaging and put it
2984 // into our string buffer with NULL termination
2987 length = min(length, (int)sizeof(stringbuf) - 1);
2988 memcpy(stringbuf, data, length);
2989 stringbuf[length] = 0;
2992 if (developer_extra.integer)
2994 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2995 Com_HexDumpToConsole(data, length);
2998 sendlength = sizeof(senddata) - 4;
2999 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
3001 case CRYPTO_NOMATCH:
3007 memcpy(senddata, "\377\377\377\377", 4);
3008 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3011 case CRYPTO_DISCARD:
3014 memcpy(senddata, "\377\377\377\377", 4);
3015 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3019 case CRYPTO_REPLACE:
3020 string = senddata+4;
3021 length = (int)sendlength;
3025 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3027 for (i = 0, best = 0, besttime = host.realtime;i < MAX_CHALLENGES;i++)
3029 if(challenges[i].time > 0)
3030 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3032 if (besttime > challenges[i].time)
3033 besttime = challenges[best = i].time;
3035 // if we did not find an exact match, choose the oldest and
3036 // update address and string
3037 if (i == MAX_CHALLENGES)
3040 challenges[i].address = *peeraddress;
3041 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3045 // flood control: drop if requesting challenge too often
3046 if(challenges[i].time > host.realtime - net_challengefloodblockingtimeout.value)
3049 challenges[i].time = host.realtime;
3050 // send the challenge
3051 memcpy(response, "\377\377\377\377", 4);
3052 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3053 response_len = strlen(response) + 1;
3054 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3055 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3058 if (length > 8 && !memcmp(string, "connect\\", 8))
3062 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3066 if(crypto && crypto->authenticated)
3068 // no need to check challenge
3069 if(crypto_developer.integer)
3071 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3072 crypto->use_aes ? "Encrypted" : "Authenticated",
3074 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3075 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3076 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3077 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3078 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3079 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3085 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3087 // validate the challenge
3088 for (i = 0;i < MAX_CHALLENGES;i++)
3089 if(challenges[i].time > 0)
3090 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3092 // if the challenge is not recognized, drop the packet
3093 if (i == MAX_CHALLENGES)
3098 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3099 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3101 if(!(islocal || sv_public.integer > -2))
3103 if (developer_extra.integer)
3104 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3105 memcpy(response, "\377\377\377\377", 4);
3106 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3107 NetConn_WriteString(mysocket, response, peeraddress);
3111 // check engine protocol
3112 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3114 if (developer_extra.integer)
3115 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3116 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3120 // see if this is a duplicate connection request or a disconnected
3121 // client who is rejoining to the same client slot
3122 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3124 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3126 // this is a known client...
3127 if(crypto && crypto->authenticated)
3129 // reject if changing key!
3130 if(client->netconnection->crypto.authenticated)
3133 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3135 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3137 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3139 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3142 if (developer_extra.integer)
3143 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3144 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3151 // reject if downgrading!
3152 if(client->netconnection->crypto.authenticated)
3154 if (developer_extra.integer)
3155 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3156 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3162 // client crashed and is coming back,
3163 // keep their stuff intact
3164 if (developer_extra.integer)
3165 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3166 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3167 if(crypto && crypto->authenticated)
3168 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3169 SV_SendServerinfo(client);
3173 // client is still trying to connect,
3174 // so we send a duplicate reply
3175 if (developer_extra.integer)
3176 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3177 if(crypto && crypto->authenticated)
3178 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3179 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3185 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3188 // find an empty client slot for this new client
3189 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3192 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3194 // allocated connection
3195 if (developer_extra.integer)
3196 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3197 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3198 // now set up the client
3199 if(crypto && crypto->authenticated)
3200 Crypto_FinishInstance(&conn->crypto, crypto);
3201 SV_ConnectClient(clientnum, conn);
3202 NetConn_Heartbeat(1);
3207 // no empty slots found - server is full
3208 if (developer_extra.integer)
3209 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3210 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3214 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3216 const char *challenge = NULL;
3218 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3221 // If there was a challenge in the getinfo message
3222 if (length > 8 && string[7] == ' ')
3223 challenge = string + 8;
3225 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3227 if (developer_extra.integer)
3228 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3229 NetConn_WriteString(mysocket, response, peeraddress);
3233 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3235 const char *challenge = NULL;
3237 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3240 // If there was a challenge in the getinfo message
3241 if (length > 10 && string[9] == ' ')
3242 challenge = string + 10;
3244 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3246 if (developer_extra.integer)
3247 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3248 NetConn_WriteString(mysocket, response, peeraddress);
3252 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3254 char *password = string + 20;
3255 char *timeval = string + 37;
3256 char *s = strchr(timeval, ' ');
3257 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3258 const char *userlevel;
3260 if(rcon_secure.integer > 1)
3264 return true; // invalid packet
3267 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3268 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3271 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3273 char *password = string + 25;
3274 char *challenge = string + 42;
3275 char *s = strchr(challenge, ' ');
3276 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3277 const char *userlevel;
3279 return true; // invalid packet
3282 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3283 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3286 if (length >= 5 && !memcmp(string, "rcon ", 5))
3289 char *s = string + 5;
3290 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3293 if(rcon_secure.integer > 0)
3296 for (j = 0;!ISWHITESPACE(*s);s++)
3297 if (j < (int)sizeof(password) - 1)
3299 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3302 if (!ISWHITESPACE(password[0]))
3304 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3305 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3309 if (!strncmp(string, "extResponse ", 12))
3311 ++sv_net_extresponse_count;
3312 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3313 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3314 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3315 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3318 if (!strncmp(string, "ping", 4))
3320 if (developer_extra.integer)
3321 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3322 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3325 if (!strncmp(string, "ack", 3))
3327 // we may not have liked the packet, but it was a command packet, so
3328 // we're done processing this packet now
3331 // netquake control packets, supported for compatibility only, and only
3332 // when running game protocols that are normally served via this connection
3334 // (this protects more modern protocols against being used for
3335 // Quake packet flood Denial Of Service attacks)
3336 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)
3340 const char *protocolname;
3341 client_t *knownclient;
3342 client_t *newclient;
3345 SZ_Clear(&sv_message);
3346 SZ_Write(&sv_message, data, length);
3347 MSG_BeginReading(&sv_message);
3348 c = MSG_ReadByte(&sv_message);
3352 if (developer_extra.integer)
3353 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3354 if(!(islocal || sv_public.integer > -2))
3356 if (developer_extra.integer)
3357 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3358 SZ_Clear(&sv_message);
3359 // save space for the header, filled in later
3360 MSG_WriteLong(&sv_message, 0);
3361 MSG_WriteByte(&sv_message, CCREP_REJECT);
3362 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3363 MSG_WriteString(&sv_message, "\n");
3364 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3365 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3366 SZ_Clear(&sv_message);
3370 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3371 protocolnumber = MSG_ReadByte(&sv_message);
3372 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3374 if (developer_extra.integer)
3375 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3376 SZ_Clear(&sv_message);
3377 // save space for the header, filled in later
3378 MSG_WriteLong(&sv_message, 0);
3379 MSG_WriteByte(&sv_message, CCREP_REJECT);
3380 MSG_WriteString(&sv_message, "Incompatible version.\n");
3381 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3382 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3383 SZ_Clear(&sv_message);
3387 // see if this connect request comes from a known client
3388 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3390 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3392 // this is either a duplicate connection request
3393 // or coming back from a timeout
3394 // (if so, keep their stuff intact)
3396 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3397 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3399 if (developer_extra.integer)
3400 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3401 SZ_Clear(&sv_message);
3402 // save space for the header, filled in later
3403 MSG_WriteLong(&sv_message, 0);
3404 MSG_WriteByte(&sv_message, CCREP_REJECT);
3405 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3406 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3407 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3408 SZ_Clear(&sv_message);
3413 if (developer_extra.integer)
3414 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3415 SZ_Clear(&sv_message);
3416 // save space for the header, filled in later
3417 MSG_WriteLong(&sv_message, 0);
3418 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3419 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3420 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3421 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3422 SZ_Clear(&sv_message);
3424 // if client is already spawned, re-send the
3425 // serverinfo message as they'll need it to play
3426 if (knownclient->begun)
3427 SV_SendServerinfo(knownclient);
3432 // this is a new client, check for connection flood
3433 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3436 // find a slot for the new client
3437 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3440 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3442 // connect to the client
3443 // everything is allocated, just fill in the details
3444 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3445 if (developer_extra.integer)
3446 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3447 // send back the info about the server connection
3448 SZ_Clear(&sv_message);
3449 // save space for the header, filled in later
3450 MSG_WriteLong(&sv_message, 0);
3451 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3452 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3453 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3454 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3455 SZ_Clear(&sv_message);
3456 // now set up the client struct
3457 SV_ConnectClient(clientnum, conn);
3458 NetConn_Heartbeat(1);
3463 if (developer_extra.integer)
3464 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3465 // no room; try to let player know
3466 SZ_Clear(&sv_message);
3467 // save space for the header, filled in later
3468 MSG_WriteLong(&sv_message, 0);
3469 MSG_WriteByte(&sv_message, CCREP_REJECT);
3470 MSG_WriteString(&sv_message, "Server is full.\n");
3471 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3472 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3473 SZ_Clear(&sv_message);
3475 case CCREQ_SERVER_INFO:
3476 if (developer_extra.integer)
3477 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3478 if(!(islocal || sv_public.integer > -1))
3481 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3484 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3487 char myaddressstring[128];
3488 if (developer_extra.integer)
3489 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3490 SZ_Clear(&sv_message);
3491 // save space for the header, filled in later
3492 MSG_WriteLong(&sv_message, 0);
3493 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3494 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3495 MSG_WriteString(&sv_message, myaddressstring);
3496 MSG_WriteString(&sv_message, hostname.string);
3497 MSG_WriteString(&sv_message, sv.name);
3498 // How many clients are there?
3499 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3500 if (svs.clients[i].active)
3502 MSG_WriteByte(&sv_message, numclients);
3503 MSG_WriteByte(&sv_message, svs.maxclients);
3504 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3505 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3506 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3507 SZ_Clear(&sv_message);
3510 case CCREQ_PLAYER_INFO:
3511 if (developer_extra.integer)
3512 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3513 if(!(islocal || sv_public.integer > -1))
3516 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3521 int playerNumber, activeNumber, clientNumber;
3524 playerNumber = MSG_ReadByte(&sv_message);
3526 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3527 if (client->active && ++activeNumber == playerNumber)
3529 if (clientNumber != svs.maxclients)
3531 SZ_Clear(&sv_message);
3532 // save space for the header, filled in later
3533 MSG_WriteLong(&sv_message, 0);
3534 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3535 MSG_WriteByte(&sv_message, playerNumber);
3536 MSG_WriteString(&sv_message, client->name);
3537 MSG_WriteLong(&sv_message, client->colors);
3538 MSG_WriteLong(&sv_message, client->frags);
3539 MSG_WriteLong(&sv_message, (int)(host.realtime - client->connecttime));
3540 if(sv_status_privacy.integer)
3541 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3543 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3544 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3545 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3546 SZ_Clear(&sv_message);
3550 case CCREQ_RULE_INFO:
3551 if (developer_extra.integer)
3552 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3553 if(!(islocal || sv_public.integer > -1))
3556 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3563 // find the search start location
3564 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3565 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CF_NOTIFY);
3567 // send the response
3568 SZ_Clear(&sv_message);
3569 // save space for the header, filled in later
3570 MSG_WriteLong(&sv_message, 0);
3571 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3574 MSG_WriteString(&sv_message, var->name);
3575 MSG_WriteString(&sv_message, var->string);
3577 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3578 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3579 SZ_Clear(&sv_message);
3583 if (developer_extra.integer)
3584 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3585 if (sv.active && !rcon_secure.integer)
3587 char password[2048];
3591 const char *userlevel;
3592 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3593 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3595 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3596 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3597 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3604 SZ_Clear(&sv_message);
3605 // we may not have liked the packet, but it was a valid control
3606 // packet, so we're done processing this packet now
3611 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3613 SV_ReadClientMessage();
3620 void NetConn_ServerFrame(void)
3623 lhnetaddress_t peeraddress;
3624 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3625 for (i = 0;i < sv_numsockets;i++)
3626 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3627 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3630 void NetConn_SleepMicroseconds(int microseconds)
3632 LHNET_SleepUntilPacket_Microseconds(microseconds);
3636 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
3640 lhnetaddress_t masteraddress;
3641 lhnetaddress_t broadcastaddress;
3644 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3647 // 26000 is the default quake server port, servers on other ports will not
3649 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3650 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3654 for (i = 0;i < cl_numsockets;i++)
3658 const char *cmdname, *extraoptions;
3659 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3661 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3663 // search LAN for Quake servers
3664 SZ_Clear(&cl_message);
3665 // save space for the header, filled in later
3666 MSG_WriteLong(&cl_message, 0);
3667 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3668 MSG_WriteString(&cl_message, "QUAKE");
3669 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3670 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3671 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3672 SZ_Clear(&cl_message);
3674 // search LAN for DarkPlaces servers
3675 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3678 // build the getservers message to send to the dpmaster master servers
3679 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3681 cmdname = "getserversExt";
3682 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3686 cmdname = "getservers";
3689 memcpy(request, "\377\377\377\377", 4);
3690 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3693 for (masternum = 0;sv_masters[masternum].name;masternum++)
3695 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3698 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3702 // search favorite servers
3703 for(j = 0; j < nFavorites; ++j)
3705 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3707 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3708 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3715 // only query QuakeWorld servers when the user wants to
3718 for (i = 0;i < cl_numsockets;i++)
3722 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3724 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3726 // search LAN for QuakeWorld servers
3727 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3729 // build the getservers message to send to the qwmaster master servers
3730 // note this has no -1 prefix, and the trailing nul byte is sent
3731 dpsnprintf(request, sizeof(request), "c\n");
3735 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3737 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3739 if (m_state != m_slist)
3741 char lookupstring[128];
3742 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3743 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3746 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3750 // search favorite servers
3751 for(j = 0; j < nFavorites; ++j)
3753 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3755 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3757 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3758 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3765 if (!masterquerycount)
3767 Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
3768 M_Update_Return_Reason("No network");
3773 void NetConn_Heartbeat(int priority)
3775 lhnetaddress_t masteraddress;
3777 lhnetsocket_t *mysocket;
3779 // if it's a state change (client connected), limit next heartbeat to no
3780 // more than 30 sec in the future
3781 if (priority == 1 && nextheartbeattime > host.realtime + 30.0)
3782 nextheartbeattime = host.realtime + 30.0;
3784 // limit heartbeatperiod to 30 to 270 second range,
3785 // lower limit is to avoid abusing master servers with excess traffic,
3786 // upper limit is to avoid timing out on the master server (which uses
3788 if (sv_heartbeatperiod.value < 30)
3789 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3790 if (sv_heartbeatperiod.value > 270)
3791 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3793 // make advertising optional and don't advertise singleplayer games, and
3794 // only send a heartbeat as often as the admin wants
3795 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
3797 nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
3798 for (masternum = 0;sv_masters[masternum].name;masternum++)
3799 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3800 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3804 static void Net_Heartbeat_f(cmd_state_t *cmd)
3807 NetConn_Heartbeat(2);
3809 Con_Print("No server running, can not heartbeat to master server.\n");
3812 static void PrintStats(netconn_t *conn)
3814 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3815 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3817 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3818 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3819 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3820 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3821 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3822 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3823 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3824 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3825 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3826 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3829 void Net_Stats_f(cmd_state_t *cmd)
3832 Con_Print("connections =\n");
3833 for (conn = netconn_list;conn;conn = conn->next)
3838 void Net_Refresh_f(cmd_state_t *cmd)
3840 if (m_state != m_slist) {
3841 Con_Print("Sending new requests to master servers\n");
3842 ServerList_QueryList(false, true, false, true);
3843 Con_Print("Listening for replies...\n");
3845 ServerList_QueryList(false, true, false, false);
3848 void Net_Slist_f(cmd_state_t *cmd)
3850 ServerList_ResetMasks();
3851 serverlist_sortbyfield = SLIF_PING;
3852 serverlist_sortflags = 0;
3853 if (m_state != m_slist) {
3854 Con_Print("Sending requests to master servers\n");
3855 ServerList_QueryList(true, true, false, true);
3856 Con_Print("Listening for replies...\n");
3858 ServerList_QueryList(true, true, false, false);
3861 void Net_SlistQW_f(cmd_state_t *cmd)
3863 ServerList_ResetMasks();
3864 serverlist_sortbyfield = SLIF_PING;
3865 serverlist_sortflags = 0;
3866 if (m_state != m_slist) {
3867 Con_Print("Sending requests to master servers\n");
3868 ServerList_QueryList(true, false, true, true);
3869 serverlist_consoleoutput = true;
3870 Con_Print("Listening for replies...\n");
3872 ServerList_QueryList(true, false, true, false);
3876 void NetConn_Init(void)
3879 lhnetaddress_t tempaddress;
3880 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3881 Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
3883 Cmd_AddCommand(CF_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
3884 Cmd_AddCommand(CF_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3885 Cmd_AddCommand(CF_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3887 Cmd_AddCommand(CF_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3888 Cvar_RegisterVariable(&net_test);
3889 Cvar_RegisterVariable(&net_usesizelimit);
3890 Cvar_RegisterVariable(&net_burstreserve);
3891 Cvar_RegisterVariable(&rcon_restricted_password);
3892 Cvar_RegisterVariable(&rcon_restricted_commands);
3893 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3894 Cvar_RegisterVariable(&net_slist_queriespersecond);
3895 Cvar_RegisterVariable(&net_slist_queriesperframe);
3896 Cvar_RegisterVariable(&net_slist_timeout);
3897 Cvar_RegisterVariable(&net_slist_maxtries);
3898 Cvar_RegisterVariable(&net_slist_favorites);
3900 Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
3902 Cvar_RegisterVariable(&net_slist_pause);
3903 #ifdef IP_TOS // register cvar only if supported
3904 Cvar_RegisterVariable(&net_tos_dscp);
3906 Cvar_RegisterVariable(&net_messagetimeout);
3907 Cvar_RegisterVariable(&net_connecttimeout);
3908 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3909 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3910 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3911 Cvar_RegisterVariable(&net_sourceaddresscheck);
3912 Cvar_RegisterVariable(&net_fakelag);
3913 Cvar_RegisterVariable(&net_fakeloss_send);
3914 Cvar_RegisterVariable(&net_fakeloss_receive);
3915 Cvar_RegisterAlias(&net_fakelag, "cl_netlocalping");
3916 Cvar_RegisterAlias(&net_fakeloss_send, "cl_netpacketloss_send");
3917 Cvar_RegisterAlias(&net_fakeloss_receive, "cl_netpacketloss_receive");
3918 Cvar_RegisterVariable(&hostname);
3919 Cvar_RegisterVariable(&developer_networking);
3920 Cvar_RegisterVariable(&cl_netport);
3921 Cvar_RegisterCallback(&cl_netport, NetConn_cl_netport_Callback);
3922 Cvar_RegisterVariable(&sv_netport);
3923 Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
3924 Cvar_RegisterVariable(&net_address);
3925 Cvar_RegisterVariable(&net_address_ipv6);
3926 Cvar_RegisterVariable(&sv_public);
3927 Cvar_RegisterVariable(&sv_public_rejectreason);
3928 Cvar_RegisterVariable(&sv_heartbeatperiod);
3929 for (i = 0;sv_masters[i].name;i++)
3930 Cvar_RegisterVariable(&sv_masters[i]);
3931 Cvar_RegisterVariable(&gameversion);
3932 Cvar_RegisterVariable(&gameversion_min);
3933 Cvar_RegisterVariable(&gameversion_max);
3934 // 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.
3935 if ((i = Sys_CheckParm("-ip")) && i + 1 < sys.argc)
3937 if (LHNETADDRESS_FromString(&tempaddress, sys.argv[i + 1], 0) == 1)
3939 Con_Printf("-ip option used, setting net_address to \"%s\"\n", sys.argv[i + 1]);
3940 Cvar_SetQuick(&net_address, sys.argv[i + 1]);
3943 Con_Printf(CON_ERROR "-ip option used, but unable to parse the address \"%s\"\n", sys.argv[i + 1]);
3945 // 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
3946 if (((i = Sys_CheckParm("-port")) || (i = Sys_CheckParm("-ipport")) || (i = Sys_CheckParm("-udpport"))) && i + 1 < sys.argc)
3948 i = atoi(sys.argv[i + 1]);
3949 if (i >= 0 && i < 65536)
3951 Con_Printf("-port option used, setting port cvar to %i\n", i);
3952 Cvar_SetValueQuick(&sv_netport, i);
3955 Con_Printf(CON_ERROR "-port option used, but %i is not a valid port number\n", i);
3959 cl_message.data = cl_message_buf;
3960 cl_message.maxsize = sizeof(cl_message_buf);
3961 cl_message.cursize = 0;
3962 sv_message.data = sv_message_buf;
3963 sv_message.maxsize = sizeof(sv_message_buf);
3964 sv_message.cursize = 0;
3966 if (Thread_HasThreads())
3967 netconn_mutex = Thread_CreateMutex();
3970 void NetConn_Shutdown(void)
3972 NetConn_CloseClientPorts();
3973 NetConn_CloseServerPorts();
3976 Thread_DestroyMutex(netconn_mutex);
3977 netconn_mutex = NULL;