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, 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, 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);
1003 #ifndef NOSUPPORTIPV6
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);
1075 #ifndef NOSUPPORTIPV6
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 unsigned int sequence, sequence_ack;
1216 qboolean 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) != 0;
1238 reliable_ack = (sequence_ack >> 31) != 0;
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);
1251 // If too may packets have been dropped, only write the
1252 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1253 // Because there's no point in writing more than
1254 // these as the netgraph is going to be full anyway.
1255 if (count > NETGRAPH_PACKETS)
1256 count = NETGRAPH_PACKETS;
1259 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1260 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1261 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1262 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1263 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1264 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1267 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1268 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1269 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1270 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1271 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1272 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1273 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1275 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1276 if (net_test.integer)
1278 if (conn->cleartime < realtime)
1279 conn->cleartime = realtime;
1282 if (reliable_ack == conn->qw.reliable_sequence)
1284 // received, now we will be able to send another reliable message
1285 conn->sendMessageLength = 0;
1286 conn->reliableMessagesReceived++;
1288 conn->qw.incoming_sequence = sequence;
1289 if (conn == cls.netcon)
1290 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1291 conn->qw.incoming_acknowledged = sequence_ack;
1292 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1293 if (reliable_message)
1294 conn->qw.incoming_reliable_sequence ^= 1;
1295 conn->lastMessageTime = realtime;
1296 conn->timeout = realtime + newtimeout;
1297 conn->unreliableMessagesReceived++;
1298 if (conn == cls.netcon)
1300 SZ_Clear(&cl_message);
1301 SZ_Write(&cl_message, data, (int)length);
1302 MSG_BeginReading(&cl_message);
1306 SZ_Clear(&sv_message);
1307 SZ_Write(&sv_message, data, (int)length);
1308 MSG_BeginReading(&sv_message);
1316 unsigned int sequence;
1321 originallength = (int)length;
1322 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1328 qlength = (unsigned int)BuffBigLong(data);
1329 flags = qlength & ~NETFLAG_LENGTH_MASK;
1330 qlength &= NETFLAG_LENGTH_MASK;
1331 // control packets were already handled
1332 if (!(flags & NETFLAG_CTL) && qlength == length)
1334 sequence = BuffBigLong(data + 4);
1335 conn->packetsReceived++;
1338 if (flags & NETFLAG_UNRELIABLE)
1340 if (sequence >= conn->nq.unreliableReceiveSequence)
1342 if (sequence > conn->nq.unreliableReceiveSequence)
1344 count = sequence - conn->nq.unreliableReceiveSequence;
1345 conn->droppedDatagrams += count;
1346 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1347 // If too may packets have been dropped, only write the
1348 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1349 // Because there's no point in writing more than
1350 // these as the netgraph is going to be full anyway.
1351 if (count > NETGRAPH_PACKETS)
1352 count = NETGRAPH_PACKETS;
1355 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1356 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1357 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1358 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1359 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1360 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1363 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1364 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1365 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1366 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1367 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1368 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1369 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1371 conn->nq.unreliableReceiveSequence = sequence + 1;
1372 conn->lastMessageTime = realtime;
1373 conn->timeout = realtime + newtimeout;
1374 conn->unreliableMessagesReceived++;
1377 if (conn == cls.netcon)
1379 SZ_Clear(&cl_message);
1380 SZ_Write(&cl_message, data, (int)length);
1381 MSG_BeginReading(&cl_message);
1385 SZ_Clear(&sv_message);
1386 SZ_Write(&sv_message, data, (int)length);
1387 MSG_BeginReading(&sv_message);
1393 // Con_DPrint("Got a stale datagram\n");
1396 else if (flags & NETFLAG_ACK)
1398 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1399 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1401 if (sequence == (conn->nq.sendSequence - 1))
1403 if (sequence == conn->nq.ackSequence)
1405 conn->nq.ackSequence++;
1406 if (conn->nq.ackSequence != conn->nq.sendSequence)
1407 Con_DPrint("ack sequencing error\n");
1408 conn->lastMessageTime = realtime;
1409 conn->timeout = realtime + newtimeout;
1410 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1412 unsigned int packetLen;
1413 unsigned int dataLen;
1416 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1417 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1419 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1421 dataLen = conn->sendMessageLength;
1426 dataLen = MAX_PACKETFRAGMENT;
1430 packetLen = NET_HEADERSIZE + dataLen;
1432 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1433 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1434 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1436 conn->nq.sendSequence++;
1438 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1439 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1441 conn->lastSendTime = realtime;
1442 conn->packetsSent++;
1446 conn->sendMessageLength = 0;
1449 // Con_DPrint("Duplicate ACK received\n");
1452 // Con_DPrint("Stale ACK received\n");
1455 else if (flags & NETFLAG_DATA)
1457 unsigned char temppacket[8];
1458 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1459 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1461 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1463 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1464 StoreBigLong(temppacket + 4, sequence);
1465 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1467 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1468 if (sequence == conn->nq.receiveSequence)
1470 conn->lastMessageTime = realtime;
1471 conn->timeout = realtime + newtimeout;
1472 conn->nq.receiveSequence++;
1473 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1474 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1475 conn->receiveMessageLength += (int)length;
1477 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1478 "Dropping the message!\n", sequence );
1479 conn->receiveMessageLength = 0;
1482 if (flags & NETFLAG_EOM)
1484 conn->reliableMessagesReceived++;
1485 length = conn->receiveMessageLength;
1486 conn->receiveMessageLength = 0;
1489 if (conn == cls.netcon)
1491 SZ_Clear(&cl_message);
1492 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1493 MSG_BeginReading(&cl_message);
1497 SZ_Clear(&sv_message);
1498 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1499 MSG_BeginReading(&sv_message);
1506 conn->receivedDuplicateCount++;
1514 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1517 cls.connect_trying = false;
1519 M_Update_Return_Reason("");
1521 // the connection request succeeded, stop current connection and set up a new connection
1523 // if we're connecting to a remote server, shut down any local server
1524 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1526 SV_LockThreadMutex();
1527 Host_ShutdownServer ();
1528 SV_UnlockThreadMutex();
1530 // allocate a net connection to keep track of things
1531 cls.netcon = NetConn_Open(mysocket, peeraddress);
1532 crypto = &cls.netcon->crypto;
1533 if(cls.crypto.authenticated)
1535 Crypto_FinishInstance(crypto, &cls.crypto);
1536 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1537 crypto->use_aes ? "Encrypted" : "Authenticated",
1538 cls.netcon->address,
1539 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1540 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1541 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1542 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1543 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1544 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1547 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1548 key_dest = key_game;
1552 cls.demonum = -1; // not in the demo loop now
1553 cls.state = ca_connected;
1554 cls.signon = 0; // need all the signon messages before playing
1555 cls.protocol = initialprotocol;
1556 // reset move sequence numbering on this new connection
1557 cls.servermovesequence = 0;
1558 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1559 Cmd_ForwardStringToServer("new");
1560 if (cls.protocol == PROTOCOL_QUAKE)
1562 // write a keepalive (clc_nop) as it seems to greatly improve the
1563 // chances of connecting to a netquake server
1565 unsigned char buf[4];
1566 memset(&msg, 0, sizeof(msg));
1568 msg.maxsize = sizeof(buf);
1569 MSG_WriteChar(&msg, clc_nop);
1570 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1574 int NetConn_IsLocalGame(void)
1576 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1582 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1586 serverlist_entry_t *entry = NULL;
1588 // search the cache for this server and update it
1589 for (n = 0;n < serverlist_cachecount;n++) {
1590 entry = &serverlist_cache[ n ];
1591 if (!strcmp(addressstring, entry->info.cname))
1595 if (n == serverlist_cachecount)
1597 // LAN search doesnt require an answer from the master server so we wont
1598 // know the ping nor will it be initialized already...
1601 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1604 if (serverlist_maxcachecount <= serverlist_cachecount)
1606 serverlist_maxcachecount += 64;
1607 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1609 entry = &serverlist_cache[n];
1611 memset(entry, 0, sizeof(*entry));
1612 // store the data the engine cares about (address and ping)
1613 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1614 entry->info.ping = 100000;
1615 entry->querytime = realtime;
1616 // if not in the slist menu we should print the server to console
1617 if (serverlist_consoleoutput)
1618 Con_Printf("querying %s\n", addressstring);
1619 ++serverlist_cachecount;
1621 // if this is the first reply from this server, count it as having replied
1622 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1623 pingtime = bound(0, pingtime, 9999);
1624 if (entry->query == SQS_REFRESHING) {
1625 entry->info.ping = pingtime;
1626 entry->query = SQS_QUERIED;
1628 // convert to unsigned to catch the -1
1629 // 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]
1630 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1634 // other server info is updated by the caller
1638 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1640 serverlist_entry_t *entry = &serverlist_cache[n];
1641 serverlist_info_t *info = &entry->info;
1642 // update description strings for engine menu and console output
1643 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);
1644 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1646 info->gameversion != gameversion.integer
1649 gameversion_min.integer >= 0 // min/max range set by user/mod?
1650 && gameversion_max.integer >= 0
1651 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1652 && gameversion_max.integer >= info->gameversion
1655 info->mod, info->map);
1656 if (entry->query == SQS_QUERIED)
1658 if(!serverlist_paused)
1659 ServerList_ViewList_Remove(entry);
1661 // if not in the slist menu we should print the server to console (if wanted)
1662 else if( serverlist_consoleoutput )
1663 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1664 // and finally, update the view set
1665 if(!serverlist_paused)
1666 ServerList_ViewList_Insert( entry );
1667 // update the entry's state
1668 serverlist_cache[n].query = SQS_QUERIED;
1671 // returns true, if it's sensible to continue the processing
1672 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1674 serverlist_entry_t *entry;
1676 // ignore the rest of the message if the serverlist is full
1677 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1679 // also ignore it if we have already queried it (other master server response)
1680 for( n = 0 ; n < serverlist_cachecount ; n++ )
1681 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1684 if( n < serverlist_cachecount ) {
1685 // the entry has already been queried once or
1689 if (serverlist_maxcachecount <= n)
1691 serverlist_maxcachecount += 64;
1692 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1695 entry = &serverlist_cache[n];
1697 memset(entry, 0, sizeof(*entry));
1698 entry->protocol = protocol;
1699 // store the data the engine cares about (address and ping)
1700 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1702 entry->info.isfavorite = isfavorite;
1704 // no, then reset the ping right away
1705 entry->info.ping = -1;
1706 // we also want to increase the serverlist_cachecount then
1707 serverlist_cachecount++;
1710 entry->query = SQS_QUERYING;
1715 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1718 if (serverlist_consoleoutput)
1719 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1722 char ipstring [128];
1725 if (data[0] == '\\')
1727 unsigned short port = data[5] * 256 + data[6];
1729 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1730 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1732 // move on to next address in packet
1737 else if (data[0] == '/' && isextended && length >= 19)
1739 unsigned short port = data[17] * 256 + data[18];
1747 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1749 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1752 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1753 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1754 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1760 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1761 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1762 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1767 // move on to next address in packet
1773 Con_Print("Error while parsing the server list\n");
1777 if (serverlist_consoleoutput && developer_networking.integer)
1778 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1780 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1786 // begin or resume serverlist queries
1787 serverlist_querysleep = false;
1788 serverlist_querywaittime = realtime + 3;
1792 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1794 qboolean fromserver;
1796 char *string, addressstring2[128];
1797 char stringbuf[16384];
1798 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1801 char infostringvalue[MAX_INPUTLINE];
1807 // quakeworld ingame packet
1808 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1810 // convert the address to a string incase we need it
1811 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1813 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1815 // received a command string - strip off the packaging and put it
1816 // into our string buffer with NULL termination
1819 length = min(length, (int)sizeof(stringbuf) - 1);
1820 memcpy(stringbuf, data, length);
1821 stringbuf[length] = 0;
1824 if (developer_networking.integer)
1826 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1827 Com_HexDumpToConsole(data, length);
1830 sendlength = sizeof(senddata) - 4;
1831 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1833 case CRYPTO_NOMATCH:
1839 memcpy(senddata, "\377\377\377\377", 4);
1840 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1843 case CRYPTO_DISCARD:
1846 memcpy(senddata, "\377\377\377\377", 4);
1847 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1851 case CRYPTO_REPLACE:
1852 string = senddata+4;
1853 length = (int)sendlength;
1857 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1860 for (j = 0;j < MAX_RCONS;j++)
1862 // note: this value from i is used outside the loop too...
1863 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1864 if(cls.rcon_commands[i][0])
1865 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1874 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1875 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1877 e = strchr(rcon_password.string, ' ');
1878 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1880 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1884 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1885 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1886 cls.rcon_commands[i][0] = 0;
1889 for (k = 0;k < MAX_RCONS;k++)
1890 if(cls.rcon_commands[k][0])
1891 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1896 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1897 // extend the timeout on other requests as we asked for a challenge
1898 for (l = 0;l < MAX_RCONS;l++)
1899 if(cls.rcon_commands[l][0])
1900 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1901 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1904 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1908 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1910 // darkplaces or quake3
1911 char protocolnames[1400];
1912 Protocol_Names(protocolnames, sizeof(protocolnames));
1913 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1915 M_Update_Return_Reason("Got challenge response");
1917 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1918 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1919 // TODO: add userinfo stuff here instead of using NQ commands?
1920 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);
1923 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1925 // darkplaces or quake3
1927 M_Update_Return_Reason("Accepted");
1929 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1932 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1934 char rejectreason[128];
1935 cls.connect_trying = false;
1937 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1938 memcpy(rejectreason, string, length);
1939 rejectreason[length] = 0;
1941 M_Update_Return_Reason(rejectreason);
1946 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1948 serverlist_info_t *info;
1953 // search the cache for this server and update it
1954 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1958 info = &serverlist_cache[n].info;
1963 info->qcstatus[0] = 0;
1964 info->players[0] = 0;
1965 info->protocol = -1;
1966 info->numplayers = 0;
1968 info->maxplayers = 0;
1969 info->gameversion = 0;
1971 p = strchr(string, '\n');
1974 *p = 0; // cut off the string there
1978 Con_Printf("statusResponse without players block?\n");
1980 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1981 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1982 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1983 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1984 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1985 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1986 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1987 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1988 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1989 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1990 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1991 info->numhumans = info->numplayers - max(0, info->numbots);
1992 info->freeslots = info->maxplayers - info->numplayers;
1994 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1998 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2000 serverlist_info_t *info;
2004 // search the cache for this server and update it
2005 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2009 info = &serverlist_cache[n].info;
2014 info->qcstatus[0] = 0;
2015 info->players[0] = 0;
2016 info->protocol = -1;
2017 info->numplayers = 0;
2019 info->maxplayers = 0;
2020 info->gameversion = 0;
2022 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2023 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2024 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2025 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2026 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2027 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2028 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2029 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2030 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2031 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2032 info->numhumans = info->numplayers - max(0, info->numbots);
2033 info->freeslots = info->maxplayers - info->numplayers;
2035 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2039 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2041 // Extract the IP addresses
2044 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2047 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2049 // Extract the IP addresses
2052 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2055 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2057 // Extract the IP addresses
2061 if (serverlist_consoleoutput)
2062 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2063 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2065 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2066 if (serverlist_consoleoutput && developer_networking.integer)
2067 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2069 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2073 // move on to next address in packet
2077 // begin or resume serverlist queries
2078 serverlist_querysleep = false;
2079 serverlist_querywaittime = realtime + 3;
2083 if (!strncmp(string, "extResponse ", 12))
2085 ++cl_net_extresponse_count;
2086 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2087 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2088 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2089 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2092 if (!strncmp(string, "ping", 4))
2094 if (developer_extra.integer)
2095 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2096 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2099 if (!strncmp(string, "ack", 3))
2101 // QuakeWorld compatibility
2102 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2104 // challenge message
2105 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2107 M_Update_Return_Reason("Got QuakeWorld challenge response");
2109 cls.qw_qport = qport.integer;
2110 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2111 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2112 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);
2115 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2119 M_Update_Return_Reason("QuakeWorld Accepted");
2121 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2124 if (length > 2 && !memcmp(string, "n\\", 2))
2127 serverlist_info_t *info;
2131 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2132 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2135 // search the cache for this server and update it
2136 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2140 info = &serverlist_cache[n].info;
2141 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2142 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2143 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2144 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2146 info->numplayers = 0; // updated below
2147 info->numhumans = 0; // updated below
2148 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2149 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2151 // count active players on server
2152 // (we could gather more info, but we're just after the number)
2153 s = strchr(string, '\n');
2157 while (s < string + length)
2159 for (;s < string + length && *s != '\n';s++)
2161 if (s >= string + length)
2169 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2173 if (string[0] == 'n')
2176 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2178 // we may not have liked the packet, but it was a command packet, so
2179 // we're done processing this packet now
2182 // quakeworld ingame packet
2183 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2186 CL_ParseServerMessage();
2189 // netquake control packets, supported for compatibility only
2190 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2194 serverlist_info_t *info;
2199 SZ_Clear(&cl_message);
2200 SZ_Write(&cl_message, data, length);
2201 MSG_BeginReading(&cl_message);
2202 c = MSG_ReadByte(&cl_message);
2206 if (developer_extra.integer)
2207 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2208 if (cls.connect_trying)
2210 lhnetaddress_t clientportaddress;
2211 clientportaddress = *peeraddress;
2212 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2213 // extra ProQuake stuff
2215 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2217 cls.proquake_servermod = 0;
2219 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2221 cls.proquake_serverversion = 0;
2223 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2225 cls.proquake_serverflags = 0;
2226 if (cls.proquake_servermod == 1)
2227 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2228 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2229 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2231 M_Update_Return_Reason("Accepted");
2233 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2237 if (developer_extra.integer)
2238 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2239 cls.connect_trying = false;
2241 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2244 case CCREP_SERVER_INFO:
2245 if (developer_extra.integer)
2246 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2248 // LordHavoc: because the quake server may report weird addresses
2249 // we just ignore it and keep the real address
2250 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2251 // search the cache for this server and update it
2252 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2256 info = &serverlist_cache[n].info;
2257 strlcpy(info->game, "Quake", sizeof(info->game));
2258 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2259 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2260 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2261 info->numplayers = MSG_ReadByte(&cl_message);
2262 info->maxplayers = MSG_ReadByte(&cl_message);
2263 info->protocol = MSG_ReadByte(&cl_message);
2265 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2268 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2269 if (developer_extra.integer)
2270 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2272 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2274 case CCREP_PLAYER_INFO:
2275 // we got a CCREP_PLAYER_INFO??
2276 //if (developer_extra.integer)
2277 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2279 case CCREP_RULE_INFO:
2280 // we got a CCREP_RULE_INFO??
2281 //if (developer_extra.integer)
2282 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2287 SZ_Clear(&cl_message);
2288 // we may not have liked the packet, but it was a valid control
2289 // packet, so we're done processing this packet now
2293 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2294 CL_ParseServerMessage();
2299 void NetConn_QueryQueueFrame(void)
2305 static double querycounter = 0;
2307 if(!net_slist_pause.integer && serverlist_paused)
2308 ServerList_RebuildViewList();
2309 serverlist_paused = net_slist_pause.integer != 0;
2311 if (serverlist_querysleep)
2314 // apply a cool down time after master server replies,
2315 // to avoid messing up the ping times on the servers
2316 if (serverlist_querywaittime > realtime)
2319 // each time querycounter reaches 1.0 issue a query
2320 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2321 maxqueries = (int)querycounter;
2322 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2323 querycounter -= maxqueries;
2325 if( maxqueries == 0 ) {
2329 // scan serverlist and issue queries as needed
2330 serverlist_querysleep = true;
2332 timeouttime = realtime - net_slist_timeout.value;
2333 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2335 serverlist_entry_t *entry = &serverlist_cache[ index ];
2336 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2341 serverlist_querysleep = false;
2342 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2347 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2349 lhnetaddress_t address;
2352 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2353 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2355 for (socket = 0; socket < cl_numsockets ; socket++)
2356 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2360 for (socket = 0; socket < cl_numsockets ; socket++)
2361 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2364 // update the entry fields
2365 entry->querytime = realtime;
2366 entry->querycounter++;
2368 // if not in the slist menu we should print the server to console
2369 if (serverlist_consoleoutput)
2370 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2376 // have we tried to refresh this server?
2377 if( entry->query == SQS_REFRESHING ) {
2378 // yes, so update the reply count (since its not responding anymore)
2380 if(!serverlist_paused)
2381 ServerList_ViewList_Remove(entry);
2383 entry->query = SQS_TIMEDOUT;
2389 void NetConn_ClientFrame(void)
2392 lhnetaddress_t peeraddress;
2393 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2394 NetConn_UpdateSockets();
2395 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2398 if (cls.connect_remainingtries == 0)
2399 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2401 cls.connect_nextsendtime = realtime + 1;
2402 cls.connect_remainingtries--;
2403 if (cls.connect_remainingtries <= -10)
2405 cls.connect_trying = false;
2407 M_Update_Return_Reason("Connect: Failed");
2411 // try challenge first (newer DP server or QW)
2412 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2413 // then try netquake as a fallback (old server, or netquake)
2414 SZ_Clear(&cl_message);
2415 // save space for the header, filled in later
2416 MSG_WriteLong(&cl_message, 0);
2417 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2418 MSG_WriteString(&cl_message, "QUAKE");
2419 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2420 // extended proquake stuff
2421 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2422 // this version matches ProQuake 3.40, the first version to support
2423 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2424 // higher clients, so we pretend we are that version...
2425 MSG_WriteByte(&cl_message, 34); // version * 10
2426 MSG_WriteByte(&cl_message, 0); // flags
2427 MSG_WriteLong(&cl_message, 0); // password
2428 // write the packetsize now...
2429 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2430 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2431 SZ_Clear(&cl_message);
2433 for (i = 0;i < cl_numsockets;i++)
2435 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2437 // R_TimeReport("clientreadnetwork");
2438 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2439 // R_TimeReport("clientparsepacket");
2443 NetConn_QueryQueueFrame();
2445 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2447 Con_Print("Connection timed out\n");
2449 SV_LockThreadMutex();
2450 Host_ShutdownServer ();
2451 SV_UnlockThreadMutex();
2455 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2459 for (i = 0;i < bufferlength - 1;i++)
2463 c = rand () % (127 - 33) + 33;
2464 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2470 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2471 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2473 prvm_prog_t *prog = SVVM_prog;
2475 unsigned int nb_clients = 0, nb_bots = 0, i;
2478 const char *crypto_idstring;
2481 // How many clients are there?
2482 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2484 if (svs.clients[i].active)
2487 if (!svs.clients[i].netconnection)
2493 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2499 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2500 if(*q != '\\' && *q != '\n')
2505 /// \TODO: we should add more information for the full status string
2506 crypto_idstring = Crypto_GetInfoResponseDataString();
2507 length = dpsnprintf(out_msg, out_size,
2508 "\377\377\377\377%s\x0A"
2509 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2510 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2515 fullstatus ? "statusResponse" : "infoResponse",
2516 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2517 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2518 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2519 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2520 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2521 fullstatus ? "\n" : "");
2523 // Make sure it fits in the buffer
2533 savelength = length;
2535 ptr = out_msg + length;
2536 left = (int)out_size - length;
2538 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2540 client_t *cl = &svs.clients[i];
2543 int nameind, cleanind, pingvalue;
2545 char cleanname [sizeof(cl->name)];
2549 // Remove all characters '"' and '\' in the player name
2554 curchar = cl->name[nameind++];
2555 if (curchar != '"' && curchar != '\\')
2557 cleanname[cleanind++] = curchar;
2558 if (cleanind == sizeof(cleanname) - 1)
2561 } while (curchar != '\0');
2562 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2564 pingvalue = (int)(cl->ping * 1000.0f);
2565 if(cl->netconnection)
2566 pingvalue = bound(1, pingvalue, 9999);
2571 ed = PRVM_EDICT_NUM(i + 1);
2572 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2578 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2579 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2584 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2586 if(cl->frags == -666) // spectator
2587 strlcpy(teambuf, " 0", sizeof(teambuf));
2588 else if(cl->colors == 0x44) // red team
2589 strlcpy(teambuf, " 1", sizeof(teambuf));
2590 else if(cl->colors == 0xDD) // blue team
2591 strlcpy(teambuf, " 2", sizeof(teambuf));
2592 else if(cl->colors == 0xCC) // yellow team
2593 strlcpy(teambuf, " 3", sizeof(teambuf));
2594 else if(cl->colors == 0x99) // pink team
2595 strlcpy(teambuf, " 4", sizeof(teambuf));
2597 strlcpy(teambuf, " 0", sizeof(teambuf));
2602 // note: team number is inserted according to SoF2 protocol
2604 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2610 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2619 // turn it into an infoResponse!
2620 out_msg[savelength] = 0;
2621 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2622 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2637 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2639 size_t floodslotnum, bestfloodslotnum;
2640 double bestfloodtime;
2641 lhnetaddress_t noportpeeraddress;
2642 // see if this is a connect flood
2643 noportpeeraddress = *peeraddress;
2644 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2645 bestfloodslotnum = 0;
2646 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2647 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2649 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2651 bestfloodtime = floodlist[floodslotnum].lasttime;
2652 bestfloodslotnum = floodslotnum;
2654 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2656 // this address matches an ongoing flood address
2657 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2661 // renew the ban on this address so it does not expire
2662 // until the flood has subsided
2663 floodlist[floodslotnum].lasttime = realtime;
2665 //Con_Printf("Flood detected!\n");
2668 // the flood appears to have subsided, so allow this
2669 bestfloodslotnum = floodslotnum; // reuse the same slot
2673 // begin a new timeout on this address
2674 floodlist[bestfloodslotnum].address = noportpeeraddress;
2675 floodlist[bestfloodslotnum].lasttime = realtime;
2676 //Con_Printf("Flood detection initiated!\n");
2680 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2682 size_t floodslotnum;
2683 lhnetaddress_t noportpeeraddress;
2684 // see if this is a connect flood
2685 noportpeeraddress = *peeraddress;
2686 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2687 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2689 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2691 // this address matches an ongoing flood address
2693 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2694 floodlist[floodslotnum].lasttime = 0;
2695 //Con_Printf("Flood cleared!\n");
2700 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2702 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2707 t1 = (long) time(NULL);
2708 t2 = strtol(s, NULL, 0);
2709 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2712 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2715 return !memcmp(mdfourbuf, hash, 16);
2718 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2723 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2726 // validate the challenge
2727 for (i = 0;i < MAX_CHALLENGES;i++)
2728 if(challenge[i].time > 0)
2729 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2731 // if the challenge is not recognized, drop the packet
2732 if (i == MAX_CHALLENGES)
2735 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2738 if(memcmp(mdfourbuf, hash, 16))
2741 // unmark challenge to prevent replay attacks
2742 challenge[i].time = 0;
2747 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2749 return !strcmp(password, hash);
2752 /// returns a string describing the user level, or NULL for auth failure
2753 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)
2755 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2756 static char buf[MAX_INPUTLINE];
2758 qboolean restricted = false;
2759 qboolean have_usernames = false;
2760 static char vabuf[1024];
2762 userpass_start = rcon_password.string;
2763 while((userpass_end = strchr(userpass_start, ' ')))
2765 have_usernames = true;
2766 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2768 if(comparator(peeraddress, buf, password, cs, cslen))
2770 userpass_start = userpass_end + 1;
2772 if(userpass_start[0])
2774 userpass_end = userpass_start + strlen(userpass_start);
2775 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2780 have_usernames = false;
2781 userpass_start = rcon_restricted_password.string;
2782 while((userpass_end = strchr(userpass_start, ' ')))
2784 have_usernames = true;
2785 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2787 if(comparator(peeraddress, buf, password, cs, cslen))
2789 userpass_start = userpass_end + 1;
2791 if(userpass_start[0])
2793 userpass_end = userpass_start + strlen(userpass_start);
2794 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2798 return NULL; // DENIED
2801 for(text = s; text != endpos; ++text)
2802 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2803 return NULL; // block possible exploits against the parser/alias expansion
2807 size_t l = strlen(s);
2810 hasquotes = (strchr(s, '"') != NULL);
2811 // sorry, we can't allow these substrings in wildcard expressions,
2812 // as they can mess with the argument counts
2813 text = rcon_restricted_commands.string;
2814 while(COM_ParseToken_Console(&text))
2816 // com_token now contains a pattern to check for...
2817 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2820 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2823 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2825 if(!strcmp(com_token, s))
2828 else // single-arg expression? must match the beginning of the command
2830 if(!strcmp(com_token, s))
2832 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2836 // if we got here, nothing matched!
2844 userpass_startpass = strchr(userpass_start, ':');
2845 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2846 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2848 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2851 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2855 // looks like a legitimate rcon command with the correct password
2856 const char *s_ptr = s;
2857 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2858 while(s_ptr != endpos)
2860 size_t l = strlen(s_ptr);
2862 Con_Printf(" %s;", s_ptr);
2867 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2868 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2871 size_t l = strlen(s);
2874 client_t *host_client_save = host_client;
2875 Cmd_ExecuteString(s, src_command, true);
2876 host_client = host_client_save;
2877 // in case it is a command that changes host_client (like restart)
2881 Con_Rcon_Redirect_End();
2885 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2889 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2891 int i, ret, clientnum, best;
2894 char *s, *string, response[1400], addressstring2[128];
2895 static char stringbuf[16384]; // server only
2896 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2897 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2898 size_t sendlength, response_len;
2899 char infostringvalue[MAX_INPUTLINE];
2905 // convert the address to a string incase we need it
2906 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2908 // see if we can identify the sender as a local player
2909 // (this is necessary for rcon to send a reliable reply if the client is
2910 // actually on the server, not sending remotely)
2911 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2912 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2914 if (i == svs.maxclients)
2917 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2919 // received a command string - strip off the packaging and put it
2920 // into our string buffer with NULL termination
2923 length = min(length, (int)sizeof(stringbuf) - 1);
2924 memcpy(stringbuf, data, length);
2925 stringbuf[length] = 0;
2928 if (developer_extra.integer)
2930 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2931 Com_HexDumpToConsole(data, length);
2934 sendlength = sizeof(senddata) - 4;
2935 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2937 case CRYPTO_NOMATCH:
2943 memcpy(senddata, "\377\377\377\377", 4);
2944 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2947 case CRYPTO_DISCARD:
2950 memcpy(senddata, "\377\377\377\377", 4);
2951 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2955 case CRYPTO_REPLACE:
2956 string = senddata+4;
2957 length = (int)sendlength;
2961 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2963 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2965 if(challenge[i].time > 0)
2966 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2968 if (besttime > challenge[i].time)
2969 besttime = challenge[best = i].time;
2971 // if we did not find an exact match, choose the oldest and
2972 // update address and string
2973 if (i == MAX_CHALLENGES)
2976 challenge[i].address = *peeraddress;
2977 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2981 // flood control: drop if requesting challenge too often
2982 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2985 challenge[i].time = realtime;
2986 // send the challenge
2987 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2988 response_len = strlen(response) + 1;
2989 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2990 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
2993 if (length > 8 && !memcmp(string, "connect\\", 8))
2995 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2999 if(crypto && crypto->authenticated)
3001 // no need to check challenge
3002 if(crypto_developer.integer)
3004 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3005 crypto->use_aes ? "Encrypted" : "Authenticated",
3007 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3008 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3009 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3010 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3011 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3012 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3018 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3020 // validate the challenge
3021 for (i = 0;i < MAX_CHALLENGES;i++)
3022 if(challenge[i].time > 0)
3023 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
3025 // if the challenge is not recognized, drop the packet
3026 if (i == MAX_CHALLENGES)
3031 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3032 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3034 if(!(islocal || sv_public.integer > -2))
3036 if (developer_extra.integer)
3037 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3038 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
3042 // check engine protocol
3043 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3045 if (developer_extra.integer)
3046 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3047 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3051 // see if this is a duplicate connection request or a disconnected
3052 // client who is rejoining to the same client slot
3053 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3055 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3057 // this is a known client...
3058 if(crypto && crypto->authenticated)
3060 // reject if changing key!
3061 if(client->netconnection->crypto.authenticated)
3064 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3066 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3068 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3070 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3073 if (developer_extra.integer)
3074 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3075 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3082 // reject if downgrading!
3083 if(client->netconnection->crypto.authenticated)
3085 if (developer_extra.integer)
3086 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3087 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3093 // client crashed and is coming back,
3094 // keep their stuff intact
3095 if (developer_extra.integer)
3096 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3097 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3098 if(crypto && crypto->authenticated)
3099 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3100 SV_SendServerinfo(client);
3104 // client is still trying to connect,
3105 // so we send a duplicate reply
3106 if (developer_extra.integer)
3107 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3108 if(crypto && crypto->authenticated)
3109 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3110 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3116 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3119 // find an empty client slot for this new client
3120 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3123 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3125 // allocated connection
3126 if (developer_extra.integer)
3127 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3128 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3129 // now set up the client
3130 if(crypto && crypto->authenticated)
3131 Crypto_FinishInstance(&conn->crypto, crypto);
3132 SV_ConnectClient(clientnum, conn);
3133 NetConn_Heartbeat(1);
3138 // no empty slots found - server is full
3139 if (developer_extra.integer)
3140 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3141 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3145 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3147 const char *challenge = NULL;
3149 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3152 // If there was a challenge in the getinfo message
3153 if (length > 8 && string[7] == ' ')
3154 challenge = string + 8;
3156 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3158 if (developer_extra.integer)
3159 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3160 NetConn_WriteString(mysocket, response, peeraddress);
3164 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3166 const char *challenge = NULL;
3168 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3171 // If there was a challenge in the getinfo message
3172 if (length > 10 && string[9] == ' ')
3173 challenge = string + 10;
3175 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3177 if (developer_extra.integer)
3178 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3179 NetConn_WriteString(mysocket, response, peeraddress);
3183 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3185 char *password = string + 20;
3186 char *timeval = string + 37;
3187 char *s = strchr(timeval, ' ');
3188 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3189 const char *userlevel;
3191 if(rcon_secure.integer > 1)
3195 return true; // invalid packet
3198 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3199 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3202 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3204 char *password = string + 25;
3205 char *challenge = string + 42;
3206 char *s = strchr(challenge, ' ');
3207 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3208 const char *userlevel;
3210 return true; // invalid packet
3213 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3214 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3217 if (length >= 5 && !memcmp(string, "rcon ", 5))
3220 char *s = string + 5;
3221 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3224 if(rcon_secure.integer > 0)
3227 for (i = 0;!ISWHITESPACE(*s);s++)
3228 if (i < (int)sizeof(password) - 1)
3230 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3233 if (!ISWHITESPACE(password[0]))
3235 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3236 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3240 if (!strncmp(string, "extResponse ", 12))
3242 ++sv_net_extresponse_count;
3243 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3244 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3245 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3246 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3249 if (!strncmp(string, "ping", 4))
3251 if (developer_extra.integer)
3252 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3253 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3256 if (!strncmp(string, "ack", 3))
3258 // we may not have liked the packet, but it was a command packet, so
3259 // we're done processing this packet now
3262 // netquake control packets, supported for compatibility only, and only
3263 // when running game protocols that are normally served via this connection
3265 // (this protects more modern protocols against being used for
3266 // Quake packet flood Denial Of Service attacks)
3267 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)
3271 const char *protocolname;
3274 SZ_Clear(&sv_message);
3275 SZ_Write(&sv_message, data, length);
3276 MSG_BeginReading(&sv_message);
3277 c = MSG_ReadByte(&sv_message);
3281 if (developer_extra.integer)
3282 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3283 if(!(islocal || sv_public.integer > -2))
3285 if (developer_extra.integer)
3286 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3287 SZ_Clear(&sv_message);
3288 // save space for the header, filled in later
3289 MSG_WriteLong(&sv_message, 0);
3290 MSG_WriteByte(&sv_message, CCREP_REJECT);
3291 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3292 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3293 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3294 SZ_Clear(&sv_message);
3298 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3299 protocolnumber = MSG_ReadByte(&sv_message);
3300 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3302 if (developer_extra.integer)
3303 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3304 SZ_Clear(&sv_message);
3305 // save space for the header, filled in later
3306 MSG_WriteLong(&sv_message, 0);
3307 MSG_WriteByte(&sv_message, CCREP_REJECT);
3308 MSG_WriteString(&sv_message, "Incompatible version.\n");
3309 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3310 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3311 SZ_Clear(&sv_message);
3315 // see if this connect request comes from a known client
3316 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3318 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3320 // this is either a duplicate connection request
3321 // or coming back from a timeout
3322 // (if so, keep their stuff intact)
3324 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3325 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3327 if (developer_extra.integer)
3328 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3329 SZ_Clear(&sv_message);
3330 // save space for the header, filled in later
3331 MSG_WriteLong(&sv_message, 0);
3332 MSG_WriteByte(&sv_message, CCREP_REJECT);
3333 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3334 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3335 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3336 SZ_Clear(&sv_message);
3341 if (developer_extra.integer)
3342 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3343 SZ_Clear(&sv_message);
3344 // save space for the header, filled in later
3345 MSG_WriteLong(&sv_message, 0);
3346 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3347 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3348 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3349 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3350 SZ_Clear(&sv_message);
3352 // if client is already spawned, re-send the
3353 // serverinfo message as they'll need it to play
3355 SV_SendServerinfo(client);
3360 // this is a new client, check for connection flood
3361 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3364 // find a slot for the new client
3365 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3368 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3370 // connect to the client
3371 // everything is allocated, just fill in the details
3372 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3373 if (developer_extra.integer)
3374 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3375 // send back the info about the server connection
3376 SZ_Clear(&sv_message);
3377 // save space for the header, filled in later
3378 MSG_WriteLong(&sv_message, 0);
3379 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3380 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3381 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3382 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3383 SZ_Clear(&sv_message);
3384 // now set up the client struct
3385 SV_ConnectClient(clientnum, conn);
3386 NetConn_Heartbeat(1);
3391 if (developer_extra.integer)
3392 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3393 // no room; try to let player know
3394 SZ_Clear(&sv_message);
3395 // save space for the header, filled in later
3396 MSG_WriteLong(&sv_message, 0);
3397 MSG_WriteByte(&sv_message, CCREP_REJECT);
3398 MSG_WriteString(&sv_message, "Server is full.\n");
3399 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3400 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3401 SZ_Clear(&sv_message);
3403 case CCREQ_SERVER_INFO:
3404 if (developer_extra.integer)
3405 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3406 if(!(islocal || sv_public.integer > -1))
3409 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3412 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3415 char myaddressstring[128];
3416 if (developer_extra.integer)
3417 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3418 SZ_Clear(&sv_message);
3419 // save space for the header, filled in later
3420 MSG_WriteLong(&sv_message, 0);
3421 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3422 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3423 MSG_WriteString(&sv_message, myaddressstring);
3424 MSG_WriteString(&sv_message, hostname.string);
3425 MSG_WriteString(&sv_message, sv.name);
3426 // How many clients are there?
3427 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3428 if (svs.clients[i].active)
3430 MSG_WriteByte(&sv_message, numclients);
3431 MSG_WriteByte(&sv_message, svs.maxclients);
3432 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3433 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3434 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3435 SZ_Clear(&sv_message);
3438 case CCREQ_PLAYER_INFO:
3439 if (developer_extra.integer)
3440 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3441 if(!(islocal || sv_public.integer > -1))
3444 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3449 int playerNumber, activeNumber, clientNumber;
3452 playerNumber = MSG_ReadByte(&sv_message);
3454 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3455 if (client->active && ++activeNumber == playerNumber)
3457 if (clientNumber != svs.maxclients)
3459 SZ_Clear(&sv_message);
3460 // save space for the header, filled in later
3461 MSG_WriteLong(&sv_message, 0);
3462 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3463 MSG_WriteByte(&sv_message, playerNumber);
3464 MSG_WriteString(&sv_message, client->name);
3465 MSG_WriteLong(&sv_message, client->colors);
3466 MSG_WriteLong(&sv_message, client->frags);
3467 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3468 if(sv_status_privacy.integer)
3469 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3471 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3472 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3473 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3474 SZ_Clear(&sv_message);
3478 case CCREQ_RULE_INFO:
3479 if (developer_extra.integer)
3480 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3481 if(!(islocal || sv_public.integer > -1))
3484 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3491 // find the search start location
3492 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3493 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3495 // send the response
3496 SZ_Clear(&sv_message);
3497 // save space for the header, filled in later
3498 MSG_WriteLong(&sv_message, 0);
3499 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3502 MSG_WriteString(&sv_message, var->name);
3503 MSG_WriteString(&sv_message, var->string);
3505 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3506 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3507 SZ_Clear(&sv_message);
3511 if (developer_extra.integer)
3512 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3513 if (sv.active && !rcon_secure.integer)
3515 char password[2048];
3519 const char *userlevel;
3520 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3521 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3523 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3524 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3525 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3532 SZ_Clear(&sv_message);
3533 // we may not have liked the packet, but it was a valid control
3534 // packet, so we're done processing this packet now
3539 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3541 SV_ReadClientMessage();
3548 void NetConn_ServerFrame(void)
3551 lhnetaddress_t peeraddress;
3552 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3553 for (i = 0;i < sv_numsockets;i++)
3554 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3555 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3556 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3558 // never timeout loopback connections
3559 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3561 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3562 SV_DropClient(false);
3567 void NetConn_SleepMicroseconds(int microseconds)
3569 LHNET_SleepUntilPacket_Microseconds(microseconds);
3573 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3577 lhnetaddress_t masteraddress;
3578 lhnetaddress_t broadcastaddress;
3581 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3584 // 26000 is the default quake server port, servers on other ports will not
3586 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3587 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3591 for (i = 0;i < cl_numsockets;i++)
3595 const char *cmdname, *extraoptions;
3596 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3598 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3600 // search LAN for Quake servers
3601 SZ_Clear(&cl_message);
3602 // save space for the header, filled in later
3603 MSG_WriteLong(&cl_message, 0);
3604 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3605 MSG_WriteString(&cl_message, "QUAKE");
3606 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3607 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3608 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3609 SZ_Clear(&cl_message);
3611 // search LAN for DarkPlaces servers
3612 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3615 // build the getservers message to send to the dpmaster master servers
3616 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3618 cmdname = "getserversExt";
3619 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3623 cmdname = "getservers";
3626 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3629 for (masternum = 0;sv_masters[masternum].name;masternum++)
3631 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3634 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3638 // search favorite servers
3639 for(j = 0; j < nFavorites; ++j)
3641 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3643 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3644 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3651 // only query QuakeWorld servers when the user wants to
3654 for (i = 0;i < cl_numsockets;i++)
3658 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3660 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3662 // search LAN for QuakeWorld servers
3663 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3665 // build the getservers message to send to the qwmaster master servers
3666 // note this has no -1 prefix, and the trailing nul byte is sent
3667 dpsnprintf(request, sizeof(request), "c\n");
3671 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3673 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3675 if (m_state != m_slist)
3677 char lookupstring[128];
3678 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3679 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3682 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3686 // search favorite servers
3687 for(j = 0; j < nFavorites; ++j)
3689 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3691 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3693 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3694 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3701 if (!masterquerycount)
3703 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3704 M_Update_Return_Reason("No network");
3709 void NetConn_Heartbeat(int priority)
3711 lhnetaddress_t masteraddress;
3713 lhnetsocket_t *mysocket;
3715 // if it's a state change (client connected), limit next heartbeat to no
3716 // more than 30 sec in the future
3717 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3718 nextheartbeattime = realtime + 30.0;
3720 // limit heartbeatperiod to 30 to 270 second range,
3721 // lower limit is to avoid abusing master servers with excess traffic,
3722 // upper limit is to avoid timing out on the master server (which uses
3724 if (sv_heartbeatperiod.value < 30)
3725 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3726 if (sv_heartbeatperiod.value > 270)
3727 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3729 // make advertising optional and don't advertise singleplayer games, and
3730 // only send a heartbeat as often as the admin wants
3731 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3733 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3734 for (masternum = 0;sv_masters[masternum].name;masternum++)
3735 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3736 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3740 static void Net_Heartbeat_f(void)
3743 NetConn_Heartbeat(2);
3745 Con_Print("No server running, can not heartbeat to master server.\n");
3748 static void PrintStats(netconn_t *conn)
3750 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3751 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3753 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3754 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3755 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3756 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3757 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3758 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3759 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3760 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3761 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3762 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3765 void Net_Stats_f(void)
3768 Con_Print("connections =\n");
3769 for (conn = netconn_list;conn;conn = conn->next)
3774 void Net_Refresh_f(void)
3776 if (m_state != m_slist) {
3777 Con_Print("Sending new requests to master servers\n");
3778 ServerList_QueryList(false, true, false, true);
3779 Con_Print("Listening for replies...\n");
3781 ServerList_QueryList(false, true, false, false);
3784 void Net_Slist_f(void)
3786 ServerList_ResetMasks();
3787 serverlist_sortbyfield = SLIF_PING;
3788 serverlist_sortflags = 0;
3789 if (m_state != m_slist) {
3790 Con_Print("Sending requests to master servers\n");
3791 ServerList_QueryList(true, true, false, true);
3792 Con_Print("Listening for replies...\n");
3794 ServerList_QueryList(true, true, false, false);
3797 void Net_SlistQW_f(void)
3799 ServerList_ResetMasks();
3800 serverlist_sortbyfield = SLIF_PING;
3801 serverlist_sortflags = 0;
3802 if (m_state != m_slist) {
3803 Con_Print("Sending requests to master servers\n");
3804 ServerList_QueryList(true, false, true, true);
3805 serverlist_consoleoutput = true;
3806 Con_Print("Listening for replies...\n");
3808 ServerList_QueryList(true, false, true, false);
3812 void NetConn_Init(void)
3815 lhnetaddress_t tempaddress;
3816 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3817 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3819 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3820 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3821 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3823 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3824 Cvar_RegisterVariable(&net_test);
3825 Cvar_RegisterVariable(&net_usesizelimit);
3826 Cvar_RegisterVariable(&net_burstreserve);
3827 Cvar_RegisterVariable(&rcon_restricted_password);
3828 Cvar_RegisterVariable(&rcon_restricted_commands);
3829 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3830 Cvar_RegisterVariable(&net_slist_queriespersecond);
3831 Cvar_RegisterVariable(&net_slist_queriesperframe);
3832 Cvar_RegisterVariable(&net_slist_timeout);
3833 Cvar_RegisterVariable(&net_slist_maxtries);
3834 Cvar_RegisterVariable(&net_slist_favorites);
3835 Cvar_RegisterVariable(&net_slist_pause);
3836 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3837 Cvar_RegisterVariable(&net_tos_dscp);
3838 Cvar_RegisterVariable(&net_messagetimeout);
3839 Cvar_RegisterVariable(&net_connecttimeout);
3840 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3841 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3842 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3843 Cvar_RegisterVariable(&cl_netlocalping);
3844 Cvar_RegisterVariable(&cl_netpacketloss_send);
3845 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3846 Cvar_RegisterVariable(&hostname);
3847 Cvar_RegisterVariable(&developer_networking);
3848 Cvar_RegisterVariable(&cl_netport);
3849 Cvar_RegisterVariable(&sv_netport);
3850 Cvar_RegisterVariable(&net_address);
3851 Cvar_RegisterVariable(&net_address_ipv6);
3852 Cvar_RegisterVariable(&sv_public);
3853 Cvar_RegisterVariable(&sv_public_rejectreason);
3854 Cvar_RegisterVariable(&sv_heartbeatperiod);
3855 for (i = 0;sv_masters[i].name;i++)
3856 Cvar_RegisterVariable(&sv_masters[i]);
3857 Cvar_RegisterVariable(&gameversion);
3858 Cvar_RegisterVariable(&gameversion_min);
3859 Cvar_RegisterVariable(&gameversion_max);
3860 // 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.
3861 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3863 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3865 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3866 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3869 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3871 // 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
3872 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3874 i = atoi(com_argv[i + 1]);
3875 if (i >= 0 && i < 65536)
3877 Con_Printf("-port option used, setting port cvar to %i\n", i);
3878 Cvar_SetValueQuick(&sv_netport, i);
3881 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3885 cl_message.data = cl_message_buf;
3886 cl_message.maxsize = sizeof(cl_message_buf);
3887 cl_message.cursize = 0;
3888 sv_message.data = sv_message_buf;
3889 sv_message.maxsize = sizeof(sv_message_buf);
3890 sv_message.cursize = 0;
3892 if (Thread_HasThreads())
3893 netconn_mutex = Thread_CreateMutex();
3896 void NetConn_Shutdown(void)
3898 NetConn_CloseClientPorts();
3899 NetConn_CloseServerPorts();
3902 Thread_DestroyMutex(netconn_mutex);
3903 netconn_mutex = NULL;