From 74d34da6cbb06a4d5659eb3287a87239c31da22c Mon Sep 17 00:00:00 2001
From: Loic Blot <loic.blot@unix-experience.fr>
Date: Fri, 13 Mar 2015 16:35:21 +0100
Subject: [PATCH] Prepare Protocol v25 init & authentication. * TOSERVER_INIT
 and TOCLIENT_INIT renamed to _LEGACY * TOSERVER_PASSWORD merged from dev-0.5,
 can use protocol v24 and v25 * TOCLIENT_ACCESS_DENIED merged from dev-0.5,
 can use protocol v24 and v25, with normalized strings an a custom id for
 custom errors * new TOSERVER_INIT packet only send MT version, supported
 compressions, protocols and serialization, this permit to rework everything
 later without break the _INIT packet * new TOSERVER_AUTH packet which auth
 the client * new TOCLIENT_HELLO packet which send server serialization
 version atm * new TOCLIENT_AUTH_ACCEPTED which is send when TOCLIENT_AUTH was
 okay. After this packet, the client load datas from servers, like after
 TOCLIENT_INIT_LEGACY packet

---
 src/client.h                          |   4 +-
 src/clientiface.h                     |   6 +
 src/network/clientopcodes.cpp         |   6 +-
 src/network/networkprotocol.h         |  34 ++-
 src/network/packethandlers/client.cpp |  58 ++++-
 src/network/packethandlers/server.cpp | 348 +++++++++++++++++++++++---
 src/network/serveropcodes.cpp         |  12 +-
 src/script/lua_api/l_server.cpp       |   2 +-
 src/server.cpp                        |  38 ++-
 src/server.h                          |  10 +-
 10 files changed, 448 insertions(+), 70 deletions(-)

diff --git a/src/client.h b/src/client.h
index a0add689a..9baa034de 100644
--- a/src/client.h
+++ b/src/client.h
@@ -349,7 +349,9 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 
 	void handleCommand_Null(NetworkPacket* pkt) {};
 	void handleCommand_Deprecated(NetworkPacket* pkt);
-	void handleCommand_Init(NetworkPacket* pkt);
+	void handleCommand_Hello(NetworkPacket* pkt);
+	void handleCommand_AuthAccept(NetworkPacket* pkt);
+	void handleCommand_InitLegacy(NetworkPacket* pkt);
 	void handleCommand_AccessDenied(NetworkPacket* pkt);
 	void handleCommand_RemoveNode(NetworkPacket* pkt);
 	void handleCommand_AddNode(NetworkPacket* pkt);
diff --git a/src/clientiface.h b/src/clientiface.h
index cc303734a..2fd293de2 100644
--- a/src/clientiface.h
+++ b/src/clientiface.h
@@ -217,6 +217,7 @@ class RemoteClient
 		m_version_minor(0),
 		m_version_patch(0),
 		m_full_version("unknown"),
+		m_supported_compressions(0),
 		m_connection_time(getTime(PRECISION_SECONDS))
 	{
 	}
@@ -293,6 +294,9 @@ class RemoteClient
 	void setPendingSerializationVersion(u8 version)
 		{ m_pending_serialization_version = version; }
 
+	void setSupportedCompressionModes(u8 byteFlag)
+		{ m_supported_compressions = byteFlag; }
+
 	void confirmSerializationVersion()
 		{ serialization_version = m_pending_serialization_version; }
 
@@ -370,6 +374,8 @@ class RemoteClient
 
 	std::string m_full_version;
 
+	u8 m_supported_compressions;
+
 	/*
 		time this client was created
 	 */
diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp
index f236b6353..556e8d0c0 100644
--- a/src/network/clientopcodes.cpp
+++ b/src/network/clientopcodes.cpp
@@ -26,8 +26,8 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
 {
 	null_command_handler, // 0x00 (never use this)
 	null_command_handler, // 0x01
-	null_command_handler, // 0x02
-	null_command_handler, // 0x03
+	{ "TOCLIENT_HELLO",                   TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Hello }, // 0x02
+	{ "TOCLIENT_AUTH_ACCEPT",             TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AuthAccept }, // 0x03
 	null_command_handler, // 0x04
 	null_command_handler, // 0x05
 	null_command_handler, // 0x06
@@ -40,7 +40,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
 	null_command_handler, // 0x0D
 	null_command_handler, // 0x0E
 	null_command_handler, // 0x0F
-	{ "TOCLIENT_INIT",                    TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Init }, // 0x10
+	{ "TOCLIENT_INIT",                    TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_InitLegacy }, // 0x10
 	null_command_handler,
 	null_command_handler,
 	null_command_handler,
diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h
index 1e0896ebf..599b70006 100644
--- a/src/network/networkprotocol.h
+++ b/src/network/networkprotocol.h
@@ -110,14 +110,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 		ContentFeatures: change number of special tiles to 6 (CF_SPECIAL_COUNT)
 	PROTOCOL_VERSION 25:
 		Rename TOCLIENT_ACCESS_DENIED to TOCLIENT_ACCESS_DENIED_LEGAGY
-		Rename TOCLIENT_DELETE_PARTICLESPAWNER to TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY
+		Rename TOCLIENT_DELETE_PARTICLESPAWNER to
+			TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY
 		Rename TOSERVER_PASSWORD to TOSERVER_PASSWORD_LEGACY
 		Rename TOSERVER_INIT to TOSERVER_INIT_LEGACY
+		Rename TOCLIENT_INIT to TOCLIENT_INIT_LEGACY
 		Add TOCLIENT_ACCESS_DENIED new opcode (0x0A), using error codes
 			for standard error, keeping customisation possible. This
 			permit translation
 		Add TOCLIENT_DELETE_PARTICLESPAWNER (0x53), fixing the u16 read and
 			reading u32
+		Add TOSERVER_INIT new opcode (0x02) for client presentation to server
+		Add TOSERVER_AUTH new opcode (0x03) for client authentication
+		Add TOCLIENT_HELLO for presenting server to client after client
+			presentation
+		Add TOCLIENT_AUTH_ACCEPT to accept connexion from client
 */
 
 #define LATEST_PROTOCOL_VERSION 24
@@ -143,6 +150,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 enum ToClientCommand
 {
+	TOCLIENT_HELLO = 0x02,
+	TOCLIENT_AUTH_ACCEPT = 0x03,
 	TOCLIENT_ACCESS_DENIED = 0x0A,
 	/*
 		u16 command
@@ -150,7 +159,7 @@ enum ToClientCommand
 		wstring reason
 	*/
 
-	TOCLIENT_INIT = 0x10,
+	TOCLIENT_INIT_LEGACY = 0x10,
 	/*
 		Server's reply to TOSERVER_INIT.
 		Sent second after connected.
@@ -585,17 +594,22 @@ enum ToClientCommand
 
 enum ToServerCommand
 {
-	TOSERVER_INIT = 0x0F,
+	TOSERVER_INIT = 0x02,
 	/*
 		Sent first after connected.
 
 		[0] u16 TOSERVER_INIT
 		[2] u8 SER_FMT_VER_HIGHEST_READ
 		[3] u8 compression_modes
-		[4] std::string player_name
-		[4+*] std::string password (new in some version)
-		[4+*+*] u16 minimum supported network protocol version (added sometime)
-		[4+*+*+2] u16 maximum supported network protocol version (added later than the previous one)
+	*/
+
+	TOSERVER_AUTH = 0x03,
+	/*
+		Sent first after presentation (INIT).
+		[0] std::string player_name
+		[0+*] std::string password (new in some version)
+		[0+*+*] u16 minimum supported network protocol version (added sometime)
+		[0+*+*+2] u16 maximum supported network protocol version (added later than the previous one)
 	*/
 
 	TOSERVER_INIT_LEGACY = 0x10,
@@ -856,8 +870,9 @@ enum AccessDeniedCode {
 	SERVER_ACCESSDENIED_TOO_MANY_USERS = 6,
 	SERVER_ACCESSDENIED_EMPTY_PASSWORD = 7,
 	SERVER_ACCESSDENIED_ALREADY_CONNECTED = 8,
-	SERVER_ACCESSDENIED_CUSTOM_STRING = 9,
-	SERVER_ACCESSDENIED_MAX = 10,
+	SERVER_ACCESSDENIED_SERVER_FAIL = 9,
+	SERVER_ACCESSDENIED_CUSTOM_STRING = 10,
+	SERVER_ACCESSDENIED_MAX = 11,
 };
 
 enum NetProtoCompressionMode {
@@ -874,6 +889,7 @@ const static std::wstring accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = {
 	L"Too many users.",
 	L"Empty passwords are disallowed. Set a password and try again.",
 	L"Another client is connected with this name. If your client closed unexpectedly, try again in a minute.",
+	L"Server authenticator failed. Maybe the servers has some problems."
 	L"",
 };
 
diff --git a/src/network/packethandlers/client.cpp b/src/network/packethandlers/client.cpp
index ae24157e0..838c85989 100644
--- a/src/network/packethandlers/client.cpp
+++ b/src/network/packethandlers/client.cpp
@@ -38,7 +38,7 @@ void Client::handleCommand_Deprecated(NetworkPacket* pkt)
 			<< pkt->getPeerId() << "!" << std::endl;
 }
 
-void Client::handleCommand_Init(NetworkPacket* pkt)
+void Client::handleCommand_Hello(NetworkPacket* pkt)
 {
 	if (pkt->getSize() < 1)
 		return;
@@ -46,11 +46,56 @@ void Client::handleCommand_Init(NetworkPacket* pkt)
 	u8 deployed;
 	*pkt >> deployed;
 
-	infostream << "Client: TOCLIENT_INIT received with "
+	infostream << "Client: TOCLIENT_HELLO received with "
 			"deployed=" << ((int)deployed & 0xff) << std::endl;
 
 	if (!ser_ver_supported(deployed)) {
-		infostream << "Client: TOCLIENT_INIT: Server sent "
+		infostream << "Client: TOCLIENT_HELLO: Server sent "
+				<< "unsupported ser_fmt_ver"<< std::endl;
+		return;
+	}
+
+	m_server_ser_ver = deployed;
+
+	// @ TODO auth to server
+}
+
+void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
+{
+	v3f playerpos;
+	*pkt >> playerpos >> m_map_seed >> m_recommended_send_interval;
+
+	playerpos -= v3f(0, BS / 2, 0);
+
+	// Set player position
+	Player *player = m_env.getLocalPlayer();
+	assert(player != NULL);
+	player->setPosition(playerpos);
+
+	infostream << "Client: received map seed: " << m_map_seed << std::endl;
+	infostream << "Client: received recommended send interval "
+					<< m_recommended_send_interval<<std::endl;
+
+	// Reply to server
+	NetworkPacket* resp_pkt = new NetworkPacket(TOSERVER_INIT2, 0);
+	Send(resp_pkt);
+
+	m_state = LC_Init;
+}
+
+void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
+{
+	if (pkt->getSize() < 1)
+		return;
+
+	u8 deployed;
+	*pkt >> deployed;
+
+	infostream << "Client: TOCLIENT_INIT_LEGACY received with "
+			"deployed=" << ((int)deployed & 0xff) << std::endl;
+
+	if (!ser_ver_supported(deployed)) {
+		infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
 				<< "unsupported ser_fmt_ver"<< std::endl;
 		return;
 	}
@@ -98,10 +143,11 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
 	m_access_denied_reason = L"Unknown";
 
 	if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
+		if (pkt->getSize() < 1)
+			return;
+
 		u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
-		if(pkt->getSize() >= 1) {
-			*pkt >> denyCode;
-		}
+		*pkt >> denyCode;
 		if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
 			*pkt >> m_access_denied_reason;
 		}
diff --git a/src/network/packethandlers/server.cpp b/src/network/packethandlers/server.cpp
index 113ca6c8f..ee30dfd06 100644
--- a/src/network/packethandlers/server.cpp
+++ b/src/network/packethandlers/server.cpp
@@ -43,6 +43,283 @@ void Server::handleCommand_Deprecated(NetworkPacket* pkt)
 		<< " not supported anymore" << std::endl;
 }
 
+void Server::handleCommand_Init(NetworkPacket* pkt)
+{
+
+	if(pkt->getSize() < 1)
+		return;
+
+	RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
+
+	std::string addr_s;
+	try {
+		Address address = getPeerAddress(pkt->getPeerId());
+		addr_s = address.serializeString();
+	}
+	catch (con::PeerNotFoundException &e) {
+		/*
+		 * no peer for this packet found
+		 * most common reason is peer timeout, e.g. peer didn't
+		 * respond for some time, your server was overloaded or
+		 * things like that.
+		 */
+		infostream << "Server::ProcessData(): Canceling: peer "
+				<< pkt->getPeerId() << " not found" << std::endl;
+		return;
+	}
+
+	// If net_proto_version is set, this client has already been handled
+	if (client->getState() > CS_Created) {
+		verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
+				<< addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
+		return;
+	}
+
+	verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
+			<< pkt->getPeerId() << ")" << std::endl;
+
+	// Do not allow multiple players in simple singleplayer mode.
+	// This isn't a perfect way to do it, but will suffice for now
+	if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
+		infostream << "Server: Not allowing another client (" << addr_s
+				<< ") to connect in simple singleplayer mode" << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SINGLEPLAYER);
+		return;
+	}
+
+	// First byte after command is maximum supported
+	// serialization version
+	u8 client_max;
+	u8 compression_modes;
+	u16 min_net_proto_version = 0;
+	u16 max_net_proto_version;
+
+	*pkt >> client_max >> compression_modes >> min_net_proto_version
+			>> max_net_proto_version;
+
+	u8 our_max = SER_FMT_VER_HIGHEST_READ;
+	// Use the highest version supported by both
+	int deployed = std::min(client_max, our_max);
+	// If it's lower than the lowest supported, give up.
+	if (deployed < SER_FMT_VER_LOWEST)
+		deployed = SER_FMT_VER_INVALID;
+
+	if (deployed == SER_FMT_VER_INVALID) {
+		actionstream << "Server: A mismatched client tried to connect from "
+				<< addr_s << std::endl;
+		infostream<<"Server: Cannot negotiate serialization version with "
+				<< addr_s << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
+		return;
+	}
+
+	client->setPendingSerializationVersion(deployed);
+
+	/*
+		Read and check network protocol version
+	*/
+
+	u16 net_proto_version = 0;
+
+	// Figure out a working version if it is possible at all
+	if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
+			min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
+		// If maximum is larger than our maximum, go with our maximum
+		if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
+			net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
+		// Else go with client's maximum
+		else
+			net_proto_version = max_net_proto_version;
+	}
+
+	verbosestream << "Server: " << addr_s << ": Protocol version: min: "
+			<< min_net_proto_version << ", max: " << max_net_proto_version
+			<< ", chosen: " << net_proto_version << std::endl;
+
+	client->net_proto_version = net_proto_version;
+
+	// On this handler protocol version 25 is required
+	if (net_proto_version < 25 ||
+			net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
+			net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
+		actionstream << "Server: A mismatched client tried to connect from "
+				<< addr_s << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
+		return;
+	}
+
+	if (g_settings->getBool("strict_protocol_version_checking")) {
+		if (net_proto_version != LATEST_PROTOCOL_VERSION) {
+			actionstream << "Server: A mismatched (strict) client tried to "
+					<< "connect from " << addr_s << std::endl;
+			DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
+			return;
+		}
+	}
+
+	// @TODO: check if we support same modes, but not required now
+
+	client->setSupportedCompressionModes(compression_modes);
+
+	m_clients.event(pkt->getPeerId(), CSE_Init);
+}
+
+void Server::handleCommand_Auth(NetworkPacket* pkt)
+{
+	std::string addr_s;
+	try {
+		Address address = getPeerAddress(pkt->getPeerId());
+		addr_s = address.serializeString();
+	}
+	catch (con::PeerNotFoundException &e) {
+		/*
+		 * no peer for this packet found
+		 * most common reason is peer timeout, e.g. peer didn't
+		 * respond for some time, your server was overloaded or
+		 * things like that.
+		 */
+		infostream << "Server::ProcessData(): Canceling: peer "
+				<< pkt->getPeerId() << " not found" << std::endl;
+		return;
+	}
+
+	std::string playerName, playerPassword;
+
+	*pkt >> playerName >> playerPassword;
+
+	const char* playername = playerName.c_str();
+
+	if (playerName.size() > PLAYERNAME_SIZE) {
+		actionstream << "Server: Player with an too long name "
+				<< "tried to connect from " << addr_s << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
+		return;
+	}
+
+	if (string_allowed(playerName, PLAYERNAME_ALLOWED_CHARS) == false) {
+		actionstream << "Server: Player with an invalid name "
+				<< "tried to connect from " << addr_s << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME);
+		return;
+	}
+
+	if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
+		actionstream << "Server: Player with the name \"singleplayer\" "
+				<< "tried to connect from " << addr_s << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
+		return;
+	}
+
+	{
+		std::string reason;
+		if(m_script->on_prejoinplayer(playername, addr_s, reason)) {
+			actionstream << "Server: Player with the name \"" << playerName << "\" "
+					<< "tried to connect from " << addr_s << " "
+					<< "but it was disallowed for the following reason: "
+					<< reason << std::endl;
+			DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
+					narrow_to_wide(reason.c_str()));
+			return;
+		}
+	}
+
+	if (playerPassword.size() > PASSWORD_SIZE) {
+		actionstream << "Server: Player with an too long password "
+				<< "tried to connect from " << addr_s << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
+		return;
+	}
+
+	infostream << "Server: New connection: \"" << playerName << "\" from "
+			<< addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
+
+	if(!base64_is_valid(playerPassword)){
+		actionstream << "Server: " << playerName
+				<< " supplied invalid password hash" << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
+		return;
+	}
+
+	// Enforce user limit.
+	// Don't enforce for users that have some admin right
+	if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
+			!checkPriv(playername, "server") &&
+			!checkPriv(playername, "ban") &&
+			!checkPriv(playername, "privs") &&
+			!checkPriv(playername, "password") &&
+			playername != g_settings->get("name")) {
+		actionstream << "Server: " << playername << " tried to join, but there"
+				<< " are already max_users="
+				<< g_settings->getU16("max_users") << " players." << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS);
+		return;
+	}
+
+	std::string checkpwd; // Password hash to check against
+	bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
+	// If no authentication info exists for user, create it
+	if (!has_auth) {
+		if (!isSingleplayer() &&
+				g_settings->getBool("disallow_empty_password") &&
+				playerPassword.empty()) {
+			actionstream << "Server: " << playerName
+					<< " supplied empty password" << std::endl;
+			DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD);
+			return;
+		}
+		std::wstring raw_default_password =
+			narrow_to_wide(g_settings->get("default_password"));
+		std::string initial_password =
+			translatePassword(playername, raw_default_password);
+
+		// If default_password is empty, allow any initial password
+		if (raw_default_password.length() == 0)
+			initial_password = playerPassword.c_str();
+
+		m_script->createAuth(playername, initial_password);
+	}
+
+	has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
+	if(!has_auth) {
+		actionstream << "Server: " << playerName << " cannot be authenticated"
+				<< " (auth handler does not work?)" << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
+		return;
+	}
+
+	if(playerPassword.c_str() != checkpwd) {
+		actionstream << "Server: " << playerName << " supplied wrong password"
+				<< std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
+		return;
+	}
+
+	RemotePlayer *player =
+			static_cast<RemotePlayer*>(m_env->getPlayer(playername));
+
+	if (player && player->peer_id != 0) {
+		errorstream << "Server: " << playername << ": Failed to emerge player"
+				<< " (player allocated to an another client)" << std::endl;
+		DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_ALREADY_CONNECTED);
+	}
+
+	m_clients.setPlayerName(pkt->getPeerId(), playername);
+
+	/*
+		Answer with a TOCLIENT_INIT
+	*/
+
+	NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, pkt->getPeerId());
+
+	resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
+			<< g_settings->getFloat("dedicated_server_step");
+
+	Send(&resp_pkt);
+	m_clients.event(pkt->getPeerId(), CSE_Init);
+}
+
 void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 {
 	// [0] u8 SER_FMT_VER_HIGHEST_READ
@@ -86,7 +363,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 	if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
 		infostream << "Server: Not allowing another client (" << addr_s
 				<< ") to connect in simple singleplayer mode" << std::endl;
-		DenyAccess(pkt->getPeerId(), L"Running in simple singleplayer mode.");
+		DenyAccess_Legacy(pkt->getPeerId(), L"Running in simple singleplayer mode.");
 		return;
 	}
 
