/*
Minetest
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "chatmessage.h"
#include "server.h"
#include "log.h"
#include "emerge.h"
#include "mapblock.h"
#include "modchannels.h"
#include "nodedef.h"
#include "remoteplayer.h"
#include "rollback_interface.h"
#include "scripting_server.h"
#include "settings.h"
#include "tool.h"
#include "version.h"
#include "network/connection.h"
#include "network/networkprotocol.h"
#include "network/serveropcodes.h"
#include "server/player_sao.h"
#include "server/serverinventorymgr.h"
#include "util/auth.h"
#include "util/base64.h"
#include "util/pointedthing.h"
#include "util/serialize.h"
#include "util/srp.h"
void Server::handleCommand_Deprecated(NetworkPacket* pkt)
{
infostream << "Server: " << toServerCommandTable[pkt->getCommand()].name
<< " not supported anymore" << std::endl;
}
void Server::handleCommand_Init(NetworkPacket* pkt)
{
if(pkt->getSize() < 1)
return;
session_t peer_id = pkt->getPeerId();
RemoteClient *client = getClient(peer_id, CS_Created);
Address addr;
std::string addr_s;
try {
addr = m_con->GetPeerAddress(peer_id);
addr_s = addr.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 " << peer_id <<
" not found" << std::endl;
return;
}
if (client->getState() > CS_Created) {
verbosestream << "Server: Ignoring multiple TOSERVER_INITs from " <<
addr_s << " (peer_id=" << peer_id << ")" << std::endl;
return;
}
client->setCachedAddress(addr);
verbosestream << "Server: Got TOSERVER_INIT from " << addr_s <<
" (peer_id=" << peer_id << ")" << 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(peer_id, SERVER_ACCESSDENIED_SINGLEPLAYER);
return;
}
// First byte after command is maximum supported
// serialization version
u8 client_max;
u16 supp_compr_modes;
u16 min_net_proto_version = 0;
u16 max_net_proto_version;
std::string playerName;
*pkt >> client_max >> supp_compr_modes >> min_net_proto_version
>> max_net_proto_version >> playerName;
u8 our_max = SER_FMT_VER_HIGHEST_READ;
// Use the highest version supported by both
u8 depl_serial_v = std::min(client_max, our_max);
// If it's lower than the lowest supported, give up.
if (depl_serial_v < SER_FMT_VER_LOWEST_READ)
depl_serial_v = SER_FMT_VER_INVALID;
if (depl_serial_v == SER_FMT_VER_INVALID) {
actionstream << "Server: A mismatched client tried to connect from " <<
addr_s << " ser_fmt_max=" << (int)client_max << std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION);
return;
}
client->setPendingSerializationVersion(depl_serial_v);
/*
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;
if ((g_settings->getBool("strict_protocol_version_checking") &&
net_proto_version != LATEST_PROTOCOL_VERSION) ||
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 << " proto_max=" << (int)max_net_proto_version << std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION);
return;
}
/*
Validate player name
*/
const char* playername = playerName.c_str();
size_t pns = playerName.size();
if (pns == 0 || pns > PLAYERNAME_SIZE) {
actionstream << "Server: Player with " <<
((pns > PLAYERNAME_SIZE) ? "a too long" : "an empty") <<
" name tried to connect from " << addr_s << std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_NAME);
return;
}
if (!string_allowed(playerName, PLAYERNAME_ALLOWED_CHARS)) {
actionstream << "Server: Player with an invalid name tried to connect "
"from " << addr_s << std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME);
return;
}
m_clients.setPlayerName(peer_id, playername);
//TODO (later) case insensitivity
std::string legacyPlayerNameCasing = playerName;
if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
actionstream << "Server: Player with the name \"singleplayer\" tried "
"to connect from " << addr_s << std::endl;
DenyAccess(peer_id, 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(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING, reason);
return;
}
}
infostream << "Server: New connection: \"" << playerName << "\" from " <<
addr_s << " (peer_id=" << peer_id << ")" << std::endl;
// Enforce user limit.
// Don't enforce for users that have some admin right or mod permits it.
if (m_clients.isUserLimitReached() &&
playername != g_settings->get("name") &&
!m_script->can_bypass_userlimit(playername, addr_s)) {
actionstream << "Server: " << playername << " tried to join from " <<
addr_s << ", but there are already max_users=" <<
g_settings->getU16("max_users") << " players." << std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_TOO_MANY_USERS);
return;
}
/*
Compose auth methods for answer
*/
std::string encpwd; // encrypted Password field for the user
bool has_auth = m_script->getAuth(playername, &encpwd, NULL);
|