2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // for secure rcon authentication
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
35 // note this defaults on for dedicated servers, off for listen servers
36 cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
37 cvar_t sv_public_rejectreason = {0, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
39 extern cvar_t sv_status_privacy;
41 static cvar_t sv_masters [] =
43 {CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
44 {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
45 {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
46 {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
47 {0, "sv_masterextra1", "ghdigital.com", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
48 {0, "sv_masterextra2", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
49 {0, "sv_masterextra3", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
54 static cvar_t sv_qwmasters [] =
56 {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
57 {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
58 {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
59 {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
60 {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
61 {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
62 {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
63 {0, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
68 static double nextheartbeattime = 0;
72 static unsigned char cl_message_buf[NET_MAXMESSAGE];
73 static unsigned char sv_message_buf[NET_MAXMESSAGE];
74 char cl_readstring[MAX_INPUTLINE];
75 char sv_readstring[MAX_INPUTLINE];
77 cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
78 cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
79 cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
80 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
81 cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
82 cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
83 cvar_t net_challengefloodblockingtimeout = {0, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
84 cvar_t net_getstatusfloodblockingtimeout = {0, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
85 cvar_t net_sourceaddresscheck = {0, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
86 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
87 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
89 cvar_t cl_netlocalping = {0, "cl_netlocalping","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
90 static cvar_t cl_netpacketloss_send = {0, "cl_netpacketloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
91 static cvar_t cl_netpacketloss_receive = {0, "cl_netpacketloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
92 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
93 static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
94 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
95 static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
96 static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
97 static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
98 static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
99 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
100 static cvar_t gameversion_min = {0, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
101 static cvar_t gameversion_max = {0, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
102 static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
103 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
104 static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
105 extern cvar_t rcon_secure;
106 extern cvar_t rcon_secure_challengetimeout;
108 double masterquerytime = -1000;
109 int masterquerycount = 0;
110 int masterreplycount = 0;
111 int serverquerycount = 0;
112 int serverreplycount = 0;
114 challenge_t challenge[MAX_CHALLENGES];
117 /// this is only false if there are still servers left to query
118 static qboolean serverlist_querysleep = true;
119 static qboolean serverlist_paused = false;
120 /// this is pushed a second or two ahead of realtime whenever a master server
121 /// reply is received, to avoid issuing queries while master replies are still
122 /// flooding in (which would make a mess of the ping times)
123 static double serverlist_querywaittime = 0;
126 static int cl_numsockets;
127 static lhnetsocket_t *cl_sockets[16];
128 static int sv_numsockets;
129 static lhnetsocket_t *sv_sockets[16];
131 netconn_t *netconn_list = NULL;
132 mempool_t *netconn_mempool = NULL;
133 void *netconn_mutex = NULL;
135 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
136 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
137 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
138 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
140 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
141 int cl_net_extresponse_count = 0;
142 int cl_net_extresponse_last = 0;
144 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
145 int sv_net_extresponse_count = 0;
146 int sv_net_extresponse_last = 0;
149 // ServerList interface
150 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
151 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
153 serverlist_infofield_t serverlist_sortbyfield;
154 int serverlist_sortflags;
156 int serverlist_viewcount = 0;
157 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
159 int serverlist_maxcachecount = 0;
160 int serverlist_cachecount = 0;
161 serverlist_entry_t *serverlist_cache = NULL;
163 qboolean serverlist_consoleoutput;
165 static int nFavorites = 0;
166 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
167 static int nFavorites_idfp = 0;
168 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
170 void NetConn_UpdateFavorites(void)
175 p = net_slist_favorites.string;
176 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
178 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
179 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
180 // (if v6 address contains port, it must start with '[')
182 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
187 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
193 /// helper function to insert a value into the viewset
194 /// spare entries will be removed
195 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
198 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
199 i = serverlist_viewcount++;
201 i = SERVERLIST_VIEWLISTSIZE - 1;
204 for( ; i > index ; i-- )
205 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
207 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
210 /// we suppose serverlist_viewcount to be valid, ie > 0
211 static void _ServerList_ViewList_Helper_Remove( int index )
213 serverlist_viewcount--;
214 for( ; index < serverlist_viewcount ; index++ )
215 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
218 /// \returns true if A should be inserted before B
219 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
221 int result = 0; // > 0 if for numbers A > B and for text if A < B
223 if( serverlist_sortflags & SLSF_CATEGORIES )
225 result = A->info.category - B->info.category;
230 if( serverlist_sortflags & SLSF_FAVORITES )
232 if(A->info.isfavorite != B->info.isfavorite)
233 return A->info.isfavorite;
236 switch( serverlist_sortbyfield ) {
238 result = A->info.ping - B->info.ping;
240 case SLIF_MAXPLAYERS:
241 result = A->info.maxplayers - B->info.maxplayers;
243 case SLIF_NUMPLAYERS:
244 result = A->info.numplayers - B->info.numplayers;
247 result = A->info.numbots - B->info.numbots;
250 result = A->info.numhumans - B->info.numhumans;
253 result = A->info.freeslots - B->info.freeslots;
256 result = A->info.protocol - B->info.protocol;
259 result = strcmp( B->info.cname, A->info.cname );
262 result = strcasecmp( B->info.game, A->info.game );
265 result = strcasecmp( B->info.map, A->info.map );
268 result = strcasecmp( B->info.mod, A->info.mod );
271 result = strcasecmp( B->info.name, A->info.name );
274 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
277 result = A->info.category - B->info.category;
279 case SLIF_ISFAVORITE:
280 result = !!B->info.isfavorite - !!A->info.isfavorite;
283 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
289 if( serverlist_sortflags & SLSF_DESCENDING )
295 // if the chosen sort key is identical, sort by index
296 // (makes this a stable sort, so that later replies from servers won't
297 // shuffle the servers around when they have the same ping)
301 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
303 // This should actually be done with some intermediate and end-of-function return
315 case SLMO_GREATEREQUAL:
317 case SLMO_NOTCONTAIN:
318 case SLMO_STARTSWITH:
319 case SLMO_NOTSTARTSWITH:
322 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
327 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
330 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
331 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
332 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
333 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
335 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
336 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
339 // Same here, also using an intermediate & final return would be more appropriate
343 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
344 case SLMO_NOTCONTAIN:
345 return !*bufferB || !strstr( bufferA, bufferB );
346 case SLMO_STARTSWITH:
347 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
348 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
349 case SLMO_NOTSTARTSWITH:
350 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
352 return strcmp( bufferA, bufferB ) < 0;
354 return strcmp( bufferA, bufferB ) <= 0;
356 return strcmp( bufferA, bufferB ) == 0;
358 return strcmp( bufferA, bufferB ) > 0;
360 return strcmp( bufferA, bufferB ) != 0;
361 case SLMO_GREATEREQUAL:
362 return strcmp( bufferA, bufferB ) >= 0;
364 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
369 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
371 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
373 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
375 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
377 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
379 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
381 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
383 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
385 if( *mask->info.cname
386 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
389 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
392 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
395 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
398 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
400 if( *mask->info.qcstatus
401 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
403 if( *mask->info.players
404 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
406 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
408 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
413 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
415 int start, end, mid, i;
418 // reject incompatible servers
420 entry->info.gameversion != gameversion.integer
423 gameversion_min.integer >= 0 // min/max range set by user/mod?
424 && gameversion_max.integer >= 0
425 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
426 && gameversion_max.integer >= entry->info.gameversion
431 // refresh the "favorite" status
432 entry->info.isfavorite = false;
433 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
435 char idfp[FP64_SIZE+1];
436 for(i = 0; i < nFavorites; ++i)
438 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
440 entry->info.isfavorite = true;
444 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
446 for(i = 0; i < nFavorites_idfp; ++i)
448 if(!strcmp(idfp, favorites_idfp[i]))
450 entry->info.isfavorite = true;
457 // refresh the "category"
458 entry->info.category = MR_GetServerListEntryCategory(entry);
460 // FIXME: change this to be more readable (...)
461 // now check whether it passes through the masks
462 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
463 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
466 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
467 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
469 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
472 if( !serverlist_viewcount ) {
473 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
476 // ok, insert it, we just need to find out where exactly:
479 // check whether to insert it as new first item
480 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
481 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
483 } // check whether to insert it as new last item
484 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
485 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
489 end = serverlist_viewcount - 1;
490 while( end > start + 1 )
492 mid = (start + end) / 2;
493 // test the item that lies in the middle between start and end
494 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
495 // the item has to be in the upper half
498 // the item has to be in the lower half
501 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
504 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
507 for( i = 0; i < serverlist_viewcount; i++ )
509 if (ServerList_GetViewEntry(i) == entry)
511 _ServerList_ViewList_Helper_Remove(i);
517 void ServerList_RebuildViewList(void)
521 serverlist_viewcount = 0;
522 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
523 serverlist_entry_t *entry = &serverlist_cache[i];
524 // also display entries that are currently being refreshed [11/8/2007 Black]
525 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
526 ServerList_ViewList_Insert( entry );
530 void ServerList_ResetMasks(void)
534 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
535 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
536 // numbots needs to be compared to -1 to always succeed
537 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
538 serverlist_andmasks[i].info.numbots = -1;
539 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
540 serverlist_ormasks[i].info.numbots = -1;
543 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
546 int numplayers = 0, maxplayers = 0;
547 for (i = 0;i < serverlist_cachecount;i++)
549 if (serverlist_cache[i].query == SQS_QUERIED)
551 numplayers += serverlist_cache[i].info.numhumans;
552 maxplayers += serverlist_cache[i].info.maxplayers;
555 *numplayerspointer = numplayers;
556 *maxplayerspointer = maxplayers;
560 static void _ServerList_Test(void)
563 if (serverlist_maxcachecount <= 1024)
565 serverlist_maxcachecount = 1024;
566 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
568 for( i = 0 ; i < 1024 ; i++ ) {
569 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
570 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
571 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
572 serverlist_cache[serverlist_cachecount].finished = true;
573 dpsnprintf( serverlist_cache[serverlist_cachecount].line1, sizeof(serverlist_cache[serverlist_cachecount].info.line1), "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
574 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
575 serverlist_cachecount++;
580 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
582 masterquerytime = realtime;
583 masterquerycount = 0;
584 masterreplycount = 0;
586 serverquerycount = 0;
587 serverreplycount = 0;
588 serverlist_cachecount = 0;
589 serverlist_viewcount = 0;
590 serverlist_maxcachecount = 0;
591 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
593 // refresh all entries
595 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
596 serverlist_entry_t *entry = &serverlist_cache[ n ];
597 entry->query = SQS_REFRESHING;
598 entry->querycounter = 0;
601 serverlist_consoleoutput = consoleoutput;
603 //_ServerList_Test();
605 NetConn_QueryMasters(querydp, queryqw);
611 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
615 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
616 Thread_LockMutex(netconn_mutex);
617 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
618 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
619 Thread_UnlockMutex(netconn_mutex);
622 if (cl_netpacketloss_receive.integer)
623 for (i = 0;i < cl_numsockets;i++)
624 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
626 if (developer_networking.integer)
628 char addressstring[128], addressstring2[128];
629 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
632 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
633 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
634 Com_HexDumpToConsole((unsigned char *)data, length);
637 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
642 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
646 if (cl_netpacketloss_send.integer)
647 for (i = 0;i < cl_numsockets;i++)
648 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
650 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
651 Thread_LockMutex(netconn_mutex);
652 ret = LHNET_Write(mysocket, data, length, peeraddress);
653 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
654 Thread_UnlockMutex(netconn_mutex);
655 if (developer_networking.integer)
657 char addressstring[128], addressstring2[128];
658 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
659 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
660 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", (void *)mysocket, addressstring, (void *)data, length, (void *)peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
661 Com_HexDumpToConsole((unsigned char *)data, length);
666 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
668 // note this does not include the trailing NULL because we add that in the parser
669 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
672 qboolean NetConn_CanSend(netconn_t *conn)
674 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
675 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = realtime;
676 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
677 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
678 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
679 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
680 if (realtime > conn->cleartime)
684 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
689 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
691 double bursttime = burstsize / (double)rate;
693 // delay later packets to obey rate limit
694 if (*cleartime < realtime - bursttime)
695 *cleartime = realtime - bursttime;
696 *cleartime = *cleartime + len / (double)rate;
698 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
699 if (net_test.integer)
701 if (*cleartime < realtime)
702 *cleartime = realtime;
706 static int NetConn_AddCryptoFlag(crypto_t *crypto)
708 // HACK: if an encrypted connection is used, randomly set some unused
709 // flags. When AES encryption is enabled, that will make resends differ
710 // from the original, so that e.g. substring filters in a router/IPS
711 // are unlikely to match a second time. See also "startkeylogger".
713 if (crypto->authenticated)
715 // Let's always set at least one of the bits.
716 int r = rand() % 7 + 1;
718 flag |= NETFLAG_CRYPTO0;
720 flag |= NETFLAG_CRYPTO1;
722 flag |= NETFLAG_CRYPTO2;
727 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
730 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
731 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
733 // if this packet was supposedly choked, but we find ourselves sending one
734 // anyway, make sure the size counting starts at zero
735 // (this mostly happens on level changes and disconnects and such)
736 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
737 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
739 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
741 if (protocol == PROTOCOL_QUAKEWORLD)
744 qboolean sendreliable;
746 // note that it is ok to send empty messages to the qw server,
747 // otherwise it won't respond to us at all
749 sendreliable = false;
750 // if the remote side dropped the last reliable message, resend it
751 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
753 // if the reliable transmit buffer is empty, copy the current message out
754 if (!conn->sendMessageLength && conn->message.cursize)
756 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
757 conn->sendMessageLength = conn->message.cursize;
758 SZ_Clear(&conn->message); // clear the message buffer
759 conn->qw.reliable_sequence ^= 1;
762 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
763 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
764 // last received unreliable packet number, and last received reliable packet number (0 or 1)
765 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
767 conn->outgoing_unreliable_sequence++;
768 // client sends qport in every packet
769 if (conn == cls.netcon)
771 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
773 // also update cls.qw_outgoing_sequence
774 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
776 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
778 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
782 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
784 // add the reliable message if there is one
787 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
788 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
789 packetLen += conn->sendMessageLength;
790 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
793 // add the unreliable message if possible
794 if (packetLen + data->cursize <= 1400)
796 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
797 memcpy(sendbuffer + packetLen, data->data, data->cursize);
798 packetLen += data->cursize;
801 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
804 conn->unreliableMessagesSent++;
806 totallen += packetLen + 28;
810 unsigned int packetLen;
811 unsigned int dataLen;
816 // if a reliable message fragment has been lost, send it again
817 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
819 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
821 dataLen = conn->sendMessageLength;
826 dataLen = MAX_PACKETFRAGMENT;
830 packetLen = NET_HEADERSIZE + dataLen;
832 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
833 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
834 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
836 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
838 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
839 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
841 conn->lastSendTime = realtime;
842 conn->packetsReSent++;
845 totallen += (int)sendmelen + 28;
848 // if we have a new reliable message to send, do so
849 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
851 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
853 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
854 conn->message.overflowed = true;
858 if (developer_networking.integer && conn == cls.netcon)
860 Con_Print("client sending reliable message to server:\n");
861 SZ_HexDumpToConsole(&conn->message);
864 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
865 conn->sendMessageLength = conn->message.cursize;
866 SZ_Clear(&conn->message);
868 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
870 dataLen = conn->sendMessageLength;
875 dataLen = MAX_PACKETFRAGMENT;
879 packetLen = NET_HEADERSIZE + dataLen;
881 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
882 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
883 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
885 conn->nq.sendSequence++;
887 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
889 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
891 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
893 conn->lastSendTime = realtime;
895 conn->reliableMessagesSent++;
897 totallen += (int)sendmelen + 28;
900 // if we have an unreliable message to send, do so
903 packetLen = NET_HEADERSIZE + data->cursize;
905 if (packetLen > (int)sizeof(sendbuffer))
907 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
911 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
912 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
913 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
915 conn->outgoing_unreliable_sequence++;
917 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
919 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
921 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
924 conn->unreliableMessagesSent++;
926 totallen += (int)sendmelen + 28;
930 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
935 qboolean NetConn_HaveClientPorts(void)
937 return !!cl_numsockets;
940 qboolean NetConn_HaveServerPorts(void)
942 return !!sv_numsockets;
945 void NetConn_CloseClientPorts(void)
947 for (;cl_numsockets > 0;cl_numsockets--)
948 if (cl_sockets[cl_numsockets - 1])
949 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
952 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
954 lhnetaddress_t address;
957 char addressstring2[1024];
958 if (addressstring && addressstring[0])
959 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
961 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
964 if ((s = LHNET_OpenSocket_Connectionless(&address)))
966 cl_sockets[cl_numsockets++] = s;
967 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
968 if (addresstype != LHNETADDRESSTYPE_LOOP)
969 Con_Printf("Client opened a socket on address %s\n", addressstring2);
973 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
974 Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
978 Con_Printf("Client unable to parse address %s\n", addressstring);
981 void NetConn_OpenClientPorts(void)
984 NetConn_CloseClientPorts();
986 SV_LockThreadMutex(); // FIXME recursive?
987 Crypto_LoadKeys(); // client sockets
988 SV_UnlockThreadMutex();
990 port = bound(0, cl_netport.integer, 65535);
991 if (cl_netport.integer != port)
992 Cvar_SetValueQuick(&cl_netport, port);
994 Con_Printf("Client using an automatically assigned port\n");
996 Con_Printf("Client using port %i\n", port);
997 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
998 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
999 #ifndef NOSUPPORTIPV6
1000 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1004 void NetConn_CloseServerPorts(void)
1006 for (;sv_numsockets > 0;sv_numsockets--)
1007 if (sv_sockets[sv_numsockets - 1])
1008 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1011 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1013 lhnetaddress_t address;
1016 char addressstring2[1024];
1019 for (port = defaultport; port <= defaultport + range; port++)
1021 if (addressstring && addressstring[0])
1022 success = LHNETADDRESS_FromString(&address, addressstring, port);
1024 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1027 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1029 sv_sockets[sv_numsockets++] = s;
1030 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1031 if (addresstype != LHNETADDRESSTYPE_LOOP)
1032 Con_Printf("Server listening on address %s\n", addressstring2);
1037 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1038 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
1043 Con_Printf("Server unable to parse address %s\n", addressstring);
1044 // if it cant parse one address, it wont be able to parse another for sure
1051 void NetConn_OpenServerPorts(int opennetports)
1054 NetConn_CloseServerPorts();
1056 SV_LockThreadMutex(); // FIXME recursive?
1057 Crypto_LoadKeys(); // server sockets
1058 SV_UnlockThreadMutex();
1060 NetConn_UpdateSockets();
1061 port = bound(0, sv_netport.integer, 65535);
1064 Con_Printf("Server using port %i\n", port);
1065 if (sv_netport.integer != port)
1066 Cvar_SetValueQuick(&sv_netport, port);
1067 if (cls.state != ca_dedicated)
1068 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1071 #ifndef NOSUPPORTIPV6
1072 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1073 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1075 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1078 if (sv_numsockets == 0)
1079 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1082 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1084 int i, a = LHNETADDRESS_GetAddressType(address);
1085 for (i = 0;i < cl_numsockets;i++)
1086 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1087 return cl_sockets[i];
1091 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1093 int i, a = LHNETADDRESS_GetAddressType(address);
1094 for (i = 0;i < sv_numsockets;i++)
1095 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1096 return sv_sockets[i];
1100 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1103 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1104 conn->mysocket = mysocket;
1105 conn->peeraddress = *peeraddress;
1106 conn->lastMessageTime = realtime;
1107 conn->message.data = conn->messagedata;
1108 conn->message.maxsize = sizeof(conn->messagedata);
1109 conn->message.cursize = 0;
1110 // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1111 // reduce effectiveness of connection request floods
1112 conn->timeout = realtime + net_connecttimeout.value;
1113 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1114 conn->next = netconn_list;
1115 netconn_list = conn;
1119 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1120 void NetConn_Close(netconn_t *conn)
1123 // remove connection from list
1125 // allow the client to reconnect immediately
1126 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1128 if (conn == netconn_list)
1129 netconn_list = conn->next;
1132 for (c = netconn_list;c;c = c->next)
1134 if (c->next == conn)
1136 c->next = conn->next;
1140 // not found in list, we'll avoid crashing here...
1148 static int clientport = -1;
1149 static int clientport2 = -1;
1150 static int hostport = -1;
1151 void NetConn_UpdateSockets(void)
1155 // TODO add logic to automatically close sockets if needed
1156 LHNET_DefaultDSCP(net_tos_dscp.integer);
1158 if (cls.state != ca_dedicated)
1160 if (clientport2 != cl_netport.integer)
1162 clientport2 = cl_netport.integer;
1163 if (cls.state == ca_connected)
1164 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1166 if (cls.state == ca_disconnected && clientport != clientport2)
1168 clientport = clientport2;
1169 NetConn_CloseClientPorts();
1171 if (cl_numsockets == 0)
1172 NetConn_OpenClientPorts();
1175 if (hostport != sv_netport.integer)
1177 hostport = sv_netport.integer;
1179 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1182 for (j = 0;j < MAX_RCONS;j++)
1184 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1185 if(cls.rcon_commands[i][0])
1187 if(realtime > cls.rcon_timeout[i])
1190 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1191 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1192 cls.rcon_commands[i][0] = 0;
1200 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1202 int originallength = (int)length;
1203 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1204 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1205 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1209 if (protocol == PROTOCOL_QUAKEWORLD)
1211 unsigned int sequence, sequence_ack;
1212 qboolean reliable_ack, reliable_message;
1216 sequence = LittleLong(*((int *)(data + 0)));
1217 sequence_ack = LittleLong(*((int *)(data + 4)));
1221 if (conn != cls.netcon)
1226 // TODO: use qport to identify that this client really is who they say they are? (and elsewhere in the code to identify the connection without a port match?)
1227 //qport = LittleShort(*((int *)(data + 8)));
1232 conn->packetsReceived++;
1233 reliable_message = (sequence >> 31) != 0;
1234 reliable_ack = (sequence_ack >> 31) != 0;
1235 sequence &= ~(1<<31);
1236 sequence_ack &= ~(1<<31);
1237 if (sequence <= conn->qw.incoming_sequence)
1239 //Con_DPrint("Got a stale datagram\n");
1242 count = sequence - (conn->qw.incoming_sequence + 1);
1245 conn->droppedDatagrams += count;
1246 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1247 // If too may packets have been dropped, only write the
1248 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1249 // Because there's no point in writing more than
1250 // these as the netgraph is going to be full anyway.
1251 if (count > NETGRAPH_PACKETS)
1252 count = NETGRAPH_PACKETS;
1255 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1256 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1257 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1258 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1259 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1260 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1263 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1264 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1265 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1266 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1267 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1268 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1269 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1271 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1272 if (net_test.integer)
1274 if (conn->cleartime < realtime)
1275 conn->cleartime = realtime;
1278 if (reliable_ack == conn->qw.reliable_sequence)
1280 // received, now we will be able to send another reliable message
1281 conn->sendMessageLength = 0;
1282 conn->reliableMessagesReceived++;
1284 conn->qw.incoming_sequence = sequence;
1285 if (conn == cls.netcon)
1286 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1287 conn->qw.incoming_acknowledged = sequence_ack;
1288 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1289 if (reliable_message)
1290 conn->qw.incoming_reliable_sequence ^= 1;
1291 conn->lastMessageTime = realtime;
1292 conn->timeout = realtime + newtimeout;
1293 conn->unreliableMessagesReceived++;
1294 if (conn == cls.netcon)
1296 SZ_Clear(&cl_message);
1297 SZ_Write(&cl_message, data, (int)length);
1298 MSG_BeginReading(&cl_message);
1302 SZ_Clear(&sv_message);
1303 SZ_Write(&sv_message, data, (int)length);
1304 MSG_BeginReading(&sv_message);
1312 unsigned int sequence;
1317 originallength = (int)length;
1318 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1324 qlength = (unsigned int)BuffBigLong(data);
1325 flags = qlength & ~NETFLAG_LENGTH_MASK;
1326 qlength &= NETFLAG_LENGTH_MASK;
1327 // control packets were already handled
1328 if (!(flags & NETFLAG_CTL) && qlength == length)
1330 sequence = BuffBigLong(data + 4);
1331 conn->packetsReceived++;
1334 if (flags & NETFLAG_UNRELIABLE)
1336 if (sequence >= conn->nq.unreliableReceiveSequence)
1338 if (sequence > conn->nq.unreliableReceiveSequence)
1340 count = sequence - conn->nq.unreliableReceiveSequence;
1341 conn->droppedDatagrams += count;
1342 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1343 // If too may packets have been dropped, only write the
1344 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1345 // Because there's no point in writing more than
1346 // these as the netgraph is going to be full anyway.
1347 if (count > NETGRAPH_PACKETS)
1348 count = NETGRAPH_PACKETS;
1351 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1352 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1353 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1354 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1355 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1356 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1359 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1360 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1361 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1362 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1363 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1364 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1365 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1367 conn->nq.unreliableReceiveSequence = sequence + 1;
1368 conn->lastMessageTime = realtime;
1369 conn->timeout = realtime + newtimeout;
1370 conn->unreliableMessagesReceived++;
1373 if (conn == cls.netcon)
1375 SZ_Clear(&cl_message);
1376 SZ_Write(&cl_message, data, (int)length);
1377 MSG_BeginReading(&cl_message);
1381 SZ_Clear(&sv_message);
1382 SZ_Write(&sv_message, data, (int)length);
1383 MSG_BeginReading(&sv_message);
1389 // Con_DPrint("Got a stale datagram\n");
1392 else if (flags & NETFLAG_ACK)
1394 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1395 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1397 if (sequence == (conn->nq.sendSequence - 1))
1399 if (sequence == conn->nq.ackSequence)
1401 conn->nq.ackSequence++;
1402 if (conn->nq.ackSequence != conn->nq.sendSequence)
1403 Con_DPrint("ack sequencing error\n");
1404 conn->lastMessageTime = realtime;
1405 conn->timeout = realtime + newtimeout;
1406 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1408 unsigned int packetLen;
1409 unsigned int dataLen;
1412 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1413 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1415 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1417 dataLen = conn->sendMessageLength;
1422 dataLen = MAX_PACKETFRAGMENT;
1426 packetLen = NET_HEADERSIZE + dataLen;
1428 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1429 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1430 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1432 conn->nq.sendSequence++;
1434 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1435 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1437 conn->lastSendTime = realtime;
1438 conn->packetsSent++;
1442 conn->sendMessageLength = 0;
1445 // Con_DPrint("Duplicate ACK received\n");
1448 // Con_DPrint("Stale ACK received\n");
1451 else if (flags & NETFLAG_DATA)
1453 unsigned char temppacket[8];
1454 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1455 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1457 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1459 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1460 StoreBigLong(temppacket + 4, sequence);
1461 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1463 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1464 if (sequence == conn->nq.receiveSequence)
1466 conn->lastMessageTime = realtime;
1467 conn->timeout = realtime + newtimeout;
1468 conn->nq.receiveSequence++;
1469 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1470 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1471 conn->receiveMessageLength += (int)length;
1473 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1474 "Dropping the message!\n", sequence );
1475 conn->receiveMessageLength = 0;
1478 if (flags & NETFLAG_EOM)
1480 conn->reliableMessagesReceived++;
1481 length = conn->receiveMessageLength;
1482 conn->receiveMessageLength = 0;
1485 if (conn == cls.netcon)
1487 SZ_Clear(&cl_message);
1488 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1489 MSG_BeginReading(&cl_message);
1493 SZ_Clear(&sv_message);
1494 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1495 MSG_BeginReading(&sv_message);
1502 conn->receivedDuplicateCount++;
1510 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1513 cls.connect_trying = false;
1515 M_Update_Return_Reason("");
1517 // the connection request succeeded, stop current connection and set up a new connection
1519 // if we're connecting to a remote server, shut down any local server
1520 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1522 SV_LockThreadMutex();
1523 Host_ShutdownServer ();
1524 SV_UnlockThreadMutex();
1526 // allocate a net connection to keep track of things
1527 cls.netcon = NetConn_Open(mysocket, peeraddress);
1528 crypto = &cls.netcon->crypto;
1529 if(cls.crypto.authenticated)
1531 Crypto_FinishInstance(crypto, &cls.crypto);
1532 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1533 crypto->use_aes ? "Encrypted" : "Authenticated",
1534 cls.netcon->address,
1535 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1536 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1537 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1538 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1539 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1540 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1543 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1544 key_dest = key_game;
1548 cls.demonum = -1; // not in the demo loop now
1549 cls.state = ca_connected;
1550 cls.signon = 0; // need all the signon messages before playing
1551 cls.protocol = initialprotocol;
1552 // reset move sequence numbering on this new connection
1553 cls.servermovesequence = 0;
1554 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1555 Cmd_ForwardStringToServer("new");
1556 if (cls.protocol == PROTOCOL_QUAKE)
1558 // write a keepalive (clc_nop) as it seems to greatly improve the
1559 // chances of connecting to a netquake server
1561 unsigned char buf[4];
1562 memset(&msg, 0, sizeof(msg));
1564 msg.maxsize = sizeof(buf);
1565 MSG_WriteChar(&msg, clc_nop);
1566 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1570 int NetConn_IsLocalGame(void)
1572 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1578 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1582 serverlist_entry_t *entry = NULL;
1584 // search the cache for this server and update it
1585 for (n = 0;n < serverlist_cachecount;n++) {
1586 entry = &serverlist_cache[ n ];
1587 if (!strcmp(addressstring, entry->info.cname))
1591 if (n == serverlist_cachecount)
1593 // LAN search doesnt require an answer from the master server so we wont
1594 // know the ping nor will it be initialized already...
1597 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1600 if (serverlist_maxcachecount <= serverlist_cachecount)
1602 serverlist_maxcachecount += 64;
1603 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1605 entry = &serverlist_cache[n];
1607 memset(entry, 0, sizeof(*entry));
1608 // store the data the engine cares about (address and ping)
1609 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1610 entry->info.ping = 100000;
1611 entry->querytime = realtime;
1612 // if not in the slist menu we should print the server to console
1613 if (serverlist_consoleoutput)
1614 Con_Printf("querying %s\n", addressstring);
1615 ++serverlist_cachecount;
1617 // if this is the first reply from this server, count it as having replied
1618 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1619 pingtime = bound(0, pingtime, 9999);
1620 if (entry->query == SQS_REFRESHING) {
1621 entry->info.ping = pingtime;
1622 entry->query = SQS_QUERIED;
1624 // convert to unsigned to catch the -1
1625 // I still dont like this but its better than the old 10000 magic ping number - as in easier to type and read :( [11/8/2007 Black]
1626 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1630 // other server info is updated by the caller
1634 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1636 serverlist_entry_t *entry = &serverlist_cache[n];
1637 serverlist_info_t *info = &entry->info;
1638 // update description strings for engine menu and console output
1639 dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
1640 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1642 info->gameversion != gameversion.integer
1645 gameversion_min.integer >= 0 // min/max range set by user/mod?
1646 && gameversion_max.integer >= 0
1647 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1648 && gameversion_max.integer >= info->gameversion
1651 info->mod, info->map);
1652 if (entry->query == SQS_QUERIED)
1654 if(!serverlist_paused)
1655 ServerList_ViewList_Remove(entry);
1657 // if not in the slist menu we should print the server to console (if wanted)
1658 else if( serverlist_consoleoutput )
1659 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1660 // and finally, update the view set
1661 if(!serverlist_paused)
1662 ServerList_ViewList_Insert( entry );
1663 // update the entry's state
1664 serverlist_cache[n].query = SQS_QUERIED;
1667 // returns true, if it's sensible to continue the processing
1668 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1670 serverlist_entry_t *entry;
1672 // ignore the rest of the message if the serverlist is full
1673 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1675 // also ignore it if we have already queried it (other master server response)
1676 for( n = 0 ; n < serverlist_cachecount ; n++ )
1677 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1680 if( n < serverlist_cachecount ) {
1681 // the entry has already been queried once or
1685 if (serverlist_maxcachecount <= n)
1687 serverlist_maxcachecount += 64;
1688 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1691 entry = &serverlist_cache[n];
1693 memset(entry, 0, sizeof(*entry));
1694 entry->protocol = protocol;
1695 // store the data the engine cares about (address and ping)
1696 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1698 entry->info.isfavorite = isfavorite;
1700 // no, then reset the ping right away
1701 entry->info.ping = -1;
1702 // we also want to increase the serverlist_cachecount then
1703 serverlist_cachecount++;
1706 entry->query = SQS_QUERYING;
1711 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1714 if (serverlist_consoleoutput)
1715 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1718 char ipstring [128];
1721 if (data[0] == '\\')
1723 unsigned short port = data[5] * 256 + data[6];
1725 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1726 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1728 // move on to next address in packet
1733 else if (data[0] == '/' && isextended && length >= 19)
1735 unsigned short port = data[17] * 256 + data[18];
1743 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1745 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1748 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1749 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1750 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1756 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1757 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1758 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1763 // move on to next address in packet
1769 Con_Print("Error while parsing the server list\n");
1773 if (serverlist_consoleoutput && developer_networking.integer)
1774 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1776 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1782 // begin or resume serverlist queries
1783 serverlist_querysleep = false;
1784 serverlist_querywaittime = realtime + 3;
1788 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1790 qboolean fromserver;
1792 char *string, addressstring2[128];
1793 char stringbuf[16384];
1794 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1797 char infostringvalue[MAX_INPUTLINE];
1802 // quakeworld ingame packet
1803 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1805 // convert the address to a string incase we need it
1806 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1808 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1810 // received a command string - strip off the packaging and put it
1811 // into our string buffer with NULL termination
1814 length = min(length, (int)sizeof(stringbuf) - 1);
1815 memcpy(stringbuf, data, length);
1816 stringbuf[length] = 0;
1819 if (developer_networking.integer)
1821 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1822 Com_HexDumpToConsole(data, length);
1825 sendlength = sizeof(senddata) - 4;
1826 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1828 case CRYPTO_NOMATCH:
1834 memcpy(senddata, "\377\377\377\377", 4);
1835 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1838 case CRYPTO_DISCARD:
1841 memcpy(senddata, "\377\377\377\377", 4);
1842 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1846 case CRYPTO_REPLACE:
1847 string = senddata+4;
1848 length = (int)sendlength;
1852 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1855 for (j = 0;j < MAX_RCONS;j++)
1857 // note: this value from i is used outside the loop too...
1858 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1859 if(cls.rcon_commands[i][0])
1860 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1869 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1870 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1872 e = strchr(rcon_password.string, ' ');
1873 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1875 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1879 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1880 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1881 cls.rcon_commands[i][0] = 0;
1884 for (k = 0;k < MAX_RCONS;k++)
1885 if(cls.rcon_commands[k][0])
1886 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1891 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1892 // extend the timeout on other requests as we asked for a challenge
1893 for (l = 0;l < MAX_RCONS;l++)
1894 if(cls.rcon_commands[l][0])
1895 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1896 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1899 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1903 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1905 // darkplaces or quake3
1906 char protocolnames[1400];
1907 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1908 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1909 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1912 Protocol_Names(protocolnames, sizeof(protocolnames));
1914 M_Update_Return_Reason("Got challenge response");
1916 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1917 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1918 // TODO: add userinfo stuff here instead of using NQ commands?
1919 memcpy(senddata, "\377\377\377\377", 4);
1920 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1921 NetConn_WriteString(mysocket, senddata, peeraddress);
1924 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1926 // darkplaces or quake3
1927 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1928 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1932 M_Update_Return_Reason("Accepted");
1934 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1937 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1939 char rejectreason[128];
1940 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1941 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1944 cls.connect_trying = false;
1946 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1947 memcpy(rejectreason, string, length);
1948 rejectreason[length] = 0;
1950 M_Update_Return_Reason(rejectreason);
1955 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1957 serverlist_info_t *info;
1962 // search the cache for this server and update it
1963 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1967 info = &serverlist_cache[n].info;
1972 info->qcstatus[0] = 0;
1973 info->players[0] = 0;
1974 info->protocol = -1;
1975 info->numplayers = 0;
1977 info->maxplayers = 0;
1978 info->gameversion = 0;
1980 p = strchr(string, '\n');
1983 *p = 0; // cut off the string there
1987 Con_Printf("statusResponse without players block?\n");
1989 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1990 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1991 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1992 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1993 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1994 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1995 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1996 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1997 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1998 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1999 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2000 info->numhumans = info->numplayers - max(0, info->numbots);
2001 info->freeslots = info->maxplayers - info->numplayers;
2003 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2007 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2009 serverlist_info_t *info;
2013 // search the cache for this server and update it
2014 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2018 info = &serverlist_cache[n].info;
2023 info->qcstatus[0] = 0;
2024 info->players[0] = 0;
2025 info->protocol = -1;
2026 info->numplayers = 0;
2028 info->maxplayers = 0;
2029 info->gameversion = 0;
2031 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2032 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2033 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2034 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2035 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2036 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2037 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2038 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2039 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2040 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2041 info->numhumans = info->numplayers - max(0, info->numbots);
2042 info->freeslots = info->maxplayers - info->numplayers;
2044 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2048 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2050 // Extract the IP addresses
2053 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2056 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2058 // Extract the IP addresses
2061 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2064 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2066 // Extract the IP addresses
2070 if (serverlist_consoleoutput)
2071 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2072 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2074 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2075 if (serverlist_consoleoutput && developer_networking.integer)
2076 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2078 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2082 // move on to next address in packet
2086 // begin or resume serverlist queries
2087 serverlist_querysleep = false;
2088 serverlist_querywaittime = realtime + 3;
2092 if (!strncmp(string, "extResponse ", 12))
2094 ++cl_net_extresponse_count;
2095 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2096 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2097 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2098 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2101 if (!strncmp(string, "ping", 4))
2103 if (developer_extra.integer)
2104 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2105 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2108 if (!strncmp(string, "ack", 3))
2110 // QuakeWorld compatibility
2111 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2113 // challenge message
2114 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2115 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2118 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2120 M_Update_Return_Reason("Got QuakeWorld challenge response");
2122 cls.qw_qport = qport.integer;
2123 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2124 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2125 memcpy(senddata, "\377\377\377\377", 4);
2126 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo);
2127 NetConn_WriteString(mysocket, senddata, peeraddress);
2130 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2133 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2134 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2138 M_Update_Return_Reason("QuakeWorld Accepted");
2140 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2143 if (length > 2 && !memcmp(string, "n\\", 2))
2146 serverlist_info_t *info;
2150 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2151 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2154 // search the cache for this server and update it
2155 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2159 info = &serverlist_cache[n].info;
2160 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2161 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2162 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2163 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2165 info->numplayers = 0; // updated below
2166 info->numhumans = 0; // updated below
2167 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2168 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2170 // count active players on server
2171 // (we could gather more info, but we're just after the number)
2172 s = strchr(string, '\n');
2176 while (s < string + length)
2178 for (;s < string + length && *s != '\n';s++)
2180 if (s >= string + length)
2188 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2192 if (string[0] == 'n')
2194 // qw print command, used by rcon replies too
2195 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2196 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2199 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2201 // we may not have liked the packet, but it was a command packet, so
2202 // we're done processing this packet now
2205 // quakeworld ingame packet
2206 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2209 CL_ParseServerMessage();
2212 // netquake control packets, supported for compatibility only
2213 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2217 serverlist_info_t *info;
2222 SZ_Clear(&cl_message);
2223 SZ_Write(&cl_message, data, length);
2224 MSG_BeginReading(&cl_message);
2225 c = MSG_ReadByte(&cl_message);
2229 if (developer_extra.integer)
2230 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2231 if (cls.connect_trying)
2233 lhnetaddress_t clientportaddress;
2234 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2235 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2238 clientportaddress = *peeraddress;
2239 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2240 // extra ProQuake stuff
2242 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2244 cls.proquake_servermod = 0;
2246 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2248 cls.proquake_serverversion = 0;
2250 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2252 cls.proquake_serverflags = 0;
2253 if (cls.proquake_servermod == 1)
2254 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2255 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2256 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2258 M_Update_Return_Reason("Accepted");
2260 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2264 if (developer_extra.integer) {
2265 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2268 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2270 cls.connect_trying = false;
2272 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2275 case CCREP_SERVER_INFO:
2276 if (developer_extra.integer)
2277 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2279 // LordHavoc: because the quake server may report weird addresses
2280 // we just ignore it and keep the real address
2281 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2282 // search the cache for this server and update it
2283 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2287 info = &serverlist_cache[n].info;
2288 strlcpy(info->game, "Quake", sizeof(info->game));
2289 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2290 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2291 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2292 info->numplayers = MSG_ReadByte(&cl_message);
2293 info->maxplayers = MSG_ReadByte(&cl_message);
2294 info->protocol = MSG_ReadByte(&cl_message);
2296 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2299 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2300 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2301 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2304 if (developer_extra.integer)
2305 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2307 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2309 case CCREP_PLAYER_INFO:
2310 // we got a CCREP_PLAYER_INFO??
2311 //if (developer_extra.integer)
2312 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2314 case CCREP_RULE_INFO:
2315 // we got a CCREP_RULE_INFO??
2316 //if (developer_extra.integer)
2317 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2322 SZ_Clear(&cl_message);
2323 // we may not have liked the packet, but it was a valid control
2324 // packet, so we're done processing this packet now
2328 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2329 CL_ParseServerMessage();
2334 void NetConn_QueryQueueFrame(void)
2340 static double querycounter = 0;
2342 if(!net_slist_pause.integer && serverlist_paused)
2343 ServerList_RebuildViewList();
2344 serverlist_paused = net_slist_pause.integer != 0;
2346 if (serverlist_querysleep)
2349 // apply a cool down time after master server replies,
2350 // to avoid messing up the ping times on the servers
2351 if (serverlist_querywaittime > realtime)
2354 // each time querycounter reaches 1.0 issue a query
2355 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2356 maxqueries = (int)querycounter;
2357 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2358 querycounter -= maxqueries;
2360 if( maxqueries == 0 ) {
2364 // scan serverlist and issue queries as needed
2365 serverlist_querysleep = true;
2367 timeouttime = realtime - net_slist_timeout.value;
2368 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2370 serverlist_entry_t *entry = &serverlist_cache[ index ];
2371 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2376 serverlist_querysleep = false;
2377 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2382 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2384 lhnetaddress_t address;
2387 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2388 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2390 for (socket = 0; socket < cl_numsockets ; socket++)
2391 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2395 for (socket = 0; socket < cl_numsockets ; socket++)
2396 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2399 // update the entry fields
2400 entry->querytime = realtime;
2401 entry->querycounter++;
2403 // if not in the slist menu we should print the server to console
2404 if (serverlist_consoleoutput)
2405 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2411 // have we tried to refresh this server?
2412 if( entry->query == SQS_REFRESHING ) {
2413 // yes, so update the reply count (since its not responding anymore)
2415 if(!serverlist_paused)
2416 ServerList_ViewList_Remove(entry);
2418 entry->query = SQS_TIMEDOUT;
2424 void NetConn_ClientFrame(void)
2427 lhnetaddress_t peeraddress;
2428 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2429 NetConn_UpdateSockets();
2430 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2433 if (cls.connect_remainingtries == 0)
2434 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2436 cls.connect_nextsendtime = realtime + 1;
2437 cls.connect_remainingtries--;
2438 if (cls.connect_remainingtries <= -10)
2440 cls.connect_trying = false;
2442 M_Update_Return_Reason("Connect: Failed");
2446 // try challenge first (newer DP server or QW)
2447 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2448 // then try netquake as a fallback (old server, or netquake)
2449 SZ_Clear(&cl_message);
2450 // save space for the header, filled in later
2451 MSG_WriteLong(&cl_message, 0);
2452 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2453 MSG_WriteString(&cl_message, "QUAKE");
2454 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2455 // extended proquake stuff
2456 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2457 // this version matches ProQuake 3.40, the first version to support
2458 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2459 // higher clients, so we pretend we are that version...
2460 MSG_WriteByte(&cl_message, 34); // version * 10
2461 MSG_WriteByte(&cl_message, 0); // flags
2462 MSG_WriteLong(&cl_message, 0); // password
2463 // write the packetsize now...
2464 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2465 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2466 SZ_Clear(&cl_message);
2468 for (i = 0;i < cl_numsockets;i++)
2470 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2472 // R_TimeReport("clientreadnetwork");
2473 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2474 // R_TimeReport("clientparsepacket");
2478 NetConn_QueryQueueFrame();
2480 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2482 Con_Print("Connection timed out\n");
2484 SV_LockThreadMutex();
2485 Host_ShutdownServer ();
2486 SV_UnlockThreadMutex();
2490 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2494 for (i = 0;i < bufferlength - 1;i++)
2498 c = rand () % (127 - 33) + 33;
2499 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2505 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2506 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2508 prvm_prog_t *prog = SVVM_prog;
2510 unsigned int nb_clients = 0, nb_bots = 0, i;
2513 const char *crypto_idstring;
2516 // How many clients are there?
2517 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2519 if (svs.clients[i].active)
2522 if (!svs.clients[i].netconnection)
2528 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2534 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2535 if(*q != '\\' && *q != '\n')
2540 /// \TODO: we should add more information for the full status string
2541 crypto_idstring = Crypto_GetInfoResponseDataString();
2542 length = dpsnprintf(out_msg, out_size,
2543 "\377\377\377\377%s\x0A"
2544 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2545 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2550 fullstatus ? "statusResponse" : "infoResponse",
2551 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2552 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2553 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2554 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2555 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2556 fullstatus ? "\n" : "");
2558 // Make sure it fits in the buffer
2568 savelength = length;
2570 ptr = out_msg + length;
2571 left = (int)out_size - length;
2573 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2575 client_t *cl = &svs.clients[i];
2578 int nameind, cleanind, pingvalue;
2580 char cleanname [sizeof(cl->name)];
2584 // Remove all characters '"' and '\' in the player name
2589 curchar = cl->name[nameind++];
2590 if (curchar != '"' && curchar != '\\')
2592 cleanname[cleanind++] = curchar;
2593 if (cleanind == sizeof(cleanname) - 1)
2596 } while (curchar != '\0');
2597 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2599 pingvalue = (int)(cl->ping * 1000.0f);
2600 if(cl->netconnection)
2601 pingvalue = bound(1, pingvalue, 9999);
2606 ed = PRVM_EDICT_NUM(i + 1);
2607 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2613 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2614 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2619 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2621 if(cl->frags == -666) // spectator
2622 strlcpy(teambuf, " 0", sizeof(teambuf));
2623 else if(cl->colors == 0x44) // red team
2624 strlcpy(teambuf, " 1", sizeof(teambuf));
2625 else if(cl->colors == 0xDD) // blue team
2626 strlcpy(teambuf, " 2", sizeof(teambuf));
2627 else if(cl->colors == 0xCC) // yellow team
2628 strlcpy(teambuf, " 3", sizeof(teambuf));
2629 else if(cl->colors == 0x99) // pink team
2630 strlcpy(teambuf, " 4", sizeof(teambuf));
2632 strlcpy(teambuf, " 0", sizeof(teambuf));
2637 // note: team number is inserted according to SoF2 protocol
2639 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2645 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2654 // turn it into an infoResponse!
2655 out_msg[savelength] = 0;
2656 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2657 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2672 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2674 size_t floodslotnum, bestfloodslotnum;
2675 double bestfloodtime;
2676 lhnetaddress_t noportpeeraddress;
2677 // see if this is a connect flood
2678 noportpeeraddress = *peeraddress;
2679 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2680 bestfloodslotnum = 0;
2681 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2682 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2684 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2686 bestfloodtime = floodlist[floodslotnum].lasttime;
2687 bestfloodslotnum = floodslotnum;
2689 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2691 // this address matches an ongoing flood address
2692 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2696 // renew the ban on this address so it does not expire
2697 // until the flood has subsided
2698 floodlist[floodslotnum].lasttime = realtime;
2700 //Con_Printf("Flood detected!\n");
2703 // the flood appears to have subsided, so allow this
2704 bestfloodslotnum = floodslotnum; // reuse the same slot
2708 // begin a new timeout on this address
2709 floodlist[bestfloodslotnum].address = noportpeeraddress;
2710 floodlist[bestfloodslotnum].lasttime = realtime;
2711 //Con_Printf("Flood detection initiated!\n");
2715 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2717 size_t floodslotnum;
2718 lhnetaddress_t noportpeeraddress;
2719 // see if this is a connect flood
2720 noportpeeraddress = *peeraddress;
2721 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2722 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2724 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2726 // this address matches an ongoing flood address
2728 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2729 floodlist[floodslotnum].lasttime = 0;
2730 //Con_Printf("Flood cleared!\n");
2735 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2737 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2742 t1 = (long) time(NULL);
2743 t2 = strtol(s, NULL, 0);
2744 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2747 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2750 return !memcmp(mdfourbuf, hash, 16);
2753 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2758 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2761 // validate the challenge
2762 for (i = 0;i < MAX_CHALLENGES;i++)
2763 if(challenge[i].time > 0)
2764 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2766 // if the challenge is not recognized, drop the packet
2767 if (i == MAX_CHALLENGES)
2770 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2773 if(memcmp(mdfourbuf, hash, 16))
2776 // unmark challenge to prevent replay attacks
2777 challenge[i].time = 0;
2782 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2784 return !strcmp(password, hash);
2787 /// returns a string describing the user level, or NULL for auth failure
2788 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)
2790 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2791 static char buf[MAX_INPUTLINE];
2793 qboolean restricted = false;
2794 qboolean have_usernames = false;
2795 static char vabuf[1024];
2797 userpass_start = rcon_password.string;
2798 while((userpass_end = strchr(userpass_start, ' ')))
2800 have_usernames = true;
2801 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2803 if(comparator(peeraddress, buf, password, cs, cslen))
2805 userpass_start = userpass_end + 1;
2807 if(userpass_start[0])
2809 userpass_end = userpass_start + strlen(userpass_start);
2810 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2815 have_usernames = false;
2816 userpass_start = rcon_restricted_password.string;
2817 while((userpass_end = strchr(userpass_start, ' ')))
2819 have_usernames = true;
2820 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2822 if(comparator(peeraddress, buf, password, cs, cslen))
2824 userpass_start = userpass_end + 1;
2826 if(userpass_start[0])
2828 userpass_end = userpass_start + strlen(userpass_start);
2829 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2833 return NULL; // DENIED
2836 for(text = s; text != endpos; ++text)
2837 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2838 return NULL; // block possible exploits against the parser/alias expansion
2842 size_t l = strlen(s);
2845 hasquotes = (strchr(s, '"') != NULL);
2846 // sorry, we can't allow these substrings in wildcard expressions,
2847 // as they can mess with the argument counts
2848 text = rcon_restricted_commands.string;
2849 while(COM_ParseToken_Console(&text))
2851 // com_token now contains a pattern to check for...
2852 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2855 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2858 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2860 if(!strcmp(com_token, s))
2863 else // single-arg expression? must match the beginning of the command
2865 if(!strcmp(com_token, s))
2867 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2871 // if we got here, nothing matched!
2879 userpass_startpass = strchr(userpass_start, ':');
2880 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2881 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2883 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2886 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2890 // looks like a legitimate rcon command with the correct password
2891 const char *s_ptr = s;
2892 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2893 while(s_ptr != endpos)
2895 size_t l = strlen(s_ptr);
2897 Con_Printf(" %s;", s_ptr);
2902 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2903 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2906 size_t l = strlen(s);
2909 client_t *host_client_save = host_client;
2910 Cmd_ExecuteString(s, src_command, true);
2911 host_client = host_client_save;
2912 // in case it is a command that changes host_client (like restart)
2916 Con_Rcon_Redirect_End();
2920 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2924 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2926 int i, ret, clientnum, best;
2929 char *s, *string, response[1400], addressstring2[128];
2930 static char stringbuf[16384]; // server only
2931 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2932 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2933 size_t sendlength, response_len;
2934 char infostringvalue[MAX_INPUTLINE];
2939 // convert the address to a string incase we need it
2940 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2942 // see if we can identify the sender as a local player
2943 // (this is necessary for rcon to send a reliable reply if the client is
2944 // actually on the server, not sending remotely)
2945 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2946 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2948 if (i == svs.maxclients)
2951 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2953 // received a command string - strip off the packaging and put it
2954 // into our string buffer with NULL termination
2957 length = min(length, (int)sizeof(stringbuf) - 1);
2958 memcpy(stringbuf, data, length);
2959 stringbuf[length] = 0;
2962 if (developer_extra.integer)
2964 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2965 Com_HexDumpToConsole(data, length);
2968 sendlength = sizeof(senddata) - 4;
2969 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2971 case CRYPTO_NOMATCH:
2977 memcpy(senddata, "\377\377\377\377", 4);
2978 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2981 case CRYPTO_DISCARD:
2984 memcpy(senddata, "\377\377\377\377", 4);
2985 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2989 case CRYPTO_REPLACE:
2990 string = senddata+4;
2991 length = (int)sendlength;
2995 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2997 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2999 if(challenge[i].time > 0)
3000 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
3002 if (besttime > challenge[i].time)
3003 besttime = challenge[best = i].time;
3005 // if we did not find an exact match, choose the oldest and
3006 // update address and string
3007 if (i == MAX_CHALLENGES)
3010 challenge[i].address = *peeraddress;
3011 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
3015 // flood control: drop if requesting challenge too often
3016 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
3019 challenge[i].time = realtime;
3020 // send the challenge
3021 memcpy(response, "\377\377\377\377", 4);
3022 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenge[i].string);
3023 response_len = strlen(response) + 1;
3024 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3025 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3028 if (length > 8 && !memcmp(string, "connect\\", 8))
3030 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3034 if(crypto && crypto->authenticated)
3036 // no need to check challenge
3037 if(crypto_developer.integer)
3039 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3040 crypto->use_aes ? "Encrypted" : "Authenticated",
3042 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3043 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3044 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3045 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3046 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3047 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3053 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3055 // validate the challenge
3056 for (i = 0;i < MAX_CHALLENGES;i++)
3057 if(challenge[i].time > 0)
3058 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
3060 // if the challenge is not recognized, drop the packet
3061 if (i == MAX_CHALLENGES)
3066 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3067 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3069 if(!(islocal || sv_public.integer > -2))
3071 if (developer_extra.integer)
3072 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3073 memcpy(response, "\377\377\377\377", 4);
3074 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3075 NetConn_WriteString(mysocket, response, peeraddress);
3079 // check engine protocol
3080 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3082 if (developer_extra.integer)
3083 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3084 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3088 // see if this is a duplicate connection request or a disconnected
3089 // client who is rejoining to the same client slot
3090 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3092 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3094 // this is a known client...
3095 if(crypto && crypto->authenticated)
3097 // reject if changing key!
3098 if(client->netconnection->crypto.authenticated)
3101 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3103 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3105 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3107 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3110 if (developer_extra.integer)
3111 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3112 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3119 // reject if downgrading!
3120 if(client->netconnection->crypto.authenticated)
3122 if (developer_extra.integer)
3123 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3124 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3130 // client crashed and is coming back,
3131 // keep their stuff intact
3132 if (developer_extra.integer)
3133 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3134 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3135 if(crypto && crypto->authenticated)
3136 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3137 SV_SendServerinfo(client);
3141 // client is still trying to connect,
3142 // so we send a duplicate reply
3143 if (developer_extra.integer)
3144 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3145 if(crypto && crypto->authenticated)
3146 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3147 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3153 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3156 // find an empty client slot for this new client
3157 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3160 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3162 // allocated connection
3163 if (developer_extra.integer)
3164 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3165 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3166 // now set up the client
3167 if(crypto && crypto->authenticated)
3168 Crypto_FinishInstance(&conn->crypto, crypto);
3169 SV_ConnectClient(clientnum, conn);
3170 NetConn_Heartbeat(1);
3175 // no empty slots found - server is full
3176 if (developer_extra.integer)
3177 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3178 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3182 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3184 const char *challenge = NULL;
3186 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3189 // If there was a challenge in the getinfo message
3190 if (length > 8 && string[7] == ' ')
3191 challenge = string + 8;
3193 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3195 if (developer_extra.integer)
3196 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3197 NetConn_WriteString(mysocket, response, peeraddress);
3201 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3203 const char *challenge = NULL;
3205 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3208 // If there was a challenge in the getinfo message
3209 if (length > 10 && string[9] == ' ')
3210 challenge = string + 10;
3212 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3214 if (developer_extra.integer)
3215 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3216 NetConn_WriteString(mysocket, response, peeraddress);
3220 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3222 char *password = string + 20;
3223 char *timeval = string + 37;
3224 char *s = strchr(timeval, ' ');
3225 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3226 const char *userlevel;
3228 if(rcon_secure.integer > 1)
3232 return true; // invalid packet
3235 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3236 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3239 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3241 char *password = string + 25;
3242 char *challenge = string + 42;
3243 char *s = strchr(challenge, ' ');
3244 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3245 const char *userlevel;
3247 return true; // invalid packet
3250 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3251 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3254 if (length >= 5 && !memcmp(string, "rcon ", 5))
3257 char *s = string + 5;
3258 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3261 if(rcon_secure.integer > 0)
3264 for (i = 0;!ISWHITESPACE(*s);s++)
3265 if (i < (int)sizeof(password) - 1)
3267 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3270 if (!ISWHITESPACE(password[0]))
3272 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3273 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3277 if (!strncmp(string, "extResponse ", 12))
3279 ++sv_net_extresponse_count;
3280 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3281 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3282 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3283 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3286 if (!strncmp(string, "ping", 4))
3288 if (developer_extra.integer)
3289 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3290 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3293 if (!strncmp(string, "ack", 3))
3295 // we may not have liked the packet, but it was a command packet, so
3296 // we're done processing this packet now
3299 // netquake control packets, supported for compatibility only, and only
3300 // when running game protocols that are normally served via this connection
3302 // (this protects more modern protocols against being used for
3303 // Quake packet flood Denial Of Service attacks)
3304 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)
3308 const char *protocolname;
3311 SZ_Clear(&sv_message);
3312 SZ_Write(&sv_message, data, length);
3313 MSG_BeginReading(&sv_message);
3314 c = MSG_ReadByte(&sv_message);
3318 if (developer_extra.integer)
3319 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3320 if(!(islocal || sv_public.integer > -2))
3322 if (developer_extra.integer)
3323 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3324 SZ_Clear(&sv_message);
3325 // save space for the header, filled in later
3326 MSG_WriteLong(&sv_message, 0);
3327 MSG_WriteByte(&sv_message, CCREP_REJECT);
3328 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3329 MSG_WriteString(&sv_message, "\n");
3330 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3331 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3332 SZ_Clear(&sv_message);
3336 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3337 protocolnumber = MSG_ReadByte(&sv_message);
3338 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3340 if (developer_extra.integer)
3341 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3342 SZ_Clear(&sv_message);
3343 // save space for the header, filled in later
3344 MSG_WriteLong(&sv_message, 0);
3345 MSG_WriteByte(&sv_message, CCREP_REJECT);
3346 MSG_WriteString(&sv_message, "Incompatible version.\n");
3347 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3348 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3349 SZ_Clear(&sv_message);
3353 // see if this connect request comes from a known client
3354 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3356 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3358 // this is either a duplicate connection request
3359 // or coming back from a timeout
3360 // (if so, keep their stuff intact)
3362 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3363 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3365 if (developer_extra.integer)
3366 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3367 SZ_Clear(&sv_message);
3368 // save space for the header, filled in later
3369 MSG_WriteLong(&sv_message, 0);
3370 MSG_WriteByte(&sv_message, CCREP_REJECT);
3371 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3372 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3373 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3374 SZ_Clear(&sv_message);
3379 if (developer_extra.integer)
3380 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3381 SZ_Clear(&sv_message);
3382 // save space for the header, filled in later
3383 MSG_WriteLong(&sv_message, 0);
3384 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3385 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3386 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3387 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3388 SZ_Clear(&sv_message);
3390 // if client is already spawned, re-send the
3391 // serverinfo message as they'll need it to play
3393 SV_SendServerinfo(client);
3398 // this is a new client, check for connection flood
3399 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3402 // find a slot for the new client
3403 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3406 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3408 // connect to the client
3409 // everything is allocated, just fill in the details
3410 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3411 if (developer_extra.integer)
3412 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3413 // send back the info about the server connection
3414 SZ_Clear(&sv_message);
3415 // save space for the header, filled in later
3416 MSG_WriteLong(&sv_message, 0);
3417 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3418 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3419 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3420 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3421 SZ_Clear(&sv_message);
3422 // now set up the client struct
3423 SV_ConnectClient(clientnum, conn);
3424 NetConn_Heartbeat(1);
3429 if (developer_extra.integer)
3430 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3431 // no room; try to let player know
3432 SZ_Clear(&sv_message);
3433 // save space for the header, filled in later
3434 MSG_WriteLong(&sv_message, 0);
3435 MSG_WriteByte(&sv_message, CCREP_REJECT);
3436 MSG_WriteString(&sv_message, "Server is full.\n");
3437 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3438 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3439 SZ_Clear(&sv_message);
3441 case CCREQ_SERVER_INFO:
3442 if (developer_extra.integer)
3443 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3444 if(!(islocal || sv_public.integer > -1))
3447 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3450 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3453 char myaddressstring[128];
3454 if (developer_extra.integer)
3455 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3456 SZ_Clear(&sv_message);
3457 // save space for the header, filled in later
3458 MSG_WriteLong(&sv_message, 0);
3459 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3460 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3461 MSG_WriteString(&sv_message, myaddressstring);
3462 MSG_WriteString(&sv_message, hostname.string);
3463 MSG_WriteString(&sv_message, sv.name);
3464 // How many clients are there?
3465 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3466 if (svs.clients[i].active)
3468 MSG_WriteByte(&sv_message, numclients);
3469 MSG_WriteByte(&sv_message, svs.maxclients);
3470 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3471 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3472 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3473 SZ_Clear(&sv_message);
3476 case CCREQ_PLAYER_INFO:
3477 if (developer_extra.integer)
3478 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3479 if(!(islocal || sv_public.integer > -1))
3482 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3487 int playerNumber, activeNumber, clientNumber;
3490 playerNumber = MSG_ReadByte(&sv_message);
3492 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3493 if (client->active && ++activeNumber == playerNumber)
3495 if (clientNumber != svs.maxclients)
3497 SZ_Clear(&sv_message);
3498 // save space for the header, filled in later
3499 MSG_WriteLong(&sv_message, 0);
3500 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3501 MSG_WriteByte(&sv_message, playerNumber);
3502 MSG_WriteString(&sv_message, client->name);
3503 MSG_WriteLong(&sv_message, client->colors);
3504 MSG_WriteLong(&sv_message, client->frags);
3505 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3506 if(sv_status_privacy.integer)
3507 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3509 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3510 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3511 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3512 SZ_Clear(&sv_message);
3516 case CCREQ_RULE_INFO:
3517 if (developer_extra.integer)
3518 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3519 if(!(islocal || sv_public.integer > -1))
3522 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3529 // find the search start location
3530 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3531 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3533 // send the response
3534 SZ_Clear(&sv_message);
3535 // save space for the header, filled in later
3536 MSG_WriteLong(&sv_message, 0);
3537 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3540 MSG_WriteString(&sv_message, var->name);
3541 MSG_WriteString(&sv_message, var->string);
3543 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3544 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3545 SZ_Clear(&sv_message);
3549 if (developer_extra.integer)
3550 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3551 if (sv.active && !rcon_secure.integer)
3553 char password[2048];
3557 const char *userlevel;
3558 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3559 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3561 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3562 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3563 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3570 SZ_Clear(&sv_message);
3571 // we may not have liked the packet, but it was a valid control
3572 // packet, so we're done processing this packet now
3577 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3579 SV_ReadClientMessage();
3586 void NetConn_ServerFrame(void)
3589 lhnetaddress_t peeraddress;
3590 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3591 for (i = 0;i < sv_numsockets;i++)
3592 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3593 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3594 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3596 // never timeout loopback connections
3597 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3599 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3600 SV_DropClient(false);
3605 void NetConn_SleepMicroseconds(int microseconds)
3607 LHNET_SleepUntilPacket_Microseconds(microseconds);
3611 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3615 lhnetaddress_t masteraddress;
3616 lhnetaddress_t broadcastaddress;
3619 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3622 // 26000 is the default quake server port, servers on other ports will not
3624 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3625 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3629 for (i = 0;i < cl_numsockets;i++)
3633 const char *cmdname, *extraoptions;
3634 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3636 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3638 // search LAN for Quake servers
3639 SZ_Clear(&cl_message);
3640 // save space for the header, filled in later
3641 MSG_WriteLong(&cl_message, 0);
3642 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3643 MSG_WriteString(&cl_message, "QUAKE");
3644 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3645 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3646 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3647 SZ_Clear(&cl_message);
3649 // search LAN for DarkPlaces servers
3650 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3653 // build the getservers message to send to the dpmaster master servers
3654 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3656 cmdname = "getserversExt";
3657 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3661 cmdname = "getservers";
3664 memcpy(request, "\377\377\377\377", 4);
3665 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3668 for (masternum = 0;sv_masters[masternum].name;masternum++)
3670 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3673 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3677 // search favorite servers
3678 for(j = 0; j < nFavorites; ++j)
3680 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3682 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3683 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3690 // only query QuakeWorld servers when the user wants to
3693 for (i = 0;i < cl_numsockets;i++)
3697 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3699 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3701 // search LAN for QuakeWorld servers
3702 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3704 // build the getservers message to send to the qwmaster master servers
3705 // note this has no -1 prefix, and the trailing nul byte is sent
3706 dpsnprintf(request, sizeof(request), "c\n");
3710 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3712 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3714 if (m_state != m_slist)
3716 char lookupstring[128];
3717 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3718 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3721 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3725 // search favorite servers
3726 for(j = 0; j < nFavorites; ++j)
3728 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3730 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3732 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3733 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3740 if (!masterquerycount)
3742 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3743 M_Update_Return_Reason("No network");
3748 void NetConn_Heartbeat(int priority)
3750 lhnetaddress_t masteraddress;
3752 lhnetsocket_t *mysocket;
3754 // if it's a state change (client connected), limit next heartbeat to no
3755 // more than 30 sec in the future
3756 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3757 nextheartbeattime = realtime + 30.0;
3759 // limit heartbeatperiod to 30 to 270 second range,
3760 // lower limit is to avoid abusing master servers with excess traffic,
3761 // upper limit is to avoid timing out on the master server (which uses
3763 if (sv_heartbeatperiod.value < 30)
3764 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3765 if (sv_heartbeatperiod.value > 270)
3766 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3768 // make advertising optional and don't advertise singleplayer games, and
3769 // only send a heartbeat as often as the admin wants
3770 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3772 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3773 for (masternum = 0;sv_masters[masternum].name;masternum++)
3774 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3775 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3779 static void Net_Heartbeat_f(void)
3782 NetConn_Heartbeat(2);
3784 Con_Print("No server running, can not heartbeat to master server.\n");
3787 static void PrintStats(netconn_t *conn)
3789 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3790 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3792 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3793 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3794 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3795 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3796 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3797 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3798 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3799 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3800 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3801 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3804 void Net_Stats_f(void)
3807 Con_Print("connections =\n");
3808 for (conn = netconn_list;conn;conn = conn->next)
3813 void Net_Refresh_f(void)
3815 if (m_state != m_slist) {
3816 Con_Print("Sending new requests to master servers\n");
3817 ServerList_QueryList(false, true, false, true);
3818 Con_Print("Listening for replies...\n");
3820 ServerList_QueryList(false, true, false, false);
3823 void Net_Slist_f(void)
3825 ServerList_ResetMasks();
3826 serverlist_sortbyfield = SLIF_PING;
3827 serverlist_sortflags = 0;
3828 if (m_state != m_slist) {
3829 Con_Print("Sending requests to master servers\n");
3830 ServerList_QueryList(true, true, false, true);
3831 Con_Print("Listening for replies...\n");
3833 ServerList_QueryList(true, true, false, false);
3836 void Net_SlistQW_f(void)
3838 ServerList_ResetMasks();
3839 serverlist_sortbyfield = SLIF_PING;
3840 serverlist_sortflags = 0;
3841 if (m_state != m_slist) {
3842 Con_Print("Sending requests to master servers\n");
3843 ServerList_QueryList(true, false, true, true);
3844 serverlist_consoleoutput = true;
3845 Con_Print("Listening for replies...\n");
3847 ServerList_QueryList(true, false, true, false);
3851 void NetConn_Init(void)
3854 lhnetaddress_t tempaddress;
3855 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3856 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3858 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3859 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3860 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3862 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3863 Cvar_RegisterVariable(&net_test);
3864 Cvar_RegisterVariable(&net_usesizelimit);
3865 Cvar_RegisterVariable(&net_burstreserve);
3866 Cvar_RegisterVariable(&rcon_restricted_password);
3867 Cvar_RegisterVariable(&rcon_restricted_commands);
3868 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3869 Cvar_RegisterVariable(&net_slist_queriespersecond);
3870 Cvar_RegisterVariable(&net_slist_queriesperframe);
3871 Cvar_RegisterVariable(&net_slist_timeout);
3872 Cvar_RegisterVariable(&net_slist_maxtries);
3873 Cvar_RegisterVariable(&net_slist_favorites);
3874 Cvar_RegisterVariable(&net_slist_pause);
3875 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3876 Cvar_RegisterVariable(&net_tos_dscp);
3877 Cvar_RegisterVariable(&net_messagetimeout);
3878 Cvar_RegisterVariable(&net_connecttimeout);
3879 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3880 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3881 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3882 Cvar_RegisterVariable(&net_sourceaddresscheck);
3883 Cvar_RegisterVariable(&cl_netlocalping);
3884 Cvar_RegisterVariable(&cl_netpacketloss_send);
3885 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3886 Cvar_RegisterVariable(&hostname);
3887 Cvar_RegisterVariable(&developer_networking);
3888 Cvar_RegisterVariable(&cl_netport);
3889 Cvar_RegisterVariable(&sv_netport);
3890 Cvar_RegisterVariable(&net_address);
3891 Cvar_RegisterVariable(&net_address_ipv6);
3892 Cvar_RegisterVariable(&sv_public);
3893 Cvar_RegisterVariable(&sv_public_rejectreason);
3894 Cvar_RegisterVariable(&sv_heartbeatperiod);
3895 for (i = 0;sv_masters[i].name;i++)
3896 Cvar_RegisterVariable(&sv_masters[i]);
3897 Cvar_RegisterVariable(&gameversion);
3898 Cvar_RegisterVariable(&gameversion_min);
3899 Cvar_RegisterVariable(&gameversion_max);
3900 // 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.
3901 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3903 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3905 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3906 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3909 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3911 // 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
3912 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3914 i = atoi(com_argv[i + 1]);
3915 if (i >= 0 && i < 65536)
3917 Con_Printf("-port option used, setting port cvar to %i\n", i);
3918 Cvar_SetValueQuick(&sv_netport, i);
3921 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3925 cl_message.data = cl_message_buf;
3926 cl_message.maxsize = sizeof(cl_message_buf);
3927 cl_message.cursize = 0;
3928 sv_message.data = sv_message_buf;
3929 sv_message.maxsize = sizeof(sv_message_buf);
3930 sv_message.cursize = 0;
3932 if (Thread_HasThreads())
3933 netconn_mutex = Thread_CreateMutex();
3936 void NetConn_Shutdown(void)
3938 NetConn_CloseClientPorts();
3939 NetConn_CloseServerPorts();
3942 Thread_DestroyMutex(netconn_mutex);
3943 netconn_mutex = NULL;