@@ -108,7 +385,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 				<< addr_s << std::endl;
 		infostream<<"Server: Cannot negotiate serialization version with "
 				<< addr_s << std::endl;
-		DenyAccess(pkt->getPeerId(), std::wstring(
+		DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
 				L"Your client's version is not supported.\n"
 				L"Server version is ")
 				+ narrow_to_wide(minetest_version_simple) + L"."
@@ -156,7 +433,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 			net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
 		actionstream << "Server: A mismatched client tried to connect from "
 				<< addr_s << std::endl;
-		DenyAccess(pkt->getPeerId(), std::wstring(
+		DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
 				L"Your client's version is not supported.\n"
 				L"Server version is ")
 				+ narrow_to_wide(minetest_version_simple) + L",\n"
@@ -176,7 +453,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 		if (net_proto_version != LATEST_PROTOCOL_VERSION) {
 			actionstream << "Server: A mismatched (strict) client tried to "
 					<< "connect from " << addr_s << std::endl;
-			DenyAccess(pkt->getPeerId(), std::wstring(
+			DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
 					L"Your client's version is not supported.\n"
 					L"Server version is ")
 					+ narrow_to_wide(minetest_version_simple) + L",\n"
@@ -205,7 +482,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 	if (playername_length == PLAYERNAME_SIZE) {
 		actionstream << "Server: Player with name exceeding max length "
 				<< "tried to connect from " << addr_s << std::endl;
-		DenyAccess(pkt->getPeerId(), L"Name too long");
+		DenyAccess_Legacy(pkt->getPeerId(), L"Name too long");
 		return;
 	}
 
@@ -213,21 +490,21 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 	if (playername[0]=='\0') {
 		actionstream << "Server: Player with an empty name "
 				<< "tried to connect from " << addr_s << std::endl;
-		DenyAccess(pkt->getPeerId(), L"Empty name");
+		DenyAccess_Legacy(pkt->getPeerId(), L"Empty name");
 		return;
 	}
 
 	if (string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false) {
 		actionstream << "Server: Player with an invalid name "
 				<< "tried to connect from " << addr_s << std::endl;
-		DenyAccess(pkt->getPeerId(), L"Name contains unallowed characters");
+		DenyAccess_Legacy(pkt->getPeerId(), L"Name contains unallowed characters");
 		return;
 	}
 
 	if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
 		actionstream << "Server: Player with the name \"singleplayer\" "
 				<< "tried to connect from " << addr_s << std::endl;
-		DenyAccess(pkt->getPeerId(), L"Name is not allowed");
+		DenyAccess_Legacy(pkt->getPeerId(), L"Name is not allowed");
 		return;
 	}
 
@@ -238,7 +515,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 					<< "tried to connect from " << addr_s << " "
 					<< "but it was disallowed for the following reason: "
 					<< reason << std::endl;
-			DenyAccess(pkt->getPeerId(), narrow_to_wide(reason.c_str()));
+			DenyAccess_Legacy(pkt->getPeerId(), narrow_to_wide(reason.c_str()));
 			return;
 		}
 	}
@@ -262,7 +539,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 	if (!base64_is_valid(given_password)) {
 		actionstream << "Server: " << playername
 				<< " supplied invalid password hash" << std::endl;
-		DenyAccess(pkt->getPeerId(), L"Invalid password hash");
+		DenyAccess_Legacy(pkt->getPeerId(), L"Invalid password hash");
 		return;
 	}
 
@@ -277,7 +554,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 		actionstream << "Server: " << playername << " tried to join, but there"
 				<< " are already max_users="
 				<< g_settings->getU16("max_users") << " players." << std::endl;
-		DenyAccess(pkt->getPeerId(), L"Too many users.");
+		DenyAccess_Legacy(pkt->getPeerId(), L"Too many users.");
 		return;
 	}
 
