Use a more sophiscated server-client token validation
This commit is contained in:
@@ -63,15 +63,6 @@ Event::Event(ENetEvent* event, std::shared_ptr<STKPeer> peer)
|
||||
}
|
||||
|
||||
m_peer = peer;
|
||||
if(m_type == EVENT_TYPE_MESSAGE && m_peer->isClientServerTokenSet() &&
|
||||
m_data->getToken()!=m_peer->getClientServerToken() )
|
||||
{
|
||||
Log::error("Event", "Received event with invalid token!");
|
||||
Log::error("Event", "HostID %d Token %d message token %d",
|
||||
m_peer->getHostId(), m_peer->getClientServerToken(),
|
||||
m_data->getToken());
|
||||
Log::error("Event", m_data->getLogMessage().c_str());
|
||||
}
|
||||
} // Event(ENetEvent)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -435,7 +435,8 @@ void ClientLobby::connectionAccepted(Event* event)
|
||||
NetworkConfig::get()->getNetworkPlayers().size());
|
||||
// connection token
|
||||
uint32_t token = data.getToken();
|
||||
peer->setClientServerToken(token);
|
||||
if (!peer->isClientServerTokenSet())
|
||||
peer->setClientServerToken(token);
|
||||
m_state = CONNECTED;
|
||||
} // connectionAccepted
|
||||
|
||||
@@ -656,7 +657,6 @@ void ClientLobby::exitResultScreen(Event *event)
|
||||
setup();
|
||||
|
||||
RaceResultGUI::getInstance()->backToLobby();
|
||||
STKHost::get()->getServerPeerForClient()->unsetClientServerToken();
|
||||
} // exitResultScreen
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -38,8 +38,8 @@ class LobbyProtocol : public Protocol
|
||||
{
|
||||
public:
|
||||
/** Lists all lobby events (LE). */
|
||||
enum
|
||||
{
|
||||
enum : uint8_t
|
||||
{
|
||||
LE_CONNECTION_REQUESTED = 1, // a connection to the server
|
||||
LE_CONNECTION_REFUSED, // Connection to server refused
|
||||
LE_CONNECTION_ACCEPTED, // Connection to server accepted
|
||||
|
||||
@@ -913,16 +913,19 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
m_available_kts.second.erase(track_erase);
|
||||
}
|
||||
|
||||
// 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));
|
||||
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->setClientServerToken(token);
|
||||
}
|
||||
// send a message to the one that asked to connect
|
||||
NetworkString *message_ack = getNetworkString(4);
|
||||
// connection success -- return the host id of peer
|
||||
|
||||
@@ -707,10 +707,11 @@ void STKHost::mainLoop()
|
||||
Log::info("STKHost", "Listening has been started.");
|
||||
ENetEvent event;
|
||||
ENetHost* host = m_network->getENetHost();
|
||||
const bool is_server = NetworkConfig::get()->isServer();
|
||||
|
||||
// A separate network connection (socket) to handle LAN requests.
|
||||
Network* direct_socket = NULL;
|
||||
if ((NetworkConfig::get()->isLAN() && NetworkConfig::get()->isServer()) ||
|
||||
if ((NetworkConfig::get()->isLAN() && is_server) ||
|
||||
NetworkConfig::get()->isPublicServer())
|
||||
{
|
||||
TransportAddress address(0,
|
||||
@@ -734,6 +735,32 @@ void STKHost::mainLoop()
|
||||
handleDirectSocketRequest(direct_socket, sl);
|
||||
} // if discovery host
|
||||
|
||||
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
|
||||
for (auto it = m_peers.begin(); it != m_peers.end();)
|
||||
{
|
||||
if (!it->second->isClientServerTokenSet() &&
|
||||
(float)StkTime::getRealTime() >
|
||||
it->second->getConnectedTime() + 7.0f)
|
||||
{
|
||||
Log::info("STKHost", "%s has no token for more than 7"
|
||||
" seconds, disconnect it by force.",
|
||||
it->second->getAddress().toString().c_str());
|
||||
enet_host_flush(host);
|
||||
enet_peer_reset(it->first);
|
||||
it = m_peers.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
peer_lock.unlock();
|
||||
}
|
||||
|
||||
std::list<std::tuple<ENetPeer*, ENetPacket*, uint32_t,
|
||||
ENetCommandType> > copied_list;
|
||||
std::unique_lock<std::mutex> lock(m_enet_cmd_mutex);
|
||||
@@ -805,7 +832,39 @@ void STKHost::mainLoop()
|
||||
|
||||
if (!stk_event && m_peers.find(event.peer) != m_peers.end())
|
||||
{
|
||||
stk_event = new Event(&event, m_peers.at(event.peer));
|
||||
auto& peer = m_peers.at(event.peer);
|
||||
unsigned token = 0;
|
||||
// Token is after the protocol type (1 byte) in stk network
|
||||
// string (network order)
|
||||
token += event.packet->data[1];
|
||||
token <<= 8;
|
||||
token += event.packet->data[2];
|
||||
token <<= 8;
|
||||
token += event.packet->data[3];
|
||||
token <<= 8;
|
||||
token += event.packet->data[4];
|
||||
|
||||
if (is_server && ((!peer->isClientServerTokenSet() &&
|
||||
!isConnectionRequestPacket(event.packet->data,
|
||||
(int)event.packet->dataLength)) ||
|
||||
(token != peer->getClientServerToken())))
|
||||
{
|
||||
// For server discard all events from wrong or unset token
|
||||
// peers if that is not a connection request
|
||||
if (token != peer->getClientServerToken())
|
||||
{
|
||||
Log::error("STKHost", "Received event with invalid token!");
|
||||
Log::error("STKHost", "HostID %d Token %d message token %d",
|
||||
peer->getHostId(), peer->getClientServerToken(), token);
|
||||
NetworkString wrong_event(event.packet->data,
|
||||
(int)event.packet->dataLength);
|
||||
Log::error("STKHost", wrong_event.getLogMessage().c_str());
|
||||
peer->unsetClientServerToken();
|
||||
}
|
||||
enet_packet_destroy(event.packet);
|
||||
continue;
|
||||
}
|
||||
stk_event = new Event(&event, peer);
|
||||
}
|
||||
else if (!stk_event)
|
||||
{
|
||||
@@ -980,7 +1039,8 @@ void STKHost::sendPacketToAllPeers(NetworkString *data, bool reliable)
|
||||
std::lock_guard<std::mutex> lock(m_peers_mutex);
|
||||
for (auto p : m_peers)
|
||||
{
|
||||
p.second->sendPacket(data, reliable);
|
||||
if (p.second->isClientServerTokenSet())
|
||||
p.second->sendPacket(data, reliable);
|
||||
}
|
||||
} // sendPacketExcept
|
||||
|
||||
@@ -997,7 +1057,7 @@ void STKHost::sendPacketExcept(STKPeer* peer, NetworkString *data,
|
||||
for (auto p : m_peers)
|
||||
{
|
||||
STKPeer* stk_peer = p.second.get();
|
||||
if (!stk_peer->isSamePeer(peer))
|
||||
if (!stk_peer->isSamePeer(peer) && p.second->isClientServerTokenSet())
|
||||
{
|
||||
stk_peer->sendPacket(data, reliable);
|
||||
}
|
||||
@@ -1059,3 +1119,13 @@ 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
|
||||
|
||||
@@ -144,6 +144,8 @@ private:
|
||||
std::shared_ptr<ServerLobby> sl);
|
||||
// ------------------------------------------------------------------------
|
||||
void mainLoop();
|
||||
// ------------------------------------------------------------------------
|
||||
bool isConnectionRequestPacket(unsigned char* data, int length);
|
||||
|
||||
public:
|
||||
/** If a network console should be started. */
|
||||
|
||||
@@ -34,10 +34,10 @@ STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
|
||||
: m_peer_address(enet_peer->address), m_host(host)
|
||||
{
|
||||
m_enet_peer = enet_peer;
|
||||
m_client_server_token = 0;
|
||||
m_host_id = host_id;
|
||||
m_token_set = false;
|
||||
m_connected_time = (float)StkTime::getRealTime();
|
||||
m_token_set.store(false);
|
||||
m_client_server_token.store(0);
|
||||
} // STKPeer
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -55,10 +56,10 @@ protected:
|
||||
ENetPeer* m_enet_peer;
|
||||
|
||||
/** The token of this client. */
|
||||
uint32_t m_client_server_token;
|
||||
std::atomic_uint32_t m_client_server_token;
|
||||
|
||||
/** True if the token for this peer has been set. */
|
||||
bool m_token_set;
|
||||
std::atomic_bool m_token_set;
|
||||
|
||||
/** Host id of this peer. */
|
||||
int m_host_id;
|
||||
@@ -99,28 +100,31 @@ public:
|
||||
{ m_players.push_back(p); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the token for this client. */
|
||||
void setClientServerToken(const uint32_t& token)
|
||||
void setClientServerToken(const uint32_t token)
|
||||
{
|
||||
m_client_server_token = token;
|
||||
m_token_set = true;
|
||||
m_client_server_token.store(token);
|
||||
m_token_set.store(true);
|
||||
} // setClientServerToken
|
||||
// ------------------------------------------------------------------------
|
||||
/** Unsets the token for this client. (used in server to invalidate peer)
|
||||
*/
|
||||
void unsetClientServerToken()
|
||||
{
|
||||
m_token_set = false;
|
||||
m_client_server_token = 0;
|
||||
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; }
|
||||
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; }
|
||||
bool isClientServerTokenSet() const { return m_token_set.load(); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the host id of this peer. */
|
||||
uint32_t getHostId() const { return m_host_id; }
|
||||
uint32_t getHostId() const { return m_host_id; }
|
||||
// ------------------------------------------------------------------------
|
||||
float getConnectedTime() const { return m_connected_time; }
|
||||
float getConnectedTime() const { return m_connected_time; }
|
||||
// ------------------------------------------------------------------------
|
||||
uint32_t getPing() const;
|
||||
|
||||
|
||||
@@ -104,7 +104,9 @@ void ServerSelection::beforeAddingWidget()
|
||||
m_server_list_widget->addColumn(_("Game mode"), 2);
|
||||
if (NetworkConfig::get()->isWAN())
|
||||
{
|
||||
m_server_list_widget->addColumn(_("Server owner"), 1);
|
||||
// I18N: In server selection screen, owner of server, only displayed
|
||||
// if it's localhost or friends'
|
||||
m_server_list_widget->addColumn(_("Owner"), 1);
|
||||
// I18N: In server selection screen, distance to server
|
||||
m_server_list_widget->addColumn(_("Distance"), 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user