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 cvar_t cl_netlocalping = {CVAR_ALIAS, "cl_netlocalping"};
90 static cvar_t net_fakeloss_send = {CVAR_CLIENT, "cl_netpacketloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
91 static cvar_t net_fakeloss_receive = {CVAR_CLIENT, "cl_netpacketloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
92 static cvar_t cl_netpacketloss_send = {CVAR_ALIAS, "cl_netpacketloss_send"};
93 static cvar_t cl_netpacketloss_receive = {CVAR_ALIAS, "cl_netpacketloss_receive"};
95 static cvar_t net_slist_queriespersecond = {CVAR_CLIENT, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
96 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)"};
97 static cvar_t net_slist_timeout = {CVAR_CLIENT, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
98 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"};
99 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)"};
100 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"};
101 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)"};
102 static cvar_t gameversion = {CVAR_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
103 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"};
104 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"};
105 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"};
106 static cvar_t rcon_restricted_commands = {CVAR_SERVER, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
107 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)"};
108 extern cvar_t rcon_secure;
109 extern cvar_t rcon_secure_challengetimeout;
111 double masterquerytime = -1000;
112 int masterquerycount = 0;
113 int masterreplycount = 0;
114 int serverquerycount = 0;
115 int serverreplycount = 0;
117 challenge_t challenges[MAX_CHALLENGES];
120 /// this is only false if there are still servers left to query
121 static qboolean serverlist_querysleep = true;
122 static qboolean serverlist_paused = false;
123 /// this is pushed a second or two ahead of realtime whenever a master server
124 /// reply is received, to avoid issuing queries while master replies are still
125 /// flooding in (which would make a mess of the ping times)
126 static double serverlist_querywaittime = 0;
129 static int cl_numsockets;
130 static lhnetsocket_t *cl_sockets[16];
131 static int sv_numsockets;
132 static lhnetsocket_t *sv_sockets[16];
134 netconn_t *netconn_list = NULL;
135 mempool_t *netconn_mempool = NULL;
136 void *netconn_mutex = NULL;
138 cvar_t cl_netport = {CVAR_CLIENT, "cl_port", "0", "forces client to use chosen port number if not 0"};
139 cvar_t sv_netport = {CVAR_SERVER, "port", "26000", "server port for players to connect to"};
140 cvar_t net_address = {CVAR_CLIENT | CVAR_SERVER, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
141 cvar_t net_address_ipv6 = {CVAR_CLIENT | CVAR_SERVER, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
143 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
144 int cl_net_extresponse_count = 0;
145 int cl_net_extresponse_last = 0;
147 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
148 int sv_net_extresponse_count = 0;
149 int sv_net_extresponse_last = 0;
152 // ServerList interface
153 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
154 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
156 serverlist_infofield_t serverlist_sortbyfield;
157 int serverlist_sortflags;
159 int serverlist_viewcount = 0;
160 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
162 int serverlist_maxcachecount = 0;
163 int serverlist_cachecount = 0;
164 serverlist_entry_t *serverlist_cache = NULL;
166 qboolean serverlist_consoleoutput;
168 static int nFavorites = 0;
169 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
170 static int nFavorites_idfp = 0;
171 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
173 void NetConn_UpdateFavorites(void)
178 p = net_slist_favorites.string;
179 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
181 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
182 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
183 // (if v6 address contains port, it must start with '[')
185 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
190 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
196 /// helper function to insert a value into the viewset
197 /// spare entries will be removed
198 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
201 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
202 i = serverlist_viewcount++;
204 i = SERVERLIST_VIEWLISTSIZE - 1;
207 for( ; i > index ; i-- )
208 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
210 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
213 /// we suppose serverlist_viewcount to be valid, ie > 0
214 static void _ServerList_ViewList_Helper_Remove( int index )
216 serverlist_viewcount--;
217 for( ; index < serverlist_viewcount ; index++ )
218 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
221 /// \returns true if A should be inserted before B
222 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
224 int result = 0; // > 0 if for numbers A > B and for text if A < B
226 if( serverlist_sortflags & SLSF_CATEGORIES )
228 result = A->info.category - B->info.category;
233 if( serverlist_sortflags & SLSF_FAVORITES )
235 if(A->info.isfavorite != B->info.isfavorite)
236 return A->info.isfavorite;
239 switch( serverlist_sortbyfield ) {
241 result = A->info.ping - B->info.ping;
243 case SLIF_MAXPLAYERS:
244 result = A->info.maxplayers - B->info.maxplayers;
246 case SLIF_NUMPLAYERS:
247 result = A->info.numplayers - B->info.numplayers;
250 result = A->info.numbots - B->info.numbots;
253 result = A->info.numhumans - B->info.numhumans;
256 result = A->info.freeslots - B->info.freeslots;
259 result = A->info.protocol - B->info.protocol;
262 result = strcmp( B->info.cname, A->info.cname );
265 result = strcasecmp( B->info.game, A->info.game );
268 result = strcasecmp( B->info.map, A->info.map );
271 result = strcasecmp( B->info.mod, A->info.mod );
274 result = strcasecmp( B->info.name, A->info.name );
277 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
280 result = A->info.category - B->info.category;
282 case SLIF_ISFAVORITE:
283 result = !!B->info.isfavorite - !!A->info.isfavorite;
286 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
292 if( serverlist_sortflags & SLSF_DESCENDING )
298 // if the chosen sort key is identical, sort by index
299 // (makes this a stable sort, so that later replies from servers won't
300 // shuffle the servers around when they have the same ping)
304 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
306 // This should actually be done with some intermediate and end-of-function return
318 case SLMO_GREATEREQUAL:
320 case SLMO_NOTCONTAIN:
321 case SLMO_STARTSWITH:
322 case SLMO_NOTSTARTSWITH:
325 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
330 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
333 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
334 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
335 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
336 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
338 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
339 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
342 // Same here, also using an intermediate & final return would be more appropriate
346 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
347 case SLMO_NOTCONTAIN:
348 return !*bufferB || !strstr( bufferA, bufferB );
349 case SLMO_STARTSWITH:
350 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
351 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
352 case SLMO_NOTSTARTSWITH:
353 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
355 return strcmp( bufferA, bufferB ) < 0;
357 return strcmp( bufferA, bufferB ) <= 0;
359 return strcmp( bufferA, bufferB ) == 0;
361 return strcmp( bufferA, bufferB ) > 0;
363 return strcmp( bufferA, bufferB ) != 0;
364 case SLMO_GREATEREQUAL:
365 return strcmp( bufferA, bufferB ) >= 0;
367 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
372 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
374 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
376 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
378 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
380 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
382 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
384 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
386 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
388 if( *mask->info.cname
389 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
392 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
395 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
398 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
401 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
403 if( *mask->info.qcstatus
404 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
406 if( *mask->info.players
407 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
409 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
411 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
416 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
418 int start, end, mid, i;
421 // reject incompatible servers
423 entry->info.gameversion != gameversion.integer
426 gameversion_min.integer >= 0 // min/max range set by user/mod?
427 && gameversion_max.integer >= 0
428 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
429 && gameversion_max.integer >= entry->info.gameversion
434 // refresh the "favorite" status
435 entry->info.isfavorite = false;
436 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
438 char idfp[FP64_SIZE+1];
439 for(i = 0; i < nFavorites; ++i)
441 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
443 entry->info.isfavorite = true;
447 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
449 for(i = 0; i < nFavorites_idfp; ++i)
451 if(!strcmp(idfp, favorites_idfp[i]))
453 entry->info.isfavorite = true;
460 // refresh the "category"
461 entry->info.category = MR_GetServerListEntryCategory(entry);
463 // FIXME: change this to be more readable (...)
464 // now check whether it passes through the masks
465 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
466 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
469 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
470 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
472 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
475 if( !serverlist_viewcount ) {
476 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
479 // ok, insert it, we just need to find out where exactly:
482 // check whether to insert it as new first item
483 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
484 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
486 } // check whether to insert it as new last item
487 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
488 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
492 end = serverlist_viewcount - 1;
493 while( end > start + 1 )
495 mid = (start + end) / 2;
496 // test the item that lies in the middle between start and end
497 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
498 // the item has to be in the upper half
501 // the item has to be in the lower half
504 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
507 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
510 for( i = 0; i < serverlist_viewcount; i++ )
512 if (ServerList_GetViewEntry(i) == entry)
514 _ServerList_ViewList_Helper_Remove(i);
520 void ServerList_RebuildViewList(void)
524 serverlist_viewcount = 0;
525 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
526 serverlist_entry_t *entry = &serverlist_cache[i];
527 // also display entries that are currently being refreshed [11/8/2007 Black]
528 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
529 ServerList_ViewList_Insert( entry );
533 void ServerList_ResetMasks(void)
537 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
538 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
539 // numbots needs to be compared to -1 to always succeed
540 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
541 serverlist_andmasks[i].info.numbots = -1;
542 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
543 serverlist_ormasks[i].info.numbots = -1;
546 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
549 int numplayers = 0, maxplayers = 0;
550 for (i = 0;i < serverlist_cachecount;i++)
552 if (serverlist_cache[i].query == SQS_QUERIED)
554 numplayers += serverlist_cache[i].info.numhumans;
555 maxplayers += serverlist_cache[i].info.maxplayers;
558 *numplayerspointer = numplayers;
559 *maxplayerspointer = maxplayers;
563 static void _ServerList_Test(void)
566 if (serverlist_maxcachecount <= 1024)
568 serverlist_maxcachecount = 1024;
569 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
571 for( i = 0 ; i < 1024 ; i++ ) {
572 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
573 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
574 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
575 serverlist_cache[serverlist_cachecount].finished = true;
576 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 );
577 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
578 serverlist_cachecount++;
583 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
585 masterquerytime = realtime;
586 masterquerycount = 0;
587 masterreplycount = 0;
589 serverquerycount = 0;
590 serverreplycount = 0;
591 serverlist_cachecount = 0;
592 serverlist_viewcount = 0;
593 serverlist_maxcachecount = 0;
594 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
596 // refresh all entries
598 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
599 serverlist_entry_t *entry = &serverlist_cache[ n ];
600 entry->query = SQS_REFRESHING;
601 entry->querycounter = 0;
604 serverlist_consoleoutput = consoleoutput;
606 //_ServerList_Test();
608 NetConn_QueryMasters(querydp, queryqw);
614 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
618 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
619 Thread_LockMutex(netconn_mutex);
620 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
621 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
622 Thread_UnlockMutex(netconn_mutex);
625 if (net_fakeloss_receive.integer)
626 for (i = 0;i < cl_numsockets;i++)
627 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_receive.integer)
629 if (developer_networking.integer)
631 char addressstring[128], addressstring2[128];
632 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
635 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
636 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
637 Com_HexDumpToConsole((unsigned char *)data, length);
640 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
645 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
649 if (net_fakeloss_send.integer)
650 for (i = 0;i < cl_numsockets;i++)
651 if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_send.integer)
653 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
654 Thread_LockMutex(netconn_mutex);
655 ret = LHNET_Write(mysocket, data, length, peeraddress);
656 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
657 Thread_UnlockMutex(netconn_mutex);
658 if (developer_networking.integer)
660 char addressstring[128], addressstring2[128];
661 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
662 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
663 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)");
664 Com_HexDumpToConsole((unsigned char *)data, length);
669 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
671 // note this does not include the trailing NULL because we add that in the parser
672 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
675 qboolean NetConn_CanSend(netconn_t *conn)
677 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
678 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = realtime;
679 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
680 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
681 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
682 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
683 if (realtime > conn->cleartime)
687 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
692 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
694 double bursttime = burstsize / (double)rate;
696 // delay later packets to obey rate limit
697 if (*cleartime < realtime - bursttime)
698 *cleartime = realtime - bursttime;
699 *cleartime = *cleartime + len / (double)rate;
701 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
702 if (net_test.integer)
704 if (*cleartime < realtime)
705 *cleartime = realtime;
709 static int NetConn_AddCryptoFlag(crypto_t *crypto)
711 // HACK: if an encrypted connection is used, randomly set some unused
712 // flags. When AES encryption is enabled, that will make resends differ
713 // from the original, so that e.g. substring filters in a router/IPS
714 // are unlikely to match a second time. See also "startkeylogger".
716 if (crypto->authenticated)
718 // Let's always set at least one of the bits.
719 int r = rand() % 7 + 1;
721 flag |= NETFLAG_CRYPTO0;
723 flag |= NETFLAG_CRYPTO1;
725 flag |= NETFLAG_CRYPTO2;
730 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
733 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
734 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
736 // if this packet was supposedly choked, but we find ourselves sending one
737 // anyway, make sure the size counting starts at zero
738 // (this mostly happens on level changes and disconnects and such)
739 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
740 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
742 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
744 if (protocol == PROTOCOL_QUAKEWORLD)
747 qboolean sendreliable;
749 // note that it is ok to send empty messages to the qw server,
750 // otherwise it won't respond to us at all
752 sendreliable = false;
753 // if the remote side dropped the last reliable message, resend it
754 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
756 // if the reliable transmit buffer is empty, copy the current message out
757 if (!conn->sendMessageLength && conn->message.cursize)
759 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
760 conn->sendMessageLength = conn->message.cursize;
761 SZ_Clear(&conn->message); // clear the message buffer
762 conn->qw.reliable_sequence ^= 1;
765 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
766 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
767 // last received unreliable packet number, and last received reliable packet number (0 or 1)
768 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
770 conn->outgoing_unreliable_sequence++;
771 // client sends qport in every packet
772 if (conn == cls.netcon)
774 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
776 // also update cls.qw_outgoing_sequence
777 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
779 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
781 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
785 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
787 // add the reliable message if there is one
790 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
791 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
792 packetLen += conn->sendMessageLength;
793 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
796 // add the unreliable message if possible
797 if (packetLen + data->cursize <= 1400)
799 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
800 memcpy(sendbuffer + packetLen, data->data, data->cursize);
801 packetLen += data->cursize;
804 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
807 conn->unreliableMessagesSent++;
809 totallen += packetLen + 28;
813 unsigned int packetLen;
814 unsigned int dataLen;
819 // if a reliable message fragment has been lost, send it again
820 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
822 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
824 dataLen = conn->sendMessageLength;
829 dataLen = MAX_PACKETFRAGMENT;
833 packetLen = NET_HEADERSIZE + dataLen;
835 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
836 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
837 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
839 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
841 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
842 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
844 conn->lastSendTime = realtime;
845 conn->packetsReSent++;
848 totallen += (int)sendmelen + 28;
851 // if we have a new reliable message to send, do so
852 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
854 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
856 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
857 conn->message.overflowed = true;
861 if (developer_networking.integer && conn == cls.netcon)
863 Con_Print("client sending reliable message to server:\n");
864 SZ_HexDumpToConsole(&conn->message);
867 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
868 conn->sendMessageLength = conn->message.cursize;
869 SZ_Clear(&conn->message);
871 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
873 dataLen = conn->sendMessageLength;
878 dataLen = MAX_PACKETFRAGMENT;
882 packetLen = NET_HEADERSIZE + dataLen;
884 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
885 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
886 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
888 conn->nq.sendSequence++;
890 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
892 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
894 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
896 conn->lastSendTime = realtime;
898 conn->reliableMessagesSent++;
900 totallen += (int)sendmelen + 28;
903 // if we have an unreliable message to send, do so
906 packetLen = NET_HEADERSIZE + data->cursize;
908 if (packetLen > (int)sizeof(sendbuffer))
910 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
914 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
915 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
916 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
918 conn->outgoing_unreliable_sequence++;
920 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
922 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
924 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
927 conn->unreliableMessagesSent++;
929 totallen += (int)sendmelen + 28;
933 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
938 qboolean NetConn_HaveClientPorts(void)
940 return !!cl_numsockets;
943 qboolean NetConn_HaveServerPorts(void)
945 return !!sv_numsockets;
948 void NetConn_CloseClientPorts(void)
950 for (;cl_numsockets > 0;cl_numsockets--)
951 if (cl_sockets[cl_numsockets - 1])
952 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
955 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
957 lhnetaddress_t address;
960 char addressstring2[1024];
961 if (addressstring && addressstring[0])
962 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
964 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
967 if ((s = LHNET_OpenSocket_Connectionless(&address)))
969 cl_sockets[cl_numsockets++] = s;
970 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
971 if (addresstype != LHNETADDRESSTYPE_LOOP)
972 Con_Printf("Client opened a socket on address %s\n", addressstring2);
976 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
977 Con_Errorf("Client failed to open a socket on address %s\n", addressstring2);
981 Con_Errorf("Client unable to parse address %s\n", addressstring);
984 void NetConn_OpenClientPorts(void)
987 NetConn_CloseClientPorts();
989 SV_LockThreadMutex(); // FIXME recursive?
990 Crypto_LoadKeys(); // client sockets
991 SV_UnlockThreadMutex();
993 port = bound(0, cl_netport.integer, 65535);
994 if (cl_netport.integer != port)
995 Cvar_SetValueQuick(&cl_netport, port);
997 Con_Printf("Client using an automatically assigned port\n");
999 Con_Printf("Client using port %i\n", port);
1000 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
1001 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
1002 #ifndef NOSUPPORTIPV6
1003 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1007 void NetConn_CloseServerPorts(void)
1009 for (;sv_numsockets > 0;sv_numsockets--)
1010 if (sv_sockets[sv_numsockets - 1])
1011 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1014 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1016 lhnetaddress_t address;
1019 char addressstring2[1024];
1022 for (port = defaultport; port <= defaultport + range; port++)
1024 if (addressstring && addressstring[0])
1025 success = LHNETADDRESS_FromString(&address, addressstring, port);
1027 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1030 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1032 sv_sockets[sv_numsockets++] = s;
1033 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1034 if (addresstype != LHNETADDRESSTYPE_LOOP)
1035 Con_Printf("Server listening on address %s\n", addressstring2);
1040 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1041 Con_Errorf("Server failed to open socket on address %s\n", addressstring2);
1046 Con_Errorf("Server unable to parse address %s\n", addressstring);
1047 // if it cant parse one address, it wont be able to parse another for sure
1054 void NetConn_OpenServerPorts(int opennetports)
1057 NetConn_CloseServerPorts();
1059 SV_LockThreadMutex(); // FIXME recursive?
1060 Crypto_LoadKeys(); // server sockets
1061 SV_UnlockThreadMutex();
1063 NetConn_UpdateSockets();
1064 port = bound(0, sv_netport.integer, 65535);
1067 Con_Printf("Server using port %i\n", port);
1068 if (sv_netport.integer != port)
1069 Cvar_SetValueQuick(&sv_netport, port);
1070 if (cls.state != ca_dedicated)
1071 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1074 #ifndef NOSUPPORTIPV6
1075 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1076 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1078 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1081 if (sv_numsockets == 0)
1082 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1085 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1087 int i, a = LHNETADDRESS_GetAddressType(address);
1088 for (i = 0;i < cl_numsockets;i++)
1089 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1090 return cl_sockets[i];
1094 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1096 int i, a = LHNETADDRESS_GetAddressType(address);
1097 for (i = 0;i < sv_numsockets;i++)
1098 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1099 return sv_sockets[i];
1103 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1106 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1107 conn->mysocket = mysocket;
1108 conn->peeraddress = *peeraddress;
1109 conn->lastMessageTime = realtime;
1110 conn->message.data = conn->messagedata;
1111 conn->message.maxsize = sizeof(conn->messagedata);
1112 conn->message.cursize = 0;
1113 // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
1114 // reduce effectiveness of connection request floods
1115 conn->timeout = realtime + net_connecttimeout.value;
1116 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1117 conn->next = netconn_list;
1118 netconn_list = conn;
1122 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1123 void NetConn_Close(netconn_t *conn)
1126 // remove connection from list
1128 // allow the client to reconnect immediately
1129 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1131 if (conn == netconn_list)
1132 netconn_list = conn->next;
1135 for (c = netconn_list;c;c = c->next)
1137 if (c->next == conn)
1139 c->next = conn->next;
1143 // not found in list, we'll avoid crashing here...
1151 static int clientport = -1;
1152 static int clientport2 = -1;
1153 static int hostport = -1;
1154 void NetConn_UpdateSockets(void)
1158 // TODO add logic to automatically close sockets if needed
1159 LHNET_DefaultDSCP(net_tos_dscp.integer);
1161 if (cls.state != ca_dedicated)
1163 if (clientport2 != cl_netport.integer)
1165 clientport2 = cl_netport.integer;
1166 if (cls.state == ca_connected)
1167 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1169 if (cls.state == ca_disconnected && clientport != clientport2)
1171 clientport = clientport2;
1172 NetConn_CloseClientPorts();
1174 if (cl_numsockets == 0)
1175 NetConn_OpenClientPorts();
1178 if (hostport != sv_netport.integer)
1180 hostport = sv_netport.integer;
1182 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1185 for (j = 0;j < MAX_RCONS;j++)
1187 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1188 if(cls.rcon_commands[i][0])
1190 if(realtime > cls.rcon_timeout[i])
1193 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1194 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1195 cls.rcon_commands[i][0] = 0;
1203 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1205 int originallength = (int)length;
1206 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1207 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1208 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1212 if (protocol == PROTOCOL_QUAKEWORLD)
1214 unsigned int sequence, sequence_ack;
1215 qboolean reliable_ack, reliable_message;
1219 sequence = LittleLong(*((int *)(data + 0)));
1220 sequence_ack = LittleLong(*((int *)(data + 4)));
1224 if (conn != cls.netcon)
1229 // 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?)
1230 //qport = LittleShort(*((int *)(data + 8)));
1235 conn->packetsReceived++;
1236 reliable_message = (sequence >> 31) != 0;
1237 reliable_ack = (sequence_ack >> 31) != 0;
1238 sequence &= ~(1<<31);
1239 sequence_ack &= ~(1<<31);
1240 if (sequence <= conn->qw.incoming_sequence)
1242 //Con_DPrint("Got a stale datagram\n");
1245 count = sequence - (conn->qw.incoming_sequence + 1);
1248 conn->droppedDatagrams += count;
1249 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1250 // If too may packets have been dropped, only write the
1251 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1252 // Because there's no point in writing more than
1253 // these as the netgraph is going to be full anyway.
1254 if (count > NETGRAPH_PACKETS)
1255 count = NETGRAPH_PACKETS;
1258 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1259 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1260 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1261 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1262 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1263 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1266 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1267 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1268 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1269 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1270 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1271 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1272 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1274 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1275 if (net_test.integer)
1277 if (conn->cleartime < realtime)
1278 conn->cleartime = realtime;
1281 if (reliable_ack == conn->qw.reliable_sequence)
1283 // received, now we will be able to send another reliable message
1284 conn->sendMessageLength = 0;
1285 conn->reliableMessagesReceived++;
1287 conn->qw.incoming_sequence = sequence;
1288 if (conn == cls.netcon)
1289 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1290 conn->qw.incoming_acknowledged = sequence_ack;
1291 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1292 if (reliable_message)
1293 conn->qw.incoming_reliable_sequence ^= 1;
1294 conn->lastMessageTime = realtime;
1295 conn->timeout = realtime + newtimeout;
1296 conn->unreliableMessagesReceived++;
1297 if (conn == cls.netcon)
1299 SZ_Clear(&cl_message);
1300 SZ_Write(&cl_message, data, (int)length);
1301 MSG_BeginReading(&cl_message);
1305 SZ_Clear(&sv_message);
1306 SZ_Write(&sv_message, data, (int)length);
1307 MSG_BeginReading(&sv_message);
1315 unsigned int sequence;
1320 originallength = (int)length;
1321 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1327 qlength = (unsigned int)BuffBigLong(data);
1328 flags = qlength & ~NETFLAG_LENGTH_MASK;
1329 qlength &= NETFLAG_LENGTH_MASK;
1330 // control packets were already handled
1331 if (!(flags & NETFLAG_CTL) && qlength == length)
1333 sequence = BuffBigLong(data + 4);
1334 conn->packetsReceived++;
1337 if (flags & NETFLAG_UNRELIABLE)
1339 if (sequence >= conn->nq.unreliableReceiveSequence)
1341 if (sequence > conn->nq.unreliableReceiveSequence)
1343 count = sequence - conn->nq.unreliableReceiveSequence;
1344 conn->droppedDatagrams += count;
1345 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1346 // If too may packets have been dropped, only write the
1347 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1348 // Because there's no point in writing more than
1349 // these as the netgraph is going to be full anyway.
1350 if (count > NETGRAPH_PACKETS)
1351 count = NETGRAPH_PACKETS;
1354 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1355 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1356 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1357 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1358 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1359 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1362 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1363 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1364 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1365 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1366 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1367 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1368 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1370 conn->nq.unreliableReceiveSequence = sequence + 1;
1371 conn->lastMessageTime = realtime;
1372 conn->timeout = realtime + newtimeout;
1373 conn->unreliableMessagesReceived++;
1376 if (conn == cls.netcon)
1378 SZ_Clear(&cl_message);
1379 SZ_Write(&cl_message, data, (int)length);
1380 MSG_BeginReading(&cl_message);
1384 SZ_Clear(&sv_message);
1385 SZ_Write(&sv_message, data, (int)length);
1386 MSG_BeginReading(&sv_message);
1392 // Con_DPrint("Got a stale datagram\n");
1395 else if (flags & NETFLAG_ACK)
1397 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1398 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1400 if (sequence == (conn->nq.sendSequence - 1))
1402 if (sequence == conn->nq.ackSequence)
1404 conn->nq.ackSequence++;
1405 if (conn->nq.ackSequence != conn->nq.sendSequence)
1406 Con_DPrint("ack sequencing error\n");
1407 conn->lastMessageTime = realtime;
1408 conn->timeout = realtime + newtimeout;
1409 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1411 unsigned int packetLen;
1412 unsigned int dataLen;
1415 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1416 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1418 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1420 dataLen = conn->sendMessageLength;
1425 dataLen = MAX_PACKETFRAGMENT;
1429 packetLen = NET_HEADERSIZE + dataLen;
1431 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1432 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1433 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1435 conn->nq.sendSequence++;
1437 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1438 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1440 conn->lastSendTime = realtime;
1441 conn->packetsSent++;
1445 conn->sendMessageLength = 0;
1448 // Con_DPrint("Duplicate ACK received\n");
1451 // Con_DPrint("Stale ACK received\n");
1454 else if (flags & NETFLAG_DATA)
1456 unsigned char temppacket[8];
1457 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1458 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1460 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1462 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1463 StoreBigLong(temppacket + 4, sequence);
1464 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1466 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1467 if (sequence == conn->nq.receiveSequence)
1469 conn->lastMessageTime = realtime;
1470 conn->timeout = realtime + newtimeout;
1471 conn->nq.receiveSequence++;
1472 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1473 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1474 conn->receiveMessageLength += (int)length;
1476 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1477 "Dropping the message!\n", sequence );
1478 conn->receiveMessageLength = 0;
1481 if (flags & NETFLAG_EOM)
1483 conn->reliableMessagesReceived++;
1484 length = conn->receiveMessageLength;
1485 conn->receiveMessageLength = 0;
1488 if (conn == cls.netcon)
1490 SZ_Clear(&cl_message);
1491 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1492 MSG_BeginReading(&cl_message);
1496 SZ_Clear(&sv_message);
1497 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1498 MSG_BeginReading(&sv_message);
1505 conn->receivedDuplicateCount++;
1513 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1516 cls.connect_trying = false;
1518 M_Update_Return_Reason("");
1520 // if we're connecting to a remote server, shut down any local server
1521 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1523 SV_LockThreadMutex();
1524 Host_ShutdownServer ();
1525 SV_UnlockThreadMutex();
1527 // allocate a net connection to keep track of things
1528 cls.netcon = NetConn_Open(mysocket, peeraddress);
1529 crypto = &cls.netcon->crypto;
1530 if(cls.crypto.authenticated)
1532 Crypto_FinishInstance(crypto, &cls.crypto);
1533 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1534 crypto->use_aes ? "Encrypted" : "Authenticated",
1535 cls.netcon->address,
1536 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1537 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1538 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1539 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1540 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1541 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1544 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1545 key_dest = key_game;
1549 cls.demonum = -1; // not in the demo loop now
1550 cls.state = ca_connected;
1551 cls.signon = 0; // need all the signon messages before playing
1552 cls.protocol = initialprotocol;
1553 // reset move sequence numbering on this new connection
1554 cls.servermovesequence = 0;
1555 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1556 Cmd_ForwardStringToServer("new");
1557 if (cls.protocol == PROTOCOL_QUAKE)
1559 // write a keepalive (clc_nop) as it seems to greatly improve the
1560 // chances of connecting to a netquake server
1562 unsigned char buf[4];
1563 memset(&msg, 0, sizeof(msg));
1565 msg.maxsize = sizeof(buf);
1566 MSG_WriteChar(&msg, clc_nop);
1567 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1571 int NetConn_IsLocalGame(void)
1573 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1579 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1583 serverlist_entry_t *entry = NULL;
1585 // search the cache for this server and update it
1586 for (n = 0;n < serverlist_cachecount;n++) {
1587 entry = &serverlist_cache[ n ];
1588 if (!strcmp(addressstring, entry->info.cname))
1592 if (n == serverlist_cachecount)
1594 // LAN search doesnt require an answer from the master server so we wont
1595 // know the ping nor will it be initialized already...
1598 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1601 if (serverlist_maxcachecount <= serverlist_cachecount)
1603 serverlist_maxcachecount += 64;
1604 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1606 entry = &serverlist_cache[n];
1608 memset(entry, 0, sizeof(*entry));
1609 // store the data the engine cares about (address and ping)
1610 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1611 entry->info.ping = 100000;
1612 entry->querytime = realtime;
1613 // if not in the slist menu we should print the server to console
1614 if (serverlist_consoleoutput)
1615 Con_Printf("querying %s\n", addressstring);
1616 ++serverlist_cachecount;
1618 // if this is the first reply from this server, count it as having replied
1619 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1620 pingtime = bound(0, pingtime, 9999);
1621 if (entry->query == SQS_REFRESHING) {
1622 entry->info.ping = pingtime;
1623 entry->query = SQS_QUERIED;
1625 // convert to unsigned to catch the -1
1626 // 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]
1627 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1631 // other server info is updated by the caller
1635 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1637 serverlist_entry_t *entry = &serverlist_cache[n];
1638 serverlist_info_t *info = &entry->info;
1639 // update description strings for engine menu and console output
1640 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);
1641 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1643 info->gameversion != gameversion.integer
1646 gameversion_min.integer >= 0 // min/max range set by user/mod?
1647 && gameversion_max.integer >= 0
1648 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1649 && gameversion_max.integer >= info->gameversion
1652 info->mod, info->map);
1653 if (entry->query == SQS_QUERIED)
1655 if(!serverlist_paused)
1656 ServerList_ViewList_Remove(entry);
1658 // if not in the slist menu we should print the server to console (if wanted)
1659 else if( serverlist_consoleoutput )
1660 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1661 // and finally, update the view set
1662 if(!serverlist_paused)
1663 ServerList_ViewList_Insert( entry );
1664 // update the entry's state
1665 serverlist_cache[n].query = SQS_QUERIED;
1668 // returns true, if it's sensible to continue the processing
1669 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1671 serverlist_entry_t *entry;
1673 // ignore the rest of the message if the serverlist is full
1674 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1676 // also ignore it if we have already queried it (other master server response)
1677 for( n = 0 ; n < serverlist_cachecount ; n++ )
1678 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1681 if( n < serverlist_cachecount ) {
1682 // the entry has already been queried once or
1686 if (serverlist_maxcachecount <= n)
1688 serverlist_maxcachecount += 64;
1689 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1692 entry = &serverlist_cache[n];
1694 memset(entry, 0, sizeof(*entry));
1695 entry->protocol = protocol;
1696 // store the data the engine cares about (address and ping)
1697 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1699 entry->info.isfavorite = isfavorite;
1701 // no, then reset the ping right away
1702 entry->info.ping = -1;
1703 // we also want to increase the serverlist_cachecount then
1704 serverlist_cachecount++;
1707 entry->query = SQS_QUERYING;
1712 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1715 if (serverlist_consoleoutput)
1716 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1719 char ipstring [128];
1722 if (data[0] == '\\')
1724 unsigned short port = data[5] * 256 + data[6];
1726 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1727 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1729 // move on to next address in packet
1734 else if (data[0] == '/' && isextended && length >= 19)
1736 unsigned short port = data[17] * 256 + data[18];
1744 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1746 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1749 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1750 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1751 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1757 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1758 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1759 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1764 // move on to next address in packet
1770 Con_Print("Error while parsing the server list\n");
1774 if (serverlist_consoleoutput && developer_networking.integer)
1775 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1777 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1783 // begin or resume serverlist queries
1784 serverlist_querysleep = false;
1785 serverlist_querywaittime = realtime + 3;
1789 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1791 qboolean fromserver;
1793 char *string, addressstring2[128];
1794 char stringbuf[16384];
1795 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1798 char infostringvalue[MAX_INPUTLINE];
1803 // quakeworld ingame packet
1804 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1806 // convert the address to a string incase we need it
1807 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1809 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1811 // received a command string - strip off the packaging and put it
1812 // into our string buffer with NULL termination
1815 length = min(length, (int)sizeof(stringbuf) - 1);
1816 memcpy(stringbuf, data, length);
1817 stringbuf[length] = 0;
1820 if (developer_networking.integer)
1822 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1823 Com_HexDumpToConsole(data, length);
1826 sendlength = sizeof(senddata) - 4;
1827 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1829 case CRYPTO_NOMATCH:
1835 memcpy(senddata, "\377\377\377\377", 4);
1836 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1839 case CRYPTO_DISCARD:
1842 memcpy(senddata, "\377\377\377\377", 4);
1843 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1847 case CRYPTO_REPLACE:
1848 string = senddata+4;
1849 length = (int)sendlength;
1853 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1856 for (j = 0;j < MAX_RCONS;j++)
1858 // note: this value from i is used outside the loop too...
1859 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1860 if(cls.rcon_commands[i][0])
1861 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1870 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1871 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1873 e = strchr(rcon_password.string, ' ');
1874 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1876 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1880 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1881 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1882 cls.rcon_commands[i][0] = 0;
1885 for (k = 0;k < MAX_RCONS;k++)
1886 if(cls.rcon_commands[k][0])
1887 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1892 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1893 // extend the timeout on other requests as we asked for a challenge
1894 for (l = 0;l < MAX_RCONS;l++)
1895 if(cls.rcon_commands[l][0])
1896 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1897 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1900 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1904 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1906 // darkplaces or quake3
1907 char protocolnames[1400];
1908 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1909 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1910 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1913 Protocol_Names(protocolnames, sizeof(protocolnames));
1915 M_Update_Return_Reason("Got challenge response");
1917 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1918 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1919 // TODO: add userinfo stuff here instead of using NQ commands?
1920 memcpy(senddata, "\377\377\377\377", 4);
1921 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1922 NetConn_WriteString(mysocket, senddata, peeraddress);
1925 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1927 // darkplaces or quake3
1928 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1929 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1933 M_Update_Return_Reason("Accepted");
1935 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1938 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1940 char rejectreason[128];
1941 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1942 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1945 cls.connect_trying = false;
1947 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1948 memcpy(rejectreason, string, length);
1949 rejectreason[length] = 0;
1951 M_Update_Return_Reason(rejectreason);
1956 if(key_dest != key_game)
1958 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1960 serverlist_info_t *info;
1965 // search the cache for this server and update it
1966 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1970 info = &serverlist_cache[n].info;
1975 info->qcstatus[0] = 0;
1976 info->players[0] = 0;
1977 info->protocol = -1;
1978 info->numplayers = 0;
1980 info->maxplayers = 0;
1981 info->gameversion = 0;
1983 p = strchr(string, '\n');
1986 *p = 0; // cut off the string there
1990 Con_Printf("statusResponse without players block?\n");
1992 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1993 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1994 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1995 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1996 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1997 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1998 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1999 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2000 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2001 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2002 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2003 info->numhumans = info->numplayers - max(0, info->numbots);
2004 info->freeslots = info->maxplayers - info->numplayers;
2006 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2010 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2012 serverlist_info_t *info;
2016 // search the cache for this server and update it
2017 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2021 info = &serverlist_cache[n].info;
2026 info->qcstatus[0] = 0;
2027 info->players[0] = 0;
2028 info->protocol = -1;
2029 info->numplayers = 0;
2031 info->maxplayers = 0;
2032 info->gameversion = 0;
2034 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2035 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2036 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2037 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2038 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2039 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2040 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2041 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2042 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2043 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2044 info->numhumans = info->numplayers - max(0, info->numbots);
2045 info->freeslots = info->maxplayers - info->numplayers;
2047 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2051 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2053 // Extract the IP addresses
2056 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2059 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2061 // Extract the IP addresses
2064 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2067 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2069 // Extract the IP addresses
2073 if (serverlist_consoleoutput)
2074 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2075 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2077 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2078 if (serverlist_consoleoutput && developer_networking.integer)
2079 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2081 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2085 // move on to next address in packet
2089 // begin or resume serverlist queries
2090 serverlist_querysleep = false;
2091 serverlist_querywaittime = realtime + 3;
2096 if (!strncmp(string, "extResponse ", 12))
2098 ++cl_net_extresponse_count;
2099 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2100 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2101 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2102 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2105 if (!strncmp(string, "ping", 4))
2107 if (developer_extra.integer)
2108 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2109 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2112 if (!strncmp(string, "ack", 3))
2114 // QuakeWorld compatibility
2115 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2117 // challenge message
2118 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2119 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2122 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2124 M_Update_Return_Reason("Got QuakeWorld challenge response");
2126 cls.qw_qport = qport.integer;
2127 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2128 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2129 memcpy(senddata, "\377\377\377\377", 4);
2130 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);
2131 NetConn_WriteString(mysocket, senddata, peeraddress);
2134 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2137 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2138 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2142 M_Update_Return_Reason("QuakeWorld Accepted");
2144 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2147 if (length > 2 && !memcmp(string, "n\\", 2))
2150 serverlist_info_t *info;
2154 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2155 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2158 // search the cache for this server and update it
2159 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2163 info = &serverlist_cache[n].info;
2164 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2165 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2166 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2167 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2169 info->numplayers = 0; // updated below
2170 info->numhumans = 0; // updated below
2171 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2172 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2174 // count active players on server
2175 // (we could gather more info, but we're just after the number)
2176 s = strchr(string, '\n');
2180 while (s < string + length)
2182 for (;s < string + length && *s != '\n';s++)
2184 if (s >= string + length)
2192 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2196 if (string[0] == 'n')
2198 // qw print command, used by rcon replies too
2199 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2200 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2203 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2205 // we may not have liked the packet, but it was a command packet, so
2206 // we're done processing this packet now
2209 // quakeworld ingame packet
2210 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2213 CL_ParseServerMessage();
2216 // netquake control packets, supported for compatibility only
2217 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2221 serverlist_info_t *info;
2226 SZ_Clear(&cl_message);
2227 SZ_Write(&cl_message, data, length);
2228 MSG_BeginReading(&cl_message);
2229 c = MSG_ReadByte(&cl_message);
2233 if (developer_extra.integer)
2234 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2235 if (cls.connect_trying)
2237 lhnetaddress_t clientportaddress;
2238 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2239 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2242 clientportaddress = *peeraddress;
2243 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2244 // extra ProQuake stuff
2246 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2248 cls.proquake_servermod = 0;
2250 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2252 cls.proquake_serverversion = 0;
2254 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2256 cls.proquake_serverflags = 0;
2257 if (cls.proquake_servermod == 1)
2258 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2259 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2260 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2262 M_Update_Return_Reason("Accepted");
2264 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2268 if (developer_extra.integer) {
2269 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2272 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2274 cls.connect_trying = false;
2276 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2279 case CCREP_SERVER_INFO:
2280 if (developer_extra.integer)
2281 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2283 // LadyHavoc: because the quake server may report weird addresses
2284 // we just ignore it and keep the real address
2285 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2286 // search the cache for this server and update it
2287 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2291 info = &serverlist_cache[n].info;
2292 strlcpy(info->game, "Quake", sizeof(info->game));
2293 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2294 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2295 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2296 info->numplayers = MSG_ReadByte(&cl_message);
2297 info->maxplayers = MSG_ReadByte(&cl_message);
2298 info->protocol = MSG_ReadByte(&cl_message);
2300 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2303 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2304 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2305 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2308 if (developer_extra.integer)
2309 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2311 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2313 case CCREP_PLAYER_INFO:
2314 // we got a CCREP_PLAYER_INFO??
2315 //if (developer_extra.integer)
2316 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2318 case CCREP_RULE_INFO:
2319 // we got a CCREP_RULE_INFO??
2320 //if (developer_extra.integer)
2321 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2326 SZ_Clear(&cl_message);
2327 // we may not have liked the packet, but it was a valid control
2328 // packet, so we're done processing this packet now
2332 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2333 CL_ParseServerMessage();
2338 void NetConn_QueryQueueFrame(void)
2344 static double querycounter = 0;
2346 if(!net_slist_pause.integer && serverlist_paused)
2347 ServerList_RebuildViewList();
2348 serverlist_paused = net_slist_pause.integer != 0;
2350 if (serverlist_querysleep)
2353 // apply a cool down time after master server replies,
2354 // to avoid messing up the ping times on the servers
2355 if (serverlist_querywaittime > realtime)
2358 // each time querycounter reaches 1.0 issue a query
2359 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2360 maxqueries = (int)querycounter;
2361 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2362 querycounter -= maxqueries;
2364 if( maxqueries == 0 ) {
2368 // scan serverlist and issue queries as needed
2369 serverlist_querysleep = true;
2371 timeouttime = realtime - net_slist_timeout.value;
2372 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2374 serverlist_entry_t *entry = &serverlist_cache[ index ];
2375 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2380 serverlist_querysleep = false;
2381 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2386 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2388 lhnetaddress_t address;
2391 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2392 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2394 for (socket = 0; socket < cl_numsockets ; socket++)
2395 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2399 for (socket = 0; socket < cl_numsockets ; socket++)
2400 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2403 // update the entry fields
2404 entry->querytime = realtime;
2405 entry->querycounter++;
2407 // if not in the slist menu we should print the server to console
2408 if (serverlist_consoleoutput)
2409 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2415 // have we tried to refresh this server?
2416 if( entry->query == SQS_REFRESHING ) {
2417 // yes, so update the reply count (since its not responding anymore)
2419 if(!serverlist_paused)
2420 ServerList_ViewList_Remove(entry);
2422 entry->query = SQS_TIMEDOUT;
2428 void NetConn_ClientFrame(void)
2431 lhnetaddress_t peeraddress;
2432 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2433 NetConn_UpdateSockets();
2434 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2437 if (cls.connect_remainingtries == 0)
2438 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2440 cls.connect_nextsendtime = realtime + 1;
2441 cls.connect_remainingtries--;
2442 if (cls.connect_remainingtries <= -10)
2444 cls.connect_trying = false;
2446 M_Update_Return_Reason("Connect: Failed");
2450 // try challenge first (newer DP server or QW)
2451 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2452 // then try netquake as a fallback (old server, or netquake)
2453 SZ_Clear(&cl_message);
2454 // save space for the header, filled in later
2455 MSG_WriteLong(&cl_message, 0);
2456 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2457 MSG_WriteString(&cl_message, "QUAKE");
2458 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2459 // extended proquake stuff
2460 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2461 // this version matches ProQuake 3.40, the first version to support
2462 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2463 // higher clients, so we pretend we are that version...
2464 MSG_WriteByte(&cl_message, 34); // version * 10
2465 MSG_WriteByte(&cl_message, 0); // flags
2466 MSG_WriteLong(&cl_message, 0); // password
2467 // write the packetsize now...
2468 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2469 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2470 SZ_Clear(&cl_message);
2472 for (i = 0;i < cl_numsockets;i++)
2474 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2476 // R_TimeReport("clientreadnetwork");
2477 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2478 // R_TimeReport("clientparsepacket");
2482 NetConn_QueryQueueFrame();
2484 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2486 Con_Print("Connection timed out\n");
2488 SV_LockThreadMutex();
2489 Host_ShutdownServer ();
2490 SV_UnlockThreadMutex();
2494 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2498 for (i = 0;i < bufferlength - 1;i++)
2502 c = rand () % (127 - 33) + 33;
2503 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2509 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2510 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2512 prvm_prog_t *prog = SVVM_prog;
2514 unsigned int nb_clients = 0, nb_bots = 0, i;
2517 const char *crypto_idstring;
2518 const char *worldstatusstr;
2520 // How many clients are there?
2521 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2523 if (svs.clients[i].active)
2526 if (!svs.clients[i].netconnection)
2532 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2533 if(worldstatusstr && *worldstatusstr)
2538 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2539 if(*q != '\\' && *q != '\n')
2544 /// \TODO: we should add more information for the full status string
2545 crypto_idstring = Crypto_GetInfoResponseDataString();
2546 length = dpsnprintf(out_msg, out_size,
2547 "\377\377\377\377%s\x0A"
2548 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2549 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2554 fullstatus ? "statusResponse" : "infoResponse",
2555 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2556 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2557 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2558 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2559 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2560 fullstatus ? "\n" : "");
2562 // Make sure it fits in the buffer
2572 savelength = length;
2574 ptr = out_msg + length;
2575 left = (int)out_size - length;
2577 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2579 client_t *client = &svs.clients[i];
2582 int nameind, cleanind, pingvalue;
2584 char cleanname [sizeof(client->name)];
2585 const char *statusstr;
2588 // Remove all characters '"' and '\' in the player name
2593 curchar = client->name[nameind++];
2594 if (curchar != '"' && curchar != '\\')
2596 cleanname[cleanind++] = curchar;
2597 if (cleanind == sizeof(cleanname) - 1)
2600 } while (curchar != '\0');
2601 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2603 pingvalue = (int)(client->ping * 1000.0f);
2604 if(client->netconnection)
2605 pingvalue = bound(1, pingvalue, 9999);
2610 ed = PRVM_EDICT_NUM(i + 1);
2611 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2612 if(statusstr && *statusstr)
2617 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2618 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2623 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2625 if(client->frags == -666) // spectator
2626 strlcpy(teambuf, " 0", sizeof(teambuf));
2627 else if(client->colors == 0x44) // red team
2628 strlcpy(teambuf, " 1", sizeof(teambuf));
2629 else if(client->colors == 0xDD) // blue team
2630 strlcpy(teambuf, " 2", sizeof(teambuf));
2631 else if(client->colors == 0xCC) // yellow team
2632 strlcpy(teambuf, " 3", sizeof(teambuf));
2633 else if(client->colors == 0x99) // pink team
2634 strlcpy(teambuf, " 4", sizeof(teambuf));
2636 strlcpy(teambuf, " 0", sizeof(teambuf));
2641 // note: team number is inserted according to SoF2 protocol
2643 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2649 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2658 // turn it into an infoResponse!
2659 out_msg[savelength] = 0;
2660 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2661 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2676 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2678 size_t floodslotnum, bestfloodslotnum;
2679 double bestfloodtime;
2680 lhnetaddress_t noportpeeraddress;
2681 // see if this is a connect flood
2682 noportpeeraddress = *peeraddress;
2683 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2684 bestfloodslotnum = 0;
2685 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2686 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2688 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2690 bestfloodtime = floodlist[floodslotnum].lasttime;
2691 bestfloodslotnum = floodslotnum;
2693 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2695 // this address matches an ongoing flood address
2696 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2700 // renew the ban on this address so it does not expire
2701 // until the flood has subsided
2702 floodlist[floodslotnum].lasttime = realtime;
2704 //Con_Printf("Flood detected!\n");
2707 // the flood appears to have subsided, so allow this
2708 bestfloodslotnum = floodslotnum; // reuse the same slot
2712 // begin a new timeout on this address
2713 floodlist[bestfloodslotnum].address = noportpeeraddress;
2714 floodlist[bestfloodslotnum].lasttime = realtime;
2715 //Con_Printf("Flood detection initiated!\n");
2719 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2721 size_t floodslotnum;
2722 lhnetaddress_t noportpeeraddress;
2723 // see if this is a connect flood
2724 noportpeeraddress = *peeraddress;
2725 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2726 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2728 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2730 // this address matches an ongoing flood address
2732 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2733 floodlist[floodslotnum].lasttime = 0;
2734 //Con_Printf("Flood cleared!\n");
2739 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2741 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2747 Con_Error("LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2751 t1 = (long) time(NULL);
2752 t2 = strtol(s, NULL, 0);
2753 if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2756 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2759 return !memcmp(mdfourbuf, hash, 16);
2762 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2768 Con_Error("LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2772 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2775 // validate the challenge
2776 for (i = 0;i < MAX_CHALLENGES;i++)
2777 if(challenges[i].time > 0)
2778 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2780 // if the challenge is not recognized, drop the packet
2781 if (i == MAX_CHALLENGES)
2784 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2787 if(memcmp(mdfourbuf, hash, 16))
2790 // unmark challenge to prevent replay attacks
2791 challenges[i].time = 0;
2796 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2799 Con_Error("LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2803 return !strcmp(password, hash);
2806 /// returns a string describing the user level, or NULL for auth failure
2807 static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
2809 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2810 static char buf[MAX_INPUTLINE];
2812 qboolean restricted = false;
2813 qboolean have_usernames = false;
2814 static char vabuf[1024];
2816 userpass_start = rcon_password.string;
2817 while((userpass_end = strchr(userpass_start, ' ')))
2819 have_usernames = true;
2820 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2821 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2822 if(comparator(peeraddress, buf, password, cs, cslen))
2824 userpass_start = userpass_end + 1;
2826 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2828 userpass_end = userpass_start + strlen(userpass_start);
2829 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2834 have_usernames = false;
2835 userpass_start = rcon_restricted_password.string;
2836 while((userpass_end = strchr(userpass_start, ' ')))
2838 have_usernames = true;
2839 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2840 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2841 if(comparator(peeraddress, buf, password, cs, cslen))
2843 userpass_start = userpass_end + 1;
2845 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2847 userpass_end = userpass_start + strlen(userpass_start);
2848 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2852 return NULL; // DENIED
2855 for(text = s; text != endpos; ++text)
2856 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2857 return NULL; // block possible exploits against the parser/alias expansion
2861 size_t l = strlen(s);
2864 hasquotes = (strchr(s, '"') != NULL);
2865 // sorry, we can't allow these substrings in wildcard expressions,
2866 // as they can mess with the argument counts
2867 text = rcon_restricted_commands.string;
2868 while(COM_ParseToken_Console(&text))
2870 // com_token now contains a pattern to check for...
2871 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2874 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2877 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2879 if(!strcmp(com_token, s))
2882 else // single-arg expression? must match the beginning of the command
2884 if(!strcmp(com_token, s))
2886 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2890 // if we got here, nothing matched!
2898 userpass_startpass = strchr(userpass_start, ':');
2899 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2900 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2902 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2905 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2909 // looks like a legitimate rcon command with the correct password
2910 const char *s_ptr = s;
2911 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2912 while(s_ptr != endpos)
2914 size_t l = strlen(s_ptr);
2916 Con_Printf(" %s;", s_ptr);
2921 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2922 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2925 size_t l = strlen(s);
2928 client_t *host_client_save = host_client;
2929 Cmd_ExecuteString(&cmd_server, s, src_command, true);
2930 host_client = host_client_save;
2931 // in case it is a command that changes host_client (like restart)
2935 Con_Rcon_Redirect_End();
2939 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2943 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2945 int i, ret, clientnum, best;
2947 char *string, response[2800], addressstring2[128];
2948 static char stringbuf[16384]; // server only
2949 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2950 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2951 size_t sendlength, response_len;
2952 char infostringvalue[MAX_INPUTLINE];
2957 // convert the address to a string incase we need it
2958 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2960 // see if we can identify the sender as a local player
2961 // (this is necessary for rcon to send a reliable reply if the client is
2962 // actually on the server, not sending remotely)
2963 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2964 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2966 if (i == svs.maxclients)
2969 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2971 // received a command string - strip off the packaging and put it
2972 // into our string buffer with NULL termination
2975 length = min(length, (int)sizeof(stringbuf) - 1);
2976 memcpy(stringbuf, data, length);
2977 stringbuf[length] = 0;
2980 if (developer_extra.integer)
2982 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2983 Com_HexDumpToConsole(data, length);
2986 sendlength = sizeof(senddata) - 4;
2987 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2989 case CRYPTO_NOMATCH:
2995 memcpy(senddata, "\377\377\377\377", 4);
2996 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2999 case CRYPTO_DISCARD:
3002 memcpy(senddata, "\377\377\377\377", 4);
3003 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3007 case CRYPTO_REPLACE:
3008 string = senddata+4;
3009 length = (int)sendlength;
3013 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3015 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
3017 if(challenges[i].time > 0)
3018 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3020 if (besttime > challenges[i].time)
3021 besttime = challenges[best = i].time;
3023 // if we did not find an exact match, choose the oldest and
3024 // update address and string
3025 if (i == MAX_CHALLENGES)
3028 challenges[i].address = *peeraddress;
3029 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3033 // flood control: drop if requesting challenge too often
3034 if(challenges[i].time > realtime - net_challengefloodblockingtimeout.value)
3037 challenges[i].time = realtime;
3038 // send the challenge
3039 memcpy(response, "\377\377\377\377", 4);
3040 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3041 response_len = strlen(response) + 1;
3042 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3043 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3046 if (length > 8 && !memcmp(string, "connect\\", 8))
3050 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3054 if(crypto && crypto->authenticated)
3056 // no need to check challenge
3057 if(crypto_developer.integer)
3059 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3060 crypto->use_aes ? "Encrypted" : "Authenticated",
3062 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3063 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3064 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3065 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3066 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3067 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3073 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3075 // validate the challenge
3076 for (i = 0;i < MAX_CHALLENGES;i++)
3077 if(challenges[i].time > 0)
3078 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3080 // if the challenge is not recognized, drop the packet
3081 if (i == MAX_CHALLENGES)
3086 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3087 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3089 if(!(islocal || sv_public.integer > -2))
3091 if (developer_extra.integer)
3092 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3093 memcpy(response, "\377\377\377\377", 4);
3094 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3095 NetConn_WriteString(mysocket, response, peeraddress);
3099 // check engine protocol
3100 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3102 if (developer_extra.integer)
3103 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3104 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3108 // see if this is a duplicate connection request or a disconnected
3109 // client who is rejoining to the same client slot
3110 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3112 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3114 // this is a known client...
3115 if(crypto && crypto->authenticated)
3117 // reject if changing key!
3118 if(client->netconnection->crypto.authenticated)
3121 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3123 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3125 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3127 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3130 if (developer_extra.integer)
3131 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3132 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3139 // reject if downgrading!
3140 if(client->netconnection->crypto.authenticated)
3142 if (developer_extra.integer)
3143 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3144 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3150 // client crashed and is coming back,
3151 // keep their stuff intact
3152 if (developer_extra.integer)
3153 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3154 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3155 if(crypto && crypto->authenticated)
3156 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3157 SV_SendServerinfo(client);
3161 // client is still trying to connect,
3162 // so we send a duplicate reply
3163 if (developer_extra.integer)
3164 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3165 if(crypto && crypto->authenticated)
3166 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3167 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3173 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3176 // find an empty client slot for this new client
3177 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3180 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3182 // allocated connection
3183 if (developer_extra.integer)
3184 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3185 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3186 // now set up the client
3187 if(crypto && crypto->authenticated)
3188 Crypto_FinishInstance(&conn->crypto, crypto);
3189 SV_ConnectClient(clientnum, conn);
3190 NetConn_Heartbeat(1);
3195 // no empty slots found - server is full
3196 if (developer_extra.integer)
3197 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3198 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3202 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3204 const char *challenge = NULL;
3206 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3209 // If there was a challenge in the getinfo message
3210 if (length > 8 && string[7] == ' ')
3211 challenge = string + 8;
3213 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3215 if (developer_extra.integer)
3216 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3217 NetConn_WriteString(mysocket, response, peeraddress);
3221 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3223 const char *challenge = NULL;
3225 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3228 // If there was a challenge in the getinfo message
3229 if (length > 10 && string[9] == ' ')
3230 challenge = string + 10;
3232 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3234 if (developer_extra.integer)
3235 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3236 NetConn_WriteString(mysocket, response, peeraddress);
3240 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3242 char *password = string + 20;
3243 char *timeval = string + 37;
3244 char *s = strchr(timeval, ' ');
3245 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3246 const char *userlevel;
3248 if(rcon_secure.integer > 1)
3252 return true; // invalid packet
3255 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3256 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3259 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3261 char *password = string + 25;
3262 char *challenge = string + 42;
3263 char *s = strchr(challenge, ' ');
3264 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3265 const char *userlevel;
3267 return true; // invalid packet
3270 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3271 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3274 if (length >= 5 && !memcmp(string, "rcon ", 5))
3277 char *s = string + 5;
3278 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3281 if(rcon_secure.integer > 0)
3284 for (j = 0;!ISWHITESPACE(*s);s++)
3285 if (j < (int)sizeof(password) - 1)
3287 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3290 if (!ISWHITESPACE(password[0]))
3292 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3293 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3297 if (!strncmp(string, "extResponse ", 12))
3299 ++sv_net_extresponse_count;
3300 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3301 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3302 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3303 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3306 if (!strncmp(string, "ping", 4))
3308 if (developer_extra.integer)
3309 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3310 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3313 if (!strncmp(string, "ack", 3))
3315 // we may not have liked the packet, but it was a command packet, so
3316 // we're done processing this packet now
3319 // netquake control packets, supported for compatibility only, and only
3320 // when running game protocols that are normally served via this connection
3322 // (this protects more modern protocols against being used for
3323 // Quake packet flood Denial Of Service attacks)
3324 if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) && !ENCRYPTION_REQUIRED)
3328 const char *protocolname;
3329 client_t *knownclient;
3330 client_t *newclient;
3333 SZ_Clear(&sv_message);
3334 SZ_Write(&sv_message, data, length);
3335 MSG_BeginReading(&sv_message);
3336 c = MSG_ReadByte(&sv_message);
3340 if (developer_extra.integer)
3341 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3342 if(!(islocal || sv_public.integer > -2))
3344 if (developer_extra.integer)
3345 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3346 SZ_Clear(&sv_message);
3347 // save space for the header, filled in later
3348 MSG_WriteLong(&sv_message, 0);
3349 MSG_WriteByte(&sv_message, CCREP_REJECT);
3350 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3351 MSG_WriteString(&sv_message, "\n");
3352 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3353 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3354 SZ_Clear(&sv_message);
3358 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3359 protocolnumber = MSG_ReadByte(&sv_message);
3360 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3362 if (developer_extra.integer)
3363 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3364 SZ_Clear(&sv_message);
3365 // save space for the header, filled in later
3366 MSG_WriteLong(&sv_message, 0);
3367 MSG_WriteByte(&sv_message, CCREP_REJECT);
3368 MSG_WriteString(&sv_message, "Incompatible version.\n");
3369 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3370 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3371 SZ_Clear(&sv_message);
3375 // see if this connect request comes from a known client
3376 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3378 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3380 // this is either a duplicate connection request
3381 // or coming back from a timeout
3382 // (if so, keep their stuff intact)
3384 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3385 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3387 if (developer_extra.integer)
3388 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3389 SZ_Clear(&sv_message);
3390 // save space for the header, filled in later
3391 MSG_WriteLong(&sv_message, 0);
3392 MSG_WriteByte(&sv_message, CCREP_REJECT);
3393 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3394 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3395 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3396 SZ_Clear(&sv_message);
3401 if (developer_extra.integer)
3402 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3403 SZ_Clear(&sv_message);
3404 // save space for the header, filled in later
3405 MSG_WriteLong(&sv_message, 0);
3406 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3407 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3408 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3409 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3410 SZ_Clear(&sv_message);
3412 // if client is already spawned, re-send the
3413 // serverinfo message as they'll need it to play
3414 if (knownclient->begun)
3415 SV_SendServerinfo(knownclient);
3420 // this is a new client, check for connection flood
3421 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3424 // find a slot for the new client
3425 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3428 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3430 // connect to the client
3431 // everything is allocated, just fill in the details
3432 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3433 if (developer_extra.integer)
3434 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3435 // send back the info about the server connection
3436 SZ_Clear(&sv_message);
3437 // save space for the header, filled in later
3438 MSG_WriteLong(&sv_message, 0);
3439 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3440 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3441 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3442 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3443 SZ_Clear(&sv_message);
3444 // now set up the client struct
3445 SV_ConnectClient(clientnum, conn);
3446 NetConn_Heartbeat(1);
3451 if (developer_extra.integer)
3452 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3453 // no room; try to let player know
3454 SZ_Clear(&sv_message);
3455 // save space for the header, filled in later
3456 MSG_WriteLong(&sv_message, 0);
3457 MSG_WriteByte(&sv_message, CCREP_REJECT);
3458 MSG_WriteString(&sv_message, "Server is full.\n");
3459 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3460 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3461 SZ_Clear(&sv_message);
3463 case CCREQ_SERVER_INFO:
3464 if (developer_extra.integer)
3465 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3466 if(!(islocal || sv_public.integer > -1))
3469 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3472 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3475 char myaddressstring[128];
3476 if (developer_extra.integer)
3477 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3478 SZ_Clear(&sv_message);
3479 // save space for the header, filled in later
3480 MSG_WriteLong(&sv_message, 0);
3481 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3482 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3483 MSG_WriteString(&sv_message, myaddressstring);
3484 MSG_WriteString(&sv_message, hostname.string);
3485 MSG_WriteString(&sv_message, sv.name);
3486 // How many clients are there?
3487 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3488 if (svs.clients[i].active)
3490 MSG_WriteByte(&sv_message, numclients);
3491 MSG_WriteByte(&sv_message, svs.maxclients);
3492 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3493 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3494 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3495 SZ_Clear(&sv_message);
3498 case CCREQ_PLAYER_INFO:
3499 if (developer_extra.integer)
3500 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3501 if(!(islocal || sv_public.integer > -1))
3504 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3509 int playerNumber, activeNumber, clientNumber;
3512 playerNumber = MSG_ReadByte(&sv_message);
3514 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3515 if (client->active && ++activeNumber == playerNumber)
3517 if (clientNumber != svs.maxclients)
3519 SZ_Clear(&sv_message);
3520 // save space for the header, filled in later
3521 MSG_WriteLong(&sv_message, 0);
3522 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3523 MSG_WriteByte(&sv_message, playerNumber);
3524 MSG_WriteString(&sv_message, client->name);
3525 MSG_WriteLong(&sv_message, client->colors);
3526 MSG_WriteLong(&sv_message, client->frags);
3527 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3528 if(sv_status_privacy.integer)
3529 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3531 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3532 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3533 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3534 SZ_Clear(&sv_message);
3538 case CCREQ_RULE_INFO:
3539 if (developer_extra.integer)
3540 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3541 if(!(islocal || sv_public.integer > -1))
3544 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3551 // find the search start location
3552 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3553 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CVAR_NOTIFY);
3555 // send the response
3556 SZ_Clear(&sv_message);
3557 // save space for the header, filled in later
3558 MSG_WriteLong(&sv_message, 0);
3559 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3562 MSG_WriteString(&sv_message, var->name);
3563 MSG_WriteString(&sv_message, var->string);
3565 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3566 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3567 SZ_Clear(&sv_message);
3571 if (developer_extra.integer)
3572 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3573 if (sv.active && !rcon_secure.integer)
3575 char password[2048];
3579 const char *userlevel;
3580 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3581 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3583 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3584 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3585 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3592 SZ_Clear(&sv_message);
3593 // we may not have liked the packet, but it was a valid control
3594 // packet, so we're done processing this packet now
3599 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3601 SV_ReadClientMessage();
3608 void NetConn_ServerFrame(void)
3611 lhnetaddress_t peeraddress;
3612 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3613 for (i = 0;i < sv_numsockets;i++)
3614 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3615 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3616 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3618 // never timeout loopback connections
3619 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3621 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3622 SV_DropClient(false);
3627 void NetConn_SleepMicroseconds(int microseconds)
3629 LHNET_SleepUntilPacket_Microseconds(microseconds);
3633 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3637 lhnetaddress_t masteraddress;
3638 lhnetaddress_t broadcastaddress;
3641 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3644 // 26000 is the default quake server port, servers on other ports will not
3646 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3647 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3651 for (i = 0;i < cl_numsockets;i++)
3655 const char *cmdname, *extraoptions;
3656 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3658 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3660 // search LAN for Quake servers
3661 SZ_Clear(&cl_message);
3662 // save space for the header, filled in later
3663 MSG_WriteLong(&cl_message, 0);
3664 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3665 MSG_WriteString(&cl_message, "QUAKE");
3666 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3667 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3668 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3669 SZ_Clear(&cl_message);
3671 // search LAN for DarkPlaces servers
3672 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3675 // build the getservers message to send to the dpmaster master servers
3676 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3678 cmdname = "getserversExt";
3679 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3683 cmdname = "getservers";
3686 memcpy(request, "\377\377\377\377", 4);
3687 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3690 for (masternum = 0;sv_masters[masternum].name;masternum++)
3692 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3695 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3699 // search favorite servers
3700 for(j = 0; j < nFavorites; ++j)
3702 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3704 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3705 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3712 // only query QuakeWorld servers when the user wants to
3715 for (i = 0;i < cl_numsockets;i++)
3719 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3721 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3723 // search LAN for QuakeWorld servers
3724 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3726 // build the getservers message to send to the qwmaster master servers
3727 // note this has no -1 prefix, and the trailing nul byte is sent
3728 dpsnprintf(request, sizeof(request), "c\n");
3732 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3734 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3736 if (m_state != m_slist)
3738 char lookupstring[128];
3739 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3740 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3743 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3747 // search favorite servers
3748 for(j = 0; j < nFavorites; ++j)
3750 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3752 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3754 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3755 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3762 if (!masterquerycount)
3764 Con_Error("Unable to query master servers, no suitable network sockets active.\n");
3765 M_Update_Return_Reason("No network");
3770 void NetConn_Heartbeat(int priority)
3772 lhnetaddress_t masteraddress;
3774 lhnetsocket_t *mysocket;
3776 // if it's a state change (client connected), limit next heartbeat to no
3777 // more than 30 sec in the future
3778 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3779 nextheartbeattime = realtime + 30.0;
3781 // limit heartbeatperiod to 30 to 270 second range,
3782 // lower limit is to avoid abusing master servers with excess traffic,
3783 // upper limit is to avoid timing out on the master server (which uses
3785 if (sv_heartbeatperiod.value < 30)
3786 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3787 if (sv_heartbeatperiod.value > 270)
3788 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3790 // make advertising optional and don't advertise singleplayer games, and
3791 // only send a heartbeat as often as the admin wants
3792 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3794 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3795 for (masternum = 0;sv_masters[masternum].name;masternum++)
3796 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3797 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3801 static void Net_Heartbeat_f(cmd_state_t *cmd)
3804 NetConn_Heartbeat(2);
3806 Con_Print("No server running, can not heartbeat to master server.\n");
3809 static void PrintStats(netconn_t *conn)
3811 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3812 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3814 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3815 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3816 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3817 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3818 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3819 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3820 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3821 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3822 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3823 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3826 void Net_Stats_f(cmd_state_t *cmd)
3829 Con_Print("connections =\n");
3830 for (conn = netconn_list;conn;conn = conn->next)
3835 void Net_Refresh_f(cmd_state_t *cmd)
3837 if (m_state != m_slist) {
3838 Con_Print("Sending new requests to master servers\n");
3839 ServerList_QueryList(false, true, false, true);
3840 Con_Print("Listening for replies...\n");
3842 ServerList_QueryList(false, true, false, false);
3845 void Net_Slist_f(cmd_state_t *cmd)
3847 ServerList_ResetMasks();
3848 serverlist_sortbyfield = SLIF_PING;
3849 serverlist_sortflags = 0;
3850 if (m_state != m_slist) {
3851 Con_Print("Sending requests to master servers\n");
3852 ServerList_QueryList(true, true, false, true);
3853 Con_Print("Listening for replies...\n");
3855 ServerList_QueryList(true, true, false, false);
3858 void Net_SlistQW_f(cmd_state_t *cmd)
3860 ServerList_ResetMasks();
3861 serverlist_sortbyfield = SLIF_PING;
3862 serverlist_sortflags = 0;
3863 if (m_state != m_slist) {
3864 Con_Print("Sending requests to master servers\n");
3865 ServerList_QueryList(true, false, true, true);
3866 serverlist_consoleoutput = true;
3867 Con_Print("Listening for replies...\n");
3869 ServerList_QueryList(true, false, true, false);
3873 void NetConn_Init(void)
3876 lhnetaddress_t tempaddress;
3877 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3878 Cmd_AddCommand(&cmd_client, "net_stats", Net_Stats_f, "print network statistics");
3879 Cmd_AddCommand(&cmd_server, "net_stats", Net_Stats_f, "print network statistics");
3881 Cmd_AddCommand(&cmd_client, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
3882 Cmd_AddCommand(&cmd_client, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3883 Cmd_AddCommand(&cmd_client, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3885 Cmd_AddCommand(&cmd_server, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3886 Cvar_RegisterVariable(&net_test);
3887 Cvar_RegisterVariable(&net_usesizelimit);
3888 Cvar_RegisterVariable(&net_burstreserve);
3889 Cvar_RegisterVariable(&rcon_restricted_password);
3890 Cvar_RegisterVariable(&rcon_restricted_commands);
3891 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3892 Cvar_RegisterVariable(&net_slist_queriespersecond);
3893 Cvar_RegisterVariable(&net_slist_queriesperframe);
3894 Cvar_RegisterVariable(&net_slist_timeout);
3895 Cvar_RegisterVariable(&net_slist_maxtries);
3896 Cvar_RegisterVariable(&net_slist_favorites);
3897 Cvar_RegisterVariable(&net_slist_pause);
3898 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3899 Cvar_RegisterVariable(&net_tos_dscp);
3900 Cvar_RegisterVariable(&net_messagetimeout);
3901 Cvar_RegisterVariable(&net_connecttimeout);
3902 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3903 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3904 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3905 Cvar_RegisterVariable(&net_sourceaddresscheck);
3906 Cvar_RegisterVariable(&net_fakelag);
3907 Cvar_RegisterVariable(&net_fakeloss_send);
3908 Cvar_RegisterVariable(&net_fakeloss_receive);
3909 Cvar_RegisterAlias(&cl_netlocalping, &net_fakelag);
3910 Cvar_RegisterAlias(&cl_netpacketloss_send, &net_fakeloss_send);
3911 Cvar_RegisterAlias(&cl_netpacketloss_receive, &net_fakeloss_receive);
3912 Cvar_RegisterVariable(&hostname);
3913 Cvar_RegisterVariable(&developer_networking);
3914 Cvar_RegisterVariable(&cl_netport);
3915 Cvar_RegisterVariable(&sv_netport);
3916 Cvar_RegisterVariable(&net_address);
3917 Cvar_RegisterVariable(&net_address_ipv6);
3918 Cvar_RegisterVariable(&sv_public);
3919 Cvar_RegisterVariable(&sv_public_rejectreason);
3920 Cvar_RegisterVariable(&sv_heartbeatperiod);
3921 for (i = 0;sv_masters[i].name;i++)
3922 Cvar_RegisterVariable(&sv_masters[i]);
3923 Cvar_RegisterVariable(&gameversion);
3924 Cvar_RegisterVariable(&gameversion_min);
3925 Cvar_RegisterVariable(&gameversion_max);
3926 // 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.
3927 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3929 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3931 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3932 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3935 Con_Errorf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3937 // 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
3938 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3940 i = atoi(com_argv[i + 1]);
3941 if (i >= 0 && i < 65536)
3943 Con_Printf("-port option used, setting port cvar to %i\n", i);
3944 Cvar_SetValueQuick(&sv_netport, i);
3947 Con_Errorf("-port option used, but %i is not a valid port number\n", i);
3951 cl_message.data = cl_message_buf;
3952 cl_message.maxsize = sizeof(cl_message_buf);
3953 cl_message.cursize = 0;
3954 sv_message.data = sv_message_buf;
3955 sv_message.maxsize = sizeof(sv_message_buf);
3956 sv_message.cursize = 0;
3958 if (Thread_HasThreads())
3959 netconn_mutex = Thread_CreateMutex();
3962 void NetConn_Shutdown(void)
3964 NetConn_CloseClientPorts();
3965 NetConn_CloseServerPorts();
3968 Thread_DestroyMutex(netconn_mutex);
3969 netconn_mutex = NULL;