@@ -291,7 +568,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 				std::string(given_password) == "") {
 			actionstream << "Server: " << playername
 					<< " supplied empty password" << std::endl;
-			DenyAccess(pkt->getPeerId(), L"Empty passwords are "
+			DenyAccess_Legacy(pkt->getPeerId(), L"Empty passwords are "
 					L"disallowed. Set a password and try again.");
 			return;
 		}
@@ -312,14 +589,14 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 	if (!has_auth) {
 		actionstream << "Server: " << playername << " cannot be authenticated"
 				<< " (auth handler does not work?)" << std::endl;
-		DenyAccess(pkt->getPeerId(), L"Not allowed to login");
+		DenyAccess_Legacy(pkt->getPeerId(), L"Not allowed to login");
 		return;
 	}
 
 	if (given_password != checkpwd) {
 		actionstream << "Server: " << playername << " supplied wrong password"
 				<< std::endl;
-		DenyAccess(pkt->getPeerId(), L"Wrong password");
+		DenyAccess_Legacy(pkt->getPeerId(), L"Wrong password");
 		return;
 	}
 
@@ -329,7 +606,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 	if (player && player->peer_id != 0) {
 		errorstream << "Server: " << playername << ": Failed to emerge player"
 				<< " (player allocated to an another client)" << std::endl;
-		DenyAccess(pkt->getPeerId(), L"Another client is connected with this "
+		DenyAccess_Legacy(pkt->getPeerId(), L"Another client is connected with this "
 				L"name. If your client closed unexpectedly, try again in "
 				L"a minute.");
 	}
@@ -340,7 +617,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
 		Answer with a TOCLIENT_INIT
 	*/
 
-	NetworkPacket* resp_pkt = new NetworkPacket(TOCLIENT_INIT, 1 + 6 + 8 + 4,
+	NetworkPacket* resp_pkt = new NetworkPacket(TOCLIENT_INIT_LEGACY, 1 + 6 + 8 + 4,
 			pkt->getPeerId());
 
 	*resp_pkt << (u8) deployed << (v3s16) floatToInt(v3f(0,0,0), BS)
@@ -942,33 +1219,36 @@ void Server::handleCommand_Breath(NetworkPacket* pkt)
 	SendPlayerBreath(pkt->getPeerId());
 }
 
-void Server::handleCommand_Password_Legacy(NetworkPacket* pkt)
+void Server::handleCommand_Password(NetworkPacket* pkt)
 {
-	/*
-		[0] u16 TOSERVER_PASSWORD
-		[2] u8[28] old password
-		[30] u8[28] new password
-	*/
-
 	errorstream << "PAssword packet size: " << pkt->getSize() << " size required: " << PASSWORD_SIZE * 2 << std::endl;
-	if (pkt->getSize() != PASSWORD_SIZE * 2)
+	if ((pkt->getCommand() == TOSERVER_PASSWORD && pkt->getSize() < 4) ||
+			pkt->getSize() != PASSWORD_SIZE * 2)
 		return;
 
 	std::string oldpwd;
 	std::string newpwd;
 
-	for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
-		char c = pkt->getChar(i);
-		if (c == 0)
-			break;
-		oldpwd += c;
+	if (pkt->getCommand() == TOSERVER_PASSWORD) {
+		*pkt >> oldpwd >> newpwd;
 	}
+	// 13/03/15
+	// Protocol v24 compat. Please remove in 1 year after
+	// client convergence to 0.4.13/0.5.x
+	else {
+		for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
+			char c = pkt->getChar(i);
+			if (c == 0)
+				break;
+			oldpwd += c;
+		}
 
-	for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
-		char c = pkt->getChar(PASSWORD_SIZE + i);
-		if (c == 0)
-			break;
-		newpwd += c;
+		for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
+			char c = pkt->getChar(PASSWORD_SIZE + i);
+			if (c == 0)
+				break;
+			newpwd += c;
+		}
 	}
 
 	Player *player = m_env->getPlayer(pkt->getPeerId());
diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp
index cecabf7a8..9c1da9ad1 100644
--- a/src/network/serveropcodes.cpp
+++ b/src/network/serveropcodes.cpp
@@ -26,8 +26,8 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
 {
 	null_command_handler, // 0x00 (never use this)
 	null_command_handler, // 0x01
-	null_command_handler, // 0x02
-	null_command_handler, // 0x03
+	{ "TOSERVER_INIT",                    TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02
+	{ "TOSERVER_AUTH",                    TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Auth }, // 0x03
 	null_command_handler, // 0x04
 	null_command_handler, // 0x05
 	null_command_handler, // 0x06
@@ -78,14 +78,14 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
 	{ "TOSERVER_SIGNNODETEXT",             TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x33
 	{ "TOSERVER_CLICK_ACTIVEOBJECT",       TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x34
 	{ "TOSERVER_DAMAGE",                   TOSERVER_STATE_INGAME, &Server::handleCommand_Damage }, // 0x35
-	{ "TOSERVER_PASSWORD_LEGACY",          TOSERVER_STATE_INGAME, &Server::handleCommand_Password_Legacy }, // 0x36
+	{ "TOSERVER_PASSWORD_LEGACY",          TOSERVER_STATE_INGAME, &Server::handleCommand_Password }, // 0x36
 	{ "TOSERVER_PLAYERITEM",               TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerItem }, // 0x37
 	{ "TOSERVER_RESPAWN",                  TOSERVER_STATE_INGAME, &Server::handleCommand_Respawn }, // 0x38
 	{ "TOSERVER_INTERACT",                 TOSERVER_STATE_INGAME, &Server::handleCommand_Interact }, // 0x39
 	{ "TOSERVER_REMOVED_SOUNDS",           TOSERVER_STATE_INGAME, &Server::handleCommand_RemovedSounds }, // 0x3a
 	{ "TOSERVER_NODEMETA_FIELDS",          TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b
 	{ "TOSERVER_INVENTORY_FIELDS",         TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryFields }, // 0x3c
-	null_command_handler, // 0x3d
+	{ "TOSERVER_PASSWORD",                 TOSERVER_STATE_INGAME, &Server::handleCommand_Password }, // 0x3d
 	null_command_handler, // 0x3e
 	null_command_handler, // 0x3f
 	{ "TOSERVER_REQUEST_MEDIA",            TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
@@ -100,8 +100,8 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
 {
 	null_command_factory, // 0x00
 	null_command_factory, // 0x01
-	null_command_factory, // 0x02
-	null_command_factory, // 0x03
+	{ "TOCLIENT_HELLO",             0, true }, // 0x02
+	{ "TOCLIENT_AUTH_ACCEPT",       0, true }, // 0x03
 	null_command_factory, // 0x04
 	null_command_factory, // 0x05
 	null_command_factory, // 0x06
diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp
index fcdd8c2ff..99e73b03e 100644
--- a/src/script/lua_api/l_server.cpp
+++ b/src/script/lua_api/l_server.cpp
@@ -306,7 +306,7 @@ int ModApiServer::l_kick_player(lua_State *L)
 		lua_pushboolean(L, false); // No such player
 		return 1;
 	}
-	getServer(L)->DenyAccess(player->peer_id, narrow_to_wide(message));
+	getServer(L)->DenyAccess_Legacy(player->peer_id, narrow_to_wide(message));
 	lua_pushboolean(L, true);
 	return 1;
 }
diff --git a/src/server.cpp b/src/server.cpp
index b230ffd18..7e0ee7d03 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -1040,7 +1040,7 @@ void Server::Receive()
 	}
 	catch(ClientStateError &e) {
 		errorstream << "ProcessData: peer=" << peer_id  << e.what() << std::endl;
-		DenyAccess(peer_id, L"Your client sent something server didn't expect."
+		DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
 				L"Try reconnecting or updating your client");
 	}
 	catch(con::PeerNotFoundException &e) {
@@ -1073,13 +1073,13 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
 		if(player && player->peer_id != 0) {
 			errorstream<<"Server: "<<playername<<": Failed to emerge player"
 					<<" (player allocated to an another client)"<<std::endl;
-			DenyAccess(peer_id, L"Another client is connected with this "
+			DenyAccess_Legacy(peer_id, L"Another client is connected with this "
 					L"name. If your client closed unexpectedly, try again in "
 					L"a minute.");
 		} else {
 			errorstream<<"Server: "<<playername<<": Failed to emerge player"
 					<<std::endl;
-			DenyAccess(peer_id, L"Could not allocate player.");
+			DenyAccess_Legacy(peer_id, L"Could not allocate player.");
 		}
 		return NULL;
 	}
@@ -1173,7 +1173,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 					<< addr_s << "; banned name was "
 					<< ban_name << std::endl;
 			// This actually doesn't seem to transfer to the client
-			DenyAccess(peer_id, L"Your ip is banned. Banned name was "
+			DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
 					+ narrow_to_wide(ban_name));
 			return;
 		}
@@ -1502,7 +1502,20 @@ void Server::SendBreath(u16 peer_id, u16 breath)
 	Send(pkt);
 }
 
-void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason)
+void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason, const std::wstring &custom_reason)
+{
+	DSTACK(__FUNCTION_NAME);
+
+	NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
+	pkt << (u8) reason;
+
+	if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING) {
+		pkt << custom_reason;
+	}
+	Send(&pkt);
+}
+
+void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
 {
 	DSTACK(__FUNCTION_NAME);
 
@@ -2525,11 +2538,22 @@ void Server::RespawnPlayer(u16 peer_id)
 	}
 }
 
