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", "ghdigital.com", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
48 {0, "sv_masterextra2", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
49 {0, "sv_masterextra3", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
54 static cvar_t sv_qwmasters [] =
56 {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
57 {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
58 {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
59 {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
60 {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
61 {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
62 {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
63 {0, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
68 static double nextheartbeattime = 0;
72 static unsigned char cl_message_buf[NET_MAXMESSAGE];
73 static unsigned char sv_message_buf[NET_MAXMESSAGE];
74 char cl_readstring[MAX_INPUTLINE];
75 char sv_readstring[MAX_INPUTLINE];
77 cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
78 cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
79 cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
80 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
81 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."};
82 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."};
83 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."};
84 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."};
85 cvar_t net_sourceaddresscheck = {0, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
86 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
87 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
89 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)"};
90 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)"};
91 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)"};
92 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
93 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)"};
94 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
95 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"};
96 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)"};
97 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"};
98 static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
99 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
100 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"};
101 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"};
102 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"};
103 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
104 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)"};
105 extern cvar_t rcon_secure;
106 extern cvar_t rcon_secure_challengetimeout;
108 double masterquerytime = -1000;
109 int masterquerycount = 0;
110 int masterreplycount = 0;
111 int serverquerycount = 0;
112 int serverreplycount = 0;
114 challenge_t challenge[MAX_CHALLENGES];
117 /// this is only false if there are still servers left to query
118 static qboolean serverlist_querysleep = true;
119 static qboolean serverlist_paused = false;
120 /// this is pushed a second or two ahead of realtime whenever a master server
121 /// reply is received, to avoid issuing queries while master replies are still
122 /// flooding in (which would make a mess of the ping times)
123 static double serverlist_querywaittime = 0;
126 static int cl_numsockets;
127 static lhnetsocket_t *cl_sockets[16];
128 static int sv_numsockets;
129 static lhnetsocket_t *sv_sockets[16];
131 netconn_t *netconn_list = NULL;
132 mempool_t *netconn_mempool = NULL;
133 void *netconn_mutex = NULL;
135 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
136 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
137 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
138 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
140 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
141 int cl_net_extresponse_count = 0;
142 int cl_net_extresponse_last = 0;
144 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
145 int sv_net_extresponse_count = 0;
146 int sv_net_extresponse_last = 0;
149 // ServerList interface
150 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
151 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
153 serverlist_infofield_t serverlist_sortbyfield;
154 int serverlist_sortflags;
156 int serverlist_viewcount = 0;
157 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
159 int serverlist_maxcachecount = 0;
160 int serverlist_cachecount = 0;
161 serverlist_entry_t *serverlist_cache = NULL;
163 qboolean serverlist_consoleoutput;
165 static int nFavorites = 0;
166 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
167 static int nFavorites_idfp = 0;
168 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
170 void NetConn_UpdateFavorites(void)
175 p = net_slist_favorites.string;
176 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
178 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
179 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
180 // (if v6 address contains port, it must start with '[')
182 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
187 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
193 /// helper function to insert a value into the viewset
194 /// spare entries will be removed
195 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
198 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
199 i = serverlist_viewcount++;
201 i = SERVERLIST_VIEWLISTSIZE - 1;
204 for( ; i > index ; i-- )
205 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
207 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
210 /// we suppose serverlist_viewcount to be valid, ie > 0
211 static void _ServerList_ViewList_Helper_Remove( int index )
213 serverlist_viewcount--;
214 for( ; index < serverlist_viewcount ; index++ )
215 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
218 /// \returns true if A should be inserted before B
219 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
221 int result = 0; // > 0 if for numbers A > B and for text if A < B
223 if( serverlist_sortflags & SLSF_CATEGORIES )
225 result = A->info.category - B->info.category;
230 if( serverlist_sortflags & SLSF_FAVORITES )
232 if(A->info.isfavorite != B->info.isfavorite)
233 return A->info.isfavorite;
236 switch( serverlist_sortbyfield ) {
238 result = A->info.ping - B->info.ping;
240 case SLIF_MAXPLAYERS:
241 result = A->info.maxplayers - B->info.maxplayers;
243 case SLIF_NUMPLAYERS:
244 result = A->info.numplayers - B->info.numplayers;
247 result = A->info.numbots - B->info.numbots;
250 result = A->info.numhumans - B->info.numhumans;
253 result = A->info.freeslots - B->info.freeslots;
256 result = A->info.protocol - B->info.protocol;
259 result = strcmp( B->info.cname, A->info.cname );
262 result = strcasecmp( B->info.game, A->info.game );
265 result = strcasecmp( B->info.map, A->info.map );
268 result = strcasecmp( B->info.mod, A->info.mod );
271 result = strcasecmp( B->info.name, A->info.name );
274 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
277 result = A->info.category - B->info.category;
279 case SLIF_ISFAVORITE:
280 result = !!B->info.isfavorite - !!A->info.isfavorite;
283 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
289 if( serverlist_sortflags & SLSF_DESCENDING )
295 // if the chosen sort key is identical, sort by index
296 // (makes this a stable sort, so that later replies from servers won't
297 // shuffle the servers around when they have the same ping)
301 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
303 // This should actually be done with some intermediate and end-of-function return
315 case SLMO_GREATEREQUAL:
317 case SLMO_NOTCONTAIN:
318 case SLMO_STARTSWITH:
319 case SLMO_NOTSTARTSWITH:
322 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
327 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
330 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
331 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
332 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
333 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
335 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
336 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
339 // Same here, also using an intermediate & final return would be more appropriate
343 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
344 case SLMO_NOTCONTAIN:
345 return !*bufferB || !strstr( bufferA, bufferB );
346 case SLMO_STARTSWITH:
347 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
348 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
349 case SLMO_NOTSTARTSWITH:
350 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
352 return strcmp( bufferA, bufferB ) < 0;
354 return strcmp( bufferA, bufferB ) <= 0;
356 return strcmp( bufferA, bufferB ) == 0;
358 return strcmp( bufferA, bufferB ) > 0;
360 return strcmp( bufferA, bufferB ) != 0;
361 case SLMO_GREATEREQUAL:
362 return strcmp( bufferA, bufferB ) >= 0;
364 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
369 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
371 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
373 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
375 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
377 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
379 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
381 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
383 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
385 if( *mask->info.cname
386 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
389 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
392 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
395 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
398 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
400 if( *mask->info.qcstatus
401 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
403 if( *mask->info.players
404 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
406 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
408 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
413 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
415 int start, end, mid, i;
418 // reject incompatible servers
420 entry->info.gameversion != gameversion.integer
423 gameversion_min.integer >= 0 // min/max range set by user/mod?
424 && gameversion_max.integer >= 0
425 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
426 && gameversion_max.integer >= entry->info.gameversion
431 // refresh the "favorite" status
432 entry->info.isfavorite = false;
433 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
435 char idfp[FP64_SIZE+1];
436 for(i = 0; i < nFavorites; ++i)
438 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
440 entry->info.isfavorite = true;
444 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
446 for(i = 0; i < nFavorites_idfp; ++i)
448 if(!strcmp(idfp, favorites_idfp[i]))
450 entry->info.isfavorite = true;
457 // refresh the "category"
458 entry->info.category = MR_GetServerListEntryCategory(entry);
460 // FIXME: change this to be more readable (...)
461 // now check whether it passes through the masks
462 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
463 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
466 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
467 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
469 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
472 if( !serverlist_viewcount ) {
473 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
476 // ok, insert it, we just need to find out where exactly:
479 // check whether to insert it as new first item
480 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
481 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
483 } // check whether to insert it as new last item
484 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
485 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
489 end = serverlist_viewcount - 1;
490 while( end > start + 1 )
492 mid = (start + end) / 2;
493 // test the item that lies in the middle between start and end
494 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
495 // the item has to be in the upper half
498 // the item has to be in the lower half
501 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
504 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
507 for( i = 0; i < serverlist_viewcount; i++ )
509 if (ServerList_GetViewEntry(i) == entry)
511 _ServerList_ViewList_Helper_Remove(i);
517 void ServerList_RebuildViewList(void)
521 serverlist_viewcount = 0;
522 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
523 serverlist_entry_t *entry = &serverlist_cache[i];
524 // also display entries that are currently being refreshed [11/8/2007 Black]
525 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
526 ServerList_ViewList_Insert( entry );
530 void ServerList_ResetMasks(void)
534 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
535 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
536 // numbots needs to be compared to -1 to always succeed
537 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
538 serverlist_andmasks[i].info.numbots = -1;
539 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
540 serverlist_ormasks[i].info.numbots = -1;
543 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
546 int numplayers = 0, maxplayers = 0;
547 for (i = 0;i < serverlist_cachecount;i++)
549 if (serverlist_cache[i].query == SQS_QUERIED)
551 numplayers += serverlist_cache[i].info.numhumans;
552 maxplayers += serverlist_cache[i].info.maxplayers;
555 *numplayerspointer = numplayers;
556 *maxplayerspointer = maxplayers;
560 static void _ServerList_Test(void)
563 if (serverlist_maxcachecount <= 1024)
565 serverlist_maxcachecount = 1024;
566 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
568 for( i = 0 ; i < 1024 ; i++ ) {
569 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
570 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
571 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
572 serverlist_cache[serverlist_cachecount].finished = true;
573 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 );
574 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
575 serverlist_cachecount++;
580 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
582 masterquerytime = realtime;
583 masterquerycount = 0;
584 masterreplycount = 0;
586 serverquerycount = 0;
587 serverreplycount = 0;
588 serverlist_cachecount = 0;
589 serverlist_viewcount = 0;
590 serverlist_maxcachecount = 0;
591 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
593 // refresh all entries
595 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
596 serverlist_entry_t *entry = &serverlist_cache[ n ];
597 entry->query = SQS_REFRESHING;
598 entry->querycounter = 0;
601 serverlist_consoleoutput = consoleoutput;
603 //_ServerList_Test();
605 NetConn_QueryMasters(querydp, queryqw);
611 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
615 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
616 Thread_LockMutex(netconn_mutex);
617 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
618 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
619 Thread_UnlockMutex(netconn_mutex);
622 if (cl_netpacketloss_receive.integer)
623 for (i = 0;i < cl_numsockets;i++)
624 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
626 if (developer_networking.integer)
628 char addressstring[128], addressstring2[128];
629 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
632 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
633 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
634 Com_HexDumpToConsole((unsigned char *)data, length);
637 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
642 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
646 if (cl_netpacketloss_send.integer)
647 for (i = 0;i < cl_numsockets;i++)
648 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
650 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
651 Thread_LockMutex(netconn_mutex);
652 ret = LHNET_Write(mysocket, data, length, peeraddress);
653 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
654 Thread_UnlockMutex(netconn_mutex);
655 if (developer_networking.integer)
657 char addressstring[128], addressstring2[128];
658 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
659 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
660 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)");
661 Com_HexDumpToConsole((unsigned char *)data, length);
666 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
668 // note this does not include the trailing NULL because we add that in the parser
669 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
672 qboolean NetConn_CanSend(netconn_t *conn)
674 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
675 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = realtime;
676 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
677 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
678 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
679 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
680 if (realtime > conn->cleartime)
684 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
689 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
691 double bursttime = burstsize / (double)rate;
693 // delay later packets to obey rate limit
694 if (*cleartime < realtime - bursttime)
695 *cleartime = realtime - bursttime;
696 *cleartime = *cleartime + len / (double)rate;
698 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
699 if (net_test.integer)
701 if (*cleartime < realtime)
702 *cleartime = realtime;
706 static int NetConn_AddCryptoFlag(crypto_t *crypto)
708 // HACK: if an encrypted connection is used, randomly set some unused
709 // flags. When AES encryption is enabled, that will make resends differ
710 // from the original, so that e.g. substring filters in a router/IPS
711 // are unlikely to match a second time. See also "startkeylogger".
713 if (crypto->authenticated)
715 // Let's always set at least one of the bits.
716 int r = rand() % 7 + 1;
718 flag |= NETFLAG_CRYPTO0;
720 flag |= NETFLAG_CRYPTO1;
722 flag |= NETFLAG_CRYPTO2;
727 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
730 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
731 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
733 // if this packet was supposedly choked, but we find ourselves sending one
734 // anyway, make sure the size counting starts at zero
735 // (this mostly happens on level changes and disconnects and such)
736 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
737 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
739 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
741 if (protocol == PROTOCOL_QUAKEWORLD)
744 qboolean sendreliable;
746 // note that it is ok to send empty messages to the qw server,
747 // otherwise it won't respond to us at all
749 sendreliable = false;
750 // if the remote side dropped the last reliable message, resend it
751 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
753 // if the reliable transmit buffer is empty, copy the current message out
754 if (!conn->sendMessageLength && conn->message.cursize)
756 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
757 conn->sendMessageLength = conn->message.cursize;
758 SZ_Clear(&conn->message); // clear the message buffer
759 conn->qw.reliable_sequence ^= 1;
762 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
763 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
764 // last received unreliable packet number, and last received reliable packet number (0 or 1)
765 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
767 conn->outgoing_unreliable_sequence++;
768 // client sends qport in every packet
769 if (conn == cls.netcon)
771 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
773 // also update cls.qw_outgoing_sequence
774 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
776 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
778 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
782 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
784 // add the reliable message if there is one
787 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
788 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
789 packetLen += conn->sendMessageLength;
790 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
793 // add the unreliable message if possible
794 if (packetLen + data->cursize <= 1400)
796 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
797 memcpy(sendbuffer + packetLen, data->data, data->cursize);
798 packetLen += data->cursize;
801 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
804 conn->unreliableMessagesSent++;
806 totallen += packetLen + 28;
810 unsigned int packetLen;
811 unsigned int dataLen;
816 // if a reliable message fragment has been lost, send it again
817 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
819 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
821 dataLen = conn->sendMessageLength;
826 dataLen = MAX_PACKETFRAGMENT;
830 packetLen = NET_HEADERSIZE + dataLen;
832 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
833 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
834 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
836 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
838 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
839 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
841 conn->lastSendTime = realtime;
842 conn->packetsReSent++;
845 totallen += (int)sendmelen + 28;
848 // if we have a new reliable message to send, do so
849 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
851 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
853 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
854 conn->message.overflowed = true;
858 if (developer_networking.integer && conn == cls.netcon)
860 Con_Print("client sending reliable message to server:\n");
861 SZ_HexDumpToConsole(&conn->message);
864 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
865 conn->sendMessageLength = conn->message.cursize;
866 SZ_Clear(&conn->message);
868 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
870 dataLen = conn->sendMessageLength;
875 dataLen = MAX_PACKETFRAGMENT;
879 packetLen = NET_HEADERSIZE + dataLen;
881 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
882 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
883 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
885 conn->nq.sendSequence++;
887 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
889 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
891 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
893 conn->lastSendTime = realtime;
895 conn->reliableMessagesSent++;
897 totallen += (int)sendmelen + 28;
900 // if we have an unreliable message to send, do so
903 packetLen = NET_HEADERSIZE + data->cursize;
905 if (packetLen > (int)sizeof(sendbuffer))
907 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
911 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
912 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
913 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
915 conn->outgoing_unreliable_sequence++;
917 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
919 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
921 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
924 conn->unreliableMessagesSent++;
926 totallen += (int)sendmelen + 28;
930 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
935 qboolean NetConn_HaveClientPorts(void)
937 return !!cl_numsockets;
940 qboolean NetConn_HaveServerPorts(void)
942 return !!sv_numsockets;
945 void NetConn_CloseClientPorts(void)
947 for (;cl_numsockets > 0;cl_numsockets--)
948 if (cl_sockets[cl_numsockets - 1])
949 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
952 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
954 lhnetaddress_t address;
957 char addressstring2[1024];
958 if (addressstring && addressstring[0])
959 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
961 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
964 if ((s = LHNET_OpenSocket_Connectionless(&address)))
966 cl_sockets[cl_numsockets++] = s;
967 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
968 if (addresstype != LHNETADDRESSTYPE_LOOP)
969 Con_Printf("Client opened a socket on address %s\n", addressstring2);
973 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
974 Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
978 Con_Printf("Client unable to parse address %s\n", addressstring);
981 void NetConn_OpenClientPorts(void)
984 NetConn_CloseClientPorts();
986 SV_LockThreadMutex(); // FIXME recursive?
987 Crypto_LoadKeys(); // client sockets
988 SV_UnlockThreadMutex();
990 port = bound(0, cl_netport.integer, 65535);
991 if (cl_netport.integer != port)
992 Cvar_SetValueQuick(&cl_netport, port);
994 Con_Printf("Client using an automatically assigned port\n");
996 Con_Printf("Client using port %i\n", port);
997 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
998 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
999 #ifndef NOSUPPORTIPV6
1000 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1004 void NetConn_CloseServerPorts(void)
1006 for (;sv_numsockets > 0;sv_numsockets--)
1007 if (sv_sockets[sv_numsockets - 1])
1008 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1011 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1013 lhnetaddress_t address;
1016 char addressstring2[1024];
1019 for (port = defaultport; port <= defaultport + range; port++)
1021 if (addressstring && addressstring[0])
1022 success = LHNETADDRESS_FromString(&address, addressstring, port);
1024 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1027 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1029 sv_sockets[sv_numsockets++] = s;
1030 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1031 if (addresstype != LHNETADDRESSTYPE_LOOP)
1032 Con_Printf("Server listening on address %s\n", addressstring2);
1037 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1038 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
1043 Con_Printf("Server unable to parse address %s\n", addressstring);
1044 // if it cant parse one address, it wont be able to parse another for sure
1051 void NetConn_OpenServerPorts(int opennetports)
1054 NetConn_CloseServerPorts();
1056 SV_LockThreadMutex(); // FIXME recursive?
1057 Crypto_LoadKeys(); // server sockets
1058 SV_UnlockThreadMutex();
1060 NetConn_UpdateSockets();
1061 port = bound(0, sv_netport.integer, 65535);
1064 Con_Printf("Server using port %i\n", port);
1065 if (sv_netport.integer != port)
1066 Cvar_SetValueQuick(&sv_netport, port);
1067 if (cls.state != ca_dedicated)
1068 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1071 #ifndef NOSUPPORTIPV6
1072 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1073 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1075 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1078 if (sv_numsockets == 0)
1079 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1082 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1084 int i, a = LHNETADDRESS_GetAddressType(address);
1085 for (i = 0;i < cl_numsockets;i++)
1086 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1087 return cl_sockets[i];
1091 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1093 int i, a = LHNETADDRESS_GetAddressType(address);
1094 for (i = 0;i < sv_numsockets;i++)
1095 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1096 return sv_sockets[i];
1100 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1103 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1104 conn->mysocket = mysocket;
1105 conn->peeraddress = *peeraddress;
1106 conn->lastMessageTime = realtime;
1107 conn->message.data = conn->messagedata;
1108 conn->message.maxsize = sizeof(conn->messagedata);
1109 conn->message.cursize = 0;
1110 // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1111 // reduce effectiveness of connection request floods
1112 conn->timeout = realtime + net_connecttimeout.value;
1113 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1114 conn->next = netconn_list;
1115 netconn_list = conn;
1119 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1120 void NetConn_Close(netconn_t *conn)
1123 // remove connection from list
1125 // allow the client to reconnect immediately
1126 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1128 if (conn == netconn_list)
1129 netconn_list = conn->next;
1132 for (c = netconn_list;c;c = c->next)
1134 if (c->next == conn)
1136 c->next = conn->next;
1140 // not found in list, we'll avoid crashing here...
1148 static int clientport = -1;
1149 static int clientport2 = -1;
1150 static int hostport = -1;
1151 void NetConn_UpdateSockets(void)
1155 // TODO add logic to automatically close sockets if needed
1156 LHNET_DefaultDSCP(net_tos_dscp.integer);
1158 if (cls.state != ca_dedicated)
1160 if (clientport2 != cl_netport.integer)
1162 clientport2 = cl_netport.integer;
1163 if (cls.state == ca_connected)
1164 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1166 if (cls.state == ca_disconnected && clientport != clientport2)
1168 clientport = clientport2;
1169 NetConn_CloseClientPorts();
1171 if (cl_numsockets == 0)
1172 NetConn_OpenClientPorts();
1175 if (hostport != sv_netport.integer)
1177 hostport = sv_netport.integer;
1179 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1182 for (j = 0;j < MAX_RCONS;j++)
1184 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1185 if(cls.rcon_commands[i][0])
1187 if(realtime > cls.rcon_timeout[i])
1190 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1191 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1192 cls.rcon_commands[i][0] = 0;
1200 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1202 int originallength = (int)length;
1203 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1204 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1205 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1209 if (protocol == PROTOCOL_QUAKEWORLD)
1211 unsigned int sequence, sequence_ack;
1212 qboolean reliable_ack, reliable_message;
1216 sequence = LittleLong(*((int *)(data + 0)));
1217 sequence_ack = LittleLong(*((int *)(data + 4)));
1221 if (conn != cls.netcon)
1226 // 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?)
1227 //qport = LittleShort(*((int *)(data + 8)));
1232 conn->packetsReceived++;
1233 reliable_message = (sequence >> 31) != 0;
1234 reliable_ack = (sequence_ack >> 31) != 0;
1235 sequence &= ~(1<<31);
1236 sequence_ack &= ~(1<<31);
1237 if (sequence <= conn->qw.incoming_sequence)
1239 //Con_DPrint("Got a stale datagram\n");
1242 count = sequence - (conn->qw.incoming_sequence + 1);
1245 conn->droppedDatagrams += count;
1246 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1247 // If too may packets have been dropped, only write the
1248 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1249 // Because there's no point in writing more than
1250 // these as the netgraph is going to be full anyway.
1251 if (count > NETGRAPH_PACKETS)
1252 count = NETGRAPH_PACKETS;
1255 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1256 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1257 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1258 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1259 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1260 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1263 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1264 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1265 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1266 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1267 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1268 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1269 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1271 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1272 if (net_test.integer)
1274 if (conn->cleartime < realtime)
1275 conn->cleartime = realtime;
1278 if (reliable_ack == conn->qw.reliable_sequence)
1280 // received, now we will be able to send another reliable message
1281 conn->sendMessageLength = 0;
1282 conn->reliableMessagesReceived++;
1284 conn->qw.incoming_sequence = sequence;
1285 if (conn == cls.netcon)
1286 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1287 conn->qw.incoming_acknowledged = sequence_ack;
1288 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1289 if (reliable_message)
1290 conn->qw.incoming_reliable_sequence ^= 1;
1291 conn->lastMessageTime = realtime;
1292 conn->timeout = realtime + newtimeout;
1293 conn->unreliableMessagesReceived++;
1294 if (conn == cls.netcon)
1296 SZ_Clear(&cl_message);
1297 SZ_Write(&cl_message, data, (int)length);
1298 MSG_BeginReading(&cl_message);
1302 SZ_Clear(&sv_message);
1303 SZ_Write(&sv_message, data, (int)length);
1304 MSG_BeginReading(&sv_message);
1312 unsigned int sequence;
1317 originallength = (int)length;
1318 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1324 qlength = (unsigned int)BuffBigLong(data);
1325 flags = qlength & ~NETFLAG_LENGTH_MASK;
1326 qlength &= NETFLAG_LENGTH_MASK;
1327 // control packets were already handled
1328 if (!(flags & NETFLAG_CTL) && qlength == length)
1330 sequence = BuffBigLong(data + 4);
1331 conn->packetsReceived++;
1334 if (flags & NETFLAG_UNRELIABLE)
1336 if (sequence >= conn->nq.unreliableReceiveSequence)
1338 if (sequence > conn->nq.unreliableReceiveSequence)
1340 count = sequence - conn->nq.unreliableReceiveSequence;
1341 conn->droppedDatagrams += count;
1342 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1343 // If too may packets have been dropped, only write the
1344 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1345 // Because there's no point in writing more than
1346 // these as the netgraph is going to be full anyway.
1347 if (count > NETGRAPH_PACKETS)
1348 count = NETGRAPH_PACKETS;
1351 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1352 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1353 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1354 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1355 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1356 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1359 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1360 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1361 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1362 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1363 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1364 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1365 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1367 conn->nq.unreliableReceiveSequence = sequence + 1;
1368 conn->lastMessageTime = realtime;
1369 conn->timeout = realtime + newtimeout;
1370 conn->unreliableMessagesReceived++;
1373 if (conn == cls.netcon)
1375 SZ_Clear(&cl_message);
1376 SZ_Write(&cl_message, data, (int)length);
1377 MSG_BeginReading(&cl_message);
1381 SZ_Clear(&sv_message);
1382 SZ_Write(&sv_message, data, (int)length);
1383 MSG_BeginReading(&sv_message);
1389 // Con_DPrint("Got a stale datagram\n");
1392 else if (flags & NETFLAG_ACK)
1394 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1395 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1397 if (sequence == (conn->nq.sendSequence - 1))
1399 if (sequence == conn->nq.ackSequence)
1401 conn->nq.ackSequence++;
1402 if (conn->nq.ackSequence != conn->nq.sendSequence)
1403 Con_DPrint("ack sequencing error\n");
1404 conn->lastMessageTime = realtime;
1405 conn->timeout = realtime + newtimeout;
1406 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1408 unsigned int packetLen;
1409 unsigned int dataLen;
1412 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1413 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1415 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1417 dataLen = conn->sendMessageLength;
1422 dataLen = MAX_PACKETFRAGMENT;
1426 packetLen = NET_HEADERSIZE + dataLen;
1428 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1429 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1430 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1432 conn->nq.sendSequence++;
1434 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1435 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1437 conn->lastSendTime = realtime;
1438 conn->packetsSent++;
1442 conn->sendMessageLength = 0;
1445 // Con_DPrint("Duplicate ACK received\n");
1448 // Con_DPrint("Stale ACK received\n");
1451 else if (flags & NETFLAG_DATA)
1453 unsigned char temppacket[8];
1454 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1455 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1457 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1459 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1460 StoreBigLong(temppacket + 4, sequence);
1461 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1463 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1464 if (sequence == conn->nq.receiveSequence)
1466 conn->lastMessageTime = realtime;
1467 conn->timeout = realtime + newtimeout;
1468 conn->nq.receiveSequence++;
1469 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1470 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1471 conn->receiveMessageLength += (int)length;
1473 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1474 "Dropping the message!\n", sequence );
1475 conn->receiveMessageLength = 0;
1478 if (flags & NETFLAG_EOM)
1480 conn->reliableMessagesReceived++;
1481 length = conn->receiveMessageLength;
1482 conn->receiveMessageLength = 0;
1485 if (conn == cls.netcon)
1487 SZ_Clear(&cl_message);
1488 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1489 MSG_BeginReading(&cl_message);
1493 SZ_Clear(&sv_message);
1494 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1495 MSG_BeginReading(&sv_message);
1502 conn->receivedDuplicateCount++;
1510 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1513 cls.connect_trying = false;
1515 M_Update_Return_Reason("");
1517 // the connection request succeeded, stop current connection and set up a new connection
1519 // if we're connecting to a remote server, shut down any local server
1520 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1522 SV_LockThreadMutex();
1523 Host_ShutdownServer ();
1524 SV_UnlockThreadMutex();
1526 // allocate a net connection to keep track of things
1527 cls.netcon = NetConn_Open(mysocket, peeraddress);
1528 crypto = &cls.netcon->crypto;
1529 if(cls.crypto.authenticated)
1531 Crypto_FinishInstance(crypto, &cls.crypto);
1532 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1533 crypto->use_aes ? "Encrypted" : "Authenticated",
1534 cls.netcon->address,
1535 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1536 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1537 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1538 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1539 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1540 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1543 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1544 key_dest = key_game;
1548 cls.demonum = -1; // not in the demo loop now
1549 cls.state = ca_connected;
1550 cls.signon = 0; // need all the signon messages before playing
1551 cls.protocol = initialprotocol;
1552 // reset move sequence numbering on this new connection
1553 cls.servermovesequence = 0;
1554 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1555 Cmd_ForwardStringToServer("new");
1556 if (cls.protocol == PROTOCOL_QUAKE)
1558 // write a keepalive (clc_nop) as it seems to greatly improve the
1559 // chances of connecting to a netquake server
1561 unsigned char buf[4];
1562 memset(&msg, 0, sizeof(msg));
1564 msg.maxsize = sizeof(buf);
1565 MSG_WriteChar(&msg, clc_nop);
1566 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1570 int NetConn_IsLocalGame(void)
1572 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1578 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1582 serverlist_entry_t *entry = NULL;
1584 // search the cache for this server and update it
1585 for (n = 0;n < serverlist_cachecount;n++) {
1586 entry = &serverlist_cache[ n ];
1587 if (!strcmp(addressstring, entry->info.cname))
1591 if (n == serverlist_cachecount)
1593 // LAN search doesnt require an answer from the master server so we wont
1594 // know the ping nor will it be initialized already...
1597 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1600 if (serverlist_maxcachecount <= serverlist_cachecount)
1602 serverlist_maxcachecount += 64;
1603 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1605 entry = &serverlist_cache[n];
1607 memset(entry, 0, sizeof(*entry));
1608 // store the data the engine cares about (address and ping)
1609 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1610 entry->info.ping = 100000;
1611 entry->querytime = realtime;
1612 // if not in the slist menu we should print the server to console
1613 if (serverlist_consoleoutput)
1614 Con_Printf("querying %s\n", addressstring);
1615 ++serverlist_cachecount;
1617 // if this is the first reply from this server, count it as having replied
1618 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1619 pingtime = bound(0, pingtime, 9999);
1620 if (entry->query == SQS_REFRESHING) {
1621 entry->info.ping = pingtime;
1622 entry->query = SQS_QUERIED;
1624 // convert to unsigned to catch the -1
1625 // 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]
1626 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1630 // other server info is updated by the caller
1634 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1636 serverlist_entry_t *entry = &serverlist_cache[n];
1637 serverlist_info_t *info = &entry->info;
1638 // update description strings for engine menu and console output
1639 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);
1640 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1642 info->gameversion != gameversion.integer
1645 gameversion_min.integer >= 0 // min/max range set by user/mod?
1646 && gameversion_max.integer >= 0
1647 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1648 && gameversion_max.integer >= info->gameversion
1651 info->mod, info->map);
1652 if (entry->query == SQS_QUERIED)
1654 if(!serverlist_paused)
1655 ServerList_ViewList_Remove(entry);
1657 // if not in the slist menu we should print the server to console (if wanted)
1658 else if( serverlist_consoleoutput )
1659 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1660 // and finally, update the view set
1661 if(!serverlist_paused)
1662 ServerList_ViewList_Insert( entry );
1663 // update the entry's state
1664 serverlist_cache[n].query = SQS_QUERIED;
1667 // returns true, if it's sensible to continue the processing
1668 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1670 serverlist_entry_t *entry;
1672 // ignore the rest of the message if the serverlist is full
1673 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1675 // also ignore it if we have already queried it (other master server response)
1676 for( n = 0 ; n < serverlist_cachecount ; n++ )
1677 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1680 if( n < serverlist_cachecount ) {
1681 // the entry has already been queried once or
1685 if (serverlist_maxcachecount <= n)
1687 serverlist_maxcachecount += 64;
1688 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1691 entry = &serverlist_cache[n];
1693 memset(entry, 0, sizeof(*entry));
1694 entry->protocol = protocol;
1695 // store the data the engine cares about (address and ping)
1696 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1698 entry->info.isfavorite = isfavorite;
1700 // no, then reset the ping right away
1701 entry->info.ping = -1;
1702 // we also want to increase the serverlist_cachecount then
1703 serverlist_cachecount++;
1706 entry->query = SQS_QUERYING;
1711 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1714 if (serverlist_consoleoutput)
1715 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1718 char ipstring [128];
1721 if (data[0] == '\\')
1723 unsigned short port = data[5] * 256 + data[6];
1725 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1726 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1728 // move on to next address in packet
1733 else if (data[0] == '/' && isextended && length >= 19)
1735 unsigned short port = data[17] * 256 + data[18];
1743 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1745 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1748 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1749 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1750 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1756 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1757 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1758 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1763 // move on to next address in packet
1769 Con_Print("Error while parsing the server list\n");
1773 if (serverlist_consoleoutput && developer_networking.integer)
1774 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1776 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1782 // begin or resume serverlist queries
1783 serverlist_querysleep = false;
1784 serverlist_querywaittime = realtime + 3;
1788 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1790 qboolean fromserver;
1792 char *string, addressstring2[128];
1793 char stringbuf[16384];
1794 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1797 char infostringvalue[MAX_INPUTLINE];
1803 // quakeworld ingame packet
1804 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1806 // convert the address to a string incase we need it
1807 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1809 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1811 // received a command string - strip off the packaging and put it
1812 // into our string buffer with NULL termination
1815 length = min(length, (int)sizeof(stringbuf) - 1);
1816 memcpy(stringbuf, data, length);
1817 stringbuf[length] = 0;
1820 if (developer_networking.integer)
1822 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1823 Com_HexDumpToConsole(data, length);
1826 sendlength = sizeof(senddata) - 4;
1827 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1829 case CRYPTO_NOMATCH:
1835 memcpy(senddata, "\377\377\377\377", 4);
1836 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1839 case CRYPTO_DISCARD:
1842 memcpy(senddata, "\377\377\377\377", 4);
1843 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1847 case CRYPTO_REPLACE:
1848 string = senddata+4;
1849 length = (int)sendlength;
1853 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1856 for (j = 0;j < MAX_RCONS;j++)
1858 // note: this value from i is used outside the loop too...
1859 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1860 if(cls.rcon_commands[i][0])
1861 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1870 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1871 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1873 e = strchr(rcon_password.string, ' ');
1874 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1876 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1880 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1881 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1882 cls.rcon_commands[i][0] = 0;
1885 for (k = 0;k < MAX_RCONS;k++)
1886 if(cls.rcon_commands[k][0])
1887 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1892 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1893 // extend the timeout on other requests as we asked for a challenge
1894 for (l = 0;l < MAX_RCONS;l++)
1895 if(cls.rcon_commands[l][0])
1896 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1897 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1900 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1904 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1906 // darkplaces or quake3
1907 char protocolnames[1400];
1908 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1909 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1910 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1913 Protocol_Names(protocolnames, sizeof(protocolnames));
1915 M_Update_Return_Reason("Got challenge response");
1917 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1918 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1919 // TODO: add userinfo stuff here instead of using NQ commands?
1920 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
1926 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1927 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1931 M_Update_Return_Reason("Accepted");
1933 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1936 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1938 char rejectreason[128];
1939 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1940 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1943 cls.connect_trying = false;
1945 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1946 memcpy(rejectreason, string, length);
1947 rejectreason[length] = 0;
1949 M_Update_Return_Reason(rejectreason);
1954 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1956 serverlist_info_t *info;
1961 // search the cache for this server and update it
1962 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1966 info = &serverlist_cache[n].info;
1971 info->qcstatus[0] = 0;
1972 info->players[0] = 0;
1973 info->protocol = -1;
1974 info->numplayers = 0;
1976 info->maxplayers = 0;
1977 info->gameversion = 0;
1979 p = strchr(string, '\n');
1982 *p = 0; // cut off the string there
1986 Con_Printf("statusResponse without players block?\n");
1988 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1989 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1990 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1991 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1992 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1993 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1994 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1995 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1996 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1997 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1998 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1999 info->numhumans = info->numplayers - max(0, info->numbots);
2000 info->freeslots = info->maxplayers - info->numplayers;
2002 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2006 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2008 serverlist_info_t *info;
2012 // search the cache for this server and update it
2013 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2017 info = &serverlist_cache[n].info;
2022 info->qcstatus[0] = 0;
2023 info->players[0] = 0;
2024 info->protocol = -1;
2025 info->numplayers = 0;
2027 info->maxplayers = 0;
2028 info->gameversion = 0;
2030 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2031 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2032 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2033 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2034 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2035 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2036 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2037 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2038 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2039 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2040 info->numhumans = info->numplayers - max(0, info->numbots);
2041 info->freeslots = info->maxplayers - info->numplayers;
2043 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2047 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2049 // Extract the IP addresses
2052 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2055 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2057 // Extract the IP addresses
2060 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2063 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2065 // Extract the IP addresses
2069 if (serverlist_consoleoutput)
2070 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2071 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2073 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2074 if (serverlist_consoleoutput && developer_networking.integer)
2075 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2077 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2081 // move on to next address in packet
2085 // begin or resume serverlist queries
2086 serverlist_querysleep = false;
2087 serverlist_querywaittime = realtime + 3;
2091 if (!strncmp(string, "extResponse ", 12))
2093 ++cl_net_extresponse_count;
2094 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2095 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2096 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2097 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2100 if (!strncmp(string, "ping", 4))
2102 if (developer_extra.integer)
2103 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2104 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2107 if (!strncmp(string, "ack", 3))
2109 // QuakeWorld compatibility
2110 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2112 // challenge message
2113 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2114 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2117 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2119 M_Update_Return_Reason("Got QuakeWorld challenge response");
2121 cls.qw_qport = qport.integer;
2122 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2123 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2124 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);
2127 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2130 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2131 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2135 M_Update_Return_Reason("QuakeWorld Accepted");
2137 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2140 if (length > 2 && !memcmp(string, "n\\", 2))
2143 serverlist_info_t *info;
2147 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2148 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2151 // search the cache for this server and update it
2152 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2156 info = &serverlist_cache[n].info;
2157 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2158 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2159 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2160 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2162 info->numplayers = 0; // updated below
2163 info->numhumans = 0; // updated below
2164 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2165 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2167 // count active players on server
2168 // (we could gather more info, but we're just after the number)
2169 s = strchr(string, '\n');
2173 while (s < string + length)
2175 for (;s < string + length && *s != '\n';s++)
2177 if (s >= string + length)
2185 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2189 if (string[0] == 'n')
2191 // qw print command, used by rcon replies too
2192 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2193 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2196 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2198 // we may not have liked the packet, but it was a command packet, so
2199 // we're done processing this packet now
2202 // quakeworld ingame packet
2203 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2206 CL_ParseServerMessage();
2209 // netquake control packets, supported for compatibility only
2210 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2214 serverlist_info_t *info;
2219 SZ_Clear(&cl_message);
2220 SZ_Write(&cl_message, data, length);
2221 MSG_BeginReading(&cl_message);
2222 c = MSG_ReadByte(&cl_message);
2226 if (developer_extra.integer)
2227 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2228 if (cls.connect_trying)
2230 lhnetaddress_t clientportaddress;
2231 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2232 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2235 clientportaddress = *peeraddress;
2236 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2237 // extra ProQuake stuff
2239 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2241 cls.proquake_servermod = 0;
2243 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2245 cls.proquake_serverversion = 0;
2247 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2249 cls.proquake_serverflags = 0;
2250 if (cls.proquake_servermod == 1)
2251 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2252 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2253 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2255 M_Update_Return_Reason("Accepted");
2257 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2261 if (developer_extra.integer) {
2262 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2265 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2267 cls.connect_trying = false;
2269 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2272 case CCREP_SERVER_INFO:
2273 if (developer_extra.integer)
2274 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2276 // LordHavoc: because the quake server may report weird addresses
2277 // we just ignore it and keep the real address
2278 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2279 // search the cache for this server and update it
2280 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2284 info = &serverlist_cache[n].info;
2285 strlcpy(info->game, "Quake", sizeof(info->game));
2286 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2287 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2288 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2289 info->numplayers = MSG_ReadByte(&cl_message);
2290 info->maxplayers = MSG_ReadByte(&cl_message);
2291 info->protocol = MSG_ReadByte(&cl_message);
2293 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2296 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2297 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2298 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2301 if (developer_extra.integer)
2302 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2304 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2306 case CCREP_PLAYER_INFO:
2307 // we got a CCREP_PLAYER_INFO??
2308 //if (developer_extra.integer)
2309 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2311 case CCREP_RULE_INFO:
2312 // we got a CCREP_RULE_INFO??
2313 //if (developer_extra.integer)
2314 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2319 SZ_Clear(&cl_message);
2320 // we may not have liked the packet, but it was a valid control
2321 // packet, so we're done processing this packet now
2325 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2326 CL_ParseServerMessage();
2331 void NetConn_QueryQueueFrame(void)
2337 static double querycounter = 0;
2339 if(!net_slist_pause.integer && serverlist_paused)
2340 ServerList_RebuildViewList();
2341 serverlist_paused = net_slist_pause.integer != 0;
2343 if (serverlist_querysleep)
2346 // apply a cool down time after master server replies,
2347 // to avoid messing up the ping times on the servers
2348 if (serverlist_querywaittime > realtime)
2351 // each time querycounter reaches 1.0 issue a query
2352 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2353 maxqueries = (int)querycounter;
2354 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2355 querycounter -= maxqueries;
2357 if( maxqueries == 0 ) {
2361 // scan serverlist and issue queries as needed
2362 serverlist_querysleep = true;
2364 timeouttime = realtime - net_slist_timeout.value;
2365 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2367 serverlist_entry_t *entry = &serverlist_cache[ index ];
2368 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2373 serverlist_querysleep = false;
2374 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2379 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2381 lhnetaddress_t address;
2384 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2385 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2387 for (socket = 0; socket < cl_numsockets ; socket++)
2388 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2392 for (socket = 0; socket < cl_numsockets ; socket++)
2393 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2396 // update the entry fields
2397 entry->querytime = realtime;
2398 entry->querycounter++;
2400 // if not in the slist menu we should print the server to console
2401 if (serverlist_consoleoutput)
2402 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2408 // have we tried to refresh this server?
2409 if( entry->query == SQS_REFRESHING ) {
2410 // yes, so update the reply count (since its not responding anymore)
2412 if(!serverlist_paused)
2413 ServerList_ViewList_Remove(entry);
2415 entry->query = SQS_TIMEDOUT;
2421 void NetConn_ClientFrame(void)
2424 lhnetaddress_t peeraddress;
2425 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2426 NetConn_UpdateSockets();
2427 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2430 if (cls.connect_remainingtries == 0)
2431 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2433 cls.connect_nextsendtime = realtime + 1;
2434 cls.connect_remainingtries--;
2435 if (cls.connect_remainingtries <= -10)
2437 cls.connect_trying = false;
2439 M_Update_Return_Reason("Connect: Failed");
2443 // try challenge first (newer DP server or QW)
2444 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2445 // then try netquake as a fallback (old server, or netquake)
2446 SZ_Clear(&cl_message);
2447 // save space for the header, filled in later
2448 MSG_WriteLong(&cl_message, 0);
2449 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2450 MSG_WriteString(&cl_message, "QUAKE");
2451 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2452 // extended proquake stuff
2453 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2454 // this version matches ProQuake 3.40, the first version to support
2455 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2456 // higher clients, so we pretend we are that version...
2457 MSG_WriteByte(&cl_message, 34); // version * 10
2458 MSG_WriteByte(&cl_message, 0); // flags
2459 MSG_WriteLong(&cl_message, 0); // password
2460 // write the packetsize now...
2461 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2462 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2463 SZ_Clear(&cl_message);
2465 for (i = 0;i < cl_numsockets;i++)
2467 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2469 // R_TimeReport("clientreadnetwork");
2470 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2471 // R_TimeReport("clientparsepacket");
2475 NetConn_QueryQueueFrame();
2477 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2479 Con_Print("Connection timed out\n");
2481 SV_LockThreadMutex();
2482 Host_ShutdownServer ();
2483 SV_UnlockThreadMutex();
2487 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2491 for (i = 0;i < bufferlength - 1;i++)
2495 c = rand () % (127 - 33) + 33;
2496 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2502 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2503 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2505 prvm_prog_t *prog = SVVM_prog;
2507 unsigned int nb_clients = 0, nb_bots = 0, i;
2510 const char *crypto_idstring;
2513 // How many clients are there?
2514 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2516 if (svs.clients[i].active)
2519 if (!svs.clients[i].netconnection)
2525 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2531 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2532 if(*q != '\\' && *q != '\n')
2537 /// \TODO: we should add more information for the full status string
2538 crypto_idstring = Crypto_GetInfoResponseDataString();
2539 length = dpsnprintf(out_msg, out_size,
2540 "\377\377\377\377%s\x0A"
2541 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2542 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2547 fullstatus ? "statusResponse" : "infoResponse",
2548 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2549 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2550 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2551 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2552 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2553 fullstatus ? "\n" : "");
2555 // Make sure it fits in the buffer
2565 savelength = length;
2567 ptr = out_msg + length;
2568 left = (int)out_size - length;
2570 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2572 client_t *cl = &svs.clients[i];
2575 int nameind, cleanind, pingvalue;
2577 char cleanname [sizeof(cl->name)];
2581 // Remove all characters '"' and '\' in the player name
2586 curchar = cl->name[nameind++];
2587 if (curchar != '"' && curchar != '\\')
2589 cleanname[cleanind++] = curchar;
2590 if (cleanind == sizeof(cleanname) - 1)
2593 } while (curchar != '\0');
2594 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2596 pingvalue = (int)(cl->ping * 1000.0f);
2597 if(cl->netconnection)
2598 pingvalue = bound(1, pingvalue, 9999);
2603 ed = PRVM_EDICT_NUM(i + 1);
2604 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2610 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2611 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2616 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2618 if(cl->frags == -666) // spectator
2619 strlcpy(teambuf, " 0", sizeof(teambuf));
2620 else if(cl->colors == 0x44) // red team
2621 strlcpy(teambuf, " 1", sizeof(teambuf));
2622 else if(cl->colors == 0xDD) // blue team
2623 strlcpy(teambuf, " 2", sizeof(teambuf));
2624 else if(cl->colors == 0xCC) // yellow team
2625 strlcpy(teambuf, " 3", sizeof(teambuf));
2626 else if(cl->colors == 0x99) // pink team
2627 strlcpy(teambuf, " 4", sizeof(teambuf));
2629 strlcpy(teambuf, " 0", sizeof(teambuf));
2634 // note: team number is inserted according to SoF2 protocol
2636 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2642 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2651 // turn it into an infoResponse!
2652 out_msg[savelength] = 0;
2653 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2654 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2669 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2671 size_t floodslotnum, bestfloodslotnum;
2672 double bestfloodtime;
2673 lhnetaddress_t noportpeeraddress;
2674 // see if this is a connect flood
2675 noportpeeraddress = *peeraddress;
2676 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2677 bestfloodslotnum = 0;
2678 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2679 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2681 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2683 bestfloodtime = floodlist[floodslotnum].lasttime;
2684 bestfloodslotnum = floodslotnum;
2686 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2688 // this address matches an ongoing flood address
2689 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2693 // renew the ban on this address so it does not expire
2694 // until the flood has subsided
2695 floodlist[floodslotnum].lasttime = realtime;
2697 //Con_Printf("Flood detected!\n");
2700 // the flood appears to have subsided, so allow this
2701 bestfloodslotnum = floodslotnum; // reuse the same slot
2705 // begin a new timeout on this address
2706 floodlist[bestfloodslotnum].address = noportpeeraddress;
2707 floodlist[bestfloodslotnum].lasttime = realtime;
2708 //Con_Printf("Flood detection initiated!\n");
2712 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2714 size_t floodslotnum;
2715 lhnetaddress_t noportpeeraddress;
2716 // see if this is a connect flood
2717 noportpeeraddress = *peeraddress;
2718 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2719 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2721 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2723 // this address matches an ongoing flood address
2725 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2726 floodlist[floodslotnum].lasttime = 0;
2727 //Con_Printf("Flood cleared!\n");
2732 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2734 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2739 t1 = (long) time(NULL);
2740 t2 = strtol(s, NULL, 0);
2741 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2744 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2747 return !memcmp(mdfourbuf, hash, 16);
2750 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2755 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2758 // validate the challenge
2759 for (i = 0;i < MAX_CHALLENGES;i++)
2760 if(challenge[i].time > 0)
2761 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2763 // if the challenge is not recognized, drop the packet
2764 if (i == MAX_CHALLENGES)
2767 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2770 if(memcmp(mdfourbuf, hash, 16))
2773 // unmark challenge to prevent replay attacks
2774 challenge[i].time = 0;
2779 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2781 return !strcmp(password, hash);
2784 /// returns a string describing the user level, or NULL for auth failure
2785 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)
2787 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2788 static char buf[MAX_INPUTLINE];
2790 qboolean restricted = false;
2791 qboolean have_usernames = false;
2792 static char vabuf[1024];
2794 userpass_start = rcon_password.string;
2795 while((userpass_end = strchr(userpass_start, ' ')))
2797 have_usernames = true;
2798 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2800 if(comparator(peeraddress, buf, password, cs, cslen))
2802 userpass_start = userpass_end + 1;
2804 if(userpass_start[0])
2806 userpass_end = userpass_start + strlen(userpass_start);
2807 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2812 have_usernames = false;
2813 userpass_start = rcon_restricted_password.string;
2814 while((userpass_end = strchr(userpass_start, ' ')))
2816 have_usernames = true;
2817 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2819 if(comparator(peeraddress, buf, password, cs, cslen))
2821 userpass_start = userpass_end + 1;
2823 if(userpass_start[0])
2825 userpass_end = userpass_start + strlen(userpass_start);
2826 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2830 return NULL; // DENIED
2833 for(text = s; text != endpos; ++text)
2834 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2835 return NULL; // block possible exploits against the parser/alias expansion
2839 size_t l = strlen(s);
2842 hasquotes = (strchr(s, '"') != NULL);
2843 // sorry, we can't allow these substrings in wildcard expressions,
2844 // as they can mess with the argument counts
2845 text = rcon_restricted_commands.string;
2846 while(COM_ParseToken_Console(&text))
2848 // com_token now contains a pattern to check for...
2849 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2852 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2855 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2857 if(!strcmp(com_token, s))
2860 else // single-arg expression? must match the beginning of the command
2862 if(!strcmp(com_token, s))
2864 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2868 // if we got here, nothing matched!
2876 userpass_startpass = strchr(userpass_start, ':');
2877 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2878 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2880 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2883 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2887 // looks like a legitimate rcon command with the correct password
2888 const char *s_ptr = s;
2889 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2890 while(s_ptr != endpos)
2892 size_t l = strlen(s_ptr);
2894 Con_Printf(" %s;", s_ptr);
2899 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2900 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2903 size_t l = strlen(s);
2906 client_t *host_client_save = host_client;
2907 Cmd_ExecuteString(s, src_command, true);
2908 host_client = host_client_save;
2909 // in case it is a command that changes host_client (like restart)
2913 Con_Rcon_Redirect_End();
2917 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2921 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2923 int i, ret, clientnum, best;
2926 char *s, *string, response[1400], addressstring2[128];
2927 static char stringbuf[16384]; // server only
2928 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2929 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2930 size_t sendlength, response_len;
2931 char infostringvalue[MAX_INPUTLINE];
2937 // convert the address to a string incase we need it
2938 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2940 // see if we can identify the sender as a local player
2941 // (this is necessary for rcon to send a reliable reply if the client is
2942 // actually on the server, not sending remotely)
2943 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2944 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2946 if (i == svs.maxclients)
2949 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2951 // received a command string - strip off the packaging and put it
2952 // into our string buffer with NULL termination
2955 length = min(length, (int)sizeof(stringbuf) - 1);
2956 memcpy(stringbuf, data, length);
2957 stringbuf[length] = 0;
2960 if (developer_extra.integer)
2962 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2963 Com_HexDumpToConsole(data, length);
2966 sendlength = sizeof(senddata) - 4;
2967 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2969 case CRYPTO_NOMATCH:
2975 memcpy(senddata, "\377\377\377\377", 4);
2976 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2979 case CRYPTO_DISCARD:
2982 memcpy(senddata, "\377\377\377\377", 4);
2983 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2987 case CRYPTO_REPLACE:
2988 string = senddata+4;
2989 length = (int)sendlength;
2993 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2995 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2997 if(challenge[i].time > 0)
2998 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
3000 if (besttime > challenge[i].time)
3001 besttime = challenge[best = i].time;
3003 // if we did not find an exact match, choose the oldest and
3004 // update address and string
3005 if (i == MAX_CHALLENGES)
3008 challenge[i].address = *peeraddress;
3009 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
3013 // flood control: drop if requesting challenge too often
3014 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
3017 challenge[i].time = realtime;
3018 // send the challenge
3019 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
3020 response_len = strlen(response) + 1;
3021 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3022 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3025 if (length > 8 && !memcmp(string, "connect\\", 8))
3027 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3031 if(crypto && crypto->authenticated)
3033 // no need to check challenge
3034 if(crypto_developer.integer)
3036 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3037 crypto->use_aes ? "Encrypted" : "Authenticated",
3039 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3040 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3041 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3042 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3043 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3044 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3050 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3052 // validate the challenge
3053 for (i = 0;i < MAX_CHALLENGES;i++)
3054 if(challenge[i].time > 0)
3055 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
3057 // if the challenge is not recognized, drop the packet
3058 if (i == MAX_CHALLENGES)
3063 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3064 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3066 if(!(islocal || sv_public.integer > -2))
3068 if (developer_extra.integer)
3069 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3070 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
3074 // check engine protocol
3075 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3077 if (developer_extra.integer)
3078 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3079 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3083 // see if this is a duplicate connection request or a disconnected
3084 // client who is rejoining to the same client slot
3085 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3087 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3089 // this is a known client...
3090 if(crypto && crypto->authenticated)
3092 // reject if changing key!
3093 if(client->netconnection->crypto.authenticated)
3096 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3098 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3100 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3102 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3105 if (developer_extra.integer)
3106 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3107 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3114 // reject if downgrading!
3115 if(client->netconnection->crypto.authenticated)
3117 if (developer_extra.integer)
3118 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3119 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3125 // client crashed and is coming back,
3126 // keep their stuff intact
3127 if (developer_extra.integer)
3128 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3129 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3130 if(crypto && crypto->authenticated)
3131 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3132 SV_SendServerinfo(client);
3136 // client is still trying to connect,
3137 // so we send a duplicate reply
3138 if (developer_extra.integer)
3139 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3140 if(crypto && crypto->authenticated)
3141 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3142 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3148 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3151 // find an empty client slot for this new client
3152 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3155 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3157 // allocated connection
3158 if (developer_extra.integer)
3159 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3160 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3161 // now set up the client
3162 if(crypto && crypto->authenticated)
3163 Crypto_FinishInstance(&conn->crypto, crypto);
3164 SV_ConnectClient(clientnum, conn);
3165 NetConn_Heartbeat(1);
3170 // no empty slots found - server is full
3171 if (developer_extra.integer)
3172 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3173 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3177 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3179 const char *challenge = NULL;
3181 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3184 // If there was a challenge in the getinfo message
3185 if (length > 8 && string[7] == ' ')
3186 challenge = string + 8;
3188 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3190 if (developer_extra.integer)
3191 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3192 NetConn_WriteString(mysocket, response, peeraddress);
3196 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3198 const char *challenge = NULL;
3200 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3203 // If there was a challenge in the getinfo message
3204 if (length > 10 && string[9] == ' ')
3205 challenge = string + 10;
3207 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3209 if (developer_extra.integer)
3210 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3211 NetConn_WriteString(mysocket, response, peeraddress);
3215 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3217 char *password = string + 20;
3218 char *timeval = string + 37;
3219 char *s = strchr(timeval, ' ');
3220 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3221 const char *userlevel;
3223 if(rcon_secure.integer > 1)
3227 return true; // invalid packet
3230 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3231 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3234 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3236 char *password = string + 25;
3237 char *challenge = string + 42;
3238 char *s = strchr(challenge, ' ');
3239 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3240 const char *userlevel;
3242 return true; // invalid packet
3245 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3246 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3249 if (length >= 5 && !memcmp(string, "rcon ", 5))
3252 char *s = string + 5;
3253 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3256 if(rcon_secure.integer > 0)
3259 for (i = 0;!ISWHITESPACE(*s);s++)
3260 if (i < (int)sizeof(password) - 1)
3262 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3265 if (!ISWHITESPACE(password[0]))
3267 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3268 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3272 if (!strncmp(string, "extResponse ", 12))
3274 ++sv_net_extresponse_count;
3275 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3276 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3277 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3278 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3281 if (!strncmp(string, "ping", 4))
3283 if (developer_extra.integer)
3284 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3285 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3288 if (!strncmp(string, "ack", 3))
3290 // we may not have liked the packet, but it was a command packet, so
3291 // we're done processing this packet now
3294 // netquake control packets, supported for compatibility only, and only
3295 // when running game protocols that are normally served via this connection
3297 // (this protects more modern protocols against being used for
3298 // Quake packet flood Denial Of Service attacks)
3299 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)
3303 const char *protocolname;
3306 SZ_Clear(&sv_message);
3307 SZ_Write(&sv_message, data, length);
3308 MSG_BeginReading(&sv_message);
3309 c = MSG_ReadByte(&sv_message);
3313 if (developer_extra.integer)
3314 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3315 if(!(islocal || sv_public.integer > -2))
3317 if (developer_extra.integer)
3318 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3319 SZ_Clear(&sv_message);
3320 // save space for the header, filled in later
3321 MSG_WriteLong(&sv_message, 0);
3322 MSG_WriteByte(&sv_message, CCREP_REJECT);
3323 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3324 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3325 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3326 SZ_Clear(&sv_message);
3330 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3331 protocolnumber = MSG_ReadByte(&sv_message);
3332 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3334 if (developer_extra.integer)
3335 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3336 SZ_Clear(&sv_message);
3337 // save space for the header, filled in later
3338 MSG_WriteLong(&sv_message, 0);
3339 MSG_WriteByte(&sv_message, CCREP_REJECT);
3340 MSG_WriteString(&sv_message, "Incompatible version.\n");
3341 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3342 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3343 SZ_Clear(&sv_message);
3347 // see if this connect request comes from a known client
3348 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3350 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3352 // this is either a duplicate connection request
3353 // or coming back from a timeout
3354 // (if so, keep their stuff intact)
3356 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3357 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3359 if (developer_extra.integer)
3360 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3361 SZ_Clear(&sv_message);
3362 // save space for the header, filled in later
3363 MSG_WriteLong(&sv_message, 0);
3364 MSG_WriteByte(&sv_message, CCREP_REJECT);
3365 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3366 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3367 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3368 SZ_Clear(&sv_message);
3373 if (developer_extra.integer)
3374 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3375 SZ_Clear(&sv_message);
3376 // save space for the header, filled in later
3377 MSG_WriteLong(&sv_message, 0);
3378 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3379 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3380 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3381 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3382 SZ_Clear(&sv_message);
3384 // if client is already spawned, re-send the
3385 // serverinfo message as they'll need it to play
3387 SV_SendServerinfo(client);
3392 // this is a new client, check for connection flood
3393 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3396 // find a slot for the new client
3397 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3400 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3402 // connect to the client
3403 // everything is allocated, just fill in the details
3404 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3405 if (developer_extra.integer)
3406 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3407 // send back the info about the server connection
3408 SZ_Clear(&sv_message);
3409 // save space for the header, filled in later
3410 MSG_WriteLong(&sv_message, 0);
3411 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3412 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3413 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3414 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3415 SZ_Clear(&sv_message);
3416 // now set up the client struct
3417 SV_ConnectClient(clientnum, conn);
3418 NetConn_Heartbeat(1);
3423 if (developer_extra.integer)
3424 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3425 // no room; try to let player know
3426 SZ_Clear(&sv_message);
3427 // save space for the header, filled in later
3428 MSG_WriteLong(&sv_message, 0);
3429 MSG_WriteByte(&sv_message, CCREP_REJECT);
3430 MSG_WriteString(&sv_message, "Server is full.\n");
3431 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3432 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3433 SZ_Clear(&sv_message);
3435 case CCREQ_SERVER_INFO:
3436 if (developer_extra.integer)
3437 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3438 if(!(islocal || sv_public.integer > -1))
3441 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3444 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3447 char myaddressstring[128];
3448 if (developer_extra.integer)
3449 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3450 SZ_Clear(&sv_message);
3451 // save space for the header, filled in later
3452 MSG_WriteLong(&sv_message, 0);
3453 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3454 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3455 MSG_WriteString(&sv_message, myaddressstring);
3456 MSG_WriteString(&sv_message, hostname.string);
3457 MSG_WriteString(&sv_message, sv.name);
3458 // How many clients are there?
3459 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3460 if (svs.clients[i].active)
3462 MSG_WriteByte(&sv_message, numclients);
3463 MSG_WriteByte(&sv_message, svs.maxclients);
3464 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3465 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3466 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3467 SZ_Clear(&sv_message);
3470 case CCREQ_PLAYER_INFO:
3471 if (developer_extra.integer)
3472 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3473 if(!(islocal || sv_public.integer > -1))
3476 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3481 int playerNumber, activeNumber, clientNumber;
3484 playerNumber = MSG_ReadByte(&sv_message);
3486 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3487 if (client->active && ++activeNumber == playerNumber)
3489 if (clientNumber != svs.maxclients)
3491 SZ_Clear(&sv_message);
3492 // save space for the header, filled in later
3493 MSG_WriteLong(&sv_message, 0);
3494 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3495 MSG_WriteByte(&sv_message, playerNumber);
3496 MSG_WriteString(&sv_message, client->name);
3497 MSG_WriteLong(&sv_message, client->colors);
3498 MSG_WriteLong(&sv_message, client->frags);
3499 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3500 if(sv_status_privacy.integer)
3501 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3503 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3504 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3505 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3506 SZ_Clear(&sv_message);
3510 case CCREQ_RULE_INFO:
3511 if (developer_extra.integer)
3512 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3513 if(!(islocal || sv_public.integer > -1))
3516 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3523 // find the search start location
3524 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3525 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3527 // send the response
3528 SZ_Clear(&sv_message);
3529 // save space for the header, filled in later
3530 MSG_WriteLong(&sv_message, 0);
3531 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3534 MSG_WriteString(&sv_message, var->name);
3535 MSG_WriteString(&sv_message, var->string);
3537 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3538 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3539 SZ_Clear(&sv_message);
3543 if (developer_extra.integer)
3544 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3545 if (sv.active && !rcon_secure.integer)
3547 char password[2048];
3551 const char *userlevel;
3552 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3553 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3555 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3556 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3557 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3564 SZ_Clear(&sv_message);
3565 // we may not have liked the packet, but it was a valid control
3566 // packet, so we're done processing this packet now
3571 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3573 SV_ReadClientMessage();
3580 void NetConn_ServerFrame(void)
3583 lhnetaddress_t peeraddress;
3584 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3585 for (i = 0;i < sv_numsockets;i++)
3586 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3587 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3588 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3590 // never timeout loopback connections
3591 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3593 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3594 SV_DropClient(false);
3599 void NetConn_SleepMicroseconds(int microseconds)
3601 LHNET_SleepUntilPacket_Microseconds(microseconds);
3605 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3609 lhnetaddress_t masteraddress;
3610 lhnetaddress_t broadcastaddress;
3613 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3616 // 26000 is the default quake server port, servers on other ports will not
3618 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3619 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3623 for (i = 0;i < cl_numsockets;i++)
3627 const char *cmdname, *extraoptions;
3628 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3630 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3632 // search LAN for Quake servers
3633 SZ_Clear(&cl_message);
3634 // save space for the header, filled in later
3635 MSG_WriteLong(&cl_message, 0);
3636 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3637 MSG_WriteString(&cl_message, "QUAKE");
3638 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3639 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3640 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3641 SZ_Clear(&cl_message);
3643 // search LAN for DarkPlaces servers
3644 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3647 // build the getservers message to send to the dpmaster master servers
3648 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3650 cmdname = "getserversExt";
3651 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3655 cmdname = "getservers";
3658 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3661 for (masternum = 0;sv_masters[masternum].name;masternum++)
3663 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3666 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3670 // search favorite servers
3671 for(j = 0; j < nFavorites; ++j)
3673 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3675 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3676 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3683 // only query QuakeWorld servers when the user wants to
3686 for (i = 0;i < cl_numsockets;i++)
3690 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3692 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3694 // search LAN for QuakeWorld servers
3695 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3697 // build the getservers message to send to the qwmaster master servers
3698 // note this has no -1 prefix, and the trailing nul byte is sent
3699 dpsnprintf(request, sizeof(request), "c\n");
3703 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3705 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3707 if (m_state != m_slist)
3709 char lookupstring[128];
3710 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3711 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3714 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3718 // search favorite servers
3719 for(j = 0; j < nFavorites; ++j)
3721 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3723 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3725 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3726 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3733 if (!masterquerycount)
3735 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3736 M_Update_Return_Reason("No network");
3741 void NetConn_Heartbeat(int priority)
3743 lhnetaddress_t masteraddress;
3745 lhnetsocket_t *mysocket;
3747 // if it's a state change (client connected), limit next heartbeat to no
3748 // more than 30 sec in the future
3749 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3750 nextheartbeattime = realtime + 30.0;
3752 // limit heartbeatperiod to 30 to 270 second range,
3753 // lower limit is to avoid abusing master servers with excess traffic,
3754 // upper limit is to avoid timing out on the master server (which uses
3756 if (sv_heartbeatperiod.value < 30)
3757 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3758 if (sv_heartbeatperiod.value > 270)
3759 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3761 // make advertising optional and don't advertise singleplayer games, and
3762 // only send a heartbeat as often as the admin wants
3763 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3765 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3766 for (masternum = 0;sv_masters[masternum].name;masternum++)
3767 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3768 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3772 static void Net_Heartbeat_f(void)
3775 NetConn_Heartbeat(2);
3777 Con_Print("No server running, can not heartbeat to master server.\n");
3780 static void PrintStats(netconn_t *conn)
3782 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3783 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3785 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3786 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3787 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3788 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3789 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3790 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3791 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3792 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3793 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3794 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3797 void Net_Stats_f(void)
3800 Con_Print("connections =\n");
3801 for (conn = netconn_list;conn;conn = conn->next)
3806 void Net_Refresh_f(void)
3808 if (m_state != m_slist) {
3809 Con_Print("Sending new requests to master servers\n");
3810 ServerList_QueryList(false, true, false, true);
3811 Con_Print("Listening for replies...\n");
3813 ServerList_QueryList(false, true, false, false);
3816 void Net_Slist_f(void)
3818 ServerList_ResetMasks();
3819 serverlist_sortbyfield = SLIF_PING;
3820 serverlist_sortflags = 0;
3821 if (m_state != m_slist) {
3822 Con_Print("Sending requests to master servers\n");
3823 ServerList_QueryList(true, true, false, true);
3824 Con_Print("Listening for replies...\n");
3826 ServerList_QueryList(true, true, false, false);
3829 void Net_SlistQW_f(void)
3831 ServerList_ResetMasks();
3832 serverlist_sortbyfield = SLIF_PING;
3833 serverlist_sortflags = 0;
3834 if (m_state != m_slist) {
3835 Con_Print("Sending requests to master servers\n");
3836 ServerList_QueryList(true, false, true, true);
3837 serverlist_consoleoutput = true;
3838 Con_Print("Listening for replies...\n");
3840 ServerList_QueryList(true, false, true, false);
3844 void NetConn_Init(void)
3847 lhnetaddress_t tempaddress;
3848 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3849 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3851 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3852 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3853 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3855 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3856 Cvar_RegisterVariable(&net_test);
3857 Cvar_RegisterVariable(&net_usesizelimit);
3858 Cvar_RegisterVariable(&net_burstreserve);
3859 Cvar_RegisterVariable(&rcon_restricted_password);
3860 Cvar_RegisterVariable(&rcon_restricted_commands);
3861 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3862 Cvar_RegisterVariable(&net_slist_queriespersecond);
3863 Cvar_RegisterVariable(&net_slist_queriesperframe);
3864 Cvar_RegisterVariable(&net_slist_timeout);
3865 Cvar_RegisterVariable(&net_slist_maxtries);
3866 Cvar_RegisterVariable(&net_slist_favorites);
3867 Cvar_RegisterVariable(&net_slist_pause);
3868 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3869 Cvar_RegisterVariable(&net_tos_dscp);
3870 Cvar_RegisterVariable(&net_messagetimeout);
3871 Cvar_RegisterVariable(&net_connecttimeout);
3872 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3873 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3874 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3875 Cvar_RegisterVariable(&net_sourceaddresscheck);
3876 Cvar_RegisterVariable(&cl_netlocalping);
3877 Cvar_RegisterVariable(&cl_netpacketloss_send);
3878 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3879 Cvar_RegisterVariable(&hostname);
3880 Cvar_RegisterVariable(&developer_networking);
3881 Cvar_RegisterVariable(&cl_netport);
3882 Cvar_RegisterVariable(&sv_netport);
3883 Cvar_RegisterVariable(&net_address);
3884 Cvar_RegisterVariable(&net_address_ipv6);
3885 Cvar_RegisterVariable(&sv_public);
3886 Cvar_RegisterVariable(&sv_public_rejectreason);
3887 Cvar_RegisterVariable(&sv_heartbeatperiod);
3888 for (i = 0;sv_masters[i].name;i++)
3889 Cvar_RegisterVariable(&sv_masters[i]);
3890 Cvar_RegisterVariable(&gameversion);
3891 Cvar_RegisterVariable(&gameversion_min);
3892 Cvar_RegisterVariable(&gameversion_max);
3893 // 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.
3894 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3896 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3898 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3899 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3902 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3904 // 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
3905 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3907 i = atoi(com_argv[i + 1]);
3908 if (i >= 0 && i < 65536)
3910 Con_Printf("-port option used, setting port cvar to %i\n", i);
3911 Cvar_SetValueQuick(&sv_netport, i);
3914 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3918 cl_message.data = cl_message_buf;
3919 cl_message.maxsize = sizeof(cl_message_buf);
3920 cl_message.cursize = 0;
3921 sv_message.data = sv_message_buf;
3922 sv_message.maxsize = sizeof(sv_message_buf);
3923 sv_message.cursize = 0;
3925 if (Thread_HasThreads())
3926 netconn_mutex = Thread_CreateMutex();
3929 void NetConn_Shutdown(void)
3931 NetConn_CloseClientPorts();
3932 NetConn_CloseServerPorts();
3935 Thread_DestroyMutex(netconn_mutex);
3936 netconn_mutex = NULL;