2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
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 = {0, "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 = {0, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {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_SAVE, "sv_master1", "", "user-chosen master server 1"},
44 {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
45 {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
46 {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
47 {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
48 {0, "sv_masterextra2", "107.161.23.68", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
49 {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
51 {0, "sv_masterextra4", "[2a03:4000:2:225::51:334d]:27950", "dpmaster.sudo.rm-f.org - default master server 4 (admin: divVerent)"}, // admin: divVerent
52 {0, "sv_masterextra5", "[2604:180::4ac:98c1]:27950", "dpmaster.deathmask.net - default master server 5 ipv6 address of dpmaster.deathmask.net (admin: Willis)"}, // admin: Willis
58 static cvar_t sv_qwmasters [] =
60 {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
61 {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
62 {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
63 {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
64 {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
65 {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
66 {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
67 {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
68 {0, "sv_qwmasterextra5", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
73 static double nextheartbeattime = 0;
77 static unsigned char cl_message_buf[NET_MAXMESSAGE];
78 static unsigned char sv_message_buf[NET_MAXMESSAGE];
79 char cl_readstring[MAX_INPUTLINE];
80 char sv_readstring[MAX_INPUTLINE];
82 cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
83 cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
84 cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
85 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
86 cvar_t net_connecttimeout = {0, "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."};
87 cvar_t net_connectfloodblockingtimeout = {0, "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."};
88 cvar_t net_challengefloodblockingtimeout = {0, "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."};
89 cvar_t net_getstatusfloodblockingtimeout = {0, "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."};
90 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
91 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
93 cvar_t cl_netlocalping = {0, "cl_netlocalping","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
94 static cvar_t cl_netpacketloss_send = {0, "cl_netpacketloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
95 static cvar_t cl_netpacketloss_receive = {0, "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)"};
96 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
97 static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
98 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
99 static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
100 static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
101 static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
102 static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
103 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
104 static cvar_t gameversion_min = {0, "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"};
105 static cvar_t gameversion_max = {0, "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"};
106 static cvar_t rcon_restricted_password = {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"};
107 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
108 static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
109 extern cvar_t rcon_secure;
110 extern cvar_t rcon_secure_challengetimeout;
112 double masterquerytime = -1000;
113 int masterquerycount = 0;
114 int masterreplycount = 0;
115 int serverquerycount = 0;
116 int serverreplycount = 0;
118 challenge_t challenge[MAX_CHALLENGES];
121 /// this is only false if there are still servers left to query
122 static qboolean serverlist_querysleep = true;
123 static qboolean serverlist_paused = false;
124 /// this is pushed a second or two ahead of realtime whenever a master server
125 /// reply is received, to avoid issuing queries while master replies are still
126 /// flooding in (which would make a mess of the ping times)
127 static double serverlist_querywaittime = 0;
130 static int cl_numsockets;
131 static lhnetsocket_t *cl_sockets[16];
132 static int sv_numsockets;
133 static lhnetsocket_t *sv_sockets[16];
135 netconn_t *netconn_list = NULL;
136 mempool_t *netconn_mempool = NULL;
137 void *netconn_mutex = NULL;
139 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
140 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
141 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
142 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
144 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
145 int cl_net_extresponse_count = 0;
146 int cl_net_extresponse_last = 0;
148 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
149 int sv_net_extresponse_count = 0;
150 int sv_net_extresponse_last = 0;
153 // ServerList interface
154 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
155 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
157 serverlist_infofield_t serverlist_sortbyfield;
158 int serverlist_sortflags;
160 int serverlist_viewcount = 0;
161 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
163 int serverlist_maxcachecount = 0;
164 int serverlist_cachecount = 0;
165 serverlist_entry_t *serverlist_cache = NULL;
167 qboolean serverlist_consoleoutput;
169 static int nFavorites = 0;
170 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
171 static int nFavorites_idfp = 0;
172 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
174 void NetConn_UpdateFavorites(void)
179 p = net_slist_favorites.string;
180 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
182 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
183 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
184 // (if v6 address contains port, it must start with '[')
186 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
191 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
197 /// helper function to insert a value into the viewset
198 /// spare entries will be removed
199 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
202 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
203 i = serverlist_viewcount++;
205 i = SERVERLIST_VIEWLISTSIZE - 1;
208 for( ; i > index ; i-- )
209 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
211 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
214 /// we suppose serverlist_viewcount to be valid, ie > 0
215 static void _ServerList_ViewList_Helper_Remove( int index )
217 serverlist_viewcount--;
218 for( ; index < serverlist_viewcount ; index++ )
219 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
222 /// \returns true if A should be inserted before B
223 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
225 int result = 0; // > 0 if for numbers A > B and for text if A < B
227 if( serverlist_sortflags & SLSF_CATEGORIES )
229 result = A->info.category - B->info.category;
234 if( serverlist_sortflags & SLSF_FAVORITES )
236 if(A->info.isfavorite != B->info.isfavorite)
237 return A->info.isfavorite;
240 switch( serverlist_sortbyfield ) {
242 result = A->info.ping - B->info.ping;
244 case SLIF_MAXPLAYERS:
245 result = A->info.maxplayers - B->info.maxplayers;
247 case SLIF_NUMPLAYERS:
248 result = A->info.numplayers - B->info.numplayers;
251 result = A->info.numbots - B->info.numbots;
254 result = A->info.numhumans - B->info.numhumans;
257 result = A->info.freeslots - B->info.freeslots;
260 result = A->info.protocol - B->info.protocol;
263 result = strcmp( B->info.cname, A->info.cname );
266 result = strcasecmp( B->info.game, A->info.game );
269 result = strcasecmp( B->info.map, A->info.map );
272 result = strcasecmp( B->info.mod, A->info.mod );
275 result = strcasecmp( B->info.name, A->info.name );
278 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
281 result = A->info.category - B->info.category;
283 case SLIF_ISFAVORITE:
284 result = !!B->info.isfavorite - !!A->info.isfavorite;
287 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
293 if( serverlist_sortflags & SLSF_DESCENDING )
299 // if the chosen sort key is identical, sort by index
300 // (makes this a stable sort, so that later replies from servers won't
301 // shuffle the servers around when they have the same ping)
305 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
307 // This should actually be done with some intermediate and end-of-function return
319 case SLMO_GREATEREQUAL:
321 case SLMO_NOTCONTAIN:
322 case SLMO_STARTSWITH:
323 case SLMO_NOTSTARTSWITH:
326 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
331 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
334 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
335 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
336 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
337 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
339 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
340 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
343 // Same here, also using an intermediate & final return would be more appropriate
347 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
348 case SLMO_NOTCONTAIN:
349 return !*bufferB || !strstr( bufferA, bufferB );
350 case SLMO_STARTSWITH:
351 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
352 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
353 case SLMO_NOTSTARTSWITH:
354 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
356 return strcmp( bufferA, bufferB ) < 0;
358 return strcmp( bufferA, bufferB ) <= 0;
360 return strcmp( bufferA, bufferB ) == 0;
362 return strcmp( bufferA, bufferB ) > 0;
364 return strcmp( bufferA, bufferB ) != 0;
365 case SLMO_GREATEREQUAL:
366 return strcmp( bufferA, bufferB ) >= 0;
368 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
373 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
375 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
377 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
379 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
381 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
383 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
385 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
387 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
389 if( *mask->info.cname
390 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
393 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
396 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
399 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
402 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
404 if( *mask->info.qcstatus
405 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
407 if( *mask->info.players
408 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
410 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
412 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
417 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
419 int start, end, mid, i;
422 // reject incompatible servers
424 entry->info.gameversion != gameversion.integer
427 gameversion_min.integer >= 0 // min/max range set by user/mod?
428 && gameversion_max.integer >= 0
429 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
430 && gameversion_max.integer >= entry->info.gameversion
435 // refresh the "favorite" status
436 entry->info.isfavorite = false;
437 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
439 char idfp[FP64_SIZE+1];
440 for(i = 0; i < nFavorites; ++i)
442 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
444 entry->info.isfavorite = true;
448 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
450 for(i = 0; i < nFavorites_idfp; ++i)
452 if(!strcmp(idfp, favorites_idfp[i]))
454 entry->info.isfavorite = true;
461 // refresh the "category"
462 entry->info.category = MR_GetServerListEntryCategory(entry);
464 // FIXME: change this to be more readable (...)
465 // now check whether it passes through the masks
466 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
467 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
470 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
471 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
473 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
476 if( !serverlist_viewcount ) {
477 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
480 // ok, insert it, we just need to find out where exactly:
483 // check whether to insert it as new first item
484 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
485 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
487 } // check whether to insert it as new last item
488 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
489 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
493 end = serverlist_viewcount - 1;
494 while( end > start + 1 )
496 mid = (start + end) / 2;
497 // test the item that lies in the middle between start and end
498 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
499 // the item has to be in the upper half
502 // the item has to be in the lower half
505 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
508 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
511 for( i = 0; i < serverlist_viewcount; i++ )
513 if (ServerList_GetViewEntry(i) == entry)
515 _ServerList_ViewList_Helper_Remove(i);
521 void ServerList_RebuildViewList(void)
525 serverlist_viewcount = 0;
526 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
527 serverlist_entry_t *entry = &serverlist_cache[i];
528 // also display entries that are currently being refreshed [11/8/2007 Black]
529 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
530 ServerList_ViewList_Insert( entry );
534 void ServerList_ResetMasks(void)
538 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
539 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
540 // numbots needs to be compared to -1 to always succeed
541 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
542 serverlist_andmasks[i].info.numbots = -1;
543 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
544 serverlist_ormasks[i].info.numbots = -1;
547 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
550 int numplayers = 0, maxplayers = 0;
551 for (i = 0;i < serverlist_cachecount;i++)
553 if (serverlist_cache[i].query == SQS_QUERIED)
555 numplayers += serverlist_cache[i].info.numhumans;
556 maxplayers += serverlist_cache[i].info.maxplayers;
559 *numplayerspointer = numplayers;
560 *maxplayerspointer = maxplayers;
564 static void _ServerList_Test(void)
567 if (serverlist_maxcachecount <= 1024)
569 serverlist_maxcachecount = 1024;
570 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
572 for( i = 0 ; i < 1024 ; i++ ) {
573 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
574 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
575 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
576 serverlist_cache[serverlist_cachecount].finished = true;
577 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 );
578 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
579 serverlist_cachecount++;
584 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
586 masterquerytime = realtime;
587 masterquerycount = 0;
588 masterreplycount = 0;
590 serverquerycount = 0;
591 serverreplycount = 0;
592 serverlist_cachecount = 0;
593 serverlist_viewcount = 0;
594 serverlist_maxcachecount = 0;
595 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
597 // refresh all entries
599 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
600 serverlist_entry_t *entry = &serverlist_cache[ n ];
601 entry->query = SQS_REFRESHING;
602 entry->querycounter = 0;
605 serverlist_consoleoutput = consoleoutput;
607 //_ServerList_Test();
609 NetConn_QueryMasters(querydp, queryqw);
615 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
619 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
620 Thread_LockMutex(netconn_mutex);
621 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
622 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
623 Thread_UnlockMutex(netconn_mutex);
626 if (cl_netpacketloss_receive.integer)
627 for (i = 0;i < cl_numsockets;i++)
628 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
630 if (developer_networking.integer)
632 char addressstring[128], addressstring2[128];
633 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
636 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
637 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
638 Com_HexDumpToConsole((unsigned char *)data, length);
641 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
646 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
650 if (cl_netpacketloss_send.integer)
651 for (i = 0;i < cl_numsockets;i++)
652 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
654 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
655 Thread_LockMutex(netconn_mutex);
656 ret = LHNET_Write(mysocket, data, length, peeraddress);
657 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
658 Thread_UnlockMutex(netconn_mutex);
659 if (developer_networking.integer)
661 char addressstring[128], addressstring2[128];
662 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
663 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
664 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)");
665 Com_HexDumpToConsole((unsigned char *)data, length);
670 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
672 // note this does not include the trailing NULL because we add that in the parser
673 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
676 qboolean NetConn_CanSend(netconn_t *conn)
678 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
679 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = realtime;
680 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
681 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
682 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
683 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
684 if (realtime > conn->cleartime)
688 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
693 void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
695 double bursttime = burstsize / (double)rate;
697 // delay later packets to obey rate limit
698 if (*cleartime < realtime - bursttime)
699 *cleartime = realtime - bursttime;
700 *cleartime = *cleartime + len / (double)rate;
702 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
703 if (net_test.integer)
705 if (*cleartime < realtime)
706 *cleartime = realtime;
710 static int NetConn_AddCryptoFlag(crypto_t *crypto)
712 // HACK: if an encrypted connection is used, randomly set some unused
713 // flags. When AES encryption is enabled, that will make resends differ
714 // from the original, so that e.g. substring filters in a router/IPS
715 // are unlikely to match a second time. See also "startkeylogger".
717 if (crypto->authenticated)
719 // Let's always set at least one of the bits.
720 int r = rand() % 7 + 1;
722 flag |= NETFLAG_CRYPTO0;
724 flag |= NETFLAG_CRYPTO1;
726 flag |= NETFLAG_CRYPTO2;
731 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
734 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
735 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
737 // if this packet was supposedly choked, but we find ourselves sending one
738 // anyway, make sure the size counting starts at zero
739 // (this mostly happens on level changes and disconnects and such)
740 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
741 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
743 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
745 if (protocol == PROTOCOL_QUAKEWORLD)
748 qboolean sendreliable;
750 // note that it is ok to send empty messages to the qw server,
751 // otherwise it won't respond to us at all
753 sendreliable = false;
754 // if the remote side dropped the last reliable message, resend it
755 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
757 // if the reliable transmit buffer is empty, copy the current message out
758 if (!conn->sendMessageLength && conn->message.cursize)
760 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
761 conn->sendMessageLength = conn->message.cursize;
762 SZ_Clear(&conn->message); // clear the message buffer
763 conn->qw.reliable_sequence ^= 1;
766 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
767 StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
768 // last received unreliable packet number, and last received reliable packet number (0 or 1)
769 StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
771 conn->outgoing_unreliable_sequence++;
772 // client sends qport in every packet
773 if (conn == cls.netcon)
775 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
777 // also update cls.qw_outgoing_sequence
778 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
780 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
782 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
786 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
788 // add the reliable message if there is one
791 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
792 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
793 packetLen += conn->sendMessageLength;
794 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
797 // add the unreliable message if possible
798 if (packetLen + data->cursize <= 1400)
800 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
801 memcpy(sendbuffer + packetLen, data->data, data->cursize);
802 packetLen += data->cursize;
805 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
808 conn->unreliableMessagesSent++;
810 totallen += packetLen + 28;
814 unsigned int packetLen;
815 unsigned int dataLen;
820 // if a reliable message fragment has been lost, send it again
821 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
823 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
825 dataLen = conn->sendMessageLength;
830 dataLen = MAX_PACKETFRAGMENT;
834 packetLen = NET_HEADERSIZE + dataLen;
836 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
837 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
838 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
840 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
842 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
843 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
845 conn->lastSendTime = realtime;
846 conn->packetsReSent++;
849 totallen += (int)sendmelen + 28;
852 // if we have a new reliable message to send, do so
853 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
855 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
857 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
858 conn->message.overflowed = true;
862 if (developer_networking.integer && conn == cls.netcon)
864 Con_Print("client sending reliable message to server:\n");
865 SZ_HexDumpToConsole(&conn->message);
868 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
869 conn->sendMessageLength = conn->message.cursize;
870 SZ_Clear(&conn->message);
872 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
874 dataLen = conn->sendMessageLength;
879 dataLen = MAX_PACKETFRAGMENT;
883 packetLen = NET_HEADERSIZE + dataLen;
885 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
886 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
887 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
889 conn->nq.sendSequence++;
891 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
893 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
895 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
897 conn->lastSendTime = realtime;
899 conn->reliableMessagesSent++;
901 totallen += (int)sendmelen + 28;
904 // if we have an unreliable message to send, do so
907 packetLen = NET_HEADERSIZE + data->cursize;
909 if (packetLen > (int)sizeof(sendbuffer))
911 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
915 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
916 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
917 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
919 conn->outgoing_unreliable_sequence++;
921 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
923 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
925 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
928 conn->unreliableMessagesSent++;
930 totallen += (int)sendmelen + 28;
934 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
939 qboolean NetConn_HaveClientPorts(void)
941 return !!cl_numsockets;
944 qboolean NetConn_HaveServerPorts(void)
946 return !!sv_numsockets;
949 void NetConn_CloseClientPorts(void)
951 for (;cl_numsockets > 0;cl_numsockets--)
952 if (cl_sockets[cl_numsockets - 1])
953 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
956 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
958 lhnetaddress_t address;
961 char addressstring2[1024];
962 if (addressstring && addressstring[0])
963 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
965 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
968 if ((s = LHNET_OpenSocket_Connectionless(&address)))
970 cl_sockets[cl_numsockets++] = s;
971 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
972 if (addresstype != LHNETADDRESSTYPE_LOOP)
973 Con_Printf("Client opened a socket on address %s\n", addressstring2);
977 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
978 Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
982 Con_Printf("Client unable to parse address %s\n", addressstring);
985 void NetConn_OpenClientPorts(void)
988 NetConn_CloseClientPorts();
990 SV_LockThreadMutex(); // FIXME recursive?
991 Crypto_LoadKeys(); // client sockets
992 SV_UnlockThreadMutex();
994 port = bound(0, cl_netport.integer, 65535);
995 if (cl_netport.integer != port)
996 Cvar_SetValueQuick(&cl_netport, port);
998 Con_Printf("Client using an automatically assigned port\n");
1000 Con_Printf("Client using port %i\n", port);
1001 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
1002 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
1004 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1008 void NetConn_CloseServerPorts(void)
1010 for (;sv_numsockets > 0;sv_numsockets--)
1011 if (sv_sockets[sv_numsockets - 1])
1012 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1015 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1017 lhnetaddress_t address;
1020 char addressstring2[1024];
1023 for (port = defaultport; port <= defaultport + range; port++)
1025 if (addressstring && addressstring[0])
1026 success = LHNETADDRESS_FromString(&address, addressstring, port);
1028 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1031 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1033 sv_sockets[sv_numsockets++] = s;
1034 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1035 if (addresstype != LHNETADDRESSTYPE_LOOP)
1036 Con_Printf("Server listening on address %s\n", addressstring2);
1041 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1042 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
1047 Con_Printf("Server unable to parse address %s\n", addressstring);
1048 // if it cant parse one address, it wont be able to parse another for sure
1055 void NetConn_OpenServerPorts(int opennetports)
1058 NetConn_CloseServerPorts();
1060 SV_LockThreadMutex(); // FIXME recursive?
1061 Crypto_LoadKeys(); // server sockets
1062 SV_UnlockThreadMutex();
1064 NetConn_UpdateSockets();
1065 port = bound(0, sv_netport.integer, 65535);
1068 Con_Printf("Server using port %i\n", port);
1069 if (sv_netport.integer != port)
1070 Cvar_SetValueQuick(&sv_netport, port);
1071 if (cls.state != ca_dedicated)
1072 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1076 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1077 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1079 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1082 if (sv_numsockets == 0)
1083 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1086 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1088 int i, a = LHNETADDRESS_GetAddressType(address);
1089 for (i = 0;i < cl_numsockets;i++)
1090 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1091 return cl_sockets[i];
1095 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1097 int i, a = LHNETADDRESS_GetAddressType(address);
1098 for (i = 0;i < sv_numsockets;i++)
1099 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1100 return sv_sockets[i];
1104 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1107 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1108 conn->mysocket = mysocket;
1109 conn->peeraddress = *peeraddress;
1110 conn->lastMessageTime = realtime;
1111 conn->message.data = conn->messagedata;
1112 conn->message.maxsize = sizeof(conn->messagedata);
1113 conn->message.cursize = 0;
1114 // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1115 // reduce effectiveness of connection request floods
1116 conn->timeout = realtime + net_connecttimeout.value;
1117 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1118 conn->next = netconn_list;
1119 netconn_list = conn;
1123 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1124 void NetConn_Close(netconn_t *conn)
1127 // remove connection from list
1129 // allow the client to reconnect immediately
1130 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1132 if (conn == netconn_list)
1133 netconn_list = conn->next;
1136 for (c = netconn_list;c;c = c->next)
1138 if (c->next == conn)
1140 c->next = conn->next;
1144 // not found in list, we'll avoid crashing here...
1152 static int clientport = -1;
1153 static int clientport2 = -1;
1154 static int hostport = -1;
1155 void NetConn_UpdateSockets(void)
1159 // TODO add logic to automatically close sockets if needed
1160 LHNET_DefaultDSCP(net_tos_dscp.integer);
1162 if (cls.state != ca_dedicated)
1164 if (clientport2 != cl_netport.integer)
1166 clientport2 = cl_netport.integer;
1167 if (cls.state == ca_connected)
1168 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1170 if (cls.state == ca_disconnected && clientport != clientport2)
1172 clientport = clientport2;
1173 NetConn_CloseClientPorts();
1175 if (cl_numsockets == 0)
1176 NetConn_OpenClientPorts();
1179 if (hostport != sv_netport.integer)
1181 hostport = sv_netport.integer;
1183 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1186 for (j = 0;j < MAX_RCONS;j++)
1188 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1189 if(cls.rcon_commands[i][0])
1191 if(realtime > cls.rcon_timeout[i])
1194 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1195 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1196 cls.rcon_commands[i][0] = 0;
1204 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1206 int originallength = (int)length;
1207 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1208 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1209 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1213 if (protocol == PROTOCOL_QUAKEWORLD)
1215 int sequence, sequence_ack;
1216 int reliable_ack, reliable_message;
1220 sequence = LittleLong(*((int *)(data + 0)));
1221 sequence_ack = LittleLong(*((int *)(data + 4)));
1225 if (conn != cls.netcon)
1230 // 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?)
1231 //qport = LittleShort(*((int *)(data + 8)));
1236 conn->packetsReceived++;
1237 reliable_message = (sequence >> 31) & 1;
1238 reliable_ack = (sequence_ack >> 31) & 1;
1239 sequence &= ~(1<<31);
1240 sequence_ack &= ~(1<<31);
1241 if (sequence <= conn->qw.incoming_sequence)
1243 //Con_DPrint("Got a stale datagram\n");
1246 count = sequence - (conn->qw.incoming_sequence + 1);
1249 conn->droppedDatagrams += count;
1250 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1253 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1254 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1255 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1256 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1257 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1258 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1261 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1262 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1263 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1264 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1265 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1266 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1267 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1269 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1270 if (net_test.integer)
1272 if (conn->cleartime < realtime)
1273 conn->cleartime = realtime;
1276 if (reliable_ack == conn->qw.reliable_sequence)
1278 // received, now we will be able to send another reliable message
1279 conn->sendMessageLength = 0;
1280 conn->reliableMessagesReceived++;
1282 conn->qw.incoming_sequence = sequence;
1283 if (conn == cls.netcon)
1284 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1285 conn->qw.incoming_acknowledged = sequence_ack;
1286 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1287 if (reliable_message)
1288 conn->qw.incoming_reliable_sequence ^= 1;
1289 conn->lastMessageTime = realtime;
1290 conn->timeout = realtime + newtimeout;
1291 conn->unreliableMessagesReceived++;
1292 if (conn == cls.netcon)
1294 SZ_Clear(&cl_message);
1295 SZ_Write(&cl_message, data, (int)length);
1296 MSG_BeginReading(&cl_message);
1300 SZ_Clear(&sv_message);
1301 SZ_Write(&sv_message, data, (int)length);
1302 MSG_BeginReading(&sv_message);
1310 unsigned int sequence;
1315 originallength = (int)length;
1316 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1322 qlength = (unsigned int)BuffBigLong(data);
1323 flags = qlength & ~NETFLAG_LENGTH_MASK;
1324 qlength &= NETFLAG_LENGTH_MASK;
1325 // control packets were already handled
1326 if (!(flags & NETFLAG_CTL) && qlength == length)
1328 sequence = BuffBigLong(data + 4);
1329 conn->packetsReceived++;
1332 if (flags & NETFLAG_UNRELIABLE)
1334 if (sequence >= conn->nq.unreliableReceiveSequence)
1336 if (sequence > conn->nq.unreliableReceiveSequence)
1338 count = sequence - conn->nq.unreliableReceiveSequence;
1339 conn->droppedDatagrams += count;
1340 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1343 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1344 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1345 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1346 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1347 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1348 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1351 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1352 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1353 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1354 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1355 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1356 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1357 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1359 conn->nq.unreliableReceiveSequence = sequence + 1;
1360 conn->lastMessageTime = realtime;
1361 conn->timeout = realtime + newtimeout;
1362 conn->unreliableMessagesReceived++;
1365 if (conn == cls.netcon)
1367 SZ_Clear(&cl_message);
1368 SZ_Write(&cl_message, data, (int)length);
1369 MSG_BeginReading(&cl_message);
1373 SZ_Clear(&sv_message);
1374 SZ_Write(&sv_message, data, (int)length);
1375 MSG_BeginReading(&sv_message);
1381 // Con_DPrint("Got a stale datagram\n");
1384 else if (flags & NETFLAG_ACK)
1386 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1387 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1389 if (sequence == (conn->nq.sendSequence - 1))
1391 if (sequence == conn->nq.ackSequence)
1393 conn->nq.ackSequence++;
1394 if (conn->nq.ackSequence != conn->nq.sendSequence)
1395 Con_DPrint("ack sequencing error\n");
1396 conn->lastMessageTime = realtime;
1397 conn->timeout = realtime + newtimeout;
1398 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1400 unsigned int packetLen;
1401 unsigned int dataLen;
1404 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1405 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1407 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1409 dataLen = conn->sendMessageLength;
1414 dataLen = MAX_PACKETFRAGMENT;
1418 packetLen = NET_HEADERSIZE + dataLen;
1420 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1421 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1422 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1424 conn->nq.sendSequence++;
1426 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1427 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1429 conn->lastSendTime = realtime;
1430 conn->packetsSent++;
1434 conn->sendMessageLength = 0;
1437 // Con_DPrint("Duplicate ACK received\n");
1440 // Con_DPrint("Stale ACK received\n");
1443 else if (flags & NETFLAG_DATA)
1445 unsigned char temppacket[8];
1446 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1447 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1449 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1451 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1452 StoreBigLong(temppacket + 4, sequence);
1453 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1455 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1456 if (sequence == conn->nq.receiveSequence)
1458 conn->lastMessageTime = realtime;
1459 conn->timeout = realtime + newtimeout;
1460 conn->nq.receiveSequence++;
1461 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1462 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1463 conn->receiveMessageLength += (int)length;
1465 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1466 "Dropping the message!\n", sequence );
1467 conn->receiveMessageLength = 0;
1470 if (flags & NETFLAG_EOM)
1472 conn->reliableMessagesReceived++;
1473 length = conn->receiveMessageLength;
1474 conn->receiveMessageLength = 0;
1477 if (conn == cls.netcon)
1479 SZ_Clear(&cl_message);
1480 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1481 MSG_BeginReading(&cl_message);
1485 SZ_Clear(&sv_message);
1486 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1487 MSG_BeginReading(&sv_message);
1494 conn->receivedDuplicateCount++;
1502 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1505 cls.connect_trying = false;
1507 M_Update_Return_Reason("");
1509 // the connection request succeeded, stop current connection and set up a new connection
1511 // if we're connecting to a remote server, shut down any local server
1512 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1514 SV_LockThreadMutex();
1515 Host_ShutdownServer ();
1516 SV_UnlockThreadMutex();
1518 // allocate a net connection to keep track of things
1519 cls.netcon = NetConn_Open(mysocket, peeraddress);
1520 crypto = &cls.netcon->crypto;
1521 if(cls.crypto.authenticated)
1523 Crypto_FinishInstance(crypto, &cls.crypto);
1524 Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
1525 crypto->use_aes ? "Encrypted" : "Authenticated",
1526 cls.netcon->address,
1527 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1528 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1529 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1530 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1533 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1534 key_dest = key_game;
1538 cls.demonum = -1; // not in the demo loop now
1539 cls.state = ca_connected;
1540 cls.signon = 0; // need all the signon messages before playing
1541 cls.protocol = initialprotocol;
1542 // reset move sequence numbering on this new connection
1543 cls.servermovesequence = 0;
1544 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1545 Cmd_ForwardStringToServer("new");
1546 if (cls.protocol == PROTOCOL_QUAKE)
1548 // write a keepalive (clc_nop) as it seems to greatly improve the
1549 // chances of connecting to a netquake server
1551 unsigned char buf[4];
1552 memset(&msg, 0, sizeof(msg));
1554 msg.maxsize = sizeof(buf);
1555 MSG_WriteChar(&msg, clc_nop);
1556 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1560 int NetConn_IsLocalGame(void)
1562 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1568 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1572 serverlist_entry_t *entry = NULL;
1574 // search the cache for this server and update it
1575 for (n = 0;n < serverlist_cachecount;n++) {
1576 entry = &serverlist_cache[ n ];
1577 if (!strcmp(addressstring, entry->info.cname))
1581 if (n == serverlist_cachecount)
1583 // LAN search doesnt require an answer from the master server so we wont
1584 // know the ping nor will it be initialized already...
1587 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1590 if (serverlist_maxcachecount <= serverlist_cachecount)
1592 serverlist_maxcachecount += 64;
1593 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1595 entry = &serverlist_cache[n];
1597 memset(entry, 0, sizeof(*entry));
1598 // store the data the engine cares about (address and ping)
1599 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1600 entry->info.ping = 100000;
1601 entry->querytime = realtime;
1602 // if not in the slist menu we should print the server to console
1603 if (serverlist_consoleoutput)
1604 Con_Printf("querying %s\n", addressstring);
1605 ++serverlist_cachecount;
1607 // if this is the first reply from this server, count it as having replied
1608 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1609 pingtime = bound(0, pingtime, 9999);
1610 if (entry->query == SQS_REFRESHING) {
1611 entry->info.ping = pingtime;
1612 entry->query = SQS_QUERIED;
1614 // convert to unsigned to catch the -1
1615 // 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]
1616 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1620 // other server info is updated by the caller
1624 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1626 serverlist_entry_t *entry = &serverlist_cache[n];
1627 serverlist_info_t *info = &entry->info;
1628 // update description strings for engine menu and console output
1629 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);
1630 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1632 info->gameversion != gameversion.integer
1635 gameversion_min.integer >= 0 // min/max range set by user/mod?
1636 && gameversion_max.integer >= 0
1637 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1638 && gameversion_max.integer >= info->gameversion
1641 info->mod, info->map);
1642 if (entry->query == SQS_QUERIED)
1644 if(!serverlist_paused)
1645 ServerList_ViewList_Remove(entry);
1647 // if not in the slist menu we should print the server to console (if wanted)
1648 else if( serverlist_consoleoutput )
1649 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1650 // and finally, update the view set
1651 if(!serverlist_paused)
1652 ServerList_ViewList_Insert( entry );
1653 // update the entry's state
1654 serverlist_cache[n].query = SQS_QUERIED;
1657 // returns true, if it's sensible to continue the processing
1658 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1660 serverlist_entry_t *entry;
1662 // ignore the rest of the message if the serverlist is full
1663 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1665 // also ignore it if we have already queried it (other master server response)
1666 for( n = 0 ; n < serverlist_cachecount ; n++ )
1667 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1670 if( n < serverlist_cachecount ) {
1671 // the entry has already been queried once or
1675 if (serverlist_maxcachecount <= n)
1677 serverlist_maxcachecount += 64;
1678 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1681 entry = &serverlist_cache[n];
1683 memset(entry, 0, sizeof(*entry));
1684 entry->protocol = protocol;
1685 // store the data the engine cares about (address and ping)
1686 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1688 entry->info.isfavorite = isfavorite;
1690 // no, then reset the ping right away
1691 entry->info.ping = -1;
1692 // we also want to increase the serverlist_cachecount then
1693 serverlist_cachecount++;
1696 entry->query = SQS_QUERYING;
1701 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1704 if (serverlist_consoleoutput)
1705 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1708 char ipstring [128];
1711 if (data[0] == '\\')
1713 unsigned short port = data[5] * 256 + data[6];
1715 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1716 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1718 // move on to next address in packet
1723 else if (data[0] == '/' && isextended && length >= 19)
1725 unsigned short port = data[17] * 256 + data[18];
1733 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1735 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1738 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1739 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1740 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1746 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1747 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1748 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1753 // move on to next address in packet
1759 Con_Print("Error while parsing the server list\n");
1763 if (serverlist_consoleoutput && developer_networking.integer)
1764 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1766 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1772 // begin or resume serverlist queries
1773 serverlist_querysleep = false;
1774 serverlist_querywaittime = realtime + 3;
1778 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1780 qboolean fromserver;
1782 char *string, addressstring2[128];
1783 char stringbuf[16384];
1784 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1787 char infostringvalue[MAX_INPUTLINE];
1793 // quakeworld ingame packet
1794 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1796 // convert the address to a string incase we need it
1797 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1799 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1801 // received a command string - strip off the packaging and put it
1802 // into our string buffer with NULL termination
1805 length = min(length, (int)sizeof(stringbuf) - 1);
1806 memcpy(stringbuf, data, length);
1807 stringbuf[length] = 0;
1810 if (developer_networking.integer)
1812 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1813 Com_HexDumpToConsole(data, length);
1816 sendlength = sizeof(senddata) - 4;
1817 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1819 case CRYPTO_NOMATCH:
1825 memcpy(senddata, "\377\377\377\377", 4);
1826 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1829 case CRYPTO_DISCARD:
1832 memcpy(senddata, "\377\377\377\377", 4);
1833 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1837 case CRYPTO_REPLACE:
1838 string = senddata+4;
1839 length = (int)sendlength;
1843 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1846 for (j = 0;j < MAX_RCONS;j++)
1848 // note: this value from i is used outside the loop too...
1849 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1850 if(cls.rcon_commands[i][0])
1851 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1860 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1861 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1863 e = strchr(rcon_password.string, ' ');
1864 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1866 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1870 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1871 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1872 cls.rcon_commands[i][0] = 0;
1875 for (k = 0;k < MAX_RCONS;k++)
1876 if(cls.rcon_commands[k][0])
1877 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1882 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1883 // extend the timeout on other requests as we asked for a challenge
1884 for (l = 0;l < MAX_RCONS;l++)
1885 if(cls.rcon_commands[l][0])
1886 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1887 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1890 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1894 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1896 // darkplaces or quake3
1897 char protocolnames[1400];
1898 Protocol_Names(protocolnames, sizeof(protocolnames));
1899 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1901 M_Update_Return_Reason("Got challenge response");
1903 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1904 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1905 // TODO: add userinfo stuff here instead of using NQ commands?
1906 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10), peeraddress);
1909 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1911 // darkplaces or quake3
1913 M_Update_Return_Reason("Accepted");
1915 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1918 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1920 char rejectreason[128];
1921 cls.connect_trying = false;
1923 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1924 memcpy(rejectreason, string, length);
1925 rejectreason[length] = 0;
1927 M_Update_Return_Reason(rejectreason);
1932 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1934 serverlist_info_t *info;
1939 // search the cache for this server and update it
1940 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1944 info = &serverlist_cache[n].info;
1949 info->qcstatus[0] = 0;
1950 info->players[0] = 0;
1951 info->protocol = -1;
1952 info->numplayers = 0;
1954 info->maxplayers = 0;
1955 info->gameversion = 0;
1957 p = strchr(string, '\n');
1960 *p = 0; // cut off the string there
1964 Con_Printf("statusResponse without players block?\n");
1966 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1967 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1968 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1969 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1970 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1971 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1972 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1973 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1974 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1975 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1976 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1977 info->numhumans = info->numplayers - max(0, info->numbots);
1978 info->freeslots = info->maxplayers - info->numplayers;
1980 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1984 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1986 serverlist_info_t *info;
1990 // search the cache for this server and update it
1991 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1995 info = &serverlist_cache[n].info;
2000 info->qcstatus[0] = 0;
2001 info->players[0] = 0;
2002 info->protocol = -1;
2003 info->numplayers = 0;
2005 info->maxplayers = 0;
2006 info->gameversion = 0;
2008 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2009 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2010 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2011 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2012 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2013 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2014 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2015 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2016 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2017 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2018 info->numhumans = info->numplayers - max(0, info->numbots);
2019 info->freeslots = info->maxplayers - info->numplayers;
2021 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2025 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2027 // Extract the IP addresses
2030 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2033 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2035 // Extract the IP addresses
2038 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2041 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2043 // Extract the IP addresses
2047 if (serverlist_consoleoutput)
2048 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2049 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2051 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2052 if (serverlist_consoleoutput && developer_networking.integer)
2053 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2055 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2059 // move on to next address in packet
2063 // begin or resume serverlist queries
2064 serverlist_querysleep = false;
2065 serverlist_querywaittime = realtime + 3;
2069 if (!strncmp(string, "extResponse ", 12))
2071 ++cl_net_extresponse_count;
2072 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2073 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2074 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2075 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2078 if (!strncmp(string, "ping", 4))
2080 if (developer_extra.integer)
2081 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2082 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2085 if (!strncmp(string, "ack", 3))
2087 // QuakeWorld compatibility
2088 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2090 // challenge message
2091 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2093 M_Update_Return_Reason("Got QuakeWorld challenge response");
2095 cls.qw_qport = qport.integer;
2096 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2097 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2098 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo), peeraddress);
2101 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2105 M_Update_Return_Reason("QuakeWorld Accepted");
2107 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2110 if (length > 2 && !memcmp(string, "n\\", 2))
2113 serverlist_info_t *info;
2117 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2118 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2121 // search the cache for this server and update it
2122 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2126 info = &serverlist_cache[n].info;
2127 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2128 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2129 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2130 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2132 info->numplayers = 0; // updated below
2133 info->numhumans = 0; // updated below
2134 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2135 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2137 // count active players on server
2138 // (we could gather more info, but we're just after the number)
2139 s = strchr(string, '\n');
2143 while (s < string + length)
2145 for (;s < string + length && *s != '\n';s++)
2147 if (s >= string + length)
2155 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2159 if (string[0] == 'n')
2162 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2164 // we may not have liked the packet, but it was a command packet, so
2165 // we're done processing this packet now
2168 // quakeworld ingame packet
2169 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2172 CL_ParseServerMessage();
2175 // netquake control packets, supported for compatibility only
2176 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2180 serverlist_info_t *info;
2185 SZ_Clear(&cl_message);
2186 SZ_Write(&cl_message, data, length);
2187 MSG_BeginReading(&cl_message);
2188 c = MSG_ReadByte(&cl_message);
2192 if (developer_extra.integer)
2193 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2194 if (cls.connect_trying)
2196 lhnetaddress_t clientportaddress;
2197 clientportaddress = *peeraddress;
2198 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2199 // extra ProQuake stuff
2201 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2203 cls.proquake_servermod = 0;
2205 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2207 cls.proquake_serverversion = 0;
2209 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2211 cls.proquake_serverflags = 0;
2212 if (cls.proquake_servermod == 1)
2213 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2214 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2215 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2217 M_Update_Return_Reason("Accepted");
2219 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2223 if (developer_extra.integer)
2224 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2225 cls.connect_trying = false;
2227 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2230 case CCREP_SERVER_INFO:
2231 if (developer_extra.integer)
2232 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2234 // LordHavoc: because the quake server may report weird addresses
2235 // we just ignore it and keep the real address
2236 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2237 // search the cache for this server and update it
2238 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2242 info = &serverlist_cache[n].info;
2243 strlcpy(info->game, "Quake", sizeof(info->game));
2244 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2245 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2246 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2247 info->numplayers = MSG_ReadByte(&cl_message);
2248 info->maxplayers = MSG_ReadByte(&cl_message);
2249 info->protocol = MSG_ReadByte(&cl_message);
2251 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2254 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2255 if (developer_extra.integer)
2256 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2258 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2260 case CCREP_PLAYER_INFO:
2261 // we got a CCREP_PLAYER_INFO??
2262 //if (developer_extra.integer)
2263 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2265 case CCREP_RULE_INFO:
2266 // we got a CCREP_RULE_INFO??
2267 //if (developer_extra.integer)
2268 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2273 SZ_Clear(&cl_message);
2274 // we may not have liked the packet, but it was a valid control
2275 // packet, so we're done processing this packet now
2279 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2280 CL_ParseServerMessage();
2285 void NetConn_QueryQueueFrame(void)
2291 static double querycounter = 0;
2293 if(!net_slist_pause.integer && serverlist_paused)
2294 ServerList_RebuildViewList();
2295 serverlist_paused = net_slist_pause.integer != 0;
2297 if (serverlist_querysleep)
2300 // apply a cool down time after master server replies,
2301 // to avoid messing up the ping times on the servers
2302 if (serverlist_querywaittime > realtime)
2305 // each time querycounter reaches 1.0 issue a query
2306 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2307 maxqueries = (int)querycounter;
2308 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2309 querycounter -= maxqueries;
2311 if( maxqueries == 0 ) {
2315 // scan serverlist and issue queries as needed
2316 serverlist_querysleep = true;
2318 timeouttime = realtime - net_slist_timeout.value;
2319 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2321 serverlist_entry_t *entry = &serverlist_cache[ index ];
2322 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2327 serverlist_querysleep = false;
2328 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2333 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2335 lhnetaddress_t address;
2338 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2339 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2341 for (socket = 0; socket < cl_numsockets ; socket++)
2342 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2346 for (socket = 0; socket < cl_numsockets ; socket++)
2347 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2350 // update the entry fields
2351 entry->querytime = realtime;
2352 entry->querycounter++;
2354 // if not in the slist menu we should print the server to console
2355 if (serverlist_consoleoutput)
2356 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2362 // have we tried to refresh this server?
2363 if( entry->query == SQS_REFRESHING ) {
2364 // yes, so update the reply count (since its not responding anymore)
2366 if(!serverlist_paused)
2367 ServerList_ViewList_Remove(entry);
2369 entry->query = SQS_TIMEDOUT;
2375 void NetConn_ClientFrame(void)
2378 lhnetaddress_t peeraddress;
2379 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2380 NetConn_UpdateSockets();
2381 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2384 if (cls.connect_remainingtries == 0)
2385 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2387 cls.connect_nextsendtime = realtime + 1;
2388 cls.connect_remainingtries--;
2389 if (cls.connect_remainingtries <= -10)
2391 cls.connect_trying = false;
2393 M_Update_Return_Reason("Connect: Failed");
2397 // try challenge first (newer DP server or QW)
2398 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2399 // then try netquake as a fallback (old server, or netquake)
2400 SZ_Clear(&cl_message);
2401 // save space for the header, filled in later
2402 MSG_WriteLong(&cl_message, 0);
2403 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2404 MSG_WriteString(&cl_message, "QUAKE");
2405 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2406 // extended proquake stuff
2407 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2408 // this version matches ProQuake 3.40, the first version to support
2409 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2410 // higher clients, so we pretend we are that version...
2411 MSG_WriteByte(&cl_message, 34); // version * 10
2412 MSG_WriteByte(&cl_message, 0); // flags
2413 MSG_WriteLong(&cl_message, 0); // password
2414 // write the packetsize now...
2415 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2416 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2417 SZ_Clear(&cl_message);
2419 for (i = 0;i < cl_numsockets;i++)
2421 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2423 // R_TimeReport("clientreadnetwork");
2424 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2425 // R_TimeReport("clientparsepacket");
2429 NetConn_QueryQueueFrame();
2431 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2433 Con_Print("Connection timed out\n");
2435 SV_LockThreadMutex();
2436 Host_ShutdownServer ();
2437 SV_UnlockThreadMutex();
2441 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2445 for (i = 0;i < bufferlength - 1;i++)
2449 c = rand () % (127 - 33) + 33;
2450 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2456 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2457 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2459 prvm_prog_t *prog = SVVM_prog;
2461 unsigned int nb_clients = 0, nb_bots = 0, i;
2464 const char *crypto_idstring;
2467 // How many clients are there?
2468 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2470 if (svs.clients[i].active)
2473 if (!svs.clients[i].netconnection)
2479 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2485 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2486 if(*q != '\\' && *q != '\n')
2491 /// \TODO: we should add more information for the full status string
2492 crypto_idstring = Crypto_GetInfoResponseDataString();
2493 length = dpsnprintf(out_msg, out_size,
2494 "\377\377\377\377%s\x0A"
2495 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2496 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2501 fullstatus ? "statusResponse" : "infoResponse",
2502 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2503 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2504 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2505 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2506 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2507 fullstatus ? "\n" : "");
2509 // Make sure it fits in the buffer
2519 savelength = length;
2521 ptr = out_msg + length;
2522 left = (int)out_size - length;
2524 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2526 client_t *cl = &svs.clients[i];
2529 int nameind, cleanind, pingvalue;
2531 char cleanname [sizeof(cl->name)];
2535 // Remove all characters '"' and '\' in the player name
2540 curchar = cl->name[nameind++];
2541 if (curchar != '"' && curchar != '\\')
2543 cleanname[cleanind++] = curchar;
2544 if (cleanind == sizeof(cleanname) - 1)
2547 } while (curchar != '\0');
2548 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2550 pingvalue = (int)(cl->ping * 1000.0f);
2551 if(cl->netconnection)
2552 pingvalue = bound(1, pingvalue, 9999);
2557 ed = PRVM_EDICT_NUM(i + 1);
2558 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2564 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2565 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2570 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2572 if(cl->frags == -666) // spectator
2573 strlcpy(teambuf, " 0", sizeof(teambuf));
2574 else if(cl->colors == 0x44) // red team
2575 strlcpy(teambuf, " 1", sizeof(teambuf));
2576 else if(cl->colors == 0xDD) // blue team
2577 strlcpy(teambuf, " 2", sizeof(teambuf));
2578 else if(cl->colors == 0xCC) // yellow team
2579 strlcpy(teambuf, " 3", sizeof(teambuf));
2580 else if(cl->colors == 0x99) // pink team
2581 strlcpy(teambuf, " 4", sizeof(teambuf));
2583 strlcpy(teambuf, " 0", sizeof(teambuf));
2588 // note: team number is inserted according to SoF2 protocol
2590 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2596 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2605 // turn it into an infoResponse!
2606 out_msg[savelength] = 0;
2607 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2608 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2623 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2625 size_t floodslotnum, bestfloodslotnum;
2626 double bestfloodtime;
2627 lhnetaddress_t noportpeeraddress;
2628 // see if this is a connect flood
2629 noportpeeraddress = *peeraddress;
2630 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2631 bestfloodslotnum = 0;
2632 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2633 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2635 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2637 bestfloodtime = floodlist[floodslotnum].lasttime;
2638 bestfloodslotnum = floodslotnum;
2640 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2642 // this address matches an ongoing flood address
2643 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2647 // renew the ban on this address so it does not expire
2648 // until the flood has subsided
2649 floodlist[floodslotnum].lasttime = realtime;
2651 //Con_Printf("Flood detected!\n");
2654 // the flood appears to have subsided, so allow this
2655 bestfloodslotnum = floodslotnum; // reuse the same slot
2659 // begin a new timeout on this address
2660 floodlist[bestfloodslotnum].address = noportpeeraddress;
2661 floodlist[bestfloodslotnum].lasttime = realtime;
2662 //Con_Printf("Flood detection initiated!\n");
2666 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2668 size_t floodslotnum;
2669 lhnetaddress_t noportpeeraddress;
2670 // see if this is a connect flood
2671 noportpeeraddress = *peeraddress;
2672 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2673 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2675 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2677 // this address matches an ongoing flood address
2679 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2680 floodlist[floodslotnum].lasttime = 0;
2681 //Con_Printf("Flood cleared!\n");
2686 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2688 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2693 t1 = (long) time(NULL);
2694 t2 = strtol(s, NULL, 0);
2695 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2698 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2701 return !memcmp(mdfourbuf, hash, 16);
2704 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2709 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2712 // validate the challenge
2713 for (i = 0;i < MAX_CHALLENGES;i++)
2714 if(challenge[i].time > 0)
2715 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2717 // if the challenge is not recognized, drop the packet
2718 if (i == MAX_CHALLENGES)
2721 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2724 if(memcmp(mdfourbuf, hash, 16))
2727 // unmark challenge to prevent replay attacks
2728 challenge[i].time = 0;
2733 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2735 return !strcmp(password, hash);
2738 /// returns a string describing the user level, or NULL for auth failure
2739 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)
2741 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2742 static char buf[MAX_INPUTLINE];
2744 qboolean restricted = false;
2745 qboolean have_usernames = false;
2748 userpass_start = rcon_password.string;
2749 while((userpass_end = strchr(userpass_start, ' ')))
2751 have_usernames = true;
2752 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2754 if(comparator(peeraddress, buf, password, cs, cslen))
2756 userpass_start = userpass_end + 1;
2758 if(userpass_start[0])
2760 userpass_end = userpass_start + strlen(userpass_start);
2761 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2766 have_usernames = false;
2767 userpass_start = rcon_restricted_password.string;
2768 while((userpass_end = strchr(userpass_start, ' ')))
2770 have_usernames = true;
2771 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2773 if(comparator(peeraddress, buf, password, cs, cslen))
2775 userpass_start = userpass_end + 1;
2777 if(userpass_start[0])
2779 userpass_end = userpass_start + strlen(userpass_start);
2780 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2784 return NULL; // DENIED
2787 for(text = s; text != endpos; ++text)
2788 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2789 return NULL; // block possible exploits against the parser/alias expansion
2793 size_t l = strlen(s);
2796 hasquotes = (strchr(s, '"') != NULL);
2797 // sorry, we can't allow these substrings in wildcard expressions,
2798 // as they can mess with the argument counts
2799 text = rcon_restricted_commands.string;
2800 while(COM_ParseToken_Console(&text))
2802 // com_token now contains a pattern to check for...
2803 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2806 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2809 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2811 if(!strcmp(com_token, s))
2814 else // single-arg expression? must match the beginning of the command
2816 if(!strcmp(com_token, s))
2818 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2822 // if we got here, nothing matched!
2830 userpass_startpass = strchr(userpass_start, ':');
2831 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2832 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2834 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2837 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2841 // looks like a legitimate rcon command with the correct password
2842 const char *s_ptr = s;
2843 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2844 while(s_ptr != endpos)
2846 size_t l = strlen(s_ptr);
2848 Con_Printf(" %s;", s_ptr);
2853 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2854 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2857 size_t l = strlen(s);
2860 client_t *host_client_save = host_client;
2861 Cmd_ExecuteString(s, src_command, true);
2862 host_client = host_client_save;
2863 // in case it is a command that changes host_client (like restart)
2867 Con_Rcon_Redirect_End();
2871 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2875 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2877 int i, ret, clientnum, best;
2880 char *s, *string, response[1400], addressstring2[128];
2881 static char stringbuf[16384]; // server only
2882 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2883 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2884 size_t sendlength, response_len;
2885 char infostringvalue[MAX_INPUTLINE];
2891 // convert the address to a string incase we need it
2892 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2894 // see if we can identify the sender as a local player
2895 // (this is necessary for rcon to send a reliable reply if the client is
2896 // actually on the server, not sending remotely)
2897 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2898 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2900 if (i == svs.maxclients)
2903 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2905 // received a command string - strip off the packaging and put it
2906 // into our string buffer with NULL termination
2909 length = min(length, (int)sizeof(stringbuf) - 1);
2910 memcpy(stringbuf, data, length);
2911 stringbuf[length] = 0;
2914 if (developer_extra.integer)
2916 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2917 Com_HexDumpToConsole(data, length);
2920 sendlength = sizeof(senddata) - 4;
2921 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2923 case CRYPTO_NOMATCH:
2929 memcpy(senddata, "\377\377\377\377", 4);
2930 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2933 case CRYPTO_DISCARD:
2936 memcpy(senddata, "\377\377\377\377", 4);
2937 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2941 case CRYPTO_REPLACE:
2942 string = senddata+4;
2943 length = (int)sendlength;
2947 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2949 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2951 if(challenge[i].time > 0)
2952 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2954 if (besttime > challenge[i].time)
2955 besttime = challenge[best = i].time;
2957 // if we did not find an exact match, choose the oldest and
2958 // update address and string
2959 if (i == MAX_CHALLENGES)
2962 challenge[i].address = *peeraddress;
2963 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2967 // flood control: drop if requesting challenge too often
2968 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2971 challenge[i].time = realtime;
2972 // send the challenge
2973 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2974 response_len = strlen(response) + 1;
2975 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2976 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
2979 if (length > 8 && !memcmp(string, "connect\\", 8))
2981 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2985 if(crypto && crypto->authenticated)
2987 // no need to check challenge
2988 if(crypto_developer.integer)
2990 Con_Printf("%s connection to %s is being established: client is %s@%.*s, I am %.*s@%.*s\n",
2991 crypto->use_aes ? "Encrypted" : "Authenticated",
2993 crypto->client_idfp[0] ? crypto->client_idfp : "-",
2994 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
2995 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
2996 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3002 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3004 // validate the challenge
3005 for (i = 0;i < MAX_CHALLENGES;i++)
3006 if(challenge[i].time > 0)
3007 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
3009 // if the challenge is not recognized, drop the packet
3010 if (i == MAX_CHALLENGES)
3015 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3016 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3018 if(!(islocal || sv_public.integer > -2))
3020 if (developer_extra.integer)
3021 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3022 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
3026 // check engine protocol
3027 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3029 if (developer_extra.integer)
3030 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3031 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3035 // see if this is a duplicate connection request or a disconnected
3036 // client who is rejoining to the same client slot
3037 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3039 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3041 // this is a known client...
3042 if(crypto && crypto->authenticated)
3044 // reject if changing key!
3045 if(client->netconnection->crypto.authenticated)
3048 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3050 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3052 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3054 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3057 if (developer_extra.integer)
3058 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3059 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3066 // reject if downgrading!
3067 if(client->netconnection->crypto.authenticated)
3069 if (developer_extra.integer)
3070 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3071 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3077 // client crashed and is coming back,
3078 // keep their stuff intact
3079 if (developer_extra.integer)
3080 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3081 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3082 if(crypto && crypto->authenticated)
3083 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3084 SV_SendServerinfo(client);
3088 // client is still trying to connect,
3089 // so we send a duplicate reply
3090 if (developer_extra.integer)
3091 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3092 if(crypto && crypto->authenticated)
3093 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3094 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3100 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3103 // find an empty client slot for this new client
3104 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3107 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3109 // allocated connection
3110 if (developer_extra.integer)
3111 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3112 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3113 // now set up the client
3114 if(crypto && crypto->authenticated)
3115 Crypto_FinishInstance(&conn->crypto, crypto);
3116 SV_ConnectClient(clientnum, conn);
3117 NetConn_Heartbeat(1);
3122 // no empty slots found - server is full
3123 if (developer_extra.integer)
3124 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3125 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3129 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3131 const char *challenge = NULL;
3133 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3136 // If there was a challenge in the getinfo message
3137 if (length > 8 && string[7] == ' ')
3138 challenge = string + 8;
3140 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3142 if (developer_extra.integer)
3143 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3144 NetConn_WriteString(mysocket, response, peeraddress);
3148 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3150 const char *challenge = NULL;
3152 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3155 // If there was a challenge in the getinfo message
3156 if (length > 10 && string[9] == ' ')
3157 challenge = string + 10;
3159 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3161 if (developer_extra.integer)
3162 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3163 NetConn_WriteString(mysocket, response, peeraddress);
3167 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3169 char *password = string + 20;
3170 char *timeval = string + 37;
3171 char *s = strchr(timeval, ' ');
3172 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3173 const char *userlevel;
3175 if(rcon_secure.integer > 1)
3179 return true; // invalid packet
3182 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3183 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3186 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3188 char *password = string + 25;
3189 char *challenge = string + 42;
3190 char *s = strchr(challenge, ' ');
3191 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3192 const char *userlevel;
3194 return true; // invalid packet
3197 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3198 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3201 if (length >= 5 && !memcmp(string, "rcon ", 5))
3204 char *s = string + 5;
3205 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3208 if(rcon_secure.integer > 0)
3211 for (i = 0;!ISWHITESPACE(*s);s++)
3212 if (i < (int)sizeof(password) - 1)
3214 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3217 if (!ISWHITESPACE(password[0]))
3219 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3220 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3224 if (!strncmp(string, "extResponse ", 12))
3226 ++sv_net_extresponse_count;
3227 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3228 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3229 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3230 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3233 if (!strncmp(string, "ping", 4))
3235 if (developer_extra.integer)
3236 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3237 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3240 if (!strncmp(string, "ack", 3))
3242 // we may not have liked the packet, but it was a command packet, so
3243 // we're done processing this packet now
3246 // netquake control packets, supported for compatibility only, and only
3247 // when running game protocols that are normally served via this connection
3249 // (this protects more modern protocols against being used for
3250 // Quake packet flood Denial Of Service attacks)
3251 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)
3255 const char *protocolname;
3258 SZ_Clear(&sv_message);
3259 SZ_Write(&sv_message, data, length);
3260 MSG_BeginReading(&sv_message);
3261 c = MSG_ReadByte(&sv_message);
3265 if (developer_extra.integer)
3266 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3267 if(!(islocal || sv_public.integer > -2))
3269 if (developer_extra.integer)
3270 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3271 SZ_Clear(&sv_message);
3272 // save space for the header, filled in later
3273 MSG_WriteLong(&sv_message, 0);
3274 MSG_WriteByte(&sv_message, CCREP_REJECT);
3275 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3276 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3277 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3278 SZ_Clear(&sv_message);
3282 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3283 protocolnumber = MSG_ReadByte(&sv_message);
3284 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3286 if (developer_extra.integer)
3287 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3288 SZ_Clear(&sv_message);
3289 // save space for the header, filled in later
3290 MSG_WriteLong(&sv_message, 0);
3291 MSG_WriteByte(&sv_message, CCREP_REJECT);
3292 MSG_WriteString(&sv_message, "Incompatible version.\n");
3293 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3294 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3295 SZ_Clear(&sv_message);
3299 // see if this connect request comes from a known client
3300 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3302 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3304 // this is either a duplicate connection request
3305 // or coming back from a timeout
3306 // (if so, keep their stuff intact)
3308 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3309 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3311 if (developer_extra.integer)
3312 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3313 SZ_Clear(&sv_message);
3314 // save space for the header, filled in later
3315 MSG_WriteLong(&sv_message, 0);
3316 MSG_WriteByte(&sv_message, CCREP_REJECT);
3317 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3318 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3319 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3320 SZ_Clear(&sv_message);
3325 if (developer_extra.integer)
3326 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3327 SZ_Clear(&sv_message);
3328 // save space for the header, filled in later
3329 MSG_WriteLong(&sv_message, 0);
3330 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3331 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3332 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3333 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3334 SZ_Clear(&sv_message);
3336 // if client is already spawned, re-send the
3337 // serverinfo message as they'll need it to play
3339 SV_SendServerinfo(client);
3344 // this is a new client, check for connection flood
3345 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3348 // find a slot for the new client
3349 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3352 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3354 // connect to the client
3355 // everything is allocated, just fill in the details
3356 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3357 if (developer_extra.integer)
3358 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3359 // send back the info about the server connection
3360 SZ_Clear(&sv_message);
3361 // save space for the header, filled in later
3362 MSG_WriteLong(&sv_message, 0);
3363 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3364 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3365 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3366 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3367 SZ_Clear(&sv_message);
3368 // now set up the client struct
3369 SV_ConnectClient(clientnum, conn);
3370 NetConn_Heartbeat(1);
3375 if (developer_extra.integer)
3376 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3377 // no room; try to let player know
3378 SZ_Clear(&sv_message);
3379 // save space for the header, filled in later
3380 MSG_WriteLong(&sv_message, 0);
3381 MSG_WriteByte(&sv_message, CCREP_REJECT);
3382 MSG_WriteString(&sv_message, "Server is full.\n");
3383 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3384 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3385 SZ_Clear(&sv_message);
3387 case CCREQ_SERVER_INFO:
3388 if (developer_extra.integer)
3389 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3390 if(!(islocal || sv_public.integer > -1))
3393 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3396 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3399 char myaddressstring[128];
3400 if (developer_extra.integer)
3401 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3402 SZ_Clear(&sv_message);
3403 // save space for the header, filled in later
3404 MSG_WriteLong(&sv_message, 0);
3405 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3406 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3407 MSG_WriteString(&sv_message, myaddressstring);
3408 MSG_WriteString(&sv_message, hostname.string);
3409 MSG_WriteString(&sv_message, sv.name);
3410 // How many clients are there?
3411 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3412 if (svs.clients[i].active)
3414 MSG_WriteByte(&sv_message, numclients);
3415 MSG_WriteByte(&sv_message, svs.maxclients);
3416 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3417 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3418 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3419 SZ_Clear(&sv_message);
3422 case CCREQ_PLAYER_INFO:
3423 if (developer_extra.integer)
3424 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3425 if(!(islocal || sv_public.integer > -1))
3428 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3433 int playerNumber, activeNumber, clientNumber;
3436 playerNumber = MSG_ReadByte(&sv_message);
3438 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3439 if (client->active && ++activeNumber == playerNumber)
3441 if (clientNumber != svs.maxclients)
3443 SZ_Clear(&sv_message);
3444 // save space for the header, filled in later
3445 MSG_WriteLong(&sv_message, 0);
3446 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3447 MSG_WriteByte(&sv_message, playerNumber);
3448 MSG_WriteString(&sv_message, client->name);
3449 MSG_WriteLong(&sv_message, client->colors);
3450 MSG_WriteLong(&sv_message, client->frags);
3451 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3452 if(sv_status_privacy.integer)
3453 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3455 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3456 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3457 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3458 SZ_Clear(&sv_message);
3462 case CCREQ_RULE_INFO:
3463 if (developer_extra.integer)
3464 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3465 if(!(islocal || sv_public.integer > -1))
3468 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3475 // find the search start location
3476 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3477 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3479 // send the response
3480 SZ_Clear(&sv_message);
3481 // save space for the header, filled in later
3482 MSG_WriteLong(&sv_message, 0);
3483 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3486 MSG_WriteString(&sv_message, var->name);
3487 MSG_WriteString(&sv_message, var->string);
3489 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3490 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3491 SZ_Clear(&sv_message);
3495 if (developer_extra.integer)
3496 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3497 if (sv.active && !rcon_secure.integer)
3499 char password[2048];
3503 const char *userlevel;
3504 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3505 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3507 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3508 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3509 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3516 SZ_Clear(&sv_message);
3517 // we may not have liked the packet, but it was a valid control
3518 // packet, so we're done processing this packet now
3523 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3525 SV_ReadClientMessage();
3532 void NetConn_ServerFrame(void)
3535 lhnetaddress_t peeraddress;
3536 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3537 for (i = 0;i < sv_numsockets;i++)
3538 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3539 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3540 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3542 // never timeout loopback connections
3543 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3545 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3546 SV_DropClient(false);
3551 void NetConn_SleepMicroseconds(int microseconds)
3553 LHNET_SleepUntilPacket_Microseconds(microseconds);
3557 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3561 lhnetaddress_t masteraddress;
3562 lhnetaddress_t broadcastaddress;
3565 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3568 // 26000 is the default quake server port, servers on other ports will not
3570 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3571 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3575 for (i = 0;i < cl_numsockets;i++)
3579 const char *cmdname, *extraoptions;
3580 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3582 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3584 // search LAN for Quake servers
3585 SZ_Clear(&cl_message);
3586 // save space for the header, filled in later
3587 MSG_WriteLong(&cl_message, 0);
3588 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3589 MSG_WriteString(&cl_message, "QUAKE");
3590 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3591 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3592 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3593 SZ_Clear(&cl_message);
3595 // search LAN for DarkPlaces servers
3596 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3599 // build the getservers message to send to the dpmaster master servers
3600 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3602 cmdname = "getserversExt";
3603 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3607 cmdname = "getservers";
3610 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3613 for (masternum = 0;sv_masters[masternum].name;masternum++)
3615 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3618 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3622 // search favorite servers
3623 for(j = 0; j < nFavorites; ++j)
3625 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3627 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3628 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3635 // only query QuakeWorld servers when the user wants to
3638 for (i = 0;i < cl_numsockets;i++)
3642 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3644 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3646 // search LAN for QuakeWorld servers
3647 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3649 // build the getservers message to send to the qwmaster master servers
3650 // note this has no -1 prefix, and the trailing nul byte is sent
3651 dpsnprintf(request, sizeof(request), "c\n");
3655 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3657 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3659 if (m_state != m_slist)
3661 char lookupstring[128];
3662 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3663 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3666 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3670 // search favorite servers
3671 for(j = 0; j < nFavorites; ++j)
3673 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3675 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3677 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3678 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3685 if (!masterquerycount)
3687 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3688 M_Update_Return_Reason("No network");
3693 void NetConn_Heartbeat(int priority)
3695 lhnetaddress_t masteraddress;
3697 lhnetsocket_t *mysocket;
3699 // if it's a state change (client connected), limit next heartbeat to no
3700 // more than 30 sec in the future
3701 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3702 nextheartbeattime = realtime + 30.0;
3704 // limit heartbeatperiod to 30 to 270 second range,
3705 // lower limit is to avoid abusing master servers with excess traffic,
3706 // upper limit is to avoid timing out on the master server (which uses
3708 if (sv_heartbeatperiod.value < 30)
3709 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3710 if (sv_heartbeatperiod.value > 270)
3711 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3713 // make advertising optional and don't advertise singleplayer games, and
3714 // only send a heartbeat as often as the admin wants
3715 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3717 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3718 for (masternum = 0;sv_masters[masternum].name;masternum++)
3719 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3720 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3724 static void Net_Heartbeat_f(void)
3727 NetConn_Heartbeat(2);
3729 Con_Print("No server running, can not heartbeat to master server.\n");
3732 static void PrintStats(netconn_t *conn)
3734 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3735 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3737 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3738 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3739 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3740 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3741 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3742 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3743 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3744 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3745 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3746 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3749 void Net_Stats_f(void)
3752 Con_Print("connections =\n");
3753 for (conn = netconn_list;conn;conn = conn->next)
3758 void Net_Refresh_f(void)
3760 if (m_state != m_slist) {
3761 Con_Print("Sending new requests to master servers\n");
3762 ServerList_QueryList(false, true, false, true);
3763 Con_Print("Listening for replies...\n");
3765 ServerList_QueryList(false, true, false, false);
3768 void Net_Slist_f(void)
3770 ServerList_ResetMasks();
3771 serverlist_sortbyfield = SLIF_PING;
3772 serverlist_sortflags = 0;
3773 if (m_state != m_slist) {
3774 Con_Print("Sending requests to master servers\n");
3775 ServerList_QueryList(true, true, false, true);
3776 Con_Print("Listening for replies...\n");
3778 ServerList_QueryList(true, true, false, false);
3781 void Net_SlistQW_f(void)
3783 ServerList_ResetMasks();
3784 serverlist_sortbyfield = SLIF_PING;
3785 serverlist_sortflags = 0;
3786 if (m_state != m_slist) {
3787 Con_Print("Sending requests to master servers\n");
3788 ServerList_QueryList(true, false, true, true);
3789 serverlist_consoleoutput = true;
3790 Con_Print("Listening for replies...\n");
3792 ServerList_QueryList(true, false, true, false);
3796 void NetConn_Init(void)
3799 lhnetaddress_t tempaddress;
3800 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3801 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3803 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3804 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3805 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3807 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3808 Cvar_RegisterVariable(&net_test);
3809 Cvar_RegisterVariable(&net_usesizelimit);
3810 Cvar_RegisterVariable(&net_burstreserve);
3811 Cvar_RegisterVariable(&rcon_restricted_password);
3812 Cvar_RegisterVariable(&rcon_restricted_commands);
3813 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3814 Cvar_RegisterVariable(&net_slist_queriespersecond);
3815 Cvar_RegisterVariable(&net_slist_queriesperframe);
3816 Cvar_RegisterVariable(&net_slist_timeout);
3817 Cvar_RegisterVariable(&net_slist_maxtries);
3818 Cvar_RegisterVariable(&net_slist_favorites);
3819 Cvar_RegisterVariable(&net_slist_pause);
3820 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3821 Cvar_RegisterVariable(&net_tos_dscp);
3822 Cvar_RegisterVariable(&net_messagetimeout);
3823 Cvar_RegisterVariable(&net_connecttimeout);
3824 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3825 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3826 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3827 Cvar_RegisterVariable(&cl_netlocalping);
3828 Cvar_RegisterVariable(&cl_netpacketloss_send);
3829 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3830 Cvar_RegisterVariable(&hostname);
3831 Cvar_RegisterVariable(&developer_networking);
3832 Cvar_RegisterVariable(&cl_netport);
3833 Cvar_RegisterVariable(&sv_netport);
3834 Cvar_RegisterVariable(&net_address);
3835 Cvar_RegisterVariable(&net_address_ipv6);
3836 Cvar_RegisterVariable(&sv_public);
3837 Cvar_RegisterVariable(&sv_public_rejectreason);
3838 Cvar_RegisterVariable(&sv_heartbeatperiod);
3839 for (i = 0;sv_masters[i].name;i++)
3840 Cvar_RegisterVariable(&sv_masters[i]);
3841 Cvar_RegisterVariable(&gameversion);
3842 Cvar_RegisterVariable(&gameversion_min);
3843 Cvar_RegisterVariable(&gameversion_max);
3844 // 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.
3845 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3847 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3849 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3850 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3853 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3855 // 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
3856 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3858 i = atoi(com_argv[i + 1]);
3859 if (i >= 0 && i < 65536)
3861 Con_Printf("-port option used, setting port cvar to %i\n", i);
3862 Cvar_SetValueQuick(&sv_netport, i);
3865 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3869 cl_message.data = cl_message_buf;
3870 cl_message.maxsize = sizeof(cl_message_buf);
3871 cl_message.cursize = 0;
3872 sv_message.data = sv_message_buf;
3873 sv_message.maxsize = sizeof(sv_message_buf);
3874 sv_message.cursize = 0;
3876 if (Thread_HasThreads())
3877 netconn_mutex = Thread_CreateMutex();
3880 void NetConn_Shutdown(void)
3882 NetConn_CloseClientPorts();
3883 NetConn_CloseServerPorts();
3886 Thread_DestroyMutex(netconn_mutex);
3887 netconn_mutex = NULL;