-void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
+void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::wstring &custom_reason)
+{
+	DSTACK(__FUNCTION_NAME);
+
+	SendAccessDenied(peer_id, reason, custom_reason);
+	m_clients.event(peer_id, CSE_SetDenied);
+	m_con.DisconnectPeer(peer_id);
+}
+
+// 13/03/15: remove this function when protocol version 25 will become
+// the minimum version for MT users, maybe in 1 year
+void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
 {
 	DSTACK(__FUNCTION_NAME);
 
-	SendAccessDenied(peer_id, reason);
+	SendAccessDenied_Legacy(peer_id, reason);
 	m_clients.event(peer_id, CSE_SetDenied);
 	m_con.DisconnectPeer(peer_id);
 }
diff --git a/src/server.h b/src/server.h
index 897841406..546a44a20 100644
--- a/src/server.h
+++ b/src/server.h
@@ -197,6 +197,8 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 
 	void handleCommand_Null(NetworkPacket* pkt) {};
 	void handleCommand_Deprecated(NetworkPacket* pkt);
+	void handleCommand_Init(NetworkPacket* pkt);
+	void handleCommand_Auth(NetworkPacket* pkt);
 	void handleCommand_Init_Legacy(NetworkPacket* pkt);
 	void handleCommand_Init2(NetworkPacket* pkt);
 	void handleCommand_RequestMedia(NetworkPacket* pkt);
