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:1::2e26:f351:3]: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 gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
97 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"};
98 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"};
99 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"};
100 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
101 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)"};
102 extern cvar_t rcon_secure;
103 extern cvar_t rcon_secure_challengetimeout;
105 double masterquerytime = -1000;
106 int masterquerycount = 0;
107 int masterreplycount = 0;
108 int serverquerycount = 0;
109 int serverreplycount = 0;
111 challenge_t challenge[MAX_CHALLENGES];
113 /// this is only false if there are still servers left to query
114 static qboolean serverlist_querysleep = true;
115 static qboolean serverlist_paused = false;
116 /// this is pushed a second or two ahead of realtime whenever a master server
117 /// reply is received, to avoid issuing queries while master replies are still
118 /// flooding in (which would make a mess of the ping times)
119 static double serverlist_querywaittime = 0;
121 static int cl_numsockets;
122 static lhnetsocket_t *cl_sockets[16];
123 static int sv_numsockets;
124 static lhnetsocket_t *sv_sockets[16];
126 netconn_t *netconn_list = NULL;
127 mempool_t *netconn_mempool = NULL;
128 void *netconn_mutex = NULL;
130 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
131 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
132 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
133 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
135 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
136 int cl_net_extresponse_count = 0;
137 int cl_net_extresponse_last = 0;
139 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
140 int sv_net_extresponse_count = 0;
141 int sv_net_extresponse_last = 0;
143 // ServerList interface
144 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
145 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
147 serverlist_infofield_t serverlist_sortbyfield;
148 int serverlist_sortflags;
150 int serverlist_viewcount = 0;
151 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
153 int serverlist_maxcachecount = 0;
154 int serverlist_cachecount = 0;
155 serverlist_entry_t *serverlist_cache = NULL;
157 qboolean serverlist_consoleoutput;
159 static int nFavorites = 0;
160 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
161 static int nFavorites_idfp = 0;
162 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
164 void NetConn_UpdateFavorites(void)
169 p = net_slist_favorites.string;
170 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
172 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
173 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
174 // (if v6 address contains port, it must start with '[')
176 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
181 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
187 /// helper function to insert a value into the viewset
188 /// spare entries will be removed
189 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
192 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
193 i = serverlist_viewcount++;
195 i = SERVERLIST_VIEWLISTSIZE - 1;
198 for( ; i > index ; i-- )
199 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
201 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
204 /// we suppose serverlist_viewcount to be valid, ie > 0
205 static void _ServerList_ViewList_Helper_Remove( int index )
207 serverlist_viewcount--;
208 for( ; index < serverlist_viewcount ; index++ )
209 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
212 /// \returns true if A should be inserted before B
213 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
215 int result = 0; // > 0 if for numbers A > B and for text if A < B
217 if( serverlist_sortflags & SLSF_FAVORITESFIRST )
219 if(A->info.isfavorite != B->info.isfavorite)
220 return A->info.isfavorite;
223 switch( serverlist_sortbyfield ) {
225 result = A->info.ping - B->info.ping;
227 case SLIF_MAXPLAYERS:
228 result = A->info.maxplayers - B->info.maxplayers;
230 case SLIF_NUMPLAYERS:
231 result = A->info.numplayers - B->info.numplayers;
234 result = A->info.numbots - B->info.numbots;
237 result = A->info.numhumans - B->info.numhumans;
240 result = A->info.freeslots - B->info.freeslots;
243 result = A->info.protocol - B->info.protocol;
246 result = strcmp( B->info.cname, A->info.cname );
249 result = strcasecmp( B->info.game, A->info.game );
252 result = strcasecmp( B->info.map, A->info.map );
255 result = strcasecmp( B->info.mod, A->info.mod );
258 result = strcasecmp( B->info.name, A->info.name );
261 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
263 case SLIF_ISFAVORITE:
264 result = !!B->info.isfavorite - !!A->info.isfavorite;
267 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
273 if( serverlist_sortflags & SLSF_DESCENDING )
279 // if the chosen sort key is identical, sort by index
280 // (makes this a stable sort, so that later replies from servers won't
281 // shuffle the servers around when they have the same ping)
285 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
287 // This should actually be done with some intermediate and end-of-function return
299 case SLMO_GREATEREQUAL:
301 case SLMO_NOTCONTAIN:
302 case SLMO_STARTSWITH:
303 case SLMO_NOTSTARTSWITH:
306 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
311 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
314 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
315 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
316 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
317 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
319 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
320 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
323 // Same here, also using an intermediate & final return would be more appropriate
327 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
328 case SLMO_NOTCONTAIN:
329 return !*bufferB || !strstr( bufferA, bufferB );
330 case SLMO_STARTSWITH:
331 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
332 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
333 case SLMO_NOTSTARTSWITH:
334 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
336 return strcmp( bufferA, bufferB ) < 0;
338 return strcmp( bufferA, bufferB ) <= 0;
340 return strcmp( bufferA, bufferB ) == 0;
342 return strcmp( bufferA, bufferB ) > 0;
344 return strcmp( bufferA, bufferB ) != 0;
345 case SLMO_GREATEREQUAL:
346 return strcmp( bufferA, bufferB ) >= 0;
348 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
353 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
355 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
357 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
359 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
361 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
363 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
365 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
367 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
369 if( *mask->info.cname
370 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
373 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
376 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
379 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
382 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
384 if( *mask->info.qcstatus
385 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
387 if( *mask->info.players
388 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
390 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
395 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
397 int start, end, mid, i;
400 // reject incompatible servers
402 entry->info.gameversion != gameversion.integer
405 gameversion_min.integer >= 0 // min/max range set by user/mod?
406 && gameversion_max.integer >= 0
407 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
408 && gameversion_max.integer >= entry->info.gameversion
413 // refresh the "favorite" status
414 entry->info.isfavorite = false;
415 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
417 char idfp[FP64_SIZE+1];
418 for(i = 0; i < nFavorites; ++i)
420 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
422 entry->info.isfavorite = true;
426 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
428 for(i = 0; i < nFavorites_idfp; ++i)
430 if(!strcmp(idfp, favorites_idfp[i]))
432 entry->info.isfavorite = true;
439 // FIXME: change this to be more readable (...)
440 // now check whether it passes through the masks
441 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
442 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
445 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
446 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
448 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
451 if( !serverlist_viewcount ) {
452 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
455 // ok, insert it, we just need to find out where exactly:
458 // check whether to insert it as new first item
459 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
460 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
462 } // check whether to insert it as new last item
463 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
464 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
468 end = serverlist_viewcount - 1;
469 while( end > start + 1 )
471 mid = (start + end) / 2;
472 // test the item that lies in the middle between start and end
473 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
474 // the item has to be in the upper half
477 // the item has to be in the lower half
480 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
483 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
486 for( i = 0; i < serverlist_viewcount; i++ )
488 if (ServerList_GetViewEntry(i) == entry)
490 _ServerList_ViewList_Helper_Remove(i);
496 void ServerList_RebuildViewList(void)
500 serverlist_viewcount = 0;
501 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
502 serverlist_entry_t *entry = &serverlist_cache[i];
503 // also display entries that are currently being refreshed [11/8/2007 Black]
504 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
505 ServerList_ViewList_Insert( entry );
509 void ServerList_ResetMasks(void)
513 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
514 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
515 // numbots needs to be compared to -1 to always succeed
516 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
517 serverlist_andmasks[i].info.numbots = -1;
518 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
519 serverlist_ormasks[i].info.numbots = -1;
522 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
525 int numplayers = 0, maxplayers = 0;
526 for (i = 0;i < serverlist_cachecount;i++)
528 if (serverlist_cache[i].query == SQS_QUERIED)
530 numplayers += serverlist_cache[i].info.numhumans;
531 maxplayers += serverlist_cache[i].info.maxplayers;
534 *numplayerspointer = numplayers;
535 *maxplayerspointer = maxplayers;
539 static void _ServerList_Test(void)
542 if (serverlist_maxcachecount <= 1024)
544 serverlist_maxcachecount = 1024;
545 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
547 for( i = 0 ; i < 1024 ; i++ ) {
548 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
549 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
550 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
551 serverlist_cache[serverlist_cachecount].finished = true;
552 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 );
553 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
554 serverlist_cachecount++;
559 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
561 masterquerytime = realtime;
562 masterquerycount = 0;
563 masterreplycount = 0;
565 serverquerycount = 0;
566 serverreplycount = 0;
567 serverlist_cachecount = 0;
568 serverlist_viewcount = 0;
569 serverlist_maxcachecount = 0;
570 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
572 // refresh all entries
574 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
575 serverlist_entry_t *entry = &serverlist_cache[ n ];
576 entry->query = SQS_REFRESHING;
577 entry->querycounter = 0;
580 serverlist_consoleoutput = consoleoutput;
582 //_ServerList_Test();
584 NetConn_QueryMasters(querydp, queryqw);
589 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
593 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
594 Thread_LockMutex(netconn_mutex);
595 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
596 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
597 Thread_UnlockMutex(netconn_mutex);
600 if (cl_netpacketloss_receive.integer)
601 for (i = 0;i < cl_numsockets;i++)
602 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
604 if (developer_networking.integer)
606 char addressstring[128], addressstring2[128];
607 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
610 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
611 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
612 Com_HexDumpToConsole((unsigned char *)data, length);
615 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
620 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
624 if (cl_netpacketloss_send.integer)
625 for (i = 0;i < cl_numsockets;i++)
626 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
628 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
629 Thread_LockMutex(netconn_mutex);
630 ret = LHNET_Write(mysocket, data, length, peeraddress);
631 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
632 Thread_UnlockMutex(netconn_mutex);
633 if (developer_networking.integer)
635 char addressstring[128], addressstring2[128];
636 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
637 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
638 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)");
639 Com_HexDumpToConsole((unsigned char *)data, length);
644 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
646 // note this does not include the trailing NULL because we add that in the parser
647 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
650 qboolean NetConn_CanSend(netconn_t *conn)
652 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
653 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = realtime;
654 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
655 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
656 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
657 if (realtime > conn->cleartime)
661 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
666 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables)
669 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
670 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
672 // if this packet was supposedly choked, but we find ourselves sending one
673 // anyway, make sure the size counting starts at zero
674 // (this mostly happens on level changes and disconnects and such)
675 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
676 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
678 if (protocol == PROTOCOL_QUAKEWORLD)
681 qboolean sendreliable;
683 // note that it is ok to send empty messages to the qw server,
684 // otherwise it won't respond to us at all
686 sendreliable = false;
687 // if the remote side dropped the last reliable message, resend it
688 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
690 // if the reliable transmit buffer is empty, copy the current message out
691 if (!conn->sendMessageLength && conn->message.cursize)
693 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
694 conn->sendMessageLength = conn->message.cursize;
695 SZ_Clear(&conn->message); // clear the message buffer
696 conn->qw.reliable_sequence ^= 1;
699 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
700 StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
701 // last received unreliable packet number, and last received reliable packet number (0 or 1)
702 StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
704 conn->outgoing_unreliable_sequence++;
705 // client sends qport in every packet
706 if (conn == cls.netcon)
708 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
710 // also update cls.qw_outgoing_sequence
711 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
713 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
715 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
719 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
721 // add the reliable message if there is one
724 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
725 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
726 packetLen += conn->sendMessageLength;
727 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
730 // add the unreliable message if possible
731 if (packetLen + data->cursize <= 1400)
733 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
734 memcpy(sendbuffer + packetLen, data->data, data->cursize);
735 packetLen += data->cursize;
738 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
741 conn->unreliableMessagesSent++;
743 totallen += packetLen + 28;
747 unsigned int packetLen;
748 unsigned int dataLen;
753 // if a reliable message fragment has been lost, send it again
754 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
756 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
758 dataLen = conn->sendMessageLength;
763 dataLen = MAX_PACKETFRAGMENT;
767 packetLen = NET_HEADERSIZE + dataLen;
769 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
770 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
771 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
773 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
775 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
776 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
778 conn->lastSendTime = realtime;
779 conn->packetsReSent++;
782 totallen += sendmelen + 28;
785 // if we have a new reliable message to send, do so
786 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
788 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
790 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
791 conn->message.overflowed = true;
795 if (developer_networking.integer && conn == cls.netcon)
797 Con_Print("client sending reliable message to server:\n");
798 SZ_HexDumpToConsole(&conn->message);
801 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
802 conn->sendMessageLength = conn->message.cursize;
803 SZ_Clear(&conn->message);
805 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
807 dataLen = conn->sendMessageLength;
812 dataLen = MAX_PACKETFRAGMENT;
816 packetLen = NET_HEADERSIZE + dataLen;
818 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
819 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
820 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
822 conn->nq.sendSequence++;
824 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
826 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
828 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
830 conn->lastSendTime = realtime;
832 conn->reliableMessagesSent++;
834 totallen += sendmelen + 28;
837 // if we have an unreliable message to send, do so
840 packetLen = NET_HEADERSIZE + data->cursize;
842 if (packetLen > (int)sizeof(sendbuffer))
844 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
848 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE);
849 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
850 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
852 conn->outgoing_unreliable_sequence++;
854 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
856 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
858 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
861 conn->unreliableMessagesSent++;
863 totallen += sendmelen + 28;
867 // delay later packets to obey rate limit
868 if (conn->cleartime < realtime - 0.1)
869 conn->cleartime = realtime - 0.1;
870 conn->cleartime = conn->cleartime + (double)totallen / (double)rate;
871 if (conn->cleartime < realtime)
872 conn->cleartime = realtime;
877 qboolean NetConn_HaveClientPorts(void)
879 return !!cl_numsockets;
882 qboolean NetConn_HaveServerPorts(void)
884 return !!sv_numsockets;
887 void NetConn_CloseClientPorts(void)
889 for (;cl_numsockets > 0;cl_numsockets--)
890 if (cl_sockets[cl_numsockets - 1])
891 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
894 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
896 lhnetaddress_t address;
899 char addressstring2[1024];
900 if (addressstring && addressstring[0])
901 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
903 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
906 if ((s = LHNET_OpenSocket_Connectionless(&address)))
908 cl_sockets[cl_numsockets++] = s;
909 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
910 if (addresstype != LHNETADDRESSTYPE_LOOP)
911 Con_Printf("Client opened a socket on address %s\n", addressstring2);
915 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
916 Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
920 Con_Printf("Client unable to parse address %s\n", addressstring);
923 void NetConn_OpenClientPorts(void)
926 NetConn_CloseClientPorts();
928 SV_LockThreadMutex(); // FIXME recursive?
929 Crypto_LoadKeys(); // client sockets
930 SV_UnlockThreadMutex();
932 port = bound(0, cl_netport.integer, 65535);
933 if (cl_netport.integer != port)
934 Cvar_SetValueQuick(&cl_netport, port);
936 Con_Printf("Client using an automatically assigned port\n");
938 Con_Printf("Client using port %i\n", port);
939 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
940 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
942 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
946 void NetConn_CloseServerPorts(void)
948 for (;sv_numsockets > 0;sv_numsockets--)
949 if (sv_sockets[sv_numsockets - 1])
950 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
953 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
955 lhnetaddress_t address;
958 char addressstring2[1024];
961 for (port = defaultport; port <= defaultport + range; port++)
963 if (addressstring && addressstring[0])
964 success = LHNETADDRESS_FromString(&address, addressstring, port);
966 success = LHNETADDRESS_FromPort(&address, addresstype, port);
969 if ((s = LHNET_OpenSocket_Connectionless(&address)))
971 sv_sockets[sv_numsockets++] = s;
972 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
973 if (addresstype != LHNETADDRESSTYPE_LOOP)
974 Con_Printf("Server listening on address %s\n", addressstring2);
979 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
980 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
985 Con_Printf("Server unable to parse address %s\n", addressstring);
986 // if it cant parse one address, it wont be able to parse another for sure
993 void NetConn_OpenServerPorts(int opennetports)
996 NetConn_CloseServerPorts();
998 SV_LockThreadMutex(); // FIXME recursive?
999 Crypto_LoadKeys(); // server sockets
1000 SV_UnlockThreadMutex();
1002 NetConn_UpdateSockets();
1003 port = bound(0, sv_netport.integer, 65535);
1006 Con_Printf("Server using port %i\n", port);
1007 if (sv_netport.integer != port)
1008 Cvar_SetValueQuick(&sv_netport, port);
1009 if (cls.state != ca_dedicated)
1010 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1014 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1015 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1017 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1020 if (sv_numsockets == 0)
1021 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1024 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1026 int i, a = LHNETADDRESS_GetAddressType(address);
1027 for (i = 0;i < cl_numsockets;i++)
1028 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1029 return cl_sockets[i];
1033 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1035 int i, a = LHNETADDRESS_GetAddressType(address);
1036 for (i = 0;i < sv_numsockets;i++)
1037 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1038 return sv_sockets[i];
1042 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1045 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1046 conn->mysocket = mysocket;
1047 conn->peeraddress = *peeraddress;
1048 conn->lastMessageTime = realtime;
1049 conn->message.data = conn->messagedata;
1050 conn->message.maxsize = sizeof(conn->messagedata);
1051 conn->message.cursize = 0;
1052 // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1053 // reduce effectiveness of connection request floods
1054 conn->timeout = realtime + net_connecttimeout.value;
1055 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1056 conn->next = netconn_list;
1057 netconn_list = conn;
1061 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1062 void NetConn_Close(netconn_t *conn)
1065 // remove connection from list
1067 // allow the client to reconnect immediately
1068 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1070 if (conn == netconn_list)
1071 netconn_list = conn->next;
1074 for (c = netconn_list;c;c = c->next)
1076 if (c->next == conn)
1078 c->next = conn->next;
1082 // not found in list, we'll avoid crashing here...
1090 static int clientport = -1;
1091 static int clientport2 = -1;
1092 static int hostport = -1;
1093 void NetConn_UpdateSockets(void)
1097 if (cls.state != ca_dedicated)
1099 if (clientport2 != cl_netport.integer)
1101 clientport2 = cl_netport.integer;
1102 if (cls.state == ca_connected)
1103 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1105 if (cls.state == ca_disconnected && clientport != clientport2)
1107 clientport = clientport2;
1108 NetConn_CloseClientPorts();
1110 if (cl_numsockets == 0)
1111 NetConn_OpenClientPorts();
1114 if (hostport != sv_netport.integer)
1116 hostport = sv_netport.integer;
1118 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1121 for (j = 0;j < MAX_RCONS;j++)
1123 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1124 if(cls.rcon_commands[i][0])
1126 if(realtime > cls.rcon_timeout[i])
1129 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1130 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1131 cls.rcon_commands[i][0] = 0;
1139 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1141 int originallength = length;
1142 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1143 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1144 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1148 if (protocol == PROTOCOL_QUAKEWORLD)
1150 int sequence, sequence_ack;
1151 int reliable_ack, reliable_message;
1155 sequence = LittleLong(*((int *)(data + 0)));
1156 sequence_ack = LittleLong(*((int *)(data + 4)));
1160 if (conn != cls.netcon)
1165 // 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?)
1166 //qport = LittleShort(*((int *)(data + 8)));
1171 conn->packetsReceived++;
1172 reliable_message = (sequence >> 31) & 1;
1173 reliable_ack = (sequence_ack >> 31) & 1;
1174 sequence &= ~(1<<31);
1175 sequence_ack &= ~(1<<31);
1176 if (sequence <= conn->qw.incoming_sequence)
1178 //Con_DPrint("Got a stale datagram\n");
1181 count = sequence - (conn->qw.incoming_sequence + 1);
1184 conn->droppedDatagrams += count;
1185 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1188 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1189 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1190 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1191 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1192 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1195 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1196 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1197 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1198 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1199 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1200 if (reliable_ack == conn->qw.reliable_sequence)
1202 // received, now we will be able to send another reliable message
1203 conn->sendMessageLength = 0;
1204 conn->reliableMessagesReceived++;
1206 conn->qw.incoming_sequence = sequence;
1207 if (conn == cls.netcon)
1208 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1209 conn->qw.incoming_acknowledged = sequence_ack;
1210 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1211 if (reliable_message)
1212 conn->qw.incoming_reliable_sequence ^= 1;
1213 conn->lastMessageTime = realtime;
1214 conn->timeout = realtime + newtimeout;
1215 conn->unreliableMessagesReceived++;
1216 if (conn == cls.netcon)
1218 SZ_Clear(&cl_message);
1219 SZ_Write(&cl_message, data, length);
1220 MSG_BeginReading(&cl_message);
1224 SZ_Clear(&sv_message);
1225 SZ_Write(&sv_message, data, length);
1226 MSG_BeginReading(&sv_message);
1234 unsigned int sequence;
1239 originallength = length;
1240 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1246 qlength = (unsigned int)BuffBigLong(data);
1247 flags = qlength & ~NETFLAG_LENGTH_MASK;
1248 qlength &= NETFLAG_LENGTH_MASK;
1249 // control packets were already handled
1250 if (!(flags & NETFLAG_CTL) && qlength == length)
1252 sequence = BuffBigLong(data + 4);
1253 conn->packetsReceived++;
1256 if (flags & NETFLAG_UNRELIABLE)
1258 if (sequence >= conn->nq.unreliableReceiveSequence)
1260 if (sequence > conn->nq.unreliableReceiveSequence)
1262 count = sequence - conn->nq.unreliableReceiveSequence;
1263 conn->droppedDatagrams += count;
1264 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1267 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1268 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1269 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1270 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1271 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1274 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1275 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1276 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1277 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1278 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1279 conn->nq.unreliableReceiveSequence = sequence + 1;
1280 conn->lastMessageTime = realtime;
1281 conn->timeout = realtime + newtimeout;
1282 conn->unreliableMessagesReceived++;
1285 if (conn == cls.netcon)
1287 SZ_Clear(&cl_message);
1288 SZ_Write(&cl_message, data, length);
1289 MSG_BeginReading(&cl_message);
1293 SZ_Clear(&sv_message);
1294 SZ_Write(&sv_message, data, length);
1295 MSG_BeginReading(&sv_message);
1301 // Con_DPrint("Got a stale datagram\n");
1304 else if (flags & NETFLAG_ACK)
1306 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1307 if (sequence == (conn->nq.sendSequence - 1))
1309 if (sequence == conn->nq.ackSequence)
1311 conn->nq.ackSequence++;
1312 if (conn->nq.ackSequence != conn->nq.sendSequence)
1313 Con_DPrint("ack sequencing error\n");
1314 conn->lastMessageTime = realtime;
1315 conn->timeout = realtime + newtimeout;
1316 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1318 unsigned int packetLen;
1319 unsigned int dataLen;
1322 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1323 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1325 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1327 dataLen = conn->sendMessageLength;
1332 dataLen = MAX_PACKETFRAGMENT;
1336 packetLen = NET_HEADERSIZE + dataLen;
1338 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
1339 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1340 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1342 conn->nq.sendSequence++;
1344 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1345 if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
1347 conn->lastSendTime = realtime;
1348 conn->packetsSent++;
1352 conn->sendMessageLength = 0;
1355 // Con_DPrint("Duplicate ACK received\n");
1358 // Con_DPrint("Stale ACK received\n");
1361 else if (flags & NETFLAG_DATA)
1363 unsigned char temppacket[8];
1364 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1365 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1366 StoreBigLong(temppacket, 8 | NETFLAG_ACK);
1367 StoreBigLong(temppacket + 4, sequence);
1368 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1370 NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
1371 if (sequence == conn->nq.receiveSequence)
1373 conn->lastMessageTime = realtime;
1374 conn->timeout = realtime + newtimeout;
1375 conn->nq.receiveSequence++;
1376 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1377 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1378 conn->receiveMessageLength += length;
1380 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1381 "Dropping the message!\n", sequence );
1382 conn->receiveMessageLength = 0;
1385 if (flags & NETFLAG_EOM)
1387 conn->reliableMessagesReceived++;
1388 length = conn->receiveMessageLength;
1389 conn->receiveMessageLength = 0;
1392 if (conn == cls.netcon)
1394 SZ_Clear(&cl_message);
1395 SZ_Write(&cl_message, conn->receiveMessage, length);
1396 MSG_BeginReading(&cl_message);
1400 SZ_Clear(&sv_message);
1401 SZ_Write(&sv_message, conn->receiveMessage, length);
1402 MSG_BeginReading(&sv_message);
1409 conn->receivedDuplicateCount++;
1417 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1420 cls.connect_trying = false;
1421 M_Update_Return_Reason("");
1422 // the connection request succeeded, stop current connection and set up a new connection
1424 // if we're connecting to a remote server, shut down any local server
1425 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1427 SV_LockThreadMutex();
1428 Host_ShutdownServer ();
1429 SV_UnlockThreadMutex();
1431 // allocate a net connection to keep track of things
1432 cls.netcon = NetConn_Open(mysocket, peeraddress);
1433 crypto = &cls.crypto;
1434 if(crypto && crypto->authenticated)
1436 Crypto_ServerFinishInstance(&cls.netcon->crypto, crypto);
1437 Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
1438 crypto->use_aes ? "Encrypted" : "Authenticated",
1439 cls.netcon->address,
1440 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1441 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1442 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1443 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1446 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1447 key_dest = key_game;
1449 cls.demonum = -1; // not in the demo loop now
1450 cls.state = ca_connected;
1451 cls.signon = 0; // need all the signon messages before playing
1452 cls.protocol = initialprotocol;
1453 // reset move sequence numbering on this new connection
1454 cls.servermovesequence = 0;
1455 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1456 Cmd_ForwardStringToServer("new");
1457 if (cls.protocol == PROTOCOL_QUAKE)
1459 // write a keepalive (clc_nop) as it seems to greatly improve the
1460 // chances of connecting to a netquake server
1462 unsigned char buf[4];
1463 memset(&msg, 0, sizeof(msg));
1465 msg.maxsize = sizeof(buf);
1466 MSG_WriteChar(&msg, clc_nop);
1467 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
1471 int NetConn_IsLocalGame(void)
1473 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1478 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1482 serverlist_entry_t *entry = NULL;
1484 // search the cache for this server and update it
1485 for (n = 0;n < serverlist_cachecount;n++) {
1486 entry = &serverlist_cache[ n ];
1487 if (!strcmp(addressstring, entry->info.cname))
1491 if (n == serverlist_cachecount)
1493 // LAN search doesnt require an answer from the master server so we wont
1494 // know the ping nor will it be initialized already...
1497 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1500 if (serverlist_maxcachecount <= serverlist_cachecount)
1502 serverlist_maxcachecount += 64;
1503 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1505 entry = &serverlist_cache[n];
1507 memset(entry, 0, sizeof(*entry));
1508 // store the data the engine cares about (address and ping)
1509 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1510 entry->info.ping = 100000;
1511 entry->querytime = realtime;
1512 // if not in the slist menu we should print the server to console
1513 if (serverlist_consoleoutput)
1514 Con_Printf("querying %s\n", addressstring);
1515 ++serverlist_cachecount;
1517 // if this is the first reply from this server, count it as having replied
1518 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1519 pingtime = bound(0, pingtime, 9999);
1520 if (entry->query == SQS_REFRESHING) {
1521 entry->info.ping = pingtime;
1522 entry->query = SQS_QUERIED;
1524 // convert to unsigned to catch the -1
1525 // 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]
1526 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1530 // other server info is updated by the caller
1534 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1536 serverlist_entry_t *entry = &serverlist_cache[n];
1537 serverlist_info_t *info = &entry->info;
1538 // update description strings for engine menu and console output
1539 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);
1540 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1542 info->gameversion != gameversion.integer
1545 gameversion_min.integer >= 0 // min/max range set by user/mod?
1546 && gameversion_max.integer >= 0
1547 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1548 && gameversion_max.integer >= info->gameversion
1551 info->mod, info->map);
1552 if (entry->query == SQS_QUERIED)
1554 if(!serverlist_paused)
1555 ServerList_ViewList_Remove(entry);
1557 // if not in the slist menu we should print the server to console (if wanted)
1558 else if( serverlist_consoleoutput )
1559 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1560 // and finally, update the view set
1561 if(!serverlist_paused)
1562 ServerList_ViewList_Insert( entry );
1563 // update the entry's state
1564 serverlist_cache[n].query = SQS_QUERIED;
1567 // returns true, if it's sensible to continue the processing
1568 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1570 serverlist_entry_t *entry;
1572 // ignore the rest of the message if the serverlist is full
1573 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1575 // also ignore it if we have already queried it (other master server response)
1576 for( n = 0 ; n < serverlist_cachecount ; n++ )
1577 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1580 if( n < serverlist_cachecount ) {
1581 // the entry has already been queried once or
1585 if (serverlist_maxcachecount <= n)
1587 serverlist_maxcachecount += 64;
1588 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1591 entry = &serverlist_cache[n];
1593 memset(entry, 0, sizeof(*entry));
1594 entry->protocol = protocol;
1595 // store the data the engine cares about (address and ping)
1596 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1598 entry->info.isfavorite = isfavorite;
1600 // no, then reset the ping right away
1601 entry->info.ping = -1;
1602 // we also want to increase the serverlist_cachecount then
1603 serverlist_cachecount++;
1606 entry->query = SQS_QUERYING;
1611 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1614 if (serverlist_consoleoutput)
1615 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1618 char ipstring [128];
1621 if (data[0] == '\\')
1623 unsigned short port = data[5] * 256 + data[6];
1625 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1626 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1628 // move on to next address in packet
1633 else if (data[0] == '/' && isextended && length >= 19)
1635 unsigned short port = data[17] * 256 + data[18];
1643 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1645 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1648 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1649 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1650 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1656 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1657 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1658 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1663 // move on to next address in packet
1669 Con_Print("Error while parsing the server list\n");
1673 if (serverlist_consoleoutput && developer_networking.integer)
1674 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1676 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1682 // begin or resume serverlist queries
1683 serverlist_querysleep = false;
1684 serverlist_querywaittime = realtime + 3;
1687 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1689 qboolean fromserver;
1692 char *string, addressstring2[128], ipstring[32];
1693 char stringbuf[16384];
1694 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1696 char infostringvalue[MAX_INPUTLINE];
1699 // quakeworld ingame packet
1700 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1702 // convert the address to a string incase we need it
1703 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1705 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1707 // received a command string - strip off the packaging and put it
1708 // into our string buffer with NULL termination
1711 length = min(length, (int)sizeof(stringbuf) - 1);
1712 memcpy(stringbuf, data, length);
1713 stringbuf[length] = 0;
1716 if (developer_networking.integer)
1718 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1719 Com_HexDumpToConsole(data, length);
1722 sendlength = sizeof(senddata) - 4;
1723 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1725 case CRYPTO_NOMATCH:
1731 memcpy(senddata, "\377\377\377\377", 4);
1732 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1735 case CRYPTO_DISCARD:
1738 memcpy(senddata, "\377\377\377\377", 4);
1739 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
1743 case CRYPTO_REPLACE:
1744 string = senddata+4;
1745 length = sendlength;
1749 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1752 for (j = 0;j < MAX_RCONS;j++)
1754 // note: this value from i is used outside the loop too...
1755 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1756 if(cls.rcon_commands[i][0])
1757 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1766 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1767 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1769 e = strchr(rcon_password.string, ' ');
1770 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1772 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
1776 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1777 NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
1778 cls.rcon_commands[i][0] = 0;
1781 for (k = 0;k < MAX_RCONS;k++)
1782 if(cls.rcon_commands[k][0])
1783 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1788 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1789 // extend the timeout on other requests as we asked for a challenge
1790 for (l = 0;l < MAX_RCONS;l++)
1791 if(cls.rcon_commands[l][0])
1792 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1793 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1796 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1800 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1802 // darkplaces or quake3
1803 char protocolnames[1400];
1804 Protocol_Names(protocolnames, sizeof(protocolnames));
1805 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1806 M_Update_Return_Reason("Got challenge response");
1807 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1808 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1809 // TODO: add userinfo stuff here instead of using NQ commands?
1810 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);
1813 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1815 // darkplaces or quake3
1816 M_Update_Return_Reason("Accepted");
1817 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1820 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1822 char rejectreason[128];
1823 cls.connect_trying = false;
1825 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1826 memcpy(rejectreason, string, length);
1827 rejectreason[length] = 0;
1828 M_Update_Return_Reason(rejectreason);
1831 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1833 serverlist_info_t *info;
1838 // search the cache for this server and update it
1839 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1843 info = &serverlist_cache[n].info;
1848 info->qcstatus[0] = 0;
1849 info->players[0] = 0;
1850 info->protocol = -1;
1851 info->numplayers = 0;
1853 info->maxplayers = 0;
1854 info->gameversion = 0;
1856 p = strchr(string, '\n');
1859 *p = 0; // cut off the string there
1863 Con_Printf("statusResponse without players block?\n");
1865 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1866 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1867 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1868 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1869 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1870 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1871 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1872 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1873 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1874 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1875 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
1876 info->numhumans = info->numplayers - max(0, info->numbots);
1877 info->freeslots = info->maxplayers - info->numplayers;
1879 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1883 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1885 serverlist_info_t *info;
1889 // search the cache for this server and update it
1890 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1894 info = &serverlist_cache[n].info;
1899 info->qcstatus[0] = 0;
1900 info->players[0] = 0;
1901 info->protocol = -1;
1902 info->numplayers = 0;
1904 info->maxplayers = 0;
1905 info->gameversion = 0;
1907 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1908 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1909 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1910 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1911 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1912 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1913 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1914 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1915 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1916 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1917 info->numhumans = info->numplayers - max(0, info->numbots);
1918 info->freeslots = info->maxplayers - info->numplayers;
1920 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1924 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1926 // Extract the IP addresses
1929 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
1932 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1934 // Extract the IP addresses
1937 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
1940 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1942 // Extract the IP addresses
1946 if (serverlist_consoleoutput)
1947 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
1948 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
1950 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
1951 if (serverlist_consoleoutput && developer_networking.integer)
1952 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
1954 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
1958 // move on to next address in packet
1962 // begin or resume serverlist queries
1963 serverlist_querysleep = false;
1964 serverlist_querywaittime = realtime + 3;
1967 if (!strncmp(string, "extResponse ", 12))
1969 ++cl_net_extresponse_count;
1970 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
1971 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
1972 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
1973 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
1976 if (!strncmp(string, "ping", 4))
1978 if (developer_extra.integer)
1979 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
1980 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
1983 if (!strncmp(string, "ack", 3))
1985 // QuakeWorld compatibility
1986 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
1988 // challenge message
1989 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
1990 M_Update_Return_Reason("Got QuakeWorld challenge response");
1991 cls.qw_qport = qport.integer;
1992 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1993 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1994 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);
1997 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2000 M_Update_Return_Reason("QuakeWorld Accepted");
2001 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2004 if (length > 2 && !memcmp(string, "n\\", 2))
2006 serverlist_info_t *info;
2010 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2011 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2014 // search the cache for this server and update it
2015 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2019 info = &serverlist_cache[n].info;
2020 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2021 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2022 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2023 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2025 info->numplayers = 0; // updated below
2026 info->numhumans = 0; // updated below
2027 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2028 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2030 // count active players on server
2031 // (we could gather more info, but we're just after the number)
2032 s = strchr(string, '\n');
2036 while (s < string + length)
2038 for (;s < string + length && *s != '\n';s++)
2040 if (s >= string + length)
2048 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2052 if (string[0] == 'n')
2055 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2057 // we may not have liked the packet, but it was a command packet, so
2058 // we're done processing this packet now
2061 // quakeworld ingame packet
2062 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2065 CL_ParseServerMessage();
2068 // netquake control packets, supported for compatibility only
2069 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2072 serverlist_info_t *info;
2076 SZ_Clear(&cl_message);
2077 SZ_Write(&cl_message, data, length);
2078 MSG_BeginReading(&cl_message);
2079 c = MSG_ReadByte(&cl_message);
2083 if (developer_extra.integer)
2084 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2085 if (cls.connect_trying)
2087 lhnetaddress_t clientportaddress;
2088 clientportaddress = *peeraddress;
2089 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2090 // extra ProQuake stuff
2092 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2094 cls.proquake_servermod = 0;
2096 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2098 cls.proquake_serverversion = 0;
2100 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2102 cls.proquake_serverflags = 0;
2103 if (cls.proquake_servermod == 1)
2104 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2105 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2106 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2107 M_Update_Return_Reason("Accepted");
2108 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2112 if (developer_extra.integer)
2113 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
2114 cls.connect_trying = false;
2115 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2117 case CCREP_SERVER_INFO:
2118 if (developer_extra.integer)
2119 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2120 // LordHavoc: because the quake server may report weird addresses
2121 // we just ignore it and keep the real address
2122 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2123 // search the cache for this server and update it
2124 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2128 info = &serverlist_cache[n].info;
2129 strlcpy(info->game, "Quake", sizeof(info->game));
2130 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2131 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2132 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2133 info->numplayers = MSG_ReadByte(&cl_message);
2134 info->maxplayers = MSG_ReadByte(&cl_message);
2135 info->protocol = MSG_ReadByte(&cl_message);
2137 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2140 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2141 if (developer_extra.integer)
2142 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2144 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2146 case CCREP_PLAYER_INFO:
2147 // we got a CCREP_PLAYER_INFO??
2148 //if (developer_extra.integer)
2149 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2151 case CCREP_RULE_INFO:
2152 // we got a CCREP_RULE_INFO??
2153 //if (developer_extra.integer)
2154 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2159 SZ_Clear(&cl_message);
2160 // we may not have liked the packet, but it was a valid control
2161 // packet, so we're done processing this packet now
2165 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2166 CL_ParseServerMessage();
2170 void NetConn_QueryQueueFrame(void)
2176 static double querycounter = 0;
2178 if(!net_slist_pause.integer && serverlist_paused)
2179 ServerList_RebuildViewList();
2180 serverlist_paused = net_slist_pause.integer != 0;
2182 if (serverlist_querysleep)
2185 // apply a cool down time after master server replies,
2186 // to avoid messing up the ping times on the servers
2187 if (serverlist_querywaittime > realtime)
2190 // each time querycounter reaches 1.0 issue a query
2191 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2192 maxqueries = (int)querycounter;
2193 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2194 querycounter -= maxqueries;
2196 if( maxqueries == 0 ) {
2200 // scan serverlist and issue queries as needed
2201 serverlist_querysleep = true;
2203 timeouttime = realtime - net_slist_timeout.value;
2204 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2206 serverlist_entry_t *entry = &serverlist_cache[ index ];
2207 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2212 serverlist_querysleep = false;
2213 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2218 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2220 lhnetaddress_t address;
2223 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2224 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2226 for (socket = 0; socket < cl_numsockets ; socket++)
2227 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2231 for (socket = 0; socket < cl_numsockets ; socket++)
2232 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2235 // update the entry fields
2236 entry->querytime = realtime;
2237 entry->querycounter++;
2239 // if not in the slist menu we should print the server to console
2240 if (serverlist_consoleoutput)
2241 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2247 // have we tried to refresh this server?
2248 if( entry->query == SQS_REFRESHING ) {
2249 // yes, so update the reply count (since its not responding anymore)
2251 if(!serverlist_paused)
2252 ServerList_ViewList_Remove(entry);
2254 entry->query = SQS_TIMEDOUT;
2259 void NetConn_ClientFrame(void)
2262 lhnetaddress_t peeraddress;
2263 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2264 NetConn_UpdateSockets();
2265 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2267 if (cls.connect_remainingtries == 0)
2268 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2269 cls.connect_nextsendtime = realtime + 1;
2270 cls.connect_remainingtries--;
2271 if (cls.connect_remainingtries <= -10)
2273 cls.connect_trying = false;
2274 M_Update_Return_Reason("Connect: Failed");
2277 // try challenge first (newer DP server or QW)
2278 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2279 // then try netquake as a fallback (old server, or netquake)
2280 SZ_Clear(&cl_message);
2281 // save space for the header, filled in later
2282 MSG_WriteLong(&cl_message, 0);
2283 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2284 MSG_WriteString(&cl_message, "QUAKE");
2285 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2286 // extended proquake stuff
2287 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2288 // this version matches ProQuake 3.40, the first version to support
2289 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2290 // higher clients, so we pretend we are that version...
2291 MSG_WriteByte(&cl_message, 34); // version * 10
2292 MSG_WriteByte(&cl_message, 0); // flags
2293 MSG_WriteLong(&cl_message, 0); // password
2294 // write the packetsize now...
2295 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2296 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2297 SZ_Clear(&cl_message);
2299 for (i = 0;i < cl_numsockets;i++)
2301 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2303 // R_TimeReport("clientreadnetwork");
2304 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2305 // R_TimeReport("clientparsepacket");
2308 NetConn_QueryQueueFrame();
2309 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2311 Con_Print("Connection timed out\n");
2313 SV_LockThreadMutex();
2314 Host_ShutdownServer ();
2315 SV_UnlockThreadMutex();
2319 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2323 for (i = 0;i < bufferlength - 1;i++)
2327 c = rand () % (127 - 33) + 33;
2328 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2334 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2335 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2337 prvm_prog_t *prog = SVVM_prog;
2339 unsigned int nb_clients = 0, nb_bots = 0, i;
2342 const char *crypto_idstring;
2345 // How many clients are there?
2346 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2348 if (svs.clients[i].active)
2351 if (!svs.clients[i].netconnection)
2357 str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2363 for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2364 if(*q != '\\' && *q != '\n')
2369 /// \TODO: we should add more information for the full status string
2370 crypto_idstring = Crypto_GetInfoResponseDataString();
2371 length = dpsnprintf(out_msg, out_size,
2372 "\377\377\377\377%s\x0A"
2373 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2374 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2379 fullstatus ? "statusResponse" : "infoResponse",
2380 gamename, com_modname, gameversion.integer, svs.maxclients,
2381 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2382 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2383 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2384 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2385 fullstatus ? "\n" : "");
2387 // Make sure it fits in the buffer
2397 savelength = length;
2399 ptr = out_msg + length;
2400 left = (int)out_size - length;
2402 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2404 client_t *cl = &svs.clients[i];
2407 int nameind, cleanind, pingvalue;
2409 char cleanname [sizeof(cl->name)];
2413 // Remove all characters '"' and '\' in the player name
2418 curchar = cl->name[nameind++];
2419 if (curchar != '"' && curchar != '\\')
2421 cleanname[cleanind++] = curchar;
2422 if (cleanind == sizeof(cleanname) - 1)
2425 } while (curchar != '\0');
2426 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2428 pingvalue = (int)(cl->ping * 1000.0f);
2429 if(cl->netconnection)
2430 pingvalue = bound(1, pingvalue, 9999);
2435 ed = PRVM_EDICT_NUM(i + 1);
2436 str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2442 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2443 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2448 if ((gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) && (teamplay.integer > 0))
2450 if(cl->frags == -666) // spectator
2451 strlcpy(teambuf, " 0", sizeof(teambuf));
2452 else if(cl->colors == 0x44) // red team
2453 strlcpy(teambuf, " 1", sizeof(teambuf));
2454 else if(cl->colors == 0xDD) // blue team
2455 strlcpy(teambuf, " 2", sizeof(teambuf));
2456 else if(cl->colors == 0xCC) // yellow team
2457 strlcpy(teambuf, " 3", sizeof(teambuf));
2458 else if(cl->colors == 0x99) // pink team
2459 strlcpy(teambuf, " 4", sizeof(teambuf));
2461 strlcpy(teambuf, " 0", sizeof(teambuf));
2466 // note: team number is inserted according to SoF2 protocol
2468 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2474 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2483 // turn it into an infoResponse!
2484 out_msg[savelength] = 0;
2485 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2486 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2501 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2503 size_t floodslotnum, bestfloodslotnum;
2504 double bestfloodtime;
2505 lhnetaddress_t noportpeeraddress;
2506 // see if this is a connect flood
2507 noportpeeraddress = *peeraddress;
2508 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2509 bestfloodslotnum = 0;
2510 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2511 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2513 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2515 bestfloodtime = floodlist[floodslotnum].lasttime;
2516 bestfloodslotnum = floodslotnum;
2518 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2520 // this address matches an ongoing flood address
2521 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2525 // renew the ban on this address so it does not expire
2526 // until the flood has subsided
2527 floodlist[floodslotnum].lasttime = realtime;
2529 //Con_Printf("Flood detected!\n");
2532 // the flood appears to have subsided, so allow this
2533 bestfloodslotnum = floodslotnum; // reuse the same slot
2537 // begin a new timeout on this address
2538 floodlist[bestfloodslotnum].address = noportpeeraddress;
2539 floodlist[bestfloodslotnum].lasttime = realtime;
2540 //Con_Printf("Flood detection initiated!\n");
2544 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2546 size_t floodslotnum;
2547 lhnetaddress_t noportpeeraddress;
2548 // see if this is a connect flood
2549 noportpeeraddress = *peeraddress;
2550 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2551 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2553 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2555 // this address matches an ongoing flood address
2557 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2558 floodlist[floodslotnum].lasttime = 0;
2559 //Con_Printf("Flood cleared!\n");
2564 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2566 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2571 t1 = (long) time(NULL);
2572 t2 = strtol(s, NULL, 0);
2573 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2576 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2579 return !memcmp(mdfourbuf, hash, 16);
2582 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2587 if(slen < (int)(sizeof(challenge[0].string)) - 1)
2590 // validate the challenge
2591 for (i = 0;i < MAX_CHALLENGES;i++)
2592 if(challenge[i].time > 0)
2593 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2595 // if the challenge is not recognized, drop the packet
2596 if (i == MAX_CHALLENGES)
2599 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2602 if(memcmp(mdfourbuf, hash, 16))
2605 // unmark challenge to prevent replay attacks
2606 challenge[i].time = 0;
2611 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2613 return !strcmp(password, hash);
2616 /// returns a string describing the user level, or NULL for auth failure
2617 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)
2619 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2620 static char buf[MAX_INPUTLINE];
2622 qboolean restricted = false;
2623 qboolean have_usernames = false;
2626 userpass_start = rcon_password.string;
2627 while((userpass_end = strchr(userpass_start, ' ')))
2629 have_usernames = true;
2630 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2632 if(comparator(peeraddress, buf, password, cs, cslen))
2634 userpass_start = userpass_end + 1;
2636 if(userpass_start[0])
2638 userpass_end = userpass_start + strlen(userpass_start);
2639 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2644 have_usernames = false;
2645 userpass_start = rcon_restricted_password.string;
2646 while((userpass_end = strchr(userpass_start, ' ')))
2648 have_usernames = true;
2649 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2651 if(comparator(peeraddress, buf, password, cs, cslen))
2653 userpass_start = userpass_end + 1;
2655 if(userpass_start[0])
2657 userpass_end = userpass_start + strlen(userpass_start);
2658 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2662 return NULL; // DENIED
2665 for(text = s; text != endpos; ++text)
2666 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2667 return NULL; // block possible exploits against the parser/alias expansion
2671 size_t l = strlen(s);
2674 hasquotes = (strchr(s, '"') != NULL);
2675 // sorry, we can't allow these substrings in wildcard expressions,
2676 // as they can mess with the argument counts
2677 text = rcon_restricted_commands.string;
2678 while(COM_ParseToken_Console(&text))
2680 // com_token now contains a pattern to check for...
2681 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2684 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2687 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2689 if(!strcmp(com_token, s))
2692 else // single-arg expression? must match the beginning of the command
2694 if(!strcmp(com_token, s))
2696 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2700 // if we got here, nothing matched!
2708 userpass_startpass = strchr(userpass_start, ':');
2709 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2710 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2712 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2715 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2719 // looks like a legitimate rcon command with the correct password
2720 const char *s_ptr = s;
2721 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2722 while(s_ptr != endpos)
2724 size_t l = strlen(s_ptr);
2726 Con_Printf(" %s;", s_ptr);
2731 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2732 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2735 size_t l = strlen(s);
2738 client_t *host_client_save = host_client;
2739 Cmd_ExecuteString(s, src_command, true);
2740 host_client = host_client_save;
2741 // in case it is a command that changes host_client (like restart)
2745 Con_Rcon_Redirect_End();
2749 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2753 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2755 int i, ret, clientnum, best;
2758 char *s, *string, response[1400], addressstring2[128];
2759 static char stringbuf[16384]; // server only
2760 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2761 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2762 size_t sendlength, response_len;
2763 char infostringvalue[MAX_INPUTLINE];
2769 // convert the address to a string incase we need it
2770 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2772 // see if we can identify the sender as a local player
2773 // (this is necessary for rcon to send a reliable reply if the client is
2774 // actually on the server, not sending remotely)
2775 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2776 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2778 if (i == svs.maxclients)
2781 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2783 // received a command string - strip off the packaging and put it
2784 // into our string buffer with NULL termination
2787 length = min(length, (int)sizeof(stringbuf) - 1);
2788 memcpy(stringbuf, data, length);
2789 stringbuf[length] = 0;
2792 if (developer_extra.integer)
2794 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2795 Com_HexDumpToConsole(data, length);
2798 sendlength = sizeof(senddata) - 4;
2799 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2801 case CRYPTO_NOMATCH:
2807 memcpy(senddata, "\377\377\377\377", 4);
2808 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2811 case CRYPTO_DISCARD:
2814 memcpy(senddata, "\377\377\377\377", 4);
2815 NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
2819 case CRYPTO_REPLACE:
2820 string = senddata+4;
2821 length = sendlength;
2825 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2827 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2829 if(challenge[i].time > 0)
2830 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2832 if (besttime > challenge[i].time)
2833 besttime = challenge[best = i].time;
2835 // if we did not find an exact match, choose the oldest and
2836 // update address and string
2837 if (i == MAX_CHALLENGES)
2840 challenge[i].address = *peeraddress;
2841 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2845 // flood control: drop if requesting challenge too often
2846 if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
2849 challenge[i].time = realtime;
2850 // send the challenge
2851 dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
2852 response_len = strlen(response) + 1;
2853 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
2854 NetConn_Write(mysocket, response, response_len, peeraddress);
2857 if (length > 8 && !memcmp(string, "connect\\", 8))
2859 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
2863 if(crypto && crypto->authenticated)
2865 // no need to check challenge
2866 if(crypto_developer.integer)
2868 Con_Printf("%s connection to %s is being established: client is %s@%.*s, I am %.*s@%.*s\n",
2869 crypto->use_aes ? "Encrypted" : "Authenticated",
2871 crypto->client_idfp[0] ? crypto->client_idfp : "-",
2872 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
2873 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
2874 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
2880 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
2882 // validate the challenge
2883 for (i = 0;i < MAX_CHALLENGES;i++)
2884 if(challenge[i].time > 0)
2885 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
2887 // if the challenge is not recognized, drop the packet
2888 if (i == MAX_CHALLENGES)
2893 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
2894 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
2896 if(!(islocal || sv_public.integer > -2))
2898 if (developer_extra.integer)
2899 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
2900 NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
2904 // check engine protocol
2905 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
2907 if (developer_extra.integer)
2908 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
2909 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
2913 // see if this is a duplicate connection request or a disconnected
2914 // client who is rejoining to the same client slot
2915 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2917 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
2919 // this is a known client...
2920 if(crypto && crypto->authenticated)
2922 // reject if changing key!
2923 if(client->netconnection->crypto.authenticated)
2926 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
2928 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
2930 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
2932 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
2935 if (developer_extra.integer)
2936 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
2937 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
2944 // reject if downgrading!
2945 if(client->netconnection->crypto.authenticated)
2947 if (developer_extra.integer)
2948 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
2949 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
2955 // client crashed and is coming back,
2956 // keep their stuff intact
2957 if (developer_extra.integer)
2958 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
2959 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2960 if(crypto && crypto->authenticated)
2961 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
2962 SV_SendServerinfo(client);
2966 // client is still trying to connect,
2967 // so we send a duplicate reply
2968 if (developer_extra.integer)
2969 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
2970 if(crypto && crypto->authenticated)
2971 Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
2972 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2978 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
2981 // find an empty client slot for this new client
2982 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2985 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
2987 // allocated connection
2988 if (developer_extra.integer)
2989 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
2990 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2991 // now set up the client
2992 if(crypto && crypto->authenticated)
2993 Crypto_ServerFinishInstance(&conn->crypto, crypto);
2994 SV_ConnectClient(clientnum, conn);
2995 NetConn_Heartbeat(1);
3000 // no empty slots found - server is full
3001 if (developer_extra.integer)
3002 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3003 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3007 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3009 const char *challenge = NULL;
3011 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3014 // If there was a challenge in the getinfo message
3015 if (length > 8 && string[7] == ' ')
3016 challenge = string + 8;
3018 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3020 if (developer_extra.integer)
3021 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3022 NetConn_WriteString(mysocket, response, peeraddress);
3026 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3028 const char *challenge = NULL;
3030 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3033 // If there was a challenge in the getinfo message
3034 if (length > 10 && string[9] == ' ')
3035 challenge = string + 10;
3037 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3039 if (developer_extra.integer)
3040 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3041 NetConn_WriteString(mysocket, response, peeraddress);
3045 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3047 char *password = string + 20;
3048 char *timeval = string + 37;
3049 char *s = strchr(timeval, ' ');
3050 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3051 const char *userlevel;
3053 if(rcon_secure.integer > 1)
3057 return true; // invalid packet
3060 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3061 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3064 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3066 char *password = string + 25;
3067 char *challenge = string + 42;
3068 char *s = strchr(challenge, ' ');
3069 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3070 const char *userlevel;
3072 return true; // invalid packet
3075 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3076 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3079 if (length >= 5 && !memcmp(string, "rcon ", 5))
3082 char *s = string + 5;
3083 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3086 if(rcon_secure.integer > 0)
3089 for (i = 0;!ISWHITESPACE(*s);s++)
3090 if (i < (int)sizeof(password) - 1)
3092 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3095 if (!ISWHITESPACE(password[0]))
3097 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3098 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3102 if (!strncmp(string, "extResponse ", 12))
3104 ++sv_net_extresponse_count;
3105 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3106 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3107 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3108 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3111 if (!strncmp(string, "ping", 4))
3113 if (developer_extra.integer)
3114 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3115 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3118 if (!strncmp(string, "ack", 3))
3120 // we may not have liked the packet, but it was a command packet, so
3121 // we're done processing this packet now
3124 // netquake control packets, supported for compatibility only, and only
3125 // when running game protocols that are normally served via this connection
3127 // (this protects more modern protocols against being used for
3128 // Quake packet flood Denial Of Service attacks)
3129 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)
3133 const char *protocolname;
3136 SZ_Clear(&sv_message);
3137 SZ_Write(&sv_message, data, length);
3138 MSG_BeginReading(&sv_message);
3139 c = MSG_ReadByte(&sv_message);
3143 if (developer_extra.integer)
3144 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3145 if(!(islocal || sv_public.integer > -2))
3147 if (developer_extra.integer)
3148 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3149 SZ_Clear(&sv_message);
3150 // save space for the header, filled in later
3151 MSG_WriteLong(&sv_message, 0);
3152 MSG_WriteByte(&sv_message, CCREP_REJECT);
3153 MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
3154 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3155 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3156 SZ_Clear(&sv_message);
3160 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3161 protocolnumber = MSG_ReadByte(&sv_message);
3162 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3164 if (developer_extra.integer)
3165 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3166 SZ_Clear(&sv_message);
3167 // save space for the header, filled in later
3168 MSG_WriteLong(&sv_message, 0);
3169 MSG_WriteByte(&sv_message, CCREP_REJECT);
3170 MSG_WriteString(&sv_message, "Incompatible version.\n");
3171 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3172 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3173 SZ_Clear(&sv_message);
3177 // see if this connect request comes from a known client
3178 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3180 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3182 // this is either a duplicate connection request
3183 // or coming back from a timeout
3184 // (if so, keep their stuff intact)
3186 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3187 if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
3189 if (developer_extra.integer)
3190 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3191 SZ_Clear(&sv_message);
3192 // save space for the header, filled in later
3193 MSG_WriteLong(&sv_message, 0);
3194 MSG_WriteByte(&sv_message, CCREP_REJECT);
3195 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3196 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3197 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3198 SZ_Clear(&sv_message);
3203 if (developer_extra.integer)
3204 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3205 SZ_Clear(&sv_message);
3206 // save space for the header, filled in later
3207 MSG_WriteLong(&sv_message, 0);
3208 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3209 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
3210 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3211 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3212 SZ_Clear(&sv_message);
3214 // if client is already spawned, re-send the
3215 // serverinfo message as they'll need it to play
3217 SV_SendServerinfo(client);
3222 // this is a new client, check for connection flood
3223 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3226 // find a slot for the new client
3227 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3230 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3232 // connect to the client
3233 // everything is allocated, just fill in the details
3234 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3235 if (developer_extra.integer)
3236 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3237 // send back the info about the server connection
3238 SZ_Clear(&sv_message);
3239 // save space for the header, filled in later
3240 MSG_WriteLong(&sv_message, 0);
3241 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3242 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3243 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3244 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3245 SZ_Clear(&sv_message);
3246 // now set up the client struct
3247 SV_ConnectClient(clientnum, conn);
3248 NetConn_Heartbeat(1);
3253 if (developer_extra.integer)
3254 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3255 // no room; try to let player know
3256 SZ_Clear(&sv_message);
3257 // save space for the header, filled in later
3258 MSG_WriteLong(&sv_message, 0);
3259 MSG_WriteByte(&sv_message, CCREP_REJECT);
3260 MSG_WriteString(&sv_message, "Server is full.\n");
3261 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3262 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3263 SZ_Clear(&sv_message);
3265 case CCREQ_SERVER_INFO:
3266 if (developer_extra.integer)
3267 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3268 if(!(islocal || sv_public.integer > -1))
3271 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3274 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3277 char myaddressstring[128];
3278 if (developer_extra.integer)
3279 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3280 SZ_Clear(&sv_message);
3281 // save space for the header, filled in later
3282 MSG_WriteLong(&sv_message, 0);
3283 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3284 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3285 MSG_WriteString(&sv_message, myaddressstring);
3286 MSG_WriteString(&sv_message, hostname.string);
3287 MSG_WriteString(&sv_message, sv.name);
3288 // How many clients are there?
3289 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3290 if (svs.clients[i].active)
3292 MSG_WriteByte(&sv_message, numclients);
3293 MSG_WriteByte(&sv_message, svs.maxclients);
3294 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3295 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3296 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3297 SZ_Clear(&sv_message);
3300 case CCREQ_PLAYER_INFO:
3301 if (developer_extra.integer)
3302 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3303 if(!(islocal || sv_public.integer > -1))
3306 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3311 int playerNumber, activeNumber, clientNumber;
3314 playerNumber = MSG_ReadByte(&sv_message);
3316 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3317 if (client->active && ++activeNumber == playerNumber)
3319 if (clientNumber != svs.maxclients)
3321 SZ_Clear(&sv_message);
3322 // save space for the header, filled in later
3323 MSG_WriteLong(&sv_message, 0);
3324 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3325 MSG_WriteByte(&sv_message, playerNumber);
3326 MSG_WriteString(&sv_message, client->name);
3327 MSG_WriteLong(&sv_message, client->colors);
3328 MSG_WriteLong(&sv_message, client->frags);
3329 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3330 if(sv_status_privacy.integer)
3331 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3333 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3334 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3335 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3336 SZ_Clear(&sv_message);
3340 case CCREQ_RULE_INFO:
3341 if (developer_extra.integer)
3342 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3343 if(!(islocal || sv_public.integer > -1))
3346 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3353 // find the search start location
3354 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3355 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3357 // send the response
3358 SZ_Clear(&sv_message);
3359 // save space for the header, filled in later
3360 MSG_WriteLong(&sv_message, 0);
3361 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3364 MSG_WriteString(&sv_message, var->name);
3365 MSG_WriteString(&sv_message, var->string);
3367 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3368 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3369 SZ_Clear(&sv_message);
3373 if (developer_extra.integer)
3374 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3375 if (sv.active && !rcon_secure.integer)
3377 char password[2048];
3381 const char *userlevel;
3382 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3383 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3385 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3386 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3387 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3394 SZ_Clear(&sv_message);
3395 // we may not have liked the packet, but it was a valid control
3396 // packet, so we're done processing this packet now
3401 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3403 SV_ReadClientMessage();
3410 void NetConn_ServerFrame(void)
3413 lhnetaddress_t peeraddress;
3414 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3415 for (i = 0;i < sv_numsockets;i++)
3416 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3417 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3418 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3420 // never timeout loopback connections
3421 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3423 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3424 SV_DropClient(false);
3429 void NetConn_SleepMicroseconds(int microseconds)
3431 LHNET_SleepUntilPacket_Microseconds(microseconds);
3434 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3438 lhnetaddress_t masteraddress;
3439 lhnetaddress_t broadcastaddress;
3442 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3445 // 26000 is the default quake server port, servers on other ports will not
3447 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3448 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3452 for (i = 0;i < cl_numsockets;i++)
3456 const char *cmdname, *extraoptions;
3457 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3459 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3461 // search LAN for Quake servers
3462 SZ_Clear(&cl_message);
3463 // save space for the header, filled in later
3464 MSG_WriteLong(&cl_message, 0);
3465 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3466 MSG_WriteString(&cl_message, "QUAKE");
3467 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3468 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3469 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3470 SZ_Clear(&cl_message);
3472 // search LAN for DarkPlaces servers
3473 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3476 // build the getservers message to send to the dpmaster master servers
3477 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3479 cmdname = "getserversExt";
3480 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3484 cmdname = "getservers";
3487 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamename, NET_PROTOCOL_VERSION, extraoptions);
3490 for (masternum = 0;sv_masters[masternum].name;masternum++)
3492 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3495 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3499 // search favorite servers
3500 for(j = 0; j < nFavorites; ++j)
3502 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3504 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3505 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3512 // only query QuakeWorld servers when the user wants to
3515 for (i = 0;i < cl_numsockets;i++)
3519 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3521 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3523 // search LAN for QuakeWorld servers
3524 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3526 // build the getservers message to send to the qwmaster master servers
3527 // note this has no -1 prefix, and the trailing nul byte is sent
3528 dpsnprintf(request, sizeof(request), "c\n");
3532 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3534 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3536 if (m_state != m_slist)
3538 char lookupstring[128];
3539 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3540 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3543 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3547 // search favorite servers
3548 for(j = 0; j < nFavorites; ++j)
3550 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3552 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3554 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3555 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3562 if (!masterquerycount)
3564 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3565 M_Update_Return_Reason("No network");
3569 void NetConn_Heartbeat(int priority)
3571 lhnetaddress_t masteraddress;
3573 lhnetsocket_t *mysocket;
3575 // if it's a state change (client connected), limit next heartbeat to no
3576 // more than 30 sec in the future
3577 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3578 nextheartbeattime = realtime + 30.0;
3580 // limit heartbeatperiod to 30 to 270 second range,
3581 // lower limit is to avoid abusing master servers with excess traffic,
3582 // upper limit is to avoid timing out on the master server (which uses
3584 if (sv_heartbeatperiod.value < 30)
3585 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3586 if (sv_heartbeatperiod.value > 270)
3587 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3589 // make advertising optional and don't advertise singleplayer games, and
3590 // only send a heartbeat as often as the admin wants
3591 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3593 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3594 for (masternum = 0;sv_masters[masternum].name;masternum++)
3595 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3596 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3600 static void Net_Heartbeat_f(void)
3603 NetConn_Heartbeat(2);
3605 Con_Print("No server running, can not heartbeat to master server.\n");
3608 static void PrintStats(netconn_t *conn)
3610 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3611 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3613 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3614 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3615 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3616 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3617 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3618 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3619 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3620 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3621 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3622 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3625 void Net_Stats_f(void)
3628 Con_Print("connections =\n");
3629 for (conn = netconn_list;conn;conn = conn->next)
3633 void Net_Refresh_f(void)
3635 if (m_state != m_slist) {
3636 Con_Print("Sending new requests to master servers\n");
3637 ServerList_QueryList(false, true, false, true);
3638 Con_Print("Listening for replies...\n");
3640 ServerList_QueryList(false, true, false, false);
3643 void Net_Slist_f(void)
3645 ServerList_ResetMasks();
3646 serverlist_sortbyfield = SLIF_PING;
3647 serverlist_sortflags = 0;
3648 if (m_state != m_slist) {
3649 Con_Print("Sending requests to master servers\n");
3650 ServerList_QueryList(true, true, false, true);
3651 Con_Print("Listening for replies...\n");
3653 ServerList_QueryList(true, true, false, false);
3656 void Net_SlistQW_f(void)
3658 ServerList_ResetMasks();
3659 serverlist_sortbyfield = SLIF_PING;
3660 serverlist_sortflags = 0;
3661 if (m_state != m_slist) {
3662 Con_Print("Sending requests to master servers\n");
3663 ServerList_QueryList(true, false, true, true);
3664 serverlist_consoleoutput = true;
3665 Con_Print("Listening for replies...\n");
3667 ServerList_QueryList(true, false, true, false);
3670 void NetConn_Init(void)
3673 lhnetaddress_t tempaddress;
3674 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3675 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3676 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3677 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3678 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3679 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3680 Cvar_RegisterVariable(&rcon_restricted_password);
3681 Cvar_RegisterVariable(&rcon_restricted_commands);
3682 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3683 Cvar_RegisterVariable(&net_slist_queriespersecond);
3684 Cvar_RegisterVariable(&net_slist_queriesperframe);
3685 Cvar_RegisterVariable(&net_slist_timeout);
3686 Cvar_RegisterVariable(&net_slist_maxtries);
3687 Cvar_RegisterVariable(&net_slist_favorites);
3688 Cvar_RegisterVariable(&net_slist_pause);
3689 Cvar_RegisterVariable(&net_messagetimeout);
3690 Cvar_RegisterVariable(&net_connecttimeout);
3691 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3692 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3693 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3694 Cvar_RegisterVariable(&cl_netlocalping);
3695 Cvar_RegisterVariable(&cl_netpacketloss_send);
3696 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3697 Cvar_RegisterVariable(&hostname);
3698 Cvar_RegisterVariable(&developer_networking);
3699 Cvar_RegisterVariable(&cl_netport);
3700 Cvar_RegisterVariable(&sv_netport);
3701 Cvar_RegisterVariable(&net_address);
3702 Cvar_RegisterVariable(&net_address_ipv6);
3703 Cvar_RegisterVariable(&sv_public);
3704 Cvar_RegisterVariable(&sv_public_rejectreason);
3705 Cvar_RegisterVariable(&sv_heartbeatperiod);
3706 for (i = 0;sv_masters[i].name;i++)
3707 Cvar_RegisterVariable(&sv_masters[i]);
3708 Cvar_RegisterVariable(&gameversion);
3709 Cvar_RegisterVariable(&gameversion_min);
3710 Cvar_RegisterVariable(&gameversion_max);
3711 // 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.
3712 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3714 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3716 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3717 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3720 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3722 // 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
3723 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3725 i = atoi(com_argv[i + 1]);
3726 if (i >= 0 && i < 65536)
3728 Con_Printf("-port option used, setting port cvar to %i\n", i);
3729 Cvar_SetValueQuick(&sv_netport, i);
3732 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3736 cl_message.data = cl_message_buf;
3737 cl_message.maxsize = sizeof(cl_message_buf);
3738 cl_message.cursize = 0;
3739 sv_message.data = sv_message_buf;
3740 sv_message.maxsize = sizeof(sv_message_buf);
3741 sv_message.cursize = 0;
3743 if (Thread_HasThreads())
3744 netconn_mutex = Thread_CreateMutex();
3747 void NetConn_Shutdown(void)
3749 NetConn_CloseClientPorts();
3750 NetConn_CloseServerPorts();
3753 Thread_DestroyMutex(netconn_mutex);
3754 netconn_mutex = NULL;