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 = {CVAR_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 = {CVAR_SERVER, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CVAR_SERVER | CVAR_SAVE, "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 {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
44 {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
45 {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
46 {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
47 {CVAR_CLIENT | CVAR_SERVER, "sv_masterextra1", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 1 (admin: Willis)"}, // admin: Willis
48 {CVAR_CLIENT | CVAR_SERVER, "sv_masterextra2", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 2 (admin: tChr)"}, // admin: tChr
53 static cvar_t sv_qwmasters [] =
55 {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
56 {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
57 {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
58 {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
59 {CVAR_CLIENT | CVAR_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
60 {CVAR_CLIENT | CVAR_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
61 {CVAR_CLIENT | CVAR_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
62 {CVAR_CLIENT | CVAR_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 = {CVAR_CLIENT | CVAR_SERVER, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
77 cvar_t net_usesizelimit = {CVAR_SERVER, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
78 cvar_t net_burstreserve = {CVAR_SERVER, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
79 cvar_t net_messagetimeout = {CVAR_CLIENT | CVAR_SERVER, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
80 cvar_t net_connecttimeout = {CVAR_CLIENT | CVAR_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 = {CVAR_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 = {CVAR_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 = {CVAR_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 = {CVAR_CLIENT, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
85 cvar_t hostname = {CVAR_SERVER | CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
86 cvar_t developer_networking = {CVAR_CLIENT | CVAR_SERVER, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
88 cvar_t net_fakelag = {CVAR_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 = {CVAR_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 = {CVAR_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 = {CVAR_CLIENT, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
92 static cvar_t net_slist_queriesperframe = {CVAR_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 = {CVAR_CLIENT, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
94 static cvar_t net_slist_pause = {CVAR_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 = {CVAR_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 = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
97 static cvar_t net_tos_dscp = {CVAR_CLIENT | CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
98 static cvar_t gameversion = {CVAR_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
99 static cvar_t gameversion_min = {CVAR_CLIENT | CVAR_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 = {CVAR_CLIENT | CVAR_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 = {CVAR_SERVER | CVAR_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 = {CVAR_SERVER, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
103 static cvar_t rcon_secure_maxdiff = {CVAR_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 qboolean serverlist_querysleep = true;
118 static qboolean 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 = {CVAR_CLIENT, "cl_port", "0", "forces client to use chosen port number if not 0"};
135 cvar_t sv_netport = {CVAR_SERVER, "port", "26000", "server port for players to connect to"};
136 cvar_t net_address = {CVAR_CLIENT | CVAR_SERVER, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
137 cvar_t net_address_ipv6 = {CVAR_CLIENT | CVAR_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 qboolean 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(void)
174 p = net_slist_favorites.string;
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 qboolean _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 qboolean _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 qboolean _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 qboolean _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(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
581 masterquerytime = 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 qboolean NetConn_CanSend(netconn_t *conn)
673 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
674 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = 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 (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 < realtime - bursttime)
694 *cleartime = 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 < realtime)
701 *cleartime = 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, qboolean 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)
743 qboolean sendreliable;
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 && (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 = 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 = 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 qboolean NetConn_HaveClientPorts(void)
936 return !!cl_numsockets;
939 qboolean 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_Errorf("Client failed to open a socket on address %s\n", addressstring2);
977 Con_Errorf("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 qboolean 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_Errorf("Server failed to open socket on address %s\n", addressstring2);
1042 Con_Errorf("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 Con_Printf("Server using port %i\n", port);
1064 if (sv_netport.integer != port)
1065 Cvar_SetValueQuick(&sv_netport, port);
1066 if (cls.state != ca_dedicated)
1067 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1070 #ifndef NOSUPPORTIPV6
1071 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1072 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1074 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1077 if (sv_numsockets == 0)
1078 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1081 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1083 int i, a = LHNETADDRESS_GetAddressType(address);
1084 for (i = 0;i < cl_numsockets;i++)
1085 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1086 return cl_sockets[i];
1090 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1092 int i, a = LHNETADDRESS_GetAddressType(address);
1093 for (i = 0;i < sv_numsockets;i++)
1094 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1095 return sv_sockets[i];
1099 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1102 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1103 conn->mysocket = mysocket;
1104 conn->peeraddress = *peeraddress;
1105 conn->lastMessageTime = realtime;
1106 conn->message.data = conn->messagedata;
1107 conn->message.maxsize = sizeof(conn->messagedata);
1108 conn->message.cursize = 0;
1109 // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
1110 // reduce effectiveness of connection request floods
1111 conn->timeout = realtime + net_connecttimeout.value;
1112 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1113 conn->next = netconn_list;
1114 netconn_list = conn;
1118 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1119 void NetConn_Close(netconn_t *conn)
1122 // remove connection from list
1124 // allow the client to reconnect immediately
1125 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1127 if (conn == netconn_list)
1128 netconn_list = conn->next;
1131 for (c = netconn_list;c;c = c->next)
1133 if (c->next == conn)
1135 c->next = conn->next;
1139 // not found in list, we'll avoid crashing here...
1147 static int clientport = -1;
1148 static int clientport2 = -1;
1149 static int hostport = -1;
1150 void NetConn_UpdateSockets(void)
1154 // TODO add logic to automatically close sockets if needed
1155 LHNET_DefaultDSCP(net_tos_dscp.integer);
1157 if (cls.state != ca_dedicated)
1159 if (clientport2 != cl_netport.integer)
1161 clientport2 = cl_netport.integer;
1162 if (cls.state == ca_connected)
1163 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1165 if (cls.state == ca_disconnected && clientport != clientport2)
1167 clientport = clientport2;
1168 NetConn_CloseClientPorts();
1170 if (cl_numsockets == 0)
1171 NetConn_OpenClientPorts();
1174 if (hostport != sv_netport.integer)
1176 hostport = sv_netport.integer;
1178 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1181 for (j = 0;j < MAX_RCONS;j++)
1183 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1184 if(cls.rcon_commands[i][0])
1186 if(realtime > cls.rcon_timeout[i])
1189 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1190 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1191 cls.rcon_commands[i][0] = 0;
1199 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1201 int originallength = (int)length;
1202 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1203 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1204 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1208 if (protocol == PROTOCOL_QUAKEWORLD)
1210 unsigned int sequence, sequence_ack;
1211 qboolean reliable_ack, reliable_message;
1215 sequence = LittleLong(*((int *)(data + 0)));
1216 sequence_ack = LittleLong(*((int *)(data + 4)));
1220 if (conn != cls.netcon)
1225 // 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?)
1226 //qport = LittleShort(*((int *)(data + 8)));
1231 conn->packetsReceived++;
1232 reliable_message = (sequence >> 31) != 0;
1233 reliable_ack = (sequence_ack >> 31) != 0;
1234 sequence &= ~(1<<31);
1235 sequence_ack &= ~(1<<31);
1236 if (sequence <= conn->qw.incoming_sequence)
1238 //Con_DPrint("Got a stale datagram\n");
1241 count = sequence - (conn->qw.incoming_sequence + 1);
1244 conn->droppedDatagrams += count;
1245 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1246 // If too may packets have been dropped, only write the
1247 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1248 // Because there's no point in writing more than
1249 // these as the netgraph is going to be full anyway.
1250 if (count > NETGRAPH_PACKETS)
1251 count = NETGRAPH_PACKETS;
1254 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1255 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1256 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1257 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1258 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1259 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1262 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1263 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1264 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1265 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1266 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1267 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1268 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1270 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1271 if (net_test.integer)
1273 if (conn->cleartime < realtime)
1274 conn->cleartime = realtime;
1277 if (reliable_ack == conn->qw.reliable_sequence)
1279 // received, now we will be able to send another reliable message
1280 conn->sendMessageLength = 0;
1281 conn->reliableMessagesReceived++;
1283 conn->qw.incoming_sequence = sequence;
1284 if (conn == cls.netcon)
1285 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1286 conn->qw.incoming_acknowledged = sequence_ack;
1287 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1288 if (reliable_message)
1289 conn->qw.incoming_reliable_sequence ^= 1;
1290 conn->lastMessageTime = realtime;
1291 conn->timeout = realtime + newtimeout;
1292 conn->unreliableMessagesReceived++;
1293 if (conn == cls.netcon)
1295 SZ_Clear(&cl_message);
1296 SZ_Write(&cl_message, data, (int)length);
1297 MSG_BeginReading(&cl_message);
1301 SZ_Clear(&sv_message);
1302 SZ_Write(&sv_message, data, (int)length);
1303 MSG_BeginReading(&sv_message);
1311 unsigned int sequence;
1316 originallength = (int)length;
1317 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1323 qlength = (unsigned int)BuffBigLong(data);
1324 flags = qlength & ~NETFLAG_LENGTH_MASK;
1325 qlength &= NETFLAG_LENGTH_MASK;
1326 // control packets were already handled
1327 if (!(flags & NETFLAG_CTL) && qlength == length)
1329 sequence = BuffBigLong(data + 4);
1330 conn->packetsReceived++;
1333 if (flags & NETFLAG_UNRELIABLE)
1335 if (sequence >= conn->nq.unreliableReceiveSequence)
1337 if (sequence > conn->nq.unreliableReceiveSequence)
1339 count = sequence - conn->nq.unreliableReceiveSequence;
1340 conn->droppedDatagrams += count;
1341 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1342 // If too may packets have been dropped, only write the
1343 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1344 // Because there's no point in writing more than
1345 // these as the netgraph is going to be full anyway.
1346 if (count > NETGRAPH_PACKETS)
1347 count = NETGRAPH_PACKETS;
1350 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1351 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1352 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1353 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1354 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1355 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1358 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1359 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1360 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1361 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1362 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1363 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1364 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1366 conn->nq.unreliableReceiveSequence = sequence + 1;
1367 conn->lastMessageTime = realtime;
1368 conn->timeout = realtime + newtimeout;
1369 conn->unreliableMessagesReceived++;
1372 if (conn == cls.netcon)
1374 SZ_Clear(&cl_message);
1375 SZ_Write(&cl_message, data, (int)length);
1376 MSG_BeginReading(&cl_message);
1380 SZ_Clear(&sv_message);
1381 SZ_Write(&sv_message, data, (int)length);
1382 MSG_BeginReading(&sv_message);
1388 // Con_DPrint("Got a stale datagram\n");
1391 else if (flags & NETFLAG_ACK)
1393 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1394 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1396 if (sequence == (conn->nq.sendSequence - 1))
1398 if (sequence == conn->nq.ackSequence)
1400 conn->nq.ackSequence++;
1401 if (conn->nq.ackSequence != conn->nq.sendSequence)
1402 Con_DPrint("ack sequencing error\n");
1403 conn->lastMessageTime = realtime;
1404 conn->timeout = realtime + newtimeout;
1405 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1407 unsigned int packetLen;
1408 unsigned int dataLen;
1411 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1412 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1414 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1416 dataLen = conn->sendMessageLength;
1421 dataLen = MAX_PACKETFRAGMENT;
1425 packetLen = NET_HEADERSIZE + dataLen;
1427 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1428 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1429 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1431 conn->nq.sendSequence++;
1433 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1434 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1436 conn->lastSendTime = realtime;
1437 conn->packetsSent++;
1441 conn->sendMessageLength = 0;
1444 // Con_DPrint("Duplicate ACK received\n");
1447 // Con_DPrint("Stale ACK received\n");
1450 else if (flags & NETFLAG_DATA)
1452 unsigned char temppacket[8];
1453 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1454 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1456 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1458 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1459 StoreBigLong(temppacket + 4, sequence);
1460 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1462 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1463 if (sequence == conn->nq.receiveSequence)
1465 conn->lastMessageTime = realtime;
1466 conn->timeout = realtime + newtimeout;
1467 conn->nq.receiveSequence++;
1468 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1469 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1470 conn->receiveMessageLength += (int)length;
1472 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1473 "Dropping the message!\n", sequence );
1474 conn->receiveMessageLength = 0;
1477 if (flags & NETFLAG_EOM)
1479 conn->reliableMessagesReceived++;
1480 length = conn->receiveMessageLength;
1481 conn->receiveMessageLength = 0;
1484 if (conn == cls.netcon)
1486 SZ_Clear(&cl_message);
1487 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1488 MSG_BeginReading(&cl_message);
1492 SZ_Clear(&sv_message);
1493 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1494 MSG_BeginReading(&sv_message);
1501 conn->receivedDuplicateCount++;
1509 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1512 cls.connect_trying = false;
1514 M_Update_Return_Reason("");
1516 // if we're connecting to a remote server, shut down any local server
1517 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1519 SV_LockThreadMutex();
1520 Host_ShutdownServer ();
1521 SV_UnlockThreadMutex();
1523 // allocate a net connection to keep track of things
1524 cls.netcon = NetConn_Open(mysocket, peeraddress);
1525 crypto = &cls.netcon->crypto;
1526 if(cls.crypto.authenticated)
1528 Crypto_FinishInstance(crypto, &cls.crypto);
1529 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1530 crypto->use_aes ? "Encrypted" : "Authenticated",
1531 cls.netcon->address,
1532 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1533 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1534 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1535 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1536 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1537 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1540 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1541 key_dest = key_game;
1545 cls.demonum = -1; // not in the demo loop now
1546 cls.state = ca_connected;
1547 cls.signon = 0; // need all the signon messages before playing
1548 cls.protocol = initialprotocol;
1549 // reset move sequence numbering on this new connection
1550 cls.servermovesequence = 0;
1551 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1552 Cmd_ForwardStringToServer("new");
1553 if (cls.protocol == PROTOCOL_QUAKE)
1555 // write a keepalive (clc_nop) as it seems to greatly improve the
1556 // chances of connecting to a netquake server
1558 unsigned char buf[4];
1559 memset(&msg, 0, sizeof(msg));
1561 msg.maxsize = sizeof(buf);
1562 MSG_WriteChar(&msg, clc_nop);
1563 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1567 int NetConn_IsLocalGame(void)
1569 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1575 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1579 serverlist_entry_t *entry = NULL;
1581 // search the cache for this server and update it
1582 for (n = 0;n < serverlist_cachecount;n++) {
1583 entry = &serverlist_cache[ n ];
1584 if (!strcmp(addressstring, entry->info.cname))
1588 if (n == serverlist_cachecount)
1590 // LAN search doesnt require an answer from the master server so we wont
1591 // know the ping nor will it be initialized already...
1594 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1597 if (serverlist_maxcachecount <= serverlist_cachecount)
1599 serverlist_maxcachecount += 64;
1600 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1602 entry = &serverlist_cache[n];
1604 memset(entry, 0, sizeof(*entry));
1605 // store the data the engine cares about (address and ping)
1606 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1607 entry->info.ping = 100000;
1608 entry->querytime = realtime;
1609 // if not in the slist menu we should print the server to console
1610 if (serverlist_consoleoutput)
1611 Con_Printf("querying %s\n", addressstring);
1612 ++serverlist_cachecount;
1614 // if this is the first reply from this server, count it as having replied
1615 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1616 pingtime = bound(0, pingtime, 9999);
1617 if (entry->query == SQS_REFRESHING) {
1618 entry->info.ping = pingtime;
1619 entry->query = SQS_QUERIED;
1621 // convert to unsigned to catch the -1
1622 // 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]
1623 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1627 // other server info is updated by the caller
1631 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1633 serverlist_entry_t *entry = &serverlist_cache[n];
1634 serverlist_info_t *info = &entry->info;
1635 // update description strings for engine menu and console output
1636 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);
1637 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1639 info->gameversion != gameversion.integer
1642 gameversion_min.integer >= 0 // min/max range set by user/mod?
1643 && gameversion_max.integer >= 0
1644 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1645 && gameversion_max.integer >= info->gameversion
1648 info->mod, info->map);
1649 if (entry->query == SQS_QUERIED)
1651 if(!serverlist_paused)
1652 ServerList_ViewList_Remove(entry);
1654 // if not in the slist menu we should print the server to console (if wanted)
1655 else if( serverlist_consoleoutput )
1656 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1657 // and finally, update the view set
1658 if(!serverlist_paused)
1659 ServerList_ViewList_Insert( entry );
1660 // update the entry's state
1661 serverlist_cache[n].query = SQS_QUERIED;
1664 // returns true, if it's sensible to continue the processing
1665 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1667 serverlist_entry_t *entry;
1669 // ignore the rest of the message if the serverlist is full
1670 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1672 // also ignore it if we have already queried it (other master server response)
1673 for( n = 0 ; n < serverlist_cachecount ; n++ )
1674 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1677 if( n < serverlist_cachecount ) {
1678 // the entry has already been queried once or
1682 if (serverlist_maxcachecount <= n)
1684 serverlist_maxcachecount += 64;
1685 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1688 entry = &serverlist_cache[n];
1690 memset(entry, 0, sizeof(*entry));
1691 entry->protocol = protocol;
1692 // store the data the engine cares about (address and ping)
1693 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1695 entry->info.isfavorite = isfavorite;
1697 // no, then reset the ping right away
1698 entry->info.ping = -1;
1699 // we also want to increase the serverlist_cachecount then
1700 serverlist_cachecount++;
1703 entry->query = SQS_QUERYING;
1708 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1711 if (serverlist_consoleoutput)
1712 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1715 char ipstring [128];
1718 if (data[0] == '\\')
1720 unsigned short port = data[5] * 256 + data[6];
1722 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1723 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1725 // move on to next address in packet
1730 else if (data[0] == '/' && isextended && length >= 19)
1732 unsigned short port = data[17] * 256 + data[18];
1740 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1742 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1745 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1746 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1747 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1753 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1754 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1755 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1760 // move on to next address in packet
1766 Con_Print("Error while parsing the server list\n");
1770 if (serverlist_consoleoutput && developer_networking.integer)
1771 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1773 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1779 // begin or resume serverlist queries
1780 serverlist_querysleep = false;
1781 serverlist_querywaittime = realtime + 3;
1785 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1787 qboolean fromserver;
1789 char *string, addressstring2[128];
1790 char stringbuf[16384];
1791 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1794 char infostringvalue[MAX_INPUTLINE];
1799 // quakeworld ingame packet
1800 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1802 // convert the address to a string incase we need it
1803 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1805 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1807 // received a command string - strip off the packaging and put it
1808 // into our string buffer with NULL termination
1811 length = min(length, (int)sizeof(stringbuf) - 1);
1812 memcpy(stringbuf, data, length);
1813 stringbuf[length] = 0;
1816 if (developer_networking.integer)
1818 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1819 Com_HexDumpToConsole(data, length);
1822 sendlength = sizeof(senddata) - 4;
1823 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1825 case CRYPTO_NOMATCH:
1831 memcpy(senddata, "\377\377\377\377", 4);
1832 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1835 case CRYPTO_DISCARD:
1838 memcpy(senddata, "\377\377\377\377", 4);
1839 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1843 case CRYPTO_REPLACE:
1844 string = senddata+4;
1845 length = (int)sendlength;
1849 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1852 for (j = 0;j < MAX_RCONS;j++)
1854 // note: this value from i is used outside the loop too...
1855 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1856 if(cls.rcon_commands[i][0])
1857 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1866 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1867 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1869 e = strchr(rcon_password.string, ' ');
1870 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1872 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1876 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1877 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1878 cls.rcon_commands[i][0] = 0;
1881 for (k = 0;k < MAX_RCONS;k++)
1882 if(cls.rcon_commands[k][0])
1883 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1888 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1889 // extend the timeout on other requests as we asked for a challenge
1890 for (l = 0;l < MAX_RCONS;l++)
1891 if(cls.rcon_commands[l][0])
1892 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1893 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1896 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1900 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1902 // darkplaces or quake3
1903 char protocolnames[1400];
1904 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1905 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1906 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1909 Protocol_Names(protocolnames, sizeof(protocolnames));
1911 M_Update_Return_Reason("Got challenge response");
1913 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1914 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1915 // TODO: add userinfo stuff here instead of using NQ commands?
1916 memcpy(senddata, "\377\377\377\377", 4);
1917 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1918 NetConn_WriteString(mysocket, senddata, peeraddress);
1921 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1923 // darkplaces or quake3
1924 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1925 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1929 M_Update_Return_Reason("Accepted");
1931 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1934 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1936 char rejectreason[128];
1937 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1938 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1941 cls.connect_trying = false;
1943 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1944 memcpy(rejectreason, string, length);
1945 rejectreason[length] = 0;
1947 M_Update_Return_Reason(rejectreason);
1952 if(key_dest != key_game)
1954 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1956 serverlist_info_t *info;
1961 // search the cache for this server and update it
1962 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1966 info = &serverlist_cache[n].info;
1971 info->qcstatus[0] = 0;
1972 info->players[0] = 0;
1973 info->protocol = -1;
1974 info->numplayers = 0;
1976 info->maxplayers = 0;
1977 info->gameversion = 0;
1979 p = strchr(string, '\n');
1982 *p = 0; // cut off the string there
1986 Con_Printf("statusResponse without players block?\n");
1988 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1989 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1990 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1991 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1992 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1993 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1994 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1995 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1996 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1997 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1998 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1999 info->numhumans = info->numplayers - max(0, info->numbots);
2000 info->freeslots = info->maxplayers - info->numplayers;
2002 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2006 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2008 serverlist_info_t *info;
2012 // search the cache for this server and update it
2013 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2017 info = &serverlist_cache[n].info;
2022 info->qcstatus[0] = 0;
2023 info->players[0] = 0;
2024 info->protocol = -1;
2025 info->numplayers = 0;
2027 info->maxplayers = 0;
2028 info->gameversion = 0;
2030 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2031 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2032 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2033 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2034 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2035 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2036 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2037 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2038 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2039 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2040 info->numhumans = info->numplayers - max(0, info->numbots);
2041 info->freeslots = info->maxplayers - info->numplayers;
2043 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2047 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2049 // Extract the IP addresses
2052 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2055 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2057 // Extract the IP addresses
2060 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2063 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2065 // Extract the IP addresses
2069 if (serverlist_consoleoutput)
2070 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2071 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2073 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2074 if (serverlist_consoleoutput && developer_networking.integer)
2075 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2077 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2081 // move on to next address in packet
2085 // begin or resume serverlist queries
2086 serverlist_querysleep = false;
2087 serverlist_querywaittime = realtime + 3;
2092 if (!strncmp(string, "extResponse ", 12))
2094 ++cl_net_extresponse_count;
2095 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2096 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2097 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2098 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2101 if (!strncmp(string, "ping", 4))
2103 if (developer_extra.integer)
2104 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2105 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2108 if (!strncmp(string, "ack", 3))
2110 // QuakeWorld compatibility
2111 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2113 // challenge message
2114 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2115 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2118 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2120 M_Update_Return_Reason("Got QuakeWorld challenge response");
2122 cls.qw_qport = qport.integer;
2123 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2124 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2125 memcpy(senddata, "\377\377\377\377", 4);
2126 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);
2127 NetConn_WriteString(mysocket, senddata, peeraddress);
2130 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2133 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2134 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2138 M_Update_Return_Reason("QuakeWorld Accepted");
2140 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2143 if (length > 2 && !memcmp(string, "n\\", 2))
2146 serverlist_info_t *info;
2150 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2151 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2154 // search the cache for this server and update it
2155 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2159 info = &serverlist_cache[n].info;
2160 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2161 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2162 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2163 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2165 info->numplayers = 0; // updated below
2166 info->numhumans = 0; // updated below
2167 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2168 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2170 // count active players on server
2171 // (we could gather more info, but we're just after the number)
2172 s = strchr(string, '\n');
2176 while (s < string + length)
2178 for (;s < string + length && *s != '\n';s++)
2180 if (s >= string + length)
2188 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2192 if (string[0] == 'n')
2194 // qw print command, used by rcon replies too
2195 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2196 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2199 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2201 // we may not have liked the packet, but it was a command packet, so
2202 // we're done processing this packet now
2205 // quakeworld ingame packet
2206 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2209 CL_ParseServerMessage();
2212 // netquake control packets, supported for compatibility only
2213 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2217 serverlist_info_t *info;
2222 SZ_Clear(&cl_message);
2223 SZ_Write(&cl_message, data, length);
2224 MSG_BeginReading(&cl_message);
2225 c = MSG_ReadByte(&cl_message);
2229 if (developer_extra.integer)
2230 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2231 if (cls.connect_trying)
2233 lhnetaddress_t clientportaddress;
2234 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2235 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2238 clientportaddress = *peeraddress;
2239 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2240 // extra ProQuake stuff
2242 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2244 cls.proquake_servermod = 0;
2246 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2248 cls.proquake_serverversion = 0;
2250 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2252 cls.proquake_serverflags = 0;
2253 if (cls.proquake_servermod == 1)
2254 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2255 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2256 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2258 M_Update_Return_Reason("Accepted");
2260 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2264 if (developer_extra.integer) {
2265 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2268 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2270 cls.connect_trying = false;
2272 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2275 case CCREP_SERVER_INFO:
2276 if (developer_extra.integer)
2277 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2279 // LadyHavoc: because the quake server may report weird addresses
2280 // we just ignore it and keep the real address
2281 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2282 // search the cache for this server and update it
2283 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2287 info = &serverlist_cache[n].info;
2288 strlcpy(info->game, "Quake", sizeof(info->game));
2289 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2290 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2291 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2292 info->numplayers = MSG_ReadByte(&cl_message);
2293 info->maxplayers = MSG_ReadByte(&cl_message);
2294 info->protocol = MSG_ReadByte(&cl_message);
2296 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2299 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2300 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2301 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2304 if (developer_extra.integer)
2305 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2307 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2309 case CCREP_PLAYER_INFO:
2310 // we got a CCREP_PLAYER_INFO??
2311 //if (developer_extra.integer)
2312 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2314 case CCREP_RULE_INFO:
2315 // we got a CCREP_RULE_INFO??
2316 //if (developer_extra.integer)
2317 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2322 SZ_Clear(&cl_message);
2323 // we may not have liked the packet, but it was a valid control
2324 // packet, so we're done processing this packet now
2328 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2329 CL_ParseServerMessage();
2334 void NetConn_QueryQueueFrame(void)
2340 static double querycounter = 0;
2342 if(!net_slist_pause.integer && serverlist_paused)
2343 ServerList_RebuildViewList();
2344 serverlist_paused = net_slist_pause.integer != 0;
2346 if (serverlist_querysleep)
2349 // apply a cool down time after master server replies,
2350 // to avoid messing up the ping times on the servers
2351 if (serverlist_querywaittime > realtime)
2354 // each time querycounter reaches 1.0 issue a query
2355 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2356 maxqueries = (int)querycounter;
2357 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2358 querycounter -= maxqueries;
2360 if( maxqueries == 0 ) {
2364 // scan serverlist and issue queries as needed
2365 serverlist_querysleep = true;
2367 timeouttime = realtime - net_slist_timeout.value;
2368 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2370 serverlist_entry_t *entry = &serverlist_cache[ index ];
2371 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2376 serverlist_querysleep = false;
2377 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2382 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2384 lhnetaddress_t address;
2387 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2388 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2390 for (socket = 0; socket < cl_numsockets ; socket++)
2391 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2395 for (socket = 0; socket < cl_numsockets ; socket++)
2396 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2399 // update the entry fields
2400 entry->querytime = realtime;
2401 entry->querycounter++;
2403 // if not in the slist menu we should print the server to console
2404 if (serverlist_consoleoutput)
2405 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2411 // have we tried to refresh this server?
2412 if( entry->query == SQS_REFRESHING ) {
2413 // yes, so update the reply count (since its not responding anymore)
2415 if(!serverlist_paused)
2416 ServerList_ViewList_Remove(entry);
2418 entry->query = SQS_TIMEDOUT;
2424 void NetConn_ClientFrame(void)
2427 lhnetaddress_t peeraddress;
2428 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2429 NetConn_UpdateSockets();
2430 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2433 if (cls.connect_remainingtries == 0)
2434 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2436 cls.connect_nextsendtime = realtime + 1;
2437 cls.connect_remainingtries--;
2438 if (cls.connect_remainingtries <= -10)
2440 cls.connect_trying = false;
2442 M_Update_Return_Reason("Connect: Failed");
2446 // try challenge first (newer DP server or QW)
2447 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2448 // then try netquake as a fallback (old server, or netquake)
2449 SZ_Clear(&cl_message);
2450 // save space for the header, filled in later
2451 MSG_WriteLong(&cl_message, 0);
2452 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2453 MSG_WriteString(&cl_message, "QUAKE");
2454 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2455 // extended proquake stuff
2456 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2457 // this version matches ProQuake 3.40, the first version to support
2458 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2459 // higher clients, so we pretend we are that version...
2460 MSG_WriteByte(&cl_message, 34); // version * 10
2461 MSG_WriteByte(&cl_message, 0); // flags
2462 MSG_WriteLong(&cl_message, 0); // password
2463 // write the packetsize now...
2464 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2465 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2466 SZ_Clear(&cl_message);
2468 for (i = 0;i < cl_numsockets;i++)
2470 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2472 // R_TimeReport("clientreadnetwork");
2473 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2474 // R_TimeReport("clientparsepacket");
2478 NetConn_QueryQueueFrame();
2480 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2482 Con_Print("Connection timed out\n");
2484 SV_LockThreadMutex();
2485 Host_ShutdownServer ();
2486 SV_UnlockThreadMutex();
2490 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2494 for (i = 0;i < bufferlength - 1;i++)
2498 c = rand () % (127 - 33) + 33;
2499 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2505 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2506 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2508 prvm_prog_t *prog = SVVM_prog;
2510 unsigned int nb_clients = 0, nb_bots = 0, i;
2513 const char *crypto_idstring;
2514 const char *worldstatusstr;
2516 // How many clients are there?
2517 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2519 if (svs.clients[i].active)
2522 if (!svs.clients[i].netconnection)
2528 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2529 if(worldstatusstr && *worldstatusstr)
2534 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2535 if(*q != '\\' && *q != '\n')
2540 /// \TODO: we should add more information for the full status string
2541 crypto_idstring = Crypto_GetInfoResponseDataString();
2542 length = dpsnprintf(out_msg, out_size,
2543 "\377\377\377\377%s\x0A"
2544 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2545 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2550 fullstatus ? "statusResponse" : "infoResponse",
2551 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2552 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2553 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2554 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2555 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2556 fullstatus ? "\n" : "");
2558 // Make sure it fits in the buffer
2568 savelength = length;
2570 ptr = out_msg + length;
2571 left = (int)out_size - length;
2573 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2575 client_t *client = &svs.clients[i];
2578 int nameind, cleanind, pingvalue;
2580 char cleanname [sizeof(client->name)];
2581 const char *statusstr;
2584 // Remove all characters '"' and '\' in the player name
2589 curchar = client->name[nameind++];
2590 if (curchar != '"' && curchar != '\\')
2592 cleanname[cleanind++] = curchar;
2593 if (cleanind == sizeof(cleanname) - 1)
2596 } while (curchar != '\0');
2597 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2599 pingvalue = (int)(client->ping * 1000.0f);
2600 if(client->netconnection)
2601 pingvalue = bound(1, pingvalue, 9999);
2606 ed = PRVM_EDICT_NUM(i + 1);
2607 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2608 if(statusstr && *statusstr)
2613 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2614 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2619 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2621 if(client->frags == -666) // spectator
2622 strlcpy(teambuf, " 0", sizeof(teambuf));
2623 else if(client->colors == 0x44) // red team
2624 strlcpy(teambuf, " 1", sizeof(teambuf));
2625 else if(client->colors == 0xDD) // blue team
2626 strlcpy(teambuf, " 2", sizeof(teambuf));
2627 else if(client->colors == 0xCC) // yellow team
2628 strlcpy(teambuf, " 3", sizeof(teambuf));
2629 else if(client->colors == 0x99) // pink team
2630 strlcpy(teambuf, " 4", sizeof(teambuf));
2632 strlcpy(teambuf, " 0", sizeof(teambuf));
2637 // note: team number is inserted according to SoF2 protocol
2639 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2645 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2654 // turn it into an infoResponse!
2655 out_msg[savelength] = 0;
2656 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2657 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2672 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2674 size_t floodslotnum, bestfloodslotnum;
2675 double bestfloodtime;
2676 lhnetaddress_t noportpeeraddress;
2677 // see if this is a connect flood
2678 noportpeeraddress = *peeraddress;
2679 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2680 bestfloodslotnum = 0;
2681 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2682 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2684 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2686 bestfloodtime = floodlist[floodslotnum].lasttime;
2687 bestfloodslotnum = floodslotnum;
2689 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2691 // this address matches an ongoing flood address
2692 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2696 // renew the ban on this address so it does not expire
2697 // until the flood has subsided
2698 floodlist[floodslotnum].lasttime = realtime;
2700 //Con_Printf("Flood detected!\n");
2703 // the flood appears to have subsided, so allow this
2704 bestfloodslotnum = floodslotnum; // reuse the same slot
2708 // begin a new timeout on this address
2709 floodlist[bestfloodslotnum].address = noportpeeraddress;
2710 floodlist[bestfloodslotnum].lasttime = realtime;
2711 //Con_Printf("Flood detection initiated!\n");
2715 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2717 size_t floodslotnum;
2718 lhnetaddress_t noportpeeraddress;
2719 // see if this is a connect flood
2720 noportpeeraddress = *peeraddress;
2721 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2722 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2724 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2726 // this address matches an ongoing flood address
2728 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2729 floodlist[floodslotnum].lasttime = 0;
2730 //Con_Printf("Flood cleared!\n");
2735 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2737 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2743 Con_Error("LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2747 t1 = (long) time(NULL);
2748 t2 = strtol(s, NULL, 0);
2749 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2752 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2755 return !memcmp(mdfourbuf, hash, 16);
2758 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2764 Con_Error("LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2768 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2771 // validate the challenge
2772 for (i = 0;i < MAX_CHALLENGES;i++)
2773 if(challenges[i].time > 0)
2774 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2776 // if the challenge is not recognized, drop the packet
2777 if (i == MAX_CHALLENGES)
2780 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2783 if(memcmp(mdfourbuf, hash, 16))
2786 // unmark challenge to prevent replay attacks
2787 challenges[i].time = 0;
2792 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2795 Con_Error("LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2799 return !strcmp(password, hash);
2802 /// returns a string describing the user level, or NULL for auth failure
2803 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)
2805 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2806 static char buf[MAX_INPUTLINE];
2808 qboolean restricted = false;
2809 qboolean have_usernames = false;
2810 static char vabuf[1024];
2812 userpass_start = rcon_password.string;
2813 while((userpass_end = strchr(userpass_start, ' ')))
2815 have_usernames = true;
2816 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2817 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2818 if(comparator(peeraddress, buf, password, cs, cslen))
2820 userpass_start = userpass_end + 1;
2822 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2824 userpass_end = userpass_start + strlen(userpass_start);
2825 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2830 have_usernames = false;
2831 userpass_start = rcon_restricted_password.string;
2832 while((userpass_end = strchr(userpass_start, ' ')))
2834 have_usernames = true;
2835 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2836 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2837 if(comparator(peeraddress, buf, password, cs, cslen))
2839 userpass_start = userpass_end + 1;
2841 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2843 userpass_end = userpass_start + strlen(userpass_start);
2844 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2848 return NULL; // DENIED
2851 for(text = s; text != endpos; ++text)
2852 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2853 return NULL; // block possible exploits against the parser/alias expansion
2857 size_t l = strlen(s);
2860 hasquotes = (strchr(s, '"') != NULL);
2861 // sorry, we can't allow these substrings in wildcard expressions,
2862 // as they can mess with the argument counts
2863 text = rcon_restricted_commands.string;
2864 while(COM_ParseToken_Console(&text))
2866 // com_token now contains a pattern to check for...
2867 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2870 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2873 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2875 if(!strcmp(com_token, s))
2878 else // single-arg expression? must match the beginning of the command
2880 if(!strcmp(com_token, s))
2882 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2886 // if we got here, nothing matched!
2894 userpass_startpass = strchr(userpass_start, ':');
2895 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2896 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2898 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2901 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2905 // looks like a legitimate rcon command with the correct password
2906 const char *s_ptr = s;
2907 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2908 while(s_ptr != endpos)
2910 size_t l = strlen(s_ptr);
2912 Con_Printf(" %s;", s_ptr);
2917 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2918 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2921 size_t l = strlen(s);
2924 client_t *host_client_save = host_client;
2925 Cmd_ExecuteString(&cmd_server, s, src_command, true);
2926 host_client = host_client_save;
2927 // in case it is a command that changes host_client (like restart)
2931 Con_Rcon_Redirect_End();
2935 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2939 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2941 int i, ret, clientnum, best;
2943 char *string, response[2800], addressstring2[128];
2944 static char stringbuf[16384]; // server only
2945 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2946 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2947 size_t sendlength, response_len;
2948 char infostringvalue[MAX_INPUTLINE];
2953 // convert the address to a string incase we need it
2954 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2956 // see if we can identify the sender as a local player
2957 // (this is necessary for rcon to send a reliable reply if the client is
2958 // actually on the server, not sending remotely)
2959 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2960 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2962 if (i == svs.maxclients)
2965 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2967 // received a command string - strip off the packaging and put it
2968 // into our string buffer with NULL termination
2971 length = min(length, (int)sizeof(stringbuf) - 1);
2972 memcpy(stringbuf, data, length);
2973 stringbuf[length] = 0;
2976 if (developer_extra.integer)
2978 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2979 Com_HexDumpToConsole(data, length);
2982 sendlength = sizeof(senddata) - 4;
2983 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2985 case CRYPTO_NOMATCH:
2991 memcpy(senddata, "\377\377\377\377", 4);
2992 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2995 case CRYPTO_DISCARD:
2998 memcpy(senddata, "\377\377\377\377", 4);
2999 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3003 case CRYPTO_REPLACE:
3004 string = senddata+4;
3005 length = (int)sendlength;
3009 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3011 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
3013 if(challenges[i].time > 0)
3014 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3016 if (besttime > challenges[i].time)
3017 besttime = challenges[best = i].time;
3019 // if we did not find an exact match, choose the oldest and
3020 // update address and string
3021 if (i == MAX_CHALLENGES)
3024 challenges[i].address = *peeraddress;
3025 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3029 // flood control: drop if requesting challenge too often
3030 if(challenges[i].time > realtime - net_challengefloodblockingtimeout.value)
3033 challenges[i].time = realtime;
3034 // send the challenge
3035 memcpy(response, "\377\377\377\377", 4);
3036 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3037 response_len = strlen(response) + 1;
3038 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3039 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3042 if (length > 8 && !memcmp(string, "connect\\", 8))
3046 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3050 if(crypto && crypto->authenticated)
3052 // no need to check challenge
3053 if(crypto_developer.integer)
3055 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3056 crypto->use_aes ? "Encrypted" : "Authenticated",
3058 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3059 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3060 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3061 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3062 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3063 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3069 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3071 // validate the challenge
3072 for (i = 0;i < MAX_CHALLENGES;i++)
3073 if(challenges[i].time > 0)
3074 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3076 // if the challenge is not recognized, drop the packet
3077 if (i == MAX_CHALLENGES)
3082 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3083 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3085 if(!(islocal || sv_public.integer > -2))
3087 if (developer_extra.integer)
3088 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3089 memcpy(response, "\377\377\377\377", 4);
3090 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3091 NetConn_WriteString(mysocket, response, peeraddress);
3095 // check engine protocol
3096 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3098 if (developer_extra.integer)
3099 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3100 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3104 // see if this is a duplicate connection request or a disconnected
3105 // client who is rejoining to the same client slot
3106 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3108 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3110 // this is a known client...
3111 if(crypto && crypto->authenticated)
3113 // reject if changing key!
3114 if(client->netconnection->crypto.authenticated)
3117 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3119 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3121 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3123 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3126 if (developer_extra.integer)
3127 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3128 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3135 // reject if downgrading!
3136 if(client->netconnection->crypto.authenticated)
3138 if (developer_extra.integer)
3139 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3140 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3146 // client crashed and is coming back,
3147 // keep their stuff intact
3148 if (developer_extra.integer)
3149 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3150 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3151 if(crypto && crypto->authenticated)
3152 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3153 SV_SendServerinfo(client);
3157 // client is still trying to connect,
3158 // so we send a duplicate reply
3159 if (developer_extra.integer)
3160 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3161 if(crypto && crypto->authenticated)
3162 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3163 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3169 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3172 // find an empty client slot for this new client
3173 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3176 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3178 // allocated connection
3179 if (developer_extra.integer)
3180 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3181 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3182 // now set up the client
3183 if(crypto && crypto->authenticated)
3184 Crypto_FinishInstance(&conn->crypto, crypto);
3185 SV_ConnectClient(clientnum, conn);
3186 NetConn_Heartbeat(1);
3191 // no empty slots found - server is full
3192 if (developer_extra.integer)
3193 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3194 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3198 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3200 const char *challenge = NULL;
3202 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3205 // If there was a challenge in the getinfo message
3206 if (length > 8 && string[7] == ' ')
3207 challenge = string + 8;
3209 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3211 if (developer_extra.integer)
3212 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3213 NetConn_WriteString(mysocket, response, peeraddress);
3217 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3219 const char *challenge = NULL;
3221 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3224 // If there was a challenge in the getinfo message
3225 if (length > 10 && string[9] == ' ')
3226 challenge = string + 10;
3228 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3230 if (developer_extra.integer)
3231 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3232 NetConn_WriteString(mysocket, response, peeraddress);
3236 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3238 char *password = string + 20;
3239 char *timeval = string + 37;
3240 char *s = strchr(timeval, ' ');
3241 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3242 const char *userlevel;
3244 if(rcon_secure.integer > 1)
3248 return true; // invalid packet
3251 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3252 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3255 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3257 char *password = string + 25;
3258 char *challenge = string + 42;
3259 char *s = strchr(challenge, ' ');
3260 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3261 const char *userlevel;
3263 return true; // invalid packet
3266 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3267 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3270 if (length >= 5 && !memcmp(string, "rcon ", 5))
3273 char *s = string + 5;
3274 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3277 if(rcon_secure.integer > 0)
3280 for (j = 0;!ISWHITESPACE(*s);s++)
3281 if (j < (int)sizeof(password) - 1)
3283 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3286 if (!ISWHITESPACE(password[0]))
3288 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3289 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3293 if (!strncmp(string, "extResponse ", 12))
3295 ++sv_net_extresponse_count;
3296 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3297 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3298 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3299 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3302 if (!strncmp(string, "ping", 4))
3304 if (developer_extra.integer)
3305 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3306 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3309 if (!strncmp(string, "ack", 3))
3311 // we may not have liked the packet, but it was a command packet, so
3312 // we're done processing this packet now
3315 // netquake control packets, supported for compatibility only, and only
3316 // when running game protocols that are normally served via this connection
3318 // (this protects more modern protocols against being used for
3319 // Quake packet flood Denial Of Service attacks)
3320 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)
3324 const char *protocolname;
3325 client_t *knownclient;
3326 client_t *newclient;
3329 SZ_Clear(&sv_message);
3330 SZ_Write(&sv_message, data, length);
3331 MSG_BeginReading(&sv_message);
3332 c = MSG_ReadByte(&sv_message);
3336 if (developer_extra.integer)
3337 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3338 if(!(islocal || sv_public.integer > -2))
3340 if (developer_extra.integer)
3341 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3342 SZ_Clear(&sv_message);
3343 // save space for the header, filled in later
3344 MSG_WriteLong(&sv_message, 0);
3345 MSG_WriteByte(&sv_message, CCREP_REJECT);
3346 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3347 MSG_WriteString(&sv_message, "\n");
3348 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3349 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3350 SZ_Clear(&sv_message);
3354 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3355 protocolnumber = MSG_ReadByte(&sv_message);
3356 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3358 if (developer_extra.integer)
3359 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3360 SZ_Clear(&sv_message);
3361 // save space for the header, filled in later
3362 MSG_WriteLong(&sv_message, 0);
3363 MSG_WriteByte(&sv_message, CCREP_REJECT);
3364 MSG_WriteString(&sv_message, "Incompatible version.\n");
3365 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3366 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3367 SZ_Clear(&sv_message);
3371 // see if this connect request comes from a known client
3372 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3374 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3376 // this is either a duplicate connection request
3377 // or coming back from a timeout
3378 // (if so, keep their stuff intact)
3380 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3381 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3383 if (developer_extra.integer)
3384 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3385 SZ_Clear(&sv_message);
3386 // save space for the header, filled in later
3387 MSG_WriteLong(&sv_message, 0);
3388 MSG_WriteByte(&sv_message, CCREP_REJECT);
3389 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3390 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3391 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3392 SZ_Clear(&sv_message);
3397 if (developer_extra.integer)
3398 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3399 SZ_Clear(&sv_message);
3400 // save space for the header, filled in later
3401 MSG_WriteLong(&sv_message, 0);
3402 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3403 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3404 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3405 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3406 SZ_Clear(&sv_message);
3408 // if client is already spawned, re-send the
3409 // serverinfo message as they'll need it to play
3410 if (knownclient->begun)
3411 SV_SendServerinfo(knownclient);
3416 // this is a new client, check for connection flood
3417 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3420 // find a slot for the new client
3421 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3424 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3426 // connect to the client
3427 // everything is allocated, just fill in the details
3428 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3429 if (developer_extra.integer)
3430 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3431 // send back the info about the server connection
3432 SZ_Clear(&sv_message);
3433 // save space for the header, filled in later
3434 MSG_WriteLong(&sv_message, 0);
3435 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3436 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3437 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3438 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3439 SZ_Clear(&sv_message);
3440 // now set up the client struct
3441 SV_ConnectClient(clientnum, conn);
3442 NetConn_Heartbeat(1);
3447 if (developer_extra.integer)
3448 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3449 // no room; try to let player know
3450 SZ_Clear(&sv_message);
3451 // save space for the header, filled in later
3452 MSG_WriteLong(&sv_message, 0);
3453 MSG_WriteByte(&sv_message, CCREP_REJECT);
3454 MSG_WriteString(&sv_message, "Server is full.\n");
3455 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3456 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3457 SZ_Clear(&sv_message);
3459 case CCREQ_SERVER_INFO:
3460 if (developer_extra.integer)
3461 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3462 if(!(islocal || sv_public.integer > -1))
3465 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3468 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3471 char myaddressstring[128];
3472 if (developer_extra.integer)
3473 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3474 SZ_Clear(&sv_message);
3475 // save space for the header, filled in later
3476 MSG_WriteLong(&sv_message, 0);
3477 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3478 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3479 MSG_WriteString(&sv_message, myaddressstring);
3480 MSG_WriteString(&sv_message, hostname.string);
3481 MSG_WriteString(&sv_message, sv.name);
3482 // How many clients are there?
3483 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3484 if (svs.clients[i].active)
3486 MSG_WriteByte(&sv_message, numclients);
3487 MSG_WriteByte(&sv_message, svs.maxclients);
3488 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3489 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3490 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3491 SZ_Clear(&sv_message);
3494 case CCREQ_PLAYER_INFO:
3495 if (developer_extra.integer)
3496 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3497 if(!(islocal || sv_public.integer > -1))
3500 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3505 int playerNumber, activeNumber, clientNumber;
3508 playerNumber = MSG_ReadByte(&sv_message);
3510 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3511 if (client->active && ++activeNumber == playerNumber)
3513 if (clientNumber != svs.maxclients)
3515 SZ_Clear(&sv_message);
3516 // save space for the header, filled in later
3517 MSG_WriteLong(&sv_message, 0);
3518 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3519 MSG_WriteByte(&sv_message, playerNumber);
3520 MSG_WriteString(&sv_message, client->name);
3521 MSG_WriteLong(&sv_message, client->colors);
3522 MSG_WriteLong(&sv_message, client->frags);
3523 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3524 if(sv_status_privacy.integer)
3525 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3527 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3528 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3529 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3530 SZ_Clear(&sv_message);
3534 case CCREQ_RULE_INFO:
3535 if (developer_extra.integer)
3536 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3537 if(!(islocal || sv_public.integer > -1))
3540 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3547 // find the search start location
3548 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3549 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CVAR_NOTIFY);
3551 // send the response
3552 SZ_Clear(&sv_message);
3553 // save space for the header, filled in later
3554 MSG_WriteLong(&sv_message, 0);
3555 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3558 MSG_WriteString(&sv_message, var->name);
3559 MSG_WriteString(&sv_message, var->string);
3561 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3562 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3563 SZ_Clear(&sv_message);
3567 if (developer_extra.integer)
3568 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3569 if (sv.active && !rcon_secure.integer)
3571 char password[2048];
3575 const char *userlevel;
3576 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3577 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3579 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3580 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3581 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3588 SZ_Clear(&sv_message);
3589 // we may not have liked the packet, but it was a valid control
3590 // packet, so we're done processing this packet now
3595 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3597 SV_ReadClientMessage();
3604 void NetConn_ServerFrame(void)
3607 lhnetaddress_t peeraddress;
3608 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3609 for (i = 0;i < sv_numsockets;i++)
3610 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3611 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3612 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3614 // never timeout loopback connections
3615 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3617 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3618 SV_DropClient(false);
3623 void NetConn_SleepMicroseconds(int microseconds)
3625 LHNET_SleepUntilPacket_Microseconds(microseconds);
3629 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3633 lhnetaddress_t masteraddress;
3634 lhnetaddress_t broadcastaddress;
3637 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3640 // 26000 is the default quake server port, servers on other ports will not
3642 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3643 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3647 for (i = 0;i < cl_numsockets;i++)
3651 const char *cmdname, *extraoptions;
3652 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3654 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3656 // search LAN for Quake servers
3657 SZ_Clear(&cl_message);
3658 // save space for the header, filled in later
3659 MSG_WriteLong(&cl_message, 0);
3660 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3661 MSG_WriteString(&cl_message, "QUAKE");
3662 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3663 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3664 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3665 SZ_Clear(&cl_message);
3667 // search LAN for DarkPlaces servers
3668 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3671 // build the getservers message to send to the dpmaster master servers
3672 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3674 cmdname = "getserversExt";
3675 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3679 cmdname = "getservers";
3682 memcpy(request, "\377\377\377\377", 4);
3683 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3686 for (masternum = 0;sv_masters[masternum].name;masternum++)
3688 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3691 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3695 // search favorite servers
3696 for(j = 0; j < nFavorites; ++j)
3698 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3700 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3701 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3708 // only query QuakeWorld servers when the user wants to
3711 for (i = 0;i < cl_numsockets;i++)
3715 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3717 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3719 // search LAN for QuakeWorld servers
3720 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3722 // build the getservers message to send to the qwmaster master servers
3723 // note this has no -1 prefix, and the trailing nul byte is sent
3724 dpsnprintf(request, sizeof(request), "c\n");
3728 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3730 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3732 if (m_state != m_slist)
3734 char lookupstring[128];
3735 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3736 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3739 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3743 // search favorite servers
3744 for(j = 0; j < nFavorites; ++j)
3746 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3748 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3750 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3751 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3758 if (!masterquerycount)
3760 Con_Error("Unable to query master servers, no suitable network sockets active.\n");
3761 M_Update_Return_Reason("No network");
3766 void NetConn_Heartbeat(int priority)
3768 lhnetaddress_t masteraddress;
3770 lhnetsocket_t *mysocket;
3772 // if it's a state change (client connected), limit next heartbeat to no
3773 // more than 30 sec in the future
3774 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3775 nextheartbeattime = realtime + 30.0;
3777 // limit heartbeatperiod to 30 to 270 second range,
3778 // lower limit is to avoid abusing master servers with excess traffic,
3779 // upper limit is to avoid timing out on the master server (which uses
3781 if (sv_heartbeatperiod.value < 30)
3782 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3783 if (sv_heartbeatperiod.value > 270)
3784 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3786 // make advertising optional and don't advertise singleplayer games, and
3787 // only send a heartbeat as often as the admin wants
3788 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3790 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3791 for (masternum = 0;sv_masters[masternum].name;masternum++)
3792 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3793 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3797 static void Net_Heartbeat_f(cmd_state_t *cmd)
3800 NetConn_Heartbeat(2);
3802 Con_Print("No server running, can not heartbeat to master server.\n");
3805 static void PrintStats(netconn_t *conn)
3807 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3808 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3810 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3811 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3812 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3813 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3814 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3815 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3816 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3817 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3818 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3819 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3822 void Net_Stats_f(cmd_state_t *cmd)
3825 Con_Print("connections =\n");
3826 for (conn = netconn_list;conn;conn = conn->next)
3831 void Net_Refresh_f(cmd_state_t *cmd)
3833 if (m_state != m_slist) {
3834 Con_Print("Sending new requests to master servers\n");
3835 ServerList_QueryList(false, true, false, true);
3836 Con_Print("Listening for replies...\n");
3838 ServerList_QueryList(false, true, false, false);
3841 void Net_Slist_f(cmd_state_t *cmd)
3843 ServerList_ResetMasks();
3844 serverlist_sortbyfield = SLIF_PING;
3845 serverlist_sortflags = 0;
3846 if (m_state != m_slist) {
3847 Con_Print("Sending requests to master servers\n");
3848 ServerList_QueryList(true, true, false, true);
3849 Con_Print("Listening for replies...\n");
3851 ServerList_QueryList(true, true, false, false);
3854 void Net_SlistQW_f(cmd_state_t *cmd)
3856 ServerList_ResetMasks();
3857 serverlist_sortbyfield = SLIF_PING;
3858 serverlist_sortflags = 0;
3859 if (m_state != m_slist) {
3860 Con_Print("Sending requests to master servers\n");
3861 ServerList_QueryList(true, false, true, true);
3862 serverlist_consoleoutput = true;
3863 Con_Print("Listening for replies...\n");
3865 ServerList_QueryList(true, false, true, false);
3869 void NetConn_Init(void)
3872 lhnetaddress_t tempaddress;
3873 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3874 Cmd_AddCommand(CMD_SHARED, "net_stats", Net_Stats_f, "print network statistics");
3876 Cmd_AddCommand(CMD_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
3877 Cmd_AddCommand(CMD_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3878 Cmd_AddCommand(CMD_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3880 Cmd_AddCommand(CMD_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3881 Cvar_RegisterVariable(&net_test);
3882 Cvar_RegisterVariable(&net_usesizelimit);
3883 Cvar_RegisterVariable(&net_burstreserve);
3884 Cvar_RegisterVariable(&rcon_restricted_password);
3885 Cvar_RegisterVariable(&rcon_restricted_commands);
3886 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3887 Cvar_RegisterVariable(&net_slist_queriespersecond);
3888 Cvar_RegisterVariable(&net_slist_queriesperframe);
3889 Cvar_RegisterVariable(&net_slist_timeout);
3890 Cvar_RegisterVariable(&net_slist_maxtries);
3891 Cvar_RegisterVariable(&net_slist_favorites);
3892 Cvar_RegisterVariable(&net_slist_pause);
3893 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3894 Cvar_RegisterVariable(&net_tos_dscp);
3895 Cvar_RegisterVariable(&net_messagetimeout);
3896 Cvar_RegisterVariable(&net_connecttimeout);
3897 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3898 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3899 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3900 Cvar_RegisterVariable(&net_sourceaddresscheck);
3901 Cvar_RegisterVariable(&net_fakelag);
3902 Cvar_RegisterVariable(&net_fakeloss_send);
3903 Cvar_RegisterVariable(&net_fakeloss_receive);
3904 Cvar_RegisterAlias(&net_fakelag, "cl_netlocalping");
3905 Cvar_RegisterAlias(&net_fakeloss_send, "cl_netpacketloss_send");
3906 Cvar_RegisterAlias(&net_fakeloss_receive, "cl_netpacketloss_receive");
3907 Cvar_RegisterVariable(&hostname);
3908 Cvar_RegisterVariable(&developer_networking);
3909 Cvar_RegisterVariable(&cl_netport);
3910 Cvar_RegisterVariable(&sv_netport);
3911 Cvar_RegisterVariable(&net_address);
3912 Cvar_RegisterVariable(&net_address_ipv6);
3913 Cvar_RegisterVariable(&sv_public);
3914 Cvar_RegisterVariable(&sv_public_rejectreason);
3915 Cvar_RegisterVariable(&sv_heartbeatperiod);
3916 for (i = 0;sv_masters[i].name;i++)
3917 Cvar_RegisterVariable(&sv_masters[i]);
3918 Cvar_RegisterVariable(&gameversion);
3919 Cvar_RegisterVariable(&gameversion_min);
3920 Cvar_RegisterVariable(&gameversion_max);
3921 // 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.
3922 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3924 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3926 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3927 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3930 Con_Errorf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3932 // 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
3933 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3935 i = atoi(com_argv[i + 1]);
3936 if (i >= 0 && i < 65536)
3938 Con_Printf("-port option used, setting port cvar to %i\n", i);
3939 Cvar_SetValueQuick(&sv_netport, i);
3942 Con_Errorf("-port option used, but %i is not a valid port number\n", i);
3946 cl_message.data = cl_message_buf;
3947 cl_message.maxsize = sizeof(cl_message_buf);
3948 cl_message.cursize = 0;
3949 sv_message.data = sv_message_buf;
3950 sv_message.maxsize = sizeof(sv_message_buf);
3951 sv_message.cursize = 0;
3953 if (Thread_HasThreads())
3954 netconn_mutex = Thread_CreateMutex();
3957 void NetConn_Shutdown(void)
3959 NetConn_CloseClientPorts();
3960 NetConn_CloseServerPorts();
3963 Thread_DestroyMutex(netconn_mutex);
3964 netconn_mutex = NULL;