@@ -209,7 +211,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	void handleCommand_ChatMessage(NetworkPacket* pkt);
 	void handleCommand_Damage(NetworkPacket* pkt);
 	void handleCommand_Breath(NetworkPacket* pkt);
-	void handleCommand_Password_Legacy(NetworkPacket* pkt);
+	void handleCommand_Password(NetworkPacket* pkt);
 	void handleCommand_PlayerItem(NetworkPacket* pkt);
 	void handleCommand_Respawn(NetworkPacket* pkt);
 	void handleCommand_Interact(NetworkPacket* pkt);
@@ -366,7 +368,8 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	void peerAdded(con::Peer *peer);
 	void deletingPeer(con::Peer *peer, bool timeout);
 
-	void DenyAccess(u16 peer_id, const std::wstring &reason);
+	void DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::wstring &custom_reason=NULL);
+	void DenyAccess_Legacy(u16 peer_id, const std::wstring &reason);
 	bool getClientConInfo(u16 peer_id, con::rtt_stat_type type,float* retval);
 	bool getClientInfo(u16 peer_id,ClientState* state, u32* uptime,
 			u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch,
@@ -388,7 +391,8 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	void SendMovement(u16 peer_id);
 	void SendHP(u16 peer_id, u8 hp);
 	void SendBreath(u16 peer_id, u16 breath);
-	void SendAccessDenied(u16 peer_id,const std::wstring &reason);
+	void SendAccessDenied(u16 peer_id, AccessDeniedCode reason, const std::wstring &custom_reason);
+	void SendAccessDenied_Legacy(u16 peer_id, const std::wstring &reason);
 	void SendDeathscreen(u16 peer_id,bool set_camera_point_target, v3f camera_point_target);
 	void SendItemDef(u16 peer_id,IItemDefManager *itemdef, u16 protocol_version);
 	void SendNodeDef(u16 peer_id,INodeDefManager *nodedef, u16 protocol_version);
-- 
GitLab