1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 1999-2004  Eidos Interactive
4 	Copyright (C) 2005-2020  Warzone 2100 Project
5 
6 	Warzone 2100 is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	Warzone 2100 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. See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with Warzone 2100; if not, write to the Free Software
18 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 /**
21  * @file netplay.c
22  *
23  * Basic netcode.
24  */
25 
26 #include "lib/framework/frame.h"
27 #include "lib/framework/wzapp.h"
28 #include "lib/framework/string_ext.h"
29 #include "lib/framework/crc.h"
30 #include "lib/framework/file.h"
31 #include "lib/gamelib/gtime.h"
32 #include "lib/exceptionhandler/dumpinfo.h"
33 #include "src/console.h"
34 #include "src/component.h"		// FIXME: we need to handle this better
35 #include "src/modding.h"		// FIXME: we need to handle this better
36 
37 #include <time.h>			// for stats
38 #include <physfs.h>
39 #include "lib/framework/physfs_ext.h"
40 #include <string.h>
41 #include <memory>
42 #include <thread>
43 #include <atomic>
44 #include <limits>
45 #include <sodium.h>
46 #include <re2/re2.h>
47 
48 #include "netplay.h"
49 #include "netlog.h"
50 #include "netsocket.h"
51 
52 #include <miniupnpc/miniwget.h>
53 #if (defined(__GNUC__) || defined(__clang__)) && !defined(__INTEL_COMPILER)
54 # pragma GCC diagnostic push
55 # pragma GCC diagnostic ignored "-Wpedantic"
56 #endif
57 #include <miniupnpc/miniupnpc.h>
58 #if (defined(__GNUC__) || defined(__clang__)) && !defined(__INTEL_COMPILER)
59 # pragma GCC diagnostic pop
60 #endif
61 #include <miniupnpc/upnpcommands.h>
62 
63 // Enforce minimum MINIUPNPC_API_VERSION
64 #if !defined(MINIUPNPC_API_VERSION) || (MINIUPNPC_API_VERSION < 9)
65 	#error lib/netplay requires MINIUPNPC_API_VERSION >= 9
66 #endif
67 
68 #include "src/multistat.h"
69 #include "src/multijoin.h"
70 #include "src/multiint.h"
71 #include "src/multiplay.h"
72 #include "src/warzoneconfig.h"
73 #include "src/version.h"
74 #include "src/loadsave.h"
75 #include "src/activity.h"
76 
77 #if defined (WZ_OS_MAC)
78 # include "lib/framework/cocoa_wrapper.h"
79 #endif
80 
81 #if defined(WZ_OS_LINUX) && defined(__GLIBC__)
82 #include <execinfo.h>  // Nonfatal runtime backtraces.
83 #endif // defined(WZ_OS_LINUX) && defined(__GLIBC__)
84 
85 // WARNING !!! This is initialised via configuration.c !!!
86 char masterserver_name[255] = {'\0'};
87 static unsigned int masterserver_port = 0, gameserver_port = 0;
88 static bool bJoinPrefTryIPv6First = true;
89 
90 // This is for command line argument override
91 // Disables port saving and reading from/to config
92 bool netGameserverPortOverride = false;
93 
94 #define NET_TIMEOUT_DELAY	2500		// we wait this amount of time for socket activity
95 #define NET_READ_TIMEOUT	0
96 /*
97 *	=== Using new socket code, this might not hold true any longer ===
98 *	NOTE /rant:  If the buffer size isn't big enough, it will invalidate the socket.
99 *	Which means that we need to allocate a buffer big enough to handle worst case
100 *	situations.
101 *	reference: MaxMsgSize in netplay.h  (currently set to 16K)
102 *
103 */
104 #define NET_BUFFER_SIZE	(MaxMsgSize)	// Would be 16K
105 
106 #define UPNP_SUCCESS 1
107 #define UPNP_ERROR_DEVICE_NOT_FOUND -1
108 #define UPNP_ERROR_CONTROL_NOT_AVAILABLE -2
109 
110 // ////////////////////////////////////////////////////////////////////////
111 // Function prototypes
112 static void NETplayerLeaving(UDWORD player);		// Cleanup sockets on player leaving (nicely)
113 static void NETplayerDropped(UDWORD player);		// Broadcast NET_PLAYER_DROPPED & cleanup
114 static void NETallowJoining();
115 static void recvDebugSync(NETQUEUE queue);
116 static bool onBanList(const char *ip);
117 static void addToBanList(const char *ip, const char *name);
118 static void NETfixPlayerCount();
119 /*
120  * Network globals, these are part of the new network API
121  */
122 SYNC_COUNTER sync_counter;		// keeps track on how well we are in sync
123 // ////////////////////////////////////////////////////////////////////////
124 // Types
125 
126 std::atomic_int upnp_status;
127 
128 struct Statistic
129 {
130 	size_t sent;
131 	size_t received;
132 };
133 
134 struct NETSTATS  // data regarding the last one second or so.
135 {
136 	Statistic       rawBytes;               // Number of actual bytes, in about 1 sec.
137 	Statistic       uncompressedBytes;      // Number of bytes sent, before compression, in about 1 sec.
138 	Statistic       packets;                // Number of calls to writeAll, in about 1 sec.
139 };
140 
141 struct NET_PLAYER_DATA
142 {
143 	uint16_t        size;
144 	void           *data;
145 	size_t          buffer_size;
146 };
147 
148 #define SERVER_UPDATE_MIN_INTERVAL 7 * GAME_TICKS_PER_SEC
149 #define SERVER_UPDATE_MAX_INTERVAL 25 * GAME_TICKS_PER_SEC
150 
151 class LobbyServerConnectionHandler
152 {
153 public:
154 	bool connect();
155 	bool disconnect();
156 	void sendUpdate();
157 	void run();
158 private:
159 	void sendUpdateNow();
160 	void sendKeepAlive();
161 
canSendServerUpdateNow()162 	inline bool canSendServerUpdateNow()
163 	{
164 		return (currentState == LobbyConnectionState::Connected)
165 			&& (realTime - lastServerUpdate >= SERVER_UPDATE_MIN_INTERVAL);
166 	}
167 
shouldSendServerKeepAliveNow()168 	inline bool shouldSendServerKeepAliveNow()
169 	{
170 		return (currentState == LobbyConnectionState::Connected)
171 			&& (realTime - lastServerUpdate >= SERVER_UPDATE_MAX_INTERVAL);
172 	}
173 private:
174 	enum class LobbyConnectionState
175 	{
176 		Disconnected,
177 		Connecting_WaitingForResponse,
178 		Connected
179 	};
180 	LobbyConnectionState currentState = LobbyConnectionState::Disconnected;
181 	Socket *rs_socket = nullptr;
182 	SocketSet* waitingForConnectionFinalize = nullptr;
183 	uint32_t lastConnectionTime = 0;
184 	uint32_t lastServerUpdate = 0;
185 	bool queuedServerUpdate = false;
186 };
187 
188 // ////////////////////////////////////////////////////////////////////////
189 // Variables
190 
191 NETPLAY	NetPlay;
192 PLAYER_IP	*IPlist = nullptr;
193 static int IPlistLast = 0;
194 static bool		allow_joining = false;
195 static	bool server_not_there = false;
196 static GAMESTRUCT	gamestruct;
197 
198 // update flags
199 bool netPlayersUpdated;
200 
201 
202 /**
203  * Socket used for these purposes:
204  *  * Host a game, be a server.
205  *  * Connect to the lobby server.
206  *  * Join a server for a game.
207  */
208 static Socket *tcp_socket = nullptr;               ///< Socket used to talk to a lobby server (hosts also seem to temporarily act as lobby servers while the client negotiates joining the game), or to listen for clients (if we're the host, in which case we use rs_socket (declaration hidden somewhere inside a function) to talk to the lobby server).
209 
210 static Socket *bsocket = nullptr;                  ///< Socket used to talk to the host (clients only). If bsocket != NULL, then tcp_socket == NULL.
211 static Socket *connected_bsocket[MAX_CONNECTED_PLAYERS] = { nullptr };  ///< Sockets used to talk to clients (host only).
212 static SocketSet *socket_set = nullptr;
213 
214 WZ_THREAD *upnpdiscover;
215 
216 static struct UPNPUrls urls;
217 static struct IGDdatas data;
218 
219 // local ip address
220 static char lanaddr[40];
221 static char externalIPAddress[40];
222 /**
223  * Used for connections with clients.
224  */
225 static Socket *tmp_socket[MAX_TMP_SOCKETS] = { nullptr };  ///< Sockets used to talk to clients which have not yet been assigned a player number (host only).
226 
227 static SocketSet *tmp_socket_set = nullptr;
228 static int32_t          NetGameFlags[4] = { 0, 0, 0, 0 };
229 char iptoconnect[PATH_MAX] = "\0"; // holds IP/hostname from command line
230 
231 static NETSTATS nStats              = {{0, 0}, {0, 0}, {0, 0}};
232 static NETSTATS nStatsLastSec       = {{0, 0}, {0, 0}, {0, 0}};
233 static NETSTATS nStatsSecondLastSec = {{0, 0}, {0, 0}, {0, 0}};
234 static const NETSTATS nZeroStats    = {{0, 0}, {0, 0}, {0, 0}};
235 static int nStatsLastUpdateTime = 0;
236 
237 unsigned NET_PlayerConnectionStatus[CONNECTIONSTATUS_NORMAL][MAX_PLAYERS];
238 
239 static LobbyServerConnectionHandler lobbyConnectionHandler;
240 
241 // ////////////////////////////////////////////////////////////////////////////
242 /************************************************************************************
243  **  NOTE (!)  Increment NETCODE_VERSION_MINOR on each release.
244  **
245  ************************************************************************************
246 **/
247 static char const *versionString = version_getVersionString();
248 
249 #include "lib/netplay/netplay_config.h"
250 
NETPLAY()251 NETPLAY::NETPLAY()
252 {
253 	players.resize(MAX_PLAYERS);
254 	playerReferences.resize(MAX_PLAYERS);
255 	for (auto i = 0; i < MAX_PLAYERS; i++)
256 	{
257 		playerReferences[i] = std::make_shared<PlayerReference>(i);
258 	}
259 }
260 
NETisCorrectVersion(uint32_t game_version_major,uint32_t game_version_minor)261 bool NETisCorrectVersion(uint32_t game_version_major, uint32_t game_version_minor)
262 {
263 	return (uint32_t)NETCODE_VERSION_MAJOR == game_version_major && (uint32_t)NETCODE_VERSION_MINOR == game_version_minor;
264 }
265 
NETisGreaterVersion(uint32_t game_version_major,uint32_t game_version_minor)266 bool NETisGreaterVersion(uint32_t game_version_major, uint32_t game_version_minor)
267 {
268 	return (game_version_major > NETCODE_VERSION_MAJOR) || ((game_version_major == NETCODE_VERSION_MAJOR) && (game_version_minor > NETCODE_VERSION_MINOR));
269 }
270 
NETGetMajorVersion()271 int NETGetMajorVersion()
272 {
273 	return NETCODE_VERSION_MAJOR;
274 }
275 
NETGetMinorVersion()276 int NETGetMinorVersion()
277 {
278 	return NETCODE_VERSION_MINOR;
279 }
280 
NETGameIsLocked()281 bool NETGameIsLocked()
282 {
283 	return NetPlay.GamePassworded;
284 }
285 
286 //	Sets if the game is password protected or not
NETGameLocked(bool flag)287 void NETGameLocked(bool flag)
288 {
289 	NetPlay.GamePassworded = flag;
290 	bool flagChanged = gamestruct.privateGame != (uint32_t)flag;
291 	gamestruct.privateGame = flag;
292 	if (allow_joining && NetPlay.isHost && flagChanged)
293 	{
294 		debug(LOG_NET, "Updating game locked status.");
295 		NETregisterServer(WZ_SERVER_UPDATE);
296 	}
297 	if (flagChanged)
298 	{
299 		ActivityManager::instance().updateMultiplayGameData(game, ingame, NETGameIsLocked());
300 	}
301 	NETlogEntry("Password is", SYNC_FLAG, NetPlay.GamePassworded);
302 	debug(LOG_NET, "Passworded game is %s", NetPlay.GamePassworded ? "TRUE" : "FALSE");
303 }
304 
NETsetLobbyOptField(const char * Value,const NET_LOBBY_OPT_FIELD Field)305 void NETsetLobbyOptField(const char *Value, const NET_LOBBY_OPT_FIELD Field)
306 {
307 	switch (Field)
308 	{
309 		case NET_LOBBY_OPT_FIELD::GNAME:
310 			sstrcpy(gamestruct.name, Value);
311 			break;
312 		case NET_LOBBY_OPT_FIELD::MAPNAME:
313 			sstrcpy(gamestruct.mapname, formatGameName(Value).toUtf8().c_str());
314 			break;
315 		case NET_LOBBY_OPT_FIELD::HOSTNAME:
316 			sstrcpy(gamestruct.hostname, Value);
317 			break;
318 		default:
319 			debug(LOG_WARNING, "Invalid field specified for NETsetGameOptField()");
320 			break;
321 	}
322 }
323 
324 //	Sets the game password
NETsetGamePassword(const char * password)325 void NETsetGamePassword(const char *password)
326 {
327 	sstrcpy(NetPlay.gamePassword, password);
328 	debug(LOG_NET, "Password entered is: [%s]", NetPlay.gamePassword);
329 	NETGameLocked(true);
330 }
331 
332 //	Resets the game password
NETresetGamePassword()333 void NETresetGamePassword()
334 {
335 	sstrcpy(NetPlay.gamePassword, _("Enter password here"));
336 	debug(LOG_NET, "password reset to 'Enter password here'");
337 	NETGameLocked(false);
338 }
339 
340 // *********** Socket with buffer that read NETMSGs ******************
341 
NET_fillBuffer(Socket ** pSocket,SocketSet * pSocketSet,uint8_t * bufstart,int bufsize)342 static size_t NET_fillBuffer(Socket **pSocket, SocketSet *pSocketSet, uint8_t *bufstart, int bufsize)
343 {
344 	Socket *socket = *pSocket;
345 	ssize_t size;
346 
347 	if (!socketReadReady(socket))
348 	{
349 		return 0;
350 	}
351 
352 	size_t rawBytes;
353 	size = readNoInt(socket, bufstart, bufsize, &rawBytes);
354 
355 	if ((size != 0 || !socketReadDisconnected(socket)) && size != SOCKET_ERROR)
356 	{
357 		nStats.rawBytes.received          += rawBytes;
358 		nStats.uncompressedBytes.received += size;
359 		nStats.packets.received           += 1;
360 
361 		return size;
362 	}
363 	else
364 	{
365 		if (size == 0)
366 		{
367 			debug(LOG_NET, "Connection closed from the other side");
368 			NETlogEntry("Connection closed from the other side..", SYNC_FLAG, selectedPlayer);
369 		}
370 		else
371 		{
372 			debug(LOG_NET, "%s tcp_socket %p is now invalid", strSockError(getSockErr()), static_cast<void *>(socket));
373 		}
374 
375 		// an error occurred, or the remote host has closed the connection.
376 		if (pSocketSet != nullptr)
377 		{
378 			SocketSet_DelSocket(pSocketSet, socket);
379 		}
380 
381 		ASSERT(size <= bufsize, "Socket buffer is too small!");
382 
383 		if (size > bufsize)
384 		{
385 			debug(LOG_ERROR, "Fatal connection error: buffer size of (%d) was too small, current byte count was %ld", bufsize, (long)size);
386 			NETlogEntry("Fatal connection error: buffer size was too small!", SYNC_FLAG, selectedPlayer);
387 		}
388 		if (bsocket == socket)
389 		{
390 			debug(LOG_NET, "Host connection was lost!");
391 			NETlogEntry("Host connection was lost!", SYNC_FLAG, selectedPlayer);
392 			bsocket = nullptr;
393 			//Game is pretty much over --should just end everything when HOST dies.
394 			NetPlay.isHostAlive = false;
395 			ingame.localJoiningInProgress = false;
396 			setLobbyError(ERROR_HOSTDROPPED);
397 			NETclose();
398 			return 0;
399 		}
400 		socketClose(socket);
401 		*pSocket = nullptr;
402 	}
403 
404 	return 0;
405 }
406 
playersPerTeam()407 static int playersPerTeam()
408 {
409 	for (int v = game.maxPlayers - 1; v > 1; --v)
410 	{
411 		if (game.maxPlayers%v == 0)
412 		{
413 			return v;
414 		}
415 	}
416 	return 1;
417 }
418 
419 /**
420  * Resets network properties of a player to safe defaults. Player slots should always be in this state
421  * before attemtping to assign a connectign player to it.
422  *
423  * Used to reset the player slot in NET_InitPlayer and to reset players slot without modifying ai/team/
424  * position configuration for the players.
425  */
initPlayerNetworkProps(int playerIndex)426 static void initPlayerNetworkProps(int playerIndex)
427 {
428 	NetPlay.players[playerIndex].allocated = false;
429 	NetPlay.players[playerIndex].autoGame = false;
430 	NetPlay.players[playerIndex].heartattacktime = 0;
431 	NetPlay.players[playerIndex].heartbeat = true;  // we always start with a heartbeat
432 	NetPlay.players[playerIndex].kick = false;
433 	NetPlay.players[playerIndex].ready = false;
434 
435 	NetPlay.players[playerIndex].wzFiles.clear();
436 	ingame.JoiningInProgress[playerIndex] = false;
437 }
438 
NET_InitPlayer(int i,bool initPosition,bool initTeams)439 void NET_InitPlayer(int i, bool initPosition, bool initTeams)
440 {
441 	initPlayerNetworkProps(i);
442 
443 	NetPlay.players[i].difficulty = AIDifficulty::DISABLED;
444 	if (ingame.localJoiningInProgress)
445 	{
446 		// only clear name outside of games.
447 		NetPlay.players[i].name[0] = '\0';
448 	}
449 	if (initPosition)
450 	{
451 		setPlayerColour(i, i);
452 		NetPlay.players[i].position = i;
453 		NetPlay.players[i].team = initTeams && i < game.maxPlayers? i/playersPerTeam() : i;
454 	}
455 	if (NetPlay.bComms)
456 	{
457 		NetPlay.players[i].ai = AI_OPEN;
458 	}
459 	else
460 	{
461 		NetPlay.players[i].ai = 0;
462 	}
463 	NetPlay.players[i].faction = FACTION_NORMAL;
464 }
465 
NET_numHumanPlayers(void)466 uint8_t NET_numHumanPlayers(void)
467 {
468 	uint8_t RetVal = 0;
469 	for (uint8_t Inc = 0; Inc < MAX_PLAYERS; ++Inc)
470 	{
471 		if (NetPlay.players[Inc].allocated) ++RetVal;
472 	}
473 
474 	return RetVal;
475 }
476 
NET_getHumanPlayers(void)477 std::vector<uint8_t> NET_getHumanPlayers(void)
478 {
479 	std::vector<uint8_t> RetVal;
480 	RetVal.reserve(MAX_PLAYERS);
481 
482 	for (uint8_t Inc = 0; Inc < MAX_PLAYERS; ++Inc)
483 	{
484 		if (NetPlay.players[Inc].allocated) RetVal.push_back(Inc);
485 	}
486 
487 	return RetVal;
488 }
489 
NET_InitPlayers(bool initTeams)490 void NET_InitPlayers(bool initTeams)
491 {
492 	for (unsigned i = 0; i < MAX_CONNECTED_PLAYERS; ++i)
493 	{
494 		NET_InitPlayer(i, true, initTeams);
495 		NetPlay.players[i].name[0] = '\0';
496 		NETinitQueue(NETnetQueue(i));
497 	}
498 	NETinitQueue(NETbroadcastQueue());
499 
500 	NetPlay.hostPlayer = NET_HOST_ONLY;	// right now, host starts always at index zero
501 	NetPlay.playercount = 0;
502 	NetPlay.wzFiles.clear();
503 	debug(LOG_NET, "Players initialized");
504 }
505 
NETSendNPlayerInfoTo(uint32_t * index,uint32_t indexLen,unsigned to)506 static void NETSendNPlayerInfoTo(uint32_t *index, uint32_t indexLen, unsigned to)
507 {
508 	NETbeginEncode(NETnetQueue(to), NET_PLAYER_INFO);
509 	NETuint32_t(&indexLen);
510 	for (unsigned n = 0; n < indexLen; ++n)
511 	{
512 		debug(LOG_NET, "sending player's (%u) info to all players", index[n]);
513 		NETlogEntry(" sending player's info to all players", SYNC_FLAG, index[n]);
514 		NETuint32_t(&index[n]);
515 		NETbool(&NetPlay.players[index[n]].allocated);
516 		NETbool(&NetPlay.players[index[n]].heartbeat);
517 		NETbool(&NetPlay.players[index[n]].kick);
518 		NETstring(NetPlay.players[index[n]].name, sizeof(NetPlay.players[index[n]].name));
519 		NETuint32_t(&NetPlay.players[index[n]].heartattacktime);
520 		NETint32_t(&NetPlay.players[index[n]].colour);
521 		NETint32_t(&NetPlay.players[index[n]].position);
522 		NETint32_t(&NetPlay.players[index[n]].team);
523 		NETbool(&NetPlay.players[index[n]].ready);
524 		NETint8_t(&NetPlay.players[index[n]].ai);
525 		NETint8_t(reinterpret_cast<int8_t*>(&NetPlay.players[index[n]].difficulty));
526 		NETuint8_t(reinterpret_cast<uint8_t *>(&NetPlay.players[index[n]].faction));
527 	}
528 	NETend();
529 	ActivityManager::instance().updateMultiplayGameData(game, ingame, NETGameIsLocked());
530 }
531 
NETSendPlayerInfoTo(uint32_t index,unsigned to)532 static void NETSendPlayerInfoTo(uint32_t index, unsigned to)
533 {
534 	NETSendNPlayerInfoTo(&index, 1, to);
535 }
536 
NETsendPlayerInfo(uint32_t index)537 static void NETsendPlayerInfo(uint32_t index)
538 {
539 	NETSendPlayerInfoTo(index, NET_HOST_ONLY);
540 }
541 
NETSendAllPlayerInfoTo(unsigned to)542 static void NETSendAllPlayerInfoTo(unsigned to)
543 {
544 	static uint32_t indices[MAX_PLAYERS];
545 	for (int i = 0; i < MAX_PLAYERS; ++i)
546 	{
547 		indices[i] = i;
548 	}
549 	ASSERT_OR_RETURN(, NetPlay.isHost == true, "Invalid call for non-host");
550 
551 	NETSendNPlayerInfoTo(indices, ARRAY_SIZE(indices), to);
552 }
553 
NETBroadcastTwoPlayerInfo(uint32_t index1,uint32_t index2)554 void NETBroadcastTwoPlayerInfo(uint32_t index1, uint32_t index2)
555 {
556 	uint32_t indices[2] = {index1, index2};
557 	NETSendNPlayerInfoTo(indices, 2, NET_ALL_PLAYERS);
558 }
559 
NETBroadcastPlayerInfo(uint32_t index)560 void NETBroadcastPlayerInfo(uint32_t index)
561 {
562 	NETSendPlayerInfoTo(index, NET_ALL_PLAYERS);
563 }
564 
NET_CreatePlayer(char const * name,bool forceTakeLowestAvailablePlayerNumber=false)565 static int NET_CreatePlayer(char const *name, bool forceTakeLowestAvailablePlayerNumber = false)
566 {
567 	int index = -1;
568 	int position = INT_MAX;
569 	// Only look for spots up to the max players allowed on the map.
570 	for (int i = 0; i < game.maxPlayers; ++i)
571 	{
572 		PLAYER const &p = NetPlay.players[i];
573 		if (!p.allocated && p.ai == AI_OPEN && p.position < position)
574 		{
575 			index = i;
576 			position = p.position;
577 			if (forceTakeLowestAvailablePlayerNumber)
578 			{
579 				break;
580 			}
581 		}
582 	}
583 
584 	if (index == -1)
585 	{
586 		debug(LOG_ERROR, "Could not find place for player %s", name);
587 		NETlogEntry("Could not find a place for player!", SYNC_FLAG, index);
588 		return -1;
589 	}
590 
591 	char buf[250] = {'\0'};
592 
593 	ssprintf(buf, "A new player has been created. Player, %s, is set to slot %u", name, index);
594 	debug(LOG_NET, "%s", buf);
595 	NETlogEntry(buf, SYNC_FLAG, index);
596 	NET_InitPlayer(index, false);  // re-init everything
597 	NetPlay.players[index].allocated = true;
598 	sstrcpy(NetPlay.players[index].name, name);
599 	++NetPlay.playercount;
600 	++sync_counter.joins;
601 	return index;
602 }
603 
NET_DestroyPlayer(unsigned int index,bool suppressActivityUpdates=false)604 static void NET_DestroyPlayer(unsigned int index, bool suppressActivityUpdates = false)
605 {
606 	debug(LOG_NET, "Freeing slot %u for a new player", index);
607 	NETlogEntry("Freeing slot for a new player.", SYNC_FLAG, index);
608 	bool wasAllocated = NetPlay.players[index].allocated;
609 	if (NetPlay.players[index].allocated)
610 	{
611 		NetPlay.players[index].allocated = false;
612 		NetPlay.playercount--;
613 		gamestruct.desc.dwCurrentPlayers = NetPlay.playercount;
614 		if (allow_joining && NetPlay.isHost)
615 		{
616 			// Update player count in the lobby by disconnecting
617 			// and reconnecting
618 			NETregisterServer(WZ_SERVER_UPDATE);
619 		}
620 	}
621 	NET_InitPlayer(index, false);  // reinitialize
622 	if (wasAllocated && !suppressActivityUpdates)
623 	{
624 		ActivityManager::instance().updateMultiplayGameData(game, ingame, NETGameIsLocked());
625 	}
626 }
627 
628 /**
629  * @note Connection dropped. Handle it gracefully.
630  * \param index
631  */
NETplayerClientDisconnect(uint32_t index)632 static void NETplayerClientDisconnect(uint32_t index)
633 {
634 	if (!NetPlay.isHost)
635 	{
636 		ASSERT(false, "Host only routine detected for client!");
637 		return;
638 	}
639 
640 	if (connected_bsocket[index])
641 	{
642 		debug(LOG_NET, "Player (%u) has left unexpectedly, closing socket %p", index, static_cast<void *>(connected_bsocket[index]));
643 
644 		NETplayerLeaving(index);
645 
646 		NETlogEntry("Player has left unexpectedly.", SYNC_FLAG, index);
647 		// Announce to the world. This was really icky, because we may have been calling the send
648 		// function recursively. We really ought to have had a send queue, and now we finally do...
649 		if (ingame.localJoiningInProgress)  // Only if game hasn't actually started yet.
650 		{
651 			NETbeginEncode(NETbroadcastQueue(), NET_PLAYER_DROPPED);
652 			NETuint32_t(&index);
653 			NETend();
654 		}
655 	}
656 	else
657 	{
658 		debug(LOG_ERROR, "Player (%u) has left unexpectedly - but socket already closed?", index);
659 	}
660 }
661 
662 /**
663  * @note When a player leaves nicely (ie, we got a NET_PLAYER_LEAVING
664  *       message), we clean up the socket that we used.
665  * \param index
666  */
NETplayerLeaving(UDWORD index)667 static void NETplayerLeaving(UDWORD index)
668 {
669 	if (connected_bsocket[index])
670 	{
671 		debug(LOG_NET, "Player (%u) has left, closing socket %p", index, static_cast<void *>(connected_bsocket[index]));
672 		NETlogEntry("Player has left nicely.", SYNC_FLAG, index);
673 
674 		// Although we can get a error result from DelSocket, it don't really matter here.
675 		SocketSet_DelSocket(socket_set, connected_bsocket[index]);
676 		socketClose(connected_bsocket[index]);
677 		connected_bsocket[index] = nullptr;
678 	}
679 	else
680 	{
681 		debug(LOG_NET, "Player (%u) has left nicely, socket already closed?", index);
682 	}
683 	sync_counter.left++;
684 	MultiPlayerLeave(index);		// more cleanup
685 	if (ingame.localJoiningInProgress)  // Only if game hasn't actually started yet.
686 	{
687 		NET_DestroyPlayer(index);       // sets index player's array to false
688 		resetReadyStatus(false);		// reset ready status for all players
689 	}
690 }
691 
692 /**
693  * @note When a player's connection is broken we broadcast the NET_PLAYER_DROPPED
694  *       message.
695  * \param index
696  */
NETplayerDropped(UDWORD index)697 static void NETplayerDropped(UDWORD index)
698 {
699 	uint32_t id = index;
700 
701 	if (!NetPlay.isHost)
702 	{
703 		ASSERT(false, "Host only routine detected for client!");
704 		return;
705 	}
706 
707 	sync_counter.drops++;
708 	MultiPlayerLeave(id);			// more cleanup
709 	if (ingame.localJoiningInProgress)  // Only if game hasn't actually started yet.
710 	{
711 		// Send message type specifically for dropped / disconnects
712 		NETbeginEncode(NETbroadcastQueue(), NET_PLAYER_DROPPED);
713 		NETuint32_t(&id);
714 		NETend();
715 		debug(LOG_INFO, "sending NET_PLAYER_DROPPED for player %d", id);
716 		NET_DestroyPlayer(id);          // just clears array
717 		resetReadyStatus(false);		// reset ready status for all players
718 	}
719 
720 	NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, id);
721 }
722 
723 /**
724  * @note Cleanup for when a player is kicked.
725  * \param index
726  */
NETplayerKicked(UDWORD index)727 void NETplayerKicked(UDWORD index)
728 {
729 	ASSERT_OR_RETURN(, index < MAX_PLAYERS, "NETplayerKicked invalid player_id: (%" PRIu32")", index);
730 
731 	// kicking a player counts as "leaving nicely", since "nicely" in this case
732 	// simply means "there wasn't a connection error."
733 	debug(LOG_INFO, "Player %u was kicked.", index);
734 	sync_counter.kicks++;
735 	NETlogEntry("Player was kicked.", SYNC_FLAG, index);
736 	if (NetPlay.isHost && NetPlay.players[index].allocated)
737 	{
738 		addToBanList(NetPlay.players[index].IPtextAddress, NetPlay.players[index].name);
739 	}
740 	NETplayerLeaving(index);		// need to close socket for the player that left.
741 	NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_LEAVING, index);
742 }
743 
744 // ////////////////////////////////////////////////////////////////////////
745 // rename the local player
NETchangePlayerName(UDWORD index,char * newName)746 bool NETchangePlayerName(UDWORD index, char *newName)
747 {
748 	if (!NetPlay.bComms)
749 	{
750 		sstrcpy(NetPlay.players[0].name, newName);
751 		return true;
752 	}
753 	debug(LOG_NET, "Requesting a change of player name for pid=%u to %s", index, newName);
754 	NETlogEntry("Player wants a name change.", SYNC_FLAG, index);
755 
756 	sstrcpy(NetPlay.players[index].name, newName);
757 	if (NetPlay.isHost)
758 	{
759 		NETSendAllPlayerInfoTo(NET_ALL_PLAYERS);
760 	}
761 	else
762 	{
763 		NETsendPlayerInfo(index);
764 	}
765 
766 	return true;
767 }
768 
NETfixDuplicatePlayerNames()769 void NETfixDuplicatePlayerNames()
770 {
771 	char name[StringSize];
772 	unsigned i, j, pass;
773 	for (i = 1; i != MAX_PLAYERS; ++i)
774 	{
775 		sstrcpy(name, NetPlay.players[i].name);
776 		if (name[0] == '\0' || !NetPlay.players[i].allocated)
777 		{
778 			continue;  // Ignore empty names.
779 		}
780 		for (pass = 0; pass != 101; ++pass)
781 		{
782 			if (pass != 0)
783 			{
784 				ssprintf(name, "%s_%X", NetPlay.players[i].name, pass + 1);
785 			}
786 
787 			for (j = 0; j != i; ++j)
788 			{
789 				if (strcmp(name, NetPlay.players[j].name) == 0)
790 				{
791 					break;  // Duplicate name.
792 				}
793 			}
794 
795 			if (i == j)
796 			{
797 				break;  // Unique name.
798 			}
799 		}
800 		if (pass != 0)
801 		{
802 			NETchangePlayerName(i, name);
803 		}
804 	}
805 }
806 
807 // ////////////////////////////////////////////////////////////////////////
808 // return one of the four user flags in the current sessiondescription.
NETgetGameFlags(UDWORD flag)809 SDWORD NETgetGameFlags(UDWORD flag)
810 {
811 	if (flag < 1 || flag > 4)
812 	{
813 		return 0;
814 	}
815 	else
816 	{
817 		return NetGameFlags[flag - 1];
818 	}
819 }
820 
NETsendGameFlags()821 static void NETsendGameFlags()
822 {
823 	debug(LOG_NET, "sending game flags");
824 	NETbeginEncode(NETbroadcastQueue(), NET_GAME_FLAGS);
825 	{
826 		// Send the amount of game flags we're about to send
827 		uint8_t i, count = ARRAY_SIZE(NetGameFlags);
828 		NETuint8_t(&count);
829 
830 		// Send over all game flags
831 		for (i = 0; i < count; ++i)
832 		{
833 			NETint32_t(&NetGameFlags[i]);
834 		}
835 	}
836 	NETend();
837 }
838 
839 // ////////////////////////////////////////////////////////////////////////
840 // Set a game flag
NETsetGameFlags(UDWORD flag,SDWORD value)841 bool NETsetGameFlags(UDWORD flag, SDWORD value)
842 {
843 	if (!NetPlay.bComms)
844 	{
845 		return true;
846 	}
847 
848 	if (flag > 0 && flag < 5)
849 	{
850 		return (NetGameFlags[flag - 1] = value);
851 	}
852 
853 	NETsendGameFlags();
854 
855 	return true;
856 }
857 
858 /**
859  * @note \c game is being sent to the master server (if hosting)
860  *       The implementation of NETsendGAMESTRUCT <em>must</em> guarantee to
861  *       pack it in network byte order (big-endian).
862  *
863  * @return true on success, false when a socket error has occurred
864  *
865  * @see GAMESTRUCT,NETrecvGAMESTRUCT
866  */
NETsendGAMESTRUCT(Socket * sock,const GAMESTRUCT * ourgamestruct)867 static bool NETsendGAMESTRUCT(Socket *sock, const GAMESTRUCT *ourgamestruct)
868 {
869 	// A buffer that's guaranteed to have the correct size (i.e. it
870 	// circumvents struct padding, which could pose a problem).  Initialise
871 	// to zero so that we can be sure we're not sending any (undefined)
872 	// memory content across the network.
873 	char buf[sizeof(ourgamestruct->GAMESTRUCT_VERSION) + sizeof(ourgamestruct->name) + sizeof(ourgamestruct->desc.host) + (sizeof(int32_t) * 8) +
874 	         sizeof(ourgamestruct->secondaryHosts) + sizeof(ourgamestruct->extra) + sizeof(ourgamestruct->hostPort) + sizeof(ourgamestruct->mapname) + sizeof(ourgamestruct->hostname) + sizeof(ourgamestruct->versionstring) +
875 	         sizeof(ourgamestruct->modlist) + (sizeof(uint32_t) * 9) ] = { 0 };
876 	char *buffer = buf;
877 	unsigned int i;
878 	ssize_t result;
879 
880 	auto push32 = [&](uint32_t value) {
881 		uint32_t swapped = htonl(value);
882 		memcpy(buffer, &swapped, sizeof(swapped));
883 		buffer += sizeof(swapped);
884 	};
885 
886 	auto push16 = [&](uint16_t value) {
887 		uint16_t swapped = htons(value);
888 		memcpy(buffer, &swapped, sizeof(swapped));
889 		buffer += sizeof(swapped);
890 	};
891 
892 	// Now dump the data into the buffer
893 	// Copy 32bit large big endian numbers
894 	push32(ourgamestruct->GAMESTRUCT_VERSION);
895 
896 	// Copy a string
897 	strlcpy(buffer, ourgamestruct->name, sizeof(ourgamestruct->name));
898 	buffer += sizeof(ourgamestruct->name);
899 
900 	// Copy 32bit large big endian numbers
901 	push32(ourgamestruct->desc.dwSize);
902 	push32(ourgamestruct->desc.dwFlags);
903 
904 	// Copy yet another string
905 	strlcpy(buffer, ourgamestruct->desc.host, sizeof(ourgamestruct->desc.host));
906 	buffer += sizeof(ourgamestruct->desc.host);
907 
908 	// Copy 32bit large big endian numbers
909 	push32(ourgamestruct->desc.dwMaxPlayers);
910 	push32(ourgamestruct->desc.dwCurrentPlayers);
911 	for (i = 0; i < ARRAY_SIZE(ourgamestruct->desc.dwUserFlags); ++i)
912 	{
913 		push32(ourgamestruct->desc.dwUserFlags[i]);
914 	}
915 
916 	// Copy a string
917 	for (i = 0; i < ARRAY_SIZE(ourgamestruct->secondaryHosts); ++i)
918 	{
919 		strlcpy(buffer, ourgamestruct->secondaryHosts[i], sizeof(ourgamestruct->secondaryHosts[i]));
920 		buffer += sizeof(ourgamestruct->secondaryHosts[i]);
921 	}
922 
923 	// Copy a string
924 	strlcpy(buffer, ourgamestruct->extra, sizeof(ourgamestruct->extra));
925 	buffer += sizeof(ourgamestruct->extra);
926 
927 	// Copy 16bit large big endian number
928 	push16(ourgamestruct->hostPort);
929 
930 	// Copy a string
931 	strlcpy(buffer, ourgamestruct->mapname, sizeof(ourgamestruct->mapname));
932 	buffer += sizeof(ourgamestruct->mapname);
933 
934 	// Copy a string
935 	strlcpy(buffer, ourgamestruct->hostname, sizeof(ourgamestruct->hostname));
936 	buffer += sizeof(ourgamestruct->hostname);
937 
938 	// Copy a string
939 	strlcpy(buffer, ourgamestruct->versionstring, sizeof(ourgamestruct->versionstring));
940 	buffer += sizeof(ourgamestruct->versionstring);
941 
942 	// Copy a string
943 	strlcpy(buffer, ourgamestruct->modlist, sizeof(ourgamestruct->modlist));
944 	buffer += sizeof(ourgamestruct->modlist);
945 
946 	// Copy 32bit large big endian numbers
947 	push32(ourgamestruct->game_version_major);
948 
949 	// Copy 32bit large big endian numbers
950 	push32(ourgamestruct->game_version_minor);
951 
952 	// Copy 32bit large big endian numbers
953 	push32(ourgamestruct->privateGame);
954 
955 	// Copy 32bit large big endian numbers
956 	push32(ourgamestruct->pureMap);
957 
958 	// Copy 32bit large big endian numbers
959 	push32(ourgamestruct->Mods);
960 
961 	// Copy 32bit large big endian numbers
962 	push32(ourgamestruct->gameId);
963 
964 	// Copy 32bit large big endian numbers
965 	push32(ourgamestruct->limits);
966 
967 	// Copy 32bit large big endian numbers
968 	push32(ourgamestruct->future3);
969 
970 	// Copy 32bit large big endian numbers
971 	push32(ourgamestruct->future4);
972 
973 	debug(LOG_NET, "sending GAMESTRUCT, size: %u", (unsigned int)sizeof(buf));
974 
975 	// Send over the GAMESTRUCT
976 	result = writeAll(sock, buf, sizeof(buf));
977 	if (result == SOCKET_ERROR)
978 	{
979 		const int err = getSockErr();
980 
981 		// If packet could not be sent, we should inform user of the error.
982 		debug(LOG_ERROR, "Failed to send GAMESTRUCT. Reason: %s", strSockError(getSockErr()));
983 		debug(LOG_ERROR, "Please make sure TCP ports %u & %u are open!", masterserver_port, gameserver_port);
984 
985 		setSockErr(err);
986 		return false;
987 	}
988 
989 	return true;
990 }
991 
992 /**
993  * @note \c game is being retrieved from the master server (if browsing the
994  *       lobby). The implementation of NETrecvGAMESTRUCT should assume the data
995  *       to be packed in network byte order (big-endian).
996  *
997  * @see GAMESTRUCT,NETsendGAMESTRUCT
998  */
NETrecvGAMESTRUCT(Socket * sock,GAMESTRUCT * ourgamestruct)999 static bool NETrecvGAMESTRUCT(Socket *sock, GAMESTRUCT *ourgamestruct)
1000 {
1001 	// A buffer that's guaranteed to have the correct size (i.e. it
1002 	// circumvents struct padding, which could pose a problem).
1003 	char buf[sizeof(ourgamestruct->GAMESTRUCT_VERSION) + sizeof(ourgamestruct->name) + sizeof(ourgamestruct->desc.host) + (sizeof(int32_t) * 8) +
1004 	         sizeof(ourgamestruct->secondaryHosts) + sizeof(ourgamestruct->extra) + sizeof(ourgamestruct->hostPort) + sizeof(ourgamestruct->mapname) + sizeof(ourgamestruct->hostname) + sizeof(ourgamestruct->versionstring) +
1005 	         sizeof(ourgamestruct->modlist) + (sizeof(uint32_t) * 9) ] = { 0 };
1006 	char *buffer = buf;
1007 	unsigned int i;
1008 	ssize_t result = 0;
1009 
1010 	auto pop32 = [&]() -> uint32_t {
1011 		uint32_t value = 0;
1012 		memcpy(&value, buffer, sizeof(value));
1013 		value = ntohl(value);
1014 		buffer += sizeof(value);
1015 		return value;
1016 	};
1017 
1018 	auto pop16 = [&]() -> uint16_t {
1019 		uint16_t value = 0;
1020 		memcpy(&value, buffer, sizeof(value));
1021 		value = ntohs(value);
1022 		buffer += sizeof(value);
1023 		return value;
1024 	};
1025 
1026 	// Read a GAMESTRUCT from the connection
1027 	result = readAll(sock, buf, sizeof(buf), NET_TIMEOUT_DELAY);
1028 	bool failed = false;
1029 	if (result == SOCKET_ERROR)
1030 	{
1031 		debug(LOG_ERROR, "Lobby server connection error: %s", strSockError(getSockErr()));
1032 		failed = true;
1033 	}
1034 	else if ((unsigned)result != sizeof(buf))
1035 	{
1036 		debug(LOG_ERROR, "GAMESTRUCT recv timed out; received %d bytes; expecting %d", (int)result, (int)sizeof(buf));
1037 		failed = true;
1038 	}
1039 	if (failed)
1040 	{
1041 		// caller handles invalidating and closing tcp_socket
1042 		return false;
1043 	}
1044 
1045 	// Now dump the data into the game struct
1046 	// Copy 32bit large big endian numbers
1047 	ourgamestruct->GAMESTRUCT_VERSION = pop32();
1048 	// Copy a string
1049 	sstrcpy(ourgamestruct->name, buffer);
1050 	buffer += sizeof(ourgamestruct->name);
1051 
1052 	// Copy 32bit large big endian numbers
1053 	ourgamestruct->desc.dwSize = pop32();
1054 	ourgamestruct->desc.dwFlags = pop32();
1055 
1056 	// Copy yet another string
1057 	sstrcpy(ourgamestruct->desc.host, buffer);
1058 	buffer += sizeof(ourgamestruct->desc.host);
1059 
1060 	// Copy 32bit large big endian numbers
1061 	ourgamestruct->desc.dwMaxPlayers = pop32();
1062 	ourgamestruct->desc.dwCurrentPlayers = pop32();
1063 	for (i = 0; i < ARRAY_SIZE(ourgamestruct->desc.dwUserFlags); ++i)
1064 	{
1065 		ourgamestruct->desc.dwUserFlags[i] = pop32();
1066 	}
1067 
1068 	// Copy a string
1069 	for (i = 0; i < ARRAY_SIZE(ourgamestruct->secondaryHosts); ++i)
1070 	{
1071 		sstrcpy(ourgamestruct->secondaryHosts[i], buffer);
1072 		buffer += sizeof(ourgamestruct->secondaryHosts[i]);
1073 	}
1074 
1075 	// Copy a string
1076 	sstrcpy(ourgamestruct->extra, buffer);
1077 	buffer += sizeof(ourgamestruct->extra);
1078 
1079 	// Copy 16-bit host port
1080 	ourgamestruct->hostPort = pop16();
1081 
1082 	// Copy a string
1083 	sstrcpy(ourgamestruct->mapname, buffer);
1084 	buffer += sizeof(ourgamestruct->mapname);
1085 
1086 	// Copy a string
1087 	sstrcpy(ourgamestruct->hostname, buffer);
1088 	buffer += sizeof(ourgamestruct->hostname);
1089 
1090 	// Copy a string
1091 	sstrcpy(ourgamestruct->versionstring, buffer);
1092 	buffer += sizeof(ourgamestruct->versionstring);
1093 
1094 	// Copy a string
1095 	sstrcpy(ourgamestruct->modlist, buffer);
1096 	buffer += sizeof(ourgamestruct->modlist);
1097 
1098 	// Copy 32bit large big endian numbers
1099 	ourgamestruct->game_version_major = pop32();
1100 	ourgamestruct->game_version_minor = pop32();
1101 	ourgamestruct->privateGame = pop32();
1102 	ourgamestruct->pureMap = pop32();
1103 	ourgamestruct->Mods = pop32();
1104 	ourgamestruct->gameId = pop32();
1105 	ourgamestruct->limits = pop32();
1106 	ourgamestruct->future3 = pop32();
1107 	ourgamestruct->future4 = pop32();
1108 
1109 	debug(LOG_NET, "received GAMESTRUCT");
1110 
1111 	return true;
1112 }
1113 
1114 // This function is run in its own thread! Do not call any non-threadsafe functions!
upnp_init(std::atomic_int & retval)1115 static void upnp_init(std::atomic_int &retval)
1116 {
1117 	struct UPNPDev *devlist;
1118 	struct UPNPDev *dev;
1119 	char *descXML;
1120 	int result = 0;
1121 	int descXMLsize = 0;
1122 	memset(&urls, 0, sizeof(struct UPNPUrls));
1123 	memset(&data, 0, sizeof(struct IGDdatas));
1124 
1125 	debug(LOG_NET, "Searching for UPnP devices for automatic port forwarding...");
1126 #if defined(MINIUPNPC_API_VERSION) && (MINIUPNPC_API_VERSION >= 14)
1127 	devlist = upnpDiscover(3000, nullptr, nullptr, 0, 0, 2, &result);
1128 #else
1129 	devlist = upnpDiscover(3000, nullptr, nullptr, 0, 0, &result);
1130 #endif
1131 	debug(LOG_NET, "UPnP device search finished.");
1132 
1133 	if (devlist)
1134 	{
1135 		dev = devlist;
1136 		while (dev)
1137 		{
1138 			if (strstr(dev->st, "InternetGatewayDevice"))
1139 			{
1140 				break;
1141 			}
1142 			dev = dev->pNext;
1143 		}
1144 		if (!dev)
1145 		{
1146 			dev = devlist; /* defaulting to first device */
1147 		}
1148 
1149 		debug(LOG_NET, "UPnP device found: %s %s\n", dev->descURL, dev->st);
1150 
1151 #if defined(MINIUPNPC_API_VERSION) && (MINIUPNPC_API_VERSION >= 16)
1152 		int status_code = -1;
1153 		descXML = (char *)miniwget_getaddr(dev->descURL, &descXMLsize, lanaddr, sizeof(lanaddr), dev->scope_id, &status_code);
1154 		if (status_code != 200)
1155 		{
1156 			if (descXML)
1157 			{
1158 				free(descXML);
1159 				descXML = nullptr;
1160 			}
1161 			debug(LOG_NET, "HTTP error %d fetching: %s", status_code, (dev->descURL) ? dev->descURL : "");
1162 		}
1163 #else
1164 		descXML = (char *)miniwget_getaddr(dev->descURL, &descXMLsize, lanaddr, sizeof(lanaddr), dev->scope_id);
1165 #endif
1166 		debug(LOG_NET, "LAN address: %s", lanaddr);
1167 		if (descXML)
1168 		{
1169 			parserootdesc(descXML, descXMLsize, &data);
1170 			free(descXML); descXML = nullptr;
1171 			GetUPNPUrls(&urls, &data, dev->descURL, dev->scope_id);
1172 		}
1173 
1174 		debug(LOG_NET, "UPnP device found: %s %s LAN address %s", dev->descURL, dev->st, lanaddr);
1175 		freeUPNPDevlist(devlist);
1176 
1177 		if (!urls.controlURL || urls.controlURL[0] == '\0')
1178 		{
1179 			retval = UPNP_ERROR_CONTROL_NOT_AVAILABLE;
1180 		}
1181 		else
1182 		{
1183 			retval = UPNP_SUCCESS;
1184 		}
1185 	}
1186 	else
1187 	{
1188 		retval = UPNP_ERROR_DEVICE_NOT_FOUND;
1189 	}
1190 }
1191 
upnp_add_redirect(int port)1192 static bool upnp_add_redirect(int port)
1193 {
1194 	char port_str[16];
1195 	char buf[512] = {'\0'};
1196 	int r;
1197 
1198 	debug(LOG_NET, "upnp_add_redir(%d)", port);
1199 	sprintf(port_str, "%d", port);
1200 	r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
1201 	                        port_str, port_str, lanaddr, "Warzone 2100", "TCP", nullptr, "0");	// "0" = lease time unlimited
1202 	if (r != UPNPCOMMAND_SUCCESS)
1203 	{
1204 		ssprintf(buf, _("Could not open required port (%s) on  (%s)"), port_str, lanaddr);
1205 		debug(LOG_NET, "%s", buf);
1206 		addConsoleMessage(buf, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
1207 		// beware of writing a line too long, it screws up console line count. \n is safe for line split
1208 		ssprintf(buf, "%s", _("You must manually configure your router & firewall to\n open port 2100 before you can host a game."));
1209 		addConsoleMessage(buf, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
1210 		return false;
1211 	}
1212 
1213 	r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
1214 	if (r != UPNPCOMMAND_SUCCESS)
1215 	{
1216 		ssprintf(externalIPAddress, "%s", "???");
1217 	}
1218 	ssprintf(buf, _("Game configured port (%s) correctly on (%s)\nYour external IP is %s"), port_str, lanaddr, externalIPAddress);
1219 	addConsoleMessage(buf, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
1220 
1221 	return true;
1222 }
1223 
upnp_rem_redirect(int port)1224 static int upnp_rem_redirect(int port)
1225 {
1226 	char port_str[16];
1227 	debug(LOG_NET, "upnp_rem_redir(%d)", port);
1228 	sprintf(port_str, "%d", port);
1229 	return UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port_str, "TCP", nullptr);
1230 }
1231 
NETaddRedirects()1232 void NETaddRedirects()
1233 {
1234 	if (upnp_add_redirect(gameserver_port))
1235 	{
1236 		debug(LOG_NET, "successful!");
1237 		NetPlay.isUPNP_CONFIGURED = true;
1238 		NetPlay.isUPNP_ERROR = false;
1239 	}
1240 	else
1241 	{
1242 		debug(LOG_NET, "failed!");
1243 		NetPlay.isUPNP_CONFIGURED = false;
1244 		NetPlay.isUPNP_ERROR = true;
1245 	}
1246 }
1247 
NETremRedirects()1248 void NETremRedirects()
1249 {
1250 	debug(LOG_NET, "upnp is %d", NetPlay.isUPNP_CONFIGURED);
1251 	if (NetPlay.isUPNP_CONFIGURED)
1252 	{
1253 		int result = upnp_rem_redirect(gameserver_port);
1254 		if (!result)
1255 		{
1256 			debug(LOG_NET, "removed UPnP entry.");
1257 		}
1258 		else
1259 		{
1260 			debug(LOG_ERROR, "Failed to remove UPnP entry for the game. You must manually remove it from your router. (%d)", result);
1261 		}
1262 	}
1263 }
1264 
NETdiscoverUPnPDevices()1265 void NETdiscoverUPnPDevices()
1266 {
1267 	if (!NetPlay.isUPNP_CONFIGURED && NetPlay.isUPNP)
1268 	{
1269 		wz::thread t(upnp_init, std::ref(upnp_status));
1270 		t.detach();
1271 	}
1272 	else if (!NetPlay.isUPNP)
1273 	{
1274 		debug(LOG_INFO, "UPnP detection disabled by user.");
1275 	}
1276 }
1277 
1278 // ////////////////////////////////////////////////////////////////////////
1279 // setup stuff
NETinit(bool bFirstCall)1280 int NETinit(bool bFirstCall)
1281 {
1282 	debug(LOG_NET, "NETinit");
1283 	upnp_status = 0;
1284 	NETlogEntry("NETinit!", SYNC_FLAG, selectedPlayer);
1285 	NET_InitPlayers(true);
1286 
1287 	SOCKETinit();
1288 
1289 	if (bFirstCall)
1290 	{
1291 		debug(LOG_NET, "NETPLAY: Init called, MORNIN'");
1292 
1293 		// NOTE NetPlay.isUPNP is already set in configuration.c!
1294 		NetPlay.bComms = true;
1295 		NetPlay.GamePassworded = false;
1296 		NetPlay.ShowedMOTD = false;
1297 		NetPlay.isHostAlive = false;
1298 		NetPlay.HaveUpgrade = false;
1299 		NetPlay.gamePassword[0] = '\0';
1300 		NetPlay.MOTD = nullptr;
1301 		sstrcpy(NetPlay.gamePassword, _("Enter password here"));
1302 		NETstartLogging();
1303 	}
1304 
1305 	if (NetPlay.MOTD)
1306 	{
1307 		free(NetPlay.MOTD);
1308 	}
1309 	NetPlay.MOTD = nullptr;
1310 	NetPlay.ShowedMOTD = false;
1311 	NetPlay.GamePassworded = false;
1312 	memset(&sync_counter, 0x0, sizeof(sync_counter));	//clear counters
1313 
1314 	return 0;
1315 }
1316 
1317 // ////////////////////////////////////////////////////////////////////////
1318 // SHUTDOWN THE CONNECTION.
NETshutdown()1319 int NETshutdown()
1320 {
1321 	debug(LOG_NET, "NETshutdown");
1322 	NETlogEntry("NETshutdown", SYNC_FLAG, selectedPlayer);
1323 	if (NetPlay.bComms && NetPlay.isUPNP)
1324 	{
1325 		NETremRedirects();
1326 		NetPlay.isUPNP_CONFIGURED = false;
1327 		NetPlay.isUPNP_ERROR = false;
1328 	}
1329 	NETstopLogging();
1330 	if (IPlist)
1331 	{
1332 		free(IPlist);
1333 	}
1334 	IPlist = nullptr;
1335 	IPlistLast = 0;
1336 	if (NetPlay.MOTD)
1337 	{
1338 		free(NetPlay.MOTD);
1339 	}
1340 	NetPlay.MOTD = nullptr;
1341 	NETdeleteQueue();
1342 	SOCKETshutdown();
1343 
1344 	// Reset net usage statistics.
1345 	nStats = nZeroStats;
1346 	nStatsLastSec = nZeroStats;
1347 	nStatsSecondLastSec = nZeroStats;
1348 
1349 	return 0;
1350 }
1351 
1352 // ////////////////////////////////////////////////////////////////////////
1353 //close the open game..
NETclose()1354 int NETclose()
1355 {
1356 	unsigned int i;
1357 
1358 	// reset flag
1359 	NetPlay.ShowedMOTD = false;
1360 	NEThaltJoining();
1361 
1362 	debug(LOG_NET, "Terminating sockets.");
1363 
1364 	NetPlay.isHost = false;
1365 	server_not_there = false;
1366 	allow_joining = false;
1367 
1368 	for (i = 0; i < MAX_CONNECTED_PLAYERS; i++)
1369 	{
1370 		if (connected_bsocket[i])
1371 		{
1372 			debug(LOG_NET, "Closing connected_bsocket[%u], %p", i, static_cast<void *>(connected_bsocket[i]));
1373 			socketClose(connected_bsocket[i]);
1374 			connected_bsocket[i] = nullptr;
1375 		}
1376 		NET_DestroyPlayer(i, true);
1377 	}
1378 
1379 	if (tmp_socket_set)
1380 	{
1381 		debug(LOG_NET, "Freeing tmp_socket_set %p", static_cast<void *>(tmp_socket_set));
1382 		deleteSocketSet(tmp_socket_set);
1383 		tmp_socket_set = nullptr;
1384 	}
1385 
1386 	for (i = 0; i < MAX_TMP_SOCKETS; i++)
1387 	{
1388 		if (tmp_socket[i])
1389 		{
1390 			// FIXME: need SocketSet_DelSocket() as well, socket_set or tmp_socket_set?
1391 			debug(LOG_NET, "Closing tmp_socket[%d] %p", i, static_cast<void *>(tmp_socket[i]));
1392 			socketClose(tmp_socket[i]);
1393 			tmp_socket[i] = nullptr;
1394 		}
1395 	}
1396 
1397 	if (socket_set)
1398 	{
1399 		// checking to make sure tcp_socket is still valid
1400 		if (tcp_socket)
1401 		{
1402 			SocketSet_DelSocket(socket_set, tcp_socket);
1403 		}
1404 		if (bsocket)
1405 		{
1406 			SocketSet_DelSocket(socket_set, bsocket);
1407 		}
1408 		debug(LOG_NET, "Freeing socket_set %p", static_cast<void *>(socket_set));
1409 		deleteSocketSet(socket_set);
1410 		socket_set = nullptr;
1411 	}
1412 	if (tcp_socket)
1413 	{
1414 		debug(LOG_NET, "Closing tcp_socket %p", static_cast<void *>(tcp_socket));
1415 		socketClose(tcp_socket);
1416 		tcp_socket = nullptr;
1417 	}
1418 	if (bsocket)
1419 	{
1420 		debug(LOG_NET, "Closing bsocket %p", static_cast<void *>(bsocket));
1421 		socketClose(bsocket);
1422 		bsocket = nullptr;
1423 	}
1424 
1425 	return 0;
1426 }
1427 
1428 
1429 // ////////////////////////////////////////////////////////////////////////
1430 // ////////////////////////////////////////////////////////////////////////
1431 // Send and Recv functions
1432 
1433 // ////////////////////////////////////////////////////////////////////////
1434 // return bytes of data sent recently.
NETgetStatistic(NetStatisticType type,bool sent,bool isTotal)1435 size_t NETgetStatistic(NetStatisticType type, bool sent, bool isTotal)
1436 {
1437 	size_t Statistic::*statisticType = sent ? &Statistic::sent : &Statistic::received;
1438 	Statistic NETSTATS::*statsType;
1439 	switch (type)
1440 	{
1441 	case NetStatisticRawBytes:          statsType = &NETSTATS::rawBytes;          break;
1442 	case NetStatisticUncompressedBytes: statsType = &NETSTATS::uncompressedBytes; break;
1443 	case NetStatisticPackets:           statsType = &NETSTATS::packets;           break;
1444 	default: ASSERT(false, " "); return 0;
1445 	}
1446 
1447 	int time = wzGetTicks();
1448 	if ((unsigned)(time - nStatsLastUpdateTime) >= (unsigned)GAME_TICKS_PER_SEC)
1449 	{
1450 		nStatsLastUpdateTime = time;
1451 		nStatsSecondLastSec = nStatsLastSec;
1452 		nStatsLastSec = nStats;
1453 	}
1454 
1455 	if (isTotal)
1456 	{
1457 		return nStats.*statsType.*statisticType;
1458 	}
1459 	return nStatsLastSec.*statsType.*statisticType - nStatsSecondLastSec.*statsType.*statisticType;
1460 }
1461 
1462 
1463 // ////////////////////////////////////////////////////////////////////////
1464 // Send a message to a player, option to guarantee message
NETsend(NETQUEUE queue,NetMessage const * message)1465 bool NETsend(NETQUEUE queue, NetMessage const *message)
1466 {
1467 	uint8_t player = queue.index;
1468 	ssize_t result = 0;
1469 
1470 	if (!NetPlay.bComms)
1471 	{
1472 		return true;
1473 	}
1474 
1475 	Socket **sockets = connected_bsocket;
1476 	bool isTmpQueue = false;
1477 	switch (queue.queueType)
1478 	{
1479 	case QUEUE_BROADCAST:
1480 		ASSERT_OR_RETURN(false, player == NET_ALL_PLAYERS, "Wrong queue index.");
1481 		break;
1482 	case QUEUE_NET:
1483 		ASSERT_OR_RETURN(false, player < MAX_CONNECTED_PLAYERS, "Wrong queue index.");
1484 		break;
1485 	case QUEUE_TMP:
1486 		sockets = tmp_socket;
1487 		isTmpQueue = true;
1488 
1489 		ASSERT_OR_RETURN(false, player < MAX_TMP_SOCKETS && NetPlay.isHost, "Wrong queue index.");
1490 		break;
1491 	default:
1492 		ASSERT_OR_RETURN(false, false, "Wrong queue type.");
1493 	}
1494 
1495 	if (NetPlay.isHost)
1496 	{
1497 		int firstPlayer = player == NET_ALL_PLAYERS ? 0                         : player;
1498 		int lastPlayer  = player == NET_ALL_PLAYERS ? MAX_CONNECTED_PLAYERS - 1 : player;
1499 		for (player = firstPlayer; player <= lastPlayer; ++player)
1500 		{
1501 			// We are the host, send directly to player.
1502 			if (sockets[player] != nullptr && player != queue.exclude)
1503 			{
1504 				uint8_t *rawData = message->rawDataDup();
1505 				ssize_t rawLen   = message->rawLen();
1506 				size_t compressedRawLen;
1507 				result = writeAll(sockets[player], rawData, rawLen, &compressedRawLen);
1508 				delete[] rawData;  // Done with the data.
1509 
1510 				if (result == rawLen)
1511 				{
1512 					nStats.rawBytes.sent          += compressedRawLen;
1513 					nStats.uncompressedBytes.sent += rawLen;
1514 					nStats.packets.sent           += 1;
1515 				}
1516 				else if (result == SOCKET_ERROR)
1517 				{
1518 					// Write error, most likely client disconnect.
1519 					debug(LOG_ERROR, "Failed to send message: %s", strSockError(getSockErr()));
1520 					if (!isTmpQueue)
1521 					{
1522 						NETlogEntry("client disconnect?", SYNC_FLAG, player);
1523 						NETplayerClientDisconnect(player);
1524 					}
1525 				}
1526 			}
1527 		}
1528 		return true;
1529 	}
1530 	else if (player == NetPlay.hostPlayer)
1531 	{
1532 		// We are a client, send directly to player, who happens to be the host.
1533 		if (bsocket)
1534 		{
1535 			uint8_t *rawData = message->rawDataDup();
1536 			ssize_t rawLen   = message->rawLen();
1537 			size_t compressedRawLen;
1538 			result = writeAll(bsocket, rawData, rawLen, &compressedRawLen);
1539 			delete[] rawData;  // Done with the data.
1540 
1541 			if (result == rawLen)
1542 			{
1543 				nStats.rawBytes.sent          += compressedRawLen;
1544 				nStats.uncompressedBytes.sent += rawLen;
1545 				nStats.packets.sent           += 1;
1546 			}
1547 			else if (result == SOCKET_ERROR)
1548 			{
1549 				// Write error, most likely host disconnect.
1550 				debug(LOG_ERROR, "Failed to send message: %s", strSockError(getSockErr()));
1551 				debug(LOG_ERROR, "Host connection was broken, socket %p.", static_cast<void *>(bsocket));
1552 				NETlogEntry("write error--client disconnect.", SYNC_FLAG, player);
1553 				SocketSet_DelSocket(socket_set, bsocket);            // mark it invalid
1554 				socketClose(bsocket);
1555 				bsocket = nullptr;
1556 				NetPlay.players[NetPlay.hostPlayer].heartbeat = false;	// mark host as dead
1557 				//Game is pretty much over --should just end everything when HOST dies.
1558 				NetPlay.isHostAlive = false;
1559 			}
1560 
1561 			return result == rawLen;
1562 		}
1563 	}
1564 	else
1565 	{
1566 		// We are a client and can't send the data directly, ask the host to send the data to the player.
1567 		uint8_t sender = selectedPlayer;
1568 		NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_SEND_TO_PLAYER);
1569 		NETuint8_t(&sender);
1570 		NETuint8_t(&player);
1571 		NETnetMessage(&message);
1572 		NETend();
1573 	}
1574 
1575 	return false;
1576 }
1577 
NETflush()1578 void NETflush()
1579 {
1580 	if (!NetPlay.bComms)
1581 	{
1582 		return;
1583 	}
1584 
1585 	NETflushGameQueues();
1586 
1587 	size_t compressedRawLen;
1588 	if (NetPlay.isHost)
1589 	{
1590 		for (int player = 0; player < MAX_CONNECTED_PLAYERS; ++player)
1591 		{
1592 			// We are the host, send directly to player.
1593 			if (connected_bsocket[player] != nullptr)
1594 			{
1595 				socketFlush(connected_bsocket[player], &compressedRawLen);
1596 				nStats.rawBytes.sent += compressedRawLen;
1597 			}
1598 		}
1599 		for (int player = 0; player < MAX_TMP_SOCKETS; ++player)
1600 		{
1601 			// We are the host, send directly to player.
1602 			if (tmp_socket[player] != nullptr)
1603 			{
1604 				socketFlush(tmp_socket[player], &compressedRawLen);
1605 				nStats.rawBytes.sent += compressedRawLen;
1606 			}
1607 		}
1608 	}
1609 	else
1610 	{
1611 		if (bsocket != nullptr)
1612 		{
1613 			socketFlush(bsocket, &compressedRawLen);
1614 			nStats.rawBytes.sent += compressedRawLen;
1615 		}
1616 	}
1617 }
1618 
1619 ///////////////////////////////////////////////////////////////////////////
1620 // Check if a message is a system message
NETprocessSystemMessage(NETQUEUE playerQueue,uint8_t type)1621 static bool NETprocessSystemMessage(NETQUEUE playerQueue, uint8_t type)
1622 {
1623 	switch (type)
1624 	{
1625 	case NET_SEND_TO_PLAYER:
1626 		{
1627 			uint8_t sender;
1628 			uint8_t receiver;
1629 			NetMessage const *message = nullptr;
1630 			NETbeginDecode(playerQueue, NET_SEND_TO_PLAYER);
1631 			NETuint8_t(&sender);
1632 			NETuint8_t(&receiver);
1633 			NETnetMessage(&message);  // Must delete message later.
1634 			std::unique_ptr<NetMessage const> deleteLater(message);
1635 			if (!NETend())
1636 			{
1637 				debug(LOG_ERROR, "Incomplete NET_SEND_TO_PLAYER.");
1638 				break;
1639 			}
1640 			if (sender >= MAX_PLAYERS || (receiver >= MAX_PLAYERS && receiver != NET_ALL_PLAYERS))
1641 			{
1642 				debug(LOG_ERROR, "Bad NET_SEND_TO_PLAYER.");
1643 				break;
1644 			}
1645 			if ((receiver == selectedPlayer || receiver == NET_ALL_PLAYERS) && playerQueue.index == NetPlay.hostPlayer)
1646 			{
1647 				// Message was sent to us via the host.
1648 				if (sender != selectedPlayer)  // Make sure host didn't send us our own broadcast messages, which shouldn't happen anyway.
1649 				{
1650 					NETinsertMessageFromNet(NETnetQueue(sender), message);
1651 					NETlogPacket(message->type, static_cast<uint32_t>(message->rawLen()), true);
1652 				}
1653 			}
1654 			else if (NetPlay.isHost && sender == playerQueue.index)
1655 			{
1656 				if (((message->type == NET_FIREUP
1657 				      || message->type == NET_KICK
1658 				      || message->type == NET_PLAYER_LEAVING
1659 				      || message->type == NET_PLAYER_DROPPED
1660 				      || message->type == NET_REJECTED
1661 				      || message->type == NET_PLAYER_JOINED) && sender != NET_HOST_ONLY)
1662 				    ||
1663 				    ((message->type == NET_HOST_DROPPED
1664 				      || message->type == NET_OPTIONS
1665 				      || message->type == NET_FILE_REQUESTED
1666 				      || message->type == NET_READY_REQUEST
1667 				      || message->type == NET_TEAMREQUEST
1668 				      || message->type == NET_COLOURREQUEST
1669 				      || message->type == NET_POSITIONREQUEST
1670 				      || message->type == NET_FILE_CANCELLED
1671 				      || message->type == NET_JOIN
1672 				      || message->type == NET_PLAYER_INFO) && receiver != NET_HOST_ONLY))
1673 				{
1674 					char msg[256] = {'\0'};
1675 
1676 					ssprintf(msg, "Auto-kicking player %u, lacked the required access level for command(%d).", (unsigned int)sender, (int)message->type);
1677 					sendRoomSystemMessage(msg);
1678 					NETlogEntry(msg, SYNC_FLAG, sender);
1679 					addToBanList(NetPlay.players[sender].IPtextAddress, NetPlay.players[sender].name);
1680 					NETplayerDropped(sender);
1681 					connected_bsocket[sender] = nullptr;
1682 					debug(LOG_ERROR, "%s", msg);
1683 					break;
1684 				}
1685 
1686 				// We are the host, and player is asking us to send the message to receiver.
1687 				NETbeginEncode(NETnetQueue(receiver, sender), NET_SEND_TO_PLAYER);
1688 				NETuint8_t(&sender);
1689 				NETuint8_t(&receiver);
1690 				NETnetMessage(&message);
1691 				NETend();
1692 
1693 				if (receiver == NET_ALL_PLAYERS)
1694 				{
1695 					NETinsertMessageFromNet(NETnetQueue(sender), message);  // Message is also for the host.
1696 					NETlogPacket(message->type, static_cast<uint32_t>(message->rawLen()), true);
1697 					// Not sure if flushing here can make a difference, maybe it can:
1698 					//NETflush();  // Send the message to everyone as fast as possible.
1699 				}
1700 			}
1701 			else
1702 			{
1703 				debug(LOG_INFO, "Report this: Player %d sent us message type (%d) addressed to %d from %d. We are %d.", (int)playerQueue.index, (int)message->type, (int)receiver, (int)sender, selectedPlayer);
1704 			}
1705 
1706 			break;
1707 		}
1708 	case NET_SHARE_GAME_QUEUE:
1709 		{
1710 			uint8_t player = 0;
1711 			uint32_t num = 0, n;
1712 			NetMessage const *message = nullptr;
1713 
1714 			// Encoded in NETprocessSystemMessage in nettypes.cpp.
1715 			NETbeginDecode(playerQueue, NET_SHARE_GAME_QUEUE);
1716 			NETuint8_t(&player);
1717 			NETuint32_t(&num);
1718 			bool isSentByCorrectClient = responsibleFor(playerQueue.index, player);
1719 			isSentByCorrectClient = isSentByCorrectClient || (playerQueue.index == NET_HOST_ONLY && playerQueue.index != selectedPlayer);  // Let host spoof other people's NET_SHARE_GAME_QUEUE messages, but not our own. This allows the host to spoof a GAME_PLAYER_LEFT message (but spoofing any message when the player is still there will fail with desynch).
1720 			if (!isSentByCorrectClient || player >= MAX_PLAYERS)
1721 			{
1722 				break;
1723 			}
1724 			for (n = 0; n < num; ++n)
1725 			{
1726 				NETnetMessage(&message);
1727 
1728 				NETinsertMessageFromNet(NETgameQueue(player), message);
1729 				NETlogPacket(message->type, static_cast<uint32_t>(message->rawLen()), true);
1730 
1731 				delete message;
1732 				message = nullptr;
1733 			}
1734 			if (!NETend())
1735 			{
1736 				debug(LOG_ERROR, "Bad NET_SHARE_GAME_QUEUE message.");
1737 				break;
1738 			}
1739 			break;
1740 		}
1741 	case NET_PLAYER_STATS:
1742 		{
1743 			recvMultiStats(playerQueue);
1744 			netPlayersUpdated = true;
1745 			break;
1746 		}
1747 	case NET_PLAYER_INFO:
1748 		{
1749 			uint32_t indexLen = 0, n;
1750 			uint32_t index = MAX_PLAYERS;
1751 			int32_t colour = 0;
1752 			int32_t position = 0;
1753 			int32_t team = 0;
1754 			int8_t ai = 0;
1755 			int8_t difficulty = 0;
1756 			uint8_t faction = FACTION_NORMAL;
1757 			bool error = false;
1758 
1759 			NETbeginDecode(playerQueue, NET_PLAYER_INFO);
1760 			NETuint32_t(&indexLen);
1761 			if (indexLen > MAX_PLAYERS || (playerQueue.index != NET_HOST_ONLY && indexLen > 1))
1762 			{
1763 				debug(LOG_ERROR, "MSG_PLAYER_INFO: Bad number of players updated: %u", indexLen);
1764 				NETend();
1765 				break;
1766 			}
1767 
1768 			for (n = 0; n < indexLen; ++n)
1769 			{
1770 				bool wasAllocated = false;
1771 				std::string oldName;
1772 
1773 				// Retrieve the player's ID
1774 				NETuint32_t(&index);
1775 
1776 				// Bail out if the given ID number is out of range
1777 				if (index >= MAX_CONNECTED_PLAYERS || (playerQueue.index != NetPlay.hostPlayer && (playerQueue.index != index || !NetPlay.players[index].allocated)))
1778 				{
1779 					debug(LOG_ERROR, "MSG_PLAYER_INFO from %u: Player ID (%u) out of range (max %u)", playerQueue.index, index, (unsigned int)MAX_CONNECTED_PLAYERS);
1780 					error = true;
1781 					break;
1782 				}
1783 
1784 				// Retrieve the rest of the data
1785 				wasAllocated = NetPlay.players[index].allocated;
1786 				NETbool(&NetPlay.players[index].allocated);
1787 				NETbool(&NetPlay.players[index].heartbeat);
1788 				NETbool(&NetPlay.players[index].kick);
1789 				oldName.clear();
1790 				oldName = NetPlay.players[index].name;
1791 				NETstring(NetPlay.players[index].name, sizeof(NetPlay.players[index].name));
1792 				NETuint32_t(&NetPlay.players[index].heartattacktime);
1793 				NETint32_t(&colour);
1794 				NETint32_t(&position);
1795 				NETint32_t(&team);
1796 				NETbool(&NetPlay.players[index].ready);
1797 				NETint8_t(&ai);
1798 				NETint8_t(&difficulty);
1799 				NETuint8_t(&faction);
1800 
1801 				auto newFactionId = uintToFactionID(faction);
1802 				if (!newFactionId.has_value())
1803 				{
1804 					debug(LOG_ERROR, "MSG_PLAYER_INFO from %u: Faction ID (%u) out of range (max %u)", playerQueue.index, (unsigned int)faction, (unsigned int)MAX_FACTION_ID);
1805 					error = true;
1806 					break;
1807 				}
1808 
1809 				// Don't let anyone except the host change these, otherwise it will end up inconsistent at some point, and the game gets really messed up.
1810 				if (playerQueue.index == NetPlay.hostPlayer)
1811 				{
1812 					setPlayerColour(index, colour);
1813 					NetPlay.players[index].position = position;
1814 					NetPlay.players[index].team = team;
1815 					NetPlay.players[index].ai = ai;
1816 					NetPlay.players[index].difficulty = static_cast<AIDifficulty>(difficulty);
1817 					NetPlay.players[index].faction = newFactionId.value();
1818 				}
1819 
1820 				debug(LOG_NET, "%s for player %u (%s)", n == 0 ? "Receiving MSG_PLAYER_INFO" : "                      and", (unsigned int)index, NetPlay.players[index].allocated ? "human" : "AI");
1821 
1822 				if (wasAllocated && NetPlay.players[index].allocated && strncmp(oldName.c_str(), NetPlay.players[index].name, sizeof(NetPlay.players[index].name)) != 0)
1823 				{
1824 					printConsoleNameChange(oldName.c_str(), NetPlay.players[index].name);
1825 				}
1826 			}
1827 			NETend();
1828 			// If we're the game host make sure to send the updated
1829 			// data to all other clients as well.
1830 			if (NetPlay.isHost && !error)
1831 			{
1832 				NETBroadcastPlayerInfo(index); // ultimately triggers updateMultiplayGameData inside NETSendNPlayerInfoTo
1833 				NETfixDuplicatePlayerNames();
1834 			}
1835 			else if (!error)
1836 			{
1837 				ActivityManager::instance().updateMultiplayGameData(game, ingame, NETGameIsLocked());
1838 			}
1839 			netPlayersUpdated = true;
1840 			break;
1841 		}
1842 	case NET_PLAYER_JOINED:
1843 		{
1844 			uint8_t index;
1845 
1846 			NETbeginDecode(playerQueue, NET_PLAYER_JOINED);
1847 			NETuint8_t(&index);
1848 			NETend();
1849 
1850 			debug(LOG_NET, "Receiving NET_PLAYER_JOINED for player %u using socket %p",
1851 			      (unsigned int)index, static_cast<void *>(bsocket));
1852 
1853 			MultiPlayerJoin(index);
1854 			netPlayersUpdated = true;
1855 			break;
1856 		}
1857 	// This message type is when player is leaving 'nicely', and socket is still valid.
1858 	case NET_PLAYER_LEAVING:
1859 		{
1860 			uint32_t index;
1861 
1862 			NETbeginDecode(playerQueue, NET_PLAYER_LEAVING);
1863 			NETuint32_t(&index);
1864 			NETend();
1865 
1866 			if (playerQueue.index != NetPlay.hostPlayer && index != playerQueue.index)
1867 			{
1868 				debug(LOG_ERROR, "Player %d left, but accidentally set player %d as leaving.", playerQueue.index, index);
1869 				index = playerQueue.index;
1870 			}
1871 
1872 			if (connected_bsocket[index])
1873 			{
1874 				debug(LOG_NET, "Receiving NET_PLAYER_LEAVING for player %u on socket %p", (unsigned int)index, static_cast<void *>(connected_bsocket[index]));
1875 			}
1876 			else
1877 			{
1878 				// dropped from join screen most likely
1879 				debug(LOG_NET, "Receiving NET_PLAYER_LEAVING for player %u (no socket?)", (unsigned int)index);
1880 			}
1881 
1882 			if (NetPlay.isHost)
1883 			{
1884 				debug(LOG_NET, "Broadcast leaving message to everyone else");
1885 				NETbeginEncode(NETbroadcastQueue(), NET_PLAYER_LEAVING);
1886 				NETuint32_t(&index);
1887 				NETend();
1888 			}
1889 
1890 			debug(LOG_INFO, "Player %u has left the game.", index);
1891 			NETplayerLeaving(index);		// need to close socket for the player that left.
1892 			NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_LEAVING, index);
1893 			break;
1894 		}
1895 	case NET_GAME_FLAGS:
1896 		{
1897 			debug(LOG_NET, "Receiving game flags");
1898 
1899 			NETbeginDecode(playerQueue, NET_GAME_FLAGS);
1900 			{
1901 				static unsigned int max_flags = ARRAY_SIZE(NetGameFlags);
1902 				// Retrieve the amount of game flags that we should receive
1903 				uint8_t i, count;
1904 				NETuint8_t(&count);
1905 
1906 				// Make sure that we won't get buffer overflows by checking that we
1907 				// have enough space to store the given amount of game flags.
1908 				if (count > max_flags)
1909 				{
1910 					debug(LOG_NET, "NET_GAME_FLAGS: More game flags sent (%u) than our buffer can hold (%u)", (unsigned int)count, max_flags);
1911 					count = max_flags;
1912 				}
1913 
1914 				// Retrieve all game flags
1915 				for (i = 0; i < count; ++i)
1916 				{
1917 					NETint32_t(&NetGameFlags[i]);
1918 				}
1919 			}
1920 			NETend();
1921 
1922 			if (NetPlay.isHost)
1923 			{
1924 				NETsendGameFlags();
1925 			}
1926 			break;
1927 		}
1928 	case NET_DEBUG_SYNC:
1929 		{
1930 			recvDebugSync(playerQueue);
1931 			break;
1932 		}
1933 
1934 	default:
1935 		return false;
1936 	}
1937 
1938 	NETpop(playerQueue);
1939 	return true;
1940 }
1941 
1942 /*
1943 *	Checks to see if a human player is still with us.
1944 *	@note: resuscitation isn't possible with current code, so once we lose
1945 *	the socket, then we have no way to connect with them again. Future
1946 *	item to enhance.
1947 */
NETcheckPlayers()1948 static void NETcheckPlayers()
1949 {
1950 	if (!NetPlay.isHost)
1951 	{
1952 		ASSERT(false, "Host only routine detected for client or not hosting yet!");
1953 		return;
1954 	}
1955 
1956 	for (int i = 0; i < MAX_PLAYERS ; i++)
1957 	{
1958 		if (NetPlay.players[i].allocated == 0)
1959 		{
1960 			continue;    // not allocated means that it most likely it is a AI player
1961 		}
1962 		if (NetPlay.players[i].heartbeat == 0 && NetPlay.players[i].heartattacktime == 0)	// looks like they are dead
1963 		{
1964 			NetPlay.players[i].heartattacktime = realTime;		// mark when this occurred
1965 		}
1966 		else
1967 		{
1968 			if (NetPlay.players[i].heartattacktime)
1969 			{
1970 				if (NetPlay.players[i].heartattacktime + (15 * GAME_TICKS_PER_SEC) <  realTime) // wait 15 secs
1971 				{
1972 					debug(LOG_NET, "Kicking due to client heart attack");
1973 					NetPlay.players[i].kick = true;		// if still dead, then kick em.
1974 				}
1975 			}
1976 		}
1977 		if (NetPlay.players[i].kick)
1978 		{
1979 			debug(LOG_NET, "Kicking player %d", i);
1980 			kickPlayer(i, "you are unwanted by the host.", ERROR_KICKED);
1981 		}
1982 	}
1983 }
1984 
1985 // ////////////////////////////////////////////////////////////////////////
1986 // Receive a message over the current connection. We return true if there
1987 // is a message for the higher level code to process, and false otherwise.
1988 // We should not block here.
NETrecvNet(NETQUEUE * queue,uint8_t * type)1989 bool NETrecvNet(NETQUEUE *queue, uint8_t *type)
1990 {
1991 	const int status = upnp_status.load(); // hack fix for clang and c++11 - fixed in standard for c++14
1992 	switch (status)
1993 	{
1994 	case UPNP_ERROR_CONTROL_NOT_AVAILABLE:
1995 	case UPNP_ERROR_DEVICE_NOT_FOUND:
1996 		if (upnp_status == UPNP_ERROR_DEVICE_NOT_FOUND)
1997 		{
1998 			debug(LOG_NET, "UPnP device not found");
1999 		}
2000 		else if (upnp_status == UPNP_ERROR_CONTROL_NOT_AVAILABLE)
2001 		{
2002 			debug(LOG_NET, "controlURL not available, UPnP disabled");
2003 		}
2004 		// beware of writing a line too long, it screws up console line count. \n is safe for line split
2005 		addConsoleMessage(_("No UPnP device found. Configure your router/firewall to open port 2100!"), DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
2006 		NetPlay.isUPNP_CONFIGURED = false;
2007 		NetPlay.isUPNP_ERROR = true;
2008 		upnp_status = 0;
2009 		break;
2010 	case UPNP_SUCCESS:
2011 		NETaddRedirects();
2012 		upnp_status = 0;
2013 		break;
2014 	default:
2015 	case 0:
2016 		ASSERT(upnp_status == 0, "bad value");
2017 		break;
2018 	}
2019 
2020 	uint32_t current;
2021 
2022 	if (!NetPlay.bComms)
2023 	{
2024 		return false;
2025 	}
2026 
2027 	if (NetPlay.isHost)
2028 	{
2029 		NETfixPlayerCount();
2030 		NETallowJoining();
2031 		lobbyConnectionHandler.run();
2032 		NETcheckPlayers();		// make sure players are still alive & well
2033 	}
2034 
2035 	if (socket_set == nullptr || checkSockets(socket_set, NET_READ_TIMEOUT) <= 0)
2036 	{
2037 		goto checkMessages;
2038 	}
2039 
2040 	for (current = 0; current < MAX_CONNECTED_PLAYERS; ++current)
2041 	{
2042 		Socket **pSocket = NetPlay.isHost ? &connected_bsocket[current] : &bsocket;
2043 		uint8_t buffer[NET_BUFFER_SIZE];
2044 		size_t dataLen;
2045 
2046 		if (!NetPlay.isHost && current != NET_HOST_ONLY)
2047 		{
2048 			continue;  // Don't have a socket open to this player.
2049 		}
2050 
2051 		if (*pSocket == nullptr)
2052 		{
2053 			continue;
2054 		}
2055 
2056 		dataLen = NET_fillBuffer(pSocket, socket_set, buffer, sizeof(buffer));
2057 		if (dataLen > 0)
2058 		{
2059 			// we received some data, add to buffer
2060 			NETinsertRawData(NETnetQueue(current), buffer, dataLen);
2061 		}
2062 		else if (*pSocket == nullptr)
2063 		{
2064 			// If there is a error in NET_fillBuffer() then socket is already invalid.
2065 			// This means that the player dropped / disconnected for whatever reason.
2066 			debug(LOG_INFO, "Player, (player %u) seems to have dropped/disconnected.", (unsigned)current);
2067 
2068 			if (NetPlay.isHost)
2069 			{
2070 				// Send message type specifically for dropped / disconnects
2071 				NETplayerDropped(current);
2072 				connected_bsocket[current] = nullptr;		// clear their socket
2073 			}
2074 			else
2075 			{
2076 				// lobby errors were set in NET_fillBuffer()
2077 				return false;
2078 			}
2079 		}
2080 	}
2081 
2082 checkMessages:
2083 	for (current = 0; current < MAX_CONNECTED_PLAYERS; ++current)
2084 	{
2085 		*queue = NETnetQueue(current);
2086 		while (NETisMessageReady(*queue))
2087 		{
2088 			*type = NETgetMessage(*queue)->type;
2089 			if (!NETprocessSystemMessage(*queue, *type))
2090 			{
2091 				return true;  // We couldn't process the message, let the caller deal with it..
2092 			}
2093 		}
2094 	}
2095 
2096 	return false;
2097 }
2098 
NETrecvGame(NETQUEUE * queue,uint8_t * type)2099 bool NETrecvGame(NETQUEUE *queue, uint8_t *type)
2100 {
2101 	for (unsigned current = 0; current < MAX_PLAYERS; ++current)
2102 	{
2103 		*queue = NETgameQueue(current);
2104 		while (!checkPlayerGameTime(current))  // Check for any messages that are scheduled to be read now.
2105 		{
2106 			if (!NETisMessageReady(*queue))
2107 			{
2108 				return false;  // Still waiting for messages from this player, and all players should process messages in the same order. Will have to freeze the game while waiting.
2109 			}
2110 
2111 			*type = NETgetMessage(*queue)->type;
2112 
2113 			if (*type == GAME_GAME_TIME)
2114 			{
2115 				recvPlayerGameTime(*queue);
2116 				NETpop(*queue);
2117 				continue;
2118 			}
2119 
2120 			return true;  // Have a message ready to read now.
2121 		}
2122 	}
2123 
2124 	return false;  // No messages sceduled to be read yet. Game can continue.
2125 }
2126 
2127 // ////////////////////////////////////////////////////////////////////////
2128 // File Transfer programs.
2129 /** Send file. It returns % of file sent when 100 it's complete. Call until it returns 100.
2130 *  @TODO: more error checking (?) different file types (?)
2131 *          Maybe should close file handle, and seek each time?
2132 *
2133 *  @NOTE: MAX_FILE_TRANSFER_PACKET is set to 2k per packet since 7*2 = 14K which is pretty
2134 *         much our limit.  Don't screw with that without having a bigger buffer!
2135 *         NET_BUFFER_SIZE is at 16k.  (also remember text chat, plus all the other cruff)
2136 */
2137 #define MAX_FILE_TRANSFER_PACKET 2048
NETsendFile(WZFile & file,unsigned player)2138 int NETsendFile(WZFile &file, unsigned player)
2139 {
2140 	ASSERT_OR_RETURN(100, NetPlay.isHost, "Trying to send a file and we are not the host!");
2141 
2142 	uint8_t inBuff[MAX_FILE_TRANSFER_PACKET];
2143 	memset(inBuff, 0x0, sizeof(inBuff));
2144 
2145 	// read some bytes.
2146 	uint32_t bytesToRead = WZ_PHYSFS_readBytes(file.handle, inBuff, MAX_FILE_TRANSFER_PACKET);
2147 	ASSERT_OR_RETURN(100, (int32_t)bytesToRead >= 0, "Error reading file.");
2148 
2149 	NETbeginEncode(NETnetQueue(player), NET_FILE_PAYLOAD);
2150 	NETbin(file.hash.bytes, file.hash.Bytes);
2151 	NETuint32_t(&file.size);  // total bytes in this file. (we don't support 64bit yet)
2152 	NETuint32_t(&file.pos);  // start byte
2153 	NETuint32_t(&bytesToRead);  // bytes in this packet
2154 	NETbin(inBuff, bytesToRead);
2155 	NETend();
2156 
2157 	file.pos += bytesToRead;  // update position!
2158 	if (file.pos == file.size)
2159 	{
2160 		PHYSFS_close(file.handle);
2161 		file.handle = nullptr;  // We are done sending to this client.
2162 	}
2163 
2164 	return (uint64_t)file.pos * 100 / file.size;
2165 }
2166 
validateReceivedFile(const WZFile & file)2167 bool validateReceivedFile(const WZFile& file)
2168 {
2169 	PHYSFS_file *fileHandle = PHYSFS_openRead(file.filename.c_str());
2170 	ASSERT_OR_RETURN(false, fileHandle != nullptr, "Could not open downloaded file %s for reading: %s", file.filename.c_str(), WZ_PHYSFS_getLastError());
2171 
2172 	PHYSFS_sint64 actualFileSize64 = PHYSFS_fileLength(fileHandle);
2173 	if (actualFileSize64 < 0)
2174 	{
2175 		debug(LOG_ERROR, "Failed to determine file size of the downloaded file!");
2176 		PHYSFS_close(fileHandle);
2177 		return false;
2178 	}
2179 	if(actualFileSize64 > std::numeric_limits<int32_t>::max())
2180 	{
2181 		debug(LOG_ERROR, "Downloaded file is too large!");
2182 		PHYSFS_close(fileHandle);
2183 		return false;
2184 	}
2185 
2186 	uint32_t actualFileSize = static_cast<uint32_t>(actualFileSize64);
2187 	if (actualFileSize != file.size)
2188 	{
2189 		debug(LOG_ERROR, "Downloaded map unexpected size! Got %" PRIu32", expected %" PRIu32"!", actualFileSize, file.size);
2190 		PHYSFS_close(fileHandle);
2191 		return false;
2192 	}
2193 
2194 	// verify actual downloaded file hash matches expected hash
2195 
2196 	Sha256 actualFileHash;
2197 	crypto_hash_sha256_state state;
2198 	crypto_hash_sha256_init(&state);
2199 	size_t bufferSize = std::min<size_t>(actualFileSize, 4 * 1024 * 1024);
2200 	std::vector<unsigned char> fileChunkBuffer(bufferSize, '\0');
2201 	PHYSFS_sint64 length_read = 0;
2202 	do {
2203 		length_read = WZ_PHYSFS_readBytes(fileHandle, fileChunkBuffer.data(), static_cast<PHYSFS_uint32>(bufferSize));
2204 		if (length_read != bufferSize)
2205 		{
2206 			if (length_read < 0 || !PHYSFS_eof(fileHandle))
2207 			{
2208 				// did not read expected amount, but did not reach end of file - some other error reading the file occurred
2209 				debug(LOG_ERROR, "Failed to read downloaded file: %s", WZ_PHYSFS_getLastError());
2210 				PHYSFS_close(fileHandle);
2211 				return false;
2212 			}
2213 		}
2214 		crypto_hash_sha256_update(&state, fileChunkBuffer.data(), static_cast<unsigned long long>(length_read));
2215 	} while (length_read == bufferSize);
2216 	crypto_hash_sha256_final(&state, actualFileHash.bytes);
2217 	fileChunkBuffer.clear();
2218 
2219 	if (actualFileHash != file.hash)
2220 	{
2221 		debug(LOG_ERROR, "Downloaded file hash (%s) does not match requested file hash (%s)", actualFileHash.toString().c_str(), file.hash.toString().c_str());
2222 		PHYSFS_close(fileHandle);
2223 		return false;
2224 	}
2225 
2226 	PHYSFS_close(fileHandle);
2227 	return true;
2228 }
2229 
markAsDownloadedFile(const std::string & filename)2230 bool markAsDownloadedFile(const std::string &filename)
2231 {
2232 	// Files are written to the PhysFS writeDir
2233 	const char * current_writeDir = PHYSFS_getWriteDir();
2234 	ASSERT(current_writeDir != nullptr, "Failed to get PhysFS writeDir: %s", WZ_PHYSFS_getLastError());
2235 	std::string fullFilePath = std::string(current_writeDir) + PHYSFS_getDirSeparator() + filename;
2236 
2237 #if defined(WZ_OS_WIN)
2238 	// On Windows:
2239 	//	- Create the Alternate Data Stream required to set the Internet Zone identifier
2240 	const wchar_t kWindowsZoneIdentifierADSSuffix[] = L":Zone.Identifier";
2241 
2242 	// Convert fullFilePath to UTF-16 wchar_t
2243 	int wstr_len = MultiByteToWideChar(CP_UTF8, 0, fullFilePath.c_str(), -1, NULL, 0);
2244 	if (wstr_len <= 0)
2245 	{
2246 		debug(LOG_ERROR, "Could not convert string from UTF-8; MultiByteToWideChar failed with error %d: %s\n", GetLastError(), fullFilePath.c_str());
2247 		return false;
2248 	}
2249 	std::vector<wchar_t> wstr_filename(wstr_len, 0);
2250 	if (MultiByteToWideChar(CP_UTF8, 0, fullFilePath.c_str(), -1, &wstr_filename[0], wstr_len) == 0)
2251 	{
2252 		debug(LOG_ERROR, "Could not convert string from UTF-8; MultiByteToWideChar[2] failed with error %d: %s\n", GetLastError(), fullFilePath.c_str());
2253 		return false;
2254 	}
2255 	std::wstring fullFilePathUTF16(wstr_filename.data());
2256 	fullFilePathUTF16 += kWindowsZoneIdentifierADSSuffix;
2257 
2258 	HANDLE hStream = CreateFileW(fullFilePathUTF16.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2259 	if(hStream == INVALID_HANDLE_VALUE)
2260 	{
2261 		// Failed to open stream
2262 		debug(LOG_ERROR, "Could not open stream; failed with error %d: %s\n", GetLastError(), fullFilePath.c_str());
2263 		return false;
2264 	}
2265 
2266 	// Set it to "downloaded from the Internet Zone" (ZoneId 3)
2267 	const char kWindowsZoneIdentifierADSDataInternetZone[] = "[ZoneTransfer]\r\nZoneId=3\r\n";
2268 	DWORD dwNumberOfBytesWritten;
2269 	if (WriteFile(hStream, kWindowsZoneIdentifierADSDataInternetZone, static_cast<DWORD>(strlen(kWindowsZoneIdentifierADSDataInternetZone)), &dwNumberOfBytesWritten, NULL) == 0)
2270 	{
2271 		debug(LOG_ERROR, "Failed to write to stream with error %d: %s\n", GetLastError(), fullFilePath.c_str());
2272 		CloseHandle(hStream);
2273 		return false;
2274 	}
2275 
2276 	FlushFileBuffers(hStream);
2277 	CloseHandle(hStream);
2278 
2279 	return true;
2280 #elif defined (WZ_OS_MAC)
2281 	// On macOS:
2282 	//	- Set the quarantine attribute on the file
2283 	return cocoaSetFileQuarantineAttribute(fullFilePath.c_str());
2284 #else
2285 	// Not currently implemented
2286 #endif
2287 	return false;
2288 }
2289 
2290 // recv file. it returns % of the file so far recvd.
NETrecvFile(NETQUEUE queue)2291 int NETrecvFile(NETQUEUE queue)
2292 {
2293 	Sha256 hash;
2294 	hash.setZero();
2295 	uint32_t size = 0;
2296 	uint32_t pos = 0;
2297 	uint32_t bytesToRead = 0;
2298 	uint8_t buf[MAX_FILE_TRANSFER_PACKET];
2299 	memset(buf, 0x0, sizeof(buf));
2300 
2301 	//read incoming bytes.
2302 	NETbeginDecode(queue, NET_FILE_PAYLOAD);
2303 	NETbin(hash.bytes, hash.Bytes);
2304 	NETuint32_t(&size);  // total bytes in this file. (we don't support 64bit yet)
2305 	NETuint32_t(&pos);  // start byte
2306 	NETuint32_t(&bytesToRead);  // bytes in this packet
2307 	ASSERT_OR_RETURN(100, bytesToRead <= sizeof(buf), "Bad value.");
2308 	NETbin(buf, bytesToRead);
2309 	NETend();
2310 
2311 	debug(LOG_NET, "New file position is %u", pos);
2312 
2313 	auto file = std::find_if(NetPlay.wzFiles.begin(), NetPlay.wzFiles.end(), [&](WZFile const &file) { return file.hash == hash; });
2314 
2315 	auto sendCancelFileDownload = [](Sha256 &hash) {
2316 		NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_FILE_CANCELLED);
2317 		NETbin(hash.bytes, hash.Bytes);
2318 		NETend();
2319 	};
2320 
2321 	if (file == NetPlay.wzFiles.end())
2322 	{
2323 		debug(LOG_WARNING, "Receiving file data we didn't request.");
2324 		sendCancelFileDownload(hash);
2325 		return 100;
2326 	}
2327 
2328 	auto terminateFileDownload = [sendCancelFileDownload](std::vector<WZFile>::iterator &file) {
2329 		int noError = PHYSFS_close(file->handle);
2330 		if (noError == 0)
2331 		{
2332 			debug(LOG_ERROR, "Could not close file handle after trying to terminate download: %s", WZ_PHYSFS_getLastError());
2333 		}
2334 		file->handle = nullptr;
2335 		sendCancelFileDownload(file->hash);
2336 		NetPlay.wzFiles.erase(file);
2337 	};
2338 
2339 	//sanity checks
2340 	if (file->size != size)
2341 	{
2342 		if (file->size == 0)
2343 		{
2344 			// host does not send the file size until the first recvFile packet
2345 			file->size = size;
2346 		}
2347 		else
2348 		{
2349 			// host sent a different file size for this file with this chunk (vs the first chunk)
2350 			// this should not happen!
2351 			debug(LOG_ERROR, "Host sent a different file size for this file; (original size: %u, new size: %u)", file->size, size);
2352 			terminateFileDownload(file); // 'file' is now an invalidated iterator.
2353 			return 100;
2354 		}
2355 	}
2356 
2357 	if (size > MAX_NET_TRANSFERRABLE_FILE_SIZE)
2358 	{
2359 		// file size is too large
2360 		debug(LOG_ERROR, "Downloaded filesize is too large; (size: %" PRIu32")", size);
2361 		terminateFileDownload(file); // 'file' is now an invalidated iterator.
2362 		return 100;
2363 	}
2364 
2365 	if (PHYSFS_tell(file->handle) != static_cast<PHYSFS_sint64>(pos))
2366 	{
2367 		// actual position in file does not equal the expected position in the file (sent by the host)
2368 		debug(LOG_ERROR, "Invalid file position in downloaded file; (desired: %" PRIu32")", pos);
2369 		terminateFileDownload(file); // 'file' is now an invalidated iterator.
2370 		return 100;
2371 	}
2372 
2373 	// Write packet to the file.
2374 	WZ_PHYSFS_writeBytes(file->handle, buf, bytesToRead);
2375 
2376 	uint32_t newPos = pos + bytesToRead;
2377 	file->pos = newPos;
2378 
2379 	if (newPos >= size)  // last packet
2380 	{
2381 		int noError = PHYSFS_close(file->handle);
2382 		if (noError == 0)
2383 		{
2384 			debug(LOG_ERROR, "Could not close file handle after trying to save map: %s", WZ_PHYSFS_getLastError());
2385 		}
2386 		file->handle = nullptr;
2387 
2388 		if(!validateReceivedFile(*file))
2389 		{
2390 			// Delete the (invalid) downloaded file
2391 			PHYSFS_delete(file->filename.c_str());
2392 		}
2393 		else
2394 		{
2395 			// Attach Quarantine / "downloaded" file attribute to file
2396 			markAsDownloadedFile(file->filename.c_str());
2397 		}
2398 
2399 		NetPlay.wzFiles.erase(file);
2400 	}
2401 	// 'file' may now be an invalidated iterator.
2402 
2403 	//return the percentage count
2404 	if (size)
2405 	{
2406 		return (newPos * 100) / size;
2407 	}
2408 	debug(LOG_ERROR, "Received 0 byte file from host?");
2409 	return 100;		// file is nullbyte, so we are done.
2410 }
2411 
NETgetDownloadProgress(unsigned player)2412 unsigned NETgetDownloadProgress(unsigned player)
2413 {
2414 	std::vector<WZFile> const &files = player == selectedPlayer ?
2415 		NetPlay.wzFiles :  // Check our own download progress.
2416 		NetPlay.players[player].wzFiles;  // Check their download progress (currently only works if we are the host).
2417 
2418 	uint32_t progress = 100;
2419 	for (WZFile const &file : files)
2420 	{
2421 		progress = std::min<uint32_t>(progress, (uint32_t)((uint64_t)file.pos * 100 / (uint64_t)std::max<uint32_t>(file.size, 1)));
2422 	}
2423 	return static_cast<unsigned>(progress);
2424 }
2425 
readLobbyResponse(Socket * sock,unsigned int timeout)2426 static ssize_t readLobbyResponse(Socket *sock, unsigned int timeout)
2427 {
2428 	uint32_t lobbyStatusCode;
2429 	uint32_t MOTDLength;
2430 	uint32_t buffer[2];
2431 	ssize_t result, received = 0;
2432 
2433 	// Get status and message length
2434 	result = readAll(sock, &buffer, sizeof(buffer), timeout);
2435 	if (result != sizeof(buffer))
2436 	{
2437 		goto error;
2438 	}
2439 	received += result;
2440 	lobbyStatusCode = ntohl(buffer[0]);
2441 	MOTDLength = ntohl(buffer[1]);
2442 
2443 	// Get status message
2444 	if (NetPlay.MOTD)
2445 	{
2446 		free(NetPlay.MOTD);
2447 	}
2448 	NetPlay.MOTD = (char *)malloc(MOTDLength + 1);
2449 	result = readAll(sock, NetPlay.MOTD, MOTDLength, timeout);
2450 	if (result != MOTDLength)
2451 	{
2452 		goto error;
2453 	}
2454 	received += result;
2455 	// NUL terminate string
2456 	NetPlay.MOTD[MOTDLength] = '\0';
2457 
2458 	switch (lobbyStatusCode)
2459 	{
2460 	case 200:
2461 		debug(LOG_NET, "Lobby success (%u): %s", (unsigned int)lobbyStatusCode, NetPlay.MOTD);
2462 		NetPlay.HaveUpgrade = false;
2463 		break;
2464 
2465 	case 400:
2466 		debug(LOG_NET, "**Upgrade available! Lobby success (%u): %s", (unsigned int)lobbyStatusCode, NetPlay.MOTD);
2467 		NetPlay.HaveUpgrade = true;
2468 		break;
2469 
2470 	default:
2471 		debug(LOG_ERROR, "Lobby error (%u): %s", (unsigned int)lobbyStatusCode, NetPlay.MOTD);
2472 		// ensure if the lobby returns an error, we are prepared to display it (once)
2473 		NetPlay.ShowedMOTD = false;
2474 		break;
2475 	}
2476 
2477 	return received;
2478 
2479 error:
2480 	if (result == SOCKET_ERROR)
2481 	{
2482 		if (NetPlay.MOTD)
2483 		{
2484 			free(NetPlay.MOTD);
2485 		}
2486 		if (asprintf(&NetPlay.MOTD, "Error while connecting to the lobby server: %s\nMake sure port %d can receive incoming connections.", strSockError(getSockErr()), gameserver_port) == -1)
2487 		{
2488 			NetPlay.MOTD = nullptr;
2489 		}
2490 		else
2491 		{
2492 			NetPlay.ShowedMOTD = false;
2493 			debug(LOG_ERROR, "%s", NetPlay.MOTD);
2494 		}
2495 	}
2496 	else
2497 	{
2498 		if (NetPlay.MOTD)
2499 		{
2500 			free(NetPlay.MOTD);
2501 		}
2502 		if (asprintf(&NetPlay.MOTD, "Disconnected from lobby server. Failed to register game.") == -1)
2503 		{
2504 			NetPlay.MOTD = nullptr;
2505 		}
2506 		else
2507 		{
2508 			NetPlay.ShowedMOTD = false;
2509 			debug(LOG_ERROR, "%s", NetPlay.MOTD);
2510 		}
2511 	}
2512 
2513 	return SOCKET_ERROR;
2514 }
2515 
readGameStructsList(Socket * sock,unsigned int timeout,const std::function<bool (const GAMESTRUCT & game)> & handleEnumerateGameFunc)2516 bool readGameStructsList(Socket *sock, unsigned int timeout, const std::function<bool (const GAMESTRUCT& game)>& handleEnumerateGameFunc)
2517 {
2518 	unsigned int gamecount = 0;
2519 	uint32_t gamesavailable = 0;
2520 	int result = 0;
2521 
2522 	if ((result = readAll(sock, &gamesavailable, sizeof(gamesavailable), NET_TIMEOUT_DELAY)) == sizeof(gamesavailable))
2523 	{
2524 		gamesavailable = ntohl(gamesavailable);
2525 	}
2526 	else
2527 	{
2528 		if (result == SOCKET_ERROR)
2529 		{
2530 			debug(LOG_NET, "Server socket encountered error: %s", strSockError(getSockErr()));
2531 		}
2532 		else
2533 		{
2534 			debug(LOG_NET, "Server didn't respond (timeout)");
2535 		}
2536 		return false;
2537 	}
2538 
2539 	debug(LOG_NET, "receiving info on %u game(s)", (unsigned int)gamesavailable);
2540 
2541 	while (gamecount < gamesavailable)
2542 	{
2543 		// Attempt to receive a game description structure
2544 		GAMESTRUCT tmpGame;
2545 		memset(&tmpGame, 0x00, sizeof(tmpGame));
2546 		if (!NETrecvGAMESTRUCT(sock, &tmpGame))
2547 		{
2548 			debug(LOG_NET, "only %u game(s) received", (unsigned int)gamecount);
2549 			return false;
2550 		}
2551 
2552 		if (tmpGame.desc.host[0] == '\0')
2553 		{
2554 			memset(tmpGame.desc.host, 0, sizeof(tmpGame.desc.host));
2555 			strncpy(tmpGame.desc.host, getSocketTextAddress(sock), sizeof(tmpGame.desc.host) - 1);
2556 		}
2557 
2558 		uint32_t Vmgr = (tmpGame.future4 & 0xFFFF0000) >> 16;
2559 		uint32_t Vmnr = (tmpGame.future4 & 0x0000FFFF);
2560 
2561 		if (NETisGreaterVersion(Vmgr, Vmnr))
2562 		{
2563 			debug(LOG_NET, "Version update %d:%d", Vmgr, Vmnr);
2564 			NetPlay.HaveUpgrade = true;
2565 		}
2566 
2567 		if (tmpGame.desc.dwSize != 0)
2568 		{
2569 			if (!handleEnumerateGameFunc(tmpGame))
2570 			{
2571 				// stop enumerating
2572 				break;
2573 			}
2574 		}
2575 
2576 		++gamecount;
2577 	}
2578 
2579 	return true;
2580 }
2581 
connect()2582 bool LobbyServerConnectionHandler::connect()
2583 {
2584 	if (server_not_there)
2585 	{
2586 		return false;
2587 	}
2588 	if (currentState == LobbyConnectionState::Connecting_WaitingForResponse || currentState == LobbyConnectionState::Connected)
2589 	{
2590 		return false; // already connecting or connected
2591 	}
2592 
2593 	bool bProcessingConnectOrDisconnectThisCall = true;
2594 	uint32_t gameId = 0;
2595 	SocketAddress *const hosts = resolveHost(masterserver_name, masterserver_port);
2596 
2597 	if (hosts == nullptr)
2598 	{
2599 		debug(LOG_ERROR, "Cannot resolve masterserver \"%s\": %s", masterserver_name, strSockError(getSockErr()));
2600 		free(NetPlay.MOTD);
2601 		if (asprintf(&NetPlay.MOTD, _("Could not resolve masterserver name (%s)!"), masterserver_name) == -1)
2602 		{
2603 			NetPlay.MOTD = nullptr;
2604 		}
2605 		server_not_there = true;
2606 		return bProcessingConnectOrDisconnectThisCall;
2607 	}
2608 
2609 	// Close an existing socket.
2610 	if (rs_socket != nullptr)
2611 	{
2612 		socketClose(rs_socket);
2613 		rs_socket = nullptr;
2614 	}
2615 
2616 	// try each address from resolveHost until we successfully connect.
2617 	rs_socket = socketOpenAny(hosts, 1500);
2618 	deleteSocketAddress(hosts);
2619 
2620 	// No address succeeded.
2621 	if (rs_socket == nullptr)
2622 	{
2623 		debug(LOG_ERROR, "Cannot connect to masterserver \"%s:%d\": %s", masterserver_name, masterserver_port, strSockError(getSockErr()));
2624 		free(NetPlay.MOTD);
2625 		if (asprintf(&NetPlay.MOTD, _("Error connecting to the lobby server: %s.\nMake sure port %d can receive incoming connections.\nIf you're using a router configure it to use UPnP\n or to forward the port to your system."),
2626 					 strSockError(getSockErr()), masterserver_port) == -1)
2627 		{
2628 			NetPlay.MOTD = nullptr;
2629 		}
2630 		server_not_there = true;
2631 		return bProcessingConnectOrDisconnectThisCall;
2632 	}
2633 
2634 	// Get a game ID
2635 	if (writeAll(rs_socket, "gaId", sizeof("gaId")) == SOCKET_ERROR
2636 		|| readAll(rs_socket, &gameId, sizeof(gameId), 10000) != sizeof(gameId))
2637 	{
2638 		free(NetPlay.MOTD);
2639 		if (asprintf(&NetPlay.MOTD, "Failed to retrieve a game ID: %s", strSockError(getSockErr())) == -1)
2640 		{
2641 			NetPlay.MOTD = nullptr;
2642 		}
2643 		else
2644 		{
2645 			debug(LOG_ERROR, "%s", NetPlay.MOTD);
2646 		}
2647 
2648 		// The socket has been invalidated, so get rid of it. (using them now may cause SIGPIPE).
2649 		disconnect();
2650 		return bProcessingConnectOrDisconnectThisCall;
2651 	}
2652 
2653 	gamestruct.gameId = ntohl(gameId);
2654 	debug(LOG_NET, "Using game ID: %u", (unsigned int)gamestruct.gameId);
2655 
2656 	// Register our game with the server
2657 	if (writeAll(rs_socket, "addg", sizeof("addg")) == SOCKET_ERROR
2658 		// and now send what the server wants
2659 		|| !NETsendGAMESTRUCT(rs_socket, &gamestruct))
2660 	{
2661 		debug(LOG_ERROR, "Failed to register game with server: %s", strSockError(getSockErr()));
2662 		disconnect();
2663 		return bProcessingConnectOrDisconnectThisCall;
2664 	}
2665 
2666 	lastServerUpdate = realTime;
2667 	queuedServerUpdate = false;
2668 
2669 	lastConnectionTime = realTime;
2670 	waitingForConnectionFinalize = allocSocketSet();
2671 	SocketSet_AddSocket(waitingForConnectionFinalize, rs_socket);
2672 
2673 	currentState = LobbyConnectionState::Connecting_WaitingForResponse;
2674 	return bProcessingConnectOrDisconnectThisCall;
2675 }
2676 
disconnect()2677 bool LobbyServerConnectionHandler::disconnect()
2678 {
2679 	if (currentState == LobbyConnectionState::Disconnected)
2680 	{
2681 		return false; // already disconnected
2682 	}
2683 
2684 	if (rs_socket != nullptr)
2685 	{
2686 		// we don't need this anymore, so clean up
2687 		socketClose(rs_socket);
2688 		rs_socket = nullptr;
2689 		server_not_there = true;
2690 	}
2691 
2692 	queuedServerUpdate = false;
2693 
2694 	currentState = LobbyConnectionState::Disconnected;
2695 	return true;
2696 }
2697 
sendUpdate()2698 void LobbyServerConnectionHandler::sendUpdate()
2699 {
2700 	if (server_not_there)
2701 	{
2702 		return;
2703 	}
2704 
2705 	if (canSendServerUpdateNow())
2706 	{
2707 		sendUpdateNow();
2708 	}
2709 	else
2710 	{
2711 		// queue future update
2712 		debug(LOG_NET, "Queueing server update");
2713 		queuedServerUpdate = true;
2714 	}
2715 }
2716 
sendUpdateNow()2717 void LobbyServerConnectionHandler::sendUpdateNow()
2718 {
2719 	ASSERT_OR_RETURN(, rs_socket != nullptr, "Null socket");
2720 	if (!NETsendGAMESTRUCT(rs_socket, &gamestruct))
2721 	{
2722 		disconnect();
2723 		ActivityManager::instance().hostGameLobbyServerDisconnect();
2724 	}
2725 	lastServerUpdate = realTime;
2726 	queuedServerUpdate = false;
2727 	// newer lobby server will return a lobby response / status after each update call
2728 	if (rs_socket && readLobbyResponse(rs_socket, NET_TIMEOUT_DELAY) == SOCKET_ERROR)
2729 	{
2730 		disconnect();
2731 		ActivityManager::instance().hostGameLobbyServerDisconnect();
2732 	}
2733 }
2734 
sendKeepAlive()2735 void LobbyServerConnectionHandler::sendKeepAlive()
2736 {
2737 	ASSERT_OR_RETURN(, rs_socket != nullptr, "Null socket");
2738 	if (writeAll(rs_socket, "keep", sizeof("keep")) == SOCKET_ERROR)
2739 	{
2740 		// The socket has been invalidated, so get rid of it. (using them now may cause SIGPIPE).
2741 		disconnect();
2742 		ActivityManager::instance().hostGameLobbyServerDisconnect();
2743 	}
2744 	lastServerUpdate = realTime;
2745 }
2746 
run()2747 void LobbyServerConnectionHandler::run()
2748 {
2749 	switch (currentState)
2750 	{
2751 		case LobbyConnectionState::Disconnected:
2752 			return;
2753 			break;
2754 		case LobbyConnectionState::Connecting_WaitingForResponse:
2755 		{
2756 			// check if response has been received
2757 			ASSERT_OR_RETURN(, waitingForConnectionFinalize != nullptr, "Null socket set");
2758 			ASSERT_OR_RETURN(, rs_socket != nullptr, "Null socket");
2759 			bool exceededTimeout = (realTime - lastConnectionTime >= 10000);
2760 			// We use readLobbyResponse to display error messages and handle state changes if there's no response
2761 			// So if exceededTimeout, just call it with a low timeout
2762 			int checkSocketRet = checkSockets(waitingForConnectionFinalize, NET_READ_TIMEOUT);
2763 			if (checkSocketRet == SOCKET_ERROR)
2764 			{
2765 				debug(LOG_ERROR, "Lost connection to lobby server");
2766 				disconnect();
2767 				break;
2768 			}
2769 			if (exceededTimeout || (checkSocketRet > 0 && socketReadReady(rs_socket)))
2770 			{
2771 				if (readLobbyResponse(rs_socket, NET_TIMEOUT_DELAY) == SOCKET_ERROR)
2772 				{
2773 					disconnect();
2774 					break;
2775 				}
2776 				deleteSocketSet(waitingForConnectionFinalize);
2777 				waitingForConnectionFinalize = nullptr;
2778 				currentState = LobbyConnectionState::Connected;
2779 			}
2780 			break;
2781 		}
2782 		case LobbyConnectionState::Connected:
2783 		{
2784 			// handle sending keep alive or queued updates
2785 			if (!queuedServerUpdate)
2786 			{
2787 				if (allow_joining && shouldSendServerKeepAliveNow())
2788 				{
2789 					// ensure that the lobby server knows we're still alive by sending a no-op "keep-alive"
2790 					sendKeepAlive();
2791 				}
2792 				break;
2793 			}
2794 			if (!canSendServerUpdateNow())
2795 			{
2796 				break;
2797 			}
2798 			queuedServerUpdate = false;
2799 			sendUpdateNow();
2800 			break;
2801 		}
2802 	}
2803 }
2804 
NETregisterServer(int state)2805 bool NETregisterServer(int state)
2806 {
2807 	switch (state)
2808 	{
2809 		case WZ_SERVER_UPDATE:
2810 			lobbyConnectionHandler.sendUpdate();
2811 			break;
2812 		case WZ_SERVER_CONNECT:
2813 			return lobbyConnectionHandler.connect();
2814 			break;
2815 		case WZ_SERVER_DISCONNECT:
2816 			return lobbyConnectionHandler.disconnect();
2817 			break;
2818 	}
2819 
2820 	return false;
2821 }
2822 
2823 // ////////////////////////////////////////////////////////////////////////
2824 //  Check player "slots" & update player count if needed.
NETfixPlayerCount()2825 void NETfixPlayerCount()
2826 {
2827 	int maxPlayers = game.maxPlayers;
2828 	unsigned playercount = 0;
2829 	for (int index = 0; index < game.maxPlayers; ++index)
2830 	{
2831 		if (NetPlay.players[index].ai == AI_CLOSED)
2832 		{
2833 			--maxPlayers;
2834 		}
2835 		else if (NetPlay.players[index].ai != AI_OPEN || NetPlay.players[index].allocated)
2836 		{
2837 			++playercount;
2838 		}
2839 	}
2840 
2841 	if (allow_joining && NetPlay.isHost && (NetPlay.playercount != playercount || gamestruct.desc.dwMaxPlayers != maxPlayers))
2842 	{
2843 		debug(LOG_NET, "Updating player count from %d/%d to %d/%d", (int)NetPlay.playercount, gamestruct.desc.dwMaxPlayers, playercount, maxPlayers);
2844 		gamestruct.desc.dwCurrentPlayers = NetPlay.playercount = playercount;
2845 		gamestruct.desc.dwMaxPlayers = maxPlayers;
2846 		NETregisterServer(WZ_SERVER_UPDATE);
2847 	}
2848 
2849 }
2850 // ////////////////////////////////////////////////////////////////////////
2851 // Host a game with a given name and player name. & 4 user game flags
NETallowJoining()2852 static void NETallowJoining()
2853 {
2854 	unsigned int i;
2855 	int32_t result;
2856 	bool connectFailed = true;
2857 	uint32_t major, minor;
2858 	ssize_t recv_result = 0;
2859 
2860 	if (allow_joining == false)
2861 	{
2862 		return;
2863 	}
2864 	ASSERT(NetPlay.isHost, "Cannot receive joins if not host!");
2865 
2866 	bool bFirstTimeConnect = NETregisterServer(WZ_SERVER_CONNECT);
2867 	if (bFirstTimeConnect)
2868 	{
2869 		ActivityManager::instance().updateMultiplayGameData(game, ingame, NETGameIsLocked());
2870 		ActivitySink::ListeningInterfaces listeningInterfaces;
2871 		if (tcp_socket != nullptr)
2872 		{
2873 			listeningInterfaces.IPv4 = socketHasIPv4(tcp_socket);
2874 			if (listeningInterfaces.IPv4)
2875 			{
2876 				listeningInterfaces.ipv4_port = NETgetGameserverPort();
2877 			}
2878 			listeningInterfaces.IPv6 = socketHasIPv6(tcp_socket);
2879 			if (listeningInterfaces.IPv6)
2880 			{
2881 				listeningInterfaces.ipv6_port = NETgetGameserverPort();
2882 			}
2883 		}
2884 		ActivityManager::instance().hostGame(gamestruct.name, NetPlay.players[0].name, NETgetMasterserverName(), NETgetMasterserverPort(), listeningInterfaces, gamestruct.gameId);
2885 	}
2886 
2887 	// This is here since we need to get the status, before we can show the info.
2888 	// FIXME: find better location to stick this?
2889 	if ((!NetPlay.ShowedMOTD) && (NetPlay.MOTD != nullptr))
2890 	{
2891 		ShowMOTD();
2892 		free(NetPlay.MOTD);
2893 		NetPlay.MOTD = nullptr;
2894 		NetPlay.ShowedMOTD = true;
2895 	}
2896 
2897 	if (tmp_socket_set == nullptr)
2898 	{
2899 		// initialize server socket set
2900 		// FIXME: why is this not done in NETinit()?? - Per
2901 		tmp_socket_set = allocSocketSet();
2902 		if (tmp_socket_set == nullptr)
2903 		{
2904 			debug(LOG_ERROR, "Cannot create socket set: %s", strSockError(getSockErr()));
2905 			return;
2906 		}
2907 	}
2908 
2909 	// Find the first empty socket slot
2910 	for (i = 0; i < MAX_TMP_SOCKETS; ++i)
2911 	{
2912 		if (tmp_socket[i] == nullptr)
2913 		{
2914 			break;
2915 		}
2916 	}
2917 	if (i == MAX_TMP_SOCKETS)
2918 	{
2919 		// this should *never* happen, it would mean we are going to reuse a socket already in use.
2920 		debug(LOG_ERROR, "all temp sockets are used up!");
2921 		return;
2922 	}
2923 
2924 	// See if there's an incoming connection
2925 	if (tmp_socket[i] == nullptr // Make sure that we're not out of sockets
2926 	    && (tmp_socket[i] = socketAccept(tcp_socket)) != nullptr)
2927 	{
2928 		NETinitQueue(NETnetTmpQueue(i));
2929 		SocketSet_AddSocket(tmp_socket_set, tmp_socket[i]);
2930 
2931 		char buffer[10] = {'\0'};
2932 		char *p_buffer = buffer;
2933 
2934 		// We check for socket activity (connection), and then we check if we got data, since it is possible to have a connection
2935 		// and have no data waiting.
2936 		if (checkSockets(tmp_socket_set, NET_TIMEOUT_DELAY) > 0
2937 		    && socketReadReady(tmp_socket[i])
2938 		    && (recv_result = readNoInt(tmp_socket[i], p_buffer, 8))
2939 		    && recv_result != SOCKET_ERROR)
2940 		{
2941 			std::string rIP = "Incoming connection from:";
2942 			rIP.append(getSocketTextAddress(tmp_socket[i]));
2943 			NETlogEntry(rIP.c_str(), SYNC_FLAG, i);
2944 			// A 2.3.7 client sends a "list" command first, just drop the connection.
2945 			if (strcmp(buffer, "list") == 0)
2946 			{
2947 				debug(LOG_INFO, "An old client tried to connect, closing the socket.");
2948 				NETlogEntry("Dropping old client.", SYNC_FLAG, i);
2949 				NETlogEntry("Invalid (old)game version", SYNC_FLAG, i);
2950 				addToBanList(rIP.c_str(), "BAD_USER");
2951 				connectFailed = true;
2952 			}
2953 			else
2954 			{
2955 				// New clients send NETCODE_VERSION_MAJOR and NETCODE_VERSION_MINOR
2956 				// Check these numbers with our own.
2957 
2958 				memcpy(&major, p_buffer, sizeof(uint32_t));
2959 				major = ntohl(major);
2960 				p_buffer += sizeof(int32_t);
2961 				memcpy(&minor, p_buffer, sizeof(uint32_t));
2962 				minor = ntohl(minor);
2963 
2964 				if (major == 0 && minor == 0)
2965 				{
2966 					// special case for lobby server "alive" check
2967 					// expects a special response that includes the gameId
2968 					const char ResponseStart[] = "WZLR";
2969 					char buf[(sizeof(char) * 4) + sizeof(uint32_t) + sizeof(uint32_t)] = { 0 };
2970 					char *pLobbyRespBuffer = buf;
2971 					auto push32 = [&pLobbyRespBuffer](uint32_t value) {
2972 						uint32_t swapped = htonl(value);
2973 						memcpy(pLobbyRespBuffer, &swapped, sizeof(swapped));
2974 						pLobbyRespBuffer += sizeof(swapped);
2975 					};
2976 
2977 					// Copy response prefix chars ("WZLR")
2978 					memcpy(pLobbyRespBuffer, ResponseStart, sizeof(char) * strlen(ResponseStart));
2979 					pLobbyRespBuffer += sizeof(char) * strlen(ResponseStart);
2980 
2981 					// Copy version of response
2982 					const uint32_t response_version = 1;
2983 					push32(response_version);
2984 
2985 					// Copy gameId (as 32bit large big endian number)
2986 					push32(gamestruct.gameId);
2987 
2988 					writeAll(tmp_socket[i], buf, sizeof(buf));
2989 					connectFailed = true;
2990 				}
2991 				else if (NETisCorrectVersion(major, minor))
2992 				{
2993 					result = htonl(ERROR_NOERROR);
2994 					memcpy(&buffer, &result, sizeof(result));
2995 					writeAll(tmp_socket[i], &buffer, sizeof(result));
2996 					socketBeginCompression(tmp_socket[i]);
2997 
2998 					// Connection is successful.
2999 					connectFailed = false;
3000 				}
3001 				else
3002 				{
3003 					debug(LOG_ERROR, "Received an invalid version \"%d.%d\".", major, minor);
3004 					result = htonl(ERROR_WRONGVERSION);
3005 					memcpy(&buffer, &result, sizeof(result));
3006 					writeAll(tmp_socket[i], &buffer, sizeof(result));
3007 					NETlogEntry("Invalid game version", SYNC_FLAG, i);
3008 					addToBanList(rIP.c_str(), "BAD_USER");
3009 					connectFailed = true;
3010 				}
3011 				if ((!connectFailed) && ((int)NetPlay.playercount == gamestruct.desc.dwMaxPlayers))
3012 				{
3013 					// early player count test, in case they happen to get in before updates.
3014 					// Tell the player that we are full.
3015 					uint8_t rejected = ERROR_FULL;
3016 					NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
3017 					NETuint8_t(&rejected);
3018 					NETend();
3019 					NETflush();
3020 					connectFailed = true;
3021 				}
3022 			}
3023 		}
3024 		else
3025 		{
3026 			debug(LOG_NET, "Failed to process joining, socket not ready or no data, recv_result is :%d", (int)recv_result);
3027 			connectFailed = true;
3028 		}
3029 
3030 		// Remove a failed connection.
3031 		if (connectFailed)
3032 		{
3033 			debug(LOG_NET, "freeing temp socket %p (%d)", static_cast<void *>(tmp_socket[i]), __LINE__);
3034 			SocketSet_DelSocket(tmp_socket_set, tmp_socket[i]);
3035 			socketClose(tmp_socket[i]);
3036 			tmp_socket[i] = nullptr;
3037 		}
3038 	}
3039 
3040 	if (checkSockets(tmp_socket_set, NET_READ_TIMEOUT) > 0)
3041 	{
3042 		for (i = 0; i < MAX_TMP_SOCKETS; ++i)
3043 		{
3044 			if (tmp_socket[i] != nullptr
3045 			    && socketReadReady(tmp_socket[i]))
3046 			{
3047 				uint8_t buffer[NET_BUFFER_SIZE];
3048 				ssize_t size = readNoInt(tmp_socket[i], buffer, sizeof(buffer));
3049 
3050 				if ((size == 0 && socketReadDisconnected(tmp_socket[i])) || size == SOCKET_ERROR)
3051 				{
3052 					// disconnect or programmer error
3053 					if (size == 0)
3054 					{
3055 						debug(LOG_NET, "Client socket disconnected.");
3056 					}
3057 					else
3058 					{
3059 						debug(LOG_NET, "Client socket encountered error: %s", strSockError(getSockErr()));
3060 					}
3061 					NETlogEntry("Client socket disconnected (allowJoining)", SYNC_FLAG, i);
3062 					debug(LOG_NET, "freeing temp socket %p (%d)", static_cast<void *>(tmp_socket[i]), __LINE__);
3063 					SocketSet_DelSocket(tmp_socket_set, tmp_socket[i]);
3064 					socketClose(tmp_socket[i]);
3065 					tmp_socket[i] = nullptr;
3066 					continue;
3067 				}
3068 
3069 				NETinsertRawData(NETnetTmpQueue(i), buffer, size);
3070 
3071 				if (NETisMessageReady(NETnetTmpQueue(i)) && NETgetMessage(NETnetTmpQueue(i))->type == NET_JOIN)
3072 				{
3073 					uint8_t j;
3074 					uint8_t index;
3075 					uint8_t rejected = 0;
3076 					int tmp;
3077 
3078 					char name[64];
3079 					char ModList[modlist_string_size] = { '\0' };
3080 					char GamePassword[password_string_size] = { '\0' };
3081 
3082 					NETbeginDecode(NETnetTmpQueue(i), NET_JOIN);
3083 					NETstring(name, sizeof(name));
3084 					NETstring(ModList, sizeof(ModList));
3085 					NETstring(GamePassword, sizeof(GamePassword));
3086 					NETend();
3087 
3088 					tmp = NET_CreatePlayer(name);
3089 
3090 					if (tmp == -1)
3091 					{
3092 						debug(LOG_ERROR, "freeing temp socket %p, couldn't create player!", static_cast<void *>(tmp_socket[i]));
3093 
3094 						// Tell the player that we are full.
3095 						rejected = ERROR_FULL;
3096 						NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
3097 						NETuint8_t(&rejected);
3098 						NETend();
3099 						NETflush();
3100 						NETpop(NETnetTmpQueue(i));
3101 
3102 						SocketSet_DelSocket(tmp_socket_set, tmp_socket[i]);
3103 						socketClose(tmp_socket[i]);
3104 						tmp_socket[i] = nullptr;
3105 						sync_counter.cantjoin++;
3106 						return;
3107 					}
3108 
3109 					NETpop(NETnetTmpQueue(i));
3110 					index = tmp;
3111 
3112 					debug(LOG_NET, "freeing temp socket %p (%d), creating permanent socket.", static_cast<void *>(tmp_socket[i]), __LINE__);
3113 					SocketSet_DelSocket(tmp_socket_set, tmp_socket[i]);
3114 					connected_bsocket[index] = tmp_socket[i];
3115 					tmp_socket[i] = nullptr;
3116 					SocketSet_AddSocket(socket_set, connected_bsocket[index]);
3117 					NETmoveQueue(NETnetTmpQueue(i), NETnetQueue(index));
3118 
3119 					// Copy player's IP address.
3120 					sstrcpy(NetPlay.players[index].IPtextAddress, getSocketTextAddress(connected_bsocket[index]));
3121 
3122 					if (onBanList(NetPlay.players[index].IPtextAddress))
3123 					{
3124 						char buf[256] = {'\0'};
3125 						ssprintf(buf, "** A player that you have kicked tried to rejoin the game, and was rejected. IP: %s", NetPlay.players[index].IPtextAddress);
3126 						debug(LOG_INFO, "%s", buf);
3127 						NETlogEntry(buf, SYNC_FLAG, i);
3128 
3129 						// Player has been kicked before, kick again.
3130 						rejected = (uint8_t)ERROR_KICKED;
3131 					}
3132 					else if (NetPlay.GamePassworded && strcmp(NetPlay.gamePassword, GamePassword) != 0)
3133 					{
3134 						// Wrong password. Reject.
3135 						rejected = (uint8_t)ERROR_WRONGPASSWORD;
3136 					}
3137 					else if ((int)NetPlay.playercount > gamestruct.desc.dwMaxPlayers)
3138 					{
3139 						// Game full. Reject.
3140 						rejected = (uint8_t)ERROR_FULL;
3141 					}
3142 
3143 					if (rejected)
3144 					{
3145 						char buf[256] = {'\0'};
3146 						ssprintf(buf, "**Rejecting player(%s), reason (%u). ", NetPlay.players[index].IPtextAddress, (unsigned int) rejected);
3147 						debug(LOG_INFO, "%s", buf);
3148 						NETlogEntry(buf, SYNC_FLAG, index);
3149 						NETbeginEncode(NETnetQueue(index), NET_REJECTED);
3150 						NETuint8_t(&rejected);
3151 						NETend();
3152 						NETflush();
3153 
3154 						allow_joining = false; // no need to inform master server
3155 						NET_DestroyPlayer(index);
3156 						allow_joining = true;
3157 
3158 						SocketSet_DelSocket(socket_set, connected_bsocket[index]);
3159 						socketClose(connected_bsocket[index]);
3160 						connected_bsocket[index] = nullptr;
3161 						return;
3162 					}
3163 
3164 					NETbeginEncode(NETnetQueue(index), NET_ACCEPTED);
3165 					NETuint8_t(&index);
3166 					NETend();
3167 
3168 					// First send info about players to newcomer.
3169 					NETSendAllPlayerInfoTo(index);
3170 					// then send info about newcomer to all players.
3171 					NETBroadcastPlayerInfo(index);
3172 
3173 					char buf[250] = {'\0'};
3174 					snprintf(buf, sizeof(buf), "Player %s has joined, IP is: %s", name, NetPlay.players[index].IPtextAddress);
3175 					debug(LOG_INFO, "%s", buf);
3176 					NETlogEntry(buf, SYNC_FLAG, index);
3177 
3178 					debug(LOG_NET, "Player, %s, with index of %u has joined using socket %p", name, (unsigned int)index, static_cast<void *>(connected_bsocket[index]));
3179 
3180 					// Increment player count
3181 					gamestruct.desc.dwCurrentPlayers++;
3182 
3183 					MultiPlayerJoin(index);
3184 
3185 					// Narrowcast to new player that everyone has joined.
3186 					for (j = 0; j < MAX_CONNECTED_PLAYERS; ++j)
3187 					{
3188 						if (index != j)  // We will broadcast the index == j case.
3189 						{
3190 							if (NetPlay.players[j].allocated)
3191 							{
3192 								NETbeginEncode(NETnetQueue(index), NET_PLAYER_JOINED);
3193 								NETuint8_t(&j);
3194 								NETend();
3195 							}
3196 						}
3197 					}
3198 
3199 					// Broadcast to everyone that a new player has joined
3200 					NETbeginEncode(NETbroadcastQueue(), NET_PLAYER_JOINED);
3201 					NETuint8_t(&index);
3202 					NETend();
3203 
3204 					for (j = 0; j < MAX_CONNECTED_PLAYERS; ++j)
3205 					{
3206 						NETBroadcastPlayerInfo(j);
3207 					}
3208 					NETfixDuplicatePlayerNames();
3209 
3210 					// Send the updated GAMESTRUCT to the masterserver
3211 					NETregisterServer(WZ_SERVER_UPDATE);
3212 
3213 					// reset flags for new players
3214 					NetPlay.players[index].wzFiles.clear();
3215 				}
3216 			}
3217 		}
3218 	}
3219 }
3220 
NETloadBanList()3221 void NETloadBanList() {
3222 	char BanListPath[4096] = {0};
3223 	strncpy(BanListPath, PHYSFS_getWriteDir(), 4095);
3224 	size_t BanListAppendFname = strlen(BanListPath);
3225 	strncpy(BanListPath+BanListAppendFname, "/banlist.txt", 4095-BanListAppendFname);
3226 	FILE* f = fopen(BanListPath, "r");
3227 	if(f == NULL) {
3228 		return;
3229 	}
3230 	debug(LOG_INFO, "Reading banlist file: [%s]\n", BanListPath);
3231 	char BanStringBuf[2048] = {0};
3232 	char ToBanIP[256] = {0};
3233 	char ToBanName[256] = {0};
3234 	while(fgets(BanStringBuf, sizeof(BanStringBuf)-1, f)) {
3235 		if(sscanf(BanStringBuf, "%255s %255[^\n]", ToBanIP, ToBanName) != 2) {
3236 			if(strlen(BanStringBuf) > 2) {
3237 				debug(LOG_ERROR, "Error reading banlist file!\n");
3238 			}
3239 		} else {
3240 			addToBanList(ToBanIP, ToBanName);
3241 		}
3242 	}
3243 	return;
3244 }
3245 
NEThostGame(const char * SessionName,const char * PlayerName,SDWORD one,SDWORD two,SDWORD three,SDWORD four,UDWORD plyrs)3246 bool NEThostGame(const char *SessionName, const char *PlayerName,
3247                  SDWORD one, SDWORD two, SDWORD three, SDWORD four,
3248                  UDWORD plyrs)	// # of players.
3249 {
3250 	debug(LOG_NET, "NEThostGame(%s, %s, %d, %d, %d, %d, %u)", SessionName, PlayerName,
3251 	      one, two, three, four, plyrs);
3252 
3253 	netPlayersUpdated = true;
3254 
3255 	for (unsigned playerIndex = 0; playerIndex < MAX_PLAYERS; ++playerIndex)
3256 	{
3257 		initPlayerNetworkProps(playerIndex);
3258 	}
3259 	if (!NetPlay.bComms)
3260 	{
3261 		selectedPlayer			= 0;
3262 		NetPlay.isHost			= true;
3263 		NetPlay.players[0].allocated	= true;
3264 		NetPlay.players[0].connection	= -1;
3265 		NetPlay.playercount		= 1;
3266 		debug(LOG_NET, "Hosting but no comms");
3267 		// Now switch player color of the host to what they normally use for MP games
3268 		if (war_getMPcolour() >= 0)
3269 		{
3270 			changeColour(NET_HOST_ONLY, war_getMPcolour(), true);
3271 		}
3272 		return true;
3273 	}
3274 
3275 	// tcp_socket is the connection to the lobby server (or machine)
3276 	if (!tcp_socket)
3277 	{
3278 		tcp_socket = socketListen(gameserver_port);
3279 	}
3280 	if (tcp_socket == nullptr)
3281 	{
3282 		debug(LOG_ERROR, "Cannot connect to master self: %s", strSockError(getSockErr()));
3283 		return false;
3284 	}
3285 	debug(LOG_NET, "New tcp_socket = %p", static_cast<void *>(tcp_socket));
3286 	// Host needs to create a socket set for MAX_PLAYERS
3287 	if (!socket_set)
3288 	{
3289 		socket_set = allocSocketSet();
3290 	}
3291 	if (socket_set == nullptr)
3292 	{
3293 		debug(LOG_ERROR, "Cannot create socket set: %s", strSockError(getSockErr()));
3294 		return false;
3295 	}
3296 	// allocate socket storage for all possible players
3297 	for (unsigned i = 0; i < MAX_CONNECTED_PLAYERS; ++i)
3298 	{
3299 		connected_bsocket[i] = nullptr;
3300 		NETinitQueue(NETnetQueue(i));
3301 	}
3302 
3303 	NetPlay.isHost = true;
3304 	NETlogEntry("Hosting game, resetting ban list.", SYNC_FLAG, 0);
3305 	if (IPlist)
3306 	{
3307 		free(IPlist);
3308 		IPlist = nullptr;
3309 		IPlistLast = 0;
3310 	}
3311 	NETloadBanList();
3312 	sstrcpy(gamestruct.name, SessionName);
3313 	memset(&gamestruct.desc, 0, sizeof(gamestruct.desc));
3314 	gamestruct.desc.dwSize = sizeof(gamestruct.desc);
3315 	//gamestruct.desc.guidApplication = GAME_GUID;
3316 	memset(gamestruct.desc.host, 0, sizeof(gamestruct.desc.host));
3317 	gamestruct.desc.dwCurrentPlayers = 1;
3318 	gamestruct.desc.dwMaxPlayers = plyrs;
3319 	gamestruct.desc.dwFlags = 0;
3320 	gamestruct.desc.dwUserFlags[0] = one;
3321 	gamestruct.desc.dwUserFlags[1] = two;
3322 	gamestruct.desc.dwUserFlags[2] = three;
3323 	gamestruct.desc.dwUserFlags[3] = four;
3324 	memset(gamestruct.secondaryHosts, 0, sizeof(gamestruct.secondaryHosts));
3325 	sstrcpy(gamestruct.extra, "Extra");						// extra string (future use)
3326 	gamestruct.hostPort = gameserver_port;
3327 	sstrcpy(gamestruct.mapname, game.map);					// map we are hosting
3328 	sstrcpy(gamestruct.hostname, PlayerName);
3329 	sstrcpy(gamestruct.versionstring, versionString);		// version (string)
3330 	sstrcpy(gamestruct.modlist, getModList().c_str());      // List of mods
3331 	gamestruct.GAMESTRUCT_VERSION = 4;						// version of this structure
3332 	gamestruct.game_version_major = NETCODE_VERSION_MAJOR;	// Netcode Major version
3333 	gamestruct.game_version_minor = NETCODE_VERSION_MINOR;	// NetCode Minor version
3334 //	gamestruct.privateGame = 0;								// if true, it is a private game
3335 	gamestruct.pureMap = game.isMapMod;								// If map-mod...
3336 	gamestruct.Mods = 0;										// number of concatenated mods?
3337 	gamestruct.gameId  = 0;
3338 	gamestruct.limits = 0x0;									// used for limits
3339 #if defined(WZ_OS_WIN)
3340 	gamestruct.future3 = 0x77696e;								// for future use
3341 #elif defined (WZ_OS_MAC)
3342 	gamestruct.future3 = 0x6d6163;								// for future use
3343 #else
3344 	gamestruct.future3 = 0x6c696e;								// for future use
3345 #endif
3346 	gamestruct.future4 = NETCODE_VERSION_MAJOR << 16 | NETCODE_VERSION_MINOR;	// for future use
3347 
3348 	selectedPlayer = NET_CreatePlayer(PlayerName, (getHostLaunch() == HostLaunch::Autohost));
3349 	ASSERT_OR_RETURN(false, selectedPlayer < MAX_PLAYERS, "Failed to create player");
3350 	realSelectedPlayer = selectedPlayer;
3351 	NetPlay.isHost	= true;
3352 	NetPlay.isHostAlive = true;
3353 	NetPlay.HaveUpgrade = false;
3354 	NetPlay.hostPlayer	= NET_HOST_ONLY;
3355 	ASSERT(selectedPlayer == NET_HOST_ONLY, "For now, host must start at player index zero, was %d", (int)selectedPlayer);
3356 
3357 	MultiPlayerJoin(selectedPlayer);
3358 
3359 	// Now switch player color of the host to what they normally use for SP games
3360 	if (war_getMPcolour() >= 0)
3361 	{
3362 		changeColour(NET_HOST_ONLY, war_getMPcolour(), true);
3363 	}
3364 
3365 	allow_joining = true;
3366 
3367 	NETregisterServer(WZ_SERVER_DISCONNECT);
3368 
3369 	debug(LOG_NET, "Hosting a server. We are player %d.", selectedPlayer);
3370 
3371 	return true;
3372 }
3373 
3374 // ////////////////////////////////////////////////////////////////////////
3375 // Stop the dplay interface from accepting more players.
NEThaltJoining()3376 bool NEThaltJoining()
3377 {
3378 	debug(LOG_NET, "temporarily locking game to prevent more players");
3379 
3380 	allow_joining = false;
3381 	// disconnect from the master server
3382 	NETregisterServer(WZ_SERVER_DISCONNECT);
3383 	return true;
3384 }
3385 
3386 // ////////////////////////////////////////////////////////////////////////
3387 // find games on open connection
NETenumerateGames(const std::function<bool (const GAMESTRUCT & game)> & handleEnumerateGameFunc)3388 bool NETenumerateGames(const std::function<bool (const GAMESTRUCT& game)>& handleEnumerateGameFunc)
3389 {
3390 	SocketAddress *hosts;
3391 	int result = 0;
3392 	debug(LOG_NET, "Looking for games...");
3393 
3394 	if (getLobbyError() == ERROR_INVALID || getLobbyError() == ERROR_KICKED || getLobbyError() == ERROR_HOSTDROPPED)
3395 	{
3396 		return false;
3397 	}
3398 	setLobbyError(ERROR_NOERROR);
3399 
3400 	if (!NetPlay.bComms)
3401 	{
3402 		debug(LOG_ERROR, "Likely missing NETinit(true) - this won't return any results");
3403 		return false;
3404 	}
3405 	if ((hosts = resolveHost(masterserver_name, masterserver_port)) == nullptr)
3406 	{
3407 		debug(LOG_ERROR, "Cannot resolve hostname \"%s\": %s", masterserver_name, strSockError(getSockErr()));
3408 		setLobbyError(ERROR_CONNECTION);
3409 		return false;
3410 	}
3411 
3412 	if (tcp_socket != nullptr)
3413 	{
3414 		debug(LOG_NET, "Deleting tcp_socket %p", static_cast<void *>(tcp_socket));
3415 		if (socket_set)
3416 		{
3417 			SocketSet_DelSocket(socket_set, tcp_socket);
3418 		}
3419 		socketClose(tcp_socket);
3420 		tcp_socket = nullptr;
3421 	}
3422 
3423 	tcp_socket = socketOpenAny(hosts, 15000);
3424 
3425 	deleteSocketAddress(hosts);
3426 	hosts = nullptr;
3427 
3428 	if (tcp_socket == nullptr)
3429 	{
3430 		debug(LOG_ERROR, "Cannot connect to \"%s:%d\": %s", masterserver_name, masterserver_port, strSockError(getSockErr()));
3431 		setLobbyError(ERROR_CONNECTION);
3432 		return false;
3433 	}
3434 	debug(LOG_NET, "New tcp_socket = %p", static_cast<void *>(tcp_socket));
3435 	// client machines only need 1 socket set
3436 	socket_set = allocSocketSet();
3437 	if (socket_set == nullptr)
3438 	{
3439 		debug(LOG_ERROR, "Cannot create socket set: %s", strSockError(getSockErr()));
3440 		setLobbyError(ERROR_CONNECTION);
3441 		return false;
3442 	}
3443 	debug(LOG_NET, "Created socket_set %p", static_cast<void *>(socket_set));
3444 
3445 	SocketSet_AddSocket(socket_set, tcp_socket);
3446 
3447 	debug(LOG_NET, "Sending list cmd");
3448 
3449 	if (writeAll(tcp_socket, "list", sizeof("list")) == SOCKET_ERROR)
3450 	{
3451 		debug(LOG_NET, "Server socket encountered error: %s", strSockError(getSockErr()));
3452 		SocketSet_DelSocket(socket_set, tcp_socket);		// mark it invalid
3453 		socketClose(tcp_socket);
3454 		tcp_socket = nullptr;
3455 
3456 		// when we fail to receive a game count, bail out
3457 		setLobbyError(ERROR_CONNECTION);
3458 		return false;
3459 	}
3460 
3461 	// Retrieve the first batch of game structs
3462 	// Earlier versions (and earlier lobby servers) restricted this to no more than 11
3463 	std::vector<GAMESTRUCT> initialBatchOfGameStructs;
3464 
3465 	if (!readGameStructsList(tcp_socket, NET_TIMEOUT_DELAY, [&initialBatchOfGameStructs](const GAMESTRUCT &lobbyGame) -> bool {
3466 		initialBatchOfGameStructs.push_back(lobbyGame);
3467 		return true; // continue enumerating
3468 	}))
3469 	{
3470 		SocketSet_DelSocket(socket_set, tcp_socket);		// mark it invalid
3471 		socketClose(tcp_socket);
3472 		tcp_socket = nullptr;
3473 
3474 		setLobbyError(ERROR_CONNECTION);
3475 		return false;
3476 	}
3477 
3478 	// read the lobby response
3479 	if (readLobbyResponse(tcp_socket, NET_TIMEOUT_DELAY) == SOCKET_ERROR)
3480 	{
3481 		socketClose(tcp_socket);
3482 		tcp_socket = nullptr;
3483 		addConsoleMessage(_("Failed to get a lobby response!"), DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
3484 
3485 		// treat as fatal error
3486 		setLobbyError(ERROR_CONNECTION);
3487 		return false;
3488 	}
3489 
3490 	// Backwards-compatible protocol enhancement, to raise game limit
3491 	// Expects a uint32_t to determine whether to ignore the first batch of game structs
3492 	// Earlier lobby servers will not provide anything, or may null-pad the lobby response / MOTD string
3493 	// Hence as long as we don't treat "0" as signifying any change in behavior, this should be safe + backwards-compatible
3494 	#define IGNORE_FIRST_BATCH 1
3495 	uint32_t responseParameters = 0;
3496 	if ((result = readAll(tcp_socket, &responseParameters, sizeof(responseParameters), NET_TIMEOUT_DELAY)) == sizeof(responseParameters))
3497 	{
3498 		responseParameters = ntohl(responseParameters);
3499 
3500 		bool requestSecondBatch = true;
3501 		bool ignoreFirstBatch = ((responseParameters & IGNORE_FIRST_BATCH) == IGNORE_FIRST_BATCH);
3502 		if (!ignoreFirstBatch)
3503 		{
3504 			// pass the first batch to the handleEnumerateGameFunc
3505 			for (const auto& lobbyGame : initialBatchOfGameStructs)
3506 			{
3507 				if (!handleEnumerateGameFunc(lobbyGame))
3508 				{
3509 					// stop enumerating
3510 					requestSecondBatch = false;
3511 					break;
3512 				}
3513 			}
3514 		}
3515 
3516 		if (requestSecondBatch)
3517 		{
3518 			if (!readGameStructsList(tcp_socket, NET_TIMEOUT_DELAY, handleEnumerateGameFunc))
3519 			{
3520 				// we failed to read a second list of game structs
3521 
3522 				if (!ignoreFirstBatch)
3523 				{
3524 					// just log and treat the error as non-fatal
3525 					debug(LOG_NET, "Second readGameStructsList call failed - ignoring");
3526 				}
3527 				else
3528 				{
3529 					// if ignoring the first batch, treat this as a fatal error
3530 					debug(LOG_NET, "Second readGameStructsList call failed");
3531 
3532 					SocketSet_DelSocket(socket_set, tcp_socket);		// mark it invalid
3533 					socketClose(tcp_socket);
3534 					tcp_socket = nullptr;
3535 
3536 					// when we fail to receive a game count, bail out
3537 					setLobbyError(ERROR_CONNECTION);
3538 					return false;
3539 				}
3540 			}
3541 		}
3542 	}
3543 	else
3544 	{
3545 		// to support earlier lobby servers that don't provide this additional second batch (and optional parameter), just process the first batch
3546 		for (const auto& lobbyGame : initialBatchOfGameStructs)
3547 		{
3548 			if (!handleEnumerateGameFunc(lobbyGame))
3549 			{
3550 				// stop enumerating
3551 				break;
3552 			}
3553 		}
3554 	}
3555 
3556 	SocketSet_DelSocket(socket_set, tcp_socket);		// mark it invalid (we are done with it)
3557 	socketClose(tcp_socket);
3558 	tcp_socket = nullptr;
3559 
3560 	return true;
3561 }
3562 
NETfindGames(std::vector<GAMESTRUCT> & results,size_t startingIndex,size_t resultsLimit,bool onlyMatchingLocalVersion)3563 bool NETfindGames(std::vector<GAMESTRUCT>& results, size_t startingIndex, size_t resultsLimit, bool onlyMatchingLocalVersion /*= false*/)
3564 {
3565 	size_t gamecount = 0;
3566 	results.clear();
3567 	bool success = NETenumerateGames([&results, &gamecount, startingIndex, resultsLimit, onlyMatchingLocalVersion](const GAMESTRUCT &lobbyGame) -> bool {
3568 		if (gamecount++ < startingIndex)
3569 		{
3570 			// skip this item, continue
3571 			return true;
3572 		}
3573 		if ((resultsLimit > 0) && (results.size() >= resultsLimit))
3574 		{
3575 			// stop processing games
3576 			return false;
3577 		}
3578 		if ((onlyMatchingLocalVersion) && ((lobbyGame.game_version_major != (unsigned)NETGetMajorVersion()) || (lobbyGame.game_version_minor != (unsigned)NETGetMinorVersion())))
3579 		{
3580 			// skip this non-matching version, continue
3581 			return true;
3582 		}
3583 		results.push_back(lobbyGame);
3584 		return true;
3585 	});
3586 
3587 	return success;
3588 }
3589 
NETfindGame(uint32_t gameId,GAMESTRUCT & output)3590 bool NETfindGame(uint32_t gameId, GAMESTRUCT& output)
3591 {
3592 	bool foundMatch = false;
3593 	memset(&output, 0x00, sizeof(output));
3594 	NETenumerateGames([&foundMatch, &output, gameId](const GAMESTRUCT &lobbyGame) -> bool {
3595 		if (lobbyGame.gameId != gameId)
3596 		{
3597 			// not a match - continue enumerating
3598 			return true;
3599 		}
3600 		output = lobbyGame;
3601 		foundMatch = true;
3602 		return false; // stop searching
3603 	});
3604 	return foundMatch;
3605 }
3606 
3607 // ////////////////////////////////////////////////////////////////////////
3608 // ////////////////////////////////////////////////////////////////////////
3609 // Functions used to setup and join games.
NETjoinGame(const char * host,uint32_t port,const char * playername)3610 bool NETjoinGame(const char *host, uint32_t port, const char *playername)
3611 {
3612 	SocketAddress *hosts = nullptr;
3613 	unsigned int i;
3614 	char buffer[sizeof(int32_t) * 2] = { 0 };
3615 	char *p_buffer;
3616 	uint32_t result;
3617 
3618 	if (port == 0)
3619 	{
3620 		port = gameserver_port;
3621 	}
3622 
3623 	debug(LOG_NET, "resetting sockets.");
3624 	NETclose();	// just to be sure :)
3625 
3626 	debug(LOG_NET, "Trying to join [%s]:%d ...", host, port);
3627 
3628 	netPlayersUpdated = true;
3629 
3630 	hosts = resolveHost(host, port);
3631 	if (hosts == nullptr)
3632 	{
3633 		debug(LOG_ERROR, "Cannot resolve hostname \"%s\": %s", host, strSockError(getSockErr()));
3634 		return false;
3635 	}
3636 
3637 	if (tcp_socket != nullptr)
3638 	{
3639 		socketClose(tcp_socket);
3640 	}
3641 
3642 	tcp_socket = socketOpenAny(hosts, 15000);
3643 	deleteSocketAddress(hosts);
3644 
3645 	if (tcp_socket == nullptr)
3646 	{
3647 		debug(LOG_ERROR, "Cannot connect to [%s]:%d, %s", host, port, strSockError(getSockErr()));
3648 		return false;
3649 	}
3650 
3651 	// client machines only need 1 socket set
3652 	socket_set = allocSocketSet();
3653 	if (socket_set == nullptr)
3654 	{
3655 		debug(LOG_ERROR, "Cannot create socket set: %s", strSockError(getSockErr()));
3656 		return false;
3657 	}
3658 	debug(LOG_NET, "Created socket_set %p", static_cast<void *>(socket_set));
3659 
3660 	// tcp_socket is used to talk to host machine
3661 	SocketSet_AddSocket(socket_set, tcp_socket);
3662 
3663 	// Send NETCODE_VERSION_MAJOR and NETCODE_VERSION_MINOR
3664 	p_buffer = buffer;
3665 	auto pushu32 = [&](uint32_t value) {
3666 		uint32_t swapped = htonl(value);
3667 		memcpy(p_buffer, &swapped, sizeof(swapped));
3668 		p_buffer += sizeof(swapped);
3669 	};
3670 	pushu32(NETCODE_VERSION_MAJOR);
3671 	pushu32(NETCODE_VERSION_MINOR);
3672 
3673 	if (writeAll(tcp_socket, buffer, sizeof(buffer)) == SOCKET_ERROR
3674 	    || readAll(tcp_socket, &result, sizeof(result), 1500) != sizeof(result))
3675 	{
3676 		debug(LOG_ERROR, "Couldn't send my version.");
3677 		return false;
3678 	}
3679 
3680 	result = ntohl(result);
3681 	if (result != ERROR_NOERROR)
3682 	{
3683 		debug(LOG_ERROR, "Received error %d", result);
3684 
3685 		SocketSet_DelSocket(socket_set, tcp_socket);
3686 		socketClose(tcp_socket);
3687 		tcp_socket = nullptr;
3688 		deleteSocketSet(socket_set);
3689 		socket_set = nullptr;
3690 
3691 		setLobbyError((LOBBY_ERROR_TYPES)result);
3692 		return false;
3693 	}
3694 
3695 	// Allocate memory for a new socket
3696 	NETinitQueue(NETnetQueue(NET_HOST_ONLY));
3697 	// NOTE: tcp_socket = bsocket now!
3698 	bsocket = tcp_socket;
3699 	tcp_socket = nullptr;
3700 	socketBeginCompression(bsocket);
3701 
3702 	// Send a join message to the host
3703 	NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_JOIN);
3704 	NETstring(playername, 64);
3705 	NETstring(getModList().c_str(), modlist_string_size);
3706 	NETstring(NetPlay.gamePassword, sizeof(NetPlay.gamePassword));
3707 	NETend();
3708 	if (bsocket == nullptr)
3709 	{
3710 		return false;  // Connection dropped while sending NET_JOIN.
3711 	}
3712 	socketFlush(bsocket);  // Make sure the message was completely sent.
3713 
3714 	i = wzGetTicks();
3715 	// Loop until we've been accepted into the game
3716 	for (;;)
3717 	{
3718 		NETQUEUE queue;
3719 		uint8_t type;
3720 
3721 		// FIXME: shouldn't there be some sort of rejection message?
3722 		if ((unsigned)wzGetTicks() > i + 5000)
3723 		{
3724 			// timeout
3725 			return false;
3726 		}
3727 
3728 		if (bsocket == nullptr)
3729 		{
3730 			return false;  // Connection dropped.
3731 		}
3732 
3733 		if (!NETrecvNet(&queue, &type))
3734 		{
3735 			continue;
3736 		}
3737 
3738 		if (type == NET_ACCEPTED)
3739 		{
3740 			// :)
3741 			uint8_t index;
3742 
3743 			NETbeginDecode(queue, NET_ACCEPTED);
3744 			// Retrieve the player ID the game host arranged for us
3745 			NETuint8_t(&index);
3746 			NETend();
3747 			NETpop(queue);
3748 
3749 			selectedPlayer = index;
3750 			realSelectedPlayer = selectedPlayer;
3751 			debug(LOG_NET, "NET_ACCEPTED received. Accepted into the game - I'm player %u using bsocket %p", (unsigned int)index, static_cast<void *>(bsocket));
3752 			NetPlay.isHost = false;
3753 			NetPlay.isHostAlive = true;
3754 
3755 			if (index >= MAX_CONNECTED_PLAYERS)
3756 			{
3757 				debug(LOG_ERROR, "Bad player number (%u) received from host!", index);
3758 				return false;
3759 			}
3760 
3761 			NetPlay.players[index].allocated = true;
3762 			sstrcpy(NetPlay.players[index].name, playername);
3763 			NetPlay.players[index].heartbeat = true;
3764 
3765 			return true;
3766 		}
3767 		else if (type == NET_REJECTED)
3768 		{
3769 			uint8_t rejection = 0;
3770 
3771 			NETbeginDecode(queue, NET_REJECTED);
3772 			NETuint8_t(&rejection);
3773 			NETend();
3774 			NETpop(queue);
3775 
3776 			debug(LOG_NET, "NET_REJECTED received. Error code: %u", (unsigned int) rejection);
3777 
3778 			setLobbyError((LOBBY_ERROR_TYPES)rejection);
3779 			NETclose();
3780 			return false;
3781 		}
3782 		else
3783 		{
3784 			debug(LOG_ERROR, "Unexpected %s.", messageTypeToString(type));
3785 			NETpop(queue);
3786 		}
3787 	}
3788 }
3789 
3790 /*!
3791  * Set the masterserver name
3792  * \param hostname The hostname of the masterserver to connect to
3793  */
NETsetMasterserverName(const char * hostname)3794 void NETsetMasterserverName(const char *hostname)
3795 {
3796 	sstrcpy(masterserver_name, hostname);
3797 }
3798 
3799 /**
3800  * @return The hostname of the masterserver we will connect to.
3801  */
NETgetMasterserverName()3802 const char *NETgetMasterserverName()
3803 {
3804 	return masterserver_name;
3805 }
3806 
3807 /*!
3808  * Set the masterserver port
3809  * \param port The port of the masterserver to connect to
3810  */
NETsetMasterserverPort(unsigned int port)3811 void NETsetMasterserverPort(unsigned int port)
3812 {
3813 	const unsigned int MAX_PORT = 65535;
3814 	if (port > MAX_PORT || port == 0)
3815 	{
3816 		debug(LOG_ERROR, "Invalid port number: %u", port);
3817 		return;
3818 	}
3819 	masterserver_port = port;
3820 }
3821 
3822 /**
3823  * @return The port of the masterserver we will connect to.
3824  */
NETgetMasterserverPort()3825 unsigned int NETgetMasterserverPort()
3826 {
3827 	return masterserver_port;
3828 }
3829 
3830 /*!
3831  * Set the port we shall host games on
3832  * \param port The port to listen to
3833  */
NETsetGameserverPort(unsigned int port)3834 void NETsetGameserverPort(unsigned int port)
3835 {
3836 	gameserver_port = port;
3837 }
3838 
3839 /**
3840  * @return The port we will host games on.
3841  */
NETgetGameserverPort()3842 unsigned int NETgetGameserverPort()
3843 {
3844 	return gameserver_port;
3845 }
3846 
3847 /*!
3848 * Set the join preference for IPv6
3849 * \param bTryIPv6First Whether to attempt IPv6 first when joining, before IPv4.
3850 */
NETsetJoinPreferenceIPv6(bool bTryIPv6First)3851 void NETsetJoinPreferenceIPv6(bool bTryIPv6First)
3852 {
3853 	bJoinPrefTryIPv6First = bTryIPv6First;
3854 }
3855 
3856 /**
3857 * @return Whether joining a game that advertises both IPv6 and IPv4 should attempt IPv6 first.
3858 */
NETgetJoinPreferenceIPv6()3859 bool NETgetJoinPreferenceIPv6()
3860 {
3861 	return bJoinPrefTryIPv6First;
3862 }
3863 
3864 
NETsetPlayerConnectionStatus(CONNECTION_STATUS status,unsigned player)3865 void NETsetPlayerConnectionStatus(CONNECTION_STATUS status, unsigned player)
3866 {
3867 	unsigned n;
3868 	const int timeouts[] = {GAME_TICKS_PER_SEC * 10, GAME_TICKS_PER_SEC * 10, GAME_TICKS_PER_SEC, GAME_TICKS_PER_SEC / 6};
3869 	ASSERT(ARRAY_SIZE(timeouts) == CONNECTIONSTATUS_NORMAL, "Connection status timeout array too small.");
3870 
3871 	if (player == NET_ALL_PLAYERS)
3872 	{
3873 		for (n = 0; n < MAX_PLAYERS; ++n)
3874 		{
3875 			NETsetPlayerConnectionStatus(status, n);
3876 		}
3877 		return;
3878 	}
3879 	if (status == CONNECTIONSTATUS_NORMAL)
3880 	{
3881 		for (n = 0; n < CONNECTIONSTATUS_NORMAL; ++n)
3882 		{
3883 			NET_PlayerConnectionStatus[n][player] = 0;
3884 		}
3885 		return;
3886 	}
3887 
3888 	NET_PlayerConnectionStatus[status][player] = realTime + timeouts[status];
3889 }
3890 
NETcheckPlayerConnectionStatus(CONNECTION_STATUS status,unsigned player)3891 bool NETcheckPlayerConnectionStatus(CONNECTION_STATUS status, unsigned player)
3892 {
3893 	unsigned n;
3894 
3895 	if (player == NET_ALL_PLAYERS)
3896 	{
3897 		for (n = 0; n < MAX_PLAYERS; ++n)
3898 		{
3899 			if (NETcheckPlayerConnectionStatus(status, n))
3900 			{
3901 				return true;
3902 			}
3903 		}
3904 		return false;
3905 	}
3906 	if (status == CONNECTIONSTATUS_NORMAL)
3907 	{
3908 		for (n = 0; n < CONNECTIONSTATUS_NORMAL; ++n)
3909 		{
3910 			if (NETcheckPlayerConnectionStatus((CONNECTION_STATUS)n, player))
3911 			{
3912 				return true;
3913 			}
3914 		}
3915 		return false;
3916 	}
3917 
3918 	return realTime < NET_PlayerConnectionStatus[status][player];
3919 }
3920 
3921 struct SyncDebugEntry
3922 {
3923 	char const *function;
3924 };
3925 
3926 struct SyncDebugString : public SyncDebugEntry
3927 {
setSyncDebugString3928 	void set(uint32_t &crc, char const *f, char const *string)
3929 	{
3930 		function = f;
3931 		crc = crcSum(crc, function, strlen(function) + 1);
3932 		crc = crcSum(crc, string,   strlen(string) + 1);
3933 	}
snprintSyncDebugString3934 	int snprint(char *buf, size_t bufSize, char const *&string) const
3935 	{
3936 		int ret = snprintf(buf, bufSize, "[%s] %s\n", function, string);
3937 		string += strlen(string) + 1;
3938 		return ret;
3939 	}
3940 };
3941 
3942 struct SyncDebugValueChange : public SyncDebugEntry
3943 {
setSyncDebugValueChange3944 	void set(uint32_t &crc, char const *f, char const *vn, int nv, int i)
3945 	{
3946 		function = f;
3947 		variableName = vn;
3948 		newValue = nv;
3949 		id = i;
3950 		uint32_t valueBytes = htonl(newValue);
3951 		crc = crcSum(crc, function,     strlen(function) + 1);
3952 		crc = crcSum(crc, variableName, strlen(variableName) + 1);
3953 		crc = crcSum(crc, &valueBytes,  4);
3954 	}
snprintSyncDebugValueChange3955 	int snprint(char *buf, size_t bufSize) const
3956 	{
3957 		if (id != -1)
3958 		{
3959 			return snprintf(buf, bufSize, "[%s] %d %s = %d\n", function, id, variableName, newValue);
3960 		}
3961 		return snprintf(buf, bufSize, "[%s] %s = %d\n", function, variableName, newValue);
3962 	}
3963 
3964 	int         newValue;
3965 	int         id;
3966 	char const *variableName;
3967 };
3968 
3969 struct SyncDebugIntList : public SyncDebugEntry
3970 {
setSyncDebugIntList3971 	void set(uint32_t &crc, char const *f, char const *s, int const *ints, size_t num)
3972 	{
3973 		function = f;
3974 		string = s;
3975 		uint32_t valueBytes[40];
3976 		numInts = std::min(num, ARRAY_SIZE(valueBytes));
3977 		for (unsigned n = 0; n < numInts; ++n)
3978 		{
3979 			valueBytes[n] = htonl(ints[n]);
3980 		}
3981 		crc = crcSum(crc, valueBytes, 4 * numInts);
3982 	}
snprintSyncDebugIntList3983 	int snprint(char *buf, size_t bufSize, int const *&ints) const
3984 	{
3985 		size_t index = 0;
3986 		if (index < bufSize)
3987 		{
3988 			index += snprintf(buf + index, bufSize - index, "[%s] ", function);
3989 		}
3990 		if (index < bufSize)
3991 		{
3992 			switch (numInts)
3993 			{
3994 			case  0: index += snprintf(buf + index, bufSize - index, "%s", string); break;
3995 			case  1: index += snprintf(buf + index, bufSize - index, string, ints[0]); break;
3996 			case  2: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1]); break;
3997 			case  3: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2]); break;
3998 			case  4: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3]); break;
3999 			case  5: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4]); break;
4000 			case  6: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5]); break;
4001 			case  7: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6]); break;
4002 			case  8: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]); break;
4003 			case  9: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8]); break;
4004 			case 10: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9]); break;
4005 			case 11: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10]); break;
4006 			case 12: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11]); break;
4007 			case 13: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12]); break;
4008 			case 14: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13]); break;
4009 			case 15: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14]); break;
4010 			case 16: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15]); break;
4011 			case 17: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16]); break;
4012 			case 18: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17]); break;
4013 			case 19: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18]); break;
4014 			case 20: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19]); break;
4015 			case 21: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20]); break;
4016 			case 22: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21]); break;
4017 			case 23: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22]); break;
4018 			case 24: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23]); break;
4019 			case 25: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24]); break;
4020 			case 26: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25]); break;
4021 			case 27: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26]); break;
4022 			case 28: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27]); break;
4023 			case 29: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28]); break;
4024 			case 30: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29]); break;
4025 			case 31: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30]); break;
4026 			case 32: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30], ints[31]); break;
4027 			case 33: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30], ints[31], ints[32]); break;
4028 			case 34: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30], ints[31], ints[32], ints[33]); break;
4029 			case 35: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30], ints[31], ints[32], ints[33], ints[34]); break;
4030 			case 36: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30], ints[31], ints[32], ints[33], ints[34], ints[35]); break;
4031 			case 37: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30], ints[31], ints[32], ints[33], ints[34], ints[35], ints[36]); break;
4032 			case 38: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30], ints[31], ints[32], ints[33], ints[34], ints[35], ints[36], ints[37]); break;
4033 			case 39: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30], ints[31], ints[32], ints[33], ints[34], ints[35], ints[36], ints[37], ints[38]); break;
4034 			case 40: index += snprintf(buf + index, bufSize - index, string, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], ints[8], ints[9], ints[10], ints[11], ints[12], ints[13], ints[14], ints[15], ints[16], ints[17], ints[18], ints[19], ints[20], ints[21], ints[22], ints[23], ints[24], ints[25], ints[26], ints[27], ints[28], ints[29], ints[30], ints[31], ints[32], ints[33], ints[34], ints[35], ints[36], ints[37], ints[38], ints[39]); break;
4035 			default: index += snprintf(buf + index, bufSize - index, "Too many ints in intlist."); break;
4036 			}
4037 		}
4038 		if (index < bufSize)
4039 		{
4040 			index += snprintf(buf + index, bufSize - index, "\n");
4041 		}
4042 		ints += numInts;
4043 		return index;
4044 	}
4045 
4046 	char const *string;
4047 	unsigned numInts;
4048 };
4049 
4050 struct SyncDebugLog
4051 {
SyncDebugLogSyncDebugLog4052 	SyncDebugLog() : time(0), crc(0x00000000) {}
clearSyncDebugLog4053 	void clear()
4054 	{
4055 		log.clear();
4056 		time = 0;
4057 		crc = 0x00000000;
4058 		//printf("Freeing %d strings, %d valueChanges, %d intLists, %d chars, %d ints\n", (int)strings.size(), (int)valueChanges.size(), (int)intLists.size(), (int)chars.size(), (int)ints.size());
4059 		strings.clear();
4060 		valueChanges.clear();
4061 		intLists.clear();
4062 		chars.clear();
4063 		ints.clear();
4064 	}
stringSyncDebugLog4065 	void string(char const *f, char const *s)
4066 	{
4067 		size_t offset = chars.size();
4068 		chars.resize(chars.size() + strlen(s) + 1);
4069 		char *buf = &chars[offset];
4070 		strcpy(buf, s);
4071 
4072 		strings.resize(strings.size() + 1);
4073 		strings.back().set(crc, f, buf);
4074 
4075 		log.push_back('s');
4076 	}
valueChangeSyncDebugLog4077 	void valueChange(char const *f, char const *vn, int nv, int i)
4078 	{
4079 		valueChanges.resize(valueChanges.size() + 1);
4080 		valueChanges.back().set(crc, f, vn, nv, i);
4081 		log.push_back('v');
4082 	}
intListSyncDebugLog4083 	void intList(char const *f, char const *s, int *begin, size_t num)
4084 	{
4085 		size_t offset = ints.size();
4086 		ints.resize(ints.size() + num);
4087 		int *buf = &ints[offset];
4088 		std::copy(begin, begin + num, buf);
4089 
4090 		intLists.resize(intLists.size() + 1);
4091 		intLists.back().set(crc, f, s, buf, num);
4092 		log.push_back('i');
4093 	}
snprintSyncDebugLog4094 	int snprint(char *buf, size_t bufSize)
4095 	{
4096 		SyncDebugString const *stringPtr = strings.empty() ? nullptr : &strings[0]; // .empty() check, since &strings[0] is undefined if strings is empty(), even if it's likely to work, anyway.
4097 		SyncDebugValueChange const *valueChangePtr = valueChanges.empty() ? nullptr : &valueChanges[0];
4098 		SyncDebugIntList const *intListPtr = intLists.empty() ? nullptr : &intLists[0];
4099 		char const *charPtr = chars.empty() ? nullptr : &chars[0];
4100 		int const *intPtr = ints.empty() ? nullptr : &ints[0];
4101 
4102 		int index = 0;
4103 		for (size_t n = 0; n < log.size() && (size_t)index < bufSize; ++n)
4104 		{
4105 			char type = log[n];
4106 			switch (type)
4107 			{
4108 			case 's':
4109 				index += stringPtr++->snprint(buf + index, bufSize - index, charPtr);
4110 				break;
4111 			case 'v':
4112 				index += valueChangePtr++->snprint(buf + index, bufSize - index);
4113 				break;
4114 			case 'i':
4115 				index += intListPtr++->snprint(buf + index, bufSize - index, intPtr);
4116 				break;
4117 			default:
4118 				abort();
4119 				break;
4120 			}
4121 		}
4122 		return index;
4123 	}
getGameTimeSyncDebugLog4124 	uint32_t getGameTime() const
4125 	{
4126 		return time;
4127 	}
getCrcSyncDebugLog4128 	uint32_t getCrc() const
4129 	{
4130 		return ~crc;  // Invert bits, since everyone else seems to do that with CRCs...
4131 	}
getNumEntriesSyncDebugLog4132 	size_t getNumEntries() const
4133 	{
4134 		return log.size();
4135 	}
setGameTimeSyncDebugLog4136 	void setGameTime(uint32_t newTime)
4137 	{
4138 		time = newTime;
4139 	}
setCrcSyncDebugLog4140 	void setCrc(uint32_t newCrc)
4141 	{
4142 		crc = ~newCrc;  // Invert bits, since everyone else seems to do that with CRCs...
4143 	}
4144 
4145 private:
4146 	std::vector<char> log;
4147 	uint32_t time;
4148 	uint32_t crc;
4149 
4150 	std::vector<SyncDebugString> strings;
4151 	std::vector<SyncDebugValueChange> valueChanges;
4152 	std::vector<SyncDebugIntList> intLists;
4153 
4154 	std::vector<char> chars;
4155 	std::vector<int> ints;
4156 
4157 private:
4158 	SyncDebugLog(SyncDebugLog const &)/* = delete*/;
4159 	SyncDebugLog &operator =(SyncDebugLog const &)/* = delete*/;
4160 };
4161 
4162 #define MAX_LEN_LOG_LINE 512  // From debug.c - no use printing something longer.
4163 #define MAX_SYNC_HISTORY 12
4164 
4165 static unsigned syncDebugNext = 0;
4166 static SyncDebugLog syncDebugLog[MAX_SYNC_HISTORY];
4167 static uint32_t syncDebugExtraGameTime;
4168 static uint32_t syncDebugExtraCrc;
4169 
4170 static uint32_t syncDebugNumDumps = 0;
4171 
_syncDebug(const char * function,const char * str,...)4172 void _syncDebug(const char *function, const char *str, ...)
4173 {
4174 #ifdef WZ_CC_MSVC
4175 	char const *f = function; while (*f != '\0') if (*f++ == ':')
4176 		{
4177 			function = f;    // Strip "Class::" from "Class::myFunction".
4178 		}
4179 #endif
4180 
4181 	va_list ap;
4182 	char outputBuffer[MAX_LEN_LOG_LINE];
4183 
4184 	va_start(ap, str);
4185 	vssprintf(outputBuffer, str, ap);
4186 	va_end(ap);
4187 
4188 	syncDebugLog[syncDebugNext].string(function, outputBuffer);
4189 }
4190 
_syncDebugIntList(const char * function,const char * str,int * ints,size_t numInts)4191 void _syncDebugIntList(const char *function, const char *str, int *ints, size_t numInts)
4192 {
4193 #ifdef WZ_CC_MSVC
4194 	char const *f = function; while (*f != '\0') if (*f++ == ':')
4195 		{
4196 			function = f;    // Strip "Class::" from "Class::myFunction".
4197 		}
4198 #endif
4199 
4200 	syncDebugLog[syncDebugNext].intList(function, str, ints, numInts);
4201 }
4202 
_syncDebugBacktrace(const char * function)4203 void _syncDebugBacktrace(const char *function)
4204 {
4205 #ifdef WZ_CC_MSVC
4206 	char const *f = function; while (*f != '\0') if (*f++ == ':')
4207 		{
4208 			function = f;    // Strip "Class::" from "Class::myFunction".
4209 		}
4210 #endif
4211 
4212 	uint32_t backupCrc = syncDebugLog[syncDebugNext].getCrc();  // Ignore CRC changes from _syncDebug(), since identical backtraces can be printed differently.
4213 
4214 #if defined(WZ_OS_LINUX) && defined(__GLIBC__)
4215 	void *btv[20];
4216 	unsigned num = backtrace(btv, sizeof(btv) / sizeof(*btv));
4217 	char **btc = backtrace_symbols(btv, num);
4218 	unsigned i;
4219 	for (i = 1; i + 2 < num; ++i)  // =1: Don't print "src/warzone2100(syncDebugBacktrace+0x16) [0x6312d1]". +2: Don't print last two lines of backtrace such as "/lib/libc.so.6(__libc_start_main+0xe6) [0x7f91e040ea26]", since the address varies (even with the same binary).
4220 	{
4221 		_syncDebug("BT", "%s", btc[i]);
4222 	}
4223 	free(btc);
4224 #else
4225 	_syncDebug("BT", "Sorry, syncDebugBacktrace() not implemented on your system. Called from %s.", function);
4226 #endif
4227 
4228 	// Use CRC of something platform-independent, to avoid false positive desynchs.
4229 	backupCrc = ~crcSum(~backupCrc, function, strlen(function) + 1);
4230 	syncDebugLog[syncDebugNext].setCrc(backupCrc);
4231 }
4232 
syncDebugGetCrc()4233 uint32_t syncDebugGetCrc()
4234 {
4235 	return syncDebugLog[syncDebugNext].getCrc();
4236 }
4237 
syncDebugSetCrc(uint32_t crc)4238 void syncDebugSetCrc(uint32_t crc)
4239 {
4240 	syncDebugLog[syncDebugNext].setCrc(crc);
4241 }
4242 
resetSyncDebug()4243 void resetSyncDebug()
4244 {
4245 	for (unsigned i = 0; i < MAX_SYNC_HISTORY; ++i)
4246 	{
4247 		syncDebugLog[i].clear();
4248 	}
4249 
4250 	syncDebugExtraGameTime = 0;
4251 	syncDebugExtraCrc = 0xFFFFFFFF;
4252 
4253 	syncDebugNext = 0;
4254 
4255 	syncDebugNumDumps = 0;
4256 }
4257 
nextDebugSync()4258 GameCrcType nextDebugSync()
4259 {
4260 	uint32_t ret = syncDebugLog[syncDebugNext].getCrc();
4261 
4262 	// Save gameTime, so we know which CRC to compare with, later.
4263 	syncDebugLog[syncDebugNext].setGameTime(gameTime);
4264 
4265 	// Go to next position, and free it ready for use.
4266 	syncDebugNext = (syncDebugNext + 1) % MAX_SYNC_HISTORY;
4267 	syncDebugLog[syncDebugNext].clear();
4268 
4269 	return (GameCrcType)ret;
4270 }
4271 
dumpDebugSync(uint8_t * buf,size_t bufLen,uint32_t time,unsigned player)4272 static void dumpDebugSync(uint8_t *buf, size_t bufLen, uint32_t time, unsigned player)
4273 {
4274 	char fname[100];
4275 	PHYSFS_file *fp;
4276 
4277 	ssprintf(fname, "logs/desync%u_p%u.txt", time, player);
4278 	fp = openSaveFile(fname);
4279 	ASSERT(bufLen <= static_cast<size_t>(std::numeric_limits<PHYSFS_uint32>::max()), "bufLen (%zu) exceeds PHYSFS_uint32::max", bufLen);
4280 	WZ_PHYSFS_writeBytes(fp, buf, static_cast<PHYSFS_uint32>(bufLen));
4281 	PHYSFS_close(fp);
4282 
4283 	debug(LOG_ERROR, "Dumped player %u's sync error at gameTime %u to file: %s%s", player, time, WZ_PHYSFS_getRealDir_String(fname).c_str(), fname);
4284 }
4285 
sendDebugSync(uint8_t * buf,uint32_t bufLen,uint32_t time)4286 static void sendDebugSync(uint8_t *buf, uint32_t bufLen, uint32_t time)
4287 {
4288 	// Save our own, before sending, so that if we have 2 clients running on the same computer, to guarantee that it is done saving before the other client saves on top.
4289 	dumpDebugSync(buf, bufLen, time, selectedPlayer);
4290 
4291 	NETbeginEncode(NETbroadcastQueue(), NET_DEBUG_SYNC);
4292 	NETuint32_t(&time);
4293 	NETuint32_t(&bufLen);
4294 	NETbin(buf, bufLen);
4295 	NETend();
4296 }
4297 
4298 static uint8_t debugSyncTmpBuf[2000000];
recvDebugSync(NETQUEUE queue)4299 static void recvDebugSync(NETQUEUE queue)
4300 {
4301 	uint32_t time = 0;
4302 	uint32_t bufLen = 0;
4303 
4304 	NETbeginDecode(queue, NET_DEBUG_SYNC);
4305 	NETuint32_t(&time);
4306 	NETuint32_t(&bufLen);
4307 	bufLen = MIN(bufLen, ARRAY_SIZE(debugSyncTmpBuf));
4308 	NETbin(debugSyncTmpBuf, bufLen);
4309 	NETend();
4310 
4311 	dumpDebugSync(debugSyncTmpBuf, bufLen, time, queue.index);
4312 }
4313 
checkDebugSync(uint32_t checkGameTime,GameCrcType checkCrc)4314 bool checkDebugSync(uint32_t checkGameTime, GameCrcType checkCrc)
4315 {
4316 	if (checkGameTime == syncDebugLog[syncDebugNext].getGameTime())  // Can't happen - and syncDebugGameTime[] == 0, until just before sending the CRC, anyway.
4317 	{
4318 		debug(LOG_ERROR, "Huh? We aren't done yet...");
4319 		return true;
4320 	}
4321 
4322 	unsigned logIndex;
4323 	for (logIndex = 0; logIndex < MAX_SYNC_HISTORY; ++logIndex)
4324 	{
4325 		if (syncDebugLog[logIndex].getGameTime() == checkGameTime)
4326 		{
4327 			if ((GameCrcType)syncDebugLog[logIndex].getCrc() == checkCrc)
4328 			{
4329 				return true;                    // Check passed. (So far... There might still be more players to compare CRCs with.)
4330 			}
4331 
4332 			break;                                  // Check failed!
4333 		}
4334 	}
4335 
4336 	if (logIndex >= MAX_SYNC_HISTORY && syncDebugExtraGameTime == checkGameTime)
4337 	{
4338 		if ((GameCrcType)syncDebugExtraCrc == checkCrc)
4339 		{
4340 			return true;
4341 		}
4342 	}
4343 
4344 	if (logIndex >= MAX_SYNC_HISTORY)
4345 	{
4346 		return false;                                   // Couldn't check. May have dumped already, or MAX_SYNC_HISTORY isn't big enough compared to the maximum latency.
4347 	}
4348 
4349 	size_t bufIndex = 0;
4350 	// Dump our version, and also erase it, so we only dump it at most once.
4351 	debug(LOG_ERROR, "Inconsistent sync debug at gameTime %u. My version has %zu entries, CRC = 0x%08X.", syncDebugLog[logIndex].getGameTime(), syncDebugLog[logIndex].getNumEntries(), syncDebugLog[logIndex].getCrc());
4352 	bufIndex += snprintf((char *)debugSyncTmpBuf + bufIndex, ARRAY_SIZE(debugSyncTmpBuf) - bufIndex, "===== BEGIN gameTime=%u, %zu entries, CRC 0x%08X =====\n", syncDebugLog[logIndex].getGameTime(), syncDebugLog[logIndex].getNumEntries(), syncDebugLog[logIndex].getCrc());
4353 	bufIndex = MIN(bufIndex, ARRAY_SIZE(debugSyncTmpBuf));  // snprintf will not overflow debugSyncTmpBuf, but returns as much as it would have printed if possible.
4354 	bufIndex += syncDebugLog[logIndex].snprint((char *)debugSyncTmpBuf + bufIndex, ARRAY_SIZE(debugSyncTmpBuf) - bufIndex);
4355 	bufIndex = MIN(bufIndex, ARRAY_SIZE(debugSyncTmpBuf));  // snprintf will not overflow debugSyncTmpBuf, but returns as much as it would have printed if possible.
4356 	bufIndex += snprintf((char *)debugSyncTmpBuf + bufIndex, ARRAY_SIZE(debugSyncTmpBuf) - bufIndex, "===== END gameTime=%u, %zu entries, CRC 0x%08X =====\n", syncDebugLog[logIndex].getGameTime(), syncDebugLog[logIndex].getNumEntries(), syncDebugLog[logIndex].getCrc());
4357 	bufIndex = MIN(bufIndex, ARRAY_SIZE(debugSyncTmpBuf));  // snprintf will not overflow debugSyncTmpBuf, but returns as much as it would have printed if possible.
4358 	if (syncDebugNumDumps < 2)
4359 	{
4360 		++syncDebugNumDumps;
4361 		sendDebugSync(debugSyncTmpBuf, static_cast<uint32_t>(bufIndex), syncDebugLog[logIndex].getGameTime());
4362 	}
4363 
4364 	// Backup correct CRC for checking against remaining players, even though we erased the logs (which were dumped already).
4365 	syncDebugExtraGameTime = syncDebugLog[logIndex].getGameTime();
4366 	syncDebugExtraCrc      = syncDebugLog[logIndex].getCrc();
4367 
4368 	// Finish erasing our version.
4369 	syncDebugLog[logIndex].clear();
4370 
4371 	return false;  // Ouch.
4372 }
4373 
messageTypeToString(unsigned messageType_)4374 const char *messageTypeToString(unsigned messageType_)
4375 {
4376 	MESSAGE_TYPES messageType = (MESSAGE_TYPES)messageType_;  // Cast to enum, so switch gives a warning if new message types are added without updating the switch.
4377 
4378 	switch (messageType)
4379 	{
4380 	// Search:  ^\s*([\w_]+).*
4381 	// Replace: case \1:                             return "\1";
4382 	// Search:  (case ...............................) *(return "[\w_]+";)
4383 	// Replace: \t\t\1\2
4384 
4385 	// Net-related messages.
4386 	case NET_MIN_TYPE:                  return "NET_MIN_TYPE";
4387 	case NET_PING:                      return "NET_PING";
4388 	case NET_PLAYER_STATS:              return "NET_PLAYER_STATS";
4389 	case NET_TEXTMSG:                   return "NET_TEXTMSG";
4390 	case NET_PLAYERRESPONDING:          return "NET_PLAYERRESPONDING";
4391 	case NET_OPTIONS:                   return "NET_OPTIONS";
4392 	case NET_KICK:                      return "NET_KICK";
4393 	case NET_FIREUP:                    return "NET_FIREUP";
4394 	case NET_COLOURREQUEST:             return "NET_COLOURREQUEST";
4395 	case NET_FACTIONREQUEST:             return "NET_FACTIONREQUEST";
4396 	case NET_AITEXTMSG:                 return "NET_AITEXTMSG";
4397 	case NET_BEACONMSG:                 return "NET_BEACONMSG";
4398 	case NET_TEAMREQUEST:               return "NET_TEAMREQUEST";
4399 	case NET_JOIN:                      return "NET_JOIN";
4400 	case NET_ACCEPTED:                  return "NET_ACCEPTED";
4401 	case NET_PLAYER_INFO:               return "NET_PLAYER_INFO";
4402 	case NET_PLAYER_JOINED:             return "NET_PLAYER_JOINED";
4403 	case NET_PLAYER_LEAVING:            return "NET_PLAYER_LEAVING";
4404 	case NET_PLAYER_DROPPED:            return "NET_PLAYER_DROPPED";
4405 	case NET_GAME_FLAGS:                return "NET_GAME_FLAGS";
4406 	case NET_READY_REQUEST:             return "NET_READY_REQUEST";
4407 	case NET_REJECTED:                  return "NET_REJECTED";
4408 	case NET_POSITIONREQUEST:           return "NET_POSITIONREQUEST";
4409 	case NET_DATA_CHECK:                return "NET_DATA_CHECK";
4410 	case NET_HOST_DROPPED:              return "NET_HOST_DROPPED";
4411 	case NET_SEND_TO_PLAYER:            return "NET_SEND_TO_PLAYER";
4412 	case NET_SHARE_GAME_QUEUE:          return "NET_SHARE_GAME_QUEUE";
4413 	case NET_FILE_REQUESTED:            return "NET_FILE_REQUESTED";
4414 	case NET_FILE_CANCELLED:            return "NET_FILE_CANCELLED";
4415 	case NET_FILE_PAYLOAD:              return "NET_FILE_PAYLOAD";
4416 	case NET_DEBUG_SYNC:                return "NET_DEBUG_SYNC";
4417 	case NET_VOTE:                      return "NET_VOTE";
4418 	case NET_VOTE_REQUEST:              return "NET_VOTE_REQUEST";
4419 	case NET_MAX_TYPE:                  return "NET_MAX_TYPE";
4420 
4421 	// Game-state-related messages, must be processed by all clients at the same game time.
4422 	case GAME_MIN_TYPE:                 return "GAME_MIN_TYPE";
4423 	case GAME_DROIDINFO:                return "GAME_DROIDINFO";
4424 	case GAME_STRUCTUREINFO:            return "GAME_STRUCTUREINFO";
4425 	case GAME_RESEARCHSTATUS:           return "GAME_RESEARCHSTATUS";
4426 	case GAME_TEMPLATE:                 return "GAME_TEMPLATE";
4427 	case GAME_TEMPLATEDEST:             return "GAME_TEMPLATEDEST";
4428 	case GAME_ALLIANCE:                 return "GAME_ALLIANCE";
4429 	case GAME_GIFT:                     return "GAME_GIFT";
4430 	case GAME_LASSAT:                   return "GAME_LASSAT";
4431 	case GAME_GAME_TIME:                return "GAME_GAME_TIME";
4432 	case GAME_PLAYER_LEFT:              return "GAME_PLAYER_LEFT";
4433 	case GAME_DROIDDISEMBARK:           return "GAME_DROIDDISEMBARK";
4434 	case GAME_SYNC_REQUEST:             return "GAME_SYNC_REQUEST";
4435 
4436 	// The following messages are used for debug mode.
4437 	case GAME_DEBUG_MODE:               return "GAME_DEBUG_MODE";
4438 	case GAME_DEBUG_ADD_DROID:          return "GAME_DEBUG_ADD_DROID";
4439 	case GAME_DEBUG_ADD_STRUCTURE:      return "GAME_DEBUG_ADD_STRUCTURE";
4440 	case GAME_DEBUG_ADD_FEATURE:        return "GAME_DEBUG_ADD_FEATURE";
4441 	case GAME_DEBUG_REMOVE_DROID:       return "GAME_DEBUG_REMOVE_DROID";
4442 	case GAME_DEBUG_REMOVE_STRUCTURE:   return "GAME_DEBUG_REMOVE_STRUCTURE";
4443 	case GAME_DEBUG_REMOVE_FEATURE:     return "GAME_DEBUG_REMOVE_FEATURE";
4444 	case GAME_DEBUG_FINISH_RESEARCH:    return "GAME_DEBUG_FINISH_RESEARCH";
4445 	// End of redundant messages.
4446 	case GAME_MAX_TYPE:                 return "GAME_MAX_TYPE";
4447 	}
4448 	return "(UNUSED)";
4449 }
4450 
isLoopbackIP(const char * ip)4451 static bool isLoopbackIP(const char *ip)
4452 {
4453 	if (!ip) return false;
4454 	if (strncmp(ip, "127.", 4) == 0)
4455 	{
4456 		return true;
4457 	}
4458 	if (strcmp(ip, "::1") == 0)
4459 	{
4460 		return true;
4461 	}
4462 	if (strcmp(ip, "0000:0000:0000:0000:0000:0000:0000:0001") == 0)
4463 	{
4464 		return true;
4465 	}
4466 	return false;
4467 }
4468 
4469 /**
4470  * Check if ip is on the banned list.
4471  * \param ip IP address converted to text
4472  */
onBanList(const char * ip)4473 static bool onBanList(const char *ip)
4474 {
4475 	int i;
4476 
4477 	if (!IPlist)
4478 	{
4479 		return false;    //if no bans are added, then don't check.
4480 	}
4481 	if (isLoopbackIP(ip))
4482 	{
4483 		return false;	// ignore loopback IPs
4484 	}
4485 	for (i = 0; i < MAX_BANS ; i++)
4486 	{
4487 		if (RE2::FullMatch(ip, IPlist[i].IPAddress))
4488 		{
4489 			return true;
4490 		}
4491 	}
4492 	return false;
4493 }
4494 
4495 /**
4496  * Create the banned list.
4497  * \param ip IP address in text format
4498  * \param name Name of the player we are banning
4499  */
addToBanList(const char * ip,const char * name)4500 static void addToBanList(const char *ip, const char *name)
4501 {
4502 	if (isLoopbackIP(ip))
4503 	{
4504 		return;
4505 	}
4506 	if (!IPlist)
4507 	{
4508 		IPlist = (PLAYER_IP *)malloc(sizeof(PLAYER_IP) * MAX_BANS + 1);
4509 		if (!IPlist)
4510 		{
4511 			debug(LOG_FATAL, "Out of memory!");
4512 			abort();
4513 		}
4514 		IPlistLast = 0;
4515 		memset(IPlist, 0x0, sizeof(PLAYER_IP) * MAX_BANS);
4516 	}
4517 	sstrcpy(IPlist[IPlistLast].IPAddress, ip);
4518 	sstrcpy(IPlist[IPlistLast].pname, name);
4519 	IPlistLast++;
4520 	sync_counter.banned++;
4521 	if (IPlistLast > MAX_BANS)
4522 	{
4523 		debug(LOG_INFO, "We have exceeded %d bans, resetting to 0", MAX_BANS);
4524 		IPlistLast = 0;
4525 	}
4526 }
4527