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, 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 static 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%.*s, I am %.*s@%s%.*s\n",
1525 crypto->use_aes ? "Encrypted" : "Authenticated",
1526 cls.netcon->address,
1527 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1528 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1529 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1530 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1531 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1532 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1535 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1536 key_dest = key_game;
1540 cls.demonum = -1; // not in the demo loop now
1541 cls.state = ca_connected;
1542 cls.signon = 0; // need all the signon messages before playing
1543 cls.protocol = initialprotocol;
1544 // reset move sequence numbering on this new connection
1545 cls.servermovesequence = 0;
1546 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1547 Cmd_ForwardStringToServer("new");
1548 if (cls.protocol == PROTOCOL_QUAKE)
1550 // write a keepalive (clc_nop) as it seems to greatly improve the
1551 // chances of connecting to a netquake server
1553 unsigned char buf[4];
1554 memset(&msg, 0, sizeof(msg));
1556 msg.maxsize = sizeof(buf);
1557 MSG_WriteChar(&msg, clc_nop);
1558 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1562 int NetConn_IsLocalGame(void)
1564 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1570 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1574 serverlist_entry_t *entry = NULL;
1576 // search the cache for this server and update it
1577 for (n = 0;n < serverlist_cachecount;n++) {
1578 entry = &serverlist_cache[ n ];
1579 if (!strcmp(addressstring, entry->info.cname))
1583 if (n == serverlist_cachecount)
1585 // LAN search doesnt require an answer from the master server so we wont
1586 // know the ping nor will it be initialized already...
1589 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1592 if (serverlist_maxcachecount <= serverlist_cachecount)
1594 serverlist_maxcachecount += 64;
1595 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1597 entry = &serverlist_cache[n];
1599 memset(entry, 0, sizeof(*entry));
1600 // store the data the engine cares about (address and ping)
1601 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1602 entry->info.ping = 100000;
1603 entry->querytime = realtime;
1604 // if not in the slist menu we should print the server to console
1605 if (serverlist_consoleoutput)
1606 Con_Printf("querying %s\n", addressstring);
1607 ++serverlist_cachecount;
1609 // if this is the first reply from this server, count it as having replied
1610 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1611 pingtime = bound(0, pingtime, 9999);
1612 if (entry->query == SQS_REFRESHING) {
1613 entry->info.ping = pingtime;
1614 entry->query = SQS_QUERIED;
1616 // convert to unsigned to catch the -1
1617 // 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]
1618 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1622 // other server info is updated by the caller
1626 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1628 serverlist_entry_t *entry = &serverlist_cache[n];
1629 serverlist_info_t *info = &entry->info;
1630 // update description strings for engine menu and console output
1631 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);
1632 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1634 info->gameversion != gameversion.integer
1637 gameversion_min.integer >= 0 // min/max range set by user/mod?
1638 && gameversion_max.integer >= 0
1639 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1640 && gameversion_max.integer >= info->gameversion
1643 info->mod, info->map);
1644 if (entry->query == SQS_QUERIED)
1646 if(!serverlist_paused)
1647 ServerList_ViewList_Remove(entry);
1649 // if not in the slist menu we should print the server to console (if wanted)
1650 else if( serverlist_consoleoutput )
1651 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1652 // and finally, update the view set
1653 if(!serverlist_paused)
1654 ServerList_ViewList_Insert( entry );
1655 // update the entry's state
1656 serverlist_cache[n].query = SQS_QUERIED;
1659 // returns true, if it's sensible to continue the processing
1660 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1662 serverlist_entry_t *entry;
1664 // ignore the rest of the message if the serverlist is full
1665 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1667 // also ignore it if we have already queried it (other master server response)
1668 for( n = 0 ; n < serverlist_cachecount ; n++ )
1669 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1672 if( n < serverlist_cachecount ) {
1673 // the entry has already been queried once or
1677 if (serverlist_maxcachecount <= n)
1679 serverlist_maxcachecount += 64;
1680 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1683 entry = &serverlist_cache[n];
1685 memset(entry, 0, sizeof(*entry));
1686 entry->protocol = protocol;
1687 // store the data the engine cares about (address and ping)
1688 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1690 entry->info.isfavorite = isfavorite;
1692 // no, then reset the ping right away
1693 entry->info.ping = -1;
1694 // we also want to increase the serverlist_cachecount then
1695 serverlist_cachecount++;
1698 entry->query = SQS_QUERYING;
1703 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1706 if (serverlist_consoleoutput)
1707 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1710 char ipstring [128];
1713 if (data[0] == '\\')
1715 unsigned short port = data[5] * 256 + data[6];
1717 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1718 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1720 // move on to next address in packet
1725 else if (data[0] == '/' && isextended && length >= 19)
1727 unsigned short port = data[17] * 256 + data[18];
1735 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1737 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1740 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1741 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1742 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1748 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1749 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1750 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1755 // move on to next address in packet
1761 Con_Print("Error while parsing the server list\n");
1765 if (serverlist_consoleoutput && developer_networking.integer)
1766 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1768 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1774 // begin or resume serverlist queries
1775 serverlist_querysleep = false;
1776 serverlist_querywaittime = realtime + 3;
1780 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1782 qboolean fromserver;
1784 char *string, addressstring2[128];
1785 char stringbuf[16384];
1786 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1789 char infostringvalue[MAX_INPUTLINE];
1795 // quakeworld ingame packet
1796 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1798 // convert the address to a string incase we need it
1799 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1801 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1803 // received a command string - strip off the packaging and put it
1804 // into our string buffer with NULL termination
1807 length = min(length, (int)sizeof(stringbuf) - 1);
1808 memcpy(stringbuf, data, length);
1809 stringbuf[length] = 0;
1812 if (developer_networking.integer)
1814 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1815 Com_HexDumpToConsole(data, length);
1818 sendlength = sizeof(senddata) - 4;
1819 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1821 case CRYPTO_NOMATCH:
1827 memcpy(senddata, "\377\377\377\377", 4);
1828 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1831 case CRYPTO_DISCARD:
1834 memcpy(senddata, "\377\377\377\377", 4);
1835 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1839 case CRYPTO_REPLACE:
1840 string = senddata+4;
1841 length = (int)sendlength;
1845 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1848 for (j = 0;j < MAX_RCONS;j++)
1850 // note: this value from i is used outside the loop too...
1851 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1852 if(cls.rcon_commands[i][0])
1853 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1862 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1863 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1865 e = strchr(rcon_password.string, ' ');
1866 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1868 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1872 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1873 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1874 cls.rcon_commands[i][0] = 0;
1877 for (k = 0;k < MAX_RCONS;k++)
1878 if(cls.rcon_commands[k][0])
1879 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1884 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1885 // extend the timeout on other requests as we asked for a challenge
1886 for (l = 0;l < MAX_RCONS;l++)
1887 if(cls.rcon_commands[l][0])
1888 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1889 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1892 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1896 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1898 // darkplaces or quake3
1899 char protocolnames[1400];
1900 Protocol_Names(protocolnames, sizeof(protocolnames));
1901 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1903 M_Update_Return_Reason("Got challenge response");
1905 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1906 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1907 // TODO: add userinfo stuff here instead of using NQ commands?
1908 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);
1911 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1913 // darkplaces or quake3
1915 M_Update_Return_Reason("Accepted");
1917 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1920 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1922 char rejectreason[128];
1923 cls.connect_trying = false;
1925 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1926 memcpy(rejectreason, string, length);
1927 rejectreason[length] = 0;
1929 M_Update_Return_Reason(rejectreason);
1934 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1936 serverlist_info_t *info;
1941 // search the cache for this server and update it
1942 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1946 info = &serverlist_cache[n].info;
1951 info->qcstatus[0] = 0;
1952 info->players[0] = 0;
1953 info->protocol = -1;
1954 info->numplayers = 0;
1956 info->maxplayers = 0;
1957 info->gameversion = 0;
1959 p = strchr(string, '\n');
1962 *p = 0; // cut off the string there
1966 Con_Printf("statusResponse without players block?\n");
1968 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1969 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1970 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1971 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1972 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1973 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1974 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1975 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1976 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1977 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1978 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1979 info->numhumans = info->numplayers - max(0, info->numbots);
1980 info->freeslots = info->maxplayers - info->numplayers;
1982 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1986 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1988 serverlist_info_t *info;
1992 // search the cache for this server and update it
1993 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1997 info = &serverlist_cache[n].info;
2002 info->qcstatus[0] = 0;
2003 info->players[0] = 0;
2004 info->protocol = -1;
2005 info->numplayers = 0;
2007 info->maxplayers = 0;
2008 info->gameversion = 0;
2010 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2011 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2012 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2013 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2014 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2015 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2016 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2017 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2018 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2019 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2020 info->numhumans = info->numplayers - max(0, info->numbots);
2021 info->freeslots = info->maxplayers - info->numplayers;
2023 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2027 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2029 // Extract the IP addresses
2032 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2035 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2037 // Extract the IP addresses
2040 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2043 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2045 // Extract the IP addresses
2049 if (serverlist_consoleoutput)
2050 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2051 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2053 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2054 if (serverlist_consoleoutput && developer_networking.integer)
2055 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2057 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2061 // move on to next address in packet
2065 // begin or resume serverlist queries
2066 serverlist_querysleep = false;
2067 serverlist_querywaittime = realtime + 3;
2071 if (!strncmp(string, "extResponse ", 12))
2073 ++cl_net_extresponse_count;
2074 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2075 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2076 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2077 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2080 if (!strncmp(string, "ping", 4))
2082 if (developer_extra.integer)
2083 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2084 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2087 if (!strncmp(string, "ack", 3))
2089 // QuakeWorld compatibility
2090 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2092 // challenge message
2093 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2095 M_Update_Return_Reason("Got QuakeWorld challenge response");
2097 cls.qw_qport = qport.integer;
2098 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2099 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2100 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);
2103 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2107 M_Update_Return_Reason("QuakeWorld Accepted");
2109 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2112 if (length > 2 && !memcmp(string, "n\\", 2))
2115 serverlist_info_t *info;
2119 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2120 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2123 // search the cache for this server and update it
2124 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2128 info = &serverlist_cache[n].info;
2129 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2130 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2131 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2132 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2134 info->numplayers = 0; // updated below
2135 info->numhumans = 0; // updated below
2136 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2137 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2139 // count active players on server
2140 // (we could gather more info, but we're just after the number)
2141 s = strchr(string, '\n');
2145 while (s < string + length)
2147 for (;s < string + length && *s != '\n';s++)
2149 if (s >= string + length)
2157 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2161 if (string[0] == 'n')
2164 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2166 // we may not have liked the packet, but it was a command packet, so
2167 // we're done processing this packet now
2170 // quakeworld ingame packet
2171 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2174 CL_ParseServerMessage();
2177 // netquake control packets, supported for compatibility only
2178 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2182 serverlist_info_t *info;
2187 SZ_Clear(&cl_message);
2188 SZ_Write(&cl_message, data, length);
2189 MSG_BeginReading(&cl_message);
2190 c = MSG_ReadByte(&cl_message);
2194 if (developer_extra.integer)
2195 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2196 if (cls.connect_trying)
2198 lhnetaddress_t clientportaddress;
2199 clientportaddress = *peeraddress;
2200 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2201 // extra ProQuake stuff
2203 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2205 cls.proquake_servermod = 0;
2207 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2209 cls.proquake_serverversion = 0;
2211 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2213 cls.proquake_serverflags = 0;
2214 if (cls.proquake_servermod == 1)
2215 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2216 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2217 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2219 M_Update_Return_Reason("Accepted");
2221 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2225 if (developer_extra.integer)
2226 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2227 cls.connect_trying = false;
2229 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2232 case CCREP_SERVER_INFO:
2233 if (developer_extra.integer)
2234 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2236 // LordHavoc: because the quake server may report weird addresses
2237 // we just ignore it and keep the real address
2238 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2239 // search the cache for this server and update it
2240 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2244 info = &serverlist_cache[n].info;
2245 strlcpy(info->game, "Quake", sizeof(info->game));
2246 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2247 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2248 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2249 info->numplayers = MSG_ReadByte(&cl_message);
2250 info->maxplayers = MSG_ReadByte(&cl_message);
2251 info->protocol = MSG_ReadByte(&cl_message);
2253 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2256 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2257 if (developer_extra.integer)
2258 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2260 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2262 case CCREP_PLAYER_INFO:
2263 // we got a CCREP_PLAYER_INFO??
2264 //if (developer_extra.integer)
2265 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2267 case CCREP_RULE_INFO:
2268 // we got a CCREP_RULE_INFO??
2269 //if (developer_extra.integer)
2270 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2275 SZ_Clear(&cl_message);
2276 // we may not have liked the packet, but it was a valid control
2277 // packet, so we're done processing this packet now
2281 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2282 CL_ParseServerMessage();
2287 void NetConn_QueryQueueFrame(void)
2293 static double querycounter = 0;
2295 if(!net_slist_pause.integer && serverlist_paused)
2296 ServerList_RebuildViewList();
2297 serverlist_paused = net_slist_pause.integer != 0;
2299 if (serverlist_querysleep)
2302 // apply a cool down time after master server replies,
2303 // to avoid messing up the ping times on the servers
2304 if (serverlist_querywaittime > realtime)
2307 // each time querycounter reaches 1.0 issue a query
2308 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2309 maxqueries = (int)querycounter;
2310 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2311 querycounter -= maxqueries;
2313 if( maxqueries == 0 ) {
2317 // scan serverlist and issue queries as needed
2318 serverlist_querysleep = true;
2320 timeouttime = realtime - net_slist_timeout.value;
2321 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2323 serverlist_entry_t *entry = &serverlist_cache[ index ];
2324 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2329 serverlist_querysleep = false;
2330 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2335 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2337 lhnetaddress_t address;
2340 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2341 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2343 for (socket = 0; socket < cl_numsockets ; socket++)
2344 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2348 for (socket = 0; socket < cl_numsockets ; socket++)
2349 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2352 // update the entry fields
2353 entry->querytime = realtime;
2354 entry->querycounter++;
2356 // if not in the slist menu we should print the server to console
2357 if (serverlist_consoleoutput)
2358 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2364 // have we tried to refresh this server?
2365 if( entry->query == SQS_REFRESHING ) {
2366 // yes, so update the reply count (since its not responding anymore)
2368 if(!serverlist_paused)
2369 ServerList_ViewList_Remove(entry);
2371 entry->query = SQS_TIMEDOUT;
2377 void NetConn_ClientFrame(void)
2380 lhnetaddress_t peeraddress;
2381 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2382 NetConn_UpdateSockets();
2383 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2386 if (cls.connect_remainingtries == 0)
2387 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2389 cls.connect_nextsendtime = realtime + 1;
2390 cls.connect_remainingtries--;
2391 if (cls.connect_remainingtries <= -10)
2393 cls.connect_trying = false;
2395 M_Update_Return_Reason("Connect: Failed");
2399 // try challenge first (newer DP server or QW)
2400 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2401 // then try netquake as a fallback (old server, or netquake)
2402 SZ_Clear(&cl_message);
2403 // save space for the header, filled in later
2404 MSG_WriteLong(&cl_message, 0);
2405 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2406 MSG_WriteString(&cl_message, "QUAKE");
2407 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2408 // extended proquake stuff
2409 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2410 // this version matches ProQuake 3.40, the first version to support
2411 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2412 // higher clients, so we pretend we are that version...
2413 MSG_WriteByte(&cl_message, 34); // version * 10
2414 MSG_WriteByte(&cl_message, 0); // flags
2415 MSG_WriteLong(&cl_message, 0); // password
2416 // write the packetsize now...
2417 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2418 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2419 SZ_Clear(&cl_message);
2421 for (i = 0;i < cl_numsockets;i++)
2423 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2425 // R_TimeReport("clientreadnetwork");
2426 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2427 // R_TimeReport("clientparsepacket");
2431 NetConn_QueryQueueFrame();
2433 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2435 Con_Print("Connection timed out\n");
2437 SV_LockThreadMutex();
2438 Host_ShutdownServer ();
2439 SV_UnlockThreadMutex();
2443 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2447 for (i = 0;i < bufferlength - 1;i++)
2451 c = rand () % (127 - 33) + 33;
2452 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2458 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2459 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2461 prvm_prog_t *prog = SVVM_prog;
2463 unsigned int nb_clients = 0, nb_bots = 0, i;
2466 const char *crypto_idstring;
2469 // How many clients are there?
2470 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2472 if (svs.clients[i].active)
2475 if (!svs.clients[i].netconnection)
2481 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2487 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2488 if(*q != '\\' && *q != '\n')
2493 /// \TODO: we should add more information for the full status string
2494 crypto_idstring = Crypto_GetInfoResponseDataString();
2495 length = dpsnprintf(out_msg, out_size,
2496 "\377\377\377\377%s\x0A"
2497 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2498 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2503 fullstatus ? "statusResponse" : "infoResponse",
2504 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2505 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2506 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2507 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2508 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2509 fullstatus ? "\n" : "");
2511 // Make sure it fits in the buffer
2521 savelength = length;
2523 ptr = out_msg + length;
2524 left = (int)out_size - length;
2526 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2528 client_t *cl = &svs.clients[i];
2531 int nameind, cleanind, pingvalue;
2533 char cleanname [sizeof(cl->name)];
2537 // Remove all characters '"' and '\' in the player name
2542 curchar = cl->name[nameind++];
2543 if (curchar != '"' && curchar != '\\')
2545 cleanname[cleanind++] = curchar;
2546 if (cleanind == sizeof(cleanname) - 1)
2549 } while (curchar != '\0');
2550 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2552 pingvalue = (int)(cl->ping * 1000.0f);
2553 if(cl->netconnection)
2554 pingvalue = bound(1, pingvalue, 9999);
2559 ed = PRVM_EDICT_NUM(i + 1);
2560 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2566 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2567 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2572 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2574 if(cl->frags == -666) // spectator
2575 strlcpy(teambuf, " 0", sizeof(teambuf));
2576 else if(cl->colors == 0x44) // red team
2577 strlcpy(teambuf, " 1", sizeof(teambuf));
2578 else if(cl->colors == 0xDD) // blue team
2579 strlcpy(teambuf, " 2", sizeof(teambuf));
2580 else if(cl->colors == 0xCC) // yellow team
2581 strlcpy(teambuf, " 3", sizeof(teambuf));
2582 else if(cl->colors == 0x99) // pink team
2583 strlcpy(teambuf, " 4", sizeof(teambuf));
2585 strlcpy(teambuf, " 0", sizeof(teambuf));
2590 // note: team number is inserted according to SoF2 protocol
2592 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2598 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2607 // turn it into an infoResponse!
2608 out_msg[savelength] = 0;
2609 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2610 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2625 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2627 size_t floodslotnum, bestfloodslotnum;
2628 double bestfloodtime;
2629 lhnetaddress_t noportpeeraddress;
2630 // see if this is a connect flood
2631 noportpeeraddress = *peeraddress;
2632 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2633 bestfloodslotnum = 0;
2634 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2635 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2637 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2639 bestfloodtime = floodlist[floodslotnum].lasttime;
2640 bestfloodslotnum = floodslotnum;
2642 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2644 // this address matches an ongoing flood address
2645 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2649 // renew the ban on this address so it does not expire
2650 // until the flood has subsided
2651 floodlist[floodslotnum].lasttime = realtime;
2653 //Con_Printf("Flood detected!\n");
2656 // the flood appears to have subsided, so allow this
2657 bestfloodslotnum = floodslotnum; // reuse the same slot
2661 // begin a new timeout on this address
2662 floodlist[bestfloodslotnum].address = noportpeeraddress;
2663 floodlist[bestfloodslotnum].lasttime = realtime;
2664 //Con_Printf("Flood detection initiated!\n");
2668 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2670 size_t floodslotnum;
2671 lhnetaddress_t noportpeeraddress;
2672 // see if this is a connect flood
2673 noportpeeraddress = *peeraddress;
2674 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2675 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2677 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2679 // this address matches an ongoing flood address
2681 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2682 floodlist[floodslotnum].lasttime = 0;
2683 //Con_Printf("Flood cleared!\n");
2688 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2690 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2695 t1 = (long) time(NULL);
2696 t2 = strtol(s, NULL, 0);
2697 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2700 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2703 return !memcmp(mdfourbuf, hash, 16);
2706 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2711 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2714 // validate the challenge
2715 for (i = 0;i < MAX_CHALLENGES;i++)
2716 if(challenge[i].time > 0)
2717 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2719 // if the challenge is not recognized, drop the packet
2720 if (i == MAX_CHALLENGES)
2723 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2726 if(memcmp(mdfourbuf, hash, 16))
2729 // unmark challenge to prevent replay attacks
2730 challenge[i].time = 0;
2735 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2737 return !strcmp(password, hash);
2740 /// returns a string describing the user level, or NULL for auth failure
2741 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)
2743 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2744 static char buf[MAX_INPUTLINE];
2746 qboolean restricted = false;
2747 qboolean have_usernames = false;
2748 static char vabuf[1024];
2750 userpass_start = rcon_password.string;
2751 while((userpass_end = strchr(userpass_start, ' ')))
2753 have_usernames = true;
2754 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2756 if(comparator(peeraddress, buf, password, cs, cslen))
2758 userpass_start = userpass_end + 1;
2760 if(userpass_start[0])
2762 userpass_end = userpass_start + strlen(userpass_start);
2763 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2768 have_usernames = false;
2769 userpass_start = rcon_restricted_password.string;
2770 while((userpass_end = strchr(userpass_start, ' ')))
2772 have_usernames = true;
2773 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2775 if(comparator(peeraddress, buf, password, cs, cslen))
2777 userpass_start = userpass_end + 1;
2779 if(userpass_start[0])
2781 userpass_end = userpass_start + strlen(userpass_start);
2782 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2786 return NULL; // DENIED
2789 for(text = s; text != endpos; ++text)
2790 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2791 return NULL; // block possible exploits against the parser/alias expansion
2795 size_t l = strlen(s);
2798 hasquotes = (strchr(s, '"') != NULL);
2799 // sorry, we can't allow these substrings in wildcard expressions,
2800 // as they can mess with the argument counts
2801 text = rcon_restricted_commands.string;
2802 while(COM_ParseToken_Console(&text))
2804 // com_token now contains a pattern to check for...
2805 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2808 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2811 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2813 if(!strcmp(com_token, s))
2816 else // single-arg expression? must match the beginning of the command
2818 if(!strcmp(com_token, s))
2820 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2824 // if we got here, nothing matched!
2832 userpass_startpass = strchr(userpass_start, ':');
2833 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2834 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2836 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2839 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2843 // looks like a legitimate rcon command with the correct password
2844 const char *s_ptr = s;
2845 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2846 while(s_ptr != endpos)
2848 size_t l = strlen(s_ptr);
2850 Con_Printf(" %s;", s_ptr);
2855 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2856 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2859 size_t l = strlen(s);
2862 client_t *host_client_save = host_client;
2863 Cmd_ExecuteString(s, src_command, true);
2864 host_client = host_client_save;
2865 // in case it is a command that changes host_client (like restart)
2869 Con_Rcon_Redirect_End();
2873 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2877 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2879 int i, ret, clientnum, best;
2882 char *s, *string, response[1400], addressstring2[128];
2883 static char stringbuf[16384]; // server only
2884 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2885 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2886 size_t sendlength, response_len;
2887 char infostringvalue[MAX_INPUTLINE];
2893 // convert the address to a string incase we need it
2894 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2896 // see if we can identify the sender as a local player
2897 // (this is necessary for rcon to send a reliable reply if the client is
2898 // actually on the server, not sending remotely)
2899 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2900 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2902 if (i == svs.maxclients)
2905 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2907 // received a command string - strip off the packaging and put it
2908 // into our string buffer with NULL termination
2911 length = min(length, (int)sizeof(stringbuf) - 1);
2912 memcpy(stringbuf, data, length);
2913 stringbuf[length] = 0;
2916 if (developer_extra.integer)
2918 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2919 Com_HexDumpToConsole(data, length);
2922 sendlength = sizeof(senddata) - 4;
2923 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2925 case CRYPTO_NOMATCH:
2931 memcpy(senddata, "\377\377\377\377", 4);
2932 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2935 case CRYPTO_DISCARD:
2938 memcpy(senddata, "\377\377\377\377", 4);
2939 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2943 case CRYPTO_REPLACE:
2944 string = senddata+4;
2945 length = (int)sendlength;
2949 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2951 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2953 if(challenge[i].time > 0)
2954 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2956 if (besttime > challenge[i].time)
2957 besttime = challenge[best = i].time;
2959 // if we did not find an exact match, choose the oldest and
2960 // update address and string
2961 if (i == MAX_CHALLENGES)
2964 challenge[i].address = *peeraddress;
2965 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2969 // flood control: drop if requesting challenge too often
2970 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2973 challenge[i].time = realtime;
2974 // send the challenge
2975 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2976 response_len = strlen(response) + 1;
2977 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2978 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
2981 if (length > 8 && !memcmp(string, "connect\\", 8))
2983 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2987 if(crypto && crypto->authenticated)
2989 // no need to check challenge
2990 if(crypto_developer.integer)
2992 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
2993 crypto->use_aes ? "Encrypted" : "Authenticated",
2995 crypto->client_idfp[0] ? crypto->client_idfp : "-",
2996 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
2997 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
2998 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
2999 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3000 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3006 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3008 // validate the challenge
3009 for (i = 0;i < MAX_CHALLENGES;i++)
3010 if(challenge[i].time > 0)
3011 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
3013 // if the challenge is not recognized, drop the packet
3014 if (i == MAX_CHALLENGES)
3019 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3020 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3022 if(!(islocal || sv_public.integer > -2))
3024 if (developer_extra.integer)
3025 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3026 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
3030 // check engine protocol
3031 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3033 if (developer_extra.integer)
3034 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3035 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3039 // see if this is a duplicate connection request or a disconnected
3040 // client who is rejoining to the same client slot
3041 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3043 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3045 // this is a known client...
3046 if(crypto && crypto->authenticated)
3048 // reject if changing key!
3049 if(client->netconnection->crypto.authenticated)
3052 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3054 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3056 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3058 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3061 if (developer_extra.integer)
3062 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3063 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3070 // reject if downgrading!
3071 if(client->netconnection->crypto.authenticated)
3073 if (developer_extra.integer)
3074 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3075 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3081 // client crashed and is coming back,
3082 // keep their stuff intact
3083 if (developer_extra.integer)
3084 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3085 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3086 if(crypto && crypto->authenticated)
3087 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3088 SV_SendServerinfo(client);
3092 // client is still trying to connect,
3093 // so we send a duplicate reply
3094 if (developer_extra.integer)
3095 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3096 if(crypto && crypto->authenticated)
3097 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3098 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3104 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3107 // find an empty client slot for this new client
3108 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3111 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3113 // allocated connection
3114 if (developer_extra.integer)
3115 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3116 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3117 // now set up the client
3118 if(crypto && crypto->authenticated)
3119 Crypto_FinishInstance(&conn->crypto, crypto);
3120 SV_ConnectClient(clientnum, conn);
3121 NetConn_Heartbeat(1);
3126 // no empty slots found - server is full
3127 if (developer_extra.integer)
3128 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3129 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3133 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3135 const char *challenge = NULL;
3137 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3140 // If there was a challenge in the getinfo message
3141 if (length > 8 && string[7] == ' ')
3142 challenge = string + 8;
3144 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3146 if (developer_extra.integer)
3147 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3148 NetConn_WriteString(mysocket, response, peeraddress);
3152 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3154 const char *challenge = NULL;
3156 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3159 // If there was a challenge in the getinfo message
3160 if (length > 10 && string[9] == ' ')
3161 challenge = string + 10;
3163 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3165 if (developer_extra.integer)
3166 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3167 NetConn_WriteString(mysocket, response, peeraddress);
3171 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3173 char *password = string + 20;
3174 char *timeval = string + 37;
3175 char *s = strchr(timeval, ' ');
3176 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3177 const char *userlevel;
3179 if(rcon_secure.integer > 1)
3183 return true; // invalid packet
3186 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3187 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3190 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3192 char *password = string + 25;
3193 char *challenge = string + 42;
3194 char *s = strchr(challenge, ' ');
3195 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3196 const char *userlevel;
3198 return true; // invalid packet
3201 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3202 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3205 if (length >= 5 && !memcmp(string, "rcon ", 5))
3208 char *s = string + 5;
3209 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3212 if(rcon_secure.integer > 0)
3215 for (i = 0;!ISWHITESPACE(*s);s++)
3216 if (i < (int)sizeof(password) - 1)
3218 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3221 if (!ISWHITESPACE(password[0]))
3223 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3224 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3228 if (!strncmp(string, "extResponse ", 12))
3230 ++sv_net_extresponse_count;
3231 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3232 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3233 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3234 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3237 if (!strncmp(string, "ping", 4))
3239 if (developer_extra.integer)
3240 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3241 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3244 if (!strncmp(string, "ack", 3))
3246 // we may not have liked the packet, but it was a command packet, so
3247 // we're done processing this packet now
3250 // netquake control packets, supported for compatibility only, and only
3251 // when running game protocols that are normally served via this connection
3253 // (this protects more modern protocols against being used for
3254 // Quake packet flood Denial Of Service attacks)
3255 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)
3259 const char *protocolname;
3262 SZ_Clear(&sv_message);
3263 SZ_Write(&sv_message, data, length);
3264 MSG_BeginReading(&sv_message);
3265 c = MSG_ReadByte(&sv_message);
3269 if (developer_extra.integer)
3270 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3271 if(!(islocal || sv_public.integer > -2))
3273 if (developer_extra.integer)
3274 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3275 SZ_Clear(&sv_message);
3276 // save space for the header, filled in later
3277 MSG_WriteLong(&sv_message, 0);
3278 MSG_WriteByte(&sv_message, CCREP_REJECT);
3279 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3280 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3281 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3282 SZ_Clear(&sv_message);
3286 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3287 protocolnumber = MSG_ReadByte(&sv_message);
3288 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3290 if (developer_extra.integer)
3291 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3292 SZ_Clear(&sv_message);
3293 // save space for the header, filled in later
3294 MSG_WriteLong(&sv_message, 0);
3295 MSG_WriteByte(&sv_message, CCREP_REJECT);
3296 MSG_WriteString(&sv_message, "Incompatible version.\n");
3297 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3298 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3299 SZ_Clear(&sv_message);
3303 // see if this connect request comes from a known client
3304 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3306 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3308 // this is either a duplicate connection request
3309 // or coming back from a timeout
3310 // (if so, keep their stuff intact)
3312 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3313 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3315 if (developer_extra.integer)
3316 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3317 SZ_Clear(&sv_message);
3318 // save space for the header, filled in later
3319 MSG_WriteLong(&sv_message, 0);
3320 MSG_WriteByte(&sv_message, CCREP_REJECT);
3321 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3322 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3323 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3324 SZ_Clear(&sv_message);
3329 if (developer_extra.integer)
3330 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3331 SZ_Clear(&sv_message);
3332 // save space for the header, filled in later
3333 MSG_WriteLong(&sv_message, 0);
3334 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3335 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3336 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3337 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3338 SZ_Clear(&sv_message);
3340 // if client is already spawned, re-send the
3341 // serverinfo message as they'll need it to play
3343 SV_SendServerinfo(client);
3348 // this is a new client, check for connection flood
3349 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3352 // find a slot for the new client
3353 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3356 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3358 // connect to the client
3359 // everything is allocated, just fill in the details
3360 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3361 if (developer_extra.integer)
3362 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3363 // send back the info about the server connection
3364 SZ_Clear(&sv_message);
3365 // save space for the header, filled in later
3366 MSG_WriteLong(&sv_message, 0);
3367 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3368 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3369 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3370 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3371 SZ_Clear(&sv_message);
3372 // now set up the client struct
3373 SV_ConnectClient(clientnum, conn);
3374 NetConn_Heartbeat(1);
3379 if (developer_extra.integer)
3380 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3381 // no room; try to let player know
3382 SZ_Clear(&sv_message);
3383 // save space for the header, filled in later
3384 MSG_WriteLong(&sv_message, 0);
3385 MSG_WriteByte(&sv_message, CCREP_REJECT);
3386 MSG_WriteString(&sv_message, "Server is full.\n");
3387 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3388 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3389 SZ_Clear(&sv_message);
3391 case CCREQ_SERVER_INFO:
3392 if (developer_extra.integer)
3393 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3394 if(!(islocal || sv_public.integer > -1))
3397 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3400 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3403 char myaddressstring[128];
3404 if (developer_extra.integer)
3405 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3406 SZ_Clear(&sv_message);
3407 // save space for the header, filled in later
3408 MSG_WriteLong(&sv_message, 0);
3409 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3410 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3411 MSG_WriteString(&sv_message, myaddressstring);
3412 MSG_WriteString(&sv_message, hostname.string);
3413 MSG_WriteString(&sv_message, sv.name);
3414 // How many clients are there?
3415 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3416 if (svs.clients[i].active)
3418 MSG_WriteByte(&sv_message, numclients);
3419 MSG_WriteByte(&sv_message, svs.maxclients);
3420 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3421 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3422 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3423 SZ_Clear(&sv_message);
3426 case CCREQ_PLAYER_INFO:
3427 if (developer_extra.integer)
3428 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3429 if(!(islocal || sv_public.integer > -1))
3432 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3437 int playerNumber, activeNumber, clientNumber;
3440 playerNumber = MSG_ReadByte(&sv_message);
3442 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3443 if (client->active && ++activeNumber == playerNumber)
3445 if (clientNumber != svs.maxclients)
3447 SZ_Clear(&sv_message);
3448 // save space for the header, filled in later
3449 MSG_WriteLong(&sv_message, 0);
3450 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3451 MSG_WriteByte(&sv_message, playerNumber);
3452 MSG_WriteString(&sv_message, client->name);
3453 MSG_WriteLong(&sv_message, client->colors);
3454 MSG_WriteLong(&sv_message, client->frags);
3455 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3456 if(sv_status_privacy.integer)
3457 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3459 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3460 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3461 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3462 SZ_Clear(&sv_message);
3466 case CCREQ_RULE_INFO:
3467 if (developer_extra.integer)
3468 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3469 if(!(islocal || sv_public.integer > -1))
3472 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3479 // find the search start location
3480 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3481 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3483 // send the response
3484 SZ_Clear(&sv_message);
3485 // save space for the header, filled in later
3486 MSG_WriteLong(&sv_message, 0);
3487 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3490 MSG_WriteString(&sv_message, var->name);
3491 MSG_WriteString(&sv_message, var->string);
3493 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3494 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3495 SZ_Clear(&sv_message);
3499 if (developer_extra.integer)
3500 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3501 if (sv.active && !rcon_secure.integer)
3503 char password[2048];
3507 const char *userlevel;
3508 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3509 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3511 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3512 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3513 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3520 SZ_Clear(&sv_message);
3521 // we may not have liked the packet, but it was a valid control
3522 // packet, so we're done processing this packet now
3527 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3529 SV_ReadClientMessage();
3536 void NetConn_ServerFrame(void)
3539 lhnetaddress_t peeraddress;
3540 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3541 for (i = 0;i < sv_numsockets;i++)
3542 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3543 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3544 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3546 // never timeout loopback connections
3547 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3549 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3550 SV_DropClient(false);
3555 void NetConn_SleepMicroseconds(int microseconds)
3557 LHNET_SleepUntilPacket_Microseconds(microseconds);
3561 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3565 lhnetaddress_t masteraddress;
3566 lhnetaddress_t broadcastaddress;
3569 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3572 // 26000 is the default quake server port, servers on other ports will not
3574 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3575 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3579 for (i = 0;i < cl_numsockets;i++)
3583 const char *cmdname, *extraoptions;
3584 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3586 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3588 // search LAN for Quake servers
3589 SZ_Clear(&cl_message);
3590 // save space for the header, filled in later
3591 MSG_WriteLong(&cl_message, 0);
3592 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3593 MSG_WriteString(&cl_message, "QUAKE");
3594 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3595 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3596 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3597 SZ_Clear(&cl_message);
3599 // search LAN for DarkPlaces servers
3600 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3603 // build the getservers message to send to the dpmaster master servers
3604 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3606 cmdname = "getserversExt";
3607 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3611 cmdname = "getservers";
3614 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3617 for (masternum = 0;sv_masters[masternum].name;masternum++)
3619 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3622 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3626 // search favorite servers
3627 for(j = 0; j < nFavorites; ++j)
3629 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3631 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3632 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3639 // only query QuakeWorld servers when the user wants to
3642 for (i = 0;i < cl_numsockets;i++)
3646 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3648 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3650 // search LAN for QuakeWorld servers
3651 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3653 // build the getservers message to send to the qwmaster master servers
3654 // note this has no -1 prefix, and the trailing nul byte is sent
3655 dpsnprintf(request, sizeof(request), "c\n");
3659 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3661 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3663 if (m_state != m_slist)
3665 char lookupstring[128];
3666 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3667 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3670 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3674 // search favorite servers
3675 for(j = 0; j < nFavorites; ++j)
3677 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3679 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3681 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3682 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3689 if (!masterquerycount)
3691 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3692 M_Update_Return_Reason("No network");
3697 void NetConn_Heartbeat(int priority)
3699 lhnetaddress_t masteraddress;
3701 lhnetsocket_t *mysocket;
3703 // if it's a state change (client connected), limit next heartbeat to no
3704 // more than 30 sec in the future
3705 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3706 nextheartbeattime = realtime + 30.0;
3708 // limit heartbeatperiod to 30 to 270 second range,
3709 // lower limit is to avoid abusing master servers with excess traffic,
3710 // upper limit is to avoid timing out on the master server (which uses
3712 if (sv_heartbeatperiod.value < 30)
3713 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3714 if (sv_heartbeatperiod.value > 270)
3715 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3717 // make advertising optional and don't advertise singleplayer games, and
3718 // only send a heartbeat as often as the admin wants
3719 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3721 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3722 for (masternum = 0;sv_masters[masternum].name;masternum++)
3723 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3724 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3728 static void Net_Heartbeat_f(void)
3731 NetConn_Heartbeat(2);
3733 Con_Print("No server running, can not heartbeat to master server.\n");
3736 static void PrintStats(netconn_t *conn)
3738 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3739 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3741 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3742 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3743 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3744 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3745 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3746 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3747 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3748 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3749 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3750 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3753 void Net_Stats_f(void)
3756 Con_Print("connections =\n");
3757 for (conn = netconn_list;conn;conn = conn->next)
3762 void Net_Refresh_f(void)
3764 if (m_state != m_slist) {
3765 Con_Print("Sending new requests to master servers\n");
3766 ServerList_QueryList(false, true, false, true);
3767 Con_Print("Listening for replies...\n");
3769 ServerList_QueryList(false, true, false, false);
3772 void Net_Slist_f(void)
3774 ServerList_ResetMasks();
3775 serverlist_sortbyfield = SLIF_PING;
3776 serverlist_sortflags = 0;
3777 if (m_state != m_slist) {
3778 Con_Print("Sending requests to master servers\n");
3779 ServerList_QueryList(true, true, false, true);
3780 Con_Print("Listening for replies...\n");
3782 ServerList_QueryList(true, true, false, false);
3785 void Net_SlistQW_f(void)
3787 ServerList_ResetMasks();
3788 serverlist_sortbyfield = SLIF_PING;
3789 serverlist_sortflags = 0;
3790 if (m_state != m_slist) {
3791 Con_Print("Sending requests to master servers\n");
3792 ServerList_QueryList(true, false, true, true);
3793 serverlist_consoleoutput = true;
3794 Con_Print("Listening for replies...\n");
3796 ServerList_QueryList(true, false, true, false);
3800 void NetConn_Init(void)
3803 lhnetaddress_t tempaddress;
3804 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3805 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3807 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3808 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3809 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3811 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3812 Cvar_RegisterVariable(&net_test);
3813 Cvar_RegisterVariable(&net_usesizelimit);
3814 Cvar_RegisterVariable(&net_burstreserve);
3815 Cvar_RegisterVariable(&rcon_restricted_password);
3816 Cvar_RegisterVariable(&rcon_restricted_commands);
3817 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3818 Cvar_RegisterVariable(&net_slist_queriespersecond);
3819 Cvar_RegisterVariable(&net_slist_queriesperframe);
3820 Cvar_RegisterVariable(&net_slist_timeout);
3821 Cvar_RegisterVariable(&net_slist_maxtries);
3822 Cvar_RegisterVariable(&net_slist_favorites);
3823 Cvar_RegisterVariable(&net_slist_pause);
3824 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3825 Cvar_RegisterVariable(&net_tos_dscp);
3826 Cvar_RegisterVariable(&net_messagetimeout);
3827 Cvar_RegisterVariable(&net_connecttimeout);
3828 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3829 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3830 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3831 Cvar_RegisterVariable(&cl_netlocalping);
3832 Cvar_RegisterVariable(&cl_netpacketloss_send);
3833 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3834 Cvar_RegisterVariable(&hostname);
3835 Cvar_RegisterVariable(&developer_networking);
3836 Cvar_RegisterVariable(&cl_netport);
3837 Cvar_RegisterVariable(&sv_netport);
3838 Cvar_RegisterVariable(&net_address);
3839 Cvar_RegisterVariable(&net_address_ipv6);
3840 Cvar_RegisterVariable(&sv_public);
3841 Cvar_RegisterVariable(&sv_public_rejectreason);
3842 Cvar_RegisterVariable(&sv_heartbeatperiod);
3843 for (i = 0;sv_masters[i].name;i++)
3844 Cvar_RegisterVariable(&sv_masters[i]);
3845 Cvar_RegisterVariable(&gameversion);
3846 Cvar_RegisterVariable(&gameversion_min);
3847 Cvar_RegisterVariable(&gameversion_max);
3848 // 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.
3849 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3851 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3853 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3854 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3857 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3859 // 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
3860 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3862 i = atoi(com_argv[i + 1]);
3863 if (i >= 0 && i < 65536)
3865 Con_Printf("-port option used, setting port cvar to %i\n", i);
3866 Cvar_SetValueQuick(&sv_netport, i);
3869 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3873 cl_message.data = cl_message_buf;
3874 cl_message.maxsize = sizeof(cl_message_buf);
3875 cl_message.cursize = 0;
3876 sv_message.data = sv_message_buf;
3877 sv_message.maxsize = sizeof(sv_message_buf);
3878 sv_message.cursize = 0;
3880 if (Thread_HasThreads())
3881 netconn_mutex = Thread_CreateMutex();
3884 void NetConn_Shutdown(void)
3886 NetConn_CloseClientPorts();
3887 NetConn_CloseServerPorts();
3890 Thread_DestroyMutex(netconn_mutex);
3891 netconn_mutex = NULL;