2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // for secure rcon authentication
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
35 // note this defaults on for dedicated servers, off for listen servers
36 cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
37 cvar_t sv_public_rejectreason = {0, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
39 extern cvar_t sv_status_privacy;
41 static cvar_t sv_masters [] =
43 {CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
44 {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
45 {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
46 {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
47 {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
48 {0, "sv_masterextra2", "64.22.107.125", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
49 {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
51 {0, "sv_masterextra4", "[2a03:4000:2:225::51:334d]:27950", "dpmaster.sudo.rm-f.org - default master server 4 (admin: divVerent)"}, // admin: divVerent
56 static cvar_t sv_qwmasters [] =
58 {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
59 {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
60 {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
61 {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
62 {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
63 {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
64 {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
65 {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
66 {0, "sv_qwmasterextra5", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
70 static double nextheartbeattime = 0;
74 static unsigned char cl_message_buf[NET_MAXMESSAGE];
75 static unsigned char sv_message_buf[NET_MAXMESSAGE];
76 char cl_readstring[MAX_INPUTLINE];
77 char sv_readstring[MAX_INPUTLINE];
79 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
80 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."};
81 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."};
82 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."};
83 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."};
84 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
85 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
87 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)"};
88 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)"};
89 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)"};
90 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
91 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)"};
92 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
93 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"};
94 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)"};
95 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"};
96 static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
97 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
98 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"};
99 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"};
100 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"};
101 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
102 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)"};
103 extern cvar_t rcon_secure;
104 extern cvar_t rcon_secure_challengetimeout;
106 double masterquerytime = -1000;
107 int masterquerycount = 0;
108 int masterreplycount = 0;
109 int serverquerycount = 0;
110 int serverreplycount = 0;
112 challenge_t challenge[MAX_CHALLENGES];
114 /// this is only false if there are still servers left to query
115 static qboolean serverlist_querysleep = true;
116 static qboolean serverlist_paused = false;
117 /// this is pushed a second or two ahead of realtime whenever a master server
118 /// reply is received, to avoid issuing queries while master replies are still
119 /// flooding in (which would make a mess of the ping times)
120 static double serverlist_querywaittime = 0;
122 static int cl_numsockets;
123 static lhnetsocket_t *cl_sockets[16];
124 static int sv_numsockets;
125 static lhnetsocket_t *sv_sockets[16];
127 netconn_t *netconn_list = NULL;
128 mempool_t *netconn_mempool = NULL;
129 void *netconn_mutex = NULL;
131 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
132 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
133 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
134 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
136 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
137 int cl_net_extresponse_count = 0;
138 int cl_net_extresponse_last = 0;
140 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
141 int sv_net_extresponse_count = 0;
142 int sv_net_extresponse_last = 0;
144 // ServerList interface
145 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
146 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
148 serverlist_infofield_t serverlist_sortbyfield;
149 int serverlist_sortflags;
151 int serverlist_viewcount = 0;
152 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
154 int serverlist_maxcachecount = 0;
155 int serverlist_cachecount = 0;
156 serverlist_entry_t *serverlist_cache = NULL;
158 qboolean serverlist_consoleoutput;
160 static int nFavorites = 0;
161 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
162 static int nFavorites_idfp = 0;
163 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
165 void NetConn_UpdateFavorites(void)
170 p = net_slist_favorites.string;
171 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
173 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
174 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
175 // (if v6 address contains port, it must start with '[')
177 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
182 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
188 /// helper function to insert a value into the viewset
189 /// spare entries will be removed
190 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
193 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
194 i = serverlist_viewcount++;
196 i = SERVERLIST_VIEWLISTSIZE - 1;
199 for( ; i > index ; i-- )
200 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
202 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
205 /// we suppose serverlist_viewcount to be valid, ie > 0
206 static void _ServerList_ViewList_Helper_Remove( int index )
208 serverlist_viewcount--;
209 for( ; index < serverlist_viewcount ; index++ )
210 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
213 /// \returns true if A should be inserted before B
214 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
216 int result = 0; // > 0 if for numbers A > B and for text if A < B
218 if( serverlist_sortflags & SLSF_FAVORITESFIRST )
220 if(A->info.isfavorite != B->info.isfavorite)
221 return A->info.isfavorite;
224 switch( serverlist_sortbyfield ) {
226 result = A->info.ping - B->info.ping;
228 case SLIF_MAXPLAYERS:
229 result = A->info.maxplayers - B->info.maxplayers;
231 case SLIF_NUMPLAYERS:
232 result = A->info.numplayers - B->info.numplayers;
235 result = A->info.numbots - B->info.numbots;
238 result = A->info.numhumans - B->info.numhumans;
241 result = A->info.freeslots - B->info.freeslots;
244 result = A->info.protocol - B->info.protocol;
247 result = strcmp( B->info.cname, A->info.cname );
250 result = strcasecmp( B->info.game, A->info.game );
253 result = strcasecmp( B->info.map, A->info.map );
256 result = strcasecmp( B->info.mod, A->info.mod );
259 result = strcasecmp( B->info.name, A->info.name );
262 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
264 case SLIF_ISFAVORITE:
265 result = !!B->info.isfavorite - !!A->info.isfavorite;
268 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
274 if( serverlist_sortflags & SLSF_DESCENDING )
280 // if the chosen sort key is identical, sort by index
281 // (makes this a stable sort, so that later replies from servers won't
282 // shuffle the servers around when they have the same ping)
286 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
288 // This should actually be done with some intermediate and end-of-function return
300 case SLMO_GREATEREQUAL:
302 case SLMO_NOTCONTAIN:
303 case SLMO_STARTSWITH:
304 case SLMO_NOTSTARTSWITH:
307 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
312 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
315 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
316 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
317 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
318 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
320 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
321 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
324 // Same here, also using an intermediate & final return would be more appropriate
328 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
329 case SLMO_NOTCONTAIN:
330 return !*bufferB || !strstr( bufferA, bufferB );
331 case SLMO_STARTSWITH:
332 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
333 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
334 case SLMO_NOTSTARTSWITH:
335 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
337 return strcmp( bufferA, bufferB ) < 0;
339 return strcmp( bufferA, bufferB ) <= 0;
341 return strcmp( bufferA, bufferB ) == 0;
343 return strcmp( bufferA, bufferB ) > 0;
345 return strcmp( bufferA, bufferB ) != 0;
346 case SLMO_GREATEREQUAL:
347 return strcmp( bufferA, bufferB ) >= 0;
349 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
354 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
356 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
358 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
360 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
362 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
364 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
366 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
368 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
370 if( *mask->info.cname
371 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
374 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
377 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
380 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
383 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
385 if( *mask->info.qcstatus
386 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
388 if( *mask->info.players
389 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
391 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
396 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
398 int start, end, mid, i;
401 // reject incompatible servers
403 entry->info.gameversion != gameversion.integer
406 gameversion_min.integer >= 0 // min/max range set by user/mod?
407 && gameversion_max.integer >= 0
408 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
409 && gameversion_max.integer >= entry->info.gameversion
414 // refresh the "favorite" status
415 entry->info.isfavorite = false;
416 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
418 char idfp[FP64_SIZE+1];
419 for(i = 0; i < nFavorites; ++i)
421 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
423 entry->info.isfavorite = true;
427 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
429 for(i = 0; i < nFavorites_idfp; ++i)
431 if(!strcmp(idfp, favorites_idfp[i]))
433 entry->info.isfavorite = true;
440 // FIXME: change this to be more readable (...)
441 // now check whether it passes through the masks
442 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
443 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
446 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
447 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
449 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
452 if( !serverlist_viewcount ) {
453 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
456 // ok, insert it, we just need to find out where exactly:
459 // check whether to insert it as new first item
460 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
461 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
463 } // check whether to insert it as new last item
464 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
465 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
469 end = serverlist_viewcount - 1;
470 while( end > start + 1 )
472 mid = (start + end) / 2;
473 // test the item that lies in the middle between start and end
474 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
475 // the item has to be in the upper half
478 // the item has to be in the lower half
481 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
484 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
487 for( i = 0; i < serverlist_viewcount; i++ )
489 if (ServerList_GetViewEntry(i) == entry)
491 _ServerList_ViewList_Helper_Remove(i);
497 void ServerList_RebuildViewList(void)
501 serverlist_viewcount = 0;
502 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
503 serverlist_entry_t *entry = &serverlist_cache[i];
504 // also display entries that are currently being refreshed [11/8/2007 Black]
505 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
506 ServerList_ViewList_Insert( entry );
510 void ServerList_ResetMasks(void)
514 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
515 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
516 // numbots needs to be compared to -1 to always succeed
517 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
518 serverlist_andmasks[i].info.numbots = -1;
519 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
520 serverlist_ormasks[i].info.numbots = -1;
523 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
526 int numplayers = 0, maxplayers = 0;
527 for (i = 0;i < serverlist_cachecount;i++)
529 if (serverlist_cache[i].query == SQS_QUERIED)
531 numplayers += serverlist_cache[i].info.numhumans;
532 maxplayers += serverlist_cache[i].info.maxplayers;
535 *numplayerspointer = numplayers;
536 *maxplayerspointer = maxplayers;
540 static void _ServerList_Test(void)
543 if (serverlist_maxcachecount <= 1024)
545 serverlist_maxcachecount = 1024;
546 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
548 for( i = 0 ; i < 1024 ; i++ ) {
549 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
550 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
551 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
552 serverlist_cache[serverlist_cachecount].finished = true;
553 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 );
554 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
555 serverlist_cachecount++;
560 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
562 masterquerytime = realtime;
563 masterquerycount = 0;
564 masterreplycount = 0;
566 serverquerycount = 0;
567 serverreplycount = 0;
568 serverlist_cachecount = 0;
569 serverlist_viewcount = 0;
570 serverlist_maxcachecount = 0;
571 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
573 // refresh all entries
575 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
576 serverlist_entry_t *entry = &serverlist_cache[ n ];
577 entry->query = SQS_REFRESHING;
578 entry->querycounter = 0;
581 serverlist_consoleoutput = consoleoutput;
583 //_ServerList_Test();
585 NetConn_QueryMasters(querydp, queryqw);
590 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
594 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
595 Thread_LockMutex(netconn_mutex);
596 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
597 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
598 Thread_UnlockMutex(netconn_mutex);
601 if (cl_netpacketloss_receive.integer)
602 for (i = 0;i < cl_numsockets;i++)
603 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
605 if (developer_networking.integer)
607 char addressstring[128], addressstring2[128];
608 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
611 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
612 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
613 Com_HexDumpToConsole((unsigned char *)data, length);
616 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
621 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
625 if (cl_netpacketloss_send.integer)
626 for (i = 0;i < cl_numsockets;i++)
627 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
629 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
630 Thread_LockMutex(netconn_mutex);
631 ret = LHNET_Write(mysocket, data, length, peeraddress);
632 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
633 Thread_UnlockMutex(netconn_mutex);
634 if (developer_networking.integer)
636 char addressstring[128], addressstring2[128];
637 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
638 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
639 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)");
640 Com_HexDumpToConsole((unsigned char *)data, length);
645 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
647 // note this does not include the trailing NULL because we add that in the parser
648 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
651 qboolean NetConn_CanSend(netconn_t *conn)
653 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
654 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = realtime;
655 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
656 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
657 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
658 if (realtime > conn->cleartime)
662 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
667 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables)
670 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
671 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
673 // if this packet was supposedly choked, but we find ourselves sending one
674 // anyway, make sure the size counting starts at zero
675 // (this mostly happens on level changes and disconnects and such)
676 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
677 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
679 if (protocol == PROTOCOL_QUAKEWORLD)
682 qboolean sendreliable;
684 // note that it is ok to send empty messages to the qw server,
685 // otherwise it won't respond to us at all
687 sendreliable = false;
688 // if the remote side dropped the last reliable message, resend it
689 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
691 // if the reliable transmit buffer is empty, copy the current message out
692 if (!conn->sendMessageLength && conn->message.cursize)
694 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
695 conn->sendMessageLength = conn->message.cursize;
696 SZ_Clear(&conn->message); // clear the message buffer
697 conn->qw.reliable_sequence ^= 1;
700 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
701 StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
702 // last received unreliable packet number, and last received reliable packet number (0 or 1)
703 StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
705 conn->outgoing_unreliable_sequence++;
706 // client sends qport in every packet
707 if (conn == cls.netcon)
709 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
711 // also update cls.qw_outgoing_sequence
712 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
714 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
716 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
720 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
722 // add the reliable message if there is one
725 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
726 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
727 packetLen += conn->sendMessageLength;
728 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
731 // add the unreliable message if possible
732 if (packetLen + data->cursize <= 1400)
734 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
735 memcpy(sendbuffer + packetLen, data->data, data->cursize);
736 packetLen += data->cursize;
739 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
742 conn->unreliableMessagesSent++;
744 totallen += packetLen + 28;
748 unsigned int packetLen;
749 unsigned int dataLen;
754 // if a reliable message fragment has been lost, send it again
755 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
757 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
759 dataLen = conn->sendMessageLength;
764 dataLen = MAX_PACKETFRAGMENT;
768 packetLen = NET_HEADERSIZE + dataLen;
770 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
771 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
772 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
774 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
776 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
777 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
779 conn->lastSendTime = realtime;
780 conn->packetsReSent++;
783 totallen += sendmelen + 28;
786 // if we have a new reliable message to send, do so
787 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
789 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
791 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
792 conn->message.overflowed = true;
796 if (developer_networking.integer && conn == cls.netcon)
798 Con_Print("client sending reliable message to server:\n");
799 SZ_HexDumpToConsole(&conn->message);
802 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
803 conn->sendMessageLength = conn->message.cursize;
804 SZ_Clear(&conn->message);
806 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
808 dataLen = conn->sendMessageLength;
813 dataLen = MAX_PACKETFRAGMENT;
817 packetLen = NET_HEADERSIZE + dataLen;
819 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
820 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
821 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
823 conn->nq.sendSequence++;
825 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
827 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
829 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
831 conn->lastSendTime = realtime;
833 conn->reliableMessagesSent++;
835 totallen += sendmelen + 28;
838 // if we have an unreliable message to send, do so
841 packetLen = NET_HEADERSIZE + data->cursize;
843 if (packetLen > (int)sizeof(sendbuffer))
845 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
849 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE);
850 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
851 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
853 conn->outgoing_unreliable_sequence++;
855 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
857 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
859 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
862 conn->unreliableMessagesSent++;
864 totallen += sendmelen + 28;
868 // delay later packets to obey rate limit
869 if (conn->cleartime < realtime - 0.1)
870 conn->cleartime = realtime - 0.1;
871 conn->cleartime = conn->cleartime + (double)totallen / (double)rate;
872 if (conn->cleartime < realtime)
873 conn->cleartime = realtime;
878 qboolean NetConn_HaveClientPorts(void)
880 return !!cl_numsockets;
883 qboolean NetConn_HaveServerPorts(void)
885 return !!sv_numsockets;
888 void NetConn_CloseClientPorts(void)
890 for (;cl_numsockets > 0;cl_numsockets--)
891 if (cl_sockets[cl_numsockets - 1])
892 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
895 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
897 lhnetaddress_t address;
900 char addressstring2[1024];
901 if (addressstring && addressstring[0])
902 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
904 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
907 if ((s = LHNET_OpenSocket_Connectionless(&address)))
909 cl_sockets[cl_numsockets++] = s;
910 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
911 if (addresstype != LHNETADDRESSTYPE_LOOP)
912 Con_Printf("Client opened a socket on address %s\n", addressstring2);
916 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
917 Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
921 Con_Printf("Client unable to parse address %s\n", addressstring);
924 void NetConn_OpenClientPorts(void)
927 NetConn_CloseClientPorts();
929 SV_LockThreadMutex(); // FIXME recursive?
930 Crypto_LoadKeys(); // client sockets
931 SV_UnlockThreadMutex();
933 port = bound(0, cl_netport.integer, 65535);
934 if (cl_netport.integer != port)
935 Cvar_SetValueQuick(&cl_netport, port);
937 Con_Printf("Client using an automatically assigned port\n");
939 Con_Printf("Client using port %i\n", port);
940 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
941 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
943 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
947 void NetConn_CloseServerPorts(void)
949 for (;sv_numsockets > 0;sv_numsockets--)
950 if (sv_sockets[sv_numsockets - 1])
951 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
954 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
956 lhnetaddress_t address;
959 char addressstring2[1024];
962 for (port = defaultport; port <= defaultport + range; port++)
964 if (addressstring && addressstring[0])
965 success = LHNETADDRESS_FromString(&address, addressstring, port);
967 success = LHNETADDRESS_FromPort(&address, addresstype, port);
970 if ((s = LHNET_OpenSocket_Connectionless(&address)))
972 sv_sockets[sv_numsockets++] = s;
973 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
974 if (addresstype != LHNETADDRESSTYPE_LOOP)
975 Con_Printf("Server listening on address %s\n", addressstring2);
980 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
981 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
986 Con_Printf("Server unable to parse address %s\n", addressstring);
987 // if it cant parse one address, it wont be able to parse another for sure
994 void NetConn_OpenServerPorts(int opennetports)
997 NetConn_CloseServerPorts();
999 SV_LockThreadMutex(); // FIXME recursive?
1000 Crypto_LoadKeys(); // server sockets
1001 SV_UnlockThreadMutex();
1003 NetConn_UpdateSockets();
1004 port = bound(0, sv_netport.integer, 65535);
1007 Con_Printf("Server using port %i\n", port);
1008 if (sv_netport.integer != port)
1009 Cvar_SetValueQuick(&sv_netport, port);
1010 if (cls.state != ca_dedicated)
1011 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1015 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1016 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1018 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1021 if (sv_numsockets == 0)
1022 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1025 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1027 int i, a = LHNETADDRESS_GetAddressType(address);
1028 for (i = 0;i < cl_numsockets;i++)
1029 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1030 return cl_sockets[i];
1034 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1036 int i, a = LHNETADDRESS_GetAddressType(address);
1037 for (i = 0;i < sv_numsockets;i++)
1038 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1039 return sv_sockets[i];
1043 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1046 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1047 conn->mysocket = mysocket;
1048 conn->peeraddress = *peeraddress;
1049 conn->lastMessageTime = realtime;
1050 conn->message.data = conn->messagedata;
1051 conn->message.maxsize = sizeof(conn->messagedata);
1052 conn->message.cursize = 0;
1053 // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1054 // reduce effectiveness of connection request floods
1055 conn->timeout = realtime + net_connecttimeout.value;
1056 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1057 conn->next = netconn_list;
1058 netconn_list = conn;
1062 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1063 void NetConn_Close(netconn_t *conn)
1066 // remove connection from list
1068 // allow the client to reconnect immediately
1069 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1071 if (conn == netconn_list)
1072 netconn_list = conn->next;
1075 for (c = netconn_list;c;c = c->next)
1077 if (c->next == conn)
1079 c->next = conn->next;
1083 // not found in list, we'll avoid crashing here...
1091 static int clientport = -1;
1092 static int clientport2 = -1;
1093 static int hostport = -1;
1094 void NetConn_UpdateSockets(void)
1098 // TODO add logic to automatically close sockets if needed
1099 LHNET_DefaultDSCP(net_tos_dscp.integer);
1101 if (cls.state != ca_dedicated)
1103 if (clientport2 != cl_netport.integer)
1105 clientport2 = cl_netport.integer;
1106 if (cls.state == ca_connected)
1107 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1109 if (cls.state == ca_disconnected && clientport != clientport2)
1111 clientport = clientport2;
1112 NetConn_CloseClientPorts();
1114 if (cl_numsockets == 0)
1115 NetConn_OpenClientPorts();
1118 if (hostport != sv_netport.integer)
1120 hostport = sv_netport.integer;
1122 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1125 for (j = 0;j < MAX_RCONS;j++)
1127 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1128 if(cls.rcon_commands[i][0])
1130 if(realtime > cls.rcon_timeout[i])
1133 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1134 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1135 cls.rcon_commands[i][0] = 0;
1143 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1145 int originallength = length;
1146 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1147 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1148 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1152 if (protocol == PROTOCOL_QUAKEWORLD)
1154 int sequence, sequence_ack;
1155 int reliable_ack, reliable_message;
1159 sequence = LittleLong(*((int *)(data + 0)));
1160 sequence_ack = LittleLong(*((int *)(data + 4)));
1164 if (conn != cls.netcon)
1169 // 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?)
1170 //qport = LittleShort(*((int *)(data + 8)));
1175 conn->packetsReceived++;
1176 reliable_message = (sequence >> 31) & 1;
1177 reliable_ack = (sequence_ack >> 31) & 1;
1178 sequence &= ~(1<<31);
1179 sequence_ack &= ~(1<<31);
1180 if (sequence <= conn->qw.incoming_sequence)
1182 //Con_DPrint("Got a stale datagram\n");
1185 count = sequence - (conn->qw.incoming_sequence + 1);
1188 conn->droppedDatagrams += count;
1189 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1192 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1193 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1194 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1195 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1196 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1199 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1200 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1201 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1202 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1203 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1204 if (reliable_ack == conn->qw.reliable_sequence)
1206 // received, now we will be able to send another reliable message
1207 conn->sendMessageLength = 0;
1208 conn->reliableMessagesReceived++;
1210 conn->qw.incoming_sequence = sequence;
1211 if (conn == cls.netcon)
1212 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1213 conn->qw.incoming_acknowledged = sequence_ack;
1214 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1215 if (reliable_message)
1216 conn->qw.incoming_reliable_sequence ^= 1;
1217 conn->lastMessageTime = realtime;
1218 conn->timeout = realtime + newtimeout;
1219 conn->unreliableMessagesReceived++;
1220 if (conn == cls.netcon)
1222 SZ_Clear(&cl_message);
1223 SZ_Write(&cl_message, data, length);
1224 MSG_BeginReading(&cl_message);
1228 SZ_Clear(&sv_message);
1229 SZ_Write(&sv_message, data, length);
1230 MSG_BeginReading(&sv_message);
1238 unsigned int sequence;
1243 originallength = length;
1244 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1250 qlength = (unsigned int)BuffBigLong(data);
1251 flags = qlength & ~NETFLAG_LENGTH_MASK;
1252 qlength &= NETFLAG_LENGTH_MASK;
1253 // control packets were already handled
1254 if (!(flags & NETFLAG_CTL) && qlength == length)
1256 sequence = BuffBigLong(data + 4);
1257 conn->packetsReceived++;
1260 if (flags & NETFLAG_UNRELIABLE)
1262 if (sequence >= conn->nq.unreliableReceiveSequence)
1264 if (sequence > conn->nq.unreliableReceiveSequence)
1266 count = sequence - conn->nq.unreliableReceiveSequence;
1267 conn->droppedDatagrams += count;
1268 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1271 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1272 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1273 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1274 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1275 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1278 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1279 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1280 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1281 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1282 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1283 conn->nq.unreliableReceiveSequence = sequence + 1;
1284 conn->lastMessageTime = realtime;
1285 conn->timeout = realtime + newtimeout;
1286 conn->unreliableMessagesReceived++;
1289 if (conn == cls.netcon)
1291 SZ_Clear(&cl_message);
1292 SZ_Write(&cl_message, data, length);
1293 MSG_BeginReading(&cl_message);
1297 SZ_Clear(&sv_message);
1298 SZ_Write(&sv_message, data, length);
1299 MSG_BeginReading(&sv_message);
1305 // Con_DPrint("Got a stale datagram\n");
1308 else if (flags & NETFLAG_ACK)
1310 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1311 if (sequence == (conn->nq.sendSequence - 1))
1313 if (sequence == conn->nq.ackSequence)
1315 conn->nq.ackSequence++;
1316 if (conn->nq.ackSequence != conn->nq.sendSequence)
1317 Con_DPrint("ack sequencing error\n");
1318 conn->lastMessageTime = realtime;
1319 conn->timeout = realtime + newtimeout;
1320 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1322 unsigned int packetLen;
1323 unsigned int dataLen;
1326 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1327 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1329 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1331 dataLen = conn->sendMessageLength;
1336 dataLen = MAX_PACKETFRAGMENT;
1340 packetLen = NET_HEADERSIZE + dataLen;
1342 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
1343 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1344 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1346 conn->nq.sendSequence++;
1348 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1349 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
1351 conn->lastSendTime = realtime;
1352 conn->packetsSent++;
1356 conn->sendMessageLength = 0;
1359 // Con_DPrint("Duplicate ACK received\n");
1362 // Con_DPrint("Stale ACK received\n");
1365 else if (flags & NETFLAG_DATA)
1367 unsigned char temppacket[8];
1368 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1369 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1370 StoreBigLong(temppacket, 8 | NETFLAG_ACK);
1371 StoreBigLong(temppacket + 4, sequence);
1372 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1374 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
1375 if (sequence == conn->nq.receiveSequence)
1377 conn->lastMessageTime = realtime;
1378 conn->timeout = realtime + newtimeout;
1379 conn->nq.receiveSequence++;
1380 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1381 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1382 conn->receiveMessageLength += length;
1384 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1385 "Dropping the message!\n", sequence );
1386 conn->receiveMessageLength = 0;
1389 if (flags & NETFLAG_EOM)
1391 conn->reliableMessagesReceived++;
1392 length = conn->receiveMessageLength;
1393 conn->receiveMessageLength = 0;
1396 if (conn == cls.netcon)
1398 SZ_Clear(&cl_message);
1399 SZ_Write(&cl_message, conn->receiveMessage, length);
1400 MSG_BeginReading(&cl_message);
1404 SZ_Clear(&sv_message);
1405 SZ_Write(&sv_message, conn->receiveMessage, length);
1406 MSG_BeginReading(&sv_message);
1413 conn->receivedDuplicateCount++;
1421 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1424 cls.connect_trying = false;
1425 M_Update_Return_Reason("");
1426 // the connection request succeeded, stop current connection and set up a new connection
1428 // if we're connecting to a remote server, shut down any local server
1429 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1431 SV_LockThreadMutex();
1432 Host_ShutdownServer ();
1433 SV_UnlockThreadMutex();
1435 // allocate a net connection to keep track of things
1436 cls.netcon = NetConn_Open(mysocket, peeraddress);
1437 crypto = &cls.crypto;
1438 if(crypto && crypto->authenticated)
1440 Crypto_ServerFinishInstance(&cls.netcon->crypto, crypto);
1441 Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
1442 crypto->use_aes ? "Encrypted" : "Authenticated",
1443 cls.netcon->address,
1444 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1445 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1446 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1447 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1450 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1451 key_dest = key_game;
1453 cls.demonum = -1; // not in the demo loop now
1454 cls.state = ca_connected;
1455 cls.signon = 0; // need all the signon messages before playing
1456 cls.protocol = initialprotocol;
1457 // reset move sequence numbering on this new connection
1458 cls.servermovesequence = 0;
1459 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1460 Cmd_ForwardStringToServer("new");
1461 if (cls.protocol == PROTOCOL_QUAKE)
1463 // write a keepalive (clc_nop) as it seems to greatly improve the
1464 // chances of connecting to a netquake server
1466 unsigned char buf[4];
1467 memset(&msg, 0, sizeof(msg));
1469 msg.maxsize = sizeof(buf);
1470 MSG_WriteChar(&msg, clc_nop);
1471 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
1475 int NetConn_IsLocalGame(void)
1477 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1482 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1486 serverlist_entry_t *entry = NULL;
1488 // search the cache for this server and update it
1489 for (n = 0;n < serverlist_cachecount;n++) {
1490 entry = &serverlist_cache[ n ];
1491 if (!strcmp(addressstring, entry->info.cname))
1495 if (n == serverlist_cachecount)
1497 // LAN search doesnt require an answer from the master server so we wont
1498 // know the ping nor will it be initialized already...
1501 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1504 if (serverlist_maxcachecount <= serverlist_cachecount)
1506 serverlist_maxcachecount += 64;
1507 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1509 entry = &serverlist_cache[n];
1511 memset(entry, 0, sizeof(*entry));
1512 // store the data the engine cares about (address and ping)
1513 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1514 entry->info.ping = 100000;
1515 entry->querytime = realtime;
1516 // if not in the slist menu we should print the server to console
1517 if (serverlist_consoleoutput)
1518 Con_Printf("querying %s\n", addressstring);
1519 ++serverlist_cachecount;
1521 // if this is the first reply from this server, count it as having replied
1522 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1523 pingtime = bound(0, pingtime, 9999);
1524 if (entry->query == SQS_REFRESHING) {
1525 entry->info.ping = pingtime;
1526 entry->query = SQS_QUERIED;
1528 // convert to unsigned to catch the -1
1529 // 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]
1530 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1534 // other server info is updated by the caller
1538 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1540 serverlist_entry_t *entry = &serverlist_cache[n];
1541 serverlist_info_t *info = &entry->info;
1542 // update description strings for engine menu and console output
1543 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);
1544 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1546 info->gameversion != gameversion.integer
1549 gameversion_min.integer >= 0 // min/max range set by user/mod?
1550 && gameversion_max.integer >= 0
1551 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1552 && gameversion_max.integer >= info->gameversion
1555 info->mod, info->map);
1556 if (entry->query == SQS_QUERIED)
1558 if(!serverlist_paused)
1559 ServerList_ViewList_Remove(entry);
1561 // if not in the slist menu we should print the server to console (if wanted)
1562 else if( serverlist_consoleoutput )
1563 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1564 // and finally, update the view set
1565 if(!serverlist_paused)
1566 ServerList_ViewList_Insert( entry );
1567 // update the entry's state
1568 serverlist_cache[n].query = SQS_QUERIED;
1571 // returns true, if it's sensible to continue the processing
1572 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1574 serverlist_entry_t *entry;
1576 // ignore the rest of the message if the serverlist is full
1577 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1579 // also ignore it if we have already queried it (other master server response)
1580 for( n = 0 ; n < serverlist_cachecount ; n++ )
1581 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1584 if( n < serverlist_cachecount ) {
1585 // the entry has already been queried once or
1589 if (serverlist_maxcachecount <= n)
1591 serverlist_maxcachecount += 64;
1592 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1595 entry = &serverlist_cache[n];
1597 memset(entry, 0, sizeof(*entry));
1598 entry->protocol = protocol;
1599 // store the data the engine cares about (address and ping)
1600 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1602 entry->info.isfavorite = isfavorite;
1604 // no, then reset the ping right away
1605 entry->info.ping = -1;
1606 // we also want to increase the serverlist_cachecount then
1607 serverlist_cachecount++;
1610 entry->query = SQS_QUERYING;
1615 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1618 if (serverlist_consoleoutput)
1619 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1622 char ipstring [128];
1625 if (data[0] == '\\')
1627 unsigned short port = data[5] * 256 + data[6];
1629 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1630 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1632 // move on to next address in packet
1637 else if (data[0] == '/' && isextended && length >= 19)
1639 unsigned short port = data[17] * 256 + data[18];
1647 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1649 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1652 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1653 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1654 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1660 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1661 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1662 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1667 // move on to next address in packet
1673 Con_Print("Error while parsing the server list\n");
1677 if (serverlist_consoleoutput && developer_networking.integer)
1678 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1680 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1686 // begin or resume serverlist queries
1687 serverlist_querysleep = false;
1688 serverlist_querywaittime = realtime + 3;
1691 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1693 qboolean fromserver;
1696 char *string, addressstring2[128], ipstring[32];
1697 char stringbuf[16384];
1698 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1700 char infostringvalue[MAX_INPUTLINE];
1703 // quakeworld ingame packet
1704 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1706 // convert the address to a string incase we need it
1707 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1709 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1711 // received a command string - strip off the packaging and put it
1712 // into our string buffer with NULL termination
1715 length = min(length, (int)sizeof(stringbuf) - 1);
1716 memcpy(stringbuf, data, length);
1717 stringbuf[length] = 0;
1720 if (developer_networking.integer)
1722 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1723 Com_HexDumpToConsole(data, length);
1726 sendlength = sizeof(senddata) - 4;
1727 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1729 case CRYPTO_NOMATCH:
1735 memcpy(senddata, "\377\377\377\377", 4);
1736 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1739 case CRYPTO_DISCARD:
1742 memcpy(senddata, "\377\377\377\377", 4);
1743 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1747 case CRYPTO_REPLACE:
1748 string = senddata+4;
1749 length = sendlength;
1753 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1756 for (j = 0;j < MAX_RCONS;j++)
1758 // note: this value from i is used outside the loop too...
1759 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1760 if(cls.rcon_commands[i][0])
1761 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1770 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1771 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1773 e = strchr(rcon_password.string, ' ');
1774 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1776 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
1780 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1781 NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
1782 cls.rcon_commands[i][0] = 0;
1785 for (k = 0;k < MAX_RCONS;k++)
1786 if(cls.rcon_commands[k][0])
1787 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1792 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1793 // extend the timeout on other requests as we asked for a challenge
1794 for (l = 0;l < MAX_RCONS;l++)
1795 if(cls.rcon_commands[l][0])
1796 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1797 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1800 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1804 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1806 // darkplaces or quake3
1807 char protocolnames[1400];
1808 Protocol_Names(protocolnames, sizeof(protocolnames));
1809 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1810 M_Update_Return_Reason("Got challenge response");
1811 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1812 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1813 // TODO: add userinfo stuff here instead of using NQ commands?
1814 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10), peeraddress);
1817 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1819 // darkplaces or quake3
1820 M_Update_Return_Reason("Accepted");
1821 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1824 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1826 char rejectreason[128];
1827 cls.connect_trying = false;
1829 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1830 memcpy(rejectreason, string, length);
1831 rejectreason[length] = 0;
1832 M_Update_Return_Reason(rejectreason);
1835 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1837 serverlist_info_t *info;
1842 // search the cache for this server and update it
1843 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1847 info = &serverlist_cache[n].info;
1852 info->qcstatus[0] = 0;
1853 info->players[0] = 0;
1854 info->protocol = -1;
1855 info->numplayers = 0;
1857 info->maxplayers = 0;
1858 info->gameversion = 0;
1860 p = strchr(string, '\n');
1863 *p = 0; // cut off the string there
1867 Con_Printf("statusResponse without players block?\n");
1869 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1870 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1871 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1872 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1873 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1874 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1875 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1876 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1877 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1878 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1879 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1880 info->numhumans = info->numplayers - max(0, info->numbots);
1881 info->freeslots = info->maxplayers - info->numplayers;
1883 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1887 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1889 serverlist_info_t *info;
1893 // search the cache for this server and update it
1894 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1898 info = &serverlist_cache[n].info;
1903 info->qcstatus[0] = 0;
1904 info->players[0] = 0;
1905 info->protocol = -1;
1906 info->numplayers = 0;
1908 info->maxplayers = 0;
1909 info->gameversion = 0;
1911 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1912 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1913 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1914 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1915 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1916 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1917 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1918 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1919 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1920 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1921 info->numhumans = info->numplayers - max(0, info->numbots);
1922 info->freeslots = info->maxplayers - info->numplayers;
1924 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1928 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1930 // Extract the IP addresses
1933 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
1936 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1938 // Extract the IP addresses
1941 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
1944 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1946 // Extract the IP addresses
1950 if (serverlist_consoleoutput)
1951 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
1952 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
1954 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
1955 if (serverlist_consoleoutput && developer_networking.integer)
1956 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
1958 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
1962 // move on to next address in packet
1966 // begin or resume serverlist queries
1967 serverlist_querysleep = false;
1968 serverlist_querywaittime = realtime + 3;
1971 if (!strncmp(string, "extResponse ", 12))
1973 ++cl_net_extresponse_count;
1974 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
1975 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
1976 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
1977 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
1980 if (!strncmp(string, "ping", 4))
1982 if (developer_extra.integer)
1983 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
1984 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
1987 if (!strncmp(string, "ack", 3))
1989 // QuakeWorld compatibility
1990 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
1992 // challenge message
1993 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
1994 M_Update_Return_Reason("Got QuakeWorld challenge response");
1995 cls.qw_qport = qport.integer;
1996 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1997 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1998 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo), peeraddress);
2001 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2004 M_Update_Return_Reason("QuakeWorld Accepted");
2005 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2008 if (length > 2 && !memcmp(string, "n\\", 2))
2010 serverlist_info_t *info;
2014 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2015 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2018 // search the cache for this server and update it
2019 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2023 info = &serverlist_cache[n].info;
2024 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2025 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2026 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2027 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2029 info->numplayers = 0; // updated below
2030 info->numhumans = 0; // updated below
2031 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2032 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2034 // count active players on server
2035 // (we could gather more info, but we're just after the number)
2036 s = strchr(string, '\n');
2040 while (s < string + length)
2042 for (;s < string + length && *s != '\n';s++)
2044 if (s >= string + length)
2052 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2056 if (string[0] == 'n')
2059 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2061 // we may not have liked the packet, but it was a command packet, so
2062 // we're done processing this packet now
2065 // quakeworld ingame packet
2066 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2069 CL_ParseServerMessage();
2072 // netquake control packets, supported for compatibility only
2073 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2076 serverlist_info_t *info;
2080 SZ_Clear(&cl_message);
2081 SZ_Write(&cl_message, data, length);
2082 MSG_BeginReading(&cl_message);
2083 c = MSG_ReadByte(&cl_message);
2087 if (developer_extra.integer)
2088 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2089 if (cls.connect_trying)
2091 lhnetaddress_t clientportaddress;
2092 clientportaddress = *peeraddress;
2093 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2094 // extra ProQuake stuff
2096 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2098 cls.proquake_servermod = 0;
2100 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2102 cls.proquake_serverversion = 0;
2104 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2106 cls.proquake_serverflags = 0;
2107 if (cls.proquake_servermod == 1)
2108 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2109 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2110 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2111 M_Update_Return_Reason("Accepted");
2112 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2116 if (developer_extra.integer)
2117 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2118 cls.connect_trying = false;
2119 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2121 case CCREP_SERVER_INFO:
2122 if (developer_extra.integer)
2123 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2124 // LordHavoc: because the quake server may report weird addresses
2125 // we just ignore it and keep the real address
2126 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2127 // search the cache for this server and update it
2128 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2132 info = &serverlist_cache[n].info;
2133 strlcpy(info->game, "Quake", sizeof(info->game));
2134 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2135 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2136 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2137 info->numplayers = MSG_ReadByte(&cl_message);
2138 info->maxplayers = MSG_ReadByte(&cl_message);
2139 info->protocol = MSG_ReadByte(&cl_message);
2141 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2144 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2145 if (developer_extra.integer)
2146 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2148 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2150 case CCREP_PLAYER_INFO:
2151 // we got a CCREP_PLAYER_INFO??
2152 //if (developer_extra.integer)
2153 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2155 case CCREP_RULE_INFO:
2156 // we got a CCREP_RULE_INFO??
2157 //if (developer_extra.integer)
2158 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2163 SZ_Clear(&cl_message);
2164 // we may not have liked the packet, but it was a valid control
2165 // packet, so we're done processing this packet now
2169 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2170 CL_ParseServerMessage();
2174 void NetConn_QueryQueueFrame(void)
2180 static double querycounter = 0;
2182 if(!net_slist_pause.integer && serverlist_paused)
2183 ServerList_RebuildViewList();
2184 serverlist_paused = net_slist_pause.integer != 0;
2186 if (serverlist_querysleep)
2189 // apply a cool down time after master server replies,
2190 // to avoid messing up the ping times on the servers
2191 if (serverlist_querywaittime > realtime)
2194 // each time querycounter reaches 1.0 issue a query
2195 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2196 maxqueries = (int)querycounter;
2197 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2198 querycounter -= maxqueries;
2200 if( maxqueries == 0 ) {
2204 // scan serverlist and issue queries as needed
2205 serverlist_querysleep = true;
2207 timeouttime = realtime - net_slist_timeout.value;
2208 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2210 serverlist_entry_t *entry = &serverlist_cache[ index ];
2211 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2216 serverlist_querysleep = false;
2217 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2222 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2224 lhnetaddress_t address;
2227 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2228 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2230 for (socket = 0; socket < cl_numsockets ; socket++)
2231 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2235 for (socket = 0; socket < cl_numsockets ; socket++)
2236 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2239 // update the entry fields
2240 entry->querytime = realtime;
2241 entry->querycounter++;
2243 // if not in the slist menu we should print the server to console
2244 if (serverlist_consoleoutput)
2245 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2251 // have we tried to refresh this server?
2252 if( entry->query == SQS_REFRESHING ) {
2253 // yes, so update the reply count (since its not responding anymore)
2255 if(!serverlist_paused)
2256 ServerList_ViewList_Remove(entry);
2258 entry->query = SQS_TIMEDOUT;
2263 void NetConn_ClientFrame(void)
2266 lhnetaddress_t peeraddress;
2267 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2268 NetConn_UpdateSockets();
2269 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2271 if (cls.connect_remainingtries == 0)
2272 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2273 cls.connect_nextsendtime = realtime + 1;
2274 cls.connect_remainingtries--;
2275 if (cls.connect_remainingtries <= -10)
2277 cls.connect_trying = false;
2278 M_Update_Return_Reason("Connect: Failed");
2281 // try challenge first (newer DP server or QW)
2282 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2283 // then try netquake as a fallback (old server, or netquake)
2284 SZ_Clear(&cl_message);
2285 // save space for the header, filled in later
2286 MSG_WriteLong(&cl_message, 0);
2287 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2288 MSG_WriteString(&cl_message, "QUAKE");
2289 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2290 // extended proquake stuff
2291 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2292 // this version matches ProQuake 3.40, the first version to support
2293 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2294 // higher clients, so we pretend we are that version...
2295 MSG_WriteByte(&cl_message, 34); // version * 10
2296 MSG_WriteByte(&cl_message, 0); // flags
2297 MSG_WriteLong(&cl_message, 0); // password
2298 // write the packetsize now...
2299 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2300 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2301 SZ_Clear(&cl_message);
2303 for (i = 0;i < cl_numsockets;i++)
2305 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2307 // R_TimeReport("clientreadnetwork");
2308 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2309 // R_TimeReport("clientparsepacket");
2312 NetConn_QueryQueueFrame();
2313 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2315 Con_Print("Connection timed out\n");
2317 SV_LockThreadMutex();
2318 Host_ShutdownServer ();
2319 SV_UnlockThreadMutex();
2323 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2327 for (i = 0;i < bufferlength - 1;i++)
2331 c = rand () % (127 - 33) + 33;
2332 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2338 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2339 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2341 prvm_prog_t *prog = SVVM_prog;
2343 unsigned int nb_clients = 0, nb_bots = 0, i;
2346 const char *crypto_idstring;
2349 // How many clients are there?
2350 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2352 if (svs.clients[i].active)
2355 if (!svs.clients[i].netconnection)
2361 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2367 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2368 if(*q != '\\' && *q != '\n')
2373 /// \TODO: we should add more information for the full status string
2374 crypto_idstring = Crypto_GetInfoResponseDataString();
2375 length = dpsnprintf(out_msg, out_size,
2376 "\377\377\377\377%s\x0A"
2377 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2378 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2383 fullstatus ? "statusResponse" : "infoResponse",
2384 gamename, com_modname, gameversion.integer, svs.maxclients,
2385 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2386 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2387 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2388 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2389 fullstatus ? "\n" : "");
2391 // Make sure it fits in the buffer
2401 savelength = length;
2403 ptr = out_msg + length;
2404 left = (int)out_size - length;
2406 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2408 client_t *cl = &svs.clients[i];
2411 int nameind, cleanind, pingvalue;
2413 char cleanname [sizeof(cl->name)];
2417 // Remove all characters '"' and '\' in the player name
2422 curchar = cl->name[nameind++];
2423 if (curchar != '"' && curchar != '\\')
2425 cleanname[cleanind++] = curchar;
2426 if (cleanind == sizeof(cleanname) - 1)
2429 } while (curchar != '\0');
2430 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2432 pingvalue = (int)(cl->ping * 1000.0f);
2433 if(cl->netconnection)
2434 pingvalue = bound(1, pingvalue, 9999);
2439 ed = PRVM_EDICT_NUM(i + 1);
2440 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2446 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2447 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2452 if ((gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) && (teamplay.integer > 0))
2454 if(cl->frags == -666) // spectator
2455 strlcpy(teambuf, " 0", sizeof(teambuf));
2456 else if(cl->colors == 0x44) // red team
2457 strlcpy(teambuf, " 1", sizeof(teambuf));
2458 else if(cl->colors == 0xDD) // blue team
2459 strlcpy(teambuf, " 2", sizeof(teambuf));
2460 else if(cl->colors == 0xCC) // yellow team
2461 strlcpy(teambuf, " 3", sizeof(teambuf));
2462 else if(cl->colors == 0x99) // pink team
2463 strlcpy(teambuf, " 4", sizeof(teambuf));
2465 strlcpy(teambuf, " 0", sizeof(teambuf));
2470 // note: team number is inserted according to SoF2 protocol
2472 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2478 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2487 // turn it into an infoResponse!
2488 out_msg[savelength] = 0;
2489 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2490 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2505 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2507 size_t floodslotnum, bestfloodslotnum;
2508 double bestfloodtime;
2509 lhnetaddress_t noportpeeraddress;
2510 // see if this is a connect flood
2511 noportpeeraddress = *peeraddress;
2512 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2513 bestfloodslotnum = 0;
2514 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2515 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2517 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2519 bestfloodtime = floodlist[floodslotnum].lasttime;
2520 bestfloodslotnum = floodslotnum;
2522 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2524 // this address matches an ongoing flood address
2525 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2529 // renew the ban on this address so it does not expire
2530 // until the flood has subsided
2531 floodlist[floodslotnum].lasttime = realtime;
2533 //Con_Printf("Flood detected!\n");
2536 // the flood appears to have subsided, so allow this
2537 bestfloodslotnum = floodslotnum; // reuse the same slot
2541 // begin a new timeout on this address
2542 floodlist[bestfloodslotnum].address = noportpeeraddress;
2543 floodlist[bestfloodslotnum].lasttime = realtime;
2544 //Con_Printf("Flood detection initiated!\n");
2548 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2550 size_t floodslotnum;
2551 lhnetaddress_t noportpeeraddress;
2552 // see if this is a connect flood
2553 noportpeeraddress = *peeraddress;
2554 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2555 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2557 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2559 // this address matches an ongoing flood address
2561 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2562 floodlist[floodslotnum].lasttime = 0;
2563 //Con_Printf("Flood cleared!\n");
2568 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2570 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2575 t1 = (long) time(NULL);
2576 t2 = strtol(s, NULL, 0);
2577 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2580 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2583 return !memcmp(mdfourbuf, hash, 16);
2586 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2591 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2594 // validate the challenge
2595 for (i = 0;i < MAX_CHALLENGES;i++)
2596 if(challenge[i].time > 0)
2597 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2599 // if the challenge is not recognized, drop the packet
2600 if (i == MAX_CHALLENGES)
2603 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2606 if(memcmp(mdfourbuf, hash, 16))
2609 // unmark challenge to prevent replay attacks
2610 challenge[i].time = 0;
2615 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2617 return !strcmp(password, hash);
2620 /// returns a string describing the user level, or NULL for auth failure
2621 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)
2623 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2624 static char buf[MAX_INPUTLINE];
2626 qboolean restricted = false;
2627 qboolean have_usernames = false;
2630 userpass_start = rcon_password.string;
2631 while((userpass_end = strchr(userpass_start, ' ')))
2633 have_usernames = true;
2634 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2636 if(comparator(peeraddress, buf, password, cs, cslen))
2638 userpass_start = userpass_end + 1;
2640 if(userpass_start[0])
2642 userpass_end = userpass_start + strlen(userpass_start);
2643 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2648 have_usernames = false;
2649 userpass_start = rcon_restricted_password.string;
2650 while((userpass_end = strchr(userpass_start, ' ')))
2652 have_usernames = true;
2653 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2655 if(comparator(peeraddress, buf, password, cs, cslen))
2657 userpass_start = userpass_end + 1;
2659 if(userpass_start[0])
2661 userpass_end = userpass_start + strlen(userpass_start);
2662 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2666 return NULL; // DENIED
2669 for(text = s; text != endpos; ++text)
2670 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2671 return NULL; // block possible exploits against the parser/alias expansion
2675 size_t l = strlen(s);
2678 hasquotes = (strchr(s, '"') != NULL);
2679 // sorry, we can't allow these substrings in wildcard expressions,
2680 // as they can mess with the argument counts
2681 text = rcon_restricted_commands.string;
2682 while(COM_ParseToken_Console(&text))
2684 // com_token now contains a pattern to check for...
2685 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2688 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2691 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2693 if(!strcmp(com_token, s))
2696 else // single-arg expression? must match the beginning of the command
2698 if(!strcmp(com_token, s))
2700 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2704 // if we got here, nothing matched!
2712 userpass_startpass = strchr(userpass_start, ':');
2713 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2714 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2716 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2719 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2723 // looks like a legitimate rcon command with the correct password
2724 const char *s_ptr = s;
2725 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2726 while(s_ptr != endpos)
2728 size_t l = strlen(s_ptr);
2730 Con_Printf(" %s;", s_ptr);
2735 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2736 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2739 size_t l = strlen(s);
2742 client_t *host_client_save = host_client;
2743 Cmd_ExecuteString(s, src_command, true);
2744 host_client = host_client_save;
2745 // in case it is a command that changes host_client (like restart)
2749 Con_Rcon_Redirect_End();
2753 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2757 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2759 int i, ret, clientnum, best;
2762 char *s, *string, response[1400], addressstring2[128];
2763 static char stringbuf[16384]; // server only
2764 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2765 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2766 size_t sendlength, response_len;
2767 char infostringvalue[MAX_INPUTLINE];
2773 // convert the address to a string incase we need it
2774 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2776 // see if we can identify the sender as a local player
2777 // (this is necessary for rcon to send a reliable reply if the client is
2778 // actually on the server, not sending remotely)
2779 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2780 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2782 if (i == svs.maxclients)
2785 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2787 // received a command string - strip off the packaging and put it
2788 // into our string buffer with NULL termination
2791 length = min(length, (int)sizeof(stringbuf) - 1);
2792 memcpy(stringbuf, data, length);
2793 stringbuf[length] = 0;
2796 if (developer_extra.integer)
2798 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2799 Com_HexDumpToConsole(data, length);
2802 sendlength = sizeof(senddata) - 4;
2803 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2805 case CRYPTO_NOMATCH:
2811 memcpy(senddata, "\377\377\377\377", 4);
2812 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2815 case CRYPTO_DISCARD:
2818 memcpy(senddata, "\377\377\377\377", 4);
2819 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2823 case CRYPTO_REPLACE:
2824 string = senddata+4;
2825 length = sendlength;
2829 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2831 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2833 if(challenge[i].time > 0)
2834 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2836 if (besttime > challenge[i].time)
2837 besttime = challenge[best = i].time;
2839 // if we did not find an exact match, choose the oldest and
2840 // update address and string
2841 if (i == MAX_CHALLENGES)
2844 challenge[i].address = *peeraddress;
2845 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2849 // flood control: drop if requesting challenge too often
2850 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2853 challenge[i].time = realtime;
2854 // send the challenge
2855 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2856 response_len = strlen(response) + 1;
2857 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2858 NetConn_Write(mysocket, response, response_len, peeraddress);
2861 if (length > 8 && !memcmp(string, "connect\\", 8))
2863 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2867 if(crypto && crypto->authenticated)
2869 // no need to check challenge
2870 if(crypto_developer.integer)
2872 Con_Printf("%s connection to %s is being established: client is %s@%.*s, I am %.*s@%.*s\n",
2873 crypto->use_aes ? "Encrypted" : "Authenticated",
2875 crypto->client_idfp[0] ? crypto->client_idfp : "-",
2876 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
2877 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
2878 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
2884 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
2886 // validate the challenge
2887 for (i = 0;i < MAX_CHALLENGES;i++)
2888 if(challenge[i].time > 0)
2889 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
2891 // if the challenge is not recognized, drop the packet
2892 if (i == MAX_CHALLENGES)
2897 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
2898 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
2900 if(!(islocal || sv_public.integer > -2))
2902 if (developer_extra.integer)
2903 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
2904 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
2908 // check engine protocol
2909 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
2911 if (developer_extra.integer)
2912 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
2913 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
2917 // see if this is a duplicate connection request or a disconnected
2918 // client who is rejoining to the same client slot
2919 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2921 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
2923 // this is a known client...
2924 if(crypto && crypto->authenticated)
2926 // reject if changing key!
2927 if(client->netconnection->crypto.authenticated)
2930 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
2932 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
2934 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
2936 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
2939 if (developer_extra.integer)
2940 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
2941 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
2948 // reject if downgrading!
2949 if(client->netconnection->crypto.authenticated)
2951 if (developer_extra.integer)
2952 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
2953 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
2959 // client crashed and is coming back,
2960 // keep their stuff intact
2961 if (developer_extra.integer)
2962 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
2963 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2964 if(crypto && crypto->authenticated)
2965 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
2966 SV_SendServerinfo(client);
2970 // client is still trying to connect,
2971 // so we send a duplicate reply
2972 if (developer_extra.integer)
2973 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
2974 if(crypto && crypto->authenticated)
2975 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
2976 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2982 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
2985 // find an empty client slot for this new client
2986 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2989 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
2991 // allocated connection
2992 if (developer_extra.integer)
2993 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
2994 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2995 // now set up the client
2996 if(crypto && crypto->authenticated)
2997 Crypto_ServerFinishInstance(&conn->crypto, crypto);
2998 SV_ConnectClient(clientnum, conn);
2999 NetConn_Heartbeat(1);
3004 // no empty slots found - server is full
3005 if (developer_extra.integer)
3006 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3007 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3011 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3013 const char *challenge = NULL;
3015 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3018 // If there was a challenge in the getinfo message
3019 if (length > 8 && string[7] == ' ')
3020 challenge = string + 8;
3022 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3024 if (developer_extra.integer)
3025 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3026 NetConn_WriteString(mysocket, response, peeraddress);
3030 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3032 const char *challenge = NULL;
3034 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3037 // If there was a challenge in the getinfo message
3038 if (length > 10 && string[9] == ' ')
3039 challenge = string + 10;
3041 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3043 if (developer_extra.integer)
3044 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3045 NetConn_WriteString(mysocket, response, peeraddress);
3049 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3051 char *password = string + 20;
3052 char *timeval = string + 37;
3053 char *s = strchr(timeval, ' ');
3054 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3055 const char *userlevel;
3057 if(rcon_secure.integer > 1)
3061 return true; // invalid packet
3064 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3065 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3068 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3070 char *password = string + 25;
3071 char *challenge = string + 42;
3072 char *s = strchr(challenge, ' ');
3073 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3074 const char *userlevel;
3076 return true; // invalid packet
3079 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3080 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3083 if (length >= 5 && !memcmp(string, "rcon ", 5))
3086 char *s = string + 5;
3087 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3090 if(rcon_secure.integer > 0)
3093 for (i = 0;!ISWHITESPACE(*s);s++)
3094 if (i < (int)sizeof(password) - 1)
3096 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3099 if (!ISWHITESPACE(password[0]))
3101 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3102 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3106 if (!strncmp(string, "extResponse ", 12))
3108 ++sv_net_extresponse_count;
3109 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3110 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3111 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3112 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3115 if (!strncmp(string, "ping", 4))
3117 if (developer_extra.integer)
3118 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3119 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3122 if (!strncmp(string, "ack", 3))
3124 // we may not have liked the packet, but it was a command packet, so
3125 // we're done processing this packet now
3128 // netquake control packets, supported for compatibility only, and only
3129 // when running game protocols that are normally served via this connection
3131 // (this protects more modern protocols against being used for
3132 // Quake packet flood Denial Of Service attacks)
3133 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)
3137 const char *protocolname;
3140 SZ_Clear(&sv_message);
3141 SZ_Write(&sv_message, data, length);
3142 MSG_BeginReading(&sv_message);
3143 c = MSG_ReadByte(&sv_message);
3147 if (developer_extra.integer)
3148 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3149 if(!(islocal || sv_public.integer > -2))
3151 if (developer_extra.integer)
3152 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3153 SZ_Clear(&sv_message);
3154 // save space for the header, filled in later
3155 MSG_WriteLong(&sv_message, 0);
3156 MSG_WriteByte(&sv_message, CCREP_REJECT);
3157 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3158 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3159 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3160 SZ_Clear(&sv_message);
3164 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3165 protocolnumber = MSG_ReadByte(&sv_message);
3166 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3168 if (developer_extra.integer)
3169 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3170 SZ_Clear(&sv_message);
3171 // save space for the header, filled in later
3172 MSG_WriteLong(&sv_message, 0);
3173 MSG_WriteByte(&sv_message, CCREP_REJECT);
3174 MSG_WriteString(&sv_message, "Incompatible version.\n");
3175 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3176 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3177 SZ_Clear(&sv_message);
3181 // see if this connect request comes from a known client
3182 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3184 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3186 // this is either a duplicate connection request
3187 // or coming back from a timeout
3188 // (if so, keep their stuff intact)
3190 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3191 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3193 if (developer_extra.integer)
3194 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3195 SZ_Clear(&sv_message);
3196 // save space for the header, filled in later
3197 MSG_WriteLong(&sv_message, 0);
3198 MSG_WriteByte(&sv_message, CCREP_REJECT);
3199 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3200 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3201 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3202 SZ_Clear(&sv_message);
3207 if (developer_extra.integer)
3208 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3209 SZ_Clear(&sv_message);
3210 // save space for the header, filled in later
3211 MSG_WriteLong(&sv_message, 0);
3212 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3213 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3214 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3215 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3216 SZ_Clear(&sv_message);
3218 // if client is already spawned, re-send the
3219 // serverinfo message as they'll need it to play
3221 SV_SendServerinfo(client);
3226 // this is a new client, check for connection flood
3227 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3230 // find a slot for the new client
3231 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3234 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3236 // connect to the client
3237 // everything is allocated, just fill in the details
3238 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3239 if (developer_extra.integer)
3240 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3241 // send back the info about the server connection
3242 SZ_Clear(&sv_message);
3243 // save space for the header, filled in later
3244 MSG_WriteLong(&sv_message, 0);
3245 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3246 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3247 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3248 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3249 SZ_Clear(&sv_message);
3250 // now set up the client struct
3251 SV_ConnectClient(clientnum, conn);
3252 NetConn_Heartbeat(1);
3257 if (developer_extra.integer)
3258 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3259 // no room; try to let player know
3260 SZ_Clear(&sv_message);
3261 // save space for the header, filled in later
3262 MSG_WriteLong(&sv_message, 0);
3263 MSG_WriteByte(&sv_message, CCREP_REJECT);
3264 MSG_WriteString(&sv_message, "Server is full.\n");
3265 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3266 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3267 SZ_Clear(&sv_message);
3269 case CCREQ_SERVER_INFO:
3270 if (developer_extra.integer)
3271 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3272 if(!(islocal || sv_public.integer > -1))
3275 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3278 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3281 char myaddressstring[128];
3282 if (developer_extra.integer)
3283 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3284 SZ_Clear(&sv_message);
3285 // save space for the header, filled in later
3286 MSG_WriteLong(&sv_message, 0);
3287 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3288 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3289 MSG_WriteString(&sv_message, myaddressstring);
3290 MSG_WriteString(&sv_message, hostname.string);
3291 MSG_WriteString(&sv_message, sv.name);
3292 // How many clients are there?
3293 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3294 if (svs.clients[i].active)
3296 MSG_WriteByte(&sv_message, numclients);
3297 MSG_WriteByte(&sv_message, svs.maxclients);
3298 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3299 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3300 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3301 SZ_Clear(&sv_message);
3304 case CCREQ_PLAYER_INFO:
3305 if (developer_extra.integer)
3306 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3307 if(!(islocal || sv_public.integer > -1))
3310 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3315 int playerNumber, activeNumber, clientNumber;
3318 playerNumber = MSG_ReadByte(&sv_message);
3320 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3321 if (client->active && ++activeNumber == playerNumber)
3323 if (clientNumber != svs.maxclients)
3325 SZ_Clear(&sv_message);
3326 // save space for the header, filled in later
3327 MSG_WriteLong(&sv_message, 0);
3328 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3329 MSG_WriteByte(&sv_message, playerNumber);
3330 MSG_WriteString(&sv_message, client->name);
3331 MSG_WriteLong(&sv_message, client->colors);
3332 MSG_WriteLong(&sv_message, client->frags);
3333 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3334 if(sv_status_privacy.integer)
3335 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3337 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3338 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3339 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3340 SZ_Clear(&sv_message);
3344 case CCREQ_RULE_INFO:
3345 if (developer_extra.integer)
3346 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3347 if(!(islocal || sv_public.integer > -1))
3350 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3357 // find the search start location
3358 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3359 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3361 // send the response
3362 SZ_Clear(&sv_message);
3363 // save space for the header, filled in later
3364 MSG_WriteLong(&sv_message, 0);
3365 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3368 MSG_WriteString(&sv_message, var->name);
3369 MSG_WriteString(&sv_message, var->string);
3371 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3372 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3373 SZ_Clear(&sv_message);
3377 if (developer_extra.integer)
3378 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3379 if (sv.active && !rcon_secure.integer)
3381 char password[2048];
3385 const char *userlevel;
3386 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3387 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3389 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3390 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3391 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3398 SZ_Clear(&sv_message);
3399 // we may not have liked the packet, but it was a valid control
3400 // packet, so we're done processing this packet now
3405 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3407 SV_ReadClientMessage();
3414 void NetConn_ServerFrame(void)
3417 lhnetaddress_t peeraddress;
3418 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3419 for (i = 0;i < sv_numsockets;i++)
3420 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3421 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3422 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3424 // never timeout loopback connections
3425 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3427 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3428 SV_DropClient(false);
3433 void NetConn_SleepMicroseconds(int microseconds)
3435 LHNET_SleepUntilPacket_Microseconds(microseconds);
3438 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3442 lhnetaddress_t masteraddress;
3443 lhnetaddress_t broadcastaddress;
3446 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3449 // 26000 is the default quake server port, servers on other ports will not
3451 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3452 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3456 for (i = 0;i < cl_numsockets;i++)
3460 const char *cmdname, *extraoptions;
3461 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3463 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3465 // search LAN for Quake servers
3466 SZ_Clear(&cl_message);
3467 // save space for the header, filled in later
3468 MSG_WriteLong(&cl_message, 0);
3469 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3470 MSG_WriteString(&cl_message, "QUAKE");
3471 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3472 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3473 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3474 SZ_Clear(&cl_message);
3476 // search LAN for DarkPlaces servers
3477 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3480 // build the getservers message to send to the dpmaster master servers
3481 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3483 cmdname = "getserversExt";
3484 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3488 cmdname = "getservers";
3491 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamename, NET_PROTOCOL_VERSION, extraoptions);
3494 for (masternum = 0;sv_masters[masternum].name;masternum++)
3496 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3499 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3503 // search favorite servers
3504 for(j = 0; j < nFavorites; ++j)
3506 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3508 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3509 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3516 // only query QuakeWorld servers when the user wants to
3519 for (i = 0;i < cl_numsockets;i++)
3523 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3525 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3527 // search LAN for QuakeWorld servers
3528 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3530 // build the getservers message to send to the qwmaster master servers
3531 // note this has no -1 prefix, and the trailing nul byte is sent
3532 dpsnprintf(request, sizeof(request), "c\n");
3536 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3538 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3540 if (m_state != m_slist)
3542 char lookupstring[128];
3543 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3544 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3547 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3551 // search favorite servers
3552 for(j = 0; j < nFavorites; ++j)
3554 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3556 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3558 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3559 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3566 if (!masterquerycount)
3568 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3569 M_Update_Return_Reason("No network");
3573 void NetConn_Heartbeat(int priority)
3575 lhnetaddress_t masteraddress;
3577 lhnetsocket_t *mysocket;
3579 // if it's a state change (client connected), limit next heartbeat to no
3580 // more than 30 sec in the future
3581 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3582 nextheartbeattime = realtime + 30.0;
3584 // limit heartbeatperiod to 30 to 270 second range,
3585 // lower limit is to avoid abusing master servers with excess traffic,
3586 // upper limit is to avoid timing out on the master server (which uses
3588 if (sv_heartbeatperiod.value < 30)
3589 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3590 if (sv_heartbeatperiod.value > 270)
3591 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3593 // make advertising optional and don't advertise singleplayer games, and
3594 // only send a heartbeat as often as the admin wants
3595 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3597 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3598 for (masternum = 0;sv_masters[masternum].name;masternum++)
3599 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3600 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3604 static void Net_Heartbeat_f(void)
3607 NetConn_Heartbeat(2);
3609 Con_Print("No server running, can not heartbeat to master server.\n");
3612 static void PrintStats(netconn_t *conn)
3614 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3615 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3617 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3618 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3619 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3620 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3621 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3622 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3623 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3624 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3625 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3626 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3629 void Net_Stats_f(void)
3632 Con_Print("connections =\n");
3633 for (conn = netconn_list;conn;conn = conn->next)
3637 void Net_Refresh_f(void)
3639 if (m_state != m_slist) {
3640 Con_Print("Sending new requests to master servers\n");
3641 ServerList_QueryList(false, true, false, true);
3642 Con_Print("Listening for replies...\n");
3644 ServerList_QueryList(false, true, false, false);
3647 void Net_Slist_f(void)
3649 ServerList_ResetMasks();
3650 serverlist_sortbyfield = SLIF_PING;
3651 serverlist_sortflags = 0;
3652 if (m_state != m_slist) {
3653 Con_Print("Sending requests to master servers\n");
3654 ServerList_QueryList(true, true, false, true);
3655 Con_Print("Listening for replies...\n");
3657 ServerList_QueryList(true, true, false, false);
3660 void Net_SlistQW_f(void)
3662 ServerList_ResetMasks();
3663 serverlist_sortbyfield = SLIF_PING;
3664 serverlist_sortflags = 0;
3665 if (m_state != m_slist) {
3666 Con_Print("Sending requests to master servers\n");
3667 ServerList_QueryList(true, false, true, true);
3668 serverlist_consoleoutput = true;
3669 Con_Print("Listening for replies...\n");
3671 ServerList_QueryList(true, false, true, false);
3674 void NetConn_Init(void)
3677 lhnetaddress_t tempaddress;
3678 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3679 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3680 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3681 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3682 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3683 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3684 Cvar_RegisterVariable(&rcon_restricted_password);
3685 Cvar_RegisterVariable(&rcon_restricted_commands);
3686 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3687 Cvar_RegisterVariable(&net_slist_queriespersecond);
3688 Cvar_RegisterVariable(&net_slist_queriesperframe);
3689 Cvar_RegisterVariable(&net_slist_timeout);
3690 Cvar_RegisterVariable(&net_slist_maxtries);
3691 Cvar_RegisterVariable(&net_slist_favorites);
3692 Cvar_RegisterVariable(&net_slist_pause);
3693 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3694 Cvar_RegisterVariable(&net_tos_dscp);
3695 Cvar_RegisterVariable(&net_messagetimeout);
3696 Cvar_RegisterVariable(&net_connecttimeout);
3697 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3698 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3699 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3700 Cvar_RegisterVariable(&cl_netlocalping);
3701 Cvar_RegisterVariable(&cl_netpacketloss_send);
3702 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3703 Cvar_RegisterVariable(&hostname);
3704 Cvar_RegisterVariable(&developer_networking);
3705 Cvar_RegisterVariable(&cl_netport);
3706 Cvar_RegisterVariable(&sv_netport);
3707 Cvar_RegisterVariable(&net_address);
3708 Cvar_RegisterVariable(&net_address_ipv6);
3709 Cvar_RegisterVariable(&sv_public);
3710 Cvar_RegisterVariable(&sv_public_rejectreason);
3711 Cvar_RegisterVariable(&sv_heartbeatperiod);
3712 for (i = 0;sv_masters[i].name;i++)
3713 Cvar_RegisterVariable(&sv_masters[i]);
3714 Cvar_RegisterVariable(&gameversion);
3715 Cvar_RegisterVariable(&gameversion_min);
3716 Cvar_RegisterVariable(&gameversion_max);
3717 // 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.
3718 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3720 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3722 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3723 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3726 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3728 // 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
3729 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3731 i = atoi(com_argv[i + 1]);
3732 if (i >= 0 && i < 65536)
3734 Con_Printf("-port option used, setting port cvar to %i\n", i);
3735 Cvar_SetValueQuick(&sv_netport, i);
3738 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3742 cl_message.data = cl_message_buf;
3743 cl_message.maxsize = sizeof(cl_message_buf);
3744 cl_message.cursize = 0;
3745 sv_message.data = sv_message_buf;
3746 sv_message.maxsize = sizeof(sv_message_buf);
3747 sv_message.cursize = 0;
3749 if (Thread_HasThreads())
3750 netconn_mutex = Thread_CreateMutex();
3753 void NetConn_Shutdown(void)
3755 NetConn_CloseClientPorts();
3756 NetConn_CloseServerPorts();
3759 Thread_DestroyMutex(netconn_mutex);
3760 netconn_mutex = NULL;