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 if (!password[0]) {
2743 Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2744 return false;
2745 }
2746
2747 t1 = (long) time(NULL);
2748 t2 = strtol(s, NULL, 0);
2749 if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2750 return false;
2751
2752 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2753 return false;
2754
2755 return !memcmp(mdfourbuf, hash, 16);
2756 }
2757
hmac_mdfour_challenge_matching(lhnetaddress_t * peeraddress,const char * password,const char * hash,const char * s,int slen)2758 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2759 {
2760 char mdfourbuf[16];
2761 int i;
2762
2763 if (!password[0]) {
2764 Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2765 return false;
2766 }
2767
2768 if(slen < (int)(sizeof(challenges[0].string)) - 1)
2769 return false;
2770
2771 // validate the challenge
2772 for (i = 0;i < MAX_CHALLENGES;i++)
2773 if(challenges[i].time > 0)
2774 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2775 break;
2776 // if the challenge is not recognized, drop the packet
2777 if (i == MAX_CHALLENGES)
2778 return false;
2779
2780 if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2781 return false;
2782
2783 if(memcmp(mdfourbuf, hash, 16))
2784 return false;
2785
2786 // unmark challenge to prevent replay attacks
2787 challenges[i].time = 0;
2788
2789 return true;
2790 }
2791
plaintext_matching(lhnetaddress_t * peeraddress,const char * password,const char * hash,const char * s,int slen)2792 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2793 {
2794 if (!password[0]) {
2795 Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2796 return false;
2797 }
2798
2799 return !strcmp(password, hash);
2800 }
2801
2802 /// 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)2803 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)
2804 {
2805 const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2806 static char buf[MAX_INPUTLINE];
2807 qboolean hasquotes;
2808 qboolean restricted = false;
2809 qboolean have_usernames = false;
2810 static char vabuf[1024];
2811
2812 userpass_start = rcon_password.string;
2813 while((userpass_end = strchr(userpass_start, ' ')))
2814 {
2815 have_usernames = true;
2816 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2817 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2818 if(comparator(peeraddress, buf, password, cs, cslen))
2819 goto allow;
2820 userpass_start = userpass_end + 1;
2821 }
2822 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2823 {
2824 userpass_end = userpass_start + strlen(userpass_start);
2825 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2826 goto allow;
2827 }
2828
2829 restricted = true;
2830 have_usernames = false;
2831 userpass_start = rcon_restricted_password.string;
2832 while((userpass_end = strchr(userpass_start, ' ')))
2833 {
2834 have_usernames = true;
2835 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2836 if(buf[0]) // Ignore empty entries due to leading/duplicate space.
2837 if(comparator(peeraddress, buf, password, cs, cslen))
2838 goto check;
2839 userpass_start = userpass_end + 1;
2840 }
2841 if(userpass_start[0]) // Ignore empty trailing entry due to trailing space or password not set.
2842 {
2843 userpass_end = userpass_start + strlen(userpass_start);
2844 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2845 goto check;
2846 }
2847
2848 return NULL; // DENIED
2849
2850 check:
2851 for(text = s; text != endpos; ++text)
2852 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2853 return NULL; // block possible exploits against the parser/alias expansion
2854
2855 while(s != endpos)
2856 {
2857 size_t l = strlen(s);
2858 if(l)
2859 {
2860 hasquotes = (strchr(s, '"') != NULL);
2861 // sorry, we can't allow these substrings in wildcard expressions,
2862 // as they can mess with the argument counts
2863 text = rcon_restricted_commands.string;
2864 while(COM_ParseToken_Console(&text))
2865 {
2866 // com_token now contains a pattern to check for...
2867 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2868 {
2869 if(!hasquotes)
2870 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2871 goto match;
2872 }
2873 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2874 {
2875 if(!strcmp(com_token, s))
2876 goto match;
2877 }
2878 else // single-arg expression? must match the beginning of the command
2879 {
2880 if(!strcmp(com_token, s))
2881 goto match;
2882 if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2883 goto match;
2884 }
2885 }
2886 // if we got here, nothing matched!
2887 return NULL;
2888 }
2889 match:
2890 s += l + 1;
2891 }
2892
2893 allow:
2894 userpass_startpass = strchr(userpass_start, ':');
2895 if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2896 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2897
2898 return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2899 }
2900
RCon_Execute(lhnetsocket_t * mysocket,lhnetaddress_t * peeraddress,const char * addressstring2,const char * userlevel,const char * s,const char * endpos,qboolean proquakeprotocol)2901 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2902 {
2903 if(userlevel)
2904 {
2905 // looks like a legitimate rcon command with the correct password
2906 const char *s_ptr = s;
2907 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2908 while(s_ptr != endpos)
2909 {
2910 size_t l = strlen(s_ptr);
2911 if(l)
2912 Con_Printf(" %s;", s_ptr);
2913 s_ptr += l + 1;
2914 }
2915 Con_Printf("\n");
2916
2917 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2918 Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2919 while(s != endpos)
2920 {
2921 size_t l = strlen(s);
2922 if(l)
2923 {
2924 client_t *host_client_save = host_client;
2925 Cmd_ExecuteString(s, src_command, true);
2926 host_client = host_client_save;
2927 // in case it is a command that changes host_client (like restart)
2928 }
2929 s += l + 1;
2930 }
2931 Con_Rcon_Redirect_End();
2932 }
2933 else
2934 {
2935 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2936 }
2937 }
2938
NetConn_ServerParsePacket(lhnetsocket_t * mysocket,unsigned char * data,int length,lhnetaddress_t * peeraddress)2939 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2940 {
2941 int i, ret, clientnum, best;
2942 double besttime;
2943 char *string, response[1400], addressstring2[128];
2944 static char stringbuf[16384]; // server only
2945 qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2946 char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2947 size_t sendlength, response_len;
2948 char infostringvalue[MAX_INPUTLINE];
2949
2950 if (!sv.active)
2951 return false;
2952
2953 // convert the address to a string incase we need it
2954 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2955
2956 // see if we can identify the sender as a local player
2957 // (this is necessary for rcon to send a reliable reply if the client is
2958 // actually on the server, not sending remotely)
2959 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2960 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2961 break;
2962 if (i == svs.maxclients)
2963 host_client = NULL;
2964
2965 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2966 {
2967 // received a command string - strip off the packaging and put it
2968 // into our string buffer with NULL termination
2969 data += 4;
2970 length -= 4;
2971 length = min(length, (int)sizeof(stringbuf) - 1);
2972 memcpy(stringbuf, data, length);
2973 stringbuf[length] = 0;
2974 string = stringbuf;
2975
2976 if (developer_extra.integer)
2977 {
2978 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2979 Com_HexDumpToConsole(data, length);
2980 }
2981
2982 sendlength = sizeof(senddata) - 4;
2983 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2984 {
2985 case CRYPTO_NOMATCH:
2986 // nothing to do
2987 break;
2988 case CRYPTO_MATCH:
2989 if(sendlength)
2990 {
2991 memcpy(senddata, "\377\377\377\377", 4);
2992 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2993 }
2994 break;
2995 case CRYPTO_DISCARD:
2996 if(sendlength)
2997 {
2998 memcpy(senddata, "\377\377\377\377", 4);
2999 NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3000 }
3001 return true;
3002 break;
3003 case CRYPTO_REPLACE:
3004 string = senddata+4;
3005 length = (int)sendlength;
3006 break;
3007 }
3008
3009 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3010 {
3011 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
3012 {
3013 if(challenges[i].time > 0)
3014 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3015 break;
3016 if (besttime > challenges[i].time)
3017 besttime = challenges[best = i].time;
3018 }
3019 // if we did not find an exact match, choose the oldest and
3020 // update address and string
3021 if (i == MAX_CHALLENGES)
3022 {
3023 i = best;
3024 challenges[i].address = *peeraddress;
3025 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3026 }
3027 else
3028 {
3029 // flood control: drop if requesting challenge too often
3030 if(challenges[i].time > realtime - net_challengefloodblockingtimeout.value)
3031 return true;
3032 }
3033 challenges[i].time = realtime;
3034 // send the challenge
3035 memcpy(response, "\377\377\377\377", 4);
3036 dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3037 response_len = strlen(response) + 1;
3038 Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3039 NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3040 return true;
3041 }
3042 if (length > 8 && !memcmp(string, "connect\\", 8))
3043 {
3044 char *s;
3045 client_t *client;
3046 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3047 string += 7;
3048 length -= 7;
3049
3050 if(crypto && crypto->authenticated)
3051 {
3052 // no need to check challenge
3053 if(crypto_developer.integer)
3054 {
3055 Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3056 crypto->use_aes ? "Encrypted" : "Authenticated",
3057 addressstring2,
3058 crypto->client_idfp[0] ? crypto->client_idfp : "-",
3059 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3060 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3061 crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3062 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3063 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3064 );
3065 }
3066 }
3067 else
3068 {
3069 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3070 {
3071 // validate the challenge
3072 for (i = 0;i < MAX_CHALLENGES;i++)
3073 if(challenges[i].time > 0)
3074 if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3075 break;
3076 // if the challenge is not recognized, drop the packet
3077 if (i == MAX_CHALLENGES)
3078 return true;
3079 }
3080 }
3081
3082 if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3083 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3084
3085 if(!(islocal || sv_public.integer > -2))
3086 {
3087 if (developer_extra.integer)
3088 Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3089 memcpy(response, "\377\377\377\377", 4);
3090 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3091 NetConn_WriteString(mysocket, response, peeraddress);
3092 return true;
3093 }
3094
3095 // check engine protocol
3096 if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3097 {
3098 if (developer_extra.integer)
3099 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3100 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3101 return true;
3102 }
3103
3104 // see if this is a duplicate connection request or a disconnected
3105 // client who is rejoining to the same client slot
3106 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3107 {
3108 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3109 {
3110 // this is a known client...
3111 if(crypto && crypto->authenticated)
3112 {
3113 // reject if changing key!
3114 if(client->netconnection->crypto.authenticated)
3115 {
3116 if(
3117 strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3118 ||
3119 strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3120 ||
3121 strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3122 ||
3123 strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3124 )
3125 {
3126 if (developer_extra.integer)
3127 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3128 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3129 return true;
3130 }
3131 }
3132 }
3133 else
3134 {
3135 // reject if downgrading!
3136 if(client->netconnection->crypto.authenticated)
3137 {
3138 if (developer_extra.integer)
3139 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3140 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3141 return true;
3142 }
3143 }
3144 if (client->begun)
3145 {
3146 // client crashed and is coming back,
3147 // keep their stuff intact
3148 if (developer_extra.integer)
3149 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3150 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3151 if(crypto && crypto->authenticated)
3152 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3153 SV_SendServerinfo(client);
3154 }
3155 else
3156 {
3157 // client is still trying to connect,
3158 // so we send a duplicate reply
3159 if (developer_extra.integer)
3160 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3161 if(crypto && crypto->authenticated)
3162 Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3163 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3164 }
3165 return true;
3166 }
3167 }
3168
3169 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3170 return true;
3171
3172 // find an empty client slot for this new client
3173 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3174 {
3175 netconn_t *conn;
3176 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3177 {
3178 // allocated connection
3179 if (developer_extra.integer)
3180 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3181 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3182 // now set up the client
3183 if(crypto && crypto->authenticated)
3184 Crypto_FinishInstance(&conn->crypto, crypto);
3185 SV_ConnectClient(clientnum, conn);
3186 NetConn_Heartbeat(1);
3187 return true;
3188 }
3189 }
3190
3191 // no empty slots found - server is full
3192 if (developer_extra.integer)
3193 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3194 NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3195
3196 return true;
3197 }
3198 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3199 {
3200 const char *challenge = NULL;
3201
3202 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3203 return true;
3204
3205 // If there was a challenge in the getinfo message
3206 if (length > 8 && string[7] == ' ')
3207 challenge = string + 8;
3208
3209 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3210 {
3211 if (developer_extra.integer)
3212 Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3213 NetConn_WriteString(mysocket, response, peeraddress);
3214 }
3215 return true;
3216 }
3217 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3218 {
3219 const char *challenge = NULL;
3220
3221 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3222 return true;
3223
3224 // If there was a challenge in the getinfo message
3225 if (length > 10 && string[9] == ' ')
3226 challenge = string + 10;
3227
3228 if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3229 {
3230 if (developer_extra.integer)
3231 Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3232 NetConn_WriteString(mysocket, response, peeraddress);
3233 }
3234 return true;
3235 }
3236 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3237 {
3238 char *password = string + 20;
3239 char *timeval = string + 37;
3240 char *s = strchr(timeval, ' ');
3241 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3242 const char *userlevel;
3243
3244 if(rcon_secure.integer > 1)
3245 return true;
3246
3247 if(!s)
3248 return true; // invalid packet
3249 ++s;
3250
3251 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 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 >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3256 {
3257 char *password = string + 25;
3258 char *challenge = string + 42;
3259 char *s = strchr(challenge, ' ');
3260 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3261 const char *userlevel;
3262 if(!s)
3263 return true; // invalid packet
3264 ++s;
3265
3266 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3267 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3268 return true;
3269 }
3270 if (length >= 5 && !memcmp(string, "rcon ", 5))
3271 {
3272 int j;
3273 char *s = string + 5;
3274 char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3275 char password[64];
3276
3277 if(rcon_secure.integer > 0)
3278 return true;
3279
3280 for (j = 0;!ISWHITESPACE(*s);s++)
3281 if (j < (int)sizeof(password) - 1)
3282 password[j++] = *s;
3283 if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3284 ++s;
3285 password[j] = 0;
3286 if (!ISWHITESPACE(password[0]))
3287 {
3288 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3289 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3290 }
3291 return true;
3292 }
3293 if (!strncmp(string, "extResponse ", 12))
3294 {
3295 ++sv_net_extresponse_count;
3296 if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3297 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3298 sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3299 dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3300 return true;
3301 }
3302 if (!strncmp(string, "ping", 4))
3303 {
3304 if (developer_extra.integer)
3305 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3306 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3307 return true;
3308 }
3309 if (!strncmp(string, "ack", 3))
3310 return true;
3311 // we may not have liked the packet, but it was a command packet, so
3312 // we're done processing this packet now
3313 return true;
3314 }
3315 // netquake control packets, supported for compatibility only, and only
3316 // when running game protocols that are normally served via this connection
3317 // protocol
3318 // (this protects more modern protocols against being used for
3319 // Quake packet flood Denial Of Service attacks)
3320 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)
3321 {
3322 int c;
3323 int protocolnumber;
3324 const char *protocolname;
3325 client_t *knownclient;
3326 client_t *newclient;
3327 data += 4;
3328 length -= 4;
3329 SZ_Clear(&sv_message);
3330 SZ_Write(&sv_message, data, length);
3331 MSG_BeginReading(&sv_message);
3332 c = MSG_ReadByte(&sv_message);
3333 switch (c)
3334 {
3335 case CCREQ_CONNECT:
3336 if (developer_extra.integer)
3337 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3338 if(!(islocal || sv_public.integer > -2))
3339 {
3340 if (developer_extra.integer)
3341 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3342 SZ_Clear(&sv_message);
3343 // save space for the header, filled in later
3344 MSG_WriteLong(&sv_message, 0);
3345 MSG_WriteByte(&sv_message, CCREP_REJECT);
3346 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3347 MSG_WriteString(&sv_message, "\n");
3348 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3349 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3350 SZ_Clear(&sv_message);
3351 break;
3352 }
3353
3354 protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3355 protocolnumber = MSG_ReadByte(&sv_message);
3356 if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3357 {
3358 if (developer_extra.integer)
3359 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3360 SZ_Clear(&sv_message);
3361 // save space for the header, filled in later
3362 MSG_WriteLong(&sv_message, 0);
3363 MSG_WriteByte(&sv_message, CCREP_REJECT);
3364 MSG_WriteString(&sv_message, "Incompatible version.\n");
3365 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3366 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3367 SZ_Clear(&sv_message);
3368 break;
3369 }
3370
3371 // see if this connect request comes from a known client
3372 for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3373 {
3374 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3375 {
3376 // this is either a duplicate connection request
3377 // or coming back from a timeout
3378 // (if so, keep their stuff intact)
3379
3380 crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3381 if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3382 {
3383 if (developer_extra.integer)
3384 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3385 SZ_Clear(&sv_message);
3386 // save space for the header, filled in later
3387 MSG_WriteLong(&sv_message, 0);
3388 MSG_WriteByte(&sv_message, CCREP_REJECT);
3389 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3390 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3391 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3392 SZ_Clear(&sv_message);
3393 return true;
3394 }
3395
3396 // send a reply
3397 if (developer_extra.integer)
3398 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3399 SZ_Clear(&sv_message);
3400 // save space for the header, filled in later
3401 MSG_WriteLong(&sv_message, 0);
3402 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3403 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3404 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3405 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3406 SZ_Clear(&sv_message);
3407
3408 // if client is already spawned, re-send the
3409 // serverinfo message as they'll need it to play
3410 if (knownclient->begun)
3411 SV_SendServerinfo(knownclient);
3412 return true;
3413 }
3414 }
3415
3416 // this is a new client, check for connection flood
3417 if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3418 break;
3419
3420 // find a slot for the new client
3421 for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3422 {
3423 netconn_t *conn;
3424 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3425 {
3426 // connect to the client
3427 // everything is allocated, just fill in the details
3428 strlcpy (conn->address, addressstring2, sizeof (conn->address));
3429 if (developer_extra.integer)
3430 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3431 // send back the info about the server connection
3432 SZ_Clear(&sv_message);
3433 // save space for the header, filled in later
3434 MSG_WriteLong(&sv_message, 0);
3435 MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3436 MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3437 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3438 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3439 SZ_Clear(&sv_message);
3440 // now set up the client struct
3441 SV_ConnectClient(clientnum, conn);
3442 NetConn_Heartbeat(1);
3443 return true;
3444 }
3445 }
3446
3447 if (developer_extra.integer)
3448 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3449 // no room; try to let player know
3450 SZ_Clear(&sv_message);
3451 // save space for the header, filled in later
3452 MSG_WriteLong(&sv_message, 0);
3453 MSG_WriteByte(&sv_message, CCREP_REJECT);
3454 MSG_WriteString(&sv_message, "Server is full.\n");
3455 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3456 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3457 SZ_Clear(&sv_message);
3458 break;
3459 case CCREQ_SERVER_INFO:
3460 if (developer_extra.integer)
3461 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3462 if(!(islocal || sv_public.integer > -1))
3463 break;
3464
3465 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3466 break;
3467
3468 if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3469 {
3470 int numclients;
3471 char myaddressstring[128];
3472 if (developer_extra.integer)
3473 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3474 SZ_Clear(&sv_message);
3475 // save space for the header, filled in later
3476 MSG_WriteLong(&sv_message, 0);
3477 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3478 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3479 MSG_WriteString(&sv_message, myaddressstring);
3480 MSG_WriteString(&sv_message, hostname.string);
3481 MSG_WriteString(&sv_message, sv.name);
3482 // How many clients are there?
3483 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3484 if (svs.clients[i].active)
3485 numclients++;
3486 MSG_WriteByte(&sv_message, numclients);
3487 MSG_WriteByte(&sv_message, svs.maxclients);
3488 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3489 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3490 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3491 SZ_Clear(&sv_message);
3492 }
3493 break;
3494 case CCREQ_PLAYER_INFO:
3495 if (developer_extra.integer)
3496 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3497 if(!(islocal || sv_public.integer > -1))
3498 break;
3499
3500 if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3501 break;
3502
3503 if (sv.active)
3504 {
3505 int playerNumber, activeNumber, clientNumber;
3506 client_t *client;
3507
3508 playerNumber = MSG_ReadByte(&sv_message);
3509 activeNumber = -1;
3510 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3511 if (client->active && ++activeNumber == playerNumber)
3512 break;
3513 if (clientNumber != svs.maxclients)
3514 {
3515 SZ_Clear(&sv_message);
3516 // save space for the header, filled in later
3517 MSG_WriteLong(&sv_message, 0);
3518 MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3519 MSG_WriteByte(&sv_message, playerNumber);
3520 MSG_WriteString(&sv_message, client->name);
3521 MSG_WriteLong(&sv_message, client->colors);
3522 MSG_WriteLong(&sv_message, client->frags);
3523 MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3524 if(sv_status_privacy.integer)
3525 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3526 else
3527 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3528 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3529 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3530 SZ_Clear(&sv_message);
3531 }
3532 }
3533 break;
3534 case CCREQ_RULE_INFO:
3535 if (developer_extra.integer)
3536 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3537 if(!(islocal || sv_public.integer > -1))
3538 break;
3539
3540 // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3541
3542 if (sv.active)
3543 {
3544 char *prevCvarName;
3545 cvar_t *var;
3546
3547 // find the search start location
3548 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3549 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3550
3551 // send the response
3552 SZ_Clear(&sv_message);
3553 // save space for the header, filled in later
3554 MSG_WriteLong(&sv_message, 0);
3555 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3556 if (var)
3557 {
3558 MSG_WriteString(&sv_message, var->name);
3559 MSG_WriteString(&sv_message, var->string);
3560 }
3561 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3562 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3563 SZ_Clear(&sv_message);
3564 }
3565 break;
3566 case CCREQ_RCON:
3567 if (developer_extra.integer)
3568 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3569 if (sv.active && !rcon_secure.integer)
3570 {
3571 char password[2048];
3572 char cmd[2048];
3573 char *s;
3574 char *endpos;
3575 const char *userlevel;
3576 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3577 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3578 s = cmd;
3579 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3580 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3581 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3582 return true;
3583 }
3584 break;
3585 default:
3586 break;
3587 }
3588 SZ_Clear(&sv_message);
3589 // we may not have liked the packet, but it was a valid control
3590 // packet, so we're done processing this packet now
3591 return true;
3592 }
3593 if (host_client)
3594 {
3595 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3596 {
3597 SV_ReadClientMessage();
3598 return ret;
3599 }
3600 }
3601 return 0;
3602 }
3603
NetConn_ServerFrame(void)3604 void NetConn_ServerFrame(void)
3605 {
3606 int i, length;
3607 lhnetaddress_t peeraddress;
3608 unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3609 for (i = 0;i < sv_numsockets;i++)
3610 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3611 NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3612 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3613 {
3614 // never timeout loopback connections
3615 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3616 {
3617 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3618 SV_DropClient(false);
3619 }
3620 }
3621 }
3622
NetConn_SleepMicroseconds(int microseconds)3623 void NetConn_SleepMicroseconds(int microseconds)
3624 {
3625 LHNET_SleepUntilPacket_Microseconds(microseconds);
3626 }
3627
3628 #ifdef CONFIG_MENU
NetConn_QueryMasters(qboolean querydp,qboolean queryqw)3629 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3630 {
3631 int i, j;
3632 int masternum;
3633 lhnetaddress_t masteraddress;
3634 lhnetaddress_t broadcastaddress;
3635 char request[256];
3636
3637 if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3638 return;
3639
3640 // 26000 is the default quake server port, servers on other ports will not
3641 // be found
3642 // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3643 LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3644
3645 if (querydp)
3646 {
3647 for (i = 0;i < cl_numsockets;i++)
3648 {
3649 if (cl_sockets[i])
3650 {
3651 const char *cmdname, *extraoptions;
3652 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3653
3654 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3655 {
3656 // search LAN for Quake servers
3657 SZ_Clear(&cl_message);
3658 // save space for the header, filled in later
3659 MSG_WriteLong(&cl_message, 0);
3660 MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3661 MSG_WriteString(&cl_message, "QUAKE");
3662 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3663 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3664 NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3665 SZ_Clear(&cl_message);
3666
3667 // search LAN for DarkPlaces servers
3668 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3669 }
3670
3671 // build the getservers message to send to the dpmaster master servers
3672 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3673 {
3674 cmdname = "getserversExt";
3675 extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
3676 }
3677 else
3678 {
3679 cmdname = "getservers";
3680 extraoptions = "";
3681 }
3682 memcpy(request, "\377\377\377\377", 4);
3683 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3684
3685 // search internet
3686 for (masternum = 0;sv_masters[masternum].name;masternum++)
3687 {
3688 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3689 {
3690 masterquerycount++;
3691 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3692 }
3693 }
3694
3695 // search favorite servers
3696 for(j = 0; j < nFavorites; ++j)
3697 {
3698 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3699 {
3700 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3701 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3702 }
3703 }
3704 }
3705 }
3706 }
3707
3708 // only query QuakeWorld servers when the user wants to
3709 if (queryqw)
3710 {
3711 for (i = 0;i < cl_numsockets;i++)
3712 {
3713 if (cl_sockets[i])
3714 {
3715 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3716
3717 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3718 {
3719 // search LAN for QuakeWorld servers
3720 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3721
3722 // build the getservers message to send to the qwmaster master servers
3723 // note this has no -1 prefix, and the trailing nul byte is sent
3724 dpsnprintf(request, sizeof(request), "c\n");
3725 }
3726
3727 // search internet
3728 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3729 {
3730 if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3731 {
3732 if (m_state != m_slist)
3733 {
3734 char lookupstring[128];
3735 LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3736 Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3737 }
3738 masterquerycount++;
3739 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3740 }
3741 }
3742
3743 // search favorite servers
3744 for(j = 0; j < nFavorites; ++j)
3745 {
3746 if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3747 {
3748 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3749 {
3750 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3751 NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3752 }
3753 }
3754 }
3755 }
3756 }
3757 }
3758 if (!masterquerycount)
3759 {
3760 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3761 M_Update_Return_Reason("No network");
3762 }
3763 }
3764 #endif
3765
NetConn_Heartbeat(int priority)3766 void NetConn_Heartbeat(int priority)
3767 {
3768 lhnetaddress_t masteraddress;
3769 int masternum;
3770 lhnetsocket_t *mysocket;
3771
3772 // if it's a state change (client connected), limit next heartbeat to no
3773 // more than 30 sec in the future
3774 if (priority == 1 && nextheartbeattime > realtime + 30.0)
3775 nextheartbeattime = realtime + 30.0;
3776
3777 // limit heartbeatperiod to 30 to 270 second range,
3778 // lower limit is to avoid abusing master servers with excess traffic,
3779 // upper limit is to avoid timing out on the master server (which uses
3780 // 300 sec timeout)
3781 if (sv_heartbeatperiod.value < 30)
3782 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3783 if (sv_heartbeatperiod.value > 270)
3784 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3785
3786 // make advertising optional and don't advertise singleplayer games, and
3787 // only send a heartbeat as often as the admin wants
3788 if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3789 {
3790 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3791 for (masternum = 0;sv_masters[masternum].name;masternum++)
3792 if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3793 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3794 }
3795 }
3796
Net_Heartbeat_f(void)3797 static void Net_Heartbeat_f(void)
3798 {
3799 if (sv.active)
3800 NetConn_Heartbeat(2);
3801 else
3802 Con_Print("No server running, can not heartbeat to master server.\n");
3803 }
3804
PrintStats(netconn_t * conn)3805 static void PrintStats(netconn_t *conn)
3806 {
3807 if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3808 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3809 else
3810 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3811 Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
3812 Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
3813 Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
3814 Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3815 Con_Printf("packetsSent = %i\n", conn->packetsSent);
3816 Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
3817 Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
3818 Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
3819 Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
3820 }
3821
Net_Stats_f(void)3822 void Net_Stats_f(void)
3823 {
3824 netconn_t *conn;
3825 Con_Print("connections =\n");
3826 for (conn = netconn_list;conn;conn = conn->next)
3827 PrintStats(conn);
3828 }
3829
3830 #ifdef CONFIG_MENU
Net_Refresh_f(void)3831 void Net_Refresh_f(void)
3832 {
3833 if (m_state != m_slist) {
3834 Con_Print("Sending new requests to master servers\n");
3835 ServerList_QueryList(false, true, false, true);
3836 Con_Print("Listening for replies...\n");
3837 } else
3838 ServerList_QueryList(false, true, false, false);
3839 }
3840
Net_Slist_f(void)3841 void Net_Slist_f(void)
3842 {
3843 ServerList_ResetMasks();
3844 serverlist_sortbyfield = SLIF_PING;
3845 serverlist_sortflags = 0;
3846 if (m_state != m_slist) {
3847 Con_Print("Sending requests to master servers\n");
3848 ServerList_QueryList(true, true, false, true);
3849 Con_Print("Listening for replies...\n");
3850 } else
3851 ServerList_QueryList(true, true, false, false);
3852 }
3853
Net_SlistQW_f(void)3854 void Net_SlistQW_f(void)
3855 {
3856 ServerList_ResetMasks();
3857 serverlist_sortbyfield = SLIF_PING;
3858 serverlist_sortflags = 0;
3859 if (m_state != m_slist) {
3860 Con_Print("Sending requests to master servers\n");
3861 ServerList_QueryList(true, false, true, true);
3862 serverlist_consoleoutput = true;
3863 Con_Print("Listening for replies...\n");
3864 } else
3865 ServerList_QueryList(true, false, true, false);
3866 }
3867 #endif
3868
NetConn_Init(void)3869 void NetConn_Init(void)
3870 {
3871 int i;
3872 lhnetaddress_t tempaddress;
3873 netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3874 Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3875 #ifdef CONFIG_MENU
3876 Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3877 Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3878 Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3879 #endif
3880 Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3881 Cvar_RegisterVariable(&net_test);
3882 Cvar_RegisterVariable(&net_usesizelimit);
3883 Cvar_RegisterVariable(&net_burstreserve);
3884 Cvar_RegisterVariable(&rcon_restricted_password);
3885 Cvar_RegisterVariable(&rcon_restricted_commands);
3886 Cvar_RegisterVariable(&rcon_secure_maxdiff);
3887 Cvar_RegisterVariable(&net_slist_queriespersecond);
3888 Cvar_RegisterVariable(&net_slist_queriesperframe);
3889 Cvar_RegisterVariable(&net_slist_timeout);
3890 Cvar_RegisterVariable(&net_slist_maxtries);
3891 Cvar_RegisterVariable(&net_slist_favorites);
3892 Cvar_RegisterVariable(&net_slist_pause);
3893 if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3894 Cvar_RegisterVariable(&net_tos_dscp);
3895 Cvar_RegisterVariable(&net_messagetimeout);
3896 Cvar_RegisterVariable(&net_connecttimeout);
3897 Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3898 Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3899 Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3900 Cvar_RegisterVariable(&net_sourceaddresscheck);
3901 Cvar_RegisterVariable(&cl_netlocalping);
3902 Cvar_RegisterVariable(&cl_netpacketloss_send);
3903 Cvar_RegisterVariable(&cl_netpacketloss_receive);
3904 Cvar_RegisterVariable(&hostname);
3905 Cvar_RegisterVariable(&developer_networking);
3906 Cvar_RegisterVariable(&cl_netport);
3907 Cvar_RegisterVariable(&sv_netport);
3908 Cvar_RegisterVariable(&net_address);
3909 Cvar_RegisterVariable(&net_address_ipv6);
3910 Cvar_RegisterVariable(&sv_public);
3911 Cvar_RegisterVariable(&sv_public_rejectreason);
3912 Cvar_RegisterVariable(&sv_heartbeatperiod);
3913 for (i = 0;sv_masters[i].name;i++)
3914 Cvar_RegisterVariable(&sv_masters[i]);
3915 Cvar_RegisterVariable(&gameversion);
3916 Cvar_RegisterVariable(&gameversion_min);
3917 Cvar_RegisterVariable(&gameversion_max);
3918 // 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.
3919 if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3920 {
3921 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3922 {
3923 Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3924 Cvar_SetQuick(&net_address, com_argv[i + 1]);
3925 }
3926 else
3927 Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3928 }
3929 // 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
3930 if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3931 {
3932 i = atoi(com_argv[i + 1]);
3933 if (i >= 0 && i < 65536)
3934 {
3935 Con_Printf("-port option used, setting port cvar to %i\n", i);
3936 Cvar_SetValueQuick(&sv_netport, i);
3937 }
3938 else
3939 Con_Printf("-port option used, but %i is not a valid port number\n", i);
3940 }
3941 cl_numsockets = 0;
3942 sv_numsockets = 0;
3943 cl_message.data = cl_message_buf;
3944 cl_message.maxsize = sizeof(cl_message_buf);
3945 cl_message.cursize = 0;
3946 sv_message.data = sv_message_buf;
3947 sv_message.maxsize = sizeof(sv_message_buf);
3948 sv_message.cursize = 0;
3949 LHNET_Init();
3950 if (Thread_HasThreads())
3951 netconn_mutex = Thread_CreateMutex();
3952 }
3953
NetConn_Shutdown(void)3954 void NetConn_Shutdown(void)
3955 {
3956 NetConn_CloseClientPorts();
3957 NetConn_CloseServerPorts();
3958 LHNET_Shutdown();
3959 if (netconn_mutex)
3960 Thread_DestroyMutex(netconn_mutex);
3961 netconn_mutex = NULL;
3962 }
3963
3964