Remove token in network string

This commit is contained in:
Benau 2018-06-04 13:25:10 +08:00
parent ec51a2dfbb
commit d89c65b0c9
15 changed files with 85 additions and 165 deletions

View File

@ -717,6 +717,9 @@ namespace UserConfigParams
PARAM_PREFIX FloatUserConfigParam m_voting_timeout
PARAM_DEFAULT(FloatUserConfigParam(20.0f, "voting-timeout",
&m_network_group, "Timeout in seconds for voting tracks in server."));
PARAM_PREFIX FloatUserConfigParam m_validation_timeout
PARAM_DEFAULT(FloatUserConfigParam(20.0f, "validation-timeout",
&m_network_group, "Timeout in seconds for validation of clients."));
PARAM_PREFIX IntUserConfigParam m_server_max_players
PARAM_DEFAULT(IntUserConfigParam(12, "server_max_players",
&m_network_group, "Maximum number of players on the server."));

View File

@ -19,6 +19,7 @@
#include "network/event.hpp"
#include "network/crypto.hpp"
#include "network/protocols/client_lobby.hpp"
#include "network/stk_peer.hpp"
#include "utils/log.hpp"
#include "utils/time.hpp"
@ -28,6 +29,16 @@
/** \brief Constructor
* \param event : The event that needs to be translated.
*/
// ============================================================================
constexpr bool isConnectionRequestPacket(unsigned char* data, size_t length)
{
// Connection request is not synchronous
return length < 2 ? false : (uint8_t)data[0] == PROTOCOL_LOBBY_ROOM &&
(uint8_t)data[1] == LobbyProtocol::LE_CONNECTION_REQUESTED;
} // isConnectionRequestPacket
// ============================================================================
Event::Event(ENetEvent* event, std::shared_ptr<STKPeer> peer)
{
m_arrival_time = (double)StkTime::getTimeSinceEpoch();
@ -52,6 +63,18 @@ Event::Event(ENetEvent* event, std::shared_ptr<STKPeer> peer)
}
if (m_type == EVENT_TYPE_MESSAGE)
{
if (!m_peer->isValidated() && !isConnectionRequestPacket
(event->packet->data, event->packet->dataLength))
{
throw std::runtime_error("Invalid packet before validation.");
}
auto cl = LobbyProtocol::get<ClientLobby>();
if (event->channelID == EVENT_CHANNEL_UNENCRYPTED && (!cl ||
(cl && !cl->waitingForServerRespond())))
{
throw std::runtime_error("Unencrypted content at wrong state.");
}
if (m_peer->getCrypto() && event->channelID == EVENT_CHANNEL_NORMAL)
{
m_data = m_peer->getCrypto()->decryptRecieve(event->packet);

View File

@ -37,28 +37,6 @@ void NetworkString::unitTesting()
s.setSynchronous(false);
assert(!s.isSynchronous());
uint32_t token = 0x12345678;
// Check token setting and reading
s.setToken(token);
assert(s.getToken()==token);
assert(s.getToken()!=0x87654321);
// Append some values from the message
s.addUInt16(12345);
s.addFloat(1.2345f);
// Since this string was not received, we need to skip the type and token explicitly.
s.skip(5);
assert(s.getUInt16() == 12345);
float f = s.getFloat();
assert(f==1.2345f);
// Check modifying a token in an already assembled message
uint32_t new_token = 0x87654321;
s.setToken(new_token);
assert(s.getToken()!=token);
assert(s.getToken()==new_token);
// Check log message format
BareNetworkString slog(28);
for(unsigned int i=0; i<28; i++)

View File

@ -45,8 +45,7 @@ typedef unsigned char uchar;
* This class allows you to easily create and parse 8-bit strings, has
* functions to add and read other data types (e.g. int, strings). It does
* not enforce any structure on the sequence (NetworkString uses this as
* a base class, and enforces a protocol type in the first byte, and a
* 4-byte authentication token in bytes 2-5)
* a base class, and enforces a protocol type in the first byte)
*/
class BareNetworkString
@ -62,8 +61,8 @@ protected:
/** To avoid copying the buffer when bytes are deleted (which only
* happens at the front), use an offset index. All positions given
* by the user will be relative to this index. Note that the type
* should be left as signed, otherwise certain arithmetic (e.g.
* 1-m_current_offset in checkToken() will be done unsigned).
* should be left as signed, otherwise certain arithmetic will be done
* unsigned).
*/
mutable int m_current_offset;
@ -359,7 +358,6 @@ public:
* manager thread.
* bits 6-0: The protocol ID, which identifies the receiving protocol
* for this message.
* Byte 1-4: A token to authenticate the sender.
*
* Otherwise this class offers template functions to add arbitrary variables,
* and retrieve them again. It kept the functionality of 'removing' bytes
@ -373,31 +371,30 @@ public:
static void unitTesting();
/** Constructor for a message to be sent. It sets the
* protocol type of this message. It adds 5 bytes to the capacity:
* 1 byte for the protocol type, and 4 bytes for the token. */
* protocol type of this message. It adds 1 byte to the capacity:
* 1 byte for the protocol type. */
NetworkString(ProtocolType type, int capacity=16)
: BareNetworkString(capacity+5)
: BareNetworkString(capacity+1)
{
m_buffer.push_back(type);
addUInt32(0); // add dummy token for now
} // NetworkString
// ------------------------------------------------------------------------
/** Constructor for a received message. It automatically ignored the first
* 5 bytes which contain the type and token. Those will be accessed using
* 5 bytes which contain the type. Those will be accessed using
* special functions. */
NetworkString(const uint8_t *data, int len)
: BareNetworkString((char*)data, len)
{
m_current_offset = 5; // ignore type and token
m_current_offset = 1; // ignore type
} // NetworkString
// ------------------------------------------------------------------------
/** Empties the string, but does not reset the pre-allocated size. */
void clear()
{
m_buffer.erase(m_buffer.begin() + 5, m_buffer.end());
m_current_offset = 5;
m_buffer.erase(m_buffer.begin() + 1, m_buffer.end());
m_current_offset = 1;
} // clear
// ------------------------------------------------------------------------
/** Returns the protocol type of this message. */
@ -422,35 +419,6 @@ public:
{
return (m_buffer[0] & PROTOCOL_SYNCHRONOUS) == PROTOCOL_SYNCHRONOUS;
} // isSynchronous
// ------------------------------------------------------------------------
/** Sets a token for a message. Note that the token in an already
* assembled message might be updated (e.g. if the same message is sent
* from the server to a set of clients). */
void setToken(uint32_t token)
{
// Make sure there is enough space for the token:
if(m_buffer.size()<5)
m_buffer.resize(5);
m_buffer[1] = (token >> 24) & 0xff;
m_buffer[2] = (token >> 16) & 0xff;
m_buffer[3] = (token >> 8) & 0xff;
m_buffer[4] = token & 0xff;
} // setToken
// ------------------------------------------------------------------------
/** Returns if the security token of this message is the same as the
* specified token. */
uint32_t getToken() const
{
// We need to reset the current position to 1 so that we can use the
// existing read 4 byte integer function to get the token.
int save_pos = m_current_offset;
m_current_offset = 1;
uint32_t token = getUInt32();
m_current_offset = save_pos;
return token;
} // getToken
}; // class NetworkString

