2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // for secure rcon authentication
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
35 // note this defaults on for dedicated servers, off for listen servers
36 cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
37 cvar_t sv_public_rejectreason = {0, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
39 extern cvar_t sv_status_privacy;
41 static cvar_t sv_masters [] =
43 {CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
44 {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
45 {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
46 {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
47 {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
48 {0, "sv_masterextra2", "64.22.107.125", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
49 {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
51 {0, "sv_masterextra4", "[2a03:4000:2:225::51:334d]:27950", "dpmaster.sudo.rm-f.org - default master server 4 (admin: divVerent)"}, // admin: divVerent
57 static cvar_t sv_qwmasters [] =
59 {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
60 {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
61 {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
62 {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
63 {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
64 {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
65 {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
66 {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
67 {0, "sv_qwmasterextra5", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
72 static double nextheartbeattime = 0;
76 static unsigned char cl_message_buf[NET_MAXMESSAGE];
77 static unsigned char sv_message_buf[NET_MAXMESSAGE];
78 char cl_readstring[MAX_INPUTLINE];
79 char sv_readstring[MAX_INPUTLINE];
81 cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
82 cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
83 cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
84 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
85 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."};
86 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."};
87 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."};
88 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."};
89 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
90 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
92 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)"};
93 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)"};
94 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)"};
95 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
96 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)"};
97 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
98 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"};
99 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)"};
100 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"};
101 static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
102 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
103 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"};
104 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"};
105 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"};
106 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
107 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)"};
108 extern cvar_t rcon_secure;
109 extern cvar_t rcon_secure_challengetimeout;
111 double masterquerytime = -1000;
112 int masterquerycount = 0;
113 int masterreplycount = 0;
114 int serverquerycount = 0;
115 int serverreplycount = 0;
117 challenge_t challenge[MAX_CHALLENGES];
120 /// this is only false if there are still servers left to query
121 static qboolean serverlist_querysleep = true;
122 static qboolean serverlist_paused = false;
123 /// this is pushed a second or two ahead of realtime whenever a master server
124 /// reply is received, to avoid issuing queries while master replies are still
125 /// flooding in (which would make a mess of the ping times)
126 static double serverlist_querywaittime = 0;
129 static int cl_numsockets;
130 static lhnetsocket_t *cl_sockets[16];
131 static int sv_numsockets;
132 static lhnetsocket_t *sv_sockets[16];
134 netconn_t *netconn_list = NULL;
135 mempool_t *netconn_mempool = NULL;
136 void *netconn_mutex = NULL;
138 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
139 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
140 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
141 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
143 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
144 int cl_net_extresponse_count = 0;
145 int cl_net_extresponse_last = 0;
147 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
148 int sv_net_extresponse_count = 0;
149 int sv_net_extresponse_last = 0;
152 // ServerList interface
153 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
154 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
156 serverlist_infofield_t serverlist_sortbyfield;
157 int serverlist_sortflags;
159 int serverlist_viewcount = 0;
160 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
162 int serverlist_maxcachecount = 0;
163 int serverlist_cachecount = 0;
164 serverlist_entry_t *serverlist_cache = NULL;
166 qboolean serverlist_consoleoutput;
168 static int nFavorites = 0;
169 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
170 static int nFavorites_idfp = 0;
171 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
173 void NetConn_UpdateFavorites(void)
178 p = net_slist_favorites.string;
179 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
181 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
182 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
183 // (if v6 address contains port, it must start with '[')
185 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
190 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
196 /// helper function to insert a value into the viewset
197 /// spare entries will be removed
198 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
201 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
202 i = serverlist_viewcount++;
204 i = SERVERLIST_VIEWLISTSIZE - 1;
207 for( ; i > index ; i-- )
208 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
210 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
213 /// we suppose serverlist_viewcount to be valid, ie > 0
214 static void _ServerList_ViewList_Helper_Remove( int index )
216 serverlist_viewcount--;
217 for( ; index < serverlist_viewcount ; index++ )
218 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
221 /// \returns true if A should be inserted before B
222 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
224 int result = 0; // > 0 if for numbers A > B and for text if A < B
226 if( serverlist_sortflags & SLSF_CATEGORIES )
228 result = A->info.category - B->info.category;
233 if( serverlist_sortflags & SLSF_FAVORITES )
235 if(A->info.isfavorite != B->info.isfavorite)
236 return A->info.isfavorite;
239 switch( serverlist_sortbyfield ) {
241 result = A->info.ping - B->info.ping;
243 case SLIF_MAXPLAYERS:
244 result = A->info.maxplayers - B->info.maxplayers;
246 case SLIF_NUMPLAYERS:
247 result = A->info.numplayers - B->info.numplayers;
250 result = A->info.numbots - B->info.numbots;
253 result = A->info.numhumans - B->info.numhumans;
256 result = A->info.freeslots - B->info.freeslots;
259 result = A->info.protocol - B->info.protocol;
262 result = strcmp( B->info.cname, A->info.cname );
265 result = strcasecmp( B->info.game, A->info.game );
268 result = strcasecmp( B->info.map, A->info.map );
271 result = strcasecmp( B->info.mod, A->info.mod );
274 result = strcasecmp( B->info.name, A->info.name );
277 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
280 result = A->info.category - B->info.category;
282 case SLIF_ISFAVORITE:
283 result = !!B->info.isfavorite - !!A->info.isfavorite;
286 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
292 if( serverlist_sortflags & SLSF_DESCENDING )
298 // if the chosen sort key is identical, sort by index
299 // (makes this a stable sort, so that later replies from servers won't
300 // shuffle the servers around when they have the same ping)
304 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
306 // This should actually be done with some intermediate and end-of-function return
318 case SLMO_GREATEREQUAL:
320 case SLMO_NOTCONTAIN:
321 case SLMO_STARTSWITH:
322 case SLMO_NOTSTARTSWITH:
325 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
330 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
333 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
334 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
335 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
336 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
338 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
339 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
342 // Same here, also using an intermediate & final return would be more appropriate
346 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
347 case SLMO_NOTCONTAIN:
348 return !*bufferB || !strstr( bufferA, bufferB );
349 case SLMO_STARTSWITH:
350 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
351 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
352 case SLMO_NOTSTARTSWITH:
353 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
355 return strcmp( bufferA, bufferB ) < 0;
357 return strcmp( bufferA, bufferB ) <= 0;
359 return strcmp( bufferA, bufferB ) == 0;
361 return strcmp( bufferA, bufferB ) > 0;
363 return strcmp( bufferA, bufferB ) != 0;
364 case SLMO_GREATEREQUAL:
365 return strcmp( bufferA, bufferB ) >= 0;
367 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
372 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
374 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
376 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
378 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
380 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
382 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
384 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
386 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
388 if( *mask->info.cname
389 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
392 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
395 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
398 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
401 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
403 if( *mask->info.qcstatus
404 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
406 if( *mask->info.players
407 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
409 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
411 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
416 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
418 int start, end, mid, i;
421 // reject incompatible servers
423 entry->info.gameversion != gameversion.integer
426 gameversion_min.integer >= 0 // min/max range set by user/mod?
427 && gameversion_max.integer >= 0
428 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
429 && gameversion_max.integer >= entry->info.gameversion
434 // refresh the "favorite" status
435 entry->info.isfavorite = false;
436 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
438 char idfp[FP64_SIZE+1];
439 for(i = 0; i < nFavorites; ++i)
441 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
443 entry->info.isfavorite = true;
447 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
449 for(i = 0; i < nFavorites_idfp; ++i)
451 if(!strcmp(idfp, favorites_idfp[i]))
453 entry->info.isfavorite = true;
460 // refresh the "category"
461 entry->info.category = MR_GetServerListEntryCategory(entry);
463 // FIXME: change this to be more readable (...)
464 // now check whether it passes through the masks
465 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
466 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
469 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
470 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
472 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
475 if( !serverlist_viewcount ) {
476 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
479 // ok, insert it, we just need to find out where exactly:
482 // check whether to insert it as new first item
483 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
484 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
486 } // check whether to insert it as new last item
487 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
488 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
492 end = serverlist_viewcount - 1;
493 while( end > start + 1 )
495 mid = (start + end) / 2;
496 // test the item that lies in the middle between start and end
497 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
498 // the item has to be in the upper half
501 // the item has to be in the lower half
504 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
507 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
510 for( i = 0; i < serverlist_viewcount; i++ )
512 if (ServerList_GetViewEntry(i) == entry)
514 _ServerList_ViewList_Helper_Remove(i);
520 void ServerList_RebuildViewList(void)
524 serverlist_viewcount = 0;
525 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
526 serverlist_entry_t *entry = &serverlist_cache[i];
527 // also display entries that are currently being refreshed [11/8/2007 Black]
528 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
529 ServerList_ViewList_Insert( entry );
533 void ServerList_ResetMasks(void)
537 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
538 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
539 // numbots needs to be compared to -1 to always succeed
540 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
541 serverlist_andmasks[i].info.numbots = -1;
542 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
543 serverlist_ormasks[i].info.numbots = -1;
546 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
549 int numplayers = 0, maxplayers = 0;
550 for (i = 0;i < serverlist_cachecount;i++)
552 if (serverlist_cache[i].query == SQS_QUERIED)
554 numplayers += serverlist_cache[i].info.numhumans;
555 maxplayers += serverlist_cache[i].info.maxplayers;
558 *numplayerspointer = numplayers;
559 *maxplayerspointer = maxplayers;
563 static void _ServerList_Test(void)
566 if (serverlist_maxcachecount <= 1024)
568 serverlist_maxcachecount = 1024;
569 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
571 for( i = 0 ; i < 1024 ; i++ ) {
572 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
573 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
574 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
575 serverlist_cache[serverlist_cachecount].finished = true;
576 dpsnprintf( serverlist_cache[serverlist_cachecount].line1, sizeof(serverlist_cache[serverlist_cachecount].info.line1), "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
577 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
578 serverlist_cachecount++;
583 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
585 masterquerytime = realtime;
586 masterquerycount = 0;
587 masterreplycount = 0;
589 serverquerycount = 0;
590 serverreplycount = 0;
591 serverlist_cachecount = 0;
592 serverlist_viewcount = 0;
593 serverlist_maxcachecount = 0;
594 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
596 // refresh all entries
598 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
599 serverlist_entry_t *entry = &serverlist_cache[ n ];
600 entry->query = SQS_REFRESHING;
601 entry->querycounter = 0;
604 serverlist_consoleoutput = consoleoutput;
606 //_ServerList_Test();
608 NetConn_QueryMasters(querydp, queryqw);
614 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
618 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
619 Thread_LockMutex(netconn_mutex);
620 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
621 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
622 Thread_UnlockMutex(netconn_mutex);
625 if (cl_netpacketloss_receive.integer)
626 for (i = 0;i < cl_numsockets;i++)
627 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
629 if (developer_networking.integer)
631 char addressstring[128], addressstring2[128];
632 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
635 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
636 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
637 Com_HexDumpToConsole((unsigned char *)data, length);
640 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
645 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
649 if (cl_netpacketloss_send.integer)
650 for (i = 0;i < cl_numsockets;i++)
651 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
653 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
654 Thread_LockMutex(netconn_mutex);
655 ret = LHNET_Write(mysocket, data, length, peeraddress);
656 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
657 Thread_UnlockMutex(netconn_mutex);
658 if (developer_networking.integer)
660 char addressstring[128], addressstring2[128];
661 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
662 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
663 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", (void *)mysocket, addressstring, (void *)data, length, (void *)peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
664 Com_HexDumpToConsole((unsigned char *)data, length);
669 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
671 // note this does not include the trailing NULL because we add that in the parser
672 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
675 qboolean NetConn_CanSend(netconn_t *conn)
677 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
678 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = realtime;
679 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
680 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
681 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
682 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
683 if (realtime > conn->cleartime)
687 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
692 void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
694 double bursttime = burstsize / (double)rate;
696 // delay later packets to obey rate limit
697 if (*cleartime < realtime - bursttime)
698 *cleartime = realtime - bursttime;
699 *cleartime = *cleartime + len / (double)rate;
701 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
702 if (net_test.integer)
704 if (*cleartime < realtime)
705 *cleartime = realtime;
709 static int NetConn_AddCryptoFlag(crypto_t *crypto)
711 // HACK: if an encrypted connection is used, randomly set some unused
712 // flags. When AES encryption is enabled, that will make resends differ
713 // from the original, so that e.g. substring filters in a router/IPS
714 // are unlikely to match a second time. See also "startkeylogger".
716 if (crypto->authenticated)
718 // Let's always set at least one of the bits.
719 int r = rand() % 7 + 1;
721 flag |= NETFLAG_CRYPTO0;
723 flag |= NETFLAG_CRYPTO1;
725 flag |= NETFLAG_CRYPTO2;
730 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
733 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
734 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
736 // if this packet was supposedly choked, but we find ourselves sending one
737 // anyway, make sure the size counting starts at zero
738 // (this mostly happens on level changes and disconnects and such)
739 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
740 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
742 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
744 if (protocol == PROTOCOL_QUAKEWORLD)
747 qboolean sendreliable;
749 // note that it is ok to send empty messages to the qw server,
750 // otherwise it won't respond to us at all
752 sendreliable = false;
753 // if the remote side dropped the last reliable message, resend it
754 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
756 // if the reliable transmit buffer is empty, copy the current message out
757 if (!conn->sendMessageLength && conn->message.cursize)
759 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
760 conn->sendMessageLength = conn->message.cursize;
761 SZ_Clear(&conn->message); // clear the message buffer
762 conn->qw.reliable_sequence ^= 1;
765 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
766 StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
767 // last received unreliable packet number, and last received reliable packet number (0 or 1)
768 StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
770 conn->outgoing_unreliable_sequence++;
771 // client sends qport in every packet
772 if (conn == cls.netcon)
774 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
776 // also update cls.qw_outgoing_sequence
777 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
779 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
781 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
785 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
787 // add the reliable message if there is one
790 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
791 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
792 packetLen += conn->sendMessageLength;
793 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
796 // add the unreliable message if possible
797 if (packetLen + data->cursize <= 1400)
799 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
800 memcpy(sendbuffer + packetLen, data->data, data->cursize);
801 packetLen += data->cursize;
804 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
807 conn->unreliableMessagesSent++;
809 totallen += packetLen + 28;
813 unsigned int packetLen;
814 unsigned int dataLen;
819 // if a reliable message fragment has been lost, send it again
820 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
822 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
824 dataLen = conn->sendMessageLength;
829 dataLen = MAX_PACKETFRAGMENT;
833 packetLen = NET_HEADERSIZE + dataLen;
835 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
836 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
837 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
839 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
841 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
842 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
844 conn->lastSendTime = realtime;
845 conn->packetsReSent++;
848 totallen += sendmelen + 28;
851 // if we have a new reliable message to send, do so
852 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
854 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
856 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
857 conn->message.overflowed = true;
861 if (developer_networking.integer && conn == cls.netcon)
863 Con_Print("client sending reliable message to server:\n");
864 SZ_HexDumpToConsole(&conn->message);
867 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
868 conn->sendMessageLength = conn->message.cursize;
869 SZ_Clear(&conn->message);
871 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
873 dataLen = conn->sendMessageLength;
878 dataLen = MAX_PACKETFRAGMENT;
882 packetLen = NET_HEADERSIZE + dataLen;
884 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
885 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
886 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
888 conn->nq.sendSequence++;
890 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
892 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
894 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
896 conn->lastSendTime = realtime;
898 conn->reliableMessagesSent++;
900 totallen += sendmelen + 28;
903 // if we have an unreliable message to send, do so
906 packetLen = NET_HEADERSIZE + data->cursize;
908 if (packetLen > (int)sizeof(sendbuffer))
910 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
914 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
915 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
916 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
918 conn->outgoing_unreliable_sequence++;
920 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
922 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
924 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
927 conn->unreliableMessagesSent++;
929 totallen += sendmelen + 28;
933 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
938 qboolean NetConn_HaveClientPorts(void)
940 return !!cl_numsockets;
943 qboolean NetConn_HaveServerPorts(void)
945 return !!sv_numsockets;
948 void NetConn_CloseClientPorts(void)
950 for (;cl_numsockets > 0;cl_numsockets--)
951 if (cl_sockets[cl_numsockets - 1])
952 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
955 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
957 lhnetaddress_t address;
960 char addressstring2[1024];
961 if (addressstring && addressstring[0])
962 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
964 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
967 if ((s = LHNET_OpenSocket_Connectionless(&address)))
969 cl_sockets[cl_numsockets++] = s;
970 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
971 if (addresstype != LHNETADDRESSTYPE_LOOP)
972 Con_Printf("Client opened a socket on address %s\n", addressstring2);
976 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
977 Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
981 Con_Printf("Client unable to parse address %s\n", addressstring);
984 void NetConn_OpenClientPorts(void)
987 NetConn_CloseClientPorts();
989 SV_LockThreadMutex(); // FIXME recursive?
990 Crypto_LoadKeys(); // client sockets
991 SV_UnlockThreadMutex();
993 port = bound(0, cl_netport.integer, 65535);
994 if (cl_netport.integer != port)
995 Cvar_SetValueQuick(&cl_netport, port);
997 Con_Printf("Client using an automatically assigned port\n");
999 Con_Printf("Client using port %i\n", port);
1000 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
1001 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
1003 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1007 void NetConn_CloseServerPorts(void)
1009 for (;sv_numsockets > 0;sv_numsockets--)
1010 if (sv_sockets[sv_numsockets - 1])
1011 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1014 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1016 lhnetaddress_t address;
1019 char addressstring2[1024];
1022 for (port = defaultport; port <= defaultport + range; port++)
1024 if (addressstring && addressstring[0])
1025 success = LHNETADDRESS_FromString(&address, addressstring, port);
1027 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1030 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1032 sv_sockets[sv_numsockets++] = s;
1033 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1034 if (addresstype != LHNETADDRESSTYPE_LOOP)
1035 Con_Printf("Server listening on address %s\n", addressstring2);
1040 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1041 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
1046 Con_Printf("Server unable to parse address %s\n", addressstring);
1047 // if it cant parse one address, it wont be able to parse another for sure
1054 void NetConn_OpenServerPorts(int opennetports)
1057 NetConn_CloseServerPorts();
1059 SV_LockThreadMutex(); // FIXME recursive?
1060 Crypto_LoadKeys(); // server sockets
1061 SV_UnlockThreadMutex();
1063 NetConn_UpdateSockets();
1064 port = bound(0, sv_netport.integer, 65535);
1067 Con_Printf("Server using port %i\n", port);
1068 if (sv_netport.integer != port)
1069 Cvar_SetValueQuick(&sv_netport, port);
1070 if (cls.state != ca_dedicated)
1071 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1075 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1076 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1078 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1081 if (sv_numsockets == 0)
1082 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1085 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1087 int i, a = LHNETADDRESS_GetAddressType(address);
1088 for (i = 0;i < cl_numsockets;i++)
1089 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1090 return cl_sockets[i];
1094 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1096 int i, a = LHNETADDRESS_GetAddressType(address);
1097 for (i = 0;i < sv_numsockets;i++)
1098 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1099 return sv_sockets[i];
1103 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1106 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1107 conn->mysocket = mysocket;
1108 conn->peeraddress = *peeraddress;
1109 conn->lastMessageTime = realtime;
1110 conn->message.data = conn->messagedata;
1111 conn->message.maxsize = sizeof(conn->messagedata);
1112 conn->message.cursize = 0;
1113 // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1114 // reduce effectiveness of connection request floods
1115 conn->timeout = realtime + net_connecttimeout.value;
1116 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1117 conn->next = netconn_list;
1118 netconn_list = conn;
1122 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1123 void NetConn_Close(netconn_t *conn)
1126 // remove connection from list
1128 // allow the client to reconnect immediately
1129 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1131 if (conn == netconn_list)
1132 netconn_list = conn->next;
1135 for (c = netconn_list;c;c = c->next)
1137 if (c->next == conn)
1139 c->next = conn->next;
1143 // not found in list, we'll avoid crashing here...
1151 static int clientport = -1;
1152 static int clientport2 = -1;
1153 static int hostport = -1;
1154 void NetConn_UpdateSockets(void)
1158 // TODO add logic to automatically close sockets if needed
1159 LHNET_DefaultDSCP(net_tos_dscp.integer);
1161 if (cls.state != ca_dedicated)
1163 if (clientport2 != cl_netport.integer)
1165 clientport2 = cl_netport.integer;
1166 if (cls.state == ca_connected)
1167 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1169 if (cls.state == ca_disconnected && clientport != clientport2)
1171 clientport = clientport2;
1172 NetConn_CloseClientPorts();
1174 if (cl_numsockets == 0)
1175 NetConn_OpenClientPorts();
1178 if (hostport != sv_netport.integer)
1180 hostport = sv_netport.integer;
1182 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1185 for (j = 0;j < MAX_RCONS;j++)
1187 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1188 if(cls.rcon_commands[i][0])
1190 if(realtime > cls.rcon_timeout[i])
1193 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1194 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1195 cls.rcon_commands[i][0] = 0;
1203 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1205 int originallength = length;
1206 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1207 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1208 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1212 if (protocol == PROTOCOL_QUAKEWORLD)
1214 int sequence, sequence_ack;
1215 int reliable_ack, reliable_message;
1219 sequence = LittleLong(*((int *)(data + 0)));
1220 sequence_ack = LittleLong(*((int *)(data + 4)));
1224 if (conn != cls.netcon)
1229 // TODO: use qport to identify that this client really is who they say they are? (and elsewhere in the code to identify the connection without a port match?)
1230 //qport = LittleShort(*((int *)(data + 8)));
1235 conn->packetsReceived++;
1236 reliable_message = (sequence >> 31) & 1;
1237 reliable_ack = (sequence_ack >> 31) & 1;
1238 sequence &= ~(1<<31);
1239 sequence_ack &= ~(1<<31);
1240 if (sequence <= conn->qw.incoming_sequence)
1242 //Con_DPrint("Got a stale datagram\n");
1245 count = sequence - (conn->qw.incoming_sequence + 1);
1248 conn->droppedDatagrams += count;
1249 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1252 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1253 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1254 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1255 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1256 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1257 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1260 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1261 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1262 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1263 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1264 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1265 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1266 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1268 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1269 if (net_test.integer)
1271 if (conn->cleartime < realtime)
1272 conn->cleartime = realtime;
1275 if (reliable_ack == conn->qw.reliable_sequence)
1277 // received, now we will be able to send another reliable message
1278 conn->sendMessageLength = 0;
1279 conn->reliableMessagesReceived++;
1281 conn->qw.incoming_sequence = sequence;
1282 if (conn == cls.netcon)
1283 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1284 conn->qw.incoming_acknowledged = sequence_ack;
1285 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1286 if (reliable_message)
1287 conn->qw.incoming_reliable_sequence ^= 1;
1288 conn->lastMessageTime = realtime;
1289 conn->timeout = realtime + newtimeout;
1290 conn->unreliableMessagesReceived++;
1291 if (conn == cls.netcon)
1293 SZ_Clear(&cl_message);
1294 SZ_Write(&cl_message, data, length);
1295 MSG_BeginReading(&cl_message);
1299 SZ_Clear(&sv_message);
1300 SZ_Write(&sv_message, data, length);
1301 MSG_BeginReading(&sv_message);
1309 unsigned int sequence;
1314 originallength = length;
1315 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1321 qlength = (unsigned int)BuffBigLong(data);
1322 flags = qlength & ~NETFLAG_LENGTH_MASK;
1323 qlength &= NETFLAG_LENGTH_MASK;
1324 // control packets were already handled
1325 if (!(flags & NETFLAG_CTL) && qlength == length)
1327 sequence = BuffBigLong(data + 4);
1328 conn->packetsReceived++;
1331 if (flags & NETFLAG_UNRELIABLE)
1333 if (sequence >= conn->nq.unreliableReceiveSequence)
1335 if (sequence > conn->nq.unreliableReceiveSequence)
1337 count = sequence - conn->nq.unreliableReceiveSequence;
1338 conn->droppedDatagrams += count;
1339 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1342 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1343 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1344 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1345 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1346 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1347 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1350 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1351 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1352 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1353 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1354 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1355 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1356 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1358 conn->nq.unreliableReceiveSequence = sequence + 1;
1359 conn->lastMessageTime = realtime;
1360 conn->timeout = realtime + newtimeout;
1361 conn->unreliableMessagesReceived++;
1364 if (conn == cls.netcon)
1366 SZ_Clear(&cl_message);
1367 SZ_Write(&cl_message, data, length);
1368 MSG_BeginReading(&cl_message);
1372 SZ_Clear(&sv_message);
1373 SZ_Write(&sv_message, data, length);
1374 MSG_BeginReading(&sv_message);
1380 // Con_DPrint("Got a stale datagram\n");
1383 else if (flags & NETFLAG_ACK)
1385 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1386 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1388 if (sequence == (conn->nq.sendSequence - 1))
1390 if (sequence == conn->nq.ackSequence)
1392 conn->nq.ackSequence++;
1393 if (conn->nq.ackSequence != conn->nq.sendSequence)
1394 Con_DPrint("ack sequencing error\n");
1395 conn->lastMessageTime = realtime;
1396 conn->timeout = realtime + newtimeout;
1397 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1399 unsigned int packetLen;
1400 unsigned int dataLen;
1403 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1404 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1406 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1408 dataLen = conn->sendMessageLength;
1413 dataLen = MAX_PACKETFRAGMENT;
1417 packetLen = NET_HEADERSIZE + dataLen;
1419 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1420 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1421 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1423 conn->nq.sendSequence++;
1425 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1426 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
1428 conn->lastSendTime = realtime;
1429 conn->packetsSent++;
1433 conn->sendMessageLength = 0;
1436 // Con_DPrint("Duplicate ACK received\n");
1439 // Con_DPrint("Stale ACK received\n");
1442 else if (flags & NETFLAG_DATA)
1444 unsigned char temppacket[8];
1445 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1446 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1448 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1450 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1451 StoreBigLong(temppacket + 4, sequence);
1452 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1454 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
1455 if (sequence == conn->nq.receiveSequence)
1457 conn->lastMessageTime = realtime;
1458 conn->timeout = realtime + newtimeout;
1459 conn->nq.receiveSequence++;
1460 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1461 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1462 conn->receiveMessageLength += length;
1464 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1465 "Dropping the message!\n", sequence );
1466 conn->receiveMessageLength = 0;
1469 if (flags & NETFLAG_EOM)
1471 conn->reliableMessagesReceived++;
1472 length = conn->receiveMessageLength;
1473 conn->receiveMessageLength = 0;
1476 if (conn == cls.netcon)
1478 SZ_Clear(&cl_message);
1479 SZ_Write(&cl_message, conn->receiveMessage, length);
1480 MSG_BeginReading(&cl_message);
1484 SZ_Clear(&sv_message);
1485 SZ_Write(&sv_message, conn->receiveMessage, length);
1486 MSG_BeginReading(&sv_message);
1493 conn->receivedDuplicateCount++;
1501 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1504 cls.connect_trying = false;
1506 M_Update_Return_Reason("");
1508 // the connection request succeeded, stop current connection and set up a new connection
1510 // if we're connecting to a remote server, shut down any local server
1511 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1513 SV_LockThreadMutex();
1514 Host_ShutdownServer ();
1515 SV_UnlockThreadMutex();
1517 // allocate a net connection to keep track of things
1518 cls.netcon = NetConn_Open(mysocket, peeraddress);
1519 crypto = &cls.netcon->crypto;
1520 if(cls.crypto.authenticated)
1522 Crypto_FinishInstance(crypto, &cls.crypto);
1523 Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
1524 crypto->use_aes ? "Encrypted" : "Authenticated",
1525 cls.netcon->address,
1526 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1527 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1528 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1529 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1532 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1533 key_dest = key_game;
1537 cls.demonum = -1; // not in the demo loop now
1538 cls.state = ca_connected;
1539 cls.signon = 0; // need all the signon messages before playing
1540 cls.protocol = initialprotocol;
1541 // reset move sequence numbering on this new connection
1542 cls.servermovesequence = 0;
1543 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1544 Cmd_ForwardStringToServer("new");
1545 if (cls.protocol == PROTOCOL_QUAKE)
1547 // write a keepalive (clc_nop) as it seems to greatly improve the
1548 // chances of connecting to a netquake server
1550 unsigned char buf[4];
1551 memset(&msg, 0, sizeof(msg));
1553 msg.maxsize = sizeof(buf);
1554 MSG_WriteChar(&msg, clc_nop);
1555 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1559 int NetConn_IsLocalGame(void)
1561 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1567 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1571 serverlist_entry_t *entry = NULL;
1573 // search the cache for this server and update it
1574 for (n = 0;n < serverlist_cachecount;n++) {
1575 entry = &serverlist_cache[ n ];
1576 if (!strcmp(addressstring, entry->info.cname))
1580 if (n == serverlist_cachecount)
1582 // LAN search doesnt require an answer from the master server so we wont
1583 // know the ping nor will it be initialized already...
1586 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1589 if (serverlist_maxcachecount <= serverlist_cachecount)
1591 serverlist_maxcachecount += 64;
1592 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1594 entry = &serverlist_cache[n];
1596 memset(entry, 0, sizeof(*entry));
1597 // store the data the engine cares about (address and ping)
1598 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1599 entry->info.ping = 100000;
1600 entry->querytime = realtime;
1601 // if not in the slist menu we should print the server to console
1602 if (serverlist_consoleoutput)
1603 Con_Printf("querying %s\n", addressstring);
1604 ++serverlist_cachecount;
1606 // if this is the first reply from this server, count it as having replied
1607 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1608 pingtime = bound(0, pingtime, 9999);
1609 if (entry->query == SQS_REFRESHING) {
1610 entry->info.ping = pingtime;
1611 entry->query = SQS_QUERIED;
1613 // convert to unsigned to catch the -1
1614 // 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]
1615 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1619 // other server info is updated by the caller
1623 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1625 serverlist_entry_t *entry = &serverlist_cache[n];
1626 serverlist_info_t *info = &entry->info;
1627 // update description strings for engine menu and console output
1628 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);
1629 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1631 info->gameversion != gameversion.integer
1634 gameversion_min.integer >= 0 // min/max range set by user/mod?
1635 && gameversion_max.integer >= 0
1636 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1637 && gameversion_max.integer >= info->gameversion
1640 info->mod, info->map);
1641 if (entry->query == SQS_QUERIED)
1643 if(!serverlist_paused)
1644 ServerList_ViewList_Remove(entry);
1646 // if not in the slist menu we should print the server to console (if wanted)
1647 else if( serverlist_consoleoutput )
1648 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1649 // and finally, update the view set
1650 if(!serverlist_paused)
1651 ServerList_ViewList_Insert( entry );
1652 // update the entry's state
1653 serverlist_cache[n].query = SQS_QUERIED;
1656 // returns true, if it's sensible to continue the processing
1657 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1659 serverlist_entry_t *entry;
1661 // ignore the rest of the message if the serverlist is full
1662 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1664 // also ignore it if we have already queried it (other master server response)
1665 for( n = 0 ; n < serverlist_cachecount ; n++ )
1666 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1669 if( n < serverlist_cachecount ) {
1670 // the entry has already been queried once or
1674 if (serverlist_maxcachecount <= n)
1676 serverlist_maxcachecount += 64;
1677 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1680 entry = &serverlist_cache[n];
1682 memset(entry, 0, sizeof(*entry));
1683 entry->protocol = protocol;
1684 // store the data the engine cares about (address and ping)
1685 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1687 entry->info.isfavorite = isfavorite;
1689 // no, then reset the ping right away
1690 entry->info.ping = -1;
1691 // we also want to increase the serverlist_cachecount then
1692 serverlist_cachecount++;
1695 entry->query = SQS_QUERYING;
1700 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1703 if (serverlist_consoleoutput)
1704 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1707 char ipstring [128];
1710 if (data[0] == '\\')
1712 unsigned short port = data[5] * 256 + data[6];
1714 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1715 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1717 // move on to next address in packet
1722 else if (data[0] == '/' && isextended && length >= 19)
1724 unsigned short port = data[17] * 256 + data[18];
1732 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1734 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1737 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1738 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1739 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1745 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1746 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1747 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1752 // move on to next address in packet
1758 Con_Print("Error while parsing the server list\n");
1762 if (serverlist_consoleoutput && developer_networking.integer)
1763 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1765 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1771 // begin or resume serverlist queries
1772 serverlist_querysleep = false;
1773 serverlist_querywaittime = realtime + 3;
1777 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1779 qboolean fromserver;
1781 char *string, addressstring2[128];
1782 char stringbuf[16384];
1783 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1786 char infostringvalue[MAX_INPUTLINE];
1792 // quakeworld ingame packet
1793 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1795 // convert the address to a string incase we need it
1796 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1798 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1800 // received a command string - strip off the packaging and put it
1801 // into our string buffer with NULL termination
1804 length = min(length, (int)sizeof(stringbuf) - 1);
1805 memcpy(stringbuf, data, length);
1806 stringbuf[length] = 0;
1809 if (developer_networking.integer)
1811 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1812 Com_HexDumpToConsole(data, length);
1815 sendlength = sizeof(senddata) - 4;
1816 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1818 case CRYPTO_NOMATCH:
1824 memcpy(senddata, "\377\377\377\377", 4);
1825 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1828 case CRYPTO_DISCARD:
1831 memcpy(senddata, "\377\377\377\377", 4);
1832 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1836 case CRYPTO_REPLACE:
1837 string = senddata+4;
1838 length = sendlength;
1842 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1845 for (j = 0;j < MAX_RCONS;j++)
1847 // note: this value from i is used outside the loop too...
1848 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1849 if(cls.rcon_commands[i][0])
1850 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1859 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1860 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1862 e = strchr(rcon_password.string, ' ');
1863 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1865 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
1869 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1870 NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
1871 cls.rcon_commands[i][0] = 0;
1874 for (k = 0;k < MAX_RCONS;k++)
1875 if(cls.rcon_commands[k][0])
1876 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1881 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1882 // extend the timeout on other requests as we asked for a challenge
1883 for (l = 0;l < MAX_RCONS;l++)
1884 if(cls.rcon_commands[l][0])
1885 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1886 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1889 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1893 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1895 // darkplaces or quake3
1896 char protocolnames[1400];
1897 Protocol_Names(protocolnames, sizeof(protocolnames));
1898 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1900 M_Update_Return_Reason("Got challenge response");
1902 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1903 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1904 // TODO: add userinfo stuff here instead of using NQ commands?
1905 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);
1908 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1910 // darkplaces or quake3
1912 M_Update_Return_Reason("Accepted");
1914 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1917 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1919 char rejectreason[128];
1920 cls.connect_trying = false;
1922 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1923 memcpy(rejectreason, string, length);
1924 rejectreason[length] = 0;
1926 M_Update_Return_Reason(rejectreason);
1931 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1933 serverlist_info_t *info;
1938 // search the cache for this server and update it
1939 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1943 info = &serverlist_cache[n].info;
1948 info->qcstatus[0] = 0;
1949 info->players[0] = 0;
1950 info->protocol = -1;
1951 info->numplayers = 0;
1953 info->maxplayers = 0;
1954 info->gameversion = 0;
1956 p = strchr(string, '\n');
1959 *p = 0; // cut off the string there
1963 Con_Printf("statusResponse without players block?\n");
1965 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1966 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1967 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1968 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1969 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1970 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1971 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1972 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1973 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1974 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1975 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1976 info->numhumans = info->numplayers - max(0, info->numbots);
1977 info->freeslots = info->maxplayers - info->numplayers;
1979 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1983 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1985 serverlist_info_t *info;
1989 // search the cache for this server and update it
1990 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1994 info = &serverlist_cache[n].info;
1999 info->qcstatus[0] = 0;
2000 info->players[0] = 0;
2001 info->protocol = -1;
2002 info->numplayers = 0;
2004 info->maxplayers = 0;
2005 info->gameversion = 0;
2007 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2008 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2009 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2010 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2011 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2012 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2013 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2014 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2015 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2016 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2017 info->numhumans = info->numplayers - max(0, info->numbots);
2018 info->freeslots = info->maxplayers - info->numplayers;
2020 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2024 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2026 // Extract the IP addresses
2029 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2032 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2034 // Extract the IP addresses
2037 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2040 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2042 // Extract the IP addresses
2046 if (serverlist_consoleoutput)
2047 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2048 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2050 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2051 if (serverlist_consoleoutput && developer_networking.integer)
2052 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2054 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2058 // move on to next address in packet
2062 // begin or resume serverlist queries
2063 serverlist_querysleep = false;
2064 serverlist_querywaittime = realtime + 3;
2068 if (!strncmp(string, "extResponse ", 12))
2070 ++cl_net_extresponse_count;
2071 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2072 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2073 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2074 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2077 if (!strncmp(string, "ping", 4))
2079 if (developer_extra.integer)
2080 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2081 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2084 if (!strncmp(string, "ack", 3))
2086 // QuakeWorld compatibility
2087 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2089 // challenge message
2090 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2092 M_Update_Return_Reason("Got QuakeWorld challenge response");
2094 cls.qw_qport = qport.integer;
2095 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2096 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2097 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);
2100 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2104 M_Update_Return_Reason("QuakeWorld Accepted");
2106 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2109 if (length > 2 && !memcmp(string, "n\\", 2))
2112 serverlist_info_t *info;
2116 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2117 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2120 // search the cache for this server and update it
2121 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2125 info = &serverlist_cache[n].info;
2126 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2127 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2128 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2129 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2131 info->numplayers = 0; // updated below
2132 info->numhumans = 0; // updated below
2133 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2134 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2136 // count active players on server
2137 // (we could gather more info, but we're just after the number)
2138 s = strchr(string, '\n');
2142 while (s < string + length)
2144 for (;s < string + length && *s != '\n';s++)
2146 if (s >= string + length)
2154 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2158 if (string[0] == 'n')
2161 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2163 // we may not have liked the packet, but it was a command packet, so
2164 // we're done processing this packet now
2167 // quakeworld ingame packet
2168 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2171 CL_ParseServerMessage();
2174 // netquake control packets, supported for compatibility only
2175 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2179 serverlist_info_t *info;
2184 SZ_Clear(&cl_message);
2185 SZ_Write(&cl_message, data, length);
2186 MSG_BeginReading(&cl_message);
2187 c = MSG_ReadByte(&cl_message);
2191 if (developer_extra.integer)
2192 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2193 if (cls.connect_trying)
2195 lhnetaddress_t clientportaddress;
2196 clientportaddress = *peeraddress;
2197 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2198 // extra ProQuake stuff
2200 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2202 cls.proquake_servermod = 0;
2204 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2206 cls.proquake_serverversion = 0;
2208 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2210 cls.proquake_serverflags = 0;
2211 if (cls.proquake_servermod == 1)
2212 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2213 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2214 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2216 M_Update_Return_Reason("Accepted");
2218 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2222 if (developer_extra.integer)
2223 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2224 cls.connect_trying = false;
2226 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2229 case CCREP_SERVER_INFO:
2230 if (developer_extra.integer)
2231 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2233 // LordHavoc: because the quake server may report weird addresses
2234 // we just ignore it and keep the real address
2235 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2236 // search the cache for this server and update it
2237 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2241 info = &serverlist_cache[n].info;
2242 strlcpy(info->game, "Quake", sizeof(info->game));
2243 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2244 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2245 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2246 info->numplayers = MSG_ReadByte(&cl_message);
2247 info->maxplayers = MSG_ReadByte(&cl_message);
2248 info->protocol = MSG_ReadByte(&cl_message);
2250 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2253 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2254 if (developer_extra.integer)
2255 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2257 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2259 case CCREP_PLAYER_INFO:
2260 // we got a CCREP_PLAYER_INFO??
2261 //if (developer_extra.integer)
2262 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2264 case CCREP_RULE_INFO:
2265 // we got a CCREP_RULE_INFO??
2266 //if (developer_extra.integer)
2267 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2272 SZ_Clear(&cl_message);
2273 // we may not have liked the packet, but it was a valid control
2274 // packet, so we're done processing this packet now
2278 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2279 CL_ParseServerMessage();
2284 void NetConn_QueryQueueFrame(void)
2290 static double querycounter = 0;
2292 if(!net_slist_pause.integer && serverlist_paused)
2293 ServerList_RebuildViewList();
2294 serverlist_paused = net_slist_pause.integer != 0;
2296 if (serverlist_querysleep)
2299 // apply a cool down time after master server replies,
2300 // to avoid messing up the ping times on the servers
2301 if (serverlist_querywaittime > realtime)
2304 // each time querycounter reaches 1.0 issue a query
2305 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2306 maxqueries = (int)querycounter;
2307 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2308 querycounter -= maxqueries;
2310 if( maxqueries == 0 ) {
2314 // scan serverlist and issue queries as needed
2315 serverlist_querysleep = true;
2317 timeouttime = realtime - net_slist_timeout.value;
2318 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2320 serverlist_entry_t *entry = &serverlist_cache[ index ];
2321 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2326 serverlist_querysleep = false;
2327 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2332 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2334 lhnetaddress_t address;
2337 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2338 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2340 for (socket = 0; socket < cl_numsockets ; socket++)
2341 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2345 for (socket = 0; socket < cl_numsockets ; socket++)
2346 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2349 // update the entry fields
2350 entry->querytime = realtime;
2351 entry->querycounter++;
2353 // if not in the slist menu we should print the server to console
2354 if (serverlist_consoleoutput)
2355 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2361 // have we tried to refresh this server?
2362 if( entry->query == SQS_REFRESHING ) {
2363 // yes, so update the reply count (since its not responding anymore)
2365 if(!serverlist_paused)
2366 ServerList_ViewList_Remove(entry);
2368 entry->query = SQS_TIMEDOUT;
2374 void NetConn_ClientFrame(void)
2377 lhnetaddress_t peeraddress;
2378 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2379 NetConn_UpdateSockets();
2380 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2383 if (cls.connect_remainingtries == 0)
2384 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2386 cls.connect_nextsendtime = realtime + 1;
2387 cls.connect_remainingtries--;
2388 if (cls.connect_remainingtries <= -10)
2390 cls.connect_trying = false;
2392 M_Update_Return_Reason("Connect: Failed");
2396 // try challenge first (newer DP server or QW)
2397 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2398 // then try netquake as a fallback (old server, or netquake)
2399 SZ_Clear(&cl_message);
2400 // save space for the header, filled in later
2401 MSG_WriteLong(&cl_message, 0);
2402 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2403 MSG_WriteString(&cl_message, "QUAKE");
2404 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2405 // extended proquake stuff
2406 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2407 // this version matches ProQuake 3.40, the first version to support
2408 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2409 // higher clients, so we pretend we are that version...
2410 MSG_WriteByte(&cl_message, 34); // version * 10
2411 MSG_WriteByte(&cl_message, 0); // flags
2412 MSG_WriteLong(&cl_message, 0); // password
2413 // write the packetsize now...
2414 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2415 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2416 SZ_Clear(&cl_message);
2418 for (i = 0;i < cl_numsockets;i++)
2420 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2422 // R_TimeReport("clientreadnetwork");
2423 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2424 // R_TimeReport("clientparsepacket");
2428 NetConn_QueryQueueFrame();
2430 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2432 Con_Print("Connection timed out\n");
2434 SV_LockThreadMutex();
2435 Host_ShutdownServer ();
2436 SV_UnlockThreadMutex();
2440 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2444 for (i = 0;i < bufferlength - 1;i++)
2448 c = rand () % (127 - 33) + 33;
2449 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2455 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2456 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2458 prvm_prog_t *prog = SVVM_prog;
2460 unsigned int nb_clients = 0, nb_bots = 0, i;
2463 const char *crypto_idstring;
2466 // How many clients are there?
2467 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2469 if (svs.clients[i].active)
2472 if (!svs.clients[i].netconnection)
2478 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2484 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2485 if(*q != '\\' && *q != '\n')
2490 /// \TODO: we should add more information for the full status string
2491 crypto_idstring = Crypto_GetInfoResponseDataString();
2492 length = dpsnprintf(out_msg, out_size,
2493 "\377\377\377\377%s\x0A"
2494 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2495 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2500 fullstatus ? "statusResponse" : "infoResponse",
2501 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2502 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2503 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2504 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2505 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2506 fullstatus ? "\n" : "");
2508 // Make sure it fits in the buffer
2518 savelength = length;
2520 ptr = out_msg + length;
2521 left = (int)out_size - length;
2523 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2525 client_t *cl = &svs.clients[i];
2528 int nameind, cleanind, pingvalue;
2530 char cleanname [sizeof(cl->name)];
2534 // Remove all characters '"' and '\' in the player name
2539 curchar = cl->name[nameind++];
2540 if (curchar != '"' && curchar != '\\')
2542 cleanname[cleanind++] = curchar;
2543 if (cleanind == sizeof(cleanname) - 1)
2546 } while (curchar != '\0');
2547 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2549 pingvalue = (int)(cl->ping * 1000.0f);
2550 if(cl->netconnection)
2551 pingvalue = bound(1, pingvalue, 9999);
2556 ed = PRVM_EDICT_NUM(i + 1);
2557 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2563 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2564 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2569 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2571 if(cl->frags == -666) // spectator
2572 strlcpy(teambuf, " 0", sizeof(teambuf));
2573 else if(cl->colors == 0x44) // red team
2574 strlcpy(teambuf, " 1", sizeof(teambuf));
2575 else if(cl->colors == 0xDD) // blue team
2576 strlcpy(teambuf, " 2", sizeof(teambuf));
2577 else if(cl->colors == 0xCC) // yellow team
2578 strlcpy(teambuf, " 3", sizeof(teambuf));
2579 else if(cl->colors == 0x99) // pink team
2580 strlcpy(teambuf, " 4", sizeof(teambuf));
2582 strlcpy(teambuf, " 0", sizeof(teambuf));
2587 // note: team number is inserted according to SoF2 protocol
2589 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2595 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2604 // turn it into an infoResponse!
2605 out_msg[savelength] = 0;
2606 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2607 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2622 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2624 size_t floodslotnum, bestfloodslotnum;
2625 double bestfloodtime;
2626 lhnetaddress_t noportpeeraddress;
2627 // see if this is a connect flood
2628 noportpeeraddress = *peeraddress;
2629 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2630 bestfloodslotnum = 0;
2631 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2632 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2634 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2636 bestfloodtime = floodlist[floodslotnum].lasttime;
2637 bestfloodslotnum = floodslotnum;
2639 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2641 // this address matches an ongoing flood address
2642 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2646 // renew the ban on this address so it does not expire
2647 // until the flood has subsided
2648 floodlist[floodslotnum].lasttime = realtime;
2650 //Con_Printf("Flood detected!\n");
2653 // the flood appears to have subsided, so allow this
2654 bestfloodslotnum = floodslotnum; // reuse the same slot
2658 // begin a new timeout on this address
2659 floodlist[bestfloodslotnum].address = noportpeeraddress;
2660 floodlist[bestfloodslotnum].lasttime = realtime;
2661 //Con_Printf("Flood detection initiated!\n");
2665 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2667 size_t floodslotnum;
2668 lhnetaddress_t noportpeeraddress;
2669 // see if this is a connect flood
2670 noportpeeraddress = *peeraddress;
2671 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2672 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2674 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2676 // this address matches an ongoing flood address
2678 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2679 floodlist[floodslotnum].lasttime = 0;
2680 //Con_Printf("Flood cleared!\n");
2685 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2687 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2692 t1 = (long) time(NULL);
2693 t2 = strtol(s, NULL, 0);
2694 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2697 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2700 return !memcmp(mdfourbuf, hash, 16);
2703 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2708 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2711 // validate the challenge
2712 for (i = 0;i < MAX_CHALLENGES;i++)
2713 if(challenge[i].time > 0)
2714 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2716 // if the challenge is not recognized, drop the packet
2717 if (i == MAX_CHALLENGES)
2720 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2723 if(memcmp(mdfourbuf, hash, 16))
2726 // unmark challenge to prevent replay attacks
2727 challenge[i].time = 0;
2732 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2734 return !strcmp(password, hash);
2737 /// returns a string describing the user level, or NULL for auth failure
2738 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)
2740 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2741 static char buf[MAX_INPUTLINE];
2743 qboolean restricted = false;
2744 qboolean have_usernames = false;
2747 userpass_start = rcon_password.string;
2748 while((userpass_end = strchr(userpass_start, ' ')))
2750 have_usernames = true;
2751 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2753 if(comparator(peeraddress, buf, password, cs, cslen))
2755 userpass_start = userpass_end + 1;
2757 if(userpass_start[0])
2759 userpass_end = userpass_start + strlen(userpass_start);
2760 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2765 have_usernames = false;
2766 userpass_start = rcon_restricted_password.string;
2767 while((userpass_end = strchr(userpass_start, ' ')))
2769 have_usernames = true;
2770 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2772 if(comparator(peeraddress, buf, password, cs, cslen))
2774 userpass_start = userpass_end + 1;
2776 if(userpass_start[0])
2778 userpass_end = userpass_start + strlen(userpass_start);
2779 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2783 return NULL; // DENIED
2786 for(text = s; text != endpos; ++text)
2787 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2788 return NULL; // block possible exploits against the parser/alias expansion
2792 size_t l = strlen(s);
2795 hasquotes = (strchr(s, '"') != NULL);
2796 // sorry, we can't allow these substrings in wildcard expressions,
2797 // as they can mess with the argument counts
2798 text = rcon_restricted_commands.string;
2799 while(COM_ParseToken_Console(&text))
2801 // com_token now contains a pattern to check for...
2802 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2805 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2808 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2810 if(!strcmp(com_token, s))
2813 else // single-arg expression? must match the beginning of the command
2815 if(!strcmp(com_token, s))
2817 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2821 // if we got here, nothing matched!
2829 userpass_startpass = strchr(userpass_start, ':');
2830 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2831 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2833 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2836 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2840 // looks like a legitimate rcon command with the correct password
2841 const char *s_ptr = s;
2842 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2843 while(s_ptr != endpos)
2845 size_t l = strlen(s_ptr);
2847 Con_Printf(" %s;", s_ptr);
2852 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2853 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2856 size_t l = strlen(s);
2859 client_t *host_client_save = host_client;
2860 Cmd_ExecuteString(s, src_command, true);
2861 host_client = host_client_save;
2862 // in case it is a command that changes host_client (like restart)
2866 Con_Rcon_Redirect_End();
2870 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2874 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2876 int i, ret, clientnum, best;
2879 char *s, *string, response[1400], addressstring2[128];
2880 static char stringbuf[16384]; // server only
2881 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2882 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2883 size_t sendlength, response_len;
2884 char infostringvalue[MAX_INPUTLINE];
2890 // convert the address to a string incase we need it
2891 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2893 // see if we can identify the sender as a local player
2894 // (this is necessary for rcon to send a reliable reply if the client is
2895 // actually on the server, not sending remotely)
2896 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2897 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2899 if (i == svs.maxclients)
2902 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2904 // received a command string - strip off the packaging and put it
2905 // into our string buffer with NULL termination
2908 length = min(length, (int)sizeof(stringbuf) - 1);
2909 memcpy(stringbuf, data, length);
2910 stringbuf[length] = 0;
2913 if (developer_extra.integer)
2915 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2916 Com_HexDumpToConsole(data, length);
2919 sendlength = sizeof(senddata) - 4;
2920 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2922 case CRYPTO_NOMATCH:
2928 memcpy(senddata, "\377\377\377\377", 4);
2929 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2932 case CRYPTO_DISCARD:
2935 memcpy(senddata, "\377\377\377\377", 4);
2936 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2940 case CRYPTO_REPLACE:
2941 string = senddata+4;
2942 length = sendlength;
2946 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2948 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2950 if(challenge[i].time > 0)
2951 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2953 if (besttime > challenge[i].time)
2954 besttime = challenge[best = i].time;
2956 // if we did not find an exact match, choose the oldest and
2957 // update address and string
2958 if (i == MAX_CHALLENGES)
2961 challenge[i].address = *peeraddress;
2962 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2966 // flood control: drop if requesting challenge too often
2967 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2970 challenge[i].time = realtime;
2971 // send the challenge
2972 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2973 response_len = strlen(response) + 1;
2974 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2975 NetConn_Write(mysocket, response, response_len, peeraddress);
2978 if (length > 8 && !memcmp(string, "connect\\", 8))
2980 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2984 if(crypto && crypto->authenticated)
2986 // no need to check challenge
2987 if(crypto_developer.integer)
2989 Con_Printf("%s connection to %s is being established: client is %s@%.*s, I am %.*s@%.*s\n",
2990 crypto->use_aes ? "Encrypted" : "Authenticated",
2992 crypto->client_idfp[0] ? crypto->client_idfp : "-",
2993 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
2994 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
2995 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3001 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3003 // validate the challenge
3004 for (i = 0;i < MAX_CHALLENGES;i++)
3005 if(challenge[i].time > 0)
3006 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
3008 // if the challenge is not recognized, drop the packet
3009 if (i == MAX_CHALLENGES)
3014 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3015 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3017 if(!(islocal || sv_public.integer > -2))
3019 if (developer_extra.integer)
3020 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3021 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
3025 // check engine protocol
3026 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3028 if (developer_extra.integer)
3029 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3030 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3034 // see if this is a duplicate connection request or a disconnected
3035 // client who is rejoining to the same client slot
3036 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3038 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3040 // this is a known client...
3041 if(crypto && crypto->authenticated)
3043 // reject if changing key!
3044 if(client->netconnection->crypto.authenticated)
3047 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3049 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3051 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3053 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3056 if (developer_extra.integer)
3057 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3058 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3065 // reject if downgrading!
3066 if(client->netconnection->crypto.authenticated)
3068 if (developer_extra.integer)
3069 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3070 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3076 // client crashed and is coming back,
3077 // keep their stuff intact
3078 if (developer_extra.integer)
3079 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3080 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3081 if(crypto && crypto->authenticated)
3082 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3083 SV_SendServerinfo(client);
3087 // client is still trying to connect,
3088 // so we send a duplicate reply
3089 if (developer_extra.integer)
3090 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3091 if(crypto && crypto->authenticated)
3092 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3093 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3099 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3102 // find an empty client slot for this new client
3103 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3106 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3108 // allocated connection
3109 if (developer_extra.integer)
3110 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3111 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3112 // now set up the client
3113 if(crypto && crypto->authenticated)
3114 Crypto_FinishInstance(&conn->crypto, crypto);
3115 SV_ConnectClient(clientnum, conn);
3116 NetConn_Heartbeat(1);
3121 // no empty slots found - server is full
3122 if (developer_extra.integer)
3123 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3124 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3128 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3130 const char *challenge = NULL;
3132 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3135 // If there was a challenge in the getinfo message
3136 if (length > 8 && string[7] == ' ')
3137 challenge = string + 8;
3139 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3141 if (developer_extra.integer)
3142 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3143 NetConn_WriteString(mysocket, response, peeraddress);
3147 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3149 const char *challenge = NULL;
3151 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3154 // If there was a challenge in the getinfo message
3155 if (length > 10 && string[9] == ' ')
3156 challenge = string + 10;
3158 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3160 if (developer_extra.integer)
3161 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3162 NetConn_WriteString(mysocket, response, peeraddress);
3166 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3168 char *password = string + 20;
3169 char *timeval = string + 37;
3170 char *s = strchr(timeval, ' ');
3171 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3172 const char *userlevel;
3174 if(rcon_secure.integer > 1)
3178 return true; // invalid packet
3181 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3182 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3185 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3187 char *password = string + 25;
3188 char *challenge = string + 42;
3189 char *s = strchr(challenge, ' ');
3190 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3191 const char *userlevel;
3193 return true; // invalid packet
3196 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3197 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3200 if (length >= 5 && !memcmp(string, "rcon ", 5))
3203 char *s = string + 5;
3204 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3207 if(rcon_secure.integer > 0)
3210 for (i = 0;!ISWHITESPACE(*s);s++)
3211 if (i < (int)sizeof(password) - 1)
3213 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3216 if (!ISWHITESPACE(password[0]))
3218 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3219 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3223 if (!strncmp(string, "extResponse ", 12))
3225 ++sv_net_extresponse_count;
3226 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3227 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3228 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3229 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3232 if (!strncmp(string, "ping", 4))
3234 if (developer_extra.integer)
3235 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3236 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3239 if (!strncmp(string, "ack", 3))
3241 // we may not have liked the packet, but it was a command packet, so
3242 // we're done processing this packet now
3245 // netquake control packets, supported for compatibility only, and only
3246 // when running game protocols that are normally served via this connection
3248 // (this protects more modern protocols against being used for
3249 // Quake packet flood Denial Of Service attacks)
3250 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)
3254 const char *protocolname;
3257 SZ_Clear(&sv_message);
3258 SZ_Write(&sv_message, data, length);
3259 MSG_BeginReading(&sv_message);
3260 c = MSG_ReadByte(&sv_message);
3264 if (developer_extra.integer)
3265 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3266 if(!(islocal || sv_public.integer > -2))
3268 if (developer_extra.integer)
3269 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3270 SZ_Clear(&sv_message);
3271 // save space for the header, filled in later
3272 MSG_WriteLong(&sv_message, 0);
3273 MSG_WriteByte(&sv_message, CCREP_REJECT);
3274 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3275 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3276 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3277 SZ_Clear(&sv_message);
3281 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3282 protocolnumber = MSG_ReadByte(&sv_message);
3283 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3285 if (developer_extra.integer)
3286 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3287 SZ_Clear(&sv_message);
3288 // save space for the header, filled in later
3289 MSG_WriteLong(&sv_message, 0);
3290 MSG_WriteByte(&sv_message, CCREP_REJECT);
3291 MSG_WriteString(&sv_message, "Incompatible version.\n");
3292 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3293 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3294 SZ_Clear(&sv_message);
3298 // see if this connect request comes from a known client
3299 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3301 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3303 // this is either a duplicate connection request
3304 // or coming back from a timeout
3305 // (if so, keep their stuff intact)
3307 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3308 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3310 if (developer_extra.integer)
3311 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3312 SZ_Clear(&sv_message);
3313 // save space for the header, filled in later
3314 MSG_WriteLong(&sv_message, 0);
3315 MSG_WriteByte(&sv_message, CCREP_REJECT);
3316 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3317 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3318 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3319 SZ_Clear(&sv_message);
3324 if (developer_extra.integer)
3325 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3326 SZ_Clear(&sv_message);
3327 // save space for the header, filled in later
3328 MSG_WriteLong(&sv_message, 0);
3329 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3330 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3331 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3332 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3333 SZ_Clear(&sv_message);
3335 // if client is already spawned, re-send the
3336 // serverinfo message as they'll need it to play
3338 SV_SendServerinfo(client);
3343 // this is a new client, check for connection flood
3344 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3347 // find a slot for the new client
3348 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3351 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3353 // connect to the client
3354 // everything is allocated, just fill in the details
3355 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3356 if (developer_extra.integer)
3357 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3358 // send back the info about the server connection
3359 SZ_Clear(&sv_message);
3360 // save space for the header, filled in later
3361 MSG_WriteLong(&sv_message, 0);
3362 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3363 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3364 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3365 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3366 SZ_Clear(&sv_message);
3367 // now set up the client struct
3368 SV_ConnectClient(clientnum, conn);
3369 NetConn_Heartbeat(1);
3374 if (developer_extra.integer)
3375 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3376 // no room; try to let player know
3377 SZ_Clear(&sv_message);
3378 // save space for the header, filled in later
3379 MSG_WriteLong(&sv_message, 0);
3380 MSG_WriteByte(&sv_message, CCREP_REJECT);
3381 MSG_WriteString(&sv_message, "Server is full.\n");
3382 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3383 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3384 SZ_Clear(&sv_message);
3386 case CCREQ_SERVER_INFO:
3387 if (developer_extra.integer)
3388 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3389 if(!(islocal || sv_public.integer > -1))
3392 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3395 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3398 char myaddressstring[128];
3399 if (developer_extra.integer)
3400 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3401 SZ_Clear(&sv_message);
3402 // save space for the header, filled in later
3403 MSG_WriteLong(&sv_message, 0);
3404 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3405 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3406 MSG_WriteString(&sv_message, myaddressstring);
3407 MSG_WriteString(&sv_message, hostname.string);
3408 MSG_WriteString(&sv_message, sv.name);
3409 // How many clients are there?
3410 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3411 if (svs.clients[i].active)
3413 MSG_WriteByte(&sv_message, numclients);
3414 MSG_WriteByte(&sv_message, svs.maxclients);
3415 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3416 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3417 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3418 SZ_Clear(&sv_message);
3421 case CCREQ_PLAYER_INFO:
3422 if (developer_extra.integer)
3423 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3424 if(!(islocal || sv_public.integer > -1))
3427 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3432 int playerNumber, activeNumber, clientNumber;
3435 playerNumber = MSG_ReadByte(&sv_message);
3437 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3438 if (client->active && ++activeNumber == playerNumber)
3440 if (clientNumber != svs.maxclients)
3442 SZ_Clear(&sv_message);
3443 // save space for the header, filled in later
3444 MSG_WriteLong(&sv_message, 0);
3445 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3446 MSG_WriteByte(&sv_message, playerNumber);
3447 MSG_WriteString(&sv_message, client->name);
3448 MSG_WriteLong(&sv_message, client->colors);
3449 MSG_WriteLong(&sv_message, client->frags);
3450 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3451 if(sv_status_privacy.integer)
3452 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3454 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3455 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3456 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3457 SZ_Clear(&sv_message);
3461 case CCREQ_RULE_INFO:
3462 if (developer_extra.integer)
3463 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3464 if(!(islocal || sv_public.integer > -1))
3467 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3474 // find the search start location
3475 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3476 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3478 // send the response
3479 SZ_Clear(&sv_message);
3480 // save space for the header, filled in later
3481 MSG_WriteLong(&sv_message, 0);
3482 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3485 MSG_WriteString(&sv_message, var->name);
3486 MSG_WriteString(&sv_message, var->string);
3488 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3489 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3490 SZ_Clear(&sv_message);
3494 if (developer_extra.integer)
3495 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3496 if (sv.active && !rcon_secure.integer)
3498 char password[2048];
3502 const char *userlevel;
3503 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3504 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3506 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3507 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3508 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3515 SZ_Clear(&sv_message);
3516 // we may not have liked the packet, but it was a valid control
3517 // packet, so we're done processing this packet now
3522 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3524 SV_ReadClientMessage();
3531 void NetConn_ServerFrame(void)
3534 lhnetaddress_t peeraddress;
3535 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3536 for (i = 0;i < sv_numsockets;i++)
3537 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3538 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3539 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3541 // never timeout loopback connections
3542 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3544 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3545 SV_DropClient(false);
3550 void NetConn_SleepMicroseconds(int microseconds)
3552 LHNET_SleepUntilPacket_Microseconds(microseconds);
3556 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3560 lhnetaddress_t masteraddress;
3561 lhnetaddress_t broadcastaddress;
3564 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3567 // 26000 is the default quake server port, servers on other ports will not
3569 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3570 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3574 for (i = 0;i < cl_numsockets;i++)
3578 const char *cmdname, *extraoptions;
3579 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3581 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3583 // search LAN for Quake servers
3584 SZ_Clear(&cl_message);
3585 // save space for the header, filled in later
3586 MSG_WriteLong(&cl_message, 0);
3587 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3588 MSG_WriteString(&cl_message, "QUAKE");
3589 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3590 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3591 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3592 SZ_Clear(&cl_message);
3594 // search LAN for DarkPlaces servers
3595 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3598 // build the getservers message to send to the dpmaster master servers
3599 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3601 cmdname = "getserversExt";
3602 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3606 cmdname = "getservers";
3609 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3612 for (masternum = 0;sv_masters[masternum].name;masternum++)
3614 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3617 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3621 // search favorite servers
3622 for(j = 0; j < nFavorites; ++j)
3624 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3626 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3627 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3634 // only query QuakeWorld servers when the user wants to
3637 for (i = 0;i < cl_numsockets;i++)
3641 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3643 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3645 // search LAN for QuakeWorld servers
3646 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3648 // build the getservers message to send to the qwmaster master servers
3649 // note this has no -1 prefix, and the trailing nul byte is sent
3650 dpsnprintf(request, sizeof(request), "c\n");
3654 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3656 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3658 if (m_state != m_slist)
3660 char lookupstring[128];
3661 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3662 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3665 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3669 // search favorite servers
3670 for(j = 0; j < nFavorites; ++j)
3672 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3674 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3676 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3677 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3684 if (!masterquerycount)
3686 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3687 M_Update_Return_Reason("No network");
3692 void NetConn_Heartbeat(int priority)
3694 lhnetaddress_t masteraddress;
3696 lhnetsocket_t *mysocket;
3698 // if it's a state change (client connected), limit next heartbeat to no
3699 // more than 30 sec in the future
3700 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3701 nextheartbeattime = realtime + 30.0;
3703 // limit heartbeatperiod to 30 to 270 second range,
3704 // lower limit is to avoid abusing master servers with excess traffic,
3705 // upper limit is to avoid timing out on the master server (which uses
3707 if (sv_heartbeatperiod.value < 30)
3708 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3709 if (sv_heartbeatperiod.value > 270)
3710 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3712 // make advertising optional and don't advertise singleplayer games, and
3713 // only send a heartbeat as often as the admin wants
3714 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3716 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3717 for (masternum = 0;sv_masters[masternum].name;masternum++)
3718 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3719 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3723 static void Net_Heartbeat_f(void)
3726 NetConn_Heartbeat(2);
3728 Con_Print("No server running, can not heartbeat to master server.\n");
3731 static void PrintStats(netconn_t *conn)
3733 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3734 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3736 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3737 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3738 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3739 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3740 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3741 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3742 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3743 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3744 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3745 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3748 void Net_Stats_f(void)
3751 Con_Print("connections =\n");
3752 for (conn = netconn_list;conn;conn = conn->next)
3757 void Net_Refresh_f(void)
3759 if (m_state != m_slist) {
3760 Con_Print("Sending new requests to master servers\n");
3761 ServerList_QueryList(false, true, false, true);
3762 Con_Print("Listening for replies...\n");
3764 ServerList_QueryList(false, true, false, false);
3767 void Net_Slist_f(void)
3769 ServerList_ResetMasks();
3770 serverlist_sortbyfield = SLIF_PING;
3771 serverlist_sortflags = 0;
3772 if (m_state != m_slist) {
3773 Con_Print("Sending requests to master servers\n");
3774 ServerList_QueryList(true, true, false, true);
3775 Con_Print("Listening for replies...\n");
3777 ServerList_QueryList(true, true, false, false);
3780 void Net_SlistQW_f(void)
3782 ServerList_ResetMasks();
3783 serverlist_sortbyfield = SLIF_PING;
3784 serverlist_sortflags = 0;
3785 if (m_state != m_slist) {
3786 Con_Print("Sending requests to master servers\n");
3787 ServerList_QueryList(true, false, true, true);
3788 serverlist_consoleoutput = true;
3789 Con_Print("Listening for replies...\n");
3791 ServerList_QueryList(true, false, true, false);
3795 void NetConn_Init(void)
3798 lhnetaddress_t tempaddress;
3799 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3800 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3802 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3803 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3804 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3806 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3807 Cvar_RegisterVariable(&net_test);
3808 Cvar_RegisterVariable(&net_usesizelimit);
3809 Cvar_RegisterVariable(&net_burstreserve);
3810 Cvar_RegisterVariable(&rcon_restricted_password);
3811 Cvar_RegisterVariable(&rcon_restricted_commands);
3812 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3813 Cvar_RegisterVariable(&net_slist_queriespersecond);
3814 Cvar_RegisterVariable(&net_slist_queriesperframe);
3815 Cvar_RegisterVariable(&net_slist_timeout);
3816 Cvar_RegisterVariable(&net_slist_maxtries);
3817 Cvar_RegisterVariable(&net_slist_favorites);
3818 Cvar_RegisterVariable(&net_slist_pause);
3819 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3820 Cvar_RegisterVariable(&net_tos_dscp);
3821 Cvar_RegisterVariable(&net_messagetimeout);
3822 Cvar_RegisterVariable(&net_connecttimeout);
3823 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3824 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3825 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3826 Cvar_RegisterVariable(&cl_netlocalping);
3827 Cvar_RegisterVariable(&cl_netpacketloss_send);
3828 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3829 Cvar_RegisterVariable(&hostname);
3830 Cvar_RegisterVariable(&developer_networking);
3831 Cvar_RegisterVariable(&cl_netport);
3832 Cvar_RegisterVariable(&sv_netport);
3833 Cvar_RegisterVariable(&net_address);
3834 Cvar_RegisterVariable(&net_address_ipv6);
3835 Cvar_RegisterVariable(&sv_public);
3836 Cvar_RegisterVariable(&sv_public_rejectreason);
3837 Cvar_RegisterVariable(&sv_heartbeatperiod);
3838 for (i = 0;sv_masters[i].name;i++)
3839 Cvar_RegisterVariable(&sv_masters[i]);
3840 Cvar_RegisterVariable(&gameversion);
3841 Cvar_RegisterVariable(&gameversion_min);
3842 Cvar_RegisterVariable(&gameversion_max);
3843 // 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.
3844 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3846 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3848 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3849 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3852 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3854 // 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
3855 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3857 i = atoi(com_argv[i + 1]);
3858 if (i >= 0 && i < 65536)
3860 Con_Printf("-port option used, setting port cvar to %i\n", i);
3861 Cvar_SetValueQuick(&sv_netport, i);
3864 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3868 cl_message.data = cl_message_buf;
3869 cl_message.maxsize = sizeof(cl_message_buf);
3870 cl_message.cursize = 0;
3871 sv_message.data = sv_message_buf;
3872 sv_message.maxsize = sizeof(sv_message_buf);
3873 sv_message.cursize = 0;
3875 if (Thread_HasThreads())
3876 netconn_mutex = Thread_CreateMutex();
3879 void NetConn_Shutdown(void)
3881 NetConn_CloseClientPorts();
3882 NetConn_CloseServerPorts();
3885 Thread_DestroyMutex(netconn_mutex);
3886 netconn_mutex = NULL;