Skip to content

Commit

Permalink
Validate client version on connect handshake
Browse files Browse the repository at this point in the history
  • Loading branch information
zpl-zak committed Jan 18, 2022
1 parent d507225 commit 9ab9396
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 5 deletions.
18 changes: 17 additions & 1 deletion code/framework/src/integrations/client/instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@

#include <networking/messages/client_handshake.h>
#include <networking/messages/client_connection_finalized.h>
#include <networking/messages/client_kick.h>
#include <networking/messages/game_sync/entity_messages.h>

#include <logging/logger.h>

#include "utils/version.h"

namespace Framework::Integrations::Client {
Instance::Instance() {
_networkingEngine = std::make_unique<Networking::Engine>();
Expand Down Expand Up @@ -131,7 +134,7 @@ namespace Framework::Integrations::Client {
Logging::GetLogger(FRAMEWORK_INNER_CLIENT)->debug("Connection accepted by server, sending handshake");

ClientHandshake msg;
msg.FromParameters(_currentState._nickname, "MY_SUPER_ID_1", "MY_SUPER_ID_2");
msg.FromParameters(_currentState._nickname, "MY_SUPER_ID_1", "MY_SUPER_ID_2", Utils::Version::rel);

net->Send(msg, SLNet::UNASSIGNED_RAKNET_GUID);
});
Expand All @@ -144,6 +147,19 @@ namespace Framework::Integrations::Client {
_onConnectionFinalized(msg->GetEntityID());
}
});
net->RegisterMessage<ClientKick>(GameMessages::GAME_CONNECTION_KICKED, [this, net](SLNet::RakNetGUID guid, ClientKick *msg) {
std::string reason = "Unknown.";

switch (msg->GetDisconnectionReason()) {
case Framework::Networking::Messages::DisconnectionReason::BANNED: reason = "You are banned."; break;
case Framework::Networking::Messages::DisconnectionReason::KICKED: reason = "You have been kicked."; break;
case Framework::Networking::Messages::DisconnectionReason::KICKED_INVALID_PACKET: reason = "You have been kicked (invalid packet)."; break;
case Framework::Networking::Messages::DisconnectionReason::WRONG_VERSION: reason = "You have been kicked (wrong client version)."; break;
case Framework::Networking::Messages::DisconnectionReason::INVALID_PASSWORD: reason = "You have been kicked (wrong password)."; break;
default: break;
}
Logging::GetLogger(FRAMEWORK_INNER_CLIENT)->debug("Connection dropped: {}", reason);
});
net->SetOnPlayerDisconnectedCallback([this](SLNet::Packet *packet, uint32_t reasonId) {
_worldEngine->OnDisconnect();

Expand Down
14 changes: 14 additions & 0 deletions code/framework/src/integrations/server/instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
#include <networking/messages/client_connection_finalized.h>
#include <networking/messages/client_handshake.h>
#include <networking/messages/game_sync/entity_client_update.h>
#include <networking/messages/client_kick.h>
#include <networking/messages/messages.h>

#include "utils/version.h"

#include <cxxopts.hpp>
#include <nlohmann/json.hpp>
#include <optick.h>
Expand Down Expand Up @@ -204,6 +207,17 @@ namespace Framework::Integrations::Server {
return;
}

const auto clientVersion = msg->GetClientVersion();

if (!Utils::Version::VersionSatisfies(clientVersion.c_str(), Utils::Version::rel)) {
Logging::GetLogger(FRAMEWORK_INNER_SERVER)->error("Client has invalid version, force-disconnecting peer");
Framework::Networking::Messages::ClientKick kick;
kick.FromParameters(Framework::Networking::Messages::DisconnectionReason::WRONG_VERSION);
net->Send(kick, guid);
net->GetPeer()->CloseConnection(guid, true);
return;
}

// Create player entity and add on world
const auto newPlayer = _worldEngine->CreateEntity();
auto newPlayerEntity = _playerFactory->SetupServer(newPlayer, guid.g);
Expand Down
11 changes: 9 additions & 2 deletions code/framework/src/networking/messages/client_handshake.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,29 @@ namespace Framework::Networking::Messages {
SLNet::RakString _playerName = "";
SLNet::RakString _playerSteamId = "";
SLNet::RakString _playerDiscordId = "";
SLNet::RakString _clientVersion = "";

public:
uint8_t GetMessageID() const override {
return GAME_CONNECTION_HANDSHAKE;
}

void FromParameters(std::string playerName, std::string playerSteamId, std::string playerDiscordId) {
void FromParameters(std::string playerName, std::string playerSteamId, std::string playerDiscordId, std::string clientVersion) {
_playerName = SLNet::RakString(playerName.c_str());
_playerSteamId = SLNet::RakString(playerSteamId.c_str());
_playerDiscordId = SLNet::RakString(playerDiscordId.c_str());
_clientVersion = SLNet::RakString(clientVersion.c_str());
}

void Serialize(SLNet::BitStream *bs, bool write) override {
bs->Serialize(write, _playerName);
bs->Serialize(write, _playerSteamId);
bs->Serialize(write, _playerDiscordId);
bs->Serialize(write, _clientVersion);
}

bool Valid() override {
return _playerName.GetLength() > 0 && (_playerSteamId.GetLength() > 0 || _playerDiscordId.GetLength() > 0);
return _playerName.GetLength() > 0 && (_playerSteamId.GetLength() > 0 || _playerDiscordId.GetLength() > 0) && _clientVersion.GetLength() > 0;
}

std::string GetPlayerName() {
Expand All @@ -51,5 +54,9 @@ namespace Framework::Networking::Messages {
std::string GetPlayerDiscordID() {
return _playerDiscordId.C_String();
}

std::string GetClientVersion() {
return _clientVersion.C_String();
}
};
} // namespace Framework::Networking::Messages
43 changes: 43 additions & 0 deletions code/framework/src/networking/messages/client_kick.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* MafiaHub OSS license
* Copyright (c) 2022, MafiaHub. All rights reserved.
*
* This file comes from MafiaHub, hosted at https://github.com/MafiaHub/Framework.
* See LICENSE file in the source repository for information regarding licensing.
*/

#pragma once

#include "messages.h"

#include <BitStream.h>

#include <flecs/flecs.h>

namespace Framework::Networking::Messages {
class ClientKick final: public IMessage {
private:
DisconnectionReason _reason;

public:
uint8_t GetMessageID() const override {
return GAME_CONNECTION_KICKED;
}

void FromParameters(DisconnectionReason reason) {
_reason = reason;
}

void Serialize(SLNet::BitStream *bs, bool write) override {
bs->Serialize(write, _reason);
}

bool Valid() override {
return true;
}

DisconnectionReason GetDisconnectionReason() const {
return _reason;
}
};
} // namespace Framework::Networking::Messages
3 changes: 2 additions & 1 deletion code/framework/src/networking/messages/messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ namespace Framework::Networking::Messages {
using PacketCallback = std::function<void(SLNet::Packet *)>;
using DisconnectPacketCallback = std::function<void(SLNet::Packet *, uint32_t reason)>;

enum DisconnectionReason { NO_FREE_SLOT, GRACEFUL_SHUTDOWN, LOST, FAILED, INVALID_PASSWORD, BANNED, KICKED, KICKED_INVALID_PACKET, UNKNOWN };
enum DisconnectionReason { NO_FREE_SLOT, GRACEFUL_SHUTDOWN, LOST, FAILED, INVALID_PASSWORD, WRONG_VERSION, BANNED, KICKED, KICKED_INVALID_PACKET, UNKNOWN };

enum InternalMessages : uint8_t { INTERNAL_RPC = ID_USER_PACKET_ENUM + 1, INTERNAL_NEXT_MESSAGE_ID };
enum GameMessages : uint8_t {
GAME_CONNECTION_HANDSHAKE = INTERNAL_NEXT_MESSAGE_ID,
GAME_CONNECTION_FINALIZED,
GAME_CONNECTION_KICKED,
GAME_SYNC_ENTITY_SPAWN,
GAME_SYNC_ENTITY_UPDATE,
GAME_SYNC_ENTITY_CLIENT_UPDATE, // client -> server
Expand Down
2 changes: 1 addition & 1 deletion code/framework/src/utils/version.cpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace Framework::Utils::Version {
const char *rel = FW_VERSION;

static bool BuildVersion(const char *ver, semver_t &ctx) {
return semver_parse(ver, &ctx);
return semver_parse(ver, &ctx) == 0;
}

bool VersionSatisfies(const char *a, const char *b) {
Expand Down

0 comments on commit 9ab9396

Please sign in to comment.