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 challenges[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;
2514 const char *worldstatusstr;
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 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2529 if(worldstatusstr && *worldstatusstr)
2534 for(q = worldstatusstr; *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 *client = &svs.clients[i];
2578 int nameind, cleanind, pingvalue;
2580 char cleanname [sizeof(client->name)];
2581 const char *statusstr;
2584 // Remove all characters '"' and '\' in the player name
2589 curchar = client->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)(client->ping * 1000.0f);
2600 if(client->netconnection)
2601 pingvalue = bound(1, pingvalue, 9999);
2606 ed = PRVM_EDICT_NUM(i + 1);
2607 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2608 if(statusstr && *statusstr)
2613 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2614 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2619 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2621 if(client->frags == -666) // spectator
2622 strlcpy(teambuf, " 0", sizeof(teambuf));
2623 else if(client->colors == 0x44) // red team
2624 strlcpy(teambuf, " 1", sizeof(teambuf));
2625 else if(client->colors == 0xDD) // blue team
2626 strlcpy(teambuf, " 2", sizeof(teambuf));
2627 else if(client->colors == 0xCC) // yellow team
2628 strlcpy(teambuf, " 3", sizeof(teambuf));
2629 else if(client->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(challenges[0].string)) - 1)
2761 // validate the challenge
2762 for (i = 0;i < MAX_CHALLENGES;i++)
2763 if(challenges[i].time > 0)
2764 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[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 challenges[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;
2928 char *string, response[1400], addressstring2[128];
2929 static char stringbuf[16384]; // server only
2930 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2931 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2932 size_t sendlength, response_len;
2933 char infostringvalue[MAX_INPUTLINE];
2938 // convert the address to a string incase we need it
2939 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2941 // see if we can identify the sender as a local player
2942 // (this is necessary for rcon to send a reliable reply if the client is
2943 // actually on the server, not sending remotely)
2944 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2945 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2947 if (i == svs.maxclients)
2950 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2952 // received a command string - strip off the packaging and put it
2953 // into our string buffer with NULL termination
2956 length = min(length, (int)sizeof(stringbuf) - 1);
2957 memcpy(stringbuf, data, length);
2958 stringbuf[length] = 0;
2961 if (developer_extra.integer)
2963 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2964 Com_HexDumpToConsole(data, length);
2967 sendlength = sizeof(senddata) - 4;
2968 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2970 case CRYPTO_NOMATCH:
2976 memcpy(senddata, "\377\377\377\377", 4);
2977 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2980 case CRYPTO_DISCARD:
2983 memcpy(senddata, "\377\377\377\377", 4);
2984 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2988 case CRYPTO_REPLACE:
2989 string = senddata+4;
2990 length = (int)sendlength;
2994 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2996 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2998 if(challenges[i].time > 0)
2999 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3001 if (besttime > challenges[i].time)
3002 besttime = challenges[best = i].time;
3004 // if we did not find an exact match, choose the oldest and
3005 // update address and string
3006 if (i == MAX_CHALLENGES)
3009 challenges[i].address = *peeraddress;
3010 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3014 // flood control: drop if requesting challenge too often
3015 if(challenges[i].time > realtime - net_challengefloodblockingtimeout.value)
3018 challenges[i].time = realtime;
3019 // send the challenge
3020 memcpy(response, "\377\377\377\377", 4);
3021 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3022 response_len = strlen(response) + 1;
3023 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3024 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3027 if (length > 8 && !memcmp(string, "connect\\", 8))
3031 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3035 if(crypto && crypto->authenticated)
3037 // no need to check challenge
3038 if(crypto_developer.integer)
3040 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3041 crypto->use_aes ? "Encrypted" : "Authenticated",
3043 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3044 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3045 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3046 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3047 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3048 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3054 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3056 // validate the challenge
3057 for (i = 0;i < MAX_CHALLENGES;i++)
3058 if(challenges[i].time > 0)
3059 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3061 // if the challenge is not recognized, drop the packet
3062 if (i == MAX_CHALLENGES)
3067 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3068 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3070 if(!(islocal || sv_public.integer > -2))
3072 if (developer_extra.integer)
3073 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3074 memcpy(response, "\377\377\377\377", 4);
3075 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3076 NetConn_WriteString(mysocket, response, peeraddress);
3080 // check engine protocol
3081 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3083 if (developer_extra.integer)
3084 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3085 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3089 // see if this is a duplicate connection request or a disconnected
3090 // client who is rejoining to the same client slot
3091 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3093 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3095 // this is a known client...
3096 if(crypto && crypto->authenticated)
3098 // reject if changing key!
3099 if(client->netconnection->crypto.authenticated)
3102 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3104 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3106 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3108 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3111 if (developer_extra.integer)
3112 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3113 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3120 // reject if downgrading!
3121 if(client->netconnection->crypto.authenticated)
3123 if (developer_extra.integer)
3124 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3125 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3131 // client crashed and is coming back,
3132 // keep their stuff intact
3133 if (developer_extra.integer)
3134 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3135 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3136 if(crypto && crypto->authenticated)
3137 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3138 SV_SendServerinfo(client);
3142 // client is still trying to connect,
3143 // so we send a duplicate reply
3144 if (developer_extra.integer)
3145 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3146 if(crypto && crypto->authenticated)
3147 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3148 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3154 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3157 // find an empty client slot for this new client
3158 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3161 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3163 // allocated connection
3164 if (developer_extra.integer)
3165 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3166 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3167 // now set up the client
3168 if(crypto && crypto->authenticated)
3169 Crypto_FinishInstance(&conn->crypto, crypto);
3170 SV_ConnectClient(clientnum, conn);
3171 NetConn_Heartbeat(1);
3176 // no empty slots found - server is full
3177 if (developer_extra.integer)
3178 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3179 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3183 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3185 const char *challenge = NULL;
3187 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3190 // If there was a challenge in the getinfo message
3191 if (length > 8 && string[7] == ' ')
3192 challenge = string + 8;
3194 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3196 if (developer_extra.integer)
3197 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3198 NetConn_WriteString(mysocket, response, peeraddress);
3202 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3204 const char *challenge = NULL;
3206 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3209 // If there was a challenge in the getinfo message
3210 if (length > 10 && string[9] == ' ')
3211 challenge = string + 10;
3213 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3215 if (developer_extra.integer)
3216 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3217 NetConn_WriteString(mysocket, response, peeraddress);
3221 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3223 char *password = string + 20;
3224 char *timeval = string + 37;
3225 char *s = strchr(timeval, ' ');
3226 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3227 const char *userlevel;
3229 if(rcon_secure.integer > 1)
3233 return true; // invalid packet
3236 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3237 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3240 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3242 char *password = string + 25;
3243 char *challenge = string + 42;
3244 char *s = strchr(challenge, ' ');
3245 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3246 const char *userlevel;
3248 return true; // invalid packet
3251 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3252 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3255 if (length >= 5 && !memcmp(string, "rcon ", 5))
3258 char *s = string + 5;
3259 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3262 if(rcon_secure.integer > 0)
3265 for (j = 0;!ISWHITESPACE(*s);s++)
3266 if (j < (int)sizeof(password) - 1)
3268 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3271 if (!ISWHITESPACE(password[0]))
3273 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3274 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3278 if (!strncmp(string, "extResponse ", 12))
3280 ++sv_net_extresponse_count;
3281 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3282 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3283 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3284 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3287 if (!strncmp(string, "ping", 4))
3289 if (developer_extra.integer)
3290 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3291 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3294 if (!strncmp(string, "ack", 3))
3296 // we may not have liked the packet, but it was a command packet, so
3297 // we're done processing this packet now
3300 // netquake control packets, supported for compatibility only, and only
3301 // when running game protocols that are normally served via this connection
3303 // (this protects more modern protocols against being used for
3304 // Quake packet flood Denial Of Service attacks)
3305 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)
3309 const char *protocolname;
3310 client_t *knownclient;
3311 client_t *newclient;
3314 SZ_Clear(&sv_message);
3315 SZ_Write(&sv_message, data, length);
3316 MSG_BeginReading(&sv_message);
3317 c = MSG_ReadByte(&sv_message);
3321 if (developer_extra.integer)
3322 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3323 if(!(islocal || sv_public.integer > -2))
3325 if (developer_extra.integer)
3326 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3327 SZ_Clear(&sv_message);
3328 // save space for the header, filled in later
3329 MSG_WriteLong(&sv_message, 0);
3330 MSG_WriteByte(&sv_message, CCREP_REJECT);
3331 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3332 MSG_WriteString(&sv_message, "\n");
3333 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3334 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3335 SZ_Clear(&sv_message);
3339 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3340 protocolnumber = MSG_ReadByte(&sv_message);
3341 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3343 if (developer_extra.integer)
3344 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3345 SZ_Clear(&sv_message);
3346 // save space for the header, filled in later
3347 MSG_WriteLong(&sv_message, 0);
3348 MSG_WriteByte(&sv_message, CCREP_REJECT);
3349 MSG_WriteString(&sv_message, "Incompatible version.\n");
3350 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3351 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3352 SZ_Clear(&sv_message);
3356 // see if this connect request comes from a known client
3357 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3359 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3361 // this is either a duplicate connection request
3362 // or coming back from a timeout
3363 // (if so, keep their stuff intact)
3365 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3366 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3368 if (developer_extra.integer)
3369 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3370 SZ_Clear(&sv_message);
3371 // save space for the header, filled in later
3372 MSG_WriteLong(&sv_message, 0);
3373 MSG_WriteByte(&sv_message, CCREP_REJECT);
3374 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3375 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3376 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3377 SZ_Clear(&sv_message);
3382 if (developer_extra.integer)
3383 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3384 SZ_Clear(&sv_message);
3385 // save space for the header, filled in later
3386 MSG_WriteLong(&sv_message, 0);
3387 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3388 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3389 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3390 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3391 SZ_Clear(&sv_message);
3393 // if client is already spawned, re-send the
3394 // serverinfo message as they'll need it to play
3395 if (knownclient->begun)
3396 SV_SendServerinfo(knownclient);
3401 // this is a new client, check for connection flood
3402 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3405 // find a slot for the new client
3406 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3409 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3411 // connect to the client
3412 // everything is allocated, just fill in the details
3413 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3414 if (developer_extra.integer)
3415 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3416 // send back the info about the server connection
3417 SZ_Clear(&sv_message);
3418 // save space for the header, filled in later
3419 MSG_WriteLong(&sv_message, 0);
3420 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3421 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3422 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3423 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3424 SZ_Clear(&sv_message);
3425 // now set up the client struct
3426 SV_ConnectClient(clientnum, conn);
3427 NetConn_Heartbeat(1);
3432 if (developer_extra.integer)
3433 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3434 // no room; try to let player know
3435 SZ_Clear(&sv_message);
3436 // save space for the header, filled in later
3437 MSG_WriteLong(&sv_message, 0);
3438 MSG_WriteByte(&sv_message, CCREP_REJECT);
3439 MSG_WriteString(&sv_message, "Server is full.\n");
3440 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3441 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3442 SZ_Clear(&sv_message);
3444 case CCREQ_SERVER_INFO:
3445 if (developer_extra.integer)
3446 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3447 if(!(islocal || sv_public.integer > -1))
3450 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3453 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3456 char myaddressstring[128];
3457 if (developer_extra.integer)
3458 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3459 SZ_Clear(&sv_message);
3460 // save space for the header, filled in later
3461 MSG_WriteLong(&sv_message, 0);
3462 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3463 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3464 MSG_WriteString(&sv_message, myaddressstring);
3465 MSG_WriteString(&sv_message, hostname.string);
3466 MSG_WriteString(&sv_message, sv.name);
3467 // How many clients are there?
3468 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3469 if (svs.clients[i].active)
3471 MSG_WriteByte(&sv_message, numclients);
3472 MSG_WriteByte(&sv_message, svs.maxclients);
3473 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3474 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3475 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3476 SZ_Clear(&sv_message);
3479 case CCREQ_PLAYER_INFO:
3480 if (developer_extra.integer)
3481 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3482 if(!(islocal || sv_public.integer > -1))
3485 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3490 int playerNumber, activeNumber, clientNumber;
3493 playerNumber = MSG_ReadByte(&sv_message);
3495 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3496 if (client->active && ++activeNumber == playerNumber)
3498 if (clientNumber != svs.maxclients)
3500 SZ_Clear(&sv_message);
3501 // save space for the header, filled in later
3502 MSG_WriteLong(&sv_message, 0);
3503 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3504 MSG_WriteByte(&sv_message, playerNumber);
3505 MSG_WriteString(&sv_message, client->name);
3506 MSG_WriteLong(&sv_message, client->colors);
3507 MSG_WriteLong(&sv_message, client->frags);
3508 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3509 if(sv_status_privacy.integer)
3510 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3512 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3513 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3514 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3515 SZ_Clear(&sv_message);
3519 case CCREQ_RULE_INFO:
3520 if (developer_extra.integer)
3521 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3522 if(!(islocal || sv_public.integer > -1))
3525 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3532 // find the search start location
3533 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3534 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3536 // send the response
3537 SZ_Clear(&sv_message);
3538 // save space for the header, filled in later
3539 MSG_WriteLong(&sv_message, 0);
3540 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3543 MSG_WriteString(&sv_message, var->name);
3544 MSG_WriteString(&sv_message, var->string);
3546 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3547 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3548 SZ_Clear(&sv_message);
3552 if (developer_extra.integer)
3553 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3554 if (sv.active && !rcon_secure.integer)
3556 char password[2048];
3560 const char *userlevel;
3561 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3562 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3564 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3565 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3566 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3573 SZ_Clear(&sv_message);
3574 // we may not have liked the packet, but it was a valid control
3575 // packet, so we're done processing this packet now
3580 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3582 SV_ReadClientMessage();
3589 void NetConn_ServerFrame(void)
3592 lhnetaddress_t peeraddress;
3593 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3594 for (i = 0;i < sv_numsockets;i++)
3595 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3596 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3597 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3599 // never timeout loopback connections
3600 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3602 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3603 SV_DropClient(false);
3608 void NetConn_SleepMicroseconds(int microseconds)
3610 LHNET_SleepUntilPacket_Microseconds(microseconds);
3614 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3618 lhnetaddress_t masteraddress;
3619 lhnetaddress_t broadcastaddress;
3622 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3625 // 26000 is the default quake server port, servers on other ports will not
3627 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3628 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3632 for (i = 0;i < cl_numsockets;i++)
3636 const char *cmdname, *extraoptions;
3637 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3639 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3641 // search LAN for Quake servers
3642 SZ_Clear(&cl_message);
3643 // save space for the header, filled in later
3644 MSG_WriteLong(&cl_message, 0);
3645 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3646 MSG_WriteString(&cl_message, "QUAKE");
3647 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3648 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3649 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3650 SZ_Clear(&cl_message);
3652 // search LAN for DarkPlaces servers
3653 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3656 // build the getservers message to send to the dpmaster master servers
3657 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3659 cmdname = "getserversExt";
3660 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3664 cmdname = "getservers";
3667 memcpy(request, "\377\377\377\377", 4);
3668 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3671 for (masternum = 0;sv_masters[masternum].name;masternum++)
3673 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3676 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3680 // search favorite servers
3681 for(j = 0; j < nFavorites; ++j)
3683 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3685 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3686 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3693 // only query QuakeWorld servers when the user wants to
3696 for (i = 0;i < cl_numsockets;i++)
3700 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3702 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3704 // search LAN for QuakeWorld servers
3705 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3707 // build the getservers message to send to the qwmaster master servers
3708 // note this has no -1 prefix, and the trailing nul byte is sent
3709 dpsnprintf(request, sizeof(request), "c\n");
3713 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3715 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3717 if (m_state != m_slist)
3719 char lookupstring[128];
3720 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3721 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3724 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3728 // search favorite servers
3729 for(j = 0; j < nFavorites; ++j)
3731 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3733 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3735 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3736 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3743 if (!masterquerycount)
3745 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3746 M_Update_Return_Reason("No network");
3751 void NetConn_Heartbeat(int priority)
3753 lhnetaddress_t masteraddress;
3755 lhnetsocket_t *mysocket;
3757 // if it's a state change (client connected), limit next heartbeat to no
3758 // more than 30 sec in the future
3759 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3760 nextheartbeattime = realtime + 30.0;
3762 // limit heartbeatperiod to 30 to 270 second range,
3763 // lower limit is to avoid abusing master servers with excess traffic,
3764 // upper limit is to avoid timing out on the master server (which uses
3766 if (sv_heartbeatperiod.value < 30)
3767 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3768 if (sv_heartbeatperiod.value > 270)
3769 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3771 // make advertising optional and don't advertise singleplayer games, and
3772 // only send a heartbeat as often as the admin wants
3773 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3775 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3776 for (masternum = 0;sv_masters[masternum].name;masternum++)
3777 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3778 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3782 static void Net_Heartbeat_f(void)
3785 NetConn_Heartbeat(2);
3787 Con_Print("No server running, can not heartbeat to master server.\n");
3790 static void PrintStats(netconn_t *conn)
3792 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3793 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3795 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3796 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3797 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3798 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3799 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3800 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3801 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3802 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3803 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3804 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3807 void Net_Stats_f(void)
3810 Con_Print("connections =\n");
3811 for (conn = netconn_list;conn;conn = conn->next)
3816 void Net_Refresh_f(void)
3818 if (m_state != m_slist) {
3819 Con_Print("Sending new requests to master servers\n");
3820 ServerList_QueryList(false, true, false, true);
3821 Con_Print("Listening for replies...\n");
3823 ServerList_QueryList(false, true, false, false);
3826 void Net_Slist_f(void)
3828 ServerList_ResetMasks();
3829 serverlist_sortbyfield = SLIF_PING;
3830 serverlist_sortflags = 0;
3831 if (m_state != m_slist) {
3832 Con_Print("Sending requests to master servers\n");
3833 ServerList_QueryList(true, true, false, true);
3834 Con_Print("Listening for replies...\n");
3836 ServerList_QueryList(true, true, false, false);
3839 void Net_SlistQW_f(void)
3841 ServerList_ResetMasks();
3842 serverlist_sortbyfield = SLIF_PING;
3843 serverlist_sortflags = 0;
3844 if (m_state != m_slist) {
3845 Con_Print("Sending requests to master servers\n");
3846 ServerList_QueryList(true, false, true, true);
3847 serverlist_consoleoutput = true;
3848 Con_Print("Listening for replies...\n");
3850 ServerList_QueryList(true, false, true, false);
3854 void NetConn_Init(void)
3857 lhnetaddress_t tempaddress;
3858 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3859 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3861 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3862 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3863 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3865 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3866 Cvar_RegisterVariable(&net_test);
3867 Cvar_RegisterVariable(&net_usesizelimit);
3868 Cvar_RegisterVariable(&net_burstreserve);
3869 Cvar_RegisterVariable(&rcon_restricted_password);
3870 Cvar_RegisterVariable(&rcon_restricted_commands);
3871 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3872 Cvar_RegisterVariable(&net_slist_queriespersecond);
3873 Cvar_RegisterVariable(&net_slist_queriesperframe);
3874 Cvar_RegisterVariable(&net_slist_timeout);
3875 Cvar_RegisterVariable(&net_slist_maxtries);
3876 Cvar_RegisterVariable(&net_slist_favorites);
3877 Cvar_RegisterVariable(&net_slist_pause);
3878 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3879 Cvar_RegisterVariable(&net_tos_dscp);
3880 Cvar_RegisterVariable(&net_messagetimeout);
3881 Cvar_RegisterVariable(&net_connecttimeout);
3882 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3883 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3884 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3885 Cvar_RegisterVariable(&net_sourceaddresscheck);
3886 Cvar_RegisterVariable(&cl_netlocalping);
3887 Cvar_RegisterVariable(&cl_netpacketloss_send);
3888 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3889 Cvar_RegisterVariable(&hostname);
3890 Cvar_RegisterVariable(&developer_networking);
3891 Cvar_RegisterVariable(&cl_netport);
3892 Cvar_RegisterVariable(&sv_netport);
3893 Cvar_RegisterVariable(&net_address);
3894 Cvar_RegisterVariable(&net_address_ipv6);
3895 Cvar_RegisterVariable(&sv_public);
3896 Cvar_RegisterVariable(&sv_public_rejectreason);
3897 Cvar_RegisterVariable(&sv_heartbeatperiod);
3898 for (i = 0;sv_masters[i].name;i++)
3899 Cvar_RegisterVariable(&sv_masters[i]);
3900 Cvar_RegisterVariable(&gameversion);
3901 Cvar_RegisterVariable(&gameversion_min);
3902 Cvar_RegisterVariable(&gameversion_max);
3903 // 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.
3904 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3906 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3908 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3909 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3912 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3914 // 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
3915 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3917 i = atoi(com_argv[i + 1]);
3918 if (i >= 0 && i < 65536)
3920 Con_Printf("-port option used, setting port cvar to %i\n", i);
3921 Cvar_SetValueQuick(&sv_netport, i);
3924 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3928 cl_message.data = cl_message_buf;
3929 cl_message.maxsize = sizeof(cl_message_buf);
3930 cl_message.cursize = 0;
3931 sv_message.data = sv_message_buf;
3932 sv_message.maxsize = sizeof(sv_message_buf);
3933 sv_message.cursize = 0;
3935 if (Thread_HasThreads())
3936 netconn_mutex = Thread_CreateMutex();
3939 void NetConn_Shutdown(void)
3941 NetConn_CloseClientPorts();
3942 NetConn_CloseServerPorts();
3945 Thread_DestroyMutex(netconn_mutex);
3946 netconn_mutex = NULL;