View File

@ -111,16 +111,15 @@ void Protocol::requestTerminate()
} // requestTerminate
// ----------------------------------------------------------------------------
/** Sends a message to all peers, inserting the peer's token into the message.
/** Sends a message to all peers, encrypt the message if needed.
* The message is composed of a 1-byte message (usually the message type)
* followed by the token of this client and then actual message).
* followed by the actual message.
* \param message The actual message content.
*/
void Protocol::sendMessageToPeersChangingToken(NetworkString *message,
bool reliable)
void Protocol::sendMessageToPeers(NetworkString *message, bool reliable)
{
STKHost::get()->sendPacketToAllPeers(message, reliable);
} // sendMessageToPeersChangingToken
} // sendMessageToPeers
// ----------------------------------------------------------------------------
/** Sends a message from a client to the server.

View File

@ -125,10 +125,8 @@ public:
/// functions to check incoming data easily
NetworkString* getNetworkString(size_t capacity = 16);
bool checkDataSize(Event* event, unsigned int minimum_size);
void sendMessageToPeersChangingToken(NetworkString *message,
bool reliable = true);
void sendToServer(NetworkString *message,
bool reliable = true);
void sendMessageToPeers(NetworkString *message, bool reliable = true);
void sendToServer(NetworkString *message, bool reliable = true);
void requestStart();
void requestPause();
void requestUnpause();

View File

@ -444,19 +444,12 @@ void ClientLobby::connectionAccepted(Event* event)
if (!checkDataSize(event, 4)) return;
NetworkString &data = event->data();
STKPeer* peer = event->getPeer();
// Accepted
// ========
Log::info("ClientLobby", "The server accepted the connection.");
STKHost::get()->setMyHostId(data.getUInt32());
assert(!NetworkConfig::get()->isAddingNetworkPlayers());
// connection token
uint32_t token = data.getToken();
peer->setClientServerToken(token);
m_state.store(CONNECTED);
} // connectionAccepted
//-----------------------------------------------------------------------------

View File

@ -80,6 +80,8 @@ public:
virtual void asynchronousUpdate() OVERRIDE {}
virtual bool allPlayersReady() const OVERRIDE
{ return m_state.load() >= PLAYING; }
bool waitingForServerRespond() const
{ return m_state.load() == REQUESTING_CONNECTION; }
};
#endif // CLIENT_LOBBY_HPP

View File

@ -41,7 +41,7 @@ bool GameEventsProtocol::notifyEvent(Event* event)
if (event->getType() != EVENT_TYPE_MESSAGE || !World::getWorld())
return true;
NetworkString &data = event->data();
if (data.size() < 1) // for token and type
if (data.size() < 1) // for type
{
Log::warn("GameEventsProtocol", "Too short message.");
return true;
@ -134,7 +134,7 @@ void GameEventsProtocol::kartFinishedRace(AbstractKart *kart, float time)
ns->setSynchronous(true);
ns->addUInt8(GE_KART_FINISHED_RACE).addUInt8(kart->getWorldKartId())
.addFloat(time);
sendMessageToPeersChangingToken(ns, /*reliable*/true);
sendMessageToPeers(ns, /*reliable*/true);
delete ns;
} // kartFinishedRace
@ -145,7 +145,7 @@ void GameEventsProtocol::kartFinishedRace(AbstractKart *kart, float time)
*/
void GameEventsProtocol::kartFinishedRace(const NetworkString &ns)
{
if (ns.size() < 5) // for token and type
if (ns.size() < 5)
{
Log::warn("GameEventsProtocol", "kartFinisheRace: Too short message.");
return;

View File

@ -306,7 +306,7 @@ void GameProtocol::sendState()
Log::info("GameProtocol", "Sending new state at %d.",
World::getWorld()->getTimeTicks());
assert(NetworkConfig::get()->isServer());
sendMessageToPeersChangingToken(m_data_to_send, /*reliable*/true);
sendMessageToPeers(m_data_to_send, /*reliable*/true);
} // sendState
// ----------------------------------------------------------------------------

View File

@ -170,11 +170,6 @@ bool ServerLobby::notifyEvent(Event* event)
//-----------------------------------------------------------------------------
void ServerLobby::handleChat(Event* event)
{
if (!event->getPeer()->hasPlayerProfiles())
{
Log::warn("ServerLobby", "Unauthorized peer wants to chat.");
return;
}
if (!checkDataSize(event, 1)) return;
core::stringw message;
@ -184,7 +179,7 @@ void ServerLobby::handleChat(Event* event)
NetworkString* chat = getNetworkString();
chat->setSynchronous(true);
chat->addUInt8(LE_CHAT).encodeString16(message);
sendMessageToPeersChangingToken(chat, /*reliable*/true);
sendMessageToPeers(chat, /*reliable*/true);
delete chat;
}
} // handleChat
@ -384,7 +379,7 @@ void ServerLobby::asynchronousUpdate()
// Reset for next state usage
resetPeersReady();
m_state = LOAD_WORLD;
sendMessageToPeersChangingToken(load_world);
sendMessageToPeers(load_world);
delete load_world;
}
break;
@ -464,7 +459,7 @@ void ServerLobby::update(int ticks)
NetworkString *exit_result_screen = getNetworkString(1);
exit_result_screen->setSynchronous(true);
exit_result_screen->addUInt8(LE_EXIT_RESULT);
sendMessageToPeersChangingToken(exit_result_screen,
sendMessageToPeers(exit_result_screen,
/*reliable*/true);
delete exit_result_screen;
std::lock_guard<std::mutex> lock(m_connection_mutex);
@ -475,7 +470,7 @@ void ServerLobby::update(int ticks)
server_info->setSynchronous(true);
server_info->addUInt8(LE_SERVER_INFO);
m_game_setup->addServerInfo(server_info);
sendMessageToPeersChangingToken(server_info);
sendMessageToPeers(server_info);
delete server_info;
setup();
}
@ -572,7 +567,7 @@ void ServerLobby::signalRaceStartToClients()
StkTime::getRealTime());
NetworkString *ns = getNetworkString(1);
ns->addUInt8(LE_START_RACE);
sendMessageToPeersChangingToken(ns, /*reliable*/true);
sendMessageToPeers(ns, /*reliable*/true);
delete ns;
} // startGame
@ -623,6 +618,8 @@ void ServerLobby::startSelection(const Event *event)
auto peers = STKHost::get()->getPeers();
for (auto peer : peers)
{
if (!peer->isValidated())
continue;
peer->eraseServerKarts(m_available_kts.first, karts_erase);
peer->eraseServerTracks(m_available_kts.second, tracks_erase);
}
@ -647,7 +644,7 @@ void ServerLobby::startSelection(const Event *event)
ns->encodeString(track);
}
sendMessageToPeersChangingToken(ns, /*reliable*/true);
sendMessageToPeers(ns, /*reliable*/true);
delete ns;
m_state = SELECTING;
@ -802,7 +799,7 @@ void ServerLobby::checkRaceFinished()
// result screen and go back to the lobby
m_timeout.store((float)StkTime::getRealTime() + 15.0f);
m_state = RESULT_DISPLAY;
sendMessageToPeersChangingToken(total, /*reliable*/ true);
sendMessageToPeers(total, /*reliable*/ true);
delete total;
Log::info("ServerLobby", "End of game message sent");
@ -1035,7 +1032,7 @@ void ServerLobby::clientDisconnected(Event* event)
msg->encodeString(name);
Log::info("ServerLobby", "%s disconnected", name.c_str());
}
sendMessageToPeersChangingToken(msg, /*reliable*/true);
sendMessageToPeers(msg, /*reliable*/true);
updatePlayerList();
delete msg;
} // clientDisconnected
@ -1086,7 +1083,7 @@ void ServerLobby::connectionRequested(Event* event)
// Check server version
int version = data.getUInt8();
if (version < stk_config->m_max_server_version ||
if (version < stk_config->m_min_server_version ||
version > stk_config->m_max_server_version)
{
NetworkString *message = getNetworkString(2);
@ -1275,20 +1272,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
per_player_difficulty, (uint8_t)i));
}
if (!peer->isClientServerTokenSet())
{
// Now answer to the peer that just connected
// ------------------------------------------
RandomGenerator token_generator;
// use 4 random numbers because rand_max is probably 2^15-1.
uint32_t token = (uint32_t)((token_generator.get(RAND_MAX) & 0xff) << 24 |
(token_generator.get(RAND_MAX) & 0xff) << 16 |
(token_generator.get(RAND_MAX) & 0xff) << 8 |
(token_generator.get(RAND_MAX) & 0xff));
peer->setClientServerToken(token);
}
peer->setValidated();
// send a message to the one that asked to connect
NetworkString* message_ack = getNetworkString(4);
message_ack->setSynchronous(true);
@ -1343,7 +1327,7 @@ void ServerLobby::updatePlayerList(bool force_update)
pl->addUInt8(server_owner);
pl->addUInt8(profile->getPerPlayerDifficulty());
}
sendMessageToPeersChangingToken(pl);
sendMessageToPeers(pl);
delete pl;
} // updatePlayerList
@ -1368,7 +1352,7 @@ void ServerLobby::updateServerOwner()
for (auto peer: peers)
{
// Only 127.0.0.1 can be server owner in case of graphics-client-server
if (peer->hasPlayerProfiles() &&
if (peer->isValidated() &&
(NetworkConfig::get()->getServerIdFile().empty() ||
peer->getAddress().getIP() == 0x7f000001))
{
@ -1466,7 +1450,7 @@ void ServerLobby::playerVote(Event* event)
uint8_t reverse = data.getUInt8();
m_peers_votes[event->getPeerSP()] =
std::make_tuple(track_name, lap, reverse == 1);
sendMessageToPeersChangingToken(&other);
sendMessageToPeers(&other);
} // playerVote

View File

@ -737,17 +737,19 @@ void STKHost::mainLoop()
if (is_server)
{
std::unique_lock<std::mutex> peer_lock(m_peers_mutex);
// Remove any peer which has no token for 7 seconds
// The token is set when the first connection request has happened
// Remove peer which has not been validated after a specific time
// It is validated when the first connection request has finished
const float timeout = UserConfigParams::m_validation_timeout;
for (auto it = m_peers.begin(); it != m_peers.end();)
{
if (!it->second->isClientServerTokenSet() &&
if (!it->second->isValidated() &&
(float)StkTime::getRealTime() >
it->second->getConnectedTime() + 7.0f)
it->second->getConnectedTime() + timeout)
{
Log::info("STKHost", "%s has no token for more than 7"
" seconds, disconnect it by force.",
it->second->getAddress().toString().c_str());
Log::info("STKHost", "%s has not been validated for more"
" than %f seconds, disconnect it by force.",
it->second->getAddress().toString().c_str(),
timeout);
enet_host_flush(host);
enet_peer_reset(it->first);
it = m_peers.erase(it);
@ -804,6 +806,9 @@ void STKHost::mainLoop()
TransportAddress addr(event.peer->address);
Log::info("STKHost", "%s has just connected. There are "
"now %u peers.", addr.toString().c_str(), getPeerCount());
// Client always trust the server
if (!is_server)
stk_peer->setValidated();
} // ENET_EVENT_TYPE_CONNECT
else if (event.type == ENET_EVENT_TYPE_DISCONNECT)
{
@ -1020,7 +1025,7 @@ void STKHost::sendPacketToAllPeers(NetworkString *data, bool reliable)
std::lock_guard<std::mutex> lock(m_peers_mutex);
for (auto p : m_peers)
{
if (p.second->isClientServerTokenSet())
if (p.second->isValidated())
p.second->sendPacket(data, reliable);
}
} // sendPacketExcept
@ -1038,7 +1043,7 @@ void STKHost::sendPacketExcept(STKPeer* peer, NetworkString *data,
for (auto p : m_peers)
{
STKPeer* stk_peer = p.second.get();
if (!stk_peer->isSamePeer(peer) && p.second->isClientServerTokenSet())
if (!stk_peer->isSamePeer(peer) && p.second->isValidated())
{
stk_peer->sendPacket(data, reliable);
}
@ -1093,6 +1098,7 @@ void STKHost::replaceNetwork(ENetEvent& event, Network* network)
m_network = network;
auto stk_peer = std::make_shared<STKPeer>(event.peer, this,
m_next_unique_host_id++);
stk_peer->setValidated();
m_peers[event.peer] = stk_peer;
setPrivatePort();
startListening();
@ -1100,13 +1106,3 @@ void STKHost::replaceNetwork(ENetEvent& event, Network* network)
if (pm && !pm->isExiting())
pm->propagateEvent(new Event(&event, stk_peer));
} // replaceNetwork
//-----------------------------------------------------------------------------
bool STKHost::isConnectionRequestPacket(unsigned char* data, int length)
{
if (length < 6)
return false;
// Connection request is not synchronous
return (uint8_t)data[0] == PROTOCOL_LOBBY_ROOM &&
(uint8_t)data[5] == LobbyProtocol::LE_CONNECTION_REQUESTED;
} // isConnectionRequestPacket

View File

@ -143,8 +143,6 @@ private:
std::shared_ptr<ServerLobby> sl);
// ------------------------------------------------------------------------
void mainLoop();
// ------------------------------------------------------------------------
bool isConnectionRequestPacket(unsigned char* data, int length);
public:
/** If a network console should be started. */

View File

@ -38,14 +38,13 @@ STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
m_enet_peer = enet_peer;
m_host_id = host_id;
m_connected_time = (float)StkTime::getRealTime();
m_token_set.store(false);
m_validated.store(false);
if (NetworkConfig::get()->isClient())
{
// This allow client to get the correct ping as fast as possible
// reset to default (0) will be done in STKHost::mainloop after 3 sec
setPingInterval(10);
}
m_client_server_token.store(0);
} // STKPeer
//-----------------------------------------------------------------------------
@ -101,7 +100,6 @@ void STKPeer::sendPacket(NetworkString *data, bool reliable, bool encrypted)
if (m_enet_peer->state != ENET_PEER_STATE_CONNECTED ||
a != m_peer_address)
return;
data->setToken(m_client_server_token);
ENetPacket* packet = NULL;
if (m_crypto && encrypted)

View File

@ -57,14 +57,11 @@ protected:
/** Pointer to the corresponding ENet peer data structure. */
ENetPeer* m_enet_peer;
/** The token of this client. */
std::atomic<uint32_t> m_client_server_token;
/** True if the token for this peer has been set. */
std::atomic_bool m_token_set;
/** True if this peer is validated by server. */
std::atomic_bool m_validated;
/** Host id of this peer. */
int m_host_id;
uint32_t m_host_id;
TransportAddress m_peer_address;
@ -108,27 +105,10 @@ public:
void addPlayer(std::shared_ptr<NetworkPlayerProfile> p)
{ m_players.push_back(p); }
// ------------------------------------------------------------------------
/** Sets the token for this client. */
void setClientServerToken(const uint32_t token)
{
m_client_server_token.store(token);
m_token_set.store(true);
} // setClientServerToken
void setValidated() { m_validated.store(true); }
// ------------------------------------------------------------------------
/** Unsets the token for this client. (used in server to invalidate peer)
*/
void unsetClientServerToken()
{
m_client_server_token.store(0);
m_token_set.store(false);
}
// ------------------------------------------------------------------------
/** Returns the token of this client. */
uint32_t getClientServerToken() const
{ return m_client_server_token.load(); }
// ------------------------------------------------------------------------
/** Returns if the token for this client is known. */
bool isClientServerTokenSet() const { return m_token_set.load(); }
/** Returns if the client is validated by server. */
bool isValidated() const { return m_validated.load(); }
// ------------------------------------------------------------------------
/** Returns the host id of this peer. */
uint32_t getHostId() const { return m_host_id; }