1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
5
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.
10
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.
14
15 See the GNU General Public License for more details.
16
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.
20
21 */
22
23 #include "quakedef.h"
24 #include "thread.h"
25 #include "lhnet.h"
26
27 // for secure rcon authentication
28 #include "hmac.h"
29 #include "mdfour.h"
30 #include <time.h>
31
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
34
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;
40
41 static cvar_t sv_masters [] =
42 {
43 {CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
44 {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
45 {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
46 {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
47 {0, "sv_masterextra1", "ghdigital.com", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
48 {0, "sv_masterextra2", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
49 {0, "sv_masterextra3", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
50 {0, NULL, NULL, NULL}
51 };
52
53 #ifdef CONFIG_MENU
54 static cvar_t sv_qwmasters [] =
55 {
56 {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
57 {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
58 {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
59 {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
60 {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
61 {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
62 {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
63 {0, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
64 {0, NULL, NULL, NULL}
65 };
66 #endif
67
68 static double nextheartbeattime = 0;
69
70 sizebuf_t cl_message;
71 sizebuf_t sv_message;
72 static unsigned char cl_message_buf[NET_MAXMESSAGE];
73 static unsigned char sv_message_buf[NET_MAXMESSAGE];
74 char cl_readstring[MAX_INPUTLINE];
75 char sv_readstring[MAX_INPUTLINE];
76
77 cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
78 cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
79 cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
80 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
81 cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
82 cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
83 cvar_t net_challengefloodblockingtimeout = {0, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
84 cvar_t net_getstatusfloodblockingtimeout = {0, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
85 cvar_t net_sourceaddresscheck = {0, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
86 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
87 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
88
89 cvar_t cl_netlocalping = {0, "cl_netlocalping","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
90 static cvar_t cl_netpacketloss_send = {0, "cl_netpacketloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
91 static cvar_t cl_netpacketloss_receive = {0, "cl_netpacketloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
92 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
93 static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
94 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
95 static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
96 static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
97 static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
98 static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
99 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
100 static cvar_t gameversion_min = {0, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
101 static cvar_t gameversion_max = {0, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
102 static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
103 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
104 static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
105 extern cvar_t rcon_secure;
106 extern cvar_t rcon_secure_challengetimeout;
107
108 double masterquerytime = -1000;
109 int masterquerycount = 0;
110 int masterreplycount = 0;
111 int serverquerycount = 0;
112 int serverreplycount = 0;
113
114 challenge_t challenges[MAX_CHALLENGES];
115
116 #ifdef CONFIG_MENU
117 /// this is only false if there are still servers left to query
118 static qboolean serverlist_querysleep = true;
119 static qboolean serverlist_paused = false;
120 /// this is pushed a second or two ahead of realtime whenever a master server
121 /// reply is received, to avoid issuing queries while master replies are still
122 /// flooding in (which would make a mess of the ping times)
123 static double serverlist_querywaittime = 0;
124 #endif
125
126 static int cl_numsockets;
127 static lhnetsocket_t *cl_sockets[16];
128 static int sv_numsockets;
129 static lhnetsocket_t *sv_sockets[16];
130
131 netconn_t *netconn_list = NULL;
132 mempool_t *netconn_mempool = NULL;
133 void *netconn_mutex = NULL;
134
135 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
136 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
137 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
138 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
139
140 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
141 int cl_net_extresponse_count = 0;
142 int cl_net_extresponse_last = 0;
143
144 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
145 int sv_net_extresponse_count = 0;
146 int sv_net_extresponse_last = 0;
147
148 #ifdef CONFIG_MENU
149 // ServerList interface
150 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
151 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
152
153 serverlist_infofield_t serverlist_sortbyfield;
154 int serverlist_sortflags;
155
156 int serverlist_viewcount = 0;
157 unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
158
159 int serverlist_maxcachecount = 0;
160 int serverlist_cachecount = 0;
161 serverlist_entry_t *serverlist_cache = NULL;
162
163 qboolean serverlist_consoleoutput;
164
165 static int nFavorites = 0;
166 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
167 static int nFavorites_idfp = 0;
168 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
169
NetConn_UpdateFavorites(void)170 void NetConn_UpdateFavorites(void)
171 {
172 const char *p;
173 nFavorites = 0;
174 nFavorites_idfp = 0;
175 p = net_slist_favorites.string;
176 while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
177 {
178 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
179 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
180 // (if v6 address contains port, it must start with '[')
181 {
182 strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
183 ++nFavorites_idfp;
184 }
185 else
186 {
187 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
188 ++nFavorites;
189 }
190 }
191 }
192
193 /// helper function to insert a value into the viewset
194 /// spare entries will be removed
_ServerList_ViewList_Helper_InsertBefore(int index,serverlist_entry_t * entry)195 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
196 {
197 int i;
198 if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
199 i = serverlist_viewcount++;
200 } else {
201 i = SERVERLIST_VIEWLISTSIZE - 1;
202 }
203
204 for( ; i > index ; i-- )
205 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
206
207 serverlist_viewlist[index] = (int)(entry - serverlist_cache);
208 }
209
210 /// we suppose serverlist_viewcount to be valid, ie > 0
_ServerList_ViewList_Helper_Remove(int index)211 static void _ServerList_ViewList_Helper_Remove( int index )
212 {
213 serverlist_viewcount--;
214 for( ; index < serverlist_viewcount ; index++ )
215 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
216 }
217
218 /// \returns true if A should be inserted before B
_ServerList_Entry_Compare(serverlist_entry_t * A,serverlist_entry_t * B)219 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
220 {
221 int result = 0; // > 0 if for numbers A > B and for text if A < B
222
223 if( serverlist_sortflags & SLSF_CATEGORIES )
224 {
225 result = A->info.category - B->info.category;
226 if (result != 0)
227 return result < 0;
228 }
229
230 if( serverlist_sortflags & SLSF_FAVORITES )
231 {
232 if(A->info.isfavorite != B->info.isfavorite)
233 return A->info.isfavorite;
234 }
235
236 switch( serverlist_sortbyfield ) {
237 case SLIF_PING:
238 result = A->info.ping - B->info.ping;
239 break;
240 case SLIF_MAXPLAYERS:
241 result = A->info.maxplayers - B->info.maxplayers;
242 break;
243 case SLIF_NUMPLAYERS:
244 result = A->info.numplayers - B->info.numplayers;
245 break;
246 case SLIF_NUMBOTS:
247 result = A->info.numbots - B->info.numbots;
248 break;
249 case SLIF_NUMHUMANS:
250 result = A->info.numhumans - B->info.numhumans;
251 break;
252 case SLIF_FREESLOTS:
253 result = A->info.freeslots - B->info.freeslots;
254 break;
255 case SLIF_PROTOCOL:
256 result = A->info.protocol - B->info.protocol;
257 break;
258 case SLIF_CNAME:
259 result = strcmp( B->info.cname, A->info.cname );
260 break;
261 case SLIF_GAME:
262 result = strcasecmp( B->info.game, A->info.game );
263 break;
264 case SLIF_MAP:
265 result = strcasecmp( B->info.map, A->info.map );
266 break;
267 case SLIF_MOD:
268 result = strcasecmp( B->info.mod, A->info.mod );
269 break;
270 case SLIF_NAME:
271 result = strcasecmp( B->info.name, A->info.name );
272 break;
273 case SLIF_QCSTATUS:
274 result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
275 break;
276 case SLIF_CATEGORY:
277 result = A->info.category - B->info.category;
278 break;
279 case SLIF_ISFAVORITE:
280 result = !!B->info.isfavorite - !!A->info.isfavorite;
281 break;
282 default:
283 Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
284 break;
285 }
286
287 if (result != 0)
288 {
289 if( serverlist_sortflags & SLSF_DESCENDING )
290 return result > 0;
291 else
292 return result < 0;
293 }
294
295 // if the chosen sort key is identical, sort by index
296 // (makes this a stable sort, so that later replies from servers won't
297 // shuffle the servers around when they have the same ping)
298 return A < B;
299 }
300
_ServerList_CompareInt(int A,serverlist_maskop_t op,int B)301 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
302 {
303 // This should actually be done with some intermediate and end-of-function return
304 switch( op ) {
305 case SLMO_LESS:
306 return A < B;
307 case SLMO_LESSEQUAL:
308 return A <= B;
309 case SLMO_EQUAL:
310 return A == B;
311 case SLMO_GREATER:
312 return A > B;
313 case SLMO_NOTEQUAL:
314 return A != B;
315 case SLMO_GREATEREQUAL:
316 case SLMO_CONTAINS:
317 case SLMO_NOTCONTAIN:
318 case SLMO_STARTSWITH:
319 case SLMO_NOTSTARTSWITH:
320 return A >= B;
321 default:
322 Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
323 return false;
324 }
325 }
326
_ServerList_CompareStr(const char * A,serverlist_maskop_t op,const char * B)327 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
328 {
329 int i;
330 char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
331 COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
332 for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
333 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
334 bufferA[i] = 0;
335 for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
336 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
337 bufferB[i] = 0;
338
339 // Same here, also using an intermediate & final return would be more appropriate
340 // A info B mask
341 switch( op ) {
342 case SLMO_CONTAINS:
343 return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
344 case SLMO_NOTCONTAIN:
345 return !*bufferB || !strstr( bufferA, bufferB );
346 case SLMO_STARTSWITH:
347 //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
348 return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
349 case SLMO_NOTSTARTSWITH:
350 return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
351 case SLMO_LESS:
352 return strcmp( bufferA, bufferB ) < 0;
353 case SLMO_LESSEQUAL:
354 return strcmp( bufferA, bufferB ) <= 0;
355 case SLMO_EQUAL:
356 return strcmp( bufferA, bufferB ) == 0;
357 case SLMO_GREATER:
358 return strcmp( bufferA, bufferB ) > 0;
359 case SLMO_NOTEQUAL:
360 return strcmp( bufferA, bufferB ) != 0;
361 case SLMO_GREATEREQUAL:
362 return strcmp( bufferA, bufferB ) >= 0;
363 default:
364 Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
365 return false;
366 }
367 }
368
_ServerList_Entry_Mask(serverlist_mask_t * mask,serverlist_info_t * info)369 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
370 {
371 if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
372 return false;
373 if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
374 return false;
375 if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
376 return false;
377 if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
378 return false;
379 if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
380 return false;
381 if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
382 return false;
383 if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
384 return false;
385 if( *mask->info.cname
386 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
387 return false;
388 if( *mask->info.game
389 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
390 return false;
391 if( *mask->info.mod
392 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
393 return false;
394 if( *mask->info.map
395 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
396 return false;
397 if( *mask->info.name
398 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
399 return false;
400 if( *mask->info.qcstatus
401 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
402 return false;
403 if( *mask->info.players
404 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
405 return false;
406 if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
407 return false;
408 if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
409 return false;
410 return true;
411 }
412
ServerList_ViewList_Insert(serverlist_entry_t * entry)413 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
414 {
415 int start, end, mid, i;
416 lhnetaddress_t addr;
417
418 // reject incompatible servers
419 if(
420 entry->info.gameversion != gameversion.integer
421 &&
422 !(
423 gameversion_min.integer >= 0 // min/max range set by user/mod?
424 && gameversion_max.integer >= 0
425 && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
426 && gameversion_max.integer >= entry->info.gameversion
427 )
428 )
429 return;
430
431 // refresh the "favorite" status
432 entry->info.isfavorite = false;
433 if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
434 {
435 char idfp[FP64_SIZE+1];
436 for(i = 0; i < nFavorites; ++i)
437 {
438 if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
439 {
440 entry->info.isfavorite = true;
441 break;
442 }
443 }
444 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
445 {
446 for(i = 0; i < nFavorites_idfp; ++i)
447 {
448 if(!strcmp(idfp, favorites_idfp[i]))
449 {
450 entry->info.isfavorite = true;
451 break;
452 }
453 }
454 }
455 }
456
457 // refresh the "category"
458 entry->info.category = MR_GetServerListEntryCategory(entry);
459
460 // FIXME: change this to be more readable (...)
461 // now check whether it passes through the masks
462 for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
463 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
464 return;
465
466 for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
467 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
468 break;
469 if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
470 return;
471
472 if( !serverlist_viewcount ) {
473 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
474 return;
475 }
476 // ok, insert it, we just need to find out where exactly:
477
478 // two special cases
479 // check whether to insert it as new first item
480 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
481 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
482 return;
483 } // check whether to insert it as new last item
484 else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
485 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
486 return;
487 }
488 start = 0;
489 end = serverlist_viewcount - 1;
490 while( end > start + 1 )
491 {
492 mid = (start + end) / 2;
493 // test the item that lies in the middle between start and end
494 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
495 // the item has to be in the upper half
496 end = mid;
497 else
498 // the item has to be in the lower half
499 start = mid;
500 }
501 _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
502 }
503
ServerList_ViewList_Remove(serverlist_entry_t * entry)504 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
505 {
506 int i;
507 for( i = 0; i < serverlist_viewcount; i++ )
508 {
509 if (ServerList_GetViewEntry(i) == entry)
510 {
511 _ServerList_ViewList_Helper_Remove(i);
512 break;
513 }
514 }
515 }
516
ServerList_RebuildViewList(void)517 void ServerList_RebuildViewList(void)
518 {
519 int i;
520
521 serverlist_viewcount = 0;
522 for( i = 0 ; i < serverlist_cachecount ; i++ ) {
523 serverlist_entry_t *entry = &serverlist_cache[i];
524 // also display entries that are currently being refreshed [11/8/2007 Black]
525 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
526 ServerList_ViewList_Insert( entry );
527 }
528 }
529
ServerList_ResetMasks(void)530 void ServerList_ResetMasks(void)
531 {
532 int i;
533
534 memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
535 memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
536 // numbots needs to be compared to -1 to always succeed
537 for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
538 serverlist_andmasks[i].info.numbots = -1;
539 for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
540 serverlist_ormasks[i].info.numbots = -1;
541 }
542
ServerList_GetPlayerStatistics(int * numplayerspointer,int * maxplayerspointer)543 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
544 {
545 int i;
546 int numplayers = 0, maxplayers = 0;
547 for (i = 0;i < serverlist_cachecount;i++)
548 {
549 if (serverlist_cache[i].query == SQS_QUERIED)
550 {
551 numplayers += serverlist_cache[i].info.numhumans;
552 maxplayers += serverlist_cache[i].info.maxplayers;
553 }
554 }
555 *numplayerspointer = numplayers;
556 *maxplayerspointer = maxplayers;
557 }
558
559 #if 0
560 static void _ServerList_Test(void)
561 {
562 int i;
563 if (serverlist_maxcachecount <= 1024)
564 {
565 serverlist_maxcachecount = 1024;
566 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
567 }
568 for( i = 0 ; i < 1024 ; i++ ) {
569 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
570 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
571 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
572 serverlist_cache[serverlist_cachecount].finished = true;
573 dpsnprintf( serverlist_cache[serverlist_cachecount].line1, sizeof(serverlist_cache[serverlist_cachecount].info.line1), "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
574 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
575 serverlist_cachecount++;
576 }
577 }
578 #endif
579
ServerList_QueryList(qboolean resetcache,qboolean querydp,qboolean queryqw,qboolean consoleoutput)580 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
581 {
582 masterquerytime = realtime;
583 masterquerycount = 0;
584 masterreplycount = 0;
585 if( resetcache ) {
586 serverquerycount = 0;
587 serverreplycount = 0;
588 serverlist_cachecount = 0;
589 serverlist_viewcount = 0;
590 serverlist_maxcachecount = 0;
591 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
592 } else {
593 // refresh all entries
594 int n;
595 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
596 serverlist_entry_t *entry = &serverlist_cache[ n ];
597 entry->query = SQS_REFRESHING;
598 entry->querycounter = 0;
599 }
600 }
601 serverlist_consoleoutput = consoleoutput;
602
603 //_ServerList_Test();
604
605 NetConn_QueryMasters(querydp, queryqw);
606 }
607 #endif
608
609 // rest
610
NetConn_Read(lhnetsocket_t * mysocket,void * data,int maxlength,lhnetaddress_t * peeraddress)611 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
612 {
613 int length;
614 int i;
615 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
616 Thread_LockMutex(netconn_mutex);
617 length = LHNET_Read(mysocket, data, maxlength, peeraddress);
618 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
619 Thread_UnlockMutex(netconn_mutex);
620 if (length == 0)
621 return 0;
622 if (cl_netpacketloss_receive.integer)
623 for (i = 0;i < cl_numsockets;i++)
624 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
625 return 0;
626 if (developer_networking.integer)
627 {
628 char addressstring[128], addressstring2[128];
629 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
630 if (length > 0)
631 {
632 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
633 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
634 Com_HexDumpToConsole((unsigned char *)data, length);
635 }
636 else
637 Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
638 }
639 return length;
640 }
641
NetConn_Write(lhnetsocket_t * mysocket,const void * data,int length,const lhnetaddress_t * peeraddress)642 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
643 {
644 int ret;
645 int i;
646 if (cl_netpacketloss_send.integer)
647 for (i = 0;i < cl_numsockets;i++)
648 if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
649 return length;
650 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
651 Thread_LockMutex(netconn_mutex);
652 ret = LHNET_Write(mysocket, data, length, peeraddress);
653 if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
654 Thread_UnlockMutex(netconn_mutex);
655 if (developer_networking.integer)
656 {
657 char addressstring[128], addressstring2[128];
658 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
659 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
660 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", (void *)mysocket, addressstring, (void *)data, length, (void *)peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
661 Com_HexDumpToConsole((unsigned char *)data, length);
662 }
663 return ret;
664 }
665
NetConn_WriteString(lhnetsocket_t * mysocket,const char * string,const lhnetaddress_t * peeraddress)666 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
667 {
668 // note this does not include the trailing NULL because we add that in the parser
669 return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
670 }
671
NetConn_CanSend(netconn_t * conn)672 qboolean NetConn_CanSend(netconn_t *conn)
673 {
674 conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
675 conn->outgoing_netgraph[conn->outgoing_packetcounter].time = realtime;
676 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
677 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
678 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
679 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
680 if (realtime > conn->cleartime)
681 return true;
682 else
683 {
684 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
685 return false;
686 }
687 }
688
NetConn_UpdateCleartime(double * cleartime,int rate,int burstsize,int len)689 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
690 {
691 double bursttime = burstsize / (double)rate;
692
693 // delay later packets to obey rate limit
694 if (*cleartime < realtime - bursttime)
695 *cleartime = realtime - bursttime;
696 *cleartime = *cleartime + len / (double)rate;
697
698 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
699 if (net_test.integer)
700 {
701 if (*cleartime < realtime)
702 *cleartime = realtime;
703 }
704 }
705
NetConn_AddCryptoFlag(crypto_t * crypto)706 static int NetConn_AddCryptoFlag(crypto_t *crypto)
707 {
708 // HACK: if an encrypted connection is used, randomly set some unused
709 // flags. When AES encryption is enabled, that will make resends differ
710 // from the original, so that e.g. substring filters in a router/IPS
711 // are unlikely to match a second time. See also "startkeylogger".
712 int flag = 0;
713 if (crypto->authenticated)
714 {
715 // Let's always set at least one of the bits.
716 int r = rand() % 7 + 1;
717 if (r & 1)
718 flag |= NETFLAG_CRYPTO0;
719 if (r & 2)
720 flag |= NETFLAG_CRYPTO1;
721 if (r & 4)
722 flag |= NETFLAG_CRYPTO2;
723 }
724 return flag;
725 }
726
NetConn_SendUnreliableMessage(netconn_t * conn,sizebuf_t * data,protocolversion_t protocol,int rate,int burstsize,qboolean quakesignon_suppressreliables)727 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
728 {
729 int totallen = 0;
730 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
731 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
732
733 // if this packet was supposedly choked, but we find ourselves sending one
734 // anyway, make sure the size counting starts at zero
735 // (this mostly happens on level changes and disconnects and such)
736 if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
737 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
738
739 conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
740
741 if (protocol == PROTOCOL_QUAKEWORLD)
742 {
743 int packetLen;
744 qboolean sendreliable;
745
746 // note that it is ok to send empty messages to the qw server,
747 // otherwise it won't respond to us at all
748
749 sendreliable = false;
750 // if the remote side dropped the last reliable message, resend it
751 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
752 sendreliable = true;
753 // if the reliable transmit buffer is empty, copy the current message out
754 if (!conn->sendMessageLength && conn->message.cursize)
755 {
756 memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
757 conn->sendMessageLength = conn->message.cursize;
758 SZ_Clear(&conn->message); // clear the message buffer
759 conn->qw.reliable_sequence ^= 1;
760 sendreliable = true;
761 }
762 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
763 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
764 // last received unreliable packet number, and last received reliable packet number (0 or 1)
765 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
766 packetLen = 8;
767 conn->outgoing_unreliable_sequence++;
768 // client sends qport in every packet
769 if (conn == cls.netcon)
770 {
771 *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
772 packetLen += 2;
773 // also update cls.qw_outgoing_sequence
774 cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
775 }
776 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
777 {
778 Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
779 return -1;
780 }
781
782 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
783
784 // add the reliable message if there is one
785 if (sendreliable)
786 {
787 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
788 memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
789 packetLen += conn->sendMessageLength;
790 conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
791 }
792
793 // add the unreliable message if possible
794 if (packetLen + data->cursize <= 1400)
795 {
796 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
797 memcpy(sendbuffer + packetLen, data->data, data->cursize);
798 packetLen += data->cursize;
799 }
800
801 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
802
803 conn->packetsSent++;
804 conn->unreliableMessagesSent++;
805
806 totallen += packetLen + 28;
807 }
808 else
809 {
810 unsigned int packetLen;
811 unsigned int dataLen;
812 unsigned int eom;
813 const void *sendme;
814 size_t sendmelen;
815
816 // if a reliable message fragment has been lost, send it again
817 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
818 {
819 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
820 {
821 dataLen = conn->sendMessageLength;
822 eom = NETFLAG_EOM;
823 }
824 else
825 {
826 dataLen = MAX_PACKETFRAGMENT;
827 eom = 0;
828 }
829
830 packetLen = NET_HEADERSIZE + dataLen;
831
832 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
833 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
834 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
835
836 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
837
838 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
839 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
840 {
841 conn->lastSendTime = realtime;
842 conn->packetsReSent++;
843 }
844
845 totallen += (int)sendmelen + 28;
846 }
847
848 // if we have a new reliable message to send, do so
849 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
850 {
851 if (conn->message.cursize > (int)sizeof(conn->sendMessage))
852 {
853 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
854 conn->message.overflowed = true;
855 return -1;
856 }
857
858 if (developer_networking.integer && conn == cls.netcon)
859 {
860 Con_Print("client sending reliable message to server:\n");
861 SZ_HexDumpToConsole(&conn->message);
862 }
863
864 memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
865 conn->sendMessageLength = conn->message.cursize;
866 SZ_Clear(&conn->message);
867
868 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
869 {
870 dataLen = conn->sendMessageLength;
871 eom = NETFLAG_EOM;
872 }
873 else
874 {
875 dataLen = MAX_PACKETFRAGMENT;
876 eom = 0;
877 }
878
879 packetLen = NET_HEADERSIZE + dataLen;
880
881 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
882 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
883 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
884
885 conn->nq.sendSequence++;
886
887 conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
888
889 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
890 if(sendme)
891 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
892
893 conn->lastSendTime = realtime;
894 conn->packetsSent++;
895 conn->reliableMessagesSent++;
896
897 totallen += (int)sendmelen + 28;
898 }
899
900 // if we have an unreliable message to send, do so
901 if (data->cursize)
902 {
903 packetLen = NET_HEADERSIZE + data->cursize;
904
905 if (packetLen > (int)sizeof(sendbuffer))
906 {
907 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
908 return -1;
909 }
910
911 StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
912 StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
913 memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
914
915 conn->outgoing_unreliable_sequence++;
916
917 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
918
919 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
920 if(sendme)
921 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
922
923 conn->packetsSent++;
924 conn->unreliableMessagesSent++;
925
926 totallen += (int)sendmelen + 28;
927 }
928 }
929
930 NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
931
932 return 0;
933 }
934
NetConn_HaveClientPorts(void)935 qboolean NetConn_HaveClientPorts(void)
936 {
937 return !!cl_numsockets;
938 }
939
NetConn_HaveServerPorts(void)940 qboolean NetConn_HaveServerPorts(void)
941 {
942 return !!sv_numsockets;
943 }
944
NetConn_CloseClientPorts(void)945 void NetConn_CloseClientPorts(void)
946 {
947 for (;cl_numsockets > 0;cl_numsockets--)
948 if (cl_sockets[cl_numsockets - 1])
949 LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
950 }
951
NetConn_OpenClientPort(const char * addressstring,lhnetaddresstype_t addresstype,int defaultport)952 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
953 {
954 lhnetaddress_t address;
955 lhnetsocket_t *s;
956 int success;
957 char addressstring2[1024];
958 if (addressstring && addressstring[0])
959 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
960 else
961 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
962 if (success)
963 {
964 if ((s = LHNET_OpenSocket_Connectionless(&address)))
965 {
966 cl_sockets[cl_numsockets++] = s;
967 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
968 if (addresstype != LHNETADDRESSTYPE_LOOP)
969 Con_Printf("Client opened a socket on address %s\n", addressstring2);
970 }
971 else
972 {
973 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
974 Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
975 }
976 }
977 else
978 Con_Printf("Client unable to parse address %s\n", addressstring);
979 }
980
NetConn_OpenClientPorts(void)981 void NetConn_OpenClientPorts(void)
982 {
983 int port;
984 NetConn_CloseClientPorts();
985
986 SV_LockThreadMutex(); // FIXME recursive?
987 Crypto_LoadKeys(); // client sockets
988 SV_UnlockThreadMutex();
989
990 port = bound(0, cl_netport.integer, 65535);
991 if (cl_netport.integer != port)
992 Cvar_SetValueQuick(&cl_netport, port);
993 if(port == 0)
994 Con_Printf("Client using an automatically assigned port\n");
995 else
996 Con_Printf("Client using port %i\n", port);
997 NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
998 NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
999 #ifndef NOSUPPORTIPV6
1000 NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1001 #endif
1002 }
1003
NetConn_CloseServerPorts(void)1004 void NetConn_CloseServerPorts(void)
1005 {
1006 for (;sv_numsockets > 0;sv_numsockets--)
1007 if (sv_sockets[sv_numsockets - 1])
1008 LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1009 }
1010
NetConn_OpenServerPort(const char * addressstring,lhnetaddresstype_t addresstype,int defaultport,int range)1011 static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1012 {
1013 lhnetaddress_t address;
1014 lhnetsocket_t *s;
1015 int port;
1016 char addressstring2[1024];
1017 int success;
1018
1019 for (port = defaultport; port <= defaultport + range; port++)
1020 {
1021 if (addressstring && addressstring[0])
1022 success = LHNETADDRESS_FromString(&address, addressstring, port);
1023 else
1024 success = LHNETADDRESS_FromPort(&address, addresstype, port);
1025 if (success)
1026 {
1027 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1028 {
1029 sv_sockets[sv_numsockets++] = s;
1030 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1031 if (addresstype != LHNETADDRESSTYPE_LOOP)
1032 Con_Printf("Server listening on address %s\n", addressstring2);
1033 return true;
1034 }
1035 else
1036 {
1037 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1038 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
1039 }
1040 }
1041 else
1042 {
1043 Con_Printf("Server unable to parse address %s\n", addressstring);
1044 // if it cant parse one address, it wont be able to parse another for sure
1045 return false;
1046 }
1047 }
1048 return false;
1049 }
1050
NetConn_OpenServerPorts(int opennetports)1051 void NetConn_OpenServerPorts(int opennetports)
1052 {
1053 int port;
1054 NetConn_CloseServerPorts();
1055
1056 SV_LockThreadMutex(); // FIXME recursive?
1057 Crypto_LoadKeys(); // server sockets
1058 SV_UnlockThreadMutex();
1059
1060 NetConn_UpdateSockets();
1061 port = bound(0, sv_netport.integer, 65535);
1062 if (port == 0)
1063 port = 26000;
1064 Con_Printf("Server using port %i\n", port);
1065 if (sv_netport.integer != port)
1066 Cvar_SetValueQuick(&sv_netport, port);
1067 if (cls.state != ca_dedicated)
1068 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1069 if (opennetports)
1070 {
1071 #ifndef NOSUPPORTIPV6
1072 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1073 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1074 #else
1075 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1076 #endif
1077 }
1078 if (sv_numsockets == 0)
1079 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1080 }
1081
NetConn_ChooseClientSocketForAddress(lhnetaddress_t * address)1082 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1083 {
1084 int i, a = LHNETADDRESS_GetAddressType(address);
1085 for (i = 0;i < cl_numsockets;i++)
1086 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1087 return cl_sockets[i];
1088 return NULL;
1089 }
1090
NetConn_ChooseServerSocketForAddress(lhnetaddress_t * address)1091 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1092 {
1093 int i, a = LHNETADDRESS_GetAddressType(address);
1094 for (i = 0;i < sv_numsockets;i++)
1095 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1096 return sv_sockets[i];
1097 return NULL;
1098 }
1099
NetConn_Open(lhnetsocket_t * mysocket,lhnetaddress_t * peeraddress)1100 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1101 {
1102 netconn_t *conn;
1103 conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1104 conn->mysocket = mysocket;
1105 conn->peeraddress = *peeraddress;
1106 conn->lastMessageTime = realtime;
1107 conn->message.data = conn->messagedata;
1108 conn->message.maxsize = sizeof(conn->messagedata);
1109 conn->message.cursize = 0;
1110 // LordHavoc: (inspired by ProQuake) use a short connect timeout to
1111 // reduce effectiveness of connection request floods
1112 conn->timeout = realtime + net_connecttimeout.value;
1113 LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1114 conn->next = netconn_list;
1115 netconn_list = conn;
1116 return conn;
1117 }
1118
1119 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
NetConn_Close(netconn_t * conn)1120 void NetConn_Close(netconn_t *conn)
1121 {
1122 netconn_t *c;
1123 // remove connection from list
1124
1125 // allow the client to reconnect immediately
1126 NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1127
1128 if (conn == netconn_list)
1129 netconn_list = conn->next;
1130 else
1131 {
1132 for (c = netconn_list;c;c = c->next)
1133 {
1134 if (c->next == conn)
1135 {
1136 c->next = conn->next;
1137 break;
1138 }
1139 }
1140 // not found in list, we'll avoid crashing here...
1141 if (!c)
1142 return;
1143 }
1144 // free connection
1145 Mem_Free(conn);
1146 }
1147
1148 static int clientport = -1;
1149 static int clientport2 = -1;
1150 static int hostport = -1;
NetConn_UpdateSockets(void)1151 void NetConn_UpdateSockets(void)
1152 {
1153 int i, j;
1154
1155 // TODO add logic to automatically close sockets if needed
1156 LHNET_DefaultDSCP(net_tos_dscp.integer);
1157
1158 if (cls.state != ca_dedicated)
1159 {
1160 if (clientport2 != cl_netport.integer)
1161 {
1162 clientport2 = cl_netport.integer;
1163 if (cls.state == ca_connected)
1164 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1165 }
1166 if (cls.state == ca_disconnected && clientport != clientport2)
1167 {
1168 clientport = clientport2;
1169 NetConn_CloseClientPorts();
1170 }
1171 if (cl_numsockets == 0)
1172 NetConn_OpenClientPorts();
1173 }
1174
1175 if (hostport != sv_netport.integer)
1176 {
1177 hostport = sv_netport.integer;
1178 if (sv.active)
1179 Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1180 }
1181
1182 for (j = 0;j < MAX_RCONS;j++)
1183 {
1184 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1185 if(cls.rcon_commands[i][0])
1186 {
1187 if(realtime > cls.rcon_timeout[i])
1188 {
1189 char s[128];
1190 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1191 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1192 cls.rcon_commands[i][0] = 0;
1193 --cls.rcon_trying;
1194 break;
1195 }
1196 }
1197 }
1198 }
1199
NetConn_ReceivedMessage(netconn_t * conn,const unsigned char * data,size_t length,protocolversion_t protocol,double newtimeout)1200 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1201 {
1202 int originallength = (int)length;
1203 unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1204 unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1205 unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1206 if (length < 8)
1207 return 0;
1208
1209 if (protocol == PROTOCOL_QUAKEWORLD)
1210 {
1211 unsigned int sequence, sequence_ack;
1212 qboolean reliable_ack, reliable_message;
1213 int count;
1214 //int qport;
1215
1216 sequence = LittleLong(*((int *)(data + 0)));
1217 sequence_ack = LittleLong(*((int *)(data + 4)));
1218 data += 8;
1219 length -= 8;
1220
1221 if (conn != cls.netcon)
1222 {
1223 // server only
1224 if (length < 2)
1225 return 0;
1226 // TODO: use qport to identify that this client really is who they say they are? (and elsewhere in the code to identify the connection without a port match?)
1227 //qport = LittleShort(*((int *)(data + 8)));
1228 data += 2;
1229 length -= 2;
1230 }
1231
1232 conn->packetsReceived++;
1233 reliable_message = (sequence >> 31) != 0;
1234 reliable_ack = (sequence_ack >> 31) != 0;
1235 sequence &= ~(1<<31);
1236 sequence_ack &= ~(1<<31);
1237 if (sequence <= conn->qw.incoming_sequence)
1238 {
1239 //Con_DPrint("Got a stale datagram\n");
1240 return 0;
1241 }
1242 count = sequence - (conn->qw.incoming_sequence + 1);
1243 if (count > 0)
1244 {
1245 conn->droppedDatagrams += count;
1246 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1247 // If too may packets have been dropped, only write the
1248 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1249 // Because there's no point in writing more than
1250 // these as the netgraph is going to be full anyway.
1251 if (count > NETGRAPH_PACKETS)
1252 count = NETGRAPH_PACKETS;
1253 while (count--)
1254 {
1255 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1256 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1257 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1258 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1259 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1260 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1261 }
1262 }
1263 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1264 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1265 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1266 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1267 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1268 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1269 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1270
1271 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1272 if (net_test.integer)
1273 {
1274 if (conn->cleartime < realtime)
1275 conn->cleartime = realtime;
1276 }
1277
1278 if (reliable_ack == conn->qw.reliable_sequence)
1279 {
1280 // received, now we will be able to send another reliable message
1281 conn->sendMessageLength = 0;
1282 conn->reliableMessagesReceived++;
1283 }
1284 conn->qw.incoming_sequence = sequence;
1285 if (conn == cls.netcon)
1286 cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1287 conn->qw.incoming_acknowledged = sequence_ack;
1288 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1289 if (reliable_message)
1290 conn->qw.incoming_reliable_sequence ^= 1;
1291 conn->lastMessageTime = realtime;
1292 conn->timeout = realtime + newtimeout;
1293 conn->unreliableMessagesReceived++;
1294 if (conn == cls.netcon)
1295 {
1296 SZ_Clear(&cl_message);
1297 SZ_Write(&cl_message, data, (int)length);
1298 MSG_BeginReading(&cl_message);
1299 }
1300 else
1301 {
1302 SZ_Clear(&sv_message);
1303 SZ_Write(&sv_message, data, (int)length);
1304 MSG_BeginReading(&sv_message);
1305 }
1306 return 2;
1307 }
1308 else
1309 {
1310 unsigned int count;
1311 unsigned int flags;
1312 unsigned int sequence;
1313 size_t qlength;
1314 const void *sendme;
1315 size_t sendmelen;
1316
1317 originallength = (int)length;
1318 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1319 if(!data)
1320 return 0;
1321 if(length < 8)
1322 return 0;
1323
1324 qlength = (unsigned int)BuffBigLong(data);
1325 flags = qlength & ~NETFLAG_LENGTH_MASK;
1326 qlength &= NETFLAG_LENGTH_MASK;
1327 // control packets were already handled
1328 if (!(flags & NETFLAG_CTL) && qlength == length)
1329 {
1330 sequence = BuffBigLong(data + 4);
1331 conn->packetsReceived++;
1332 data += 8;
1333 length -= 8;
1334 if (flags & NETFLAG_UNRELIABLE)
1335 {
1336 if (sequence >= conn->nq.unreliableReceiveSequence)
1337 {
1338 if (sequence > conn->nq.unreliableReceiveSequence)
1339 {
1340 count = sequence - conn->nq.unreliableReceiveSequence;
1341 conn->droppedDatagrams += count;
1342 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1343 // If too may packets have been dropped, only write the
1344 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1345 // Because there's no point in writing more than
1346 // these as the netgraph is going to be full anyway.
1347 if (count > NETGRAPH_PACKETS)
1348 count = NETGRAPH_PACKETS;
1349 while (count--)
1350 {
1351 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1352 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1353 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1354 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1355 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1356 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1357 }
1358 }
1359 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1360 conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
1361 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
1362 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1363 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
1364 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
1365 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1366
1367 conn->nq.unreliableReceiveSequence = sequence + 1;
1368 conn->lastMessageTime = realtime;
1369 conn->timeout = realtime + newtimeout;
1370 conn->unreliableMessagesReceived++;
1371 if (length > 0)
1372 {
1373 if (conn == cls.netcon)
1374 {
1375 SZ_Clear(&cl_message);
1376 SZ_Write(&cl_message, data, (int)length);
1377 MSG_BeginReading(&cl_message);
1378 }
1379 else
1380 {
1381 SZ_Clear(&sv_message);
1382 SZ_Write(&sv_message, data, (int)length);
1383 MSG_BeginReading(&sv_message);
1384 }
1385 return 2;
1386 }
1387 }
1388 //else
1389 // Con_DPrint("Got a stale datagram\n");
1390 return 1;
1391 }
1392 else if (flags & NETFLAG_ACK)
1393 {
1394 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1395 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1396
1397 if (sequence == (conn->nq.sendSequence - 1))
1398 {
1399 if (sequence == conn->nq.ackSequence)
1400 {
1401 conn->nq.ackSequence++;
1402 if (conn->nq.ackSequence != conn->nq.sendSequence)
1403 Con_DPrint("ack sequencing error\n");
1404 conn->lastMessageTime = realtime;
1405 conn->timeout = realtime + newtimeout;
1406 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1407 {
1408 unsigned int packetLen;
1409 unsigned int dataLen;
1410 unsigned int eom;
1411
1412 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1413 memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1414
1415 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1416 {
1417 dataLen = conn->sendMessageLength;
1418 eom = NETFLAG_EOM;
1419 }
1420 else
1421 {
1422 dataLen = MAX_PACKETFRAGMENT;
1423 eom = 0;
1424 }
1425
1426 packetLen = NET_HEADERSIZE + dataLen;
1427
1428 StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1429 StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1430 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1431
1432 conn->nq.sendSequence++;
1433
1434 sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1435 if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1436 {
1437 conn->lastSendTime = realtime;
1438 conn->packetsSent++;
1439 }
1440 }
1441 else
1442 conn->sendMessageLength = 0;
1443 }
1444 //else
1445 // Con_DPrint("Duplicate ACK received\n");
1446 }
1447 //else
1448 // Con_DPrint("Stale ACK received\n");
1449 return 1;
1450 }
1451 else if (flags & NETFLAG_DATA)
1452 {
1453 unsigned char temppacket[8];
1454 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
1455 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1456
1457 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
1458
1459 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1460 StoreBigLong(temppacket + 4, sequence);
1461 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1462 if(sendme)
1463 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1464 if (sequence == conn->nq.receiveSequence)
1465 {
1466 conn->lastMessageTime = realtime;
1467 conn->timeout = realtime + newtimeout;
1468 conn->nq.receiveSequence++;
1469 if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1470 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1471 conn->receiveMessageLength += (int)length;
1472 } else {
1473 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1474 "Dropping the message!\n", sequence );
1475 conn->receiveMessageLength = 0;
1476 return 1;
1477 }
1478 if (flags & NETFLAG_EOM)
1479 {
1480 conn->reliableMessagesReceived++;
1481 length = conn->receiveMessageLength;
1482 conn->receiveMessageLength = 0;
1483 if (length > 0)
1484 {
1485 if (conn == cls.netcon)
1486 {
1487 SZ_Clear(&cl_message);
1488 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1489 MSG_BeginReading(&cl_message);
1490 }
1491 else
1492 {
1493 SZ_Clear(&sv_message);
1494 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1495 MSG_BeginReading(&sv_message);
1496 }
1497 return 2;
1498 }
1499 }
1500 }
1501 else
1502 conn->receivedDuplicateCount++;
1503 return 1;
1504 }
1505 }
1506 }
1507 return 0;
1508 }
1509
NetConn_ConnectionEstablished(lhnetsocket_t * mysocket,lhnetaddress_t * peeraddress,protocolversion_t initialprotocol)1510 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1511 {
1512 crypto_t *crypto;
1513 cls.connect_trying = false;
1514 #ifdef CONFIG_MENU
1515 M_Update_Return_Reason("");
1516 #endif
1517 // the connection request succeeded, stop current connection and set up a new connection
1518 CL_Disconnect();
1519 // if we're connecting to a remote server, shut down any local server
1520 if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1521 {
1522 SV_LockThreadMutex();
1523 Host_ShutdownServer ();
1524 SV_UnlockThreadMutex();
1525 }
1526 // allocate a net connection to keep track of things
1527 cls.netcon = NetConn_Open(mysocket, peeraddress);
1528 crypto = &cls.netcon->crypto;
1529 if(cls.crypto.authenticated)
1530 {
1531 Crypto_FinishInstance(crypto, &cls.crypto);
1532 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1533 crypto->use_aes ? "Encrypted" : "Authenticated",
1534 cls.netcon->address,
1535 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1536 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1537 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1538 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1539 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1540 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1541 );
1542 }
1543 Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1544 key_dest = key_game;
1545 #ifdef CONFIG_MENU
1546 m_state = m_none;
1547 #endif
1548 cls.demonum = -1; // not in the demo loop now
1549 cls.state = ca_connected;
1550 cls.signon = 0; // need all the signon messages before playing
1551 cls.protocol = initialprotocol;
1552 // reset move sequence numbering on this new connection
1553 cls.servermovesequence = 0;
1554 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1555 Cmd_ForwardStringToServer("new");
1556 if (cls.protocol == PROTOCOL_QUAKE)
1557 {
1558 // write a keepalive (clc_nop) as it seems to greatly improve the
1559 // chances of connecting to a netquake server
1560 sizebuf_t msg;
1561 unsigned char buf[4];
1562 memset(&msg, 0, sizeof(msg));
1563 msg.data = buf;
1564 msg.maxsize = sizeof(buf);
1565 MSG_WriteChar(&msg, clc_nop);
1566 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1567 }
1568 }
1569
NetConn_IsLocalGame(void)1570 int NetConn_IsLocalGame(void)
1571 {
1572 if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1573 return true;
1574 return false;
1575 }
1576
1577 #ifdef CONFIG_MENU
NetConn_ClientParsePacket_ServerList_ProcessReply(const char * addressstring)1578 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1579 {
1580 int n;
1581 int pingtime;
1582 serverlist_entry_t *entry = NULL;
1583
1584 // search the cache for this server and update it
1585 for (n = 0;n < serverlist_cachecount;n++) {
1586 entry = &serverlist_cache[ n ];
1587 if (!strcmp(addressstring, entry->info.cname))
1588 break;
1589 }
1590
1591 if (n == serverlist_cachecount)
1592 {
1593 // LAN search doesnt require an answer from the master server so we wont
1594 // know the ping nor will it be initialized already...
1595
1596 // find a slot
1597 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1598 return -1;
1599
1600 if (serverlist_maxcachecount <= serverlist_cachecount)
1601 {
1602 serverlist_maxcachecount += 64;
1603 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1604 }
1605 entry = &serverlist_cache[n];
1606
1607 memset(entry, 0, sizeof(*entry));
1608 // store the data the engine cares about (address and ping)
1609 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1610 entry->info.ping = 100000;
1611 entry->querytime = realtime;
1612 // if not in the slist menu we should print the server to console
1613 if (serverlist_consoleoutput)
1614 Con_Printf("querying %s\n", addressstring);
1615 ++serverlist_cachecount;
1616 }
1617 // if this is the first reply from this server, count it as having replied
1618 pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1619 pingtime = bound(0, pingtime, 9999);
1620 if (entry->query == SQS_REFRESHING) {
1621 entry->info.ping = pingtime;
1622 entry->query = SQS_QUERIED;
1623 } else {
1624 // convert to unsigned to catch the -1
1625 // I still dont like this but its better than the old 10000 magic ping number - as in easier to type and read :( [11/8/2007 Black]
1626 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1627 serverreplycount++;
1628 }
1629
1630 // other server info is updated by the caller
1631 return n;
1632 }
1633
NetConn_ClientParsePacket_ServerList_UpdateCache(int n)1634 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1635 {
1636 serverlist_entry_t *entry = &serverlist_cache[n];
1637 serverlist_info_t *info = &entry->info;
1638 // update description strings for engine menu and console output
1639 dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
1640 dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1641 (
1642 info->gameversion != gameversion.integer
1643 &&
1644 !(
1645 gameversion_min.integer >= 0 // min/max range set by user/mod?
1646 && gameversion_max.integer >= 0
1647 && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1648 && gameversion_max.integer >= info->gameversion
1649 )
1650 ) ? '1' : '4',
1651 info->mod, info->map);
1652 if (entry->query == SQS_QUERIED)
1653 {
1654 if(!serverlist_paused)
1655 ServerList_ViewList_Remove(entry);
1656 }
1657 // if not in the slist menu we should print the server to console (if wanted)
1658 else if( serverlist_consoleoutput )
1659 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1660 // and finally, update the view set
1661 if(!serverlist_paused)
1662 ServerList_ViewList_Insert( entry );
1663 // update the entry's state
1664 serverlist_cache[n].query = SQS_QUERIED;
1665 }
1666
1667 // returns true, if it's sensible to continue the processing
NetConn_ClientParsePacket_ServerList_PrepareQuery(int protocol,const char * ipstring,qboolean isfavorite)1668 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1669 int n;
1670 serverlist_entry_t *entry;
1671
1672 // ignore the rest of the message if the serverlist is full
1673 if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1674 return false;
1675 // also ignore it if we have already queried it (other master server response)
1676 for( n = 0 ; n < serverlist_cachecount ; n++ )
1677 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1678 break;
1679
1680 if( n < serverlist_cachecount ) {
1681 // the entry has already been queried once or
1682 return true;
1683 }
1684
1685 if (serverlist_maxcachecount <= n)
1686 {
1687 serverlist_maxcachecount += 64;
1688 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1689 }
1690
1691 entry = &serverlist_cache[n];
1692
1693 memset(entry, 0, sizeof(*entry));
1694 entry->protocol = protocol;
1695 // store the data the engine cares about (address and ping)
1696 strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1697
1698 entry->info.isfavorite = isfavorite;
1699
1700 // no, then reset the ping right away
1701 entry->info.ping = -1;
1702 // we also want to increase the serverlist_cachecount then
1703 serverlist_cachecount++;
1704 serverquerycount++;
1705
1706 entry->query = SQS_QUERYING;
1707
1708 return true;
1709 }
1710
NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t * senderaddress,const unsigned char * data,int length,qboolean isextended)1711 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1712 {
1713 masterreplycount++;
1714 if (serverlist_consoleoutput)
1715 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1716 while (length >= 7)
1717 {
1718 char ipstring [128];
1719
1720 // IPv4 address
1721 if (data[0] == '\\')
1722 {
1723 unsigned short port = data[5] * 256 + data[6];
1724
1725 if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1726 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1727
1728 // move on to next address in packet
1729 data += 7;
1730 length -= 7;
1731 }
1732 // IPv6 address
1733 else if (data[0] == '/' && isextended && length >= 19)
1734 {
1735 unsigned short port = data[17] * 256 + data[18];
1736
1737 if (port != 0)
1738 {
1739 #ifdef WHY_JUST_WHY
1740 const char *ifname;
1741 char ifnamebuf[16];
1742
1743 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1744
1745 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1746 if (ifname != NULL)
1747 {
1748 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1749 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1750 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1751 ifname, port);
1752 }
1753 else
1754 #endif
1755 {
1756 dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1757 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1758 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1759 port);
1760 }
1761 }
1762
1763 // move on to next address in packet
1764 data += 19;
1765 length -= 19;
1766 }
1767 else
1768 {
1769 Con_Print("Error while parsing the server list\n");
1770 break;
1771 }
1772
1773 if (serverlist_consoleoutput && developer_networking.integer)
1774 Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1775
1776 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1777 break;
1778 }
1779
1780 }
1781
1782 // begin or resume serverlist queries
1783 serverlist_querysleep = false;
1784 serverlist_querywaittime = realtime + 3;
1785 }
1786 #endif
1787
NetConn_ClientParsePacket(lhnetsocket_t * mysocket,unsigned char * data,int length,lhnetaddress_t * peeraddress)1788 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1789 {
1790 qboolean fromserver;
1791 int ret, c;
1792 char *string, addressstring2[128];
1793 char stringbuf[16384];
1794 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1795 size_t sendlength;
1796 #ifdef CONFIG_MENU
1797 char infostringvalue[MAX_INPUTLINE];
1798 char ipstring[32];
1799 const char *s;
1800 #endif
1801
1802 // quakeworld ingame packet
1803 fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1804
1805 // convert the address to a string incase we need it
1806 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1807
1808 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1809 {
1810 // received a command string - strip off the packaging and put it
1811 // into our string buffer with NULL termination
1812 data += 4;
1813 length -= 4;
1814 length = min(length, (int)sizeof(stringbuf) - 1);
1815 memcpy(stringbuf, data, length);
1816 stringbuf[length] = 0;
1817 string = stringbuf;
1818
1819 if (developer_networking.integer)
1820 {
1821 Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1822 Com_HexDumpToConsole(data, length);
1823 }
1824
1825 sendlength = sizeof(senddata) - 4;
1826 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
1827 {
1828 case CRYPTO_NOMATCH:
1829 // nothing to do
1830 break;
1831 case CRYPTO_MATCH:
1832 if(sendlength)
1833 {
1834 memcpy(senddata, "\377\377\377\377", 4);
1835 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1836 }
1837 break;
1838 case CRYPTO_DISCARD:
1839 if(sendlength)
1840 {
1841 memcpy(senddata, "\377\377\377\377", 4);
1842 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
1843 }
1844 return true;
1845 break;
1846 case CRYPTO_REPLACE:
1847 string = senddata+4;
1848 length = (int)sendlength;
1849 break;
1850 }
1851
1852 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1853 {
1854 int i = 0, j;
1855 for (j = 0;j < MAX_RCONS;j++)
1856 {
1857 // note: this value from i is used outside the loop too...
1858 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1859 if(cls.rcon_commands[i][0])
1860 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1861 break;
1862 }
1863 if (j < MAX_RCONS)
1864 {
1865 char buf[1500];
1866 char argbuf[1500];
1867 const char *e;
1868 int n;
1869 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1870 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1871
1872 e = strchr(rcon_password.string, ' ');
1873 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1874
1875 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
1876 {
1877 int k;
1878 buf[45] = ' ';
1879 strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1880 NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
1881 cls.rcon_commands[i][0] = 0;
1882 --cls.rcon_trying;
1883
1884 for (k = 0;k < MAX_RCONS;k++)
1885 if(cls.rcon_commands[k][0])
1886 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
1887 break;
1888 if(k < MAX_RCONS)
1889 {
1890 int l;
1891 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1892 // extend the timeout on other requests as we asked for a challenge
1893 for (l = 0;l < MAX_RCONS;l++)
1894 if(cls.rcon_commands[l][0])
1895 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
1896 cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
1897 }
1898
1899 return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1900 }
1901 }
1902 }
1903 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1904 {
1905 // darkplaces or quake3
1906 char protocolnames[1400];
1907 Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1908 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1909 Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
1910 return true;
1911 }
1912 Protocol_Names(protocolnames, sizeof(protocolnames));
1913 #ifdef CONFIG_MENU
1914 M_Update_Return_Reason("Got challenge response");
1915 #endif
1916 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1917 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1918 // TODO: add userinfo stuff here instead of using NQ commands?
1919 memcpy(senddata, "\377\377\377\377", 4);
1920 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
1921 NetConn_WriteString(mysocket, senddata, peeraddress);
1922 return true;
1923 }
1924 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1925 {
1926 // darkplaces or quake3
1927 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1928 Con_DPrintf("accept message from wrong server %s\n", addressstring2);
1929 return true;
1930 }
1931 #ifdef CONFIG_MENU
1932 M_Update_Return_Reason("Accepted");
1933 #endif
1934 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1935 return true;
1936 }
1937 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1938 {
1939 char rejectreason[128];
1940 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
1941 Con_DPrintf("reject message from wrong server %s\n", addressstring2);
1942 return true;
1943 }
1944 cls.connect_trying = false;
1945 string += 7;
1946 length = min(length - 7, (int)sizeof(rejectreason) - 1);
1947 memcpy(rejectreason, string, length);
1948 rejectreason[length] = 0;
1949 #ifdef CONFIG_MENU
1950 M_Update_Return_Reason(rejectreason);
1951 #endif
1952 return true;
1953 }
1954 #ifdef CONFIG_MENU
1955 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1956 {
1957 serverlist_info_t *info;
1958 char *p;
1959 int n;
1960
1961 string += 15;
1962 // search the cache for this server and update it
1963 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1964 if (n < 0)
1965 return true;
1966
1967 info = &serverlist_cache[n].info;
1968 info->game[0] = 0;
1969 info->mod[0] = 0;
1970 info->map[0] = 0;
1971 info->name[0] = 0;
1972 info->qcstatus[0] = 0;
1973 info->players[0] = 0;
1974 info->protocol = -1;
1975 info->numplayers = 0;
1976 info->numbots = -1;
1977 info->maxplayers = 0;
1978 info->gameversion = 0;
1979
1980 p = strchr(string, '\n');
1981 if(p)
1982 {
1983 *p = 0; // cut off the string there
1984 ++p;
1985 }
1986 else
1987 Con_Printf("statusResponse without players block?\n");
1988
1989 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
1990 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1991 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1992 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
1993 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
1994 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
1995 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
1996 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
1997 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
1998 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1999 if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
2000 info->numhumans = info->numplayers - max(0, info->numbots);
2001 info->freeslots = info->maxplayers - info->numplayers;
2002
2003 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2004
2005 return true;
2006 }
2007 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2008 {
2009 serverlist_info_t *info;
2010 int n;
2011
2012 string += 13;
2013 // search the cache for this server and update it
2014 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2015 if (n < 0)
2016 return true;
2017
2018 info = &serverlist_cache[n].info;
2019 info->game[0] = 0;
2020 info->mod[0] = 0;
2021 info->map[0] = 0;
2022 info->name[0] = 0;
2023 info->qcstatus[0] = 0;
2024 info->players[0] = 0;
2025 info->protocol = -1;
2026 info->numplayers = 0;
2027 info->numbots = -1;
2028 info->maxplayers = 0;
2029 info->gameversion = 0;
2030
2031 if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2032 if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2033 if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2034 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2035 if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2036 if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2037 if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2038 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2039 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2040 if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2041 info->numhumans = info->numplayers - max(0, info->numbots);
2042 info->freeslots = info->maxplayers - info->numplayers;
2043
2044 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2045
2046 return true;
2047 }
2048 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2049 {
2050 // Extract the IP addresses
2051 data += 18;
2052 length -= 18;
2053 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
2054 return true;
2055 }
2056 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2057 {
2058 // Extract the IP addresses
2059 data += 21;
2060 length -= 21;
2061 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
2062 return true;
2063 }
2064 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2065 {
2066 // Extract the IP addresses
2067 data += 2;
2068 length -= 2;
2069 masterreplycount++;
2070 if (serverlist_consoleoutput)
2071 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
2072 while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
2073 {
2074 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
2075 if (serverlist_consoleoutput && developer_networking.integer)
2076 Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
2077
2078 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
2079 break;
2080 }
2081
2082 // move on to next address in packet
2083 data += 6;
2084 length -= 6;
2085 }
2086 // begin or resume serverlist queries
2087 serverlist_querysleep = false;
2088 serverlist_querywaittime = realtime + 3;
2089 return true;
2090 }
2091 #endif
2092 if (!strncmp(string, "extResponse ", 12))
2093 {
2094 ++cl_net_extresponse_count;
2095 if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2096 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2097 cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2098 dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2099 return true;
2100 }
2101 if (!strncmp(string, "ping", 4))
2102 {
2103 if (developer_extra.integer)
2104 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2105 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2106 return true;
2107 }
2108 if (!strncmp(string, "ack", 3))
2109 return true;
2110 // QuakeWorld compatibility
2111 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2112 {
2113 // challenge message
2114 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2115 Con_DPrintf("c message from wrong server %s\n", addressstring2);
2116 return true;
2117 }
2118 Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2119 #ifdef CONFIG_MENU
2120 M_Update_Return_Reason("Got QuakeWorld challenge response");
2121 #endif
2122 cls.qw_qport = qport.integer;
2123 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2124 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2125 memcpy(senddata, "\377\377\377\377", 4);
2126 dpsnprintf(senddata+4, sizeof(senddata)-4, "connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo);
2127 NetConn_WriteString(mysocket, senddata, peeraddress);
2128 return true;
2129 }
2130 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2131 {
2132 // accept message
2133 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2134 Con_DPrintf("j message from wrong server %s\n", addressstring2);
2135 return true;
2136 }
2137 #ifdef CONFIG_MENU
2138 M_Update_Return_Reason("QuakeWorld Accepted");
2139 #endif
2140 NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2141 return true;
2142 }
2143 if (length > 2 && !memcmp(string, "n\\", 2))
2144 {
2145 #ifdef CONFIG_MENU
2146 serverlist_info_t *info;
2147 int n;
2148
2149 // qw server status
2150 if (serverlist_consoleoutput && developer_networking.integer >= 2)
2151 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2152
2153 string += 1;
2154 // search the cache for this server and update it
2155 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2156 if (n < 0)
2157 return true;
2158
2159 info = &serverlist_cache[n].info;
2160 strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2161 if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
2162 if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
2163 if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2164 info->protocol = 0;
2165 info->numplayers = 0; // updated below
2166 info->numhumans = 0; // updated below
2167 if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
2168 if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2169
2170 // count active players on server
2171 // (we could gather more info, but we're just after the number)
2172 s = strchr(string, '\n');
2173 if (s)
2174 {
2175 s++;
2176 while (s < string + length)
2177 {
2178 for (;s < string + length && *s != '\n';s++)
2179 ;
2180 if (s >= string + length)
2181 break;
2182 info->numplayers++;
2183 info->numhumans++;
2184 s++;
2185 }
2186 }
2187
2188 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2189 #endif
2190 return true;
2191 }
2192 if (string[0] == 'n')
2193 {
2194 // qw print command, used by rcon replies too
2195 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2196 Con_DPrintf("n message from wrong server %s\n", addressstring2);
2197 return true;
2198 }
2199 Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2200 }
2201 // we may not have liked the packet, but it was a command packet, so
2202 // we're done processing this packet now
2203 return true;
2204 }
2205 // quakeworld ingame packet
2206 if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2207 {
2208 ret = 0;
2209 CL_ParseServerMessage();
2210 return ret;
2211 }
2212 // netquake control packets, supported for compatibility only
2213 if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2214 {
2215 #ifdef CONFIG_MENU
2216 int n;
2217 serverlist_info_t *info;
2218 #endif
2219
2220 data += 4;
2221 length -= 4;
2222 SZ_Clear(&cl_message);
2223 SZ_Write(&cl_message, data, length);
2224 MSG_BeginReading(&cl_message);
2225 c = MSG_ReadByte(&cl_message);
2226 switch (c)
2227 {
2228 case CCREP_ACCEPT:
2229 if (developer_extra.integer)
2230 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2231 if (cls.connect_trying)
2232 {
2233 lhnetaddress_t clientportaddress;
2234 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2235 Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2236 break;
2237 }
2238 clientportaddress = *peeraddress;
2239 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2240 // extra ProQuake stuff
2241 if (length >= 6)
2242 cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2243 else
2244 cls.proquake_servermod = 0;
2245 if (length >= 7)
2246 cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2247 else
2248 cls.proquake_serverversion = 0;
2249 if (length >= 8)
2250 cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2251 else
2252 cls.proquake_serverflags = 0;
2253 if (cls.proquake_servermod == 1)
2254 Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2255 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2256 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2257 #ifdef CONFIG_MENU
2258 M_Update_Return_Reason("Accepted");
2259 #endif
2260 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2261 }
2262 break;
2263 case CCREP_REJECT:
2264 if (developer_extra.integer) {
2265 Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
2266 break;
2267 }
2268 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2269 break;
2270 cls.connect_trying = false;
2271 #ifdef CONFIG_MENU
2272 M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2273 #endif
2274 break;
2275 case CCREP_SERVER_INFO:
2276 if (developer_extra.integer)
2277 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2278 #ifdef CONFIG_MENU
2279 // LordHavoc: because the quake server may report weird addresses
2280 // we just ignore it and keep the real address
2281 MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2282 // search the cache for this server and update it
2283 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
2284 if (n < 0)
2285 break;
2286
2287 info = &serverlist_cache[n].info;
2288 strlcpy(info->game, "Quake", sizeof(info->game));
2289 strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2290 strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2291 strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2292 info->numplayers = MSG_ReadByte(&cl_message);
2293 info->maxplayers = MSG_ReadByte(&cl_message);
2294 info->protocol = MSG_ReadByte(&cl_message);
2295
2296 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2297 #endif
2298 break;
2299 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2300 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2301 Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
2302 break;
2303 }
2304 if (developer_extra.integer)
2305 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2306
2307 Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2308 break;
2309 case CCREP_PLAYER_INFO:
2310 // we got a CCREP_PLAYER_INFO??
2311 //if (developer_extra.integer)
2312 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2313 break;
2314 case CCREP_RULE_INFO:
2315 // we got a CCREP_RULE_INFO??
2316 //if (developer_extra.integer)
2317 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2318 break;
2319 default:
2320 break;
2321 }
2322 SZ_Clear(&cl_message);
2323 // we may not have liked the packet, but it was a valid control
2324 // packet, so we're done processing this packet now
2325 return true;
2326 }
2327 ret = 0;
2328 if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2329 CL_ParseServerMessage();
2330 return ret;
2331 }
2332
2333 #ifdef CONFIG_MENU
NetConn_QueryQueueFrame(void)2334 void NetConn_QueryQueueFrame(void)
2335 {
2336 int index;
2337 int queries;
2338 int maxqueries;
2339 double timeouttime;
2340 static double querycounter = 0;
2341
2342 if(!net_slist_pause.integer && serverlist_paused)
2343 ServerList_RebuildViewList();
2344 serverlist_paused = net_slist_pause.integer != 0;
2345
2346 if (serverlist_querysleep)
2347 return;
2348
2349 // apply a cool down time after master server replies,
2350 // to avoid messing up the ping times on the servers
2351 if (serverlist_querywaittime > realtime)
2352 return;
2353
2354 // each time querycounter reaches 1.0 issue a query
2355 querycounter += cl.realframetime * net_slist_queriespersecond.value;
2356 maxqueries = (int)querycounter;
2357 maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2358 querycounter -= maxqueries;
2359
2360 if( maxqueries == 0 ) {
2361 return;
2362 }
2363
2364 // scan serverlist and issue queries as needed
2365 serverlist_querysleep = true;
2366
2367 timeouttime = realtime - net_slist_timeout.value;
2368 for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
2369 {
2370 serverlist_entry_t *entry = &serverlist_cache[ index ];
2371 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2372 {
2373 continue;
2374 }
2375
2376 serverlist_querysleep = false;
2377 if( entry->querycounter != 0 && entry->querytime > timeouttime )
2378 {
2379 continue;
2380 }
2381
2382 if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
2383 {
2384 lhnetaddress_t address;
2385 int socket;
2386
2387 LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2388 if (entry->protocol == PROTOCOL_QUAKEWORLD)
2389 {
2390 for (socket = 0; socket < cl_numsockets ; socket++)
2391 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2392 }
2393 else
2394 {
2395 for (socket = 0; socket < cl_numsockets ; socket++)
2396 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2397 }
2398
2399 // update the entry fields
2400 entry->querytime = realtime;
2401 entry->querycounter++;
2402
2403 // if not in the slist menu we should print the server to console
2404 if (serverlist_consoleoutput)
2405 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2406
2407 queries++;
2408 }
2409 else
2410 {
2411 // have we tried to refresh this server?
2412 if( entry->query == SQS_REFRESHING ) {
2413 // yes, so update the reply count (since its not responding anymore)
2414 serverreplycount--;
2415 if(!serverlist_paused)
2416 ServerList_ViewList_Remove(entry);
2417 }
2418 entry->query = SQS_TIMEDOUT;
2419 }
2420 }
2421 }
2422 #endif
2423
NetConn_ClientFrame(void)2424 void NetConn_ClientFrame(void)
2425 {
2426 int i, length;
2427 lhnetaddress_t peeraddress;
2428 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2429 NetConn_UpdateSockets();
2430 if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2431 {
2432 #ifdef CONFIG_MENU
2433 if (cls.connect_remainingtries == 0)
2434 M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2435 #endif
2436 cls.connect_nextsendtime = realtime + 1;
2437 cls.connect_remainingtries--;
2438 if (cls.connect_remainingtries <= -10)
2439 {
2440 cls.connect_trying = false;
2441 #ifdef CONFIG_MENU
2442 M_Update_Return_Reason("Connect: Failed");
2443 #endif
2444 return;
2445 }
2446 // try challenge first (newer DP server or QW)
2447 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2448 // then try netquake as a fallback (old server, or netquake)
2449 SZ_Clear(&cl_message);
2450 // save space for the header, filled in later
2451 MSG_WriteLong(&cl_message, 0);
2452 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2453 MSG_WriteString(&cl_message, "QUAKE");
2454 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2455 // extended proquake stuff
2456 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2457 // this version matches ProQuake 3.40, the first version to support
2458 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2459 // higher clients, so we pretend we are that version...
2460 MSG_WriteByte(&cl_message, 34); // version * 10
2461 MSG_WriteByte(&cl_message, 0); // flags
2462 MSG_WriteLong(&cl_message, 0); // password
2463 // write the packetsize now...
2464 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2465 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2466 SZ_Clear(&cl_message);
2467 }
2468 for (i = 0;i < cl_numsockets;i++)
2469 {
2470 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2471 {
2472 // R_TimeReport("clientreadnetwork");
2473 NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2474 // R_TimeReport("clientparsepacket");
2475 }
2476 }
2477 #ifdef CONFIG_MENU
2478 NetConn_QueryQueueFrame();
2479 #endif
2480 if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2481 {
2482 Con_Print("Connection timed out\n");
2483 CL_Disconnect();
2484 SV_LockThreadMutex();
2485 Host_ShutdownServer ();
2486 SV_UnlockThreadMutex();
2487 }
2488 }
2489
NetConn_BuildChallengeString(char * buffer,int bufferlength)2490 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2491 {
2492 int i;
2493 char c;
2494 for (i = 0;i < bufferlength - 1;i++)
2495 {
2496 do
2497 {
2498 c = rand () % (127 - 33) + 33;
2499 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2500 buffer[i] = c;
2501 }
2502 buffer[i] = 0;
2503 }
2504
2505 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
NetConn_BuildStatusResponse(const char * challenge,char * out_msg,size_t out_size,qboolean fullstatus)2506 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2507 {
2508 prvm_prog_t *prog = SVVM_prog;
2509 char qcstatus[256];
2510 unsigned int nb_clients = 0, nb_bots = 0, i;
2511 int length;
2512 char teambuf[3];
2513 const char *crypto_idstring;
2514 const char *worldstatusstr;
2515
2516 // How many clients are there?
2517 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2518 {
2519 if (svs.clients[i].active)
2520 {
2521 nb_clients++;
2522 if (!svs.clients[i].netconnection)
2523 nb_bots++;
2524 }
2525 }
2526
2527 *qcstatus = 0;
2528 worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2529 if(worldstatusstr && *worldstatusstr)
2530 {
2531 char *p;
2532 const char *q;
2533 p = qcstatus;
2534 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2535 if(*q != '\\' && *q != '\n')
2536 *p++ = *q;
2537 *p = 0;
2538 }
2539
2540 /// \TODO: we should add more information for the full status string
2541 crypto_idstring = Crypto_GetInfoResponseDataString();
2542 length = dpsnprintf(out_msg, out_size,
2543 "\377\377\377\377%s\x0A"
2544 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2545 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2546 "%s%s"
2547 "%s%s"
2548 "%s%s"
2549 "%s",
2550 fullstatus ? "statusResponse" : "infoResponse",
2551 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2552 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2553 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2554 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2555 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2556 fullstatus ? "\n" : "");
2557
2558 // Make sure it fits in the buffer
2559 if (length < 0)
2560 goto bad;
2561
2562 if (fullstatus)
2563 {
2564 char *ptr;
2565 int left;
2566 int savelength;
2567
2568 savelength = length;
2569
2570 ptr = out_msg + length;
2571 left = (int)out_size - length;
2572
2573 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2574 {
2575 client_t *client = &svs.clients[i];
2576 if (client->active)
2577 {
2578 int nameind, cleanind, pingvalue;
2579 char curchar;
2580 char cleanname [sizeof(client->name)];
2581 const char *statusstr;
2582 prvm_edict_t *ed;
2583
2584 // Remove all characters '"' and '\' in the player name
2585 nameind = 0;
2586 cleanind = 0;
2587 do
2588 {
2589 curchar = client->name[nameind++];
2590 if (curchar != '"' && curchar != '\\')
2591 {
2592 cleanname[cleanind++] = curchar;
2593 if (cleanind == sizeof(cleanname) - 1)
2594 break;
2595 }
2596 } while (curchar != '\0');
2597 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2598
2599 pingvalue = (int)(client->ping * 1000.0f);
2600 if(client->netconnection)
2601 pingvalue = bound(1, pingvalue, 9999);
2602 else
2603 pingvalue = 0;
2604
2605 *qcstatus = 0;
2606 ed = PRVM_EDICT_NUM(i + 1);
2607 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2608 if(statusstr && *statusstr)
2609 {
2610 char *p;
2611 const char *q;
2612 p = qcstatus;
2613 for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2614 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2615 *p++ = *q;
2616 *p = 0;
2617 }
2618
2619 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2620 {
2621 if(client->frags == -666) // spectator
2622 strlcpy(teambuf, " 0", sizeof(teambuf));
2623 else if(client->colors == 0x44) // red team
2624 strlcpy(teambuf, " 1", sizeof(teambuf));
2625 else if(client->colors == 0xDD) // blue team
2626 strlcpy(teambuf, " 2", sizeof(teambuf));
2627 else if(client->colors == 0xCC) // yellow team
2628 strlcpy(teambuf, " 3", sizeof(teambuf));
2629 else if(client->colors == 0x99) // pink team
2630 strlcpy(teambuf, " 4", sizeof(teambuf));
2631 else
2632 strlcpy(teambuf, " 0", sizeof(teambuf));
2633 }
2634 else
2635 *teambuf = 0;
2636
2637 // note: team number is inserted according to SoF2 protocol
2638 if(*qcstatus)
2639 length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2640 qcstatus,
2641 pingvalue,
2642 teambuf,
2643 cleanname);
2644 else
2645 length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2646 client->frags,
2647 pingvalue,
2648 teambuf,
2649 cleanname);
2650
2651 if(length < 0)
2652 {
2653 // out of space?
2654 // turn it into an infoResponse!
2655 out_msg[savelength] = 0;
2656 memcpy(out_msg + 4, "infoResponse\x0A", 13);
2657 memmove(out_msg + 17, out_msg + 19, savelength - 19);
2658 break;
2659 }
2660 left -= length;
2661 ptr += length;
2662 }
2663 }
2664 }
2665
2666 return true;
2667
2668 bad:
2669 return false;
2670 }
2671
NetConn_PreventFlood(lhnetaddress_t * peeraddress,server_floodaddress_t * floodlist,size_t floodlength,double floodtime,qboolean renew)2672 static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
2673 {
2674 size_t floodslotnum, bestfloodslotnum;
2675 double bestfloodtime;
2676 lhnetaddress_t noportpeeraddress;
2677 // see if this is a connect flood
2678 noportpeeraddress = *peeraddress;
2679 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2680 bestfloodslotnum = 0;
2681 bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2682 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2683 {
2684 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2685 {
2686 bestfloodtime = floodlist[floodslotnum].lasttime;
2687 bestfloodslotnum = floodslotnum;
2688 }
2689 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2690 {
2691 // this address matches an ongoing flood address
2692 if (realtime < floodlist[floodslotnum].lasttime + floodtime)
2693 {
2694 if(renew)
2695 {
2696 // renew the ban on this address so it does not expire
2697 // until the flood has subsided
2698 floodlist[floodslotnum].lasttime = realtime;
2699 }
2700 //Con_Printf("Flood detected!\n");
2701 return true;
2702 }
2703 // the flood appears to have subsided, so allow this
2704 bestfloodslotnum = floodslotnum; // reuse the same slot
2705 break;
2706 }
2707 }
2708 // begin a new timeout on this address
2709 floodlist[bestfloodslotnum].address = noportpeeraddress;
2710 floodlist[bestfloodslotnum].lasttime = realtime;
2711 //Con_Printf("Flood detection initiated!\n");
2712 return false;
2713 }
2714
NetConn_ClearFlood(lhnetaddress_t * peeraddress,server_floodaddress_t * floodlist,size_t floodlength)2715 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2716 {
2717 size_t floodslotnum;
2718 lhnetaddress_t noportpeeraddress;
2719 // see if this is a connect flood
2720 noportpeeraddress = *peeraddress;
2721 LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2722 for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2723 {
2724 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2725 {
2726 // this address matches an ongoing flood address
2727 // remove the ban
2728 floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2729 floodlist[floodslotnum].lasttime = 0;
2730 //Con_Printf("Flood cleared!\n");
2731 }
2732 }
2733 }
2734
2735 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2736
hmac_mdfour_time_matching(lhnetaddress_t * peeraddress,const char * password,const char * hash,const char * s,int slen)2737 static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2738 {
2739 char mdfourbuf[16];
2740 long t1, t2;
2741
2742 t1 = (long) time(NULL);
2743 t2 = strtol(s, NULL, 0);
2744 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2745 return false;
2746
2747 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2748 return false;
2749
2750 return !memcmp(mdfourbuf, hash, 16);
2751 }
2752
hmac_mdfour_challenge_matching(lhnetaddress_t * peeraddress,const char * password,const char * hash,const char * s,int slen)2753 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2754 {
2755 char mdfourbuf[16];
2756 int i;
2757
2758 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2759 return false;
2760
2761 // validate the challenge
2762 for (i = 0;i < MAX_CHALLENGES;i++)
2763 if(challenges[i].time > 0)
2764 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2765 break;
2766 // if the challenge is not recognized, drop the packet
2767 if (i == MAX_CHALLENGES)
2768 return false;
2769
2770 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2771 return false;
2772
2773 if(memcmp(mdfourbuf, hash, 16))
2774 return false;
2775
2776 // unmark challenge to prevent replay attacks
2777 challenges[i].time = 0;
2778
2779 return true;
2780 }
2781
plaintext_matching(lhnetaddress_t * peeraddress,const char * password,const char * hash,const char * s,int slen)2782 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2783 {
2784 return !strcmp(password, hash);
2785 }
2786
2787 /// returns a string describing the user level, or NULL for auth failure
RCon_Authenticate(lhnetaddress_t * peeraddress,const char * password,const char * s,const char * endpos,rcon_matchfunc_t comparator,const char * cs,int cslen)2788 static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
2789 {
2790 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2791 static char buf[MAX_INPUTLINE];
2792 qboolean hasquotes;
2793 qboolean restricted = false;
2794 qboolean have_usernames = false;
2795 static char vabuf[1024];
2796
2797 userpass_start = rcon_password.string;
2798 while((userpass_end = strchr(userpass_start, ' ')))
2799 {
2800 have_usernames = true;
2801 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2802 if(buf[0])
2803 if(comparator(peeraddress, buf, password, cs, cslen))
2804 goto allow;
2805 userpass_start = userpass_end + 1;
2806 }
2807 if(userpass_start[0])
2808 {
2809 userpass_end = userpass_start + strlen(userpass_start);
2810 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2811 goto allow;
2812 }
2813
2814 restricted = true;
2815 have_usernames = false;
2816 userpass_start = rcon_restricted_password.string;
2817 while((userpass_end = strchr(userpass_start, ' ')))
2818 {
2819 have_usernames = true;
2820 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2821 if(buf[0])
2822 if(comparator(peeraddress, buf, password, cs, cslen))
2823 goto check;
2824 userpass_start = userpass_end + 1;
2825 }
2826 if(userpass_start[0])
2827 {
2828 userpass_end = userpass_start + strlen(userpass_start);
2829 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2830 goto check;
2831 }
2832
2833 return NULL; // DENIED
2834
2835 check:
2836 for(text = s; text != endpos; ++text)
2837 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2838 return NULL; // block possible exploits against the parser/alias expansion
2839
2840 while(s != endpos)
2841 {
2842 size_t l = strlen(s);
2843 if(l)
2844 {
2845 hasquotes = (strchr(s, '"') != NULL);
2846 // sorry, we can't allow these substrings in wildcard expressions,
2847 // as they can mess with the argument counts
2848 text = rcon_restricted_commands.string;
2849 while(COM_ParseToken_Console(&text))
2850 {
2851 // com_token now contains a pattern to check for...
2852 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2853 {
2854 if(!hasquotes)
2855 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2856 goto match;
2857 }
2858 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2859 {
2860 if(!strcmp(com_token, s))
2861 goto match;
2862 }
2863 else // single-arg expression? must match the beginning of the command
2864 {
2865 if(!strcmp(com_token, s))
2866 goto match;
2867 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2868 goto match;
2869 }
2870 }
2871 // if we got here, nothing matched!
2872 return NULL;
2873 }
2874 match:
2875 s += l + 1;
2876 }
2877
2878 allow:
2879 userpass_startpass = strchr(userpass_start, ':');
2880 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2881 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2882
2883 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2884 }
2885
RCon_Execute(lhnetsocket_t * mysocket,lhnetaddress_t * peeraddress,const char * addressstring2,const char * userlevel,const char * s,const char * endpos,qboolean proquakeprotocol)2886 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2887 {
2888 if(userlevel)
2889 {
2890 // looks like a legitimate rcon command with the correct password
2891 const char *s_ptr = s;
2892 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2893 while(s_ptr != endpos)
2894 {
2895 size_t l = strlen(s_ptr);
2896 if(l)
2897 Con_Printf(" %s;", s_ptr);
2898 s_ptr += l + 1;
2899 }
2900 Con_Printf("\n");
2901
2902 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2903 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2904 while(s != endpos)
2905 {
2906 size_t l = strlen(s);
2907 if(l)
2908 {
2909 client_t *host_client_save = host_client;
2910 Cmd_ExecuteString(s, src_command, true);
2911 host_client = host_client_save;
2912 // in case it is a command that changes host_client (like restart)
2913 }
2914 s += l + 1;
2915 }
2916 Con_Rcon_Redirect_End();
2917 }
2918 else
2919 {
2920 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2921 }
2922 }
2923
NetConn_ServerParsePacket(lhnetsocket_t * mysocket,unsigned char * data,int length,lhnetaddress_t * peeraddress)2924 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2925 {
2926 int i, ret, clientnum, best;
2927 double besttime;
2928 char *string, response[1400], addressstring2[128];
2929 static char stringbuf[16384]; // server only
2930 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2931 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2932 size_t sendlength, response_len;
2933 char infostringvalue[MAX_INPUTLINE];
2934
2935 if (!sv.active)
2936 return false;
2937
2938 // convert the address to a string incase we need it
2939 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2940
2941 // see if we can identify the sender as a local player
2942 // (this is necessary for rcon to send a reliable reply if the client is
2943 // actually on the server, not sending remotely)
2944 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2945 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2946 break;
2947 if (i == svs.maxclients)
2948 host_client = NULL;
2949
2950 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2951 {
2952 // received a command string - strip off the packaging and put it
2953 // into our string buffer with NULL termination
2954 data += 4;
2955 length -= 4;
2956 length = min(length, (int)sizeof(stringbuf) - 1);
2957 memcpy(stringbuf, data, length);
2958 stringbuf[length] = 0;
2959 string = stringbuf;
2960
2961 if (developer_extra.integer)
2962 {
2963 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2964 Com_HexDumpToConsole(data, length);
2965 }
2966
2967 sendlength = sizeof(senddata) - 4;
2968 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2969 {
2970 case CRYPTO_NOMATCH:
2971 // nothing to do
2972 break;
2973 case CRYPTO_MATCH:
2974 if(sendlength)
2975 {
2976 memcpy(senddata, "\377\377\377\377", 4);
2977 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2978 }
2979 break;
2980 case CRYPTO_DISCARD:
2981 if(sendlength)
2982 {
2983 memcpy(senddata, "\377\377\377\377", 4);
2984 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2985 }
2986 return true;
2987 break;
2988 case CRYPTO_REPLACE:
2989 string = senddata+4;
2990 length = (int)sendlength;
2991 break;
2992 }
2993
2994 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
2995 {
2996 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2997 {
2998 if(challenges[i].time > 0)
2999 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3000 break;
3001 if (besttime > challenges[i].time)
3002 besttime = challenges[best = i].time;
3003 }
3004 // if we did not find an exact match, choose the oldest and
3005 // update address and string
3006 if (i == MAX_CHALLENGES)
3007 {
3008 i = best;
3009 challenges[i].address = *peeraddress;
3010 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3011 }
3012 else
3013 {
3014 // flood control: drop if requesting challenge too often
3015 if(challenges[i].time > realtime - net_challengefloodblockingtimeout.value)
3016 return true;
3017 }
3018 challenges[i].time = realtime;
3019 // send the challenge
3020 memcpy(response, "\377\377\377\377", 4);
3021 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3022 response_len = strlen(response) + 1;
3023 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3024 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3025 return true;
3026 }
3027 if (length > 8 && !memcmp(string, "connect\\", 8))
3028 {
3029 char *s;
3030 client_t *client;
3031 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3032 string += 7;
3033 length -= 7;
3034
3035 if(crypto && crypto->authenticated)
3036 {
3037 // no need to check challenge
3038 if(crypto_developer.integer)
3039 {
3040 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3041 crypto->use_aes ? "Encrypted" : "Authenticated",
3042 addressstring2,
3043 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3044 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3045 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3046 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3047 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3048 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3049 );
3050 }
3051 }
3052 else
3053 {
3054 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3055 {
3056 // validate the challenge
3057 for (i = 0;i < MAX_CHALLENGES;i++)
3058 if(challenges[i].time > 0)
3059 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3060 break;
3061 // if the challenge is not recognized, drop the packet
3062 if (i == MAX_CHALLENGES)
3063 return true;
3064 }
3065 }
3066
3067 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3068 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3069
3070 if(!(islocal || sv_public.integer > -2))
3071 {
3072 if (developer_extra.integer)
3073 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3074 memcpy(response, "\377\377\377\377", 4);
3075 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3076 NetConn_WriteString(mysocket, response, peeraddress);
3077 return true;
3078 }
3079
3080 // check engine protocol
3081 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3082 {
3083 if (developer_extra.integer)
3084 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3085 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3086 return true;
3087 }
3088
3089 // see if this is a duplicate connection request or a disconnected
3090 // client who is rejoining to the same client slot
3091 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3092 {
3093 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3094 {
3095 // this is a known client...
3096 if(crypto && crypto->authenticated)
3097 {
3098 // reject if changing key!
3099 if(client->netconnection->crypto.authenticated)
3100 {
3101 if(
3102 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3103 ||
3104 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3105 ||
3106 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3107 ||
3108 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3109 )
3110 {
3111 if (developer_extra.integer)
3112 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3113 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3114 return true;
3115 }
3116 }
3117 }
3118 else
3119 {
3120 // reject if downgrading!
3121 if(client->netconnection->crypto.authenticated)
3122 {
3123 if (developer_extra.integer)
3124 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3125 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3126 return true;
3127 }
3128 }
3129 if (client->begun)
3130 {
3131 // client crashed and is coming back,
3132 // keep their stuff intact
3133 if (developer_extra.integer)
3134 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3135 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3136 if(crypto && crypto->authenticated)
3137 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3138 SV_SendServerinfo(client);
3139 }
3140 else
3141 {
3142 // client is still trying to connect,
3143 // so we send a duplicate reply
3144 if (developer_extra.integer)
3145 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3146 if(crypto && crypto->authenticated)
3147 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3148 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3149 }
3150 return true;
3151 }
3152 }
3153
3154 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3155 return true;
3156
3157 // find an empty client slot for this new client
3158 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3159 {
3160 netconn_t *conn;
3161 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3162 {
3163 // allocated connection
3164 if (developer_extra.integer)
3165 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3166 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3167 // now set up the client
3168 if(crypto && crypto->authenticated)
3169 Crypto_FinishInstance(&conn->crypto, crypto);
3170 SV_ConnectClient(clientnum, conn);
3171 NetConn_Heartbeat(1);
3172 return true;
3173 }
3174 }
3175
3176 // no empty slots found - server is full
3177 if (developer_extra.integer)
3178 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3179 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3180
3181 return true;
3182 }
3183 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3184 {
3185 const char *challenge = NULL;
3186
3187 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3188 return true;
3189
3190 // If there was a challenge in the getinfo message
3191 if (length > 8 && string[7] == ' ')
3192 challenge = string + 8;
3193
3194 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3195 {
3196 if (developer_extra.integer)
3197 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3198 NetConn_WriteString(mysocket, response, peeraddress);
3199 }
3200 return true;
3201 }
3202 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3203 {
3204 const char *challenge = NULL;
3205
3206 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3207 return true;
3208
3209 // If there was a challenge in the getinfo message
3210 if (length > 10 && string[9] == ' ')
3211 challenge = string + 10;
3212
3213 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3214 {
3215 if (developer_extra.integer)
3216 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3217 NetConn_WriteString(mysocket, response, peeraddress);
3218 }
3219 return true;
3220 }
3221 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3222 {
3223 char *password = string + 20;
3224 char *timeval = string + 37;
3225 char *s = strchr(timeval, ' ');
3226 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3227 const char *userlevel;
3228
3229 if(rcon_secure.integer > 1)
3230 return true;
3231
3232 if(!s)
3233 return true; // invalid packet
3234 ++s;
3235
3236 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3237 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3238 return true;
3239 }
3240 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3241 {
3242 char *password = string + 25;
3243 char *challenge = string + 42;
3244 char *s = strchr(challenge, ' ');
3245 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3246 const char *userlevel;
3247 if(!s)
3248 return true; // invalid packet
3249 ++s;
3250
3251 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3252 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3253 return true;
3254 }
3255 if (length >= 5 && !memcmp(string, "rcon ", 5))
3256 {
3257 int j;
3258 char *s = string + 5;
3259 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3260 char password[64];
3261
3262 if(rcon_secure.integer > 0)
3263 return true;
3264
3265 for (j = 0;!ISWHITESPACE(*s);s++)
3266 if (j < (int)sizeof(password) - 1)
3267 password[j++] = *s;
3268 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3269 ++s;
3270 password[j] = 0;
3271 if (!ISWHITESPACE(password[0]))
3272 {
3273 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3274 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3275 }
3276 return true;
3277 }
3278 if (!strncmp(string, "extResponse ", 12))
3279 {
3280 ++sv_net_extresponse_count;
3281 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3282 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3283 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3284 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3285 return true;
3286 }
3287 if (!strncmp(string, "ping", 4))
3288 {
3289 if (developer_extra.integer)
3290 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3291 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3292 return true;
3293 }
3294 if (!strncmp(string, "ack", 3))
3295 return true;
3296 // we may not have liked the packet, but it was a command packet, so
3297 // we're done processing this packet now
3298 return true;
3299 }
3300 // netquake control packets, supported for compatibility only, and only
3301 // when running game protocols that are normally served via this connection
3302 // protocol
3303 // (this protects more modern protocols against being used for
3304 // Quake packet flood Denial Of Service attacks)
3305 if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) && !ENCRYPTION_REQUIRED)
3306 {
3307 int c;
3308 int protocolnumber;
3309 const char *protocolname;
3310 client_t *knownclient;
3311 client_t *newclient;
3312 data += 4;
3313 length -= 4;
3314 SZ_Clear(&sv_message);
3315 SZ_Write(&sv_message, data, length);
3316 MSG_BeginReading(&sv_message);
3317 c = MSG_ReadByte(&sv_message);
3318 switch (c)
3319 {
3320 case CCREQ_CONNECT:
3321 if (developer_extra.integer)
3322 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3323 if(!(islocal || sv_public.integer > -2))
3324 {
3325 if (developer_extra.integer)
3326 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3327 SZ_Clear(&sv_message);
3328 // save space for the header, filled in later
3329 MSG_WriteLong(&sv_message, 0);
3330 MSG_WriteByte(&sv_message, CCREP_REJECT);
3331 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3332 MSG_WriteString(&sv_message, "\n");
3333 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3334 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3335 SZ_Clear(&sv_message);
3336 break;
3337 }
3338
3339 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3340 protocolnumber = MSG_ReadByte(&sv_message);
3341 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3342 {
3343 if (developer_extra.integer)
3344 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3345 SZ_Clear(&sv_message);
3346 // save space for the header, filled in later
3347 MSG_WriteLong(&sv_message, 0);
3348 MSG_WriteByte(&sv_message, CCREP_REJECT);
3349 MSG_WriteString(&sv_message, "Incompatible version.\n");
3350 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3351 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3352 SZ_Clear(&sv_message);
3353 break;
3354 }
3355
3356 // see if this connect request comes from a known client
3357 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3358 {
3359 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3360 {
3361 // this is either a duplicate connection request
3362 // or coming back from a timeout
3363 // (if so, keep their stuff intact)
3364
3365 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3366 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3367 {
3368 if (developer_extra.integer)
3369 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3370 SZ_Clear(&sv_message);
3371 // save space for the header, filled in later
3372 MSG_WriteLong(&sv_message, 0);
3373 MSG_WriteByte(&sv_message, CCREP_REJECT);
3374 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3375 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3376 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3377 SZ_Clear(&sv_message);
3378 return true;
3379 }
3380
3381 // send a reply
3382 if (developer_extra.integer)
3383 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3384 SZ_Clear(&sv_message);
3385 // save space for the header, filled in later
3386 MSG_WriteLong(&sv_message, 0);
3387 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3388 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3389 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3390 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3391 SZ_Clear(&sv_message);
3392
3393 // if client is already spawned, re-send the
3394 // serverinfo message as they'll need it to play
3395 if (knownclient->begun)
3396 SV_SendServerinfo(knownclient);
3397 return true;
3398 }
3399 }
3400
3401 // this is a new client, check for connection flood
3402 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3403 break;
3404
3405 // find a slot for the new client
3406 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3407 {
3408 netconn_t *conn;
3409 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3410 {
3411 // connect to the client
3412 // everything is allocated, just fill in the details
3413 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3414 if (developer_extra.integer)
3415 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3416 // send back the info about the server connection
3417 SZ_Clear(&sv_message);
3418 // save space for the header, filled in later
3419 MSG_WriteLong(&sv_message, 0);
3420 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3421 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3422 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3423 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3424 SZ_Clear(&sv_message);
3425 // now set up the client struct
3426 SV_ConnectClient(clientnum, conn);
3427 NetConn_Heartbeat(1);
3428 return true;
3429 }
3430 }
3431
3432 if (developer_extra.integer)
3433 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3434 // no room; try to let player know
3435 SZ_Clear(&sv_message);
3436 // save space for the header, filled in later
3437 MSG_WriteLong(&sv_message, 0);
3438 MSG_WriteByte(&sv_message, CCREP_REJECT);
3439 MSG_WriteString(&sv_message, "Server is full.\n");
3440 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3441 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3442 SZ_Clear(&sv_message);
3443 break;
3444 case CCREQ_SERVER_INFO:
3445 if (developer_extra.integer)
3446 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3447 if(!(islocal || sv_public.integer > -1))
3448 break;
3449
3450 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3451 break;
3452
3453 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3454 {
3455 int numclients;
3456 char myaddressstring[128];
3457 if (developer_extra.integer)
3458 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3459 SZ_Clear(&sv_message);
3460 // save space for the header, filled in later
3461 MSG_WriteLong(&sv_message, 0);
3462 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3463 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3464 MSG_WriteString(&sv_message, myaddressstring);
3465 MSG_WriteString(&sv_message, hostname.string);
3466 MSG_WriteString(&sv_message, sv.name);
3467 // How many clients are there?
3468 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3469 if (svs.clients[i].active)
3470 numclients++;
3471 MSG_WriteByte(&sv_message, numclients);
3472 MSG_WriteByte(&sv_message, svs.maxclients);
3473 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3474 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3475 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3476 SZ_Clear(&sv_message);
3477 }
3478 break;
3479 case CCREQ_PLAYER_INFO:
3480 if (developer_extra.integer)
3481 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3482 if(!(islocal || sv_public.integer > -1))
3483 break;
3484
3485 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3486 break;
3487
3488 if (sv.active)
3489 {
3490 int playerNumber, activeNumber, clientNumber;
3491 client_t *client;
3492
3493 playerNumber = MSG_ReadByte(&sv_message);
3494 activeNumber = -1;
3495 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3496 if (client->active && ++activeNumber == playerNumber)
3497 break;
3498 if (clientNumber != svs.maxclients)
3499 {
3500 SZ_Clear(&sv_message);
3501 // save space for the header, filled in later
3502 MSG_WriteLong(&sv_message, 0);
3503 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3504 MSG_WriteByte(&sv_message, playerNumber);
3505 MSG_WriteString(&sv_message, client->name);
3506 MSG_WriteLong(&sv_message, client->colors);
3507 MSG_WriteLong(&sv_message, client->frags);
3508 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3509 if(sv_status_privacy.integer)
3510 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3511 else
3512 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3513 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3514 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3515 SZ_Clear(&sv_message);
3516 }
3517 }
3518 break;
3519 case CCREQ_RULE_INFO:
3520 if (developer_extra.integer)
3521 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3522 if(!(islocal || sv_public.integer > -1))
3523 break;
3524
3525 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3526
3527 if (sv.active)
3528 {
3529 char *prevCvarName;
3530 cvar_t *var;
3531
3532 // find the search start location
3533 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3534 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3535
3536 // send the response
3537 SZ_Clear(&sv_message);
3538 // save space for the header, filled in later
3539 MSG_WriteLong(&sv_message, 0);
3540 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3541 if (var)
3542 {
3543 MSG_WriteString(&sv_message, var->name);
3544 MSG_WriteString(&sv_message, var->string);
3545 }
3546 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3547 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3548 SZ_Clear(&sv_message);
3549 }
3550 break;
3551 case CCREQ_RCON:
3552 if (developer_extra.integer)
3553 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3554 if (sv.active && !rcon_secure.integer)
3555 {
3556 char password[2048];
3557 char cmd[2048];
3558 char *s;
3559 char *endpos;
3560 const char *userlevel;
3561 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3562 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3563 s = cmd;
3564 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3565 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3566 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3567 return true;
3568 }
3569 break;
3570 default:
3571 break;
3572 }
3573 SZ_Clear(&sv_message);
3574 // we may not have liked the packet, but it was a valid control
3575 // packet, so we're done processing this packet now
3576 return true;
3577 }
3578 if (host_client)
3579 {
3580 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3581 {
3582 SV_ReadClientMessage();
3583 return ret;
3584 }
3585 }
3586 return 0;
3587 }
3588
NetConn_ServerFrame(void)3589 void NetConn_ServerFrame(void)
3590 {
3591 int i, length;
3592 lhnetaddress_t peeraddress;
3593 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3594 for (i = 0;i < sv_numsockets;i++)
3595 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3596 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3597 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3598 {
3599 // never timeout loopback connections
3600 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3601 {
3602 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3603 SV_DropClient(false);
3604 }
3605 }
3606 }
3607
NetConn_SleepMicroseconds(int microseconds)3608 void NetConn_SleepMicroseconds(int microseconds)
3609 {
3610 LHNET_SleepUntilPacket_Microseconds(microseconds);
3611 }
3612
3613 #ifdef CONFIG_MENU
NetConn_QueryMasters(qboolean querydp,qboolean queryqw)3614 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3615 {
3616 int i, j;
3617 int masternum;
3618 lhnetaddress_t masteraddress;
3619 lhnetaddress_t broadcastaddress;
3620 char request[256];
3621
3622 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3623 return;
3624
3625 // 26000 is the default quake server port, servers on other ports will not
3626 // be found
3627 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3628 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3629
3630 if (querydp)
3631 {
3632 for (i = 0;i < cl_numsockets;i++)
3633 {
3634 if (cl_sockets[i])
3635 {
3636 const char *cmdname, *extraoptions;
3637 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3638
3639 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3640 {
3641 // search LAN for Quake servers
3642 SZ_Clear(&cl_message);
3643 // save space for the header, filled in later
3644 MSG_WriteLong(&cl_message, 0);
3645 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3646 MSG_WriteString(&cl_message, "QUAKE");
3647 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3648 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3649 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3650 SZ_Clear(&cl_message);
3651
3652 // search LAN for DarkPlaces servers
3653 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3654 }
3655
3656 // build the getservers message to send to the dpmaster master servers
3657 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3658 {
3659 cmdname = "getserversExt";
3660 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3661 }
3662 else
3663 {
3664 cmdname = "getservers";
3665 extraoptions = "";
3666 }
3667 memcpy(request, "\377\377\377\377", 4);
3668 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3669
3670 // search internet
3671 for (masternum = 0;sv_masters[masternum].name;masternum++)
3672 {
3673 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3674 {
3675 masterquerycount++;
3676 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3677 }
3678 }
3679
3680 // search favorite servers
3681 for(j = 0; j < nFavorites; ++j)
3682 {
3683 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3684 {
3685 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3686 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3687 }
3688 }
3689 }
3690 }
3691 }
3692
3693 // only query QuakeWorld servers when the user wants to
3694 if (queryqw)
3695 {
3696 for (i = 0;i < cl_numsockets;i++)
3697 {
3698 if (cl_sockets[i])
3699 {
3700 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3701
3702 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3703 {
3704 // search LAN for QuakeWorld servers
3705 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3706
3707 // build the getservers message to send to the qwmaster master servers
3708 // note this has no -1 prefix, and the trailing nul byte is sent
3709 dpsnprintf(request, sizeof(request), "c\n");
3710 }
3711
3712 // search internet
3713 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3714 {
3715 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3716 {
3717 if (m_state != m_slist)
3718 {
3719 char lookupstring[128];
3720 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3721 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3722 }
3723 masterquerycount++;
3724 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3725 }
3726 }
3727
3728 // search favorite servers
3729 for(j = 0; j < nFavorites; ++j)
3730 {
3731 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3732 {
3733 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3734 {
3735 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3736 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3737 }
3738 }
3739 }
3740 }
3741 }
3742 }
3743 if (!masterquerycount)
3744 {
3745 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3746 M_Update_Return_Reason("No network");
3747 }
3748 }
3749 #endif
3750
NetConn_Heartbeat(int priority)3751 void NetConn_Heartbeat(int priority)
3752 {
3753 lhnetaddress_t masteraddress;
3754 int masternum;
3755 lhnetsocket_t *mysocket;
3756
3757 // if it's a state change (client connected), limit next heartbeat to no
3758 // more than 30 sec in the future
3759 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3760 nextheartbeattime = realtime + 30.0;
3761
3762 // limit heartbeatperiod to 30 to 270 second range,
3763 // lower limit is to avoid abusing master servers with excess traffic,
3764 // upper limit is to avoid timing out on the master server (which uses
3765 // 300 sec timeout)
3766 if (sv_heartbeatperiod.value < 30)
3767 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3768 if (sv_heartbeatperiod.value > 270)
3769 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3770
3771 // make advertising optional and don't advertise singleplayer games, and
3772 // only send a heartbeat as often as the admin wants
3773 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3774 {
3775 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3776 for (masternum = 0;sv_masters[masternum].name;masternum++)
3777 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3778 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3779 }
3780 }
3781
Net_Heartbeat_f(void)3782 static void Net_Heartbeat_f(void)
3783 {
3784 if (sv.active)
3785 NetConn_Heartbeat(2);
3786 else
3787 Con_Print("No server running, can not heartbeat to master server.\n");
3788 }
3789
PrintStats(netconn_t * conn)3790 static void PrintStats(netconn_t *conn)
3791 {
3792 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3793 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3794 else
3795 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3796 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3797 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3798 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3799 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3800 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3801 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3802 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3803 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3804 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3805 }
3806
Net_Stats_f(void)3807 void Net_Stats_f(void)
3808 {
3809 netconn_t *conn;
3810 Con_Print("connections =\n");
3811 for (conn = netconn_list;conn;conn = conn->next)
3812 PrintStats(conn);
3813 }
3814
3815 #ifdef CONFIG_MENU
Net_Refresh_f(void)3816 void Net_Refresh_f(void)
3817 {
3818 if (m_state != m_slist) {
3819 Con_Print("Sending new requests to master servers\n");
3820 ServerList_QueryList(false, true, false, true);
3821 Con_Print("Listening for replies...\n");
3822 } else
3823 ServerList_QueryList(false, true, false, false);
3824 }
3825
Net_Slist_f(void)3826 void Net_Slist_f(void)
3827 {
3828 ServerList_ResetMasks();
3829 serverlist_sortbyfield = SLIF_PING;
3830 serverlist_sortflags = 0;
3831 if (m_state != m_slist) {
3832 Con_Print("Sending requests to master servers\n");
3833 ServerList_QueryList(true, true, false, true);
3834 Con_Print("Listening for replies...\n");
3835 } else
3836 ServerList_QueryList(true, true, false, false);
3837 }
3838
Net_SlistQW_f(void)3839 void Net_SlistQW_f(void)
3840 {
3841 ServerList_ResetMasks();
3842 serverlist_sortbyfield = SLIF_PING;
3843 serverlist_sortflags = 0;
3844 if (m_state != m_slist) {
3845 Con_Print("Sending requests to master servers\n");
3846 ServerList_QueryList(true, false, true, true);
3847 serverlist_consoleoutput = true;
3848 Con_Print("Listening for replies...\n");
3849 } else
3850 ServerList_QueryList(true, false, true, false);
3851 }
3852 #endif
3853
NetConn_Init(void)3854 void NetConn_Init(void)
3855 {
3856 int i;
3857 lhnetaddress_t tempaddress;
3858 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3859 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3860 #ifdef CONFIG_MENU
3861 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3862 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3863 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3864 #endif
3865 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3866 Cvar_RegisterVariable(&net_test);
3867 Cvar_RegisterVariable(&net_usesizelimit);
3868 Cvar_RegisterVariable(&net_burstreserve);
3869 Cvar_RegisterVariable(&rcon_restricted_password);
3870 Cvar_RegisterVariable(&rcon_restricted_commands);
3871 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3872 Cvar_RegisterVariable(&net_slist_queriespersecond);
3873 Cvar_RegisterVariable(&net_slist_queriesperframe);
3874 Cvar_RegisterVariable(&net_slist_timeout);
3875 Cvar_RegisterVariable(&net_slist_maxtries);
3876 Cvar_RegisterVariable(&net_slist_favorites);
3877 Cvar_RegisterVariable(&net_slist_pause);
3878 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3879 Cvar_RegisterVariable(&net_tos_dscp);
3880 Cvar_RegisterVariable(&net_messagetimeout);
3881 Cvar_RegisterVariable(&net_connecttimeout);
3882 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3883 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3884 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3885 Cvar_RegisterVariable(&net_sourceaddresscheck);
3886 Cvar_RegisterVariable(&cl_netlocalping);
3887 Cvar_RegisterVariable(&cl_netpacketloss_send);
3888 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3889 Cvar_RegisterVariable(&hostname);
3890 Cvar_RegisterVariable(&developer_networking);
3891 Cvar_RegisterVariable(&cl_netport);
3892 Cvar_RegisterVariable(&sv_netport);
3893 Cvar_RegisterVariable(&net_address);
3894 Cvar_RegisterVariable(&net_address_ipv6);
3895 Cvar_RegisterVariable(&sv_public);
3896 Cvar_RegisterVariable(&sv_public_rejectreason);
3897 Cvar_RegisterVariable(&sv_heartbeatperiod);
3898 for (i = 0;sv_masters[i].name;i++)
3899 Cvar_RegisterVariable(&sv_masters[i]);
3900 Cvar_RegisterVariable(&gameversion);
3901 Cvar_RegisterVariable(&gameversion_min);
3902 Cvar_RegisterVariable(&gameversion_max);
3903 // COMMANDLINEOPTION: Server: -ip <ipaddress> sets the ip address of this machine for purposes of networking (default 0.0.0.0 also known as INADDR_ANY), use only if you have multiple network adapters and need to choose one specifically.
3904 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3905 {
3906 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3907 {
3908 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3909 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3910 }
3911 else
3912 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3913 }
3914 // COMMANDLINEOPTION: Server: -port <portnumber> sets the port to use for a server (default 26000, the same port as QUAKE itself), useful if you host multiple servers on your machine
3915 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3916 {
3917 i = atoi(com_argv[i + 1]);
3918 if (i >= 0 && i < 65536)
3919 {
3920 Con_Printf("-port option used, setting port cvar to %i\n", i);
3921 Cvar_SetValueQuick(&sv_netport, i);
3922 }
3923 else
3924 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3925 }
3926 cl_numsockets = 0;
3927 sv_numsockets = 0;
3928 cl_message.data = cl_message_buf;
3929 cl_message.maxsize = sizeof(cl_message_buf);
3930 cl_message.cursize = 0;
3931 sv_message.data = sv_message_buf;
3932 sv_message.maxsize = sizeof(sv_message_buf);
3933 sv_message.cursize = 0;
3934 LHNET_Init();
3935 if (Thread_HasThreads())
3936 netconn_mutex = Thread_CreateMutex();
3937 }
3938
NetConn_Shutdown(void)3939 void NetConn_Shutdown(void)
3940 {
3941 NetConn_CloseClientPorts();
3942 NetConn_CloseServerPorts();
3943 LHNET_Shutdown();
3944 if (netconn_mutex)
3945 Thread_DestroyMutex(netconn_mutex);
3946 netconn_mutex = NULL;
3947 }
3948
3949