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 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
712 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
713 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
715 // if this packet was supposedly choked, but we find ourselves sending one
716 // anyway, make sure the size counting starts at zero
717 // (this mostly happens on level changes and disconnects and such)
718 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
719 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
721 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
723 if (protocol == PROTOCOL_QUAKEWORLD)
726 qboolean sendreliable;
728 // note that it is ok to send empty messages to the qw server,
729 // otherwise it won't respond to us at all
731 sendreliable = false;
732 // if the remote side dropped the last reliable message, resend it
733 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
735 // if the reliable transmit buffer is empty, copy the current message out
736 if (!conn->sendMessageLength && conn->message.cursize)
738 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
739 conn->sendMessageLength = conn->message.cursize;
740 SZ_Clear(&conn->message); // clear the message buffer
741 conn->qw.reliable_sequence ^= 1;
744 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
745 StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
746 // last received unreliable packet number, and last received reliable packet number (0 or 1)
747 StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
749 conn->outgoing_unreliable_sequence++;
750 // client sends qport in every packet
751 if (conn == cls.netcon)
753 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
755 // also update cls.qw_outgoing_sequence
756 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
758 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
760 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
764 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
766 // add the reliable message if there is one
769 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
770 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
771 packetLen += conn->sendMessageLength;
772 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
775 // add the unreliable message if possible
776 if (packetLen + data->cursize <= 1400)
778 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
779 memcpy(sendbuffer + packetLen, data->data, data->cursize);
780 packetLen += data->cursize;
783 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
786 conn->unreliableMessagesSent++;
788 totallen += packetLen + 28;
792 unsigned int packetLen;
793 unsigned int dataLen;
798 // if a reliable message fragment has been lost, send it again
799 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
801 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
803 dataLen = conn->sendMessageLength;
808 dataLen = MAX_PACKETFRAGMENT;
812 packetLen = NET_HEADERSIZE + dataLen;
814 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
815 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
816 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
818 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
820 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
821 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
823 conn->lastSendTime = realtime;
824 conn->packetsReSent++;
827 totallen += sendmelen + 28;
830 // if we have a new reliable message to send, do so
831 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
833 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
835 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
836 conn->message.overflowed = true;
840 if (developer_networking.integer && conn == cls.netcon)
842 Con_Print("client sending reliable message to server:\n");
843 SZ_HexDumpToConsole(&conn->message);
846 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
847 conn->sendMessageLength = conn->message.cursize;
848 SZ_Clear(&conn->message);
850 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
852 dataLen = conn->sendMessageLength;
857 dataLen = MAX_PACKETFRAGMENT;
861 packetLen = NET_HEADERSIZE + dataLen;
863 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
864 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
865 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
867 conn->nq.sendSequence++;
869 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
871 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
873 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
875 conn->lastSendTime = realtime;
877 conn->reliableMessagesSent++;
879 totallen += sendmelen + 28;
882 // if we have an unreliable message to send, do so
885 packetLen = NET_HEADERSIZE + data->cursize;
887 if (packetLen > (int)sizeof(sendbuffer))
889 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
893 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE);
894 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
895 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
897 conn->outgoing_unreliable_sequence++;
899 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
901 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
903 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
906 conn->unreliableMessagesSent++;
908 totallen += sendmelen + 28;
912 NetConn_UpdateCleartime(&conn->cleartime, cl_rate.integer, cl_rate_burstsize.integer, totallen);
917 qboolean NetConn_HaveClientPorts(void)
919 return !!cl_numsockets;
922 qboolean NetConn_HaveServerPorts(void)
924 return !!sv_numsockets;
927 void NetConn_CloseClientPorts(void)
929 for (;cl_numsockets > 0;cl_numsockets--)
930 if (cl_sockets[cl_numsockets - 1])
931 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
934 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
936 lhnetaddress_t address;
939 char addressstring2[1024];
940 if (addressstring && addressstring[0])
941 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
943 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
946 if ((s = LHNET_OpenSocket_Connectionless(&address)))
948 cl_sockets[cl_numsockets++] = s;
949 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
950 if (addresstype != LHNETADDRESSTYPE_LOOP)
951 Con_Printf("Client opened a socket on address %s\n", addressstring2);
955 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
956 Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
960 Con_Printf("Client unable to parse address %s\n", addressstring);
963 void NetConn_OpenClientPorts(void)
966 NetConn_CloseClientPorts();
968 SV_LockThreadMutex(); // FIXME recursive?
969 Crypto_LoadKeys(); // client sockets
970 SV_UnlockThreadMutex();
972 port = bound(0, cl_netport.integer, 65535);
973 if (cl_netport.integer != port)
974 Cvar_SetValueQuick(&cl_netport, port);
976 Con_Printf("Client using an automatically assigned port\n");
978 Con_Printf("Client using port %i\n", port);
979 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
980 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
982 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
986 void NetConn_CloseServerPorts(void)
988 for (;sv_numsockets > 0;sv_numsockets--)
989 if (sv_sockets[sv_numsockets - 1])
990 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
993 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
995 lhnetaddress_t address;
998 char addressstring2[1024];
1001 for (port = defaultport; port <= defaultport + range; port++)
1003 if (addressstring && addressstring[0])
1004 success = LHNETADDRESS_FromString(&address, addressstring, port);
1006 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1009 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1011 sv_sockets[sv_numsockets++] = s;
1012 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1013 if (addresstype != LHNETADDRESSTYPE_LOOP)
1014 Con_Printf("Server listening on address %s\n", addressstring2);
1019 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1020 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
1025 Con_Printf("Server unable to parse address %s\n", addressstring);
1026 // if it cant parse one address, it wont be able to parse another for sure
1033 void NetConn_OpenServerPorts(int opennetports)
1036 NetConn_CloseServerPorts();
1038 SV_LockThreadMutex(); // FIXME recursive?
1039 Crypto_LoadKeys(); // server sockets
1040 SV_UnlockThreadMutex();
1042 NetConn_UpdateSockets();
1043 port = bound(0, sv_netport.integer, 65535);
1046 Con_Printf("Server using port %i\n", port);
1047 if (sv_netport.integer != port)
1048 Cvar_SetValueQuick(&sv_netport, port);
1049 if (cls.state != ca_dedicated)
1050 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1054 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1055 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1057 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1060 if (sv_numsockets == 0)
1061 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1064 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1066 int i, a = LHNETADDRESS_GetAddressType(address);
1067 for (i = 0;i < cl_numsockets;i++)
1068 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1069 return cl_sockets[i];
1073 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1075 int i, a = LHNETADDRESS_GetAddressType(address);
1076 for (i = 0;i < sv_numsockets;i++)
1077 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1078 return sv_sockets[i];
1082 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1085 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1086 conn->mysocket = mysocket;
1087 conn->peeraddress = *peeraddress;
1088 conn->lastMessageTime = realtime;
1089 conn->message.data = conn->messagedata;
1090 conn->message.maxsize = sizeof(conn->messagedata);
1091 conn->message.cursize = 0;
1092 // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1093 // reduce effectiveness of connection request floods
1094 conn->timeout = realtime + net_connecttimeout.value;
1095 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1096 conn->next = netconn_list;
1097 netconn_list = conn;
1101 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1102 void NetConn_Close(netconn_t *conn)
1105 // remove connection from list
1107 // allow the client to reconnect immediately
1108 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1110 if (conn == netconn_list)
1111 netconn_list = conn->next;
1114 for (c = netconn_list;c;c = c->next)
1116 if (c->next == conn)
1118 c->next = conn->next;
1122 // not found in list, we'll avoid crashing here...
1130 static int clientport = -1;
1131 static int clientport2 = -1;
1132 static int hostport = -1;
1133 void NetConn_UpdateSockets(void)
1137 // TODO add logic to automatically close sockets if needed
1138 LHNET_DefaultDSCP(net_tos_dscp.integer);
1140 if (cls.state != ca_dedicated)
1142 if (clientport2 != cl_netport.integer)
1144 clientport2 = cl_netport.integer;
1145 if (cls.state == ca_connected)
1146 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1148 if (cls.state == ca_disconnected && clientport != clientport2)
1150 clientport = clientport2;
1151 NetConn_CloseClientPorts();
1153 if (cl_numsockets == 0)
1154 NetConn_OpenClientPorts();
1157 if (hostport != sv_netport.integer)
1159 hostport = sv_netport.integer;
1161 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1164 for (j = 0;j < MAX_RCONS;j++)
1166 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1167 if(cls.rcon_commands[i][0])
1169 if(realtime > cls.rcon_timeout[i])
1172 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1173 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1174 cls.rcon_commands[i][0] = 0;
1182 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1184 int originallength = length;
1185 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1186 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1187 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1191 if (protocol == PROTOCOL_QUAKEWORLD)
1193 int sequence, sequence_ack;
1194 int reliable_ack, reliable_message;
1198 sequence = LittleLong(*((int *)(data + 0)));
1199 sequence_ack = LittleLong(*((int *)(data + 4)));
1203 if (conn != cls.netcon)
1208 // 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?)
1209 //qport = LittleShort(*((int *)(data + 8)));
1214 conn->packetsReceived++;
1215 reliable_message = (sequence >> 31) & 1;
1216 reliable_ack = (sequence_ack >> 31) & 1;
1217 sequence &= ~(1<<31);
1218 sequence_ack &= ~(1<<31);
1219 if (sequence <= conn->qw.incoming_sequence)
1221 //Con_DPrint("Got a stale datagram\n");
1224 count = sequence - (conn->qw.incoming_sequence + 1);
1227 conn->droppedDatagrams += count;
1228 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1231 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1232 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1233 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1234 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1235 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1236 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1239 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1240 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1241 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1242 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1243 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1244 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1245 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1247 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1248 if (net_test.integer)
1250 if (conn->cleartime < realtime)
1251 conn->cleartime = realtime;
1254 if (reliable_ack == conn->qw.reliable_sequence)
1256 // received, now we will be able to send another reliable message
1257 conn->sendMessageLength = 0;
1258 conn->reliableMessagesReceived++;
1260 conn->qw.incoming_sequence = sequence;
1261 if (conn == cls.netcon)
1262 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1263 conn->qw.incoming_acknowledged = sequence_ack;
1264 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1265 if (reliable_message)
1266 conn->qw.incoming_reliable_sequence ^= 1;
1267 conn->lastMessageTime = realtime;
1268 conn->timeout = realtime + newtimeout;
1269 conn->unreliableMessagesReceived++;
1270 if (conn == cls.netcon)
1272 SZ_Clear(&cl_message);
1273 SZ_Write(&cl_message, data, length);
1274 MSG_BeginReading(&cl_message);
1278 SZ_Clear(&sv_message);
1279 SZ_Write(&sv_message, data, length);
1280 MSG_BeginReading(&sv_message);
1288 unsigned int sequence;
1293 originallength = length;
1294 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1300 qlength = (unsigned int)BuffBigLong(data);
1301 flags = qlength & ~NETFLAG_LENGTH_MASK;
1302 qlength &= NETFLAG_LENGTH_MASK;
1303 // control packets were already handled
1304 if (!(flags & NETFLAG_CTL) && qlength == length)
1306 sequence = BuffBigLong(data + 4);
1307 conn->packetsReceived++;
1310 if (flags & NETFLAG_UNRELIABLE)
1312 if (sequence >= conn->nq.unreliableReceiveSequence)
1314 if (sequence > conn->nq.unreliableReceiveSequence)
1316 count = sequence - conn->nq.unreliableReceiveSequence;
1317 conn->droppedDatagrams += count;
1318 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1321 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1322 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1323 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1324 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1325 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1326 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1329 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1330 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1331 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1332 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1333 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1334 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1335 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1337 conn->nq.unreliableReceiveSequence = sequence + 1;
1338 conn->lastMessageTime = realtime;
1339 conn->timeout = realtime + newtimeout;
1340 conn->unreliableMessagesReceived++;
1343 if (conn == cls.netcon)
1345 SZ_Clear(&cl_message);
1346 SZ_Write(&cl_message, data, length);
1347 MSG_BeginReading(&cl_message);
1351 SZ_Clear(&sv_message);
1352 SZ_Write(&sv_message, data, length);
1353 MSG_BeginReading(&sv_message);
1359 // Con_DPrint("Got a stale datagram\n");
1362 else if (flags & NETFLAG_ACK)
1364 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1365 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1367 if (sequence == (conn->nq.sendSequence - 1))
1369 if (sequence == conn->nq.ackSequence)
1371 conn->nq.ackSequence++;
1372 if (conn->nq.ackSequence != conn->nq.sendSequence)
1373 Con_DPrint("ack sequencing error\n");
1374 conn->lastMessageTime = realtime;
1375 conn->timeout = realtime + newtimeout;
1376 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1378 unsigned int packetLen;
1379 unsigned int dataLen;
1382 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1383 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1385 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1387 dataLen = conn->sendMessageLength;
1392 dataLen = MAX_PACKETFRAGMENT;
1396 packetLen = NET_HEADERSIZE + dataLen;
1398 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
1399 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1400 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1402 conn->nq.sendSequence++;
1404 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1405 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
1407 conn->lastSendTime = realtime;
1408 conn->packetsSent++;
1412 conn->sendMessageLength = 0;
1415 // Con_DPrint("Duplicate ACK received\n");
1418 // Con_DPrint("Stale ACK received\n");
1421 else if (flags & NETFLAG_DATA)
1423 unsigned char temppacket[8];
1424 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1425 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1427 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1429 StoreBigLong(temppacket, 8 | NETFLAG_ACK);
1430 StoreBigLong(temppacket + 4, sequence);
1431 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1433 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
1434 if (sequence == conn->nq.receiveSequence)
1436 conn->lastMessageTime = realtime;
1437 conn->timeout = realtime + newtimeout;
1438 conn->nq.receiveSequence++;
1439 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1440 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1441 conn->receiveMessageLength += length;
1443 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1444 "Dropping the message!\n", sequence );
1445 conn->receiveMessageLength = 0;
1448 if (flags & NETFLAG_EOM)
1450 conn->reliableMessagesReceived++;
1451 length = conn->receiveMessageLength;
1452 conn->receiveMessageLength = 0;
1455 if (conn == cls.netcon)
1457 SZ_Clear(&cl_message);
1458 SZ_Write(&cl_message, conn->receiveMessage, length);
1459 MSG_BeginReading(&cl_message);
1463 SZ_Clear(&sv_message);
1464 SZ_Write(&sv_message, conn->receiveMessage, length);
1465 MSG_BeginReading(&sv_message);
1472 conn->receivedDuplicateCount++;
1480 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1483 cls.connect_trying = false;
1485 M_Update_Return_Reason("");
1487 // the connection request succeeded, stop current connection and set up a new connection
1489 // if we're connecting to a remote server, shut down any local server
1490 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1492 SV_LockThreadMutex();
1493 Host_ShutdownServer ();
1494 SV_UnlockThreadMutex();
1496 // allocate a net connection to keep track of things
1497 cls.netcon = NetConn_Open(mysocket, peeraddress);
1498 crypto = &cls.crypto;
1499 if(crypto && crypto->authenticated)
1501 Crypto_ServerFinishInstance(&cls.netcon->crypto, crypto);
1502 Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
1503 crypto->use_aes ? "Encrypted" : "Authenticated",
1504 cls.netcon->address,
1505 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1506 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1507 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1508 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1511 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1512 key_dest = key_game;
1516 cls.demonum = -1; // not in the demo loop now
1517 cls.state = ca_connected;
1518 cls.signon = 0; // need all the signon messages before playing
1519 cls.protocol = initialprotocol;
1520 // reset move sequence numbering on this new connection
1521 cls.servermovesequence = 0;
1522 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1523 Cmd_ForwardStringToServer("new");
1524 if (cls.protocol == PROTOCOL_QUAKE)
1526 // write a keepalive (clc_nop) as it seems to greatly improve the
1527 // chances of connecting to a netquake server
1529 unsigned char buf[4];
1530 memset(&msg, 0, sizeof(msg));
1532 msg.maxsize = sizeof(buf);
1533 MSG_WriteChar(&msg, clc_nop);
1534 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1538 int NetConn_IsLocalGame(void)
1540 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1546 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1550 serverlist_entry_t *entry = NULL;
1552 // search the cache for this server and update it
1553 for (n = 0;n < serverlist_cachecount;n++) {
1554 entry = &serverlist_cache[ n ];
1555 if (!strcmp(addressstring, entry->info.cname))
1559 if (n == serverlist_cachecount)
1561 // LAN search doesnt require an answer from the master server so we wont
1562 // know the ping nor will it be initialized already...
1565 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1568 if (serverlist_maxcachecount <= serverlist_cachecount)
1570 serverlist_maxcachecount += 64;
1571 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1573 entry = &serverlist_cache[n];
1575 memset(entry, 0, sizeof(*entry));
1576 // store the data the engine cares about (address and ping)
1577 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1578 entry->info.ping = 100000;
1579 entry->querytime = realtime;
1580 // if not in the slist menu we should print the server to console
1581 if (serverlist_consoleoutput)
1582 Con_Printf("querying %s\n", addressstring);
1583 ++serverlist_cachecount;
1585 // if this is the first reply from this server, count it as having replied
1586 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1587 pingtime = bound(0, pingtime, 9999);
1588 if (entry->query == SQS_REFRESHING) {
1589 entry->info.ping = pingtime;
1590 entry->query = SQS_QUERIED;
1592 // convert to unsigned to catch the -1
1593 // 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]
1594 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1598 // other server info is updated by the caller
1602 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1604 serverlist_entry_t *entry = &serverlist_cache[n];
1605 serverlist_info_t *info = &entry->info;
1606 // update description strings for engine menu and console output
1607 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);
1608 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1610 info->gameversion != gameversion.integer
1613 gameversion_min.integer >= 0 // min/max range set by user/mod?
1614 && gameversion_max.integer >= 0
1615 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1616 && gameversion_max.integer >= info->gameversion
1619 info->mod, info->map);
1620 if (entry->query == SQS_QUERIED)
1622 if(!serverlist_paused)
1623 ServerList_ViewList_Remove(entry);
1625 // if not in the slist menu we should print the server to console (if wanted)
1626 else if( serverlist_consoleoutput )
1627 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1628 // and finally, update the view set
1629 if(!serverlist_paused)
1630 ServerList_ViewList_Insert( entry );
1631 // update the entry's state
1632 serverlist_cache[n].query = SQS_QUERIED;
1635 // returns true, if it's sensible to continue the processing
1636 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1638 serverlist_entry_t *entry;
1640 // ignore the rest of the message if the serverlist is full
1641 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1643 // also ignore it if we have already queried it (other master server response)
1644 for( n = 0 ; n < serverlist_cachecount ; n++ )
1645 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1648 if( n < serverlist_cachecount ) {
1649 // the entry has already been queried once or
1653 if (serverlist_maxcachecount <= n)
1655 serverlist_maxcachecount += 64;
1656 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1659 entry = &serverlist_cache[n];
1661 memset(entry, 0, sizeof(*entry));
1662 entry->protocol = protocol;
1663 // store the data the engine cares about (address and ping)
1664 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1666 entry->info.isfavorite = isfavorite;
1668 // no, then reset the ping right away
1669 entry->info.ping = -1;
1670 // we also want to increase the serverlist_cachecount then
1671 serverlist_cachecount++;
1674 entry->query = SQS_QUERYING;
1679 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1682 if (serverlist_consoleoutput)
1683 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1686 char ipstring [128];
1689 if (data[0] == '\\')
1691 unsigned short port = data[5] * 256 + data[6];
1693 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1694 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1696 // move on to next address in packet
1701 else if (data[0] == '/' && isextended && length >= 19)
1703 unsigned short port = data[17] * 256 + data[18];
1711 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1713 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1716 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1717 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1718 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1724 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1725 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1726 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1731 // move on to next address in packet
1737 Con_Print("Error while parsing the server list\n");
1741 if (serverlist_consoleoutput && developer_networking.integer)
1742 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1744 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1750 // begin or resume serverlist queries
1751 serverlist_querysleep = false;
1752 serverlist_querywaittime = realtime + 3;
1756 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1758 qboolean fromserver;
1760 char *string, addressstring2[128];
1761 char stringbuf[16384];
1762 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1765 char infostringvalue[MAX_INPUTLINE];
1771 // quakeworld ingame packet
1772 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1774 // convert the address to a string incase we need it
1775 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1777 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1779 // received a command string - strip off the packaging and put it
1780 // into our string buffer with NULL termination
1783 length = min(length, (int)sizeof(stringbuf) - 1);
1784 memcpy(stringbuf, data, length);
1785 stringbuf[length] = 0;
1788 if (developer_networking.integer)
1790 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1791 Com_HexDumpToConsole(data, length);
1794 sendlength = sizeof(senddata) - 4;
1795 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1797 case CRYPTO_NOMATCH:
1803 memcpy(senddata, "\377\377\377\377", 4);
1804 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1807 case CRYPTO_DISCARD:
1810 memcpy(senddata, "\377\377\377\377", 4);
1811 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1815 case CRYPTO_REPLACE:
1816 string = senddata+4;
1817 length = sendlength;
1821 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1824 for (j = 0;j < MAX_RCONS;j++)
1826 // note: this value from i is used outside the loop too...
1827 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1828 if(cls.rcon_commands[i][0])
1829 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1838 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1839 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1841 e = strchr(rcon_password.string, ' ');
1842 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1844 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
1848 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1849 NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
1850 cls.rcon_commands[i][0] = 0;
1853 for (k = 0;k < MAX_RCONS;k++)
1854 if(cls.rcon_commands[k][0])
1855 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1860 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1861 // extend the timeout on other requests as we asked for a challenge
1862 for (l = 0;l < MAX_RCONS;l++)
1863 if(cls.rcon_commands[l][0])
1864 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1865 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1868 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1872 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1874 // darkplaces or quake3
1875 char protocolnames[1400];
1876 Protocol_Names(protocolnames, sizeof(protocolnames));
1877 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1879 M_Update_Return_Reason("Got challenge response");
1881 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1882 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1883 // TODO: add userinfo stuff here instead of using NQ commands?
1884 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);
1887 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1889 // darkplaces or quake3
1891 M_Update_Return_Reason("Accepted");
1893 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1896 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1898 char rejectreason[128];
1899 cls.connect_trying = false;
1901 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1902 memcpy(rejectreason, string, length);
1903 rejectreason[length] = 0;
1905 M_Update_Return_Reason(rejectreason);
1910 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1912 serverlist_info_t *info;
1917 // search the cache for this server and update it
1918 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1922 info = &serverlist_cache[n].info;
1927 info->qcstatus[0] = 0;
1928 info->players[0] = 0;
1929 info->protocol = -1;
1930 info->numplayers = 0;
1932 info->maxplayers = 0;
1933 info->gameversion = 0;
1935 p = strchr(string, '\n');
1938 *p = 0; // cut off the string there
1942 Con_Printf("statusResponse without players block?\n");
1944 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1945 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1946 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1947 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1948 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1949 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1950 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1951 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1952 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1953 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1954 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1955 info->numhumans = info->numplayers - max(0, info->numbots);
1956 info->freeslots = info->maxplayers - info->numplayers;
1958 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1962 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1964 serverlist_info_t *info;
1968 // search the cache for this server and update it
1969 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1973 info = &serverlist_cache[n].info;
1978 info->qcstatus[0] = 0;
1979 info->players[0] = 0;
1980 info->protocol = -1;
1981 info->numplayers = 0;
1983 info->maxplayers = 0;
1984 info->gameversion = 0;
1986 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1987 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1988 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1989 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1990 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1991 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1992 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1993 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1994 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1995 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1996 info->numhumans = info->numplayers - max(0, info->numbots);
1997 info->freeslots = info->maxplayers - info->numplayers;
1999 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2003 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2005 // Extract the IP addresses
2008 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2011 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2013 // Extract the IP addresses
2016 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2019 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2021 // Extract the IP addresses
2025 if (serverlist_consoleoutput)
2026 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2027 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2029 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2030 if (serverlist_consoleoutput && developer_networking.integer)
2031 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2033 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2037 // move on to next address in packet
2041 // begin or resume serverlist queries
2042 serverlist_querysleep = false;
2043 serverlist_querywaittime = realtime + 3;
2047 if (!strncmp(string, "extResponse ", 12))
2049 ++cl_net_extresponse_count;
2050 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2051 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2052 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2053 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2056 if (!strncmp(string, "ping", 4))
2058 if (developer_extra.integer)
2059 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2060 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2063 if (!strncmp(string, "ack", 3))
2065 // QuakeWorld compatibility
2066 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2068 // challenge message
2069 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2071 M_Update_Return_Reason("Got QuakeWorld challenge response");
2073 cls.qw_qport = qport.integer;
2074 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2075 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2076 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);
2079 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2083 M_Update_Return_Reason("QuakeWorld Accepted");
2085 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2088 if (length > 2 && !memcmp(string, "n\\", 2))
2091 serverlist_info_t *info;
2095 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2096 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2099 // search the cache for this server and update it
2100 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2104 info = &serverlist_cache[n].info;
2105 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2106 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2107 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2108 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2110 info->numplayers = 0; // updated below
2111 info->numhumans = 0; // updated below
2112 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2113 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2115 // count active players on server
2116 // (we could gather more info, but we're just after the number)
2117 s = strchr(string, '\n');
2121 while (s < string + length)
2123 for (;s < string + length && *s != '\n';s++)
2125 if (s >= string + length)
2133 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2137 if (string[0] == 'n')
2140 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2142 // we may not have liked the packet, but it was a command packet, so
2143 // we're done processing this packet now
2146 // quakeworld ingame packet
2147 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2150 CL_ParseServerMessage();
2153 // netquake control packets, supported for compatibility only
2154 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2158 serverlist_info_t *info;
2163 SZ_Clear(&cl_message);
2164 SZ_Write(&cl_message, data, length);
2165 MSG_BeginReading(&cl_message);
2166 c = MSG_ReadByte(&cl_message);
2170 if (developer_extra.integer)
2171 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2172 if (cls.connect_trying)
2174 lhnetaddress_t clientportaddress;
2175 clientportaddress = *peeraddress;
2176 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2177 // extra ProQuake stuff
2179 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2181 cls.proquake_servermod = 0;
2183 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2185 cls.proquake_serverversion = 0;
2187 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2189 cls.proquake_serverflags = 0;
2190 if (cls.proquake_servermod == 1)
2191 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2192 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2193 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2195 M_Update_Return_Reason("Accepted");
2197 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2201 if (developer_extra.integer)
2202 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2203 cls.connect_trying = false;
2205 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2208 case CCREP_SERVER_INFO:
2209 if (developer_extra.integer)
2210 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2212 // LordHavoc: because the quake server may report weird addresses
2213 // we just ignore it and keep the real address
2214 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2215 // search the cache for this server and update it
2216 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2220 info = &serverlist_cache[n].info;
2221 strlcpy(info->game, "Quake", sizeof(info->game));
2222 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2223 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2224 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2225 info->numplayers = MSG_ReadByte(&cl_message);
2226 info->maxplayers = MSG_ReadByte(&cl_message);
2227 info->protocol = MSG_ReadByte(&cl_message);
2229 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2232 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2233 if (developer_extra.integer)
2234 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2236 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2238 case CCREP_PLAYER_INFO:
2239 // we got a CCREP_PLAYER_INFO??
2240 //if (developer_extra.integer)
2241 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2243 case CCREP_RULE_INFO:
2244 // we got a CCREP_RULE_INFO??
2245 //if (developer_extra.integer)
2246 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2251 SZ_Clear(&cl_message);
2252 // we may not have liked the packet, but it was a valid control
2253 // packet, so we're done processing this packet now
2257 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2258 CL_ParseServerMessage();
2263 void NetConn_QueryQueueFrame(void)
2269 static double querycounter = 0;
2271 if(!net_slist_pause.integer && serverlist_paused)
2272 ServerList_RebuildViewList();
2273 serverlist_paused = net_slist_pause.integer != 0;
2275 if (serverlist_querysleep)
2278 // apply a cool down time after master server replies,
2279 // to avoid messing up the ping times on the servers
2280 if (serverlist_querywaittime > realtime)
2283 // each time querycounter reaches 1.0 issue a query
2284 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2285 maxqueries = (int)querycounter;
2286 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2287 querycounter -= maxqueries;
2289 if( maxqueries == 0 ) {
2293 // scan serverlist and issue queries as needed
2294 serverlist_querysleep = true;
2296 timeouttime = realtime - net_slist_timeout.value;
2297 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2299 serverlist_entry_t *entry = &serverlist_cache[ index ];
2300 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2305 serverlist_querysleep = false;
2306 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2311 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2313 lhnetaddress_t address;
2316 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2317 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2319 for (socket = 0; socket < cl_numsockets ; socket++)
2320 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2324 for (socket = 0; socket < cl_numsockets ; socket++)
2325 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2328 // update the entry fields
2329 entry->querytime = realtime;
2330 entry->querycounter++;
2332 // if not in the slist menu we should print the server to console
2333 if (serverlist_consoleoutput)
2334 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2340 // have we tried to refresh this server?
2341 if( entry->query == SQS_REFRESHING ) {
2342 // yes, so update the reply count (since its not responding anymore)
2344 if(!serverlist_paused)
2345 ServerList_ViewList_Remove(entry);
2347 entry->query = SQS_TIMEDOUT;
2353 void NetConn_ClientFrame(void)
2356 lhnetaddress_t peeraddress;
2357 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2358 NetConn_UpdateSockets();
2359 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2362 if (cls.connect_remainingtries == 0)
2363 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2365 cls.connect_nextsendtime = realtime + 1;
2366 cls.connect_remainingtries--;
2367 if (cls.connect_remainingtries <= -10)
2369 cls.connect_trying = false;
2371 M_Update_Return_Reason("Connect: Failed");
2375 // try challenge first (newer DP server or QW)
2376 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2377 // then try netquake as a fallback (old server, or netquake)
2378 SZ_Clear(&cl_message);
2379 // save space for the header, filled in later
2380 MSG_WriteLong(&cl_message, 0);
2381 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2382 MSG_WriteString(&cl_message, "QUAKE");
2383 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2384 // extended proquake stuff
2385 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2386 // this version matches ProQuake 3.40, the first version to support
2387 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2388 // higher clients, so we pretend we are that version...
2389 MSG_WriteByte(&cl_message, 34); // version * 10
2390 MSG_WriteByte(&cl_message, 0); // flags
2391 MSG_WriteLong(&cl_message, 0); // password
2392 // write the packetsize now...
2393 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2394 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2395 SZ_Clear(&cl_message);
2397 for (i = 0;i < cl_numsockets;i++)
2399 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2401 // R_TimeReport("clientreadnetwork");
2402 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2403 // R_TimeReport("clientparsepacket");
2407 NetConn_QueryQueueFrame();
2409 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2411 Con_Print("Connection timed out\n");
2413 SV_LockThreadMutex();
2414 Host_ShutdownServer ();
2415 SV_UnlockThreadMutex();
2419 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2423 for (i = 0;i < bufferlength - 1;i++)
2427 c = rand () % (127 - 33) + 33;
2428 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2434 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2435 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2437 prvm_prog_t *prog = SVVM_prog;
2439 unsigned int nb_clients = 0, nb_bots = 0, i;
2442 const char *crypto_idstring;
2445 // How many clients are there?
2446 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2448 if (svs.clients[i].active)
2451 if (!svs.clients[i].netconnection)
2457 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2463 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2464 if(*q != '\\' && *q != '\n')
2469 /// \TODO: we should add more information for the full status string
2470 crypto_idstring = Crypto_GetInfoResponseDataString();
2471 length = dpsnprintf(out_msg, out_size,
2472 "\377\377\377\377%s\x0A"
2473 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2474 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2479 fullstatus ? "statusResponse" : "infoResponse",
2480 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2481 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2482 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2483 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2484 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2485 fullstatus ? "\n" : "");
2487 // Make sure it fits in the buffer
2497 savelength = length;
2499 ptr = out_msg + length;
2500 left = (int)out_size - length;
2502 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2504 client_t *cl = &svs.clients[i];
2507 int nameind, cleanind, pingvalue;
2509 char cleanname [sizeof(cl->name)];
2513 // Remove all characters '"' and '\' in the player name
2518 curchar = cl->name[nameind++];
2519 if (curchar != '"' && curchar != '\\')
2521 cleanname[cleanind++] = curchar;
2522 if (cleanind == sizeof(cleanname) - 1)
2525 } while (curchar != '\0');
2526 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2528 pingvalue = (int)(cl->ping * 1000.0f);
2529 if(cl->netconnection)
2530 pingvalue = bound(1, pingvalue, 9999);
2535 ed = PRVM_EDICT_NUM(i + 1);
2536 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2542 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2543 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2548 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2550 if(cl->frags == -666) // spectator
2551 strlcpy(teambuf, " 0", sizeof(teambuf));
2552 else if(cl->colors == 0x44) // red team
2553 strlcpy(teambuf, " 1", sizeof(teambuf));
2554 else if(cl->colors == 0xDD) // blue team
2555 strlcpy(teambuf, " 2", sizeof(teambuf));
2556 else if(cl->colors == 0xCC) // yellow team
2557 strlcpy(teambuf, " 3", sizeof(teambuf));
2558 else if(cl->colors == 0x99) // pink team
2559 strlcpy(teambuf, " 4", sizeof(teambuf));
2561 strlcpy(teambuf, " 0", sizeof(teambuf));
2566 // note: team number is inserted according to SoF2 protocol
2568 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2574 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2583 // turn it into an infoResponse!
2584 out_msg[savelength] = 0;
2585 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2586 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2601 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2603 size_t floodslotnum, bestfloodslotnum;
2604 double bestfloodtime;
2605 lhnetaddress_t noportpeeraddress;
2606 // see if this is a connect flood
2607 noportpeeraddress = *peeraddress;
2608 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2609 bestfloodslotnum = 0;
2610 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2611 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2613 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2615 bestfloodtime = floodlist[floodslotnum].lasttime;
2616 bestfloodslotnum = floodslotnum;
2618 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2620 // this address matches an ongoing flood address
2621 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2625 // renew the ban on this address so it does not expire
2626 // until the flood has subsided
2627 floodlist[floodslotnum].lasttime = realtime;
2629 //Con_Printf("Flood detected!\n");
2632 // the flood appears to have subsided, so allow this
2633 bestfloodslotnum = floodslotnum; // reuse the same slot
2637 // begin a new timeout on this address
2638 floodlist[bestfloodslotnum].address = noportpeeraddress;
2639 floodlist[bestfloodslotnum].lasttime = realtime;
2640 //Con_Printf("Flood detection initiated!\n");
2644 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2646 size_t floodslotnum;
2647 lhnetaddress_t noportpeeraddress;
2648 // see if this is a connect flood
2649 noportpeeraddress = *peeraddress;
2650 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2651 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2653 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2655 // this address matches an ongoing flood address
2657 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2658 floodlist[floodslotnum].lasttime = 0;
2659 //Con_Printf("Flood cleared!\n");
2664 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2666 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2671 t1 = (long) time(NULL);
2672 t2 = strtol(s, NULL, 0);
2673 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2676 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2679 return !memcmp(mdfourbuf, hash, 16);
2682 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2687 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2690 // validate the challenge
2691 for (i = 0;i < MAX_CHALLENGES;i++)
2692 if(challenge[i].time > 0)
2693 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2695 // if the challenge is not recognized, drop the packet
2696 if (i == MAX_CHALLENGES)
2699 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2702 if(memcmp(mdfourbuf, hash, 16))
2705 // unmark challenge to prevent replay attacks
2706 challenge[i].time = 0;
2711 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2713 return !strcmp(password, hash);
2716 /// returns a string describing the user level, or NULL for auth failure
2717 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)
2719 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2720 static char buf[MAX_INPUTLINE];
2722 qboolean restricted = false;
2723 qboolean have_usernames = false;
2726 userpass_start = rcon_password.string;
2727 while((userpass_end = strchr(userpass_start, ' ')))
2729 have_usernames = true;
2730 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2732 if(comparator(peeraddress, buf, password, cs, cslen))
2734 userpass_start = userpass_end + 1;
2736 if(userpass_start[0])
2738 userpass_end = userpass_start + strlen(userpass_start);
2739 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2744 have_usernames = false;
2745 userpass_start = rcon_restricted_password.string;
2746 while((userpass_end = strchr(userpass_start, ' ')))
2748 have_usernames = true;
2749 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2751 if(comparator(peeraddress, buf, password, cs, cslen))
2753 userpass_start = userpass_end + 1;
2755 if(userpass_start[0])
2757 userpass_end = userpass_start + strlen(userpass_start);
2758 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2762 return NULL; // DENIED
2765 for(text = s; text != endpos; ++text)
2766 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2767 return NULL; // block possible exploits against the parser/alias expansion
2771 size_t l = strlen(s);
2774 hasquotes = (strchr(s, '"') != NULL);
2775 // sorry, we can't allow these substrings in wildcard expressions,
2776 // as they can mess with the argument counts
2777 text = rcon_restricted_commands.string;
2778 while(COM_ParseToken_Console(&text))
2780 // com_token now contains a pattern to check for...
2781 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2784 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2787 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2789 if(!strcmp(com_token, s))
2792 else // single-arg expression? must match the beginning of the command
2794 if(!strcmp(com_token, s))
2796 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2800 // if we got here, nothing matched!
2808 userpass_startpass = strchr(userpass_start, ':');
2809 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2810 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2812 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2815 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2819 // looks like a legitimate rcon command with the correct password
2820 const char *s_ptr = s;
2821 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2822 while(s_ptr != endpos)
2824 size_t l = strlen(s_ptr);
2826 Con_Printf(" %s;", s_ptr);
2831 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2832 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2835 size_t l = strlen(s);
2838 client_t *host_client_save = host_client;
2839 Cmd_ExecuteString(s, src_command, true);
2840 host_client = host_client_save;
2841 // in case it is a command that changes host_client (like restart)
2845 Con_Rcon_Redirect_End();
2849 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2853 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2855 int i, ret, clientnum, best;
2858 char *s, *string, response[1400], addressstring2[128];
2859 static char stringbuf[16384]; // server only
2860 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2861 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2862 size_t sendlength, response_len;
2863 char infostringvalue[MAX_INPUTLINE];
2869 // convert the address to a string incase we need it
2870 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2872 // see if we can identify the sender as a local player
2873 // (this is necessary for rcon to send a reliable reply if the client is
2874 // actually on the server, not sending remotely)
2875 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2876 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2878 if (i == svs.maxclients)
2881 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2883 // received a command string - strip off the packaging and put it
2884 // into our string buffer with NULL termination
2887 length = min(length, (int)sizeof(stringbuf) - 1);
2888 memcpy(stringbuf, data, length);
2889 stringbuf[length] = 0;
2892 if (developer_extra.integer)
2894 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2895 Com_HexDumpToConsole(data, length);
2898 sendlength = sizeof(senddata) - 4;
2899 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2901 case CRYPTO_NOMATCH:
2907 memcpy(senddata, "\377\377\377\377", 4);
2908 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2911 case CRYPTO_DISCARD:
2914 memcpy(senddata, "\377\377\377\377", 4);
2915 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2919 case CRYPTO_REPLACE:
2920 string = senddata+4;
2921 length = sendlength;
2925 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2927 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2929 if(challenge[i].time > 0)
2930 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2932 if (besttime > challenge[i].time)
2933 besttime = challenge[best = i].time;
2935 // if we did not find an exact match, choose the oldest and
2936 // update address and string
2937 if (i == MAX_CHALLENGES)
2940 challenge[i].address = *peeraddress;
2941 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2945 // flood control: drop if requesting challenge too often
2946 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2949 challenge[i].time = realtime;
2950 // send the challenge
2951 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2952 response_len = strlen(response) + 1;
2953 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2954 NetConn_Write(mysocket, response, response_len, peeraddress);
2957 if (length > 8 && !memcmp(string, "connect\\", 8))
2959 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2963 if(crypto && crypto->authenticated)
2965 // no need to check challenge
2966 if(crypto_developer.integer)
2968 Con_Printf("%s connection to %s is being established: client is %s@%.*s, I am %.*s@%.*s\n",
2969 crypto->use_aes ? "Encrypted" : "Authenticated",
2971 crypto->client_idfp[0] ? crypto->client_idfp : "-",
2972 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
2973 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
2974 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
2980 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
2982 // validate the challenge
2983 for (i = 0;i < MAX_CHALLENGES;i++)
2984 if(challenge[i].time > 0)
2985 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
2987 // if the challenge is not recognized, drop the packet
2988 if (i == MAX_CHALLENGES)
2993 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
2994 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
2996 if(!(islocal || sv_public.integer > -2))
2998 if (developer_extra.integer)
2999 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3000 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
3004 // check engine protocol
3005 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3007 if (developer_extra.integer)
3008 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3009 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3013 // see if this is a duplicate connection request or a disconnected
3014 // client who is rejoining to the same client slot
3015 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3017 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3019 // this is a known client...
3020 if(crypto && crypto->authenticated)
3022 // reject if changing key!
3023 if(client->netconnection->crypto.authenticated)
3026 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3028 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3030 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3032 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3035 if (developer_extra.integer)
3036 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3037 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3044 // reject if downgrading!
3045 if(client->netconnection->crypto.authenticated)
3047 if (developer_extra.integer)
3048 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3049 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3055 // client crashed and is coming back,
3056 // keep their stuff intact
3057 if (developer_extra.integer)
3058 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3059 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3060 if(crypto && crypto->authenticated)
3061 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
3062 SV_SendServerinfo(client);
3066 // client is still trying to connect,
3067 // so we send a duplicate reply
3068 if (developer_extra.integer)
3069 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3070 if(crypto && crypto->authenticated)
3071 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
3072 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3078 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3081 // find an empty client slot for this new client
3082 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3085 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3087 // allocated connection
3088 if (developer_extra.integer)
3089 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3090 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3091 // now set up the client
3092 if(crypto && crypto->authenticated)
3093 Crypto_ServerFinishInstance(&conn->crypto, crypto);
3094 SV_ConnectClient(clientnum, conn);
3095 NetConn_Heartbeat(1);
3100 // no empty slots found - server is full
3101 if (developer_extra.integer)
3102 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3103 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3107 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3109 const char *challenge = NULL;
3111 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3114 // If there was a challenge in the getinfo message
3115 if (length > 8 && string[7] == ' ')
3116 challenge = string + 8;
3118 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3120 if (developer_extra.integer)
3121 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3122 NetConn_WriteString(mysocket, response, peeraddress);
3126 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3128 const char *challenge = NULL;
3130 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3133 // If there was a challenge in the getinfo message
3134 if (length > 10 && string[9] == ' ')
3135 challenge = string + 10;
3137 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3139 if (developer_extra.integer)
3140 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3141 NetConn_WriteString(mysocket, response, peeraddress);
3145 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3147 char *password = string + 20;
3148 char *timeval = string + 37;
3149 char *s = strchr(timeval, ' ');
3150 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3151 const char *userlevel;
3153 if(rcon_secure.integer > 1)
3157 return true; // invalid packet
3160 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3161 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3164 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3166 char *password = string + 25;
3167 char *challenge = string + 42;
3168 char *s = strchr(challenge, ' ');
3169 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3170 const char *userlevel;
3172 return true; // invalid packet
3175 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3176 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3179 if (length >= 5 && !memcmp(string, "rcon ", 5))
3182 char *s = string + 5;
3183 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3186 if(rcon_secure.integer > 0)
3189 for (i = 0;!ISWHITESPACE(*s);s++)
3190 if (i < (int)sizeof(password) - 1)
3192 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3195 if (!ISWHITESPACE(password[0]))
3197 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3198 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3202 if (!strncmp(string, "extResponse ", 12))
3204 ++sv_net_extresponse_count;
3205 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3206 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3207 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3208 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3211 if (!strncmp(string, "ping", 4))
3213 if (developer_extra.integer)
3214 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3215 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3218 if (!strncmp(string, "ack", 3))
3220 // we may not have liked the packet, but it was a command packet, so
3221 // we're done processing this packet now
3224 // netquake control packets, supported for compatibility only, and only
3225 // when running game protocols that are normally served via this connection
3227 // (this protects more modern protocols against being used for
3228 // Quake packet flood Denial Of Service attacks)
3229 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)
3233 const char *protocolname;
3236 SZ_Clear(&sv_message);
3237 SZ_Write(&sv_message, data, length);
3238 MSG_BeginReading(&sv_message);
3239 c = MSG_ReadByte(&sv_message);
3243 if (developer_extra.integer)
3244 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3245 if(!(islocal || sv_public.integer > -2))
3247 if (developer_extra.integer)
3248 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3249 SZ_Clear(&sv_message);
3250 // save space for the header, filled in later
3251 MSG_WriteLong(&sv_message, 0);
3252 MSG_WriteByte(&sv_message, CCREP_REJECT);
3253 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3254 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3255 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3256 SZ_Clear(&sv_message);
3260 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3261 protocolnumber = MSG_ReadByte(&sv_message);
3262 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3264 if (developer_extra.integer)
3265 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3266 SZ_Clear(&sv_message);
3267 // save space for the header, filled in later
3268 MSG_WriteLong(&sv_message, 0);
3269 MSG_WriteByte(&sv_message, CCREP_REJECT);
3270 MSG_WriteString(&sv_message, "Incompatible version.\n");
3271 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3272 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3273 SZ_Clear(&sv_message);
3277 // see if this connect request comes from a known client
3278 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3280 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3282 // this is either a duplicate connection request
3283 // or coming back from a timeout
3284 // (if so, keep their stuff intact)
3286 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3287 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3289 if (developer_extra.integer)
3290 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3291 SZ_Clear(&sv_message);
3292 // save space for the header, filled in later
3293 MSG_WriteLong(&sv_message, 0);
3294 MSG_WriteByte(&sv_message, CCREP_REJECT);
3295 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3296 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3297 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3298 SZ_Clear(&sv_message);
3303 if (developer_extra.integer)
3304 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3305 SZ_Clear(&sv_message);
3306 // save space for the header, filled in later
3307 MSG_WriteLong(&sv_message, 0);
3308 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3309 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3310 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3311 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3312 SZ_Clear(&sv_message);
3314 // if client is already spawned, re-send the
3315 // serverinfo message as they'll need it to play
3317 SV_SendServerinfo(client);
3322 // this is a new client, check for connection flood
3323 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3326 // find a slot for the new client
3327 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3330 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3332 // connect to the client
3333 // everything is allocated, just fill in the details
3334 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3335 if (developer_extra.integer)
3336 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3337 // send back the info about the server connection
3338 SZ_Clear(&sv_message);
3339 // save space for the header, filled in later
3340 MSG_WriteLong(&sv_message, 0);
3341 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3342 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3343 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3344 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3345 SZ_Clear(&sv_message);
3346 // now set up the client struct
3347 SV_ConnectClient(clientnum, conn);
3348 NetConn_Heartbeat(1);
3353 if (developer_extra.integer)
3354 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3355 // no room; try to let player know
3356 SZ_Clear(&sv_message);
3357 // save space for the header, filled in later
3358 MSG_WriteLong(&sv_message, 0);
3359 MSG_WriteByte(&sv_message, CCREP_REJECT);
3360 MSG_WriteString(&sv_message, "Server is full.\n");
3361 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3362 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3363 SZ_Clear(&sv_message);
3365 case CCREQ_SERVER_INFO:
3366 if (developer_extra.integer)
3367 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3368 if(!(islocal || sv_public.integer > -1))
3371 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3374 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3377 char myaddressstring[128];
3378 if (developer_extra.integer)
3379 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3380 SZ_Clear(&sv_message);
3381 // save space for the header, filled in later
3382 MSG_WriteLong(&sv_message, 0);
3383 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3384 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3385 MSG_WriteString(&sv_message, myaddressstring);
3386 MSG_WriteString(&sv_message, hostname.string);
3387 MSG_WriteString(&sv_message, sv.name);
3388 // How many clients are there?
3389 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3390 if (svs.clients[i].active)
3392 MSG_WriteByte(&sv_message, numclients);
3393 MSG_WriteByte(&sv_message, svs.maxclients);
3394 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3395 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3396 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3397 SZ_Clear(&sv_message);
3400 case CCREQ_PLAYER_INFO:
3401 if (developer_extra.integer)
3402 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3403 if(!(islocal || sv_public.integer > -1))
3406 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3411 int playerNumber, activeNumber, clientNumber;
3414 playerNumber = MSG_ReadByte(&sv_message);
3416 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3417 if (client->active && ++activeNumber == playerNumber)
3419 if (clientNumber != svs.maxclients)
3421 SZ_Clear(&sv_message);
3422 // save space for the header, filled in later
3423 MSG_WriteLong(&sv_message, 0);
3424 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3425 MSG_WriteByte(&sv_message, playerNumber);
3426 MSG_WriteString(&sv_message, client->name);
3427 MSG_WriteLong(&sv_message, client->colors);
3428 MSG_WriteLong(&sv_message, client->frags);
3429 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3430 if(sv_status_privacy.integer)
3431 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3433 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3434 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3435 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3436 SZ_Clear(&sv_message);
3440 case CCREQ_RULE_INFO:
3441 if (developer_extra.integer)
3442 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3443 if(!(islocal || sv_public.integer > -1))
3446 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3453 // find the search start location
3454 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3455 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3457 // send the response
3458 SZ_Clear(&sv_message);
3459 // save space for the header, filled in later
3460 MSG_WriteLong(&sv_message, 0);
3461 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3464 MSG_WriteString(&sv_message, var->name);
3465 MSG_WriteString(&sv_message, var->string);
3467 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3468 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3469 SZ_Clear(&sv_message);
3473 if (developer_extra.integer)
3474 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3475 if (sv.active && !rcon_secure.integer)
3477 char password[2048];
3481 const char *userlevel;
3482 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3483 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3485 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3486 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3487 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3494 SZ_Clear(&sv_message);
3495 // we may not have liked the packet, but it was a valid control
3496 // packet, so we're done processing this packet now
3501 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3503 SV_ReadClientMessage();
3510 void NetConn_ServerFrame(void)
3513 lhnetaddress_t peeraddress;
3514 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3515 for (i = 0;i < sv_numsockets;i++)
3516 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3517 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3518 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3520 // never timeout loopback connections
3521 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3523 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3524 SV_DropClient(false);
3529 void NetConn_SleepMicroseconds(int microseconds)
3531 LHNET_SleepUntilPacket_Microseconds(microseconds);
3535 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3539 lhnetaddress_t masteraddress;
3540 lhnetaddress_t broadcastaddress;
3543 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3546 // 26000 is the default quake server port, servers on other ports will not
3548 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3549 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3553 for (i = 0;i < cl_numsockets;i++)
3557 const char *cmdname, *extraoptions;
3558 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3560 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3562 // search LAN for Quake servers
3563 SZ_Clear(&cl_message);
3564 // save space for the header, filled in later
3565 MSG_WriteLong(&cl_message, 0);
3566 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3567 MSG_WriteString(&cl_message, "QUAKE");
3568 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3569 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3570 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3571 SZ_Clear(&cl_message);
3573 // search LAN for DarkPlaces servers
3574 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3577 // build the getservers message to send to the dpmaster master servers
3578 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3580 cmdname = "getserversExt";
3581 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3585 cmdname = "getservers";
3588 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3591 for (masternum = 0;sv_masters[masternum].name;masternum++)
3593 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3596 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3600 // search favorite servers
3601 for(j = 0; j < nFavorites; ++j)
3603 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3605 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3606 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3613 // only query QuakeWorld servers when the user wants to
3616 for (i = 0;i < cl_numsockets;i++)
3620 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3622 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3624 // search LAN for QuakeWorld servers
3625 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3627 // build the getservers message to send to the qwmaster master servers
3628 // note this has no -1 prefix, and the trailing nul byte is sent
3629 dpsnprintf(request, sizeof(request), "c\n");
3633 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3635 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3637 if (m_state != m_slist)
3639 char lookupstring[128];
3640 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3641 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3644 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3648 // search favorite servers
3649 for(j = 0; j < nFavorites; ++j)
3651 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3653 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3655 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3656 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3663 if (!masterquerycount)
3665 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3666 M_Update_Return_Reason("No network");
3671 void NetConn_Heartbeat(int priority)
3673 lhnetaddress_t masteraddress;
3675 lhnetsocket_t *mysocket;
3677 // if it's a state change (client connected), limit next heartbeat to no
3678 // more than 30 sec in the future
3679 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3680 nextheartbeattime = realtime + 30.0;
3682 // limit heartbeatperiod to 30 to 270 second range,
3683 // lower limit is to avoid abusing master servers with excess traffic,
3684 // upper limit is to avoid timing out on the master server (which uses
3686 if (sv_heartbeatperiod.value < 30)
3687 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3688 if (sv_heartbeatperiod.value > 270)
3689 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3691 // make advertising optional and don't advertise singleplayer games, and
3692 // only send a heartbeat as often as the admin wants
3693 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3695 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3696 for (masternum = 0;sv_masters[masternum].name;masternum++)
3697 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3698 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3702 static void Net_Heartbeat_f(void)
3705 NetConn_Heartbeat(2);
3707 Con_Print("No server running, can not heartbeat to master server.\n");
3710 static void PrintStats(netconn_t *conn)
3712 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3713 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3715 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3716 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3717 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3718 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3719 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3720 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3721 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3722 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3723 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3724 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3727 void Net_Stats_f(void)
3730 Con_Print("connections =\n");
3731 for (conn = netconn_list;conn;conn = conn->next)
3736 void Net_Refresh_f(void)
3738 if (m_state != m_slist) {
3739 Con_Print("Sending new requests to master servers\n");
3740 ServerList_QueryList(false, true, false, true);
3741 Con_Print("Listening for replies...\n");
3743 ServerList_QueryList(false, true, false, false);
3746 void Net_Slist_f(void)
3748 ServerList_ResetMasks();
3749 serverlist_sortbyfield = SLIF_PING;
3750 serverlist_sortflags = 0;
3751 if (m_state != m_slist) {
3752 Con_Print("Sending requests to master servers\n");
3753 ServerList_QueryList(true, true, false, true);
3754 Con_Print("Listening for replies...\n");
3756 ServerList_QueryList(true, true, false, false);
3759 void Net_SlistQW_f(void)
3761 ServerList_ResetMasks();
3762 serverlist_sortbyfield = SLIF_PING;
3763 serverlist_sortflags = 0;
3764 if (m_state != m_slist) {
3765 Con_Print("Sending requests to master servers\n");
3766 ServerList_QueryList(true, false, true, true);
3767 serverlist_consoleoutput = true;
3768 Con_Print("Listening for replies...\n");
3770 ServerList_QueryList(true, false, true, false);
3774 void NetConn_Init(void)
3777 lhnetaddress_t tempaddress;
3778 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3779 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3781 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3782 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3783 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3785 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3786 Cvar_RegisterVariable(&net_test);
3787 Cvar_RegisterVariable(&net_usesizelimit);
3788 Cvar_RegisterVariable(&net_burstreserve);
3789 Cvar_RegisterVariable(&rcon_restricted_password);
3790 Cvar_RegisterVariable(&rcon_restricted_commands);
3791 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3792 Cvar_RegisterVariable(&net_slist_queriespersecond);
3793 Cvar_RegisterVariable(&net_slist_queriesperframe);
3794 Cvar_RegisterVariable(&net_slist_timeout);
3795 Cvar_RegisterVariable(&net_slist_maxtries);
3796 Cvar_RegisterVariable(&net_slist_favorites);
3797 Cvar_RegisterVariable(&net_slist_pause);
3798 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3799 Cvar_RegisterVariable(&net_tos_dscp);
3800 Cvar_RegisterVariable(&net_messagetimeout);
3801 Cvar_RegisterVariable(&net_connecttimeout);
3802 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3803 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3804 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3805 Cvar_RegisterVariable(&cl_netlocalping);
3806 Cvar_RegisterVariable(&cl_netpacketloss_send);
3807 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3808 Cvar_RegisterVariable(&hostname);
3809 Cvar_RegisterVariable(&developer_networking);
3810 Cvar_RegisterVariable(&cl_netport);
3811 Cvar_RegisterVariable(&sv_netport);
3812 Cvar_RegisterVariable(&net_address);
3813 Cvar_RegisterVariable(&net_address_ipv6);
3814 Cvar_RegisterVariable(&sv_public);
3815 Cvar_RegisterVariable(&sv_public_rejectreason);
3816 Cvar_RegisterVariable(&sv_heartbeatperiod);
3817 for (i = 0;sv_masters[i].name;i++)
3818 Cvar_RegisterVariable(&sv_masters[i]);
3819 Cvar_RegisterVariable(&gameversion);
3820 Cvar_RegisterVariable(&gameversion_min);
3821 Cvar_RegisterVariable(&gameversion_max);
3822 // 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.
3823 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3825 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3827 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3828 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3831 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3833 // 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
3834 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3836 i = atoi(com_argv[i + 1]);
3837 if (i >= 0 && i < 65536)
3839 Con_Printf("-port option used, setting port cvar to %i\n", i);
3840 Cvar_SetValueQuick(&sv_netport, i);
3843 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3847 cl_message.data = cl_message_buf;
3848 cl_message.maxsize = sizeof(cl_message_buf);
3849 cl_message.cursize = 0;
3850 sv_message.data = sv_message_buf;
3851 sv_message.maxsize = sizeof(sv_message_buf);
3852 sv_message.cursize = 0;
3854 if (Thread_HasThreads())
3855 netconn_mutex = Thread_CreateMutex();
3858 void NetConn_Shutdown(void)
3860 NetConn_CloseClientPorts();
3861 NetConn_CloseServerPorts();
3864 Thread_DestroyMutex(netconn_mutex);
3865 netconn_mutex = NULL;