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;
1463 M_Update_Return_Reason("");
1464 // the connection request succeeded, stop current connection and set up a new connection
1466 // if we're connecting to a remote server, shut down any local server
1467 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1469 SV_LockThreadMutex();
1470 Host_ShutdownServer ();
1471 SV_UnlockThreadMutex();
1473 // allocate a net connection to keep track of things
1474 cls.netcon = NetConn_Open(mysocket, peeraddress);
1475 crypto = &cls.crypto;
1476 if(crypto && crypto->authenticated)
1478 Crypto_ServerFinishInstance(&cls.netcon->crypto, crypto);
1479 Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
1480 crypto->use_aes ? "Encrypted" : "Authenticated",
1481 cls.netcon->address,
1482 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1483 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1484 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1485 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1488 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1489 key_dest = key_game;
1491 cls.demonum = -1; // not in the demo loop now
1492 cls.state = ca_connected;
1493 cls.signon = 0; // need all the signon messages before playing
1494 cls.protocol = initialprotocol;
1495 // reset move sequence numbering on this new connection
1496 cls.servermovesequence = 0;
1497 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1498 Cmd_ForwardStringToServer("new");
1499 if (cls.protocol == PROTOCOL_QUAKE)
1501 // write a keepalive (clc_nop) as it seems to greatly improve the
1502 // chances of connecting to a netquake server
1504 unsigned char buf[4];
1505 memset(&msg, 0, sizeof(msg));
1507 msg.maxsize = sizeof(buf);
1508 MSG_WriteChar(&msg, clc_nop);
1509 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1513 int NetConn_IsLocalGame(void)
1515 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1520 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1524 serverlist_entry_t *entry = NULL;
1526 // search the cache for this server and update it
1527 for (n = 0;n < serverlist_cachecount;n++) {
1528 entry = &serverlist_cache[ n ];
1529 if (!strcmp(addressstring, entry->info.cname))
1533 if (n == serverlist_cachecount)
1535 // LAN search doesnt require an answer from the master server so we wont
1536 // know the ping nor will it be initialized already...
1539 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1542 if (serverlist_maxcachecount <= serverlist_cachecount)
1544 serverlist_maxcachecount += 64;
1545 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1547 entry = &serverlist_cache[n];
1549 memset(entry, 0, sizeof(*entry));
1550 // store the data the engine cares about (address and ping)
1551 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1552 entry->info.ping = 100000;
1553 entry->querytime = realtime;
1554 // if not in the slist menu we should print the server to console
1555 if (serverlist_consoleoutput)
1556 Con_Printf("querying %s\n", addressstring);
1557 ++serverlist_cachecount;
1559 // if this is the first reply from this server, count it as having replied
1560 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1561 pingtime = bound(0, pingtime, 9999);
1562 if (entry->query == SQS_REFRESHING) {
1563 entry->info.ping = pingtime;
1564 entry->query = SQS_QUERIED;
1566 // convert to unsigned to catch the -1
1567 // 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]
1568 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1572 // other server info is updated by the caller
1576 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1578 serverlist_entry_t *entry = &serverlist_cache[n];
1579 serverlist_info_t *info = &entry->info;
1580 // update description strings for engine menu and console output
1581 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);
1582 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1584 info->gameversion != gameversion.integer
1587 gameversion_min.integer >= 0 // min/max range set by user/mod?
1588 && gameversion_max.integer >= 0
1589 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1590 && gameversion_max.integer >= info->gameversion
1593 info->mod, info->map);
1594 if (entry->query == SQS_QUERIED)
1596 if(!serverlist_paused)
1597 ServerList_ViewList_Remove(entry);
1599 // if not in the slist menu we should print the server to console (if wanted)
1600 else if( serverlist_consoleoutput )
1601 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1602 // and finally, update the view set
1603 if(!serverlist_paused)
1604 ServerList_ViewList_Insert( entry );
1605 // update the entry's state
1606 serverlist_cache[n].query = SQS_QUERIED;
1609 // returns true, if it's sensible to continue the processing
1610 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1612 serverlist_entry_t *entry;
1614 // ignore the rest of the message if the serverlist is full
1615 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1617 // also ignore it if we have already queried it (other master server response)
1618 for( n = 0 ; n < serverlist_cachecount ; n++ )
1619 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1622 if( n < serverlist_cachecount ) {
1623 // the entry has already been queried once or
1627 if (serverlist_maxcachecount <= n)
1629 serverlist_maxcachecount += 64;
1630 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1633 entry = &serverlist_cache[n];
1635 memset(entry, 0, sizeof(*entry));
1636 entry->protocol = protocol;
1637 // store the data the engine cares about (address and ping)
1638 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1640 entry->info.isfavorite = isfavorite;
1642 // no, then reset the ping right away
1643 entry->info.ping = -1;
1644 // we also want to increase the serverlist_cachecount then
1645 serverlist_cachecount++;
1648 entry->query = SQS_QUERYING;
1653 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1656 if (serverlist_consoleoutput)
1657 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1660 char ipstring [128];
1663 if (data[0] == '\\')
1665 unsigned short port = data[5] * 256 + data[6];
1667 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1668 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1670 // move on to next address in packet
1675 else if (data[0] == '/' && isextended && length >= 19)
1677 unsigned short port = data[17] * 256 + data[18];
1685 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1687 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1690 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1691 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1692 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1698 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1699 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1700 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1705 // move on to next address in packet
1711 Con_Print("Error while parsing the server list\n");
1715 if (serverlist_consoleoutput && developer_networking.integer)
1716 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1718 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1724 // begin or resume serverlist queries
1725 serverlist_querysleep = false;
1726 serverlist_querywaittime = realtime + 3;
1729 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1731 qboolean fromserver;
1734 char *string, addressstring2[128], ipstring[32];
1735 char stringbuf[16384];
1736 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1738 char infostringvalue[MAX_INPUTLINE];
1741 // quakeworld ingame packet
1742 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1744 // convert the address to a string incase we need it
1745 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1747 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1749 // received a command string - strip off the packaging and put it
1750 // into our string buffer with NULL termination
1753 length = min(length, (int)sizeof(stringbuf) - 1);
1754 memcpy(stringbuf, data, length);
1755 stringbuf[length] = 0;
1758 if (developer_networking.integer)
1760 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1761 Com_HexDumpToConsole(data, length);
1764 sendlength = sizeof(senddata) - 4;
1765 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1767 case CRYPTO_NOMATCH:
1773 memcpy(senddata, "\377\377\377\377", 4);
1774 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1777 case CRYPTO_DISCARD:
1780 memcpy(senddata, "\377\377\377\377", 4);
1781 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1785 case CRYPTO_REPLACE:
1786 string = senddata+4;
1787 length = sendlength;
1791 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1794 for (j = 0;j < MAX_RCONS;j++)
1796 // note: this value from i is used outside the loop too...
1797 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1798 if(cls.rcon_commands[i][0])
1799 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1808 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1809 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1811 e = strchr(rcon_password.string, ' ');
1812 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1814 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
1818 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1819 NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
1820 cls.rcon_commands[i][0] = 0;
1823 for (k = 0;k < MAX_RCONS;k++)
1824 if(cls.rcon_commands[k][0])
1825 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1830 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1831 // extend the timeout on other requests as we asked for a challenge
1832 for (l = 0;l < MAX_RCONS;l++)
1833 if(cls.rcon_commands[l][0])
1834 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1835 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1838 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1842 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1844 // darkplaces or quake3
1845 char protocolnames[1400];
1846 Protocol_Names(protocolnames, sizeof(protocolnames));
1847 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1848 M_Update_Return_Reason("Got challenge response");
1849 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1850 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1851 // TODO: add userinfo stuff here instead of using NQ commands?
1852 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);
1855 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1857 // darkplaces or quake3
1858 M_Update_Return_Reason("Accepted");
1859 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1862 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1864 char rejectreason[128];
1865 cls.connect_trying = false;
1867 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1868 memcpy(rejectreason, string, length);
1869 rejectreason[length] = 0;
1870 M_Update_Return_Reason(rejectreason);
1873 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1875 serverlist_info_t *info;
1880 // search the cache for this server and update it
1881 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1885 info = &serverlist_cache[n].info;
1890 info->qcstatus[0] = 0;
1891 info->players[0] = 0;
1892 info->protocol = -1;
1893 info->numplayers = 0;
1895 info->maxplayers = 0;
1896 info->gameversion = 0;
1898 p = strchr(string, '\n');
1901 *p = 0; // cut off the string there
1905 Con_Printf("statusResponse without players block?\n");
1907 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1908 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1909 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1910 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1911 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1912 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1913 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1914 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1915 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1916 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1917 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1918 info->numhumans = info->numplayers - max(0, info->numbots);
1919 info->freeslots = info->maxplayers - info->numplayers;
1921 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1925 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1927 serverlist_info_t *info;
1931 // search the cache for this server and update it
1932 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1936 info = &serverlist_cache[n].info;
1941 info->qcstatus[0] = 0;
1942 info->players[0] = 0;
1943 info->protocol = -1;
1944 info->numplayers = 0;
1946 info->maxplayers = 0;
1947 info->gameversion = 0;
1949 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1950 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1951 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1952 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1953 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1954 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1955 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1956 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1957 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1958 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1959 info->numhumans = info->numplayers - max(0, info->numbots);
1960 info->freeslots = info->maxplayers - info->numplayers;
1962 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1966 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1968 // Extract the IP addresses
1971 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
1974 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1976 // Extract the IP addresses
1979 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
1982 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1984 // Extract the IP addresses
1988 if (serverlist_consoleoutput)
1989 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
1990 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
1992 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
1993 if (serverlist_consoleoutput && developer_networking.integer)
1994 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
1996 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2000 // move on to next address in packet
2004 // begin or resume serverlist queries
2005 serverlist_querysleep = false;
2006 serverlist_querywaittime = realtime + 3;
2009 if (!strncmp(string, "extResponse ", 12))
2011 ++cl_net_extresponse_count;
2012 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2013 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2014 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2015 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2018 if (!strncmp(string, "ping", 4))
2020 if (developer_extra.integer)
2021 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2022 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2025 if (!strncmp(string, "ack", 3))
2027 // QuakeWorld compatibility
2028 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2030 // challenge message
2031 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2032 M_Update_Return_Reason("Got QuakeWorld challenge response");
2033 cls.qw_qport = qport.integer;
2034 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2035 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2036 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);
2039 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2042 M_Update_Return_Reason("QuakeWorld Accepted");
2043 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2046 if (length > 2 && !memcmp(string, "n\\", 2))
2048 serverlist_info_t *info;
2052 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2053 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2056 // search the cache for this server and update it
2057 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2061 info = &serverlist_cache[n].info;
2062 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2063 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2064 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2065 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2067 info->numplayers = 0; // updated below
2068 info->numhumans = 0; // updated below
2069 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2070 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2072 // count active players on server
2073 // (we could gather more info, but we're just after the number)
2074 s = strchr(string, '\n');
2078 while (s < string + length)
2080 for (;s < string + length && *s != '\n';s++)
2082 if (s >= string + length)
2090 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2094 if (string[0] == 'n')
2097 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2099 // we may not have liked the packet, but it was a command packet, so
2100 // we're done processing this packet now
2103 // quakeworld ingame packet
2104 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2107 CL_ParseServerMessage();
2110 // netquake control packets, supported for compatibility only
2111 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2114 serverlist_info_t *info;
2118 SZ_Clear(&cl_message);
2119 SZ_Write(&cl_message, data, length);
2120 MSG_BeginReading(&cl_message);
2121 c = MSG_ReadByte(&cl_message);
2125 if (developer_extra.integer)
2126 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2127 if (cls.connect_trying)
2129 lhnetaddress_t clientportaddress;
2130 clientportaddress = *peeraddress;
2131 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2132 // extra ProQuake stuff
2134 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2136 cls.proquake_servermod = 0;
2138 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2140 cls.proquake_serverversion = 0;
2142 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2144 cls.proquake_serverflags = 0;
2145 if (cls.proquake_servermod == 1)
2146 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2147 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2148 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2149 M_Update_Return_Reason("Accepted");
2150 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2154 if (developer_extra.integer)
2155 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2156 cls.connect_trying = false;
2157 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2159 case CCREP_SERVER_INFO:
2160 if (developer_extra.integer)
2161 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2162 // LordHavoc: because the quake server may report weird addresses
2163 // we just ignore it and keep the real address
2164 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2165 // search the cache for this server and update it
2166 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2170 info = &serverlist_cache[n].info;
2171 strlcpy(info->game, "Quake", sizeof(info->game));
2172 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2173 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2174 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2175 info->numplayers = MSG_ReadByte(&cl_message);
2176 info->maxplayers = MSG_ReadByte(&cl_message);
2177 info->protocol = MSG_ReadByte(&cl_message);
2179 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2182 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2183 if (developer_extra.integer)
2184 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2186 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2188 case CCREP_PLAYER_INFO:
2189 // we got a CCREP_PLAYER_INFO??
2190 //if (developer_extra.integer)
2191 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2193 case CCREP_RULE_INFO:
2194 // we got a CCREP_RULE_INFO??
2195 //if (developer_extra.integer)
2196 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2201 SZ_Clear(&cl_message);
2202 // we may not have liked the packet, but it was a valid control
2203 // packet, so we're done processing this packet now
2207 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2208 CL_ParseServerMessage();
2212 void NetConn_QueryQueueFrame(void)
2218 static double querycounter = 0;
2220 if(!net_slist_pause.integer && serverlist_paused)
2221 ServerList_RebuildViewList();
2222 serverlist_paused = net_slist_pause.integer != 0;
2224 if (serverlist_querysleep)
2227 // apply a cool down time after master server replies,
2228 // to avoid messing up the ping times on the servers
2229 if (serverlist_querywaittime > realtime)
2232 // each time querycounter reaches 1.0 issue a query
2233 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2234 maxqueries = (int)querycounter;
2235 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2236 querycounter -= maxqueries;
2238 if( maxqueries == 0 ) {
2242 // scan serverlist and issue queries as needed
2243 serverlist_querysleep = true;
2245 timeouttime = realtime - net_slist_timeout.value;
2246 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2248 serverlist_entry_t *entry = &serverlist_cache[ index ];
2249 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2254 serverlist_querysleep = false;
2255 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2260 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2262 lhnetaddress_t address;
2265 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2266 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2268 for (socket = 0; socket < cl_numsockets ; socket++)
2269 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2273 for (socket = 0; socket < cl_numsockets ; socket++)
2274 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2277 // update the entry fields
2278 entry->querytime = realtime;
2279 entry->querycounter++;
2281 // if not in the slist menu we should print the server to console
2282 if (serverlist_consoleoutput)
2283 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2289 // have we tried to refresh this server?
2290 if( entry->query == SQS_REFRESHING ) {
2291 // yes, so update the reply count (since its not responding anymore)
2293 if(!serverlist_paused)
2294 ServerList_ViewList_Remove(entry);
2296 entry->query = SQS_TIMEDOUT;
2301 void NetConn_ClientFrame(void)
2304 lhnetaddress_t peeraddress;
2305 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2306 NetConn_UpdateSockets();
2307 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2309 if (cls.connect_remainingtries == 0)
2310 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2311 cls.connect_nextsendtime = realtime + 1;
2312 cls.connect_remainingtries--;
2313 if (cls.connect_remainingtries <= -10)
2315 cls.connect_trying = false;
2316 M_Update_Return_Reason("Connect: Failed");
2319 // try challenge first (newer DP server or QW)
2320 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2321 // then try netquake as a fallback (old server, or netquake)
2322 SZ_Clear(&cl_message);
2323 // save space for the header, filled in later
2324 MSG_WriteLong(&cl_message, 0);
2325 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2326 MSG_WriteString(&cl_message, "QUAKE");
2327 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2328 // extended proquake stuff
2329 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2330 // this version matches ProQuake 3.40, the first version to support
2331 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2332 // higher clients, so we pretend we are that version...
2333 MSG_WriteByte(&cl_message, 34); // version * 10
2334 MSG_WriteByte(&cl_message, 0); // flags
2335 MSG_WriteLong(&cl_message, 0); // password
2336 // write the packetsize now...
2337 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2338 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2339 SZ_Clear(&cl_message);
2341 for (i = 0;i < cl_numsockets;i++)
2343 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2345 // R_TimeReport("clientreadnetwork");
2346 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2347 // R_TimeReport("clientparsepacket");
2350 NetConn_QueryQueueFrame();
2351 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2353 Con_Print("Connection timed out\n");
2355 SV_LockThreadMutex();
2356 Host_ShutdownServer ();
2357 SV_UnlockThreadMutex();
2361 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2365 for (i = 0;i < bufferlength - 1;i++)
2369 c = rand () % (127 - 33) + 33;
2370 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2376 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2377 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2379 prvm_prog_t *prog = SVVM_prog;
2381 unsigned int nb_clients = 0, nb_bots = 0, i;
2384 const char *crypto_idstring;
2387 // How many clients are there?
2388 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2390 if (svs.clients[i].active)
2393 if (!svs.clients[i].netconnection)
2399 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2405 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2406 if(*q != '\\' && *q != '\n')
2411 /// \TODO: we should add more information for the full status string
2412 crypto_idstring = Crypto_GetInfoResponseDataString();
2413 length = dpsnprintf(out_msg, out_size,
2414 "\377\377\377\377%s\x0A"
2415 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2416 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2421 fullstatus ? "statusResponse" : "infoResponse",
2422 gamename, com_modname, gameversion.integer, svs.maxclients,
2423 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2424 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2425 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2426 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2427 fullstatus ? "\n" : "");
2429 // Make sure it fits in the buffer
2439 savelength = length;
2441 ptr = out_msg + length;
2442 left = (int)out_size - length;
2444 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2446 client_t *cl = &svs.clients[i];
2449 int nameind, cleanind, pingvalue;
2451 char cleanname [sizeof(cl->name)];
2455 // Remove all characters '"' and '\' in the player name
2460 curchar = cl->name[nameind++];
2461 if (curchar != '"' && curchar != '\\')
2463 cleanname[cleanind++] = curchar;
2464 if (cleanind == sizeof(cleanname) - 1)
2467 } while (curchar != '\0');
2468 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2470 pingvalue = (int)(cl->ping * 1000.0f);
2471 if(cl->netconnection)
2472 pingvalue = bound(1, pingvalue, 9999);
2477 ed = PRVM_EDICT_NUM(i + 1);
2478 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2484 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2485 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2490 if ((gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) && (teamplay.integer > 0))
2492 if(cl->frags == -666) // spectator
2493 strlcpy(teambuf, " 0", sizeof(teambuf));
2494 else if(cl->colors == 0x44) // red team
2495 strlcpy(teambuf, " 1", sizeof(teambuf));
2496 else if(cl->colors == 0xDD) // blue team
2497 strlcpy(teambuf, " 2", sizeof(teambuf));
2498 else if(cl->colors == 0xCC) // yellow team
2499 strlcpy(teambuf, " 3", sizeof(teambuf));
2500 else if(cl->colors == 0x99) // pink team
2501 strlcpy(teambuf, " 4", sizeof(teambuf));
2503 strlcpy(teambuf, " 0", sizeof(teambuf));
2508 // note: team number is inserted according to SoF2 protocol
2510 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2516 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2525 // turn it into an infoResponse!
2526 out_msg[savelength] = 0;
2527 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2528 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2543 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2545 size_t floodslotnum, bestfloodslotnum;
2546 double bestfloodtime;
2547 lhnetaddress_t noportpeeraddress;
2548 // see if this is a connect flood
2549 noportpeeraddress = *peeraddress;
2550 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2551 bestfloodslotnum = 0;
2552 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2553 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2555 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2557 bestfloodtime = floodlist[floodslotnum].lasttime;
2558 bestfloodslotnum = floodslotnum;
2560 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2562 // this address matches an ongoing flood address
2563 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2567 // renew the ban on this address so it does not expire
2568 // until the flood has subsided
2569 floodlist[floodslotnum].lasttime = realtime;
2571 //Con_Printf("Flood detected!\n");
2574 // the flood appears to have subsided, so allow this
2575 bestfloodslotnum = floodslotnum; // reuse the same slot
2579 // begin a new timeout on this address
2580 floodlist[bestfloodslotnum].address = noportpeeraddress;
2581 floodlist[bestfloodslotnum].lasttime = realtime;
2582 //Con_Printf("Flood detection initiated!\n");
2586 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2588 size_t floodslotnum;
2589 lhnetaddress_t noportpeeraddress;
2590 // see if this is a connect flood
2591 noportpeeraddress = *peeraddress;
2592 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2593 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2595 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2597 // this address matches an ongoing flood address
2599 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2600 floodlist[floodslotnum].lasttime = 0;
2601 //Con_Printf("Flood cleared!\n");
2606 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2608 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2613 t1 = (long) time(NULL);
2614 t2 = strtol(s, NULL, 0);
2615 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2618 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2621 return !memcmp(mdfourbuf, hash, 16);
2624 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2629 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2632 // validate the challenge
2633 for (i = 0;i < MAX_CHALLENGES;i++)
2634 if(challenge[i].time > 0)
2635 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2637 // if the challenge is not recognized, drop the packet
2638 if (i == MAX_CHALLENGES)
2641 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2644 if(memcmp(mdfourbuf, hash, 16))
2647 // unmark challenge to prevent replay attacks
2648 challenge[i].time = 0;
2653 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2655 return !strcmp(password, hash);
2658 /// returns a string describing the user level, or NULL for auth failure
2659 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)
2661 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2662 static char buf[MAX_INPUTLINE];
2664 qboolean restricted = false;
2665 qboolean have_usernames = false;
2668 userpass_start = rcon_password.string;
2669 while((userpass_end = strchr(userpass_start, ' ')))
2671 have_usernames = true;
2672 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2674 if(comparator(peeraddress, buf, password, cs, cslen))
2676 userpass_start = userpass_end + 1;
2678 if(userpass_start[0])
2680 userpass_end = userpass_start + strlen(userpass_start);
2681 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2686 have_usernames = false;
2687 userpass_start = rcon_restricted_password.string;
2688 while((userpass_end = strchr(userpass_start, ' ')))
2690 have_usernames = true;
2691 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2693 if(comparator(peeraddress, buf, password, cs, cslen))
2695 userpass_start = userpass_end + 1;
2697 if(userpass_start[0])
2699 userpass_end = userpass_start + strlen(userpass_start);
2700 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2704 return NULL; // DENIED
2707 for(text = s; text != endpos; ++text)
2708 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2709 return NULL; // block possible exploits against the parser/alias expansion
2713 size_t l = strlen(s);
2716 hasquotes = (strchr(s, '"') != NULL);
2717 // sorry, we can't allow these substrings in wildcard expressions,
2718 // as they can mess with the argument counts
2719 text = rcon_restricted_commands.string;
2720 while(COM_ParseToken_Console(&text))
2722 // com_token now contains a pattern to check for...
2723 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2726 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2729 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2731 if(!strcmp(com_token, s))
2734 else // single-arg expression? must match the beginning of the command
2736 if(!strcmp(com_token, s))
2738 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2742 // if we got here, nothing matched!
2750 userpass_startpass = strchr(userpass_start, ':');
2751 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2752 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2754 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2757 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2761 // looks like a legitimate rcon command with the correct password
2762 const char *s_ptr = s;
2763 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2764 while(s_ptr != endpos)
2766 size_t l = strlen(s_ptr);
2768 Con_Printf(" %s;", s_ptr);
2773 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2774 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2777 size_t l = strlen(s);
2780 client_t *host_client_save = host_client;
2781 Cmd_ExecuteString(s, src_command, true);
2782 host_client = host_client_save;
2783 // in case it is a command that changes host_client (like restart)
2787 Con_Rcon_Redirect_End();
2791 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2795 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2797 int i, ret, clientnum, best;
2800 char *s, *string, response[1400], addressstring2[128];
2801 static char stringbuf[16384]; // server only
2802 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2803 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2804 size_t sendlength, response_len;
2805 char infostringvalue[MAX_INPUTLINE];
2811 // convert the address to a string incase we need it
2812 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2814 // see if we can identify the sender as a local player
2815 // (this is necessary for rcon to send a reliable reply if the client is
2816 // actually on the server, not sending remotely)
2817 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2818 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2820 if (i == svs.maxclients)
2823 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2825 // received a command string - strip off the packaging and put it
2826 // into our string buffer with NULL termination
2829 length = min(length, (int)sizeof(stringbuf) - 1);
2830 memcpy(stringbuf, data, length);
2831 stringbuf[length] = 0;
2834 if (developer_extra.integer)
2836 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2837 Com_HexDumpToConsole(data, length);
2840 sendlength = sizeof(senddata) - 4;
2841 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2843 case CRYPTO_NOMATCH:
2849 memcpy(senddata, "\377\377\377\377", 4);
2850 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2853 case CRYPTO_DISCARD:
2856 memcpy(senddata, "\377\377\377\377", 4);
2857 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2861 case CRYPTO_REPLACE:
2862 string = senddata+4;
2863 length = sendlength;
2867 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2869 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2871 if(challenge[i].time > 0)
2872 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2874 if (besttime > challenge[i].time)
2875 besttime = challenge[best = i].time;
2877 // if we did not find an exact match, choose the oldest and
2878 // update address and string
2879 if (i == MAX_CHALLENGES)
2882 challenge[i].address = *peeraddress;
2883 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2887 // flood control: drop if requesting challenge too often
2888 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2891 challenge[i].time = realtime;
2892 // send the challenge
2893 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2894 response_len = strlen(response) + 1;
2895 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2896 NetConn_Write(mysocket, response, response_len, peeraddress);
2899 if (length > 8 && !memcmp(string, "connect\\", 8))
2901 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2905 if(crypto && crypto->authenticated)
2907 // no need to check challenge
2908 if(crypto_developer.integer)
2910 Con_Printf("%s connection to %s is being established: client is %s@%.*s, I am %.*s@%.*s\n",
2911 crypto->use_aes ? "Encrypted" : "Authenticated",
2913 crypto->client_idfp[0] ? crypto->client_idfp : "-",
2914 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
2915 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
2916 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
2922 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
2924 // validate the challenge
2925 for (i = 0;i < MAX_CHALLENGES;i++)
2926 if(challenge[i].time > 0)
2927 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
2929 // if the challenge is not recognized, drop the packet
2930 if (i == MAX_CHALLENGES)
2935 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
2936 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
2938 if(!(islocal || sv_public.integer > -2))
2940 if (developer_extra.integer)
2941 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
2942 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
2946 // check engine protocol
2947 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
2949 if (developer_extra.integer)
2950 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
2951 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
2955 // see if this is a duplicate connection request or a disconnected
2956 // client who is rejoining to the same client slot
2957 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2959 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
2961 // this is a known client...
2962 if(crypto && crypto->authenticated)
2964 // reject if changing key!
2965 if(client->netconnection->crypto.authenticated)
2968 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
2970 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
2972 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
2974 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
2977 if (developer_extra.integer)
2978 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
2979 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
2986 // reject if downgrading!
2987 if(client->netconnection->crypto.authenticated)
2989 if (developer_extra.integer)
2990 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
2991 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
2997 // client crashed and is coming back,
2998 // keep their stuff intact
2999 if (developer_extra.integer)
3000 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3001 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3002 if(crypto && crypto->authenticated)
3003 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
3004 SV_SendServerinfo(client);
3008 // client is still trying to connect,
3009 // so we send a duplicate reply
3010 if (developer_extra.integer)
3011 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3012 if(crypto && crypto->authenticated)
3013 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
3014 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3020 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3023 // find an empty client slot for this new client
3024 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3027 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3029 // allocated connection
3030 if (developer_extra.integer)
3031 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3032 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3033 // now set up the client
3034 if(crypto && crypto->authenticated)
3035 Crypto_ServerFinishInstance(&conn->crypto, crypto);
3036 SV_ConnectClient(clientnum, conn);
3037 NetConn_Heartbeat(1);
3042 // no empty slots found - server is full
3043 if (developer_extra.integer)
3044 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3045 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3049 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3051 const char *challenge = NULL;
3053 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3056 // If there was a challenge in the getinfo message
3057 if (length > 8 && string[7] == ' ')
3058 challenge = string + 8;
3060 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3062 if (developer_extra.integer)
3063 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3064 NetConn_WriteString(mysocket, response, peeraddress);
3068 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3070 const char *challenge = NULL;
3072 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3075 // If there was a challenge in the getinfo message
3076 if (length > 10 && string[9] == ' ')
3077 challenge = string + 10;
3079 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3081 if (developer_extra.integer)
3082 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3083 NetConn_WriteString(mysocket, response, peeraddress);
3087 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3089 char *password = string + 20;
3090 char *timeval = string + 37;
3091 char *s = strchr(timeval, ' ');
3092 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3093 const char *userlevel;
3095 if(rcon_secure.integer > 1)
3099 return true; // invalid packet
3102 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3103 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3106 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3108 char *password = string + 25;
3109 char *challenge = string + 42;
3110 char *s = strchr(challenge, ' ');
3111 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3112 const char *userlevel;
3114 return true; // invalid packet
3117 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3118 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3121 if (length >= 5 && !memcmp(string, "rcon ", 5))
3124 char *s = string + 5;
3125 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3128 if(rcon_secure.integer > 0)
3131 for (i = 0;!ISWHITESPACE(*s);s++)
3132 if (i < (int)sizeof(password) - 1)
3134 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3137 if (!ISWHITESPACE(password[0]))
3139 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3140 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3144 if (!strncmp(string, "extResponse ", 12))
3146 ++sv_net_extresponse_count;
3147 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3148 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3149 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3150 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3153 if (!strncmp(string, "ping", 4))
3155 if (developer_extra.integer)
3156 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3157 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3160 if (!strncmp(string, "ack", 3))
3162 // we may not have liked the packet, but it was a command packet, so
3163 // we're done processing this packet now
3166 // netquake control packets, supported for compatibility only, and only
3167 // when running game protocols that are normally served via this connection
3169 // (this protects more modern protocols against being used for
3170 // Quake packet flood Denial Of Service attacks)
3171 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)
3175 const char *protocolname;
3178 SZ_Clear(&sv_message);
3179 SZ_Write(&sv_message, data, length);
3180 MSG_BeginReading(&sv_message);
3181 c = MSG_ReadByte(&sv_message);
3185 if (developer_extra.integer)
3186 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3187 if(!(islocal || sv_public.integer > -2))
3189 if (developer_extra.integer)
3190 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3191 SZ_Clear(&sv_message);
3192 // save space for the header, filled in later
3193 MSG_WriteLong(&sv_message, 0);
3194 MSG_WriteByte(&sv_message, CCREP_REJECT);
3195 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3196 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3197 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3198 SZ_Clear(&sv_message);
3202 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3203 protocolnumber = MSG_ReadByte(&sv_message);
3204 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3206 if (developer_extra.integer)
3207 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3208 SZ_Clear(&sv_message);
3209 // save space for the header, filled in later
3210 MSG_WriteLong(&sv_message, 0);
3211 MSG_WriteByte(&sv_message, CCREP_REJECT);
3212 MSG_WriteString(&sv_message, "Incompatible version.\n");
3213 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3214 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3215 SZ_Clear(&sv_message);
3219 // see if this connect request comes from a known client
3220 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3222 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3224 // this is either a duplicate connection request
3225 // or coming back from a timeout
3226 // (if so, keep their stuff intact)
3228 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3229 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3231 if (developer_extra.integer)
3232 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3233 SZ_Clear(&sv_message);
3234 // save space for the header, filled in later
3235 MSG_WriteLong(&sv_message, 0);
3236 MSG_WriteByte(&sv_message, CCREP_REJECT);
3237 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3238 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3239 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3240 SZ_Clear(&sv_message);
3245 if (developer_extra.integer)
3246 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3247 SZ_Clear(&sv_message);
3248 // save space for the header, filled in later
3249 MSG_WriteLong(&sv_message, 0);
3250 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3251 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3252 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3253 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3254 SZ_Clear(&sv_message);
3256 // if client is already spawned, re-send the
3257 // serverinfo message as they'll need it to play
3259 SV_SendServerinfo(client);
3264 // this is a new client, check for connection flood
3265 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3268 // find a slot for the new client
3269 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3272 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3274 // connect to the client
3275 // everything is allocated, just fill in the details
3276 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3277 if (developer_extra.integer)
3278 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3279 // send back the info about the server connection
3280 SZ_Clear(&sv_message);
3281 // save space for the header, filled in later
3282 MSG_WriteLong(&sv_message, 0);
3283 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3284 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3285 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3286 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3287 SZ_Clear(&sv_message);
3288 // now set up the client struct
3289 SV_ConnectClient(clientnum, conn);
3290 NetConn_Heartbeat(1);
3295 if (developer_extra.integer)
3296 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3297 // no room; try to let player know
3298 SZ_Clear(&sv_message);
3299 // save space for the header, filled in later
3300 MSG_WriteLong(&sv_message, 0);
3301 MSG_WriteByte(&sv_message, CCREP_REJECT);
3302 MSG_WriteString(&sv_message, "Server is full.\n");
3303 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3304 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3305 SZ_Clear(&sv_message);
3307 case CCREQ_SERVER_INFO:
3308 if (developer_extra.integer)
3309 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3310 if(!(islocal || sv_public.integer > -1))
3313 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3316 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3319 char myaddressstring[128];
3320 if (developer_extra.integer)
3321 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3322 SZ_Clear(&sv_message);
3323 // save space for the header, filled in later
3324 MSG_WriteLong(&sv_message, 0);
3325 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3326 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3327 MSG_WriteString(&sv_message, myaddressstring);
3328 MSG_WriteString(&sv_message, hostname.string);
3329 MSG_WriteString(&sv_message, sv.name);
3330 // How many clients are there?
3331 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3332 if (svs.clients[i].active)
3334 MSG_WriteByte(&sv_message, numclients);
3335 MSG_WriteByte(&sv_message, svs.maxclients);
3336 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3337 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3338 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3339 SZ_Clear(&sv_message);
3342 case CCREQ_PLAYER_INFO:
3343 if (developer_extra.integer)
3344 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3345 if(!(islocal || sv_public.integer > -1))
3348 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3353 int playerNumber, activeNumber, clientNumber;
3356 playerNumber = MSG_ReadByte(&sv_message);
3358 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3359 if (client->active && ++activeNumber == playerNumber)
3361 if (clientNumber != svs.maxclients)
3363 SZ_Clear(&sv_message);
3364 // save space for the header, filled in later
3365 MSG_WriteLong(&sv_message, 0);
3366 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3367 MSG_WriteByte(&sv_message, playerNumber);
3368 MSG_WriteString(&sv_message, client->name);
3369 MSG_WriteLong(&sv_message, client->colors);
3370 MSG_WriteLong(&sv_message, client->frags);
3371 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3372 if(sv_status_privacy.integer)
3373 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3375 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3376 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3377 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3378 SZ_Clear(&sv_message);
3382 case CCREQ_RULE_INFO:
3383 if (developer_extra.integer)
3384 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3385 if(!(islocal || sv_public.integer > -1))
3388 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3395 // find the search start location
3396 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3397 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3399 // send the response
3400 SZ_Clear(&sv_message);
3401 // save space for the header, filled in later
3402 MSG_WriteLong(&sv_message, 0);
3403 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3406 MSG_WriteString(&sv_message, var->name);
3407 MSG_WriteString(&sv_message, var->string);
3409 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3410 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3411 SZ_Clear(&sv_message);
3415 if (developer_extra.integer)
3416 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3417 if (sv.active && !rcon_secure.integer)
3419 char password[2048];
3423 const char *userlevel;
3424 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3425 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3427 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3428 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3429 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3436 SZ_Clear(&sv_message);
3437 // we may not have liked the packet, but it was a valid control
3438 // packet, so we're done processing this packet now
3443 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3445 SV_ReadClientMessage();
3452 void NetConn_ServerFrame(void)
3455 lhnetaddress_t peeraddress;
3456 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3457 for (i = 0;i < sv_numsockets;i++)
3458 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3459 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3460 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3462 // never timeout loopback connections
3463 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3465 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3466 SV_DropClient(false);
3471 void NetConn_SleepMicroseconds(int microseconds)
3473 LHNET_SleepUntilPacket_Microseconds(microseconds);
3476 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3480 lhnetaddress_t masteraddress;
3481 lhnetaddress_t broadcastaddress;
3484 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3487 // 26000 is the default quake server port, servers on other ports will not
3489 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3490 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3494 for (i = 0;i < cl_numsockets;i++)
3498 const char *cmdname, *extraoptions;
3499 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3501 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3503 // search LAN for Quake servers
3504 SZ_Clear(&cl_message);
3505 // save space for the header, filled in later
3506 MSG_WriteLong(&cl_message, 0);
3507 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3508 MSG_WriteString(&cl_message, "QUAKE");
3509 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3510 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3511 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3512 SZ_Clear(&cl_message);
3514 // search LAN for DarkPlaces servers
3515 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3518 // build the getservers message to send to the dpmaster master servers
3519 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3521 cmdname = "getserversExt";
3522 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3526 cmdname = "getservers";
3529 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamename, NET_PROTOCOL_VERSION, extraoptions);
3532 for (masternum = 0;sv_masters[masternum].name;masternum++)
3534 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3537 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3541 // search favorite servers
3542 for(j = 0; j < nFavorites; ++j)
3544 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3546 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3547 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3554 // only query QuakeWorld servers when the user wants to
3557 for (i = 0;i < cl_numsockets;i++)
3561 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3563 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3565 // search LAN for QuakeWorld servers
3566 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3568 // build the getservers message to send to the qwmaster master servers
3569 // note this has no -1 prefix, and the trailing nul byte is sent
3570 dpsnprintf(request, sizeof(request), "c\n");
3574 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3576 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3578 if (m_state != m_slist)
3580 char lookupstring[128];
3581 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3582 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3585 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3589 // search favorite servers
3590 for(j = 0; j < nFavorites; ++j)
3592 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3594 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3596 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3597 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3604 if (!masterquerycount)
3606 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3607 M_Update_Return_Reason("No network");
3611 void NetConn_Heartbeat(int priority)
3613 lhnetaddress_t masteraddress;
3615 lhnetsocket_t *mysocket;
3617 // if it's a state change (client connected), limit next heartbeat to no
3618 // more than 30 sec in the future
3619 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3620 nextheartbeattime = realtime + 30.0;
3622 // limit heartbeatperiod to 30 to 270 second range,
3623 // lower limit is to avoid abusing master servers with excess traffic,
3624 // upper limit is to avoid timing out on the master server (which uses
3626 if (sv_heartbeatperiod.value < 30)
3627 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3628 if (sv_heartbeatperiod.value > 270)
3629 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3631 // make advertising optional and don't advertise singleplayer games, and
3632 // only send a heartbeat as often as the admin wants
3633 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3635 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3636 for (masternum = 0;sv_masters[masternum].name;masternum++)
3637 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3638 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3642 static void Net_Heartbeat_f(void)
3645 NetConn_Heartbeat(2);
3647 Con_Print("No server running, can not heartbeat to master server.\n");
3650 static void PrintStats(netconn_t *conn)
3652 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3653 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3655 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3656 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3657 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3658 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3659 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3660 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3661 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3662 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3663 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3664 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3667 void Net_Stats_f(void)
3670 Con_Print("connections =\n");
3671 for (conn = netconn_list;conn;conn = conn->next)
3675 void Net_Refresh_f(void)
3677 if (m_state != m_slist) {
3678 Con_Print("Sending new requests to master servers\n");
3679 ServerList_QueryList(false, true, false, true);
3680 Con_Print("Listening for replies...\n");
3682 ServerList_QueryList(false, true, false, false);
3685 void Net_Slist_f(void)
3687 ServerList_ResetMasks();
3688 serverlist_sortbyfield = SLIF_PING;
3689 serverlist_sortflags = 0;
3690 if (m_state != m_slist) {
3691 Con_Print("Sending requests to master servers\n");
3692 ServerList_QueryList(true, true, false, true);
3693 Con_Print("Listening for replies...\n");
3695 ServerList_QueryList(true, true, false, false);
3698 void Net_SlistQW_f(void)
3700 ServerList_ResetMasks();
3701 serverlist_sortbyfield = SLIF_PING;
3702 serverlist_sortflags = 0;
3703 if (m_state != m_slist) {
3704 Con_Print("Sending requests to master servers\n");
3705 ServerList_QueryList(true, false, true, true);
3706 serverlist_consoleoutput = true;
3707 Con_Print("Listening for replies...\n");
3709 ServerList_QueryList(true, false, true, false);
3712 void NetConn_Init(void)
3715 lhnetaddress_t tempaddress;
3716 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3717 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3718 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3719 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3720 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3721 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3722 Cvar_RegisterVariable(&net_test);
3723 Cvar_RegisterVariable(&net_usesizelimit);
3724 Cvar_RegisterVariable(&net_burstreserve);
3725 Cvar_RegisterVariable(&rcon_restricted_password);
3726 Cvar_RegisterVariable(&rcon_restricted_commands);
3727 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3728 Cvar_RegisterVariable(&net_slist_queriespersecond);
3729 Cvar_RegisterVariable(&net_slist_queriesperframe);
3730 Cvar_RegisterVariable(&net_slist_timeout);
3731 Cvar_RegisterVariable(&net_slist_maxtries);
3732 Cvar_RegisterVariable(&net_slist_favorites);
3733 Cvar_RegisterVariable(&net_slist_pause);
3734 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3735 Cvar_RegisterVariable(&net_tos_dscp);
3736 Cvar_RegisterVariable(&net_messagetimeout);
3737 Cvar_RegisterVariable(&net_connecttimeout);
3738 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3739 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3740 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3741 Cvar_RegisterVariable(&cl_netlocalping);
3742 Cvar_RegisterVariable(&cl_netpacketloss_send);
3743 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3744 Cvar_RegisterVariable(&hostname);
3745 Cvar_RegisterVariable(&developer_networking);
3746 Cvar_RegisterVariable(&cl_netport);
3747 Cvar_RegisterVariable(&sv_netport);
3748 Cvar_RegisterVariable(&net_address);
3749 Cvar_RegisterVariable(&net_address_ipv6);
3750 Cvar_RegisterVariable(&sv_public);
3751 Cvar_RegisterVariable(&sv_public_rejectreason);
3752 Cvar_RegisterVariable(&sv_heartbeatperiod);
3753 for (i = 0;sv_masters[i].name;i++)
3754 Cvar_RegisterVariable(&sv_masters[i]);
3755 Cvar_RegisterVariable(&gameversion);
3756 Cvar_RegisterVariable(&gameversion_min);
3757 Cvar_RegisterVariable(&gameversion_max);
3758 // 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.
3759 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3761 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3763 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3764 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3767 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3769 // 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
3770 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3772 i = atoi(com_argv[i + 1]);
3773 if (i >= 0 && i < 65536)
3775 Con_Printf("-port option used, setting port cvar to %i\n", i);
3776 Cvar_SetValueQuick(&sv_netport, i);
3779 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3783 cl_message.data = cl_message_buf;
3784 cl_message.maxsize = sizeof(cl_message_buf);
3785 cl_message.cursize = 0;
3786 sv_message.data = sv_message_buf;
3787 sv_message.maxsize = sizeof(sv_message_buf);
3788 sv_message.cursize = 0;
3790 if (Thread_HasThreads())
3791 netconn_mutex = Thread_CreateMutex();
3794 void NetConn_Shutdown(void)
3796 NetConn_CloseClientPorts();
3797 NetConn_CloseServerPorts();
3800 Thread_DestroyMutex(netconn_mutex);
3801 netconn_mutex = NULL;