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
56 static cvar_t sv_qwmasters [] =
58 {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
59 {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
60 {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
61 {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
62 {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
63 {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
64 {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
65 {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
66 {0, "sv_qwmasterextra5", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
70 static double nextheartbeattime = 0;
74 static unsigned char cl_message_buf[NET_MAXMESSAGE];
75 static unsigned char sv_message_buf[NET_MAXMESSAGE];
76 char cl_readstring[MAX_INPUTLINE];
77 char sv_readstring[MAX_INPUTLINE];
79 cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
80 cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
81 cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
82 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
83 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."};
84 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."};
85 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."};
86 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."};
87 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
88 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
90 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)"};
91 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)"};
92 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)"};
93 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
94 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)"};
95 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
96 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"};
97 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)"};
98 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"};
99 static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
100 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
101 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"};
102 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"};
103 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"};
104 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
105 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)"};
106 extern cvar_t rcon_secure;
107 extern cvar_t rcon_secure_challengetimeout;
109 double masterquerytime = -1000;
110 int masterquerycount = 0;
111 int masterreplycount = 0;
112 int serverquerycount = 0;
113 int serverreplycount = 0;
115 challenge_t challenge[MAX_CHALLENGES];
117 /// this is only false if there are still servers left to query
118 static qboolean serverlist_querysleep = true;
119 static qboolean serverlist_paused = false;
120 /// this is pushed a second or two ahead of realtime whenever a master server
121 /// reply is received, to avoid issuing queries while master replies are still
122 /// flooding in (which would make a mess of the ping times)
123 static double serverlist_querywaittime = 0;
125 static int cl_numsockets;
126 static lhnetsocket_t *cl_sockets[16];
127 static int sv_numsockets;
128 static lhnetsocket_t *sv_sockets[16];
130 netconn_t *netconn_list = NULL;
131 mempool_t *netconn_mempool = NULL;
132 void *netconn_mutex = NULL;
134 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
135 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
136 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
137 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
139 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
140 int cl_net_extresponse_count = 0;
141 int cl_net_extresponse_last = 0;
143 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
144 int sv_net_extresponse_count = 0;
145 int sv_net_extresponse_last = 0;
147 // ServerList interface
148 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
149 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
151 serverlist_infofield_t serverlist_sortbyfield;
152 int serverlist_sortflags;
154 int serverlist_viewcount = 0;
155 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
157 int serverlist_maxcachecount = 0;
158 int serverlist_cachecount = 0;
159 serverlist_entry_t *serverlist_cache = NULL;
161 qboolean serverlist_consoleoutput;
163 static int nFavorites = 0;
164 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
165 static int nFavorites_idfp = 0;
166 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
168 void NetConn_UpdateFavorites(void)
173 p = net_slist_favorites.string;
174 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
176 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
177 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
178 // (if v6 address contains port, it must start with '[')
180 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
185 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
191 /// helper function to insert a value into the viewset
192 /// spare entries will be removed
193 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
196 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
197 i = serverlist_viewcount++;
199 i = SERVERLIST_VIEWLISTSIZE - 1;
202 for( ; i > index ; i-- )
203 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
205 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
208 /// we suppose serverlist_viewcount to be valid, ie > 0
209 static void _ServerList_ViewList_Helper_Remove( int index )
211 serverlist_viewcount--;
212 for( ; index < serverlist_viewcount ; index++ )
213 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
216 /// \returns true if A should be inserted before B
217 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
219 int result = 0; // > 0 if for numbers A > B and for text if A < B
221 if( serverlist_sortflags & SLSF_FAVORITESFIRST )
223 if(A->info.isfavorite != B->info.isfavorite)
224 return A->info.isfavorite;
227 switch( serverlist_sortbyfield ) {
229 result = A->info.ping - B->info.ping;
231 case SLIF_MAXPLAYERS:
232 result = A->info.maxplayers - B->info.maxplayers;
234 case SLIF_NUMPLAYERS:
235 result = A->info.numplayers - B->info.numplayers;
238 result = A->info.numbots - B->info.numbots;
241 result = A->info.numhumans - B->info.numhumans;
244 result = A->info.freeslots - B->info.freeslots;
247 result = A->info.protocol - B->info.protocol;
250 result = strcmp( B->info.cname, A->info.cname );
253 result = strcasecmp( B->info.game, A->info.game );
256 result = strcasecmp( B->info.map, A->info.map );
259 result = strcasecmp( B->info.mod, A->info.mod );
262 result = strcasecmp( B->info.name, A->info.name );
265 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
267 case SLIF_ISFAVORITE:
268 result = !!B->info.isfavorite - !!A->info.isfavorite;
271 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
277 if( serverlist_sortflags & SLSF_DESCENDING )
283 // if the chosen sort key is identical, sort by index
284 // (makes this a stable sort, so that later replies from servers won't
285 // shuffle the servers around when they have the same ping)
289 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
291 // This should actually be done with some intermediate and end-of-function return
303 case SLMO_GREATEREQUAL:
305 case SLMO_NOTCONTAIN:
306 case SLMO_STARTSWITH:
307 case SLMO_NOTSTARTSWITH:
310 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
315 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
318 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
319 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
320 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
321 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
323 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
324 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
327 // Same here, also using an intermediate & final return would be more appropriate
331 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
332 case SLMO_NOTCONTAIN:
333 return !*bufferB || !strstr( bufferA, bufferB );
334 case SLMO_STARTSWITH:
335 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
336 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
337 case SLMO_NOTSTARTSWITH:
338 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
340 return strcmp( bufferA, bufferB ) < 0;
342 return strcmp( bufferA, bufferB ) <= 0;
344 return strcmp( bufferA, bufferB ) == 0;
346 return strcmp( bufferA, bufferB ) > 0;
348 return strcmp( bufferA, bufferB ) != 0;
349 case SLMO_GREATEREQUAL:
350 return strcmp( bufferA, bufferB ) >= 0;
352 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
357 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
359 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
361 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
363 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
365 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
367 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
369 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
371 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
373 if( *mask->info.cname
374 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
377 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
380 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
383 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
386 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
388 if( *mask->info.qcstatus
389 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
391 if( *mask->info.players
392 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
394 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
399 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
401 int start, end, mid, i;
404 // reject incompatible servers
406 entry->info.gameversion != gameversion.integer
409 gameversion_min.integer >= 0 // min/max range set by user/mod?
410 && gameversion_max.integer >= 0
411 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
412 && gameversion_max.integer >= entry->info.gameversion
417 // refresh the "favorite" status
418 entry->info.isfavorite = false;
419 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
421 char idfp[FP64_SIZE+1];
422 for(i = 0; i < nFavorites; ++i)
424 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
426 entry->info.isfavorite = true;
430 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
432 for(i = 0; i < nFavorites_idfp; ++i)
434 if(!strcmp(idfp, favorites_idfp[i]))
436 entry->info.isfavorite = true;
443 // FIXME: change this to be more readable (...)
444 // now check whether it passes through the masks
445 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
446 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
449 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
450 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
452 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
455 if( !serverlist_viewcount ) {
456 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
459 // ok, insert it, we just need to find out where exactly:
462 // check whether to insert it as new first item
463 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
464 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
466 } // check whether to insert it as new last item
467 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
468 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
472 end = serverlist_viewcount - 1;
473 while( end > start + 1 )
475 mid = (start + end) / 2;
476 // test the item that lies in the middle between start and end
477 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
478 // the item has to be in the upper half
481 // the item has to be in the lower half
484 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
487 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
490 for( i = 0; i < serverlist_viewcount; i++ )
492 if (ServerList_GetViewEntry(i) == entry)
494 _ServerList_ViewList_Helper_Remove(i);
500 void ServerList_RebuildViewList(void)
504 serverlist_viewcount = 0;
505 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
506 serverlist_entry_t *entry = &serverlist_cache[i];
507 // also display entries that are currently being refreshed [11/8/2007 Black]
508 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
509 ServerList_ViewList_Insert( entry );
513 void ServerList_ResetMasks(void)
517 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
518 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
519 // numbots needs to be compared to -1 to always succeed
520 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
521 serverlist_andmasks[i].info.numbots = -1;
522 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
523 serverlist_ormasks[i].info.numbots = -1;
526 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
529 int numplayers = 0, maxplayers = 0;
530 for (i = 0;i < serverlist_cachecount;i++)
532 if (serverlist_cache[i].query == SQS_QUERIED)
534 numplayers += serverlist_cache[i].info.numhumans;
535 maxplayers += serverlist_cache[i].info.maxplayers;
538 *numplayerspointer = numplayers;
539 *maxplayerspointer = maxplayers;
543 static void _ServerList_Test(void)
546 if (serverlist_maxcachecount <= 1024)
548 serverlist_maxcachecount = 1024;
549 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
551 for( i = 0 ; i < 1024 ; i++ ) {
552 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
553 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
554 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
555 serverlist_cache[serverlist_cachecount].finished = true;
556 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 );
557 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
558 serverlist_cachecount++;
563 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
565 masterquerytime = realtime;
566 masterquerycount = 0;
567 masterreplycount = 0;
569 serverquerycount = 0;
570 serverreplycount = 0;
571 serverlist_cachecount = 0;
572 serverlist_viewcount = 0;
573 serverlist_maxcachecount = 0;
574 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
576 // refresh all entries
578 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
579 serverlist_entry_t *entry = &serverlist_cache[ n ];
580 entry->query = SQS_REFRESHING;
581 entry->querycounter = 0;
584 serverlist_consoleoutput = consoleoutput;
586 //_ServerList_Test();
588 NetConn_QueryMasters(querydp, queryqw);
593 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
597 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
598 Thread_LockMutex(netconn_mutex);
599 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
600 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
601 Thread_UnlockMutex(netconn_mutex);
604 if (cl_netpacketloss_receive.integer)
605 for (i = 0;i < cl_numsockets;i++)
606 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
608 if (developer_networking.integer)
610 char addressstring[128], addressstring2[128];
611 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
614 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
615 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
616 Com_HexDumpToConsole((unsigned char *)data, length);
619 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
624 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
628 if (cl_netpacketloss_send.integer)
629 for (i = 0;i < cl_numsockets;i++)
630 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
632 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
633 Thread_LockMutex(netconn_mutex);
634 ret = LHNET_Write(mysocket, data, length, peeraddress);
635 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
636 Thread_UnlockMutex(netconn_mutex);
637 if (developer_networking.integer)
639 char addressstring[128], addressstring2[128];
640 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
641 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
642 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)");
643 Com_HexDumpToConsole((unsigned char *)data, length);
648 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
650 // note this does not include the trailing NULL because we add that in the parser
651 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
654 qboolean NetConn_CanSend(netconn_t *conn)
656 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
657 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = realtime;
658 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
659 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
660 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
661 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
662 if (realtime > conn->cleartime)
666 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
671 void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
673 double bursttime = burstsize / (double)rate;
675 // delay later packets to obey rate limit
676 if (*cleartime < realtime - bursttime)
677 *cleartime = realtime - bursttime;
678 *cleartime = *cleartime + len / (double)rate;
680 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
681 if (net_test.integer)
683 if (*cleartime < realtime)
684 *cleartime = realtime;
688 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
691 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
692 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
694 // if this packet was supposedly choked, but we find ourselves sending one
695 // anyway, make sure the size counting starts at zero
696 // (this mostly happens on level changes and disconnects and such)
697 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
698 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
700 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
702 if (protocol == PROTOCOL_QUAKEWORLD)
705 qboolean sendreliable;
707 // note that it is ok to send empty messages to the qw server,
708 // otherwise it won't respond to us at all
710 sendreliable = false;
711 // if the remote side dropped the last reliable message, resend it
712 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
714 // if the reliable transmit buffer is empty, copy the current message out
715 if (!conn->sendMessageLength && conn->message.cursize)
717 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
718 conn->sendMessageLength = conn->message.cursize;
719 SZ_Clear(&conn->message); // clear the message buffer
720 conn->qw.reliable_sequence ^= 1;
723 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
724 StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
725 // last received unreliable packet number, and last received reliable packet number (0 or 1)
726 StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
728 conn->outgoing_unreliable_sequence++;
729 // client sends qport in every packet
730 if (conn == cls.netcon)
732 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
734 // also update cls.qw_outgoing_sequence
735 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
737 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
739 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
743 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
745 // add the reliable message if there is one
748 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
749 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
750 packetLen += conn->sendMessageLength;
751 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
754 // add the unreliable message if possible
755 if (packetLen + data->cursize <= 1400)
757 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
758 memcpy(sendbuffer + packetLen, data->data, data->cursize);
759 packetLen += data->cursize;
762 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
765 conn->unreliableMessagesSent++;
767 totallen += packetLen + 28;
771 unsigned int packetLen;
772 unsigned int dataLen;
777 // if a reliable message fragment has been lost, send it again
778 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
780 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
782 dataLen = conn->sendMessageLength;
787 dataLen = MAX_PACKETFRAGMENT;
791 packetLen = NET_HEADERSIZE + dataLen;
793 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
794 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
795 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
797 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
799 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
800 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
802 conn->lastSendTime = realtime;
803 conn->packetsReSent++;
806 totallen += sendmelen + 28;
809 // if we have a new reliable message to send, do so
810 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
812 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
814 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
815 conn->message.overflowed = true;
819 if (developer_networking.integer && conn == cls.netcon)
821 Con_Print("client sending reliable message to server:\n");
822 SZ_HexDumpToConsole(&conn->message);
825 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
826 conn->sendMessageLength = conn->message.cursize;
827 SZ_Clear(&conn->message);
829 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
831 dataLen = conn->sendMessageLength;
836 dataLen = MAX_PACKETFRAGMENT;
840 packetLen = NET_HEADERSIZE + dataLen;
842 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
843 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
844 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
846 conn->nq.sendSequence++;
848 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
850 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
852 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
854 conn->lastSendTime = realtime;
856 conn->reliableMessagesSent++;
858 totallen += sendmelen + 28;
861 // if we have an unreliable message to send, do so
864 packetLen = NET_HEADERSIZE + data->cursize;
866 if (packetLen > (int)sizeof(sendbuffer))
868 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
872 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE);
873 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
874 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
876 conn->outgoing_unreliable_sequence++;
878 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
880 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
882 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
885 conn->unreliableMessagesSent++;
887 totallen += sendmelen + 28;
891 NetConn_UpdateCleartime(&conn->cleartime, cl_rate.integer, cl_rate_burstsize.integer, totallen);
896 qboolean NetConn_HaveClientPorts(void)
898 return !!cl_numsockets;
901 qboolean NetConn_HaveServerPorts(void)
903 return !!sv_numsockets;
906 void NetConn_CloseClientPorts(void)
908 for (;cl_numsockets > 0;cl_numsockets--)
909 if (cl_sockets[cl_numsockets - 1])
910 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
913 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
915 lhnetaddress_t address;
918 char addressstring2[1024];
919 if (addressstring && addressstring[0])
920 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
922 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
925 if ((s = LHNET_OpenSocket_Connectionless(&address)))
927 cl_sockets[cl_numsockets++] = s;
928 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
929 if (addresstype != LHNETADDRESSTYPE_LOOP)
930 Con_Printf("Client opened a socket on address %s\n", addressstring2);
934 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
935 Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
939 Con_Printf("Client unable to parse address %s\n", addressstring);
942 void NetConn_OpenClientPorts(void)
945 NetConn_CloseClientPorts();
947 SV_LockThreadMutex(); // FIXME recursive?
948 Crypto_LoadKeys(); // client sockets
949 SV_UnlockThreadMutex();
951 port = bound(0, cl_netport.integer, 65535);
952 if (cl_netport.integer != port)
953 Cvar_SetValueQuick(&cl_netport, port);
955 Con_Printf("Client using an automatically assigned port\n");
957 Con_Printf("Client using port %i\n", port);
958 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
959 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
961 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
965 void NetConn_CloseServerPorts(void)
967 for (;sv_numsockets > 0;sv_numsockets--)
968 if (sv_sockets[sv_numsockets - 1])
969 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
972 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
974 lhnetaddress_t address;
977 char addressstring2[1024];
980 for (port = defaultport; port <= defaultport + range; port++)
982 if (addressstring && addressstring[0])
983 success = LHNETADDRESS_FromString(&address, addressstring, port);
985 success = LHNETADDRESS_FromPort(&address, addresstype, port);
988 if ((s = LHNET_OpenSocket_Connectionless(&address)))
990 sv_sockets[sv_numsockets++] = s;
991 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
992 if (addresstype != LHNETADDRESSTYPE_LOOP)
993 Con_Printf("Server listening on address %s\n", addressstring2);
998 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
999 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
1004 Con_Printf("Server unable to parse address %s\n", addressstring);
1005 // if it cant parse one address, it wont be able to parse another for sure
1012 void NetConn_OpenServerPorts(int opennetports)
1015 NetConn_CloseServerPorts();
1017 SV_LockThreadMutex(); // FIXME recursive?
1018 Crypto_LoadKeys(); // server sockets
1019 SV_UnlockThreadMutex();
1021 NetConn_UpdateSockets();
1022 port = bound(0, sv_netport.integer, 65535);
1025 Con_Printf("Server using port %i\n", port);
1026 if (sv_netport.integer != port)
1027 Cvar_SetValueQuick(&sv_netport, port);
1028 if (cls.state != ca_dedicated)
1029 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1033 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1034 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1036 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1039 if (sv_numsockets == 0)
1040 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1043 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1045 int i, a = LHNETADDRESS_GetAddressType(address);
1046 for (i = 0;i < cl_numsockets;i++)
1047 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1048 return cl_sockets[i];
1052 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1054 int i, a = LHNETADDRESS_GetAddressType(address);
1055 for (i = 0;i < sv_numsockets;i++)
1056 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1057 return sv_sockets[i];
1061 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1064 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1065 conn->mysocket = mysocket;
1066 conn->peeraddress = *peeraddress;
1067 conn->lastMessageTime = realtime;
1068 conn->message.data = conn->messagedata;
1069 conn->message.maxsize = sizeof(conn->messagedata);
1070 conn->message.cursize = 0;
1071 // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1072 // reduce effectiveness of connection request floods
1073 conn->timeout = realtime + net_connecttimeout.value;
1074 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1075 conn->next = netconn_list;
1076 netconn_list = conn;
1080 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1081 void NetConn_Close(netconn_t *conn)
1084 // remove connection from list
1086 // allow the client to reconnect immediately
1087 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1089 if (conn == netconn_list)
1090 netconn_list = conn->next;
1093 for (c = netconn_list;c;c = c->next)
1095 if (c->next == conn)
1097 c->next = conn->next;
1101 // not found in list, we'll avoid crashing here...
1109 static int clientport = -1;
1110 static int clientport2 = -1;
1111 static int hostport = -1;
1112 void NetConn_UpdateSockets(void)
1116 // TODO add logic to automatically close sockets if needed
1117 LHNET_DefaultDSCP(net_tos_dscp.integer);
1119 if (cls.state != ca_dedicated)
1121 if (clientport2 != cl_netport.integer)
1123 clientport2 = cl_netport.integer;
1124 if (cls.state == ca_connected)
1125 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1127 if (cls.state == ca_disconnected && clientport != clientport2)
1129 clientport = clientport2;
1130 NetConn_CloseClientPorts();
1132 if (cl_numsockets == 0)
1133 NetConn_OpenClientPorts();
1136 if (hostport != sv_netport.integer)
1138 hostport = sv_netport.integer;
1140 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1143 for (j = 0;j < MAX_RCONS;j++)
1145 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1146 if(cls.rcon_commands[i][0])
1148 if(realtime > cls.rcon_timeout[i])
1151 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1152 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1153 cls.rcon_commands[i][0] = 0;
1161 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1163 int originallength = length;
1164 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1165 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1166 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1170 if (protocol == PROTOCOL_QUAKEWORLD)
1172 int sequence, sequence_ack;
1173 int reliable_ack, reliable_message;
1177 sequence = LittleLong(*((int *)(data + 0)));
1178 sequence_ack = LittleLong(*((int *)(data + 4)));
1182 if (conn != cls.netcon)
1187 // 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?)
1188 //qport = LittleShort(*((int *)(data + 8)));
1193 conn->packetsReceived++;
1194 reliable_message = (sequence >> 31) & 1;
1195 reliable_ack = (sequence_ack >> 31) & 1;
1196 sequence &= ~(1<<31);
1197 sequence_ack &= ~(1<<31);
1198 if (sequence <= conn->qw.incoming_sequence)
1200 //Con_DPrint("Got a stale datagram\n");
1203 count = sequence - (conn->qw.incoming_sequence + 1);
1206 conn->droppedDatagrams += count;
1207 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1210 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1211 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1212 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1213 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1214 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1215 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1218 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1219 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1220 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1221 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1222 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1223 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1224 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1226 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1227 if (net_test.integer)
1229 if (conn->cleartime < realtime)
1230 conn->cleartime = realtime;
1233 if (reliable_ack == conn->qw.reliable_sequence)
1235 // received, now we will be able to send another reliable message
1236 conn->sendMessageLength = 0;
1237 conn->reliableMessagesReceived++;
1239 conn->qw.incoming_sequence = sequence;
1240 if (conn == cls.netcon)
1241 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1242 conn->qw.incoming_acknowledged = sequence_ack;
1243 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1244 if (reliable_message)
1245 conn->qw.incoming_reliable_sequence ^= 1;
1246 conn->lastMessageTime = realtime;
1247 conn->timeout = realtime + newtimeout;
1248 conn->unreliableMessagesReceived++;
1249 if (conn == cls.netcon)
1251 SZ_Clear(&cl_message);
1252 SZ_Write(&cl_message, data, length);
1253 MSG_BeginReading(&cl_message);
1257 SZ_Clear(&sv_message);
1258 SZ_Write(&sv_message, data, length);
1259 MSG_BeginReading(&sv_message);
1267 unsigned int sequence;
1272 originallength = length;
1273 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1279 qlength = (unsigned int)BuffBigLong(data);
1280 flags = qlength & ~NETFLAG_LENGTH_MASK;
1281 qlength &= NETFLAG_LENGTH_MASK;
1282 // control packets were already handled
1283 if (!(flags & NETFLAG_CTL) && qlength == length)
1285 sequence = BuffBigLong(data + 4);
1286 conn->packetsReceived++;
1289 if (flags & NETFLAG_UNRELIABLE)
1291 if (sequence >= conn->nq.unreliableReceiveSequence)
1293 if (sequence > conn->nq.unreliableReceiveSequence)
1295 count = sequence - conn->nq.unreliableReceiveSequence;
1296 conn->droppedDatagrams += count;
1297 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1300 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1301 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1302 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1303 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1304 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1305 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1308 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1309 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1310 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1311 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1312 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1313 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1314 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1316 conn->nq.unreliableReceiveSequence = sequence + 1;
1317 conn->lastMessageTime = realtime;
1318 conn->timeout = realtime + newtimeout;
1319 conn->unreliableMessagesReceived++;
1322 if (conn == cls.netcon)
1324 SZ_Clear(&cl_message);
1325 SZ_Write(&cl_message, data, length);
1326 MSG_BeginReading(&cl_message);
1330 SZ_Clear(&sv_message);
1331 SZ_Write(&sv_message, data, length);
1332 MSG_BeginReading(&sv_message);
1338 // Con_DPrint("Got a stale datagram\n");
1341 else if (flags & NETFLAG_ACK)
1343 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1344 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1346 if (sequence == (conn->nq.sendSequence - 1))
1348 if (sequence == conn->nq.ackSequence)
1350 conn->nq.ackSequence++;
1351 if (conn->nq.ackSequence != conn->nq.sendSequence)
1352 Con_DPrint("ack sequencing error\n");
1353 conn->lastMessageTime = realtime;
1354 conn->timeout = realtime + newtimeout;
1355 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1357 unsigned int packetLen;
1358 unsigned int dataLen;
1361 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1362 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1364 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1366 dataLen = conn->sendMessageLength;
1371 dataLen = MAX_PACKETFRAGMENT;
1375 packetLen = NET_HEADERSIZE + dataLen;
1377 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
1378 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1379 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1381 conn->nq.sendSequence++;
1383 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1384 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
1386 conn->lastSendTime = realtime;
1387 conn->packetsSent++;
1391 conn->sendMessageLength = 0;
1394 // Con_DPrint("Duplicate ACK received\n");
1397 // Con_DPrint("Stale ACK received\n");
1400 else if (flags & NETFLAG_DATA)
1402 unsigned char temppacket[8];
1403 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1404 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1406 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1408 StoreBigLong(temppacket, 8 | NETFLAG_ACK);
1409 StoreBigLong(temppacket + 4, sequence);
1410 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1412 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
1413 if (sequence == conn->nq.receiveSequence)
1415 conn->lastMessageTime = realtime;
1416 conn->timeout = realtime + newtimeout;
1417 conn->nq.receiveSequence++;
1418 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1419 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1420 conn->receiveMessageLength += length;
1422 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1423 "Dropping the message!\n", sequence );
1424 conn->receiveMessageLength = 0;
1427 if (flags & NETFLAG_EOM)
1429 conn->reliableMessagesReceived++;
1430 length = conn->receiveMessageLength;
1431 conn->receiveMessageLength = 0;
1434 if (conn == cls.netcon)
1436 SZ_Clear(&cl_message);
1437 SZ_Write(&cl_message, conn->receiveMessage, length);
1438 MSG_BeginReading(&cl_message);
1442 SZ_Clear(&sv_message);
1443 SZ_Write(&sv_message, conn->receiveMessage, length);
1444 MSG_BeginReading(&sv_message);
1451 conn->receivedDuplicateCount++;
1459 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1462 cls.connect_trying = false;
1464 M_Update_Return_Reason("");
1466 // the connection request succeeded, stop current connection and set up a new connection
1468 // if we're connecting to a remote server, shut down any local server
1469 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1471 SV_LockThreadMutex();
1472 Host_ShutdownServer ();
1473 SV_UnlockThreadMutex();
1475 // allocate a net connection to keep track of things
1476 cls.netcon = NetConn_Open(mysocket, peeraddress);
1477 crypto = &cls.crypto;
1478 if(crypto && crypto->authenticated)
1480 Crypto_ServerFinishInstance(&cls.netcon->crypto, crypto);
1481 Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
1482 crypto->use_aes ? "Encrypted" : "Authenticated",
1483 cls.netcon->address,
1484 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1485 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1486 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1487 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1490 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1491 key_dest = key_game;
1495 cls.demonum = -1; // not in the demo loop now
1496 cls.state = ca_connected;
1497 cls.signon = 0; // need all the signon messages before playing
1498 cls.protocol = initialprotocol;
1499 // reset move sequence numbering on this new connection
1500 cls.servermovesequence = 0;
1501 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1502 Cmd_ForwardStringToServer("new");
1503 if (cls.protocol == PROTOCOL_QUAKE)
1505 // write a keepalive (clc_nop) as it seems to greatly improve the
1506 // chances of connecting to a netquake server
1508 unsigned char buf[4];
1509 memset(&msg, 0, sizeof(msg));
1511 msg.maxsize = sizeof(buf);
1512 MSG_WriteChar(&msg, clc_nop);
1513 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1517 int NetConn_IsLocalGame(void)
1519 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1524 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1528 serverlist_entry_t *entry = NULL;
1530 // search the cache for this server and update it
1531 for (n = 0;n < serverlist_cachecount;n++) {
1532 entry = &serverlist_cache[ n ];
1533 if (!strcmp(addressstring, entry->info.cname))
1537 if (n == serverlist_cachecount)
1539 // LAN search doesnt require an answer from the master server so we wont
1540 // know the ping nor will it be initialized already...
1543 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1546 if (serverlist_maxcachecount <= serverlist_cachecount)
1548 serverlist_maxcachecount += 64;
1549 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1551 entry = &serverlist_cache[n];
1553 memset(entry, 0, sizeof(*entry));
1554 // store the data the engine cares about (address and ping)
1555 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1556 entry->info.ping = 100000;
1557 entry->querytime = realtime;
1558 // if not in the slist menu we should print the server to console
1559 if (serverlist_consoleoutput)
1560 Con_Printf("querying %s\n", addressstring);
1561 ++serverlist_cachecount;
1563 // if this is the first reply from this server, count it as having replied
1564 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1565 pingtime = bound(0, pingtime, 9999);
1566 if (entry->query == SQS_REFRESHING) {
1567 entry->info.ping = pingtime;
1568 entry->query = SQS_QUERIED;
1570 // convert to unsigned to catch the -1
1571 // 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]
1572 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1576 // other server info is updated by the caller
1580 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1582 serverlist_entry_t *entry = &serverlist_cache[n];
1583 serverlist_info_t *info = &entry->info;
1584 // update description strings for engine menu and console output
1585 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);
1586 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1588 info->gameversion != gameversion.integer
1591 gameversion_min.integer >= 0 // min/max range set by user/mod?
1592 && gameversion_max.integer >= 0
1593 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1594 && gameversion_max.integer >= info->gameversion
1597 info->mod, info->map);
1598 if (entry->query == SQS_QUERIED)
1600 if(!serverlist_paused)
1601 ServerList_ViewList_Remove(entry);
1603 // if not in the slist menu we should print the server to console (if wanted)
1604 else if( serverlist_consoleoutput )
1605 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1606 // and finally, update the view set
1607 if(!serverlist_paused)
1608 ServerList_ViewList_Insert( entry );
1609 // update the entry's state
1610 serverlist_cache[n].query = SQS_QUERIED;
1613 // returns true, if it's sensible to continue the processing
1614 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1616 serverlist_entry_t *entry;
1618 // ignore the rest of the message if the serverlist is full
1619 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1621 // also ignore it if we have already queried it (other master server response)
1622 for( n = 0 ; n < serverlist_cachecount ; n++ )
1623 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1626 if( n < serverlist_cachecount ) {
1627 // the entry has already been queried once or
1631 if (serverlist_maxcachecount <= n)
1633 serverlist_maxcachecount += 64;
1634 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1637 entry = &serverlist_cache[n];
1639 memset(entry, 0, sizeof(*entry));
1640 entry->protocol = protocol;
1641 // store the data the engine cares about (address and ping)
1642 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1644 entry->info.isfavorite = isfavorite;
1646 // no, then reset the ping right away
1647 entry->info.ping = -1;
1648 // we also want to increase the serverlist_cachecount then
1649 serverlist_cachecount++;
1652 entry->query = SQS_QUERYING;
1657 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1660 if (serverlist_consoleoutput)
1661 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1664 char ipstring [128];
1667 if (data[0] == '\\')
1669 unsigned short port = data[5] * 256 + data[6];
1671 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1672 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1674 // move on to next address in packet
1679 else if (data[0] == '/' && isextended && length >= 19)
1681 unsigned short port = data[17] * 256 + data[18];
1689 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1691 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1694 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1695 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1696 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1702 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1703 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1704 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1709 // move on to next address in packet
1715 Con_Print("Error while parsing the server list\n");
1719 if (serverlist_consoleoutput && developer_networking.integer)
1720 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1722 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1728 // begin or resume serverlist queries
1729 serverlist_querysleep = false;
1730 serverlist_querywaittime = realtime + 3;
1733 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1735 qboolean fromserver;
1738 char *string, addressstring2[128], ipstring[32];
1739 char stringbuf[16384];
1740 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1742 char infostringvalue[MAX_INPUTLINE];
1745 // quakeworld ingame packet
1746 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1748 // convert the address to a string incase we need it
1749 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1751 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1753 // received a command string - strip off the packaging and put it
1754 // into our string buffer with NULL termination
1757 length = min(length, (int)sizeof(stringbuf) - 1);
1758 memcpy(stringbuf, data, length);
1759 stringbuf[length] = 0;
1762 if (developer_networking.integer)
1764 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1765 Com_HexDumpToConsole(data, length);
1768 sendlength = sizeof(senddata) - 4;
1769 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1771 case CRYPTO_NOMATCH:
1777 memcpy(senddata, "\377\377\377\377", 4);
1778 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1781 case CRYPTO_DISCARD:
1784 memcpy(senddata, "\377\377\377\377", 4);
1785 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1789 case CRYPTO_REPLACE:
1790 string = senddata+4;
1791 length = sendlength;
1795 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1798 for (j = 0;j < MAX_RCONS;j++)
1800 // note: this value from i is used outside the loop too...
1801 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1802 if(cls.rcon_commands[i][0])
1803 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1812 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1813 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1815 e = strchr(rcon_password.string, ' ');
1816 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1818 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
1822 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1823 NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
1824 cls.rcon_commands[i][0] = 0;
1827 for (k = 0;k < MAX_RCONS;k++)
1828 if(cls.rcon_commands[k][0])
1829 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1834 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1835 // extend the timeout on other requests as we asked for a challenge
1836 for (l = 0;l < MAX_RCONS;l++)
1837 if(cls.rcon_commands[l][0])
1838 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1839 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1842 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1846 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1848 // darkplaces or quake3
1849 char protocolnames[1400];
1850 Protocol_Names(protocolnames, sizeof(protocolnames));
1851 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1853 M_Update_Return_Reason("Got challenge response");
1855 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1856 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1857 // TODO: add userinfo stuff here instead of using NQ commands?
1858 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);
1861 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1863 // darkplaces or quake3
1865 M_Update_Return_Reason("Accepted");
1867 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1870 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1872 char rejectreason[128];
1873 cls.connect_trying = false;
1875 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1876 memcpy(rejectreason, string, length);
1877 rejectreason[length] = 0;
1879 M_Update_Return_Reason(rejectreason);
1883 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1885 serverlist_info_t *info;
1890 // search the cache for this server and update it
1891 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1895 info = &serverlist_cache[n].info;
1900 info->qcstatus[0] = 0;
1901 info->players[0] = 0;
1902 info->protocol = -1;
1903 info->numplayers = 0;
1905 info->maxplayers = 0;
1906 info->gameversion = 0;
1908 p = strchr(string, '\n');
1911 *p = 0; // cut off the string there
1915 Con_Printf("statusResponse without players block?\n");
1917 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1918 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1919 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1920 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1921 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1922 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1923 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1924 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1925 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1926 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1927 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1928 info->numhumans = info->numplayers - max(0, info->numbots);
1929 info->freeslots = info->maxplayers - info->numplayers;
1931 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1935 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1937 serverlist_info_t *info;
1941 // search the cache for this server and update it
1942 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1946 info = &serverlist_cache[n].info;
1951 info->qcstatus[0] = 0;
1952 info->players[0] = 0;
1953 info->protocol = -1;
1954 info->numplayers = 0;
1956 info->maxplayers = 0;
1957 info->gameversion = 0;
1959 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1960 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1961 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1962 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1963 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1964 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1965 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1966 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1967 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1968 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1969 info->numhumans = info->numplayers - max(0, info->numbots);
1970 info->freeslots = info->maxplayers - info->numplayers;
1972 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1976 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1978 // Extract the IP addresses
1981 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
1984 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1986 // Extract the IP addresses
1989 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
1992 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1994 // Extract the IP addresses
1998 if (serverlist_consoleoutput)
1999 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2000 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2002 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2003 if (serverlist_consoleoutput && developer_networking.integer)
2004 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2006 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2010 // move on to next address in packet
2014 // begin or resume serverlist queries
2015 serverlist_querysleep = false;
2016 serverlist_querywaittime = realtime + 3;
2019 if (!strncmp(string, "extResponse ", 12))
2021 ++cl_net_extresponse_count;
2022 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2023 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2024 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2025 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2028 if (!strncmp(string, "ping", 4))
2030 if (developer_extra.integer)
2031 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2032 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2035 if (!strncmp(string, "ack", 3))
2037 // QuakeWorld compatibility
2038 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2040 // challenge message
2041 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2043 M_Update_Return_Reason("Got QuakeWorld challenge response");
2045 cls.qw_qport = qport.integer;
2046 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2047 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2048 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);
2051 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2055 M_Update_Return_Reason("QuakeWorld Accepted");
2057 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2060 if (length > 2 && !memcmp(string, "n\\", 2))
2062 serverlist_info_t *info;
2066 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2067 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2070 // search the cache for this server and update it
2071 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2075 info = &serverlist_cache[n].info;
2076 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2077 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2078 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2079 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2081 info->numplayers = 0; // updated below
2082 info->numhumans = 0; // updated below
2083 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2084 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2086 // count active players on server
2087 // (we could gather more info, but we're just after the number)
2088 s = strchr(string, '\n');
2092 while (s < string + length)
2094 for (;s < string + length && *s != '\n';s++)
2096 if (s >= string + length)
2104 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2108 if (string[0] == 'n')
2111 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2113 // we may not have liked the packet, but it was a command packet, so
2114 // we're done processing this packet now
2117 // quakeworld ingame packet
2118 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2121 CL_ParseServerMessage();
2124 // netquake control packets, supported for compatibility only
2125 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2128 serverlist_info_t *info;
2132 SZ_Clear(&cl_message);
2133 SZ_Write(&cl_message, data, length);
2134 MSG_BeginReading(&cl_message);
2135 c = MSG_ReadByte(&cl_message);
2139 if (developer_extra.integer)
2140 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2141 if (cls.connect_trying)
2143 lhnetaddress_t clientportaddress;
2144 clientportaddress = *peeraddress;
2145 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2146 // extra ProQuake stuff
2148 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2150 cls.proquake_servermod = 0;
2152 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2154 cls.proquake_serverversion = 0;
2156 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2158 cls.proquake_serverflags = 0;
2159 if (cls.proquake_servermod == 1)
2160 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2161 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2162 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2164 M_Update_Return_Reason("Accepted");
2166 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2170 if (developer_extra.integer)
2171 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2172 cls.connect_trying = false;
2174 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2177 case CCREP_SERVER_INFO:
2178 if (developer_extra.integer)
2179 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2180 // LordHavoc: because the quake server may report weird addresses
2181 // we just ignore it and keep the real address
2182 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2183 // search the cache for this server and update it
2184 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2188 info = &serverlist_cache[n].info;
2189 strlcpy(info->game, "Quake", sizeof(info->game));
2190 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2191 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2192 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2193 info->numplayers = MSG_ReadByte(&cl_message);
2194 info->maxplayers = MSG_ReadByte(&cl_message);
2195 info->protocol = MSG_ReadByte(&cl_message);
2197 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2200 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2201 if (developer_extra.integer)
2202 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2204 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2206 case CCREP_PLAYER_INFO:
2207 // we got a CCREP_PLAYER_INFO??
2208 //if (developer_extra.integer)
2209 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2211 case CCREP_RULE_INFO:
2212 // we got a CCREP_RULE_INFO??
2213 //if (developer_extra.integer)
2214 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2219 SZ_Clear(&cl_message);
2220 // we may not have liked the packet, but it was a valid control
2221 // packet, so we're done processing this packet now
2225 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2226 CL_ParseServerMessage();
2230 void NetConn_QueryQueueFrame(void)
2236 static double querycounter = 0;
2238 if(!net_slist_pause.integer && serverlist_paused)
2239 ServerList_RebuildViewList();
2240 serverlist_paused = net_slist_pause.integer != 0;
2242 if (serverlist_querysleep)
2245 // apply a cool down time after master server replies,
2246 // to avoid messing up the ping times on the servers
2247 if (serverlist_querywaittime > realtime)
2250 // each time querycounter reaches 1.0 issue a query
2251 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2252 maxqueries = (int)querycounter;
2253 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2254 querycounter -= maxqueries;
2256 if( maxqueries == 0 ) {
2260 // scan serverlist and issue queries as needed
2261 serverlist_querysleep = true;
2263 timeouttime = realtime - net_slist_timeout.value;
2264 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2266 serverlist_entry_t *entry = &serverlist_cache[ index ];
2267 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2272 serverlist_querysleep = false;
2273 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2278 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2280 lhnetaddress_t address;
2283 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2284 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2286 for (socket = 0; socket < cl_numsockets ; socket++)
2287 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2291 for (socket = 0; socket < cl_numsockets ; socket++)
2292 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2295 // update the entry fields
2296 entry->querytime = realtime;
2297 entry->querycounter++;
2299 // if not in the slist menu we should print the server to console
2300 if (serverlist_consoleoutput)
2301 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2307 // have we tried to refresh this server?
2308 if( entry->query == SQS_REFRESHING ) {
2309 // yes, so update the reply count (since its not responding anymore)
2311 if(!serverlist_paused)
2312 ServerList_ViewList_Remove(entry);
2314 entry->query = SQS_TIMEDOUT;
2319 void NetConn_ClientFrame(void)
2322 lhnetaddress_t peeraddress;
2323 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2324 NetConn_UpdateSockets();
2325 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2328 if (cls.connect_remainingtries == 0)
2329 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2331 cls.connect_nextsendtime = realtime + 1;
2332 cls.connect_remainingtries--;
2333 if (cls.connect_remainingtries <= -10)
2335 cls.connect_trying = false;
2337 M_Update_Return_Reason("Connect: Failed");
2341 // try challenge first (newer DP server or QW)
2342 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2343 // then try netquake as a fallback (old server, or netquake)
2344 SZ_Clear(&cl_message);
2345 // save space for the header, filled in later
2346 MSG_WriteLong(&cl_message, 0);
2347 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2348 MSG_WriteString(&cl_message, "QUAKE");
2349 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2350 // extended proquake stuff
2351 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2352 // this version matches ProQuake 3.40, the first version to support
2353 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2354 // higher clients, so we pretend we are that version...
2355 MSG_WriteByte(&cl_message, 34); // version * 10
2356 MSG_WriteByte(&cl_message, 0); // flags
2357 MSG_WriteLong(&cl_message, 0); // password
2358 // write the packetsize now...
2359 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2360 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2361 SZ_Clear(&cl_message);
2363 for (i = 0;i < cl_numsockets;i++)
2365 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2367 // R_TimeReport("clientreadnetwork");
2368 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2369 // R_TimeReport("clientparsepacket");
2372 NetConn_QueryQueueFrame();
2373 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2375 Con_Print("Connection timed out\n");
2377 SV_LockThreadMutex();
2378 Host_ShutdownServer ();
2379 SV_UnlockThreadMutex();
2383 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2387 for (i = 0;i < bufferlength - 1;i++)
2391 c = rand () % (127 - 33) + 33;
2392 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2398 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2399 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2401 prvm_prog_t *prog = SVVM_prog;
2403 unsigned int nb_clients = 0, nb_bots = 0, i;
2406 const char *crypto_idstring;
2409 // How many clients are there?
2410 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2412 if (svs.clients[i].active)
2415 if (!svs.clients[i].netconnection)
2421 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2427 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2428 if(*q != '\\' && *q != '\n')
2433 /// \TODO: we should add more information for the full status string
2434 crypto_idstring = Crypto_GetInfoResponseDataString();
2435 length = dpsnprintf(out_msg, out_size,
2436 "\377\377\377\377%s\x0A"
2437 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2438 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2443 fullstatus ? "statusResponse" : "infoResponse",
2444 gamename, com_modname, gameversion.integer, svs.maxclients,
2445 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2446 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2447 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2448 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2449 fullstatus ? "\n" : "");
2451 // Make sure it fits in the buffer
2461 savelength = length;
2463 ptr = out_msg + length;
2464 left = (int)out_size - length;
2466 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2468 client_t *cl = &svs.clients[i];
2471 int nameind, cleanind, pingvalue;
2473 char cleanname [sizeof(cl->name)];
2477 // Remove all characters '"' and '\' in the player name
2482 curchar = cl->name[nameind++];
2483 if (curchar != '"' && curchar != '\\')
2485 cleanname[cleanind++] = curchar;
2486 if (cleanind == sizeof(cleanname) - 1)
2489 } while (curchar != '\0');
2490 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2492 pingvalue = (int)(cl->ping * 1000.0f);
2493 if(cl->netconnection)
2494 pingvalue = bound(1, pingvalue, 9999);
2499 ed = PRVM_EDICT_NUM(i + 1);
2500 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2506 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2507 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2512 if ((gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) && (teamplay.integer > 0))
2514 if(cl->frags == -666) // spectator
2515 strlcpy(teambuf, " 0", sizeof(teambuf));
2516 else if(cl->colors == 0x44) // red team
2517 strlcpy(teambuf, " 1", sizeof(teambuf));
2518 else if(cl->colors == 0xDD) // blue team
2519 strlcpy(teambuf, " 2", sizeof(teambuf));
2520 else if(cl->colors == 0xCC) // yellow team
2521 strlcpy(teambuf, " 3", sizeof(teambuf));
2522 else if(cl->colors == 0x99) // pink team
2523 strlcpy(teambuf, " 4", sizeof(teambuf));
2525 strlcpy(teambuf, " 0", sizeof(teambuf));
2530 // note: team number is inserted according to SoF2 protocol
2532 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2538 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2547 // turn it into an infoResponse!
2548 out_msg[savelength] = 0;
2549 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2550 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2565 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2567 size_t floodslotnum, bestfloodslotnum;
2568 double bestfloodtime;
2569 lhnetaddress_t noportpeeraddress;
2570 // see if this is a connect flood
2571 noportpeeraddress = *peeraddress;
2572 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2573 bestfloodslotnum = 0;
2574 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2575 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2577 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2579 bestfloodtime = floodlist[floodslotnum].lasttime;
2580 bestfloodslotnum = floodslotnum;
2582 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2584 // this address matches an ongoing flood address
2585 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2589 // renew the ban on this address so it does not expire
2590 // until the flood has subsided
2591 floodlist[floodslotnum].lasttime = realtime;
2593 //Con_Printf("Flood detected!\n");
2596 // the flood appears to have subsided, so allow this
2597 bestfloodslotnum = floodslotnum; // reuse the same slot
2601 // begin a new timeout on this address
2602 floodlist[bestfloodslotnum].address = noportpeeraddress;
2603 floodlist[bestfloodslotnum].lasttime = realtime;
2604 //Con_Printf("Flood detection initiated!\n");
2608 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2610 size_t floodslotnum;
2611 lhnetaddress_t noportpeeraddress;
2612 // see if this is a connect flood
2613 noportpeeraddress = *peeraddress;
2614 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2615 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2617 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2619 // this address matches an ongoing flood address
2621 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2622 floodlist[floodslotnum].lasttime = 0;
2623 //Con_Printf("Flood cleared!\n");
2628 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2630 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2635 t1 = (long) time(NULL);
2636 t2 = strtol(s, NULL, 0);
2637 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2640 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2643 return !memcmp(mdfourbuf, hash, 16);
2646 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2651 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2654 // validate the challenge
2655 for (i = 0;i < MAX_CHALLENGES;i++)
2656 if(challenge[i].time > 0)
2657 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2659 // if the challenge is not recognized, drop the packet
2660 if (i == MAX_CHALLENGES)
2663 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2666 if(memcmp(mdfourbuf, hash, 16))
2669 // unmark challenge to prevent replay attacks
2670 challenge[i].time = 0;
2675 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2677 return !strcmp(password, hash);
2680 /// returns a string describing the user level, or NULL for auth failure
2681 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)
2683 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2684 static char buf[MAX_INPUTLINE];
2686 qboolean restricted = false;
2687 qboolean have_usernames = false;
2690 userpass_start = rcon_password.string;
2691 while((userpass_end = strchr(userpass_start, ' ')))
2693 have_usernames = true;
2694 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2696 if(comparator(peeraddress, buf, password, cs, cslen))
2698 userpass_start = userpass_end + 1;
2700 if(userpass_start[0])
2702 userpass_end = userpass_start + strlen(userpass_start);
2703 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2708 have_usernames = false;
2709 userpass_start = rcon_restricted_password.string;
2710 while((userpass_end = strchr(userpass_start, ' ')))
2712 have_usernames = true;
2713 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2715 if(comparator(peeraddress, buf, password, cs, cslen))
2717 userpass_start = userpass_end + 1;
2719 if(userpass_start[0])
2721 userpass_end = userpass_start + strlen(userpass_start);
2722 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2726 return NULL; // DENIED
2729 for(text = s; text != endpos; ++text)
2730 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2731 return NULL; // block possible exploits against the parser/alias expansion
2735 size_t l = strlen(s);
2738 hasquotes = (strchr(s, '"') != NULL);
2739 // sorry, we can't allow these substrings in wildcard expressions,
2740 // as they can mess with the argument counts
2741 text = rcon_restricted_commands.string;
2742 while(COM_ParseToken_Console(&text))
2744 // com_token now contains a pattern to check for...
2745 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2748 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2751 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2753 if(!strcmp(com_token, s))
2756 else // single-arg expression? must match the beginning of the command
2758 if(!strcmp(com_token, s))
2760 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2764 // if we got here, nothing matched!
2772 userpass_startpass = strchr(userpass_start, ':');
2773 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2774 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2776 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2779 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2783 // looks like a legitimate rcon command with the correct password
2784 const char *s_ptr = s;
2785 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2786 while(s_ptr != endpos)
2788 size_t l = strlen(s_ptr);
2790 Con_Printf(" %s;", s_ptr);
2795 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2796 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2799 size_t l = strlen(s);
2802 client_t *host_client_save = host_client;
2803 Cmd_ExecuteString(s, src_command, true);
2804 host_client = host_client_save;
2805 // in case it is a command that changes host_client (like restart)
2809 Con_Rcon_Redirect_End();
2813 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2817 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2819 int i, ret, clientnum, best;
2822 char *s, *string, response[1400], addressstring2[128];
2823 static char stringbuf[16384]; // server only
2824 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2825 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2826 size_t sendlength, response_len;
2827 char infostringvalue[MAX_INPUTLINE];
2833 // convert the address to a string incase we need it
2834 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2836 // see if we can identify the sender as a local player
2837 // (this is necessary for rcon to send a reliable reply if the client is
2838 // actually on the server, not sending remotely)
2839 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2840 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2842 if (i == svs.maxclients)
2845 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2847 // received a command string - strip off the packaging and put it
2848 // into our string buffer with NULL termination
2851 length = min(length, (int)sizeof(stringbuf) - 1);
2852 memcpy(stringbuf, data, length);
2853 stringbuf[length] = 0;
2856 if (developer_extra.integer)
2858 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2859 Com_HexDumpToConsole(data, length);
2862 sendlength = sizeof(senddata) - 4;
2863 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2865 case CRYPTO_NOMATCH:
2871 memcpy(senddata, "\377\377\377\377", 4);
2872 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2875 case CRYPTO_DISCARD:
2878 memcpy(senddata, "\377\377\377\377", 4);
2879 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2883 case CRYPTO_REPLACE:
2884 string = senddata+4;
2885 length = sendlength;
2889 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2891 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2893 if(challenge[i].time > 0)
2894 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2896 if (besttime > challenge[i].time)
2897 besttime = challenge[best = i].time;
2899 // if we did not find an exact match, choose the oldest and
2900 // update address and string
2901 if (i == MAX_CHALLENGES)
2904 challenge[i].address = *peeraddress;
2905 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2909 // flood control: drop if requesting challenge too often
2910 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2913 challenge[i].time = realtime;
2914 // send the challenge
2915 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2916 response_len = strlen(response) + 1;
2917 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2918 NetConn_Write(mysocket, response, response_len, peeraddress);
2921 if (length > 8 && !memcmp(string, "connect\\", 8))
2923 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2927 if(crypto && crypto->authenticated)
2929 // no need to check challenge
2930 if(crypto_developer.integer)
2932 Con_Printf("%s connection to %s is being established: client is %s@%.*s, I am %.*s@%.*s\n",
2933 crypto->use_aes ? "Encrypted" : "Authenticated",
2935 crypto->client_idfp[0] ? crypto->client_idfp : "-",
2936 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
2937 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
2938 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
2944 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
2946 // validate the challenge
2947 for (i = 0;i < MAX_CHALLENGES;i++)
2948 if(challenge[i].time > 0)
2949 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
2951 // if the challenge is not recognized, drop the packet
2952 if (i == MAX_CHALLENGES)
2957 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
2958 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
2960 if(!(islocal || sv_public.integer > -2))
2962 if (developer_extra.integer)
2963 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
2964 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
2968 // check engine protocol
2969 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
2971 if (developer_extra.integer)
2972 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
2973 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
2977 // see if this is a duplicate connection request or a disconnected
2978 // client who is rejoining to the same client slot
2979 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2981 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
2983 // this is a known client...
2984 if(crypto && crypto->authenticated)
2986 // reject if changing key!
2987 if(client->netconnection->crypto.authenticated)
2990 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
2992 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
2994 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
2996 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
2999 if (developer_extra.integer)
3000 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3001 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3008 // reject if downgrading!
3009 if(client->netconnection->crypto.authenticated)
3011 if (developer_extra.integer)
3012 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3013 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3019 // client crashed and is coming back,
3020 // keep their stuff intact
3021 if (developer_extra.integer)
3022 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3023 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3024 if(crypto && crypto->authenticated)
3025 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
3026 SV_SendServerinfo(client);
3030 // client is still trying to connect,
3031 // so we send a duplicate reply
3032 if (developer_extra.integer)
3033 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3034 if(crypto && crypto->authenticated)
3035 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
3036 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3042 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3045 // find an empty client slot for this new client
3046 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3049 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3051 // allocated connection
3052 if (developer_extra.integer)
3053 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3054 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3055 // now set up the client
3056 if(crypto && crypto->authenticated)
3057 Crypto_ServerFinishInstance(&conn->crypto, crypto);
3058 SV_ConnectClient(clientnum, conn);
3059 NetConn_Heartbeat(1);
3064 // no empty slots found - server is full
3065 if (developer_extra.integer)
3066 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3067 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3071 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3073 const char *challenge = NULL;
3075 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3078 // If there was a challenge in the getinfo message
3079 if (length > 8 && string[7] == ' ')
3080 challenge = string + 8;
3082 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3084 if (developer_extra.integer)
3085 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3086 NetConn_WriteString(mysocket, response, peeraddress);
3090 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3092 const char *challenge = NULL;
3094 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3097 // If there was a challenge in the getinfo message
3098 if (length > 10 && string[9] == ' ')
3099 challenge = string + 10;
3101 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3103 if (developer_extra.integer)
3104 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3105 NetConn_WriteString(mysocket, response, peeraddress);
3109 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3111 char *password = string + 20;
3112 char *timeval = string + 37;
3113 char *s = strchr(timeval, ' ');
3114 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3115 const char *userlevel;
3117 if(rcon_secure.integer > 1)
3121 return true; // invalid packet
3124 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3125 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3128 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3130 char *password = string + 25;
3131 char *challenge = string + 42;
3132 char *s = strchr(challenge, ' ');
3133 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3134 const char *userlevel;
3136 return true; // invalid packet
3139 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3140 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3143 if (length >= 5 && !memcmp(string, "rcon ", 5))
3146 char *s = string + 5;
3147 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3150 if(rcon_secure.integer > 0)
3153 for (i = 0;!ISWHITESPACE(*s);s++)
3154 if (i < (int)sizeof(password) - 1)
3156 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3159 if (!ISWHITESPACE(password[0]))
3161 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3162 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3166 if (!strncmp(string, "extResponse ", 12))
3168 ++sv_net_extresponse_count;
3169 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3170 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3171 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3172 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3175 if (!strncmp(string, "ping", 4))
3177 if (developer_extra.integer)
3178 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3179 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3182 if (!strncmp(string, "ack", 3))
3184 // we may not have liked the packet, but it was a command packet, so
3185 // we're done processing this packet now
3188 // netquake control packets, supported for compatibility only, and only
3189 // when running game protocols that are normally served via this connection
3191 // (this protects more modern protocols against being used for
3192 // Quake packet flood Denial Of Service attacks)
3193 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)
3197 const char *protocolname;
3200 SZ_Clear(&sv_message);
3201 SZ_Write(&sv_message, data, length);
3202 MSG_BeginReading(&sv_message);
3203 c = MSG_ReadByte(&sv_message);
3207 if (developer_extra.integer)
3208 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3209 if(!(islocal || sv_public.integer > -2))
3211 if (developer_extra.integer)
3212 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3213 SZ_Clear(&sv_message);
3214 // save space for the header, filled in later
3215 MSG_WriteLong(&sv_message, 0);
3216 MSG_WriteByte(&sv_message, CCREP_REJECT);
3217 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3218 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3219 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3220 SZ_Clear(&sv_message);
3224 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3225 protocolnumber = MSG_ReadByte(&sv_message);
3226 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3228 if (developer_extra.integer)
3229 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3230 SZ_Clear(&sv_message);
3231 // save space for the header, filled in later
3232 MSG_WriteLong(&sv_message, 0);
3233 MSG_WriteByte(&sv_message, CCREP_REJECT);
3234 MSG_WriteString(&sv_message, "Incompatible version.\n");
3235 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3236 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3237 SZ_Clear(&sv_message);
3241 // see if this connect request comes from a known client
3242 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3244 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3246 // this is either a duplicate connection request
3247 // or coming back from a timeout
3248 // (if so, keep their stuff intact)
3250 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3251 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3253 if (developer_extra.integer)
3254 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3255 SZ_Clear(&sv_message);
3256 // save space for the header, filled in later
3257 MSG_WriteLong(&sv_message, 0);
3258 MSG_WriteByte(&sv_message, CCREP_REJECT);
3259 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3260 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3261 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3262 SZ_Clear(&sv_message);
3267 if (developer_extra.integer)
3268 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3269 SZ_Clear(&sv_message);
3270 // save space for the header, filled in later
3271 MSG_WriteLong(&sv_message, 0);
3272 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3273 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3274 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3275 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3276 SZ_Clear(&sv_message);
3278 // if client is already spawned, re-send the
3279 // serverinfo message as they'll need it to play
3281 SV_SendServerinfo(client);
3286 // this is a new client, check for connection flood
3287 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3290 // find a slot for the new client
3291 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3294 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3296 // connect to the client
3297 // everything is allocated, just fill in the details
3298 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3299 if (developer_extra.integer)
3300 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3301 // send back the info about the server connection
3302 SZ_Clear(&sv_message);
3303 // save space for the header, filled in later
3304 MSG_WriteLong(&sv_message, 0);
3305 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3306 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3307 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3308 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3309 SZ_Clear(&sv_message);
3310 // now set up the client struct
3311 SV_ConnectClient(clientnum, conn);
3312 NetConn_Heartbeat(1);
3317 if (developer_extra.integer)
3318 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3319 // no room; try to let player know
3320 SZ_Clear(&sv_message);
3321 // save space for the header, filled in later
3322 MSG_WriteLong(&sv_message, 0);
3323 MSG_WriteByte(&sv_message, CCREP_REJECT);
3324 MSG_WriteString(&sv_message, "Server is full.\n");
3325 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3326 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3327 SZ_Clear(&sv_message);
3329 case CCREQ_SERVER_INFO:
3330 if (developer_extra.integer)
3331 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3332 if(!(islocal || sv_public.integer > -1))
3335 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3338 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3341 char myaddressstring[128];
3342 if (developer_extra.integer)
3343 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3344 SZ_Clear(&sv_message);
3345 // save space for the header, filled in later
3346 MSG_WriteLong(&sv_message, 0);
3347 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3348 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3349 MSG_WriteString(&sv_message, myaddressstring);
3350 MSG_WriteString(&sv_message, hostname.string);
3351 MSG_WriteString(&sv_message, sv.name);
3352 // How many clients are there?
3353 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3354 if (svs.clients[i].active)
3356 MSG_WriteByte(&sv_message, numclients);
3357 MSG_WriteByte(&sv_message, svs.maxclients);
3358 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3359 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3360 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3361 SZ_Clear(&sv_message);
3364 case CCREQ_PLAYER_INFO:
3365 if (developer_extra.integer)
3366 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3367 if(!(islocal || sv_public.integer > -1))
3370 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3375 int playerNumber, activeNumber, clientNumber;
3378 playerNumber = MSG_ReadByte(&sv_message);
3380 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3381 if (client->active && ++activeNumber == playerNumber)
3383 if (clientNumber != svs.maxclients)
3385 SZ_Clear(&sv_message);
3386 // save space for the header, filled in later
3387 MSG_WriteLong(&sv_message, 0);
3388 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3389 MSG_WriteByte(&sv_message, playerNumber);
3390 MSG_WriteString(&sv_message, client->name);
3391 MSG_WriteLong(&sv_message, client->colors);
3392 MSG_WriteLong(&sv_message, client->frags);
3393 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3394 if(sv_status_privacy.integer)
3395 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3397 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3398 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3399 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3400 SZ_Clear(&sv_message);
3404 case CCREQ_RULE_INFO:
3405 if (developer_extra.integer)
3406 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3407 if(!(islocal || sv_public.integer > -1))
3410 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3417 // find the search start location
3418 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3419 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3421 // send the response
3422 SZ_Clear(&sv_message);
3423 // save space for the header, filled in later
3424 MSG_WriteLong(&sv_message, 0);
3425 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3428 MSG_WriteString(&sv_message, var->name);
3429 MSG_WriteString(&sv_message, var->string);
3431 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3432 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3433 SZ_Clear(&sv_message);
3437 if (developer_extra.integer)
3438 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3439 if (sv.active && !rcon_secure.integer)
3441 char password[2048];
3445 const char *userlevel;
3446 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3447 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3449 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3450 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3451 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3458 SZ_Clear(&sv_message);
3459 // we may not have liked the packet, but it was a valid control
3460 // packet, so we're done processing this packet now
3465 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3467 SV_ReadClientMessage();
3474 void NetConn_ServerFrame(void)
3477 lhnetaddress_t peeraddress;
3478 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3479 for (i = 0;i < sv_numsockets;i++)
3480 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3481 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3482 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3484 // never timeout loopback connections
3485 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3487 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3488 SV_DropClient(false);
3493 void NetConn_SleepMicroseconds(int microseconds)
3495 LHNET_SleepUntilPacket_Microseconds(microseconds);
3498 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3502 lhnetaddress_t masteraddress;
3503 lhnetaddress_t broadcastaddress;
3506 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3509 // 26000 is the default quake server port, servers on other ports will not
3511 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3512 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3516 for (i = 0;i < cl_numsockets;i++)
3520 const char *cmdname, *extraoptions;
3521 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3523 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3525 // search LAN for Quake servers
3526 SZ_Clear(&cl_message);
3527 // save space for the header, filled in later
3528 MSG_WriteLong(&cl_message, 0);
3529 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3530 MSG_WriteString(&cl_message, "QUAKE");
3531 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3532 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3533 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3534 SZ_Clear(&cl_message);
3536 // search LAN for DarkPlaces servers
3537 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3540 // build the getservers message to send to the dpmaster master servers
3541 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3543 cmdname = "getserversExt";
3544 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3548 cmdname = "getservers";
3551 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamename, NET_PROTOCOL_VERSION, extraoptions);
3554 for (masternum = 0;sv_masters[masternum].name;masternum++)
3556 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3559 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3563 // search favorite servers
3564 for(j = 0; j < nFavorites; ++j)
3566 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3568 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3569 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3576 // only query QuakeWorld servers when the user wants to
3579 for (i = 0;i < cl_numsockets;i++)
3583 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3585 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3587 // search LAN for QuakeWorld servers
3588 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3590 // build the getservers message to send to the qwmaster master servers
3591 // note this has no -1 prefix, and the trailing nul byte is sent
3592 dpsnprintf(request, sizeof(request), "c\n");
3596 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3598 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3601 if (m_state != m_slist)
3604 char lookupstring[128];
3605 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3606 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3611 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3615 // search favorite servers
3616 for(j = 0; j < nFavorites; ++j)
3618 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3620 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3622 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3623 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3630 if (!masterquerycount)
3632 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3634 M_Update_Return_Reason("No network");
3639 void NetConn_Heartbeat(int priority)
3641 lhnetaddress_t masteraddress;
3643 lhnetsocket_t *mysocket;
3645 // if it's a state change (client connected), limit next heartbeat to no
3646 // more than 30 sec in the future
3647 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3648 nextheartbeattime = realtime + 30.0;
3650 // limit heartbeatperiod to 30 to 270 second range,
3651 // lower limit is to avoid abusing master servers with excess traffic,
3652 // upper limit is to avoid timing out on the master server (which uses
3654 if (sv_heartbeatperiod.value < 30)
3655 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3656 if (sv_heartbeatperiod.value > 270)
3657 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3659 // make advertising optional and don't advertise singleplayer games, and
3660 // only send a heartbeat as often as the admin wants
3661 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3663 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3664 for (masternum = 0;sv_masters[masternum].name;masternum++)
3665 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3666 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3670 static void Net_Heartbeat_f(void)
3673 NetConn_Heartbeat(2);
3675 Con_Print("No server running, can not heartbeat to master server.\n");
3678 static void PrintStats(netconn_t *conn)
3680 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3681 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3683 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3684 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3685 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3686 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3687 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3688 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3689 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3690 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3691 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3692 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3695 void Net_Stats_f(void)
3698 Con_Print("connections =\n");
3699 for (conn = netconn_list;conn;conn = conn->next)
3703 void Net_Refresh_f(void)
3706 if (m_state != m_slist) {
3708 Con_Print("Sending new requests to master servers\n");
3709 ServerList_QueryList(false, true, false, true);
3710 Con_Print("Listening for replies...\n");
3713 ServerList_QueryList(false, true, false, false);
3717 void Net_Slist_f(void)
3719 ServerList_ResetMasks();
3720 serverlist_sortbyfield = SLIF_PING;
3721 serverlist_sortflags = 0;
3723 if (m_state != m_slist) {
3725 Con_Print("Sending requests to master servers\n");
3726 ServerList_QueryList(true, true, false, true);
3727 Con_Print("Listening for replies...\n");
3730 ServerList_QueryList(true, true, false, false);
3734 void Net_SlistQW_f(void)
3736 ServerList_ResetMasks();
3737 serverlist_sortbyfield = SLIF_PING;
3738 serverlist_sortflags = 0;
3740 if (m_state != m_slist) {
3742 Con_Print("Sending requests to master servers\n");
3743 ServerList_QueryList(true, false, true, true);
3744 serverlist_consoleoutput = true;
3745 Con_Print("Listening for replies...\n");
3748 ServerList_QueryList(true, false, true, false);
3752 void NetConn_Init(void)
3755 lhnetaddress_t tempaddress;
3756 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3757 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3758 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3759 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3760 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3761 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3762 Cvar_RegisterVariable(&net_test);
3763 Cvar_RegisterVariable(&net_usesizelimit);
3764 Cvar_RegisterVariable(&net_burstreserve);
3765 Cvar_RegisterVariable(&rcon_restricted_password);
3766 Cvar_RegisterVariable(&rcon_restricted_commands);
3767 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3768 Cvar_RegisterVariable(&net_slist_queriespersecond);
3769 Cvar_RegisterVariable(&net_slist_queriesperframe);
3770 Cvar_RegisterVariable(&net_slist_timeout);
3771 Cvar_RegisterVariable(&net_slist_maxtries);
3772 Cvar_RegisterVariable(&net_slist_favorites);
3773 Cvar_RegisterVariable(&net_slist_pause);
3774 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3775 Cvar_RegisterVariable(&net_tos_dscp);
3776 Cvar_RegisterVariable(&net_messagetimeout);
3777 Cvar_RegisterVariable(&net_connecttimeout);
3778 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3779 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3780 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3781 Cvar_RegisterVariable(&cl_netlocalping);
3782 Cvar_RegisterVariable(&cl_netpacketloss_send);
3783 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3784 Cvar_RegisterVariable(&hostname);
3785 Cvar_RegisterVariable(&developer_networking);
3786 Cvar_RegisterVariable(&cl_netport);
3787 Cvar_RegisterVariable(&sv_netport);
3788 Cvar_RegisterVariable(&net_address);
3789 Cvar_RegisterVariable(&net_address_ipv6);
3790 Cvar_RegisterVariable(&sv_public);
3791 Cvar_RegisterVariable(&sv_public_rejectreason);
3792 Cvar_RegisterVariable(&sv_heartbeatperiod);
3793 for (i = 0;sv_masters[i].name;i++)
3794 Cvar_RegisterVariable(&sv_masters[i]);
3795 Cvar_RegisterVariable(&gameversion);
3796 Cvar_RegisterVariable(&gameversion_min);
3797 Cvar_RegisterVariable(&gameversion_max);
3798 // 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.
3799 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3801 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3803 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3804 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3807 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3809 // 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
3810 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3812 i = atoi(com_argv[i + 1]);
3813 if (i >= 0 && i < 65536)
3815 Con_Printf("-port option used, setting port cvar to %i\n", i);
3816 Cvar_SetValueQuick(&sv_netport, i);
3819 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3823 cl_message.data = cl_message_buf;
3824 cl_message.maxsize = sizeof(cl_message_buf);
3825 cl_message.cursize = 0;
3826 sv_message.data = sv_message_buf;
3827 sv_message.maxsize = sizeof(sv_message_buf);
3828 sv_message.cursize = 0;
3830 if (Thread_HasThreads())
3831 netconn_mutex = Thread_CreateMutex();
3834 void NetConn_Shutdown(void)
3836 NetConn_CloseClientPorts();
3837 NetConn_CloseServerPorts();
3840 Thread_DestroyMutex(netconn_mutex);
3841 netconn_mutex = NULL;