Add AES encryption and validation for wan player

This commit is contained in:
Benau 2018-06-02 12:28:29 +08:00
parent 7cb0ffe844
commit b43a5e4d18
31 changed files with 789 additions and 313 deletions

View File

@ -460,11 +460,15 @@ endif()
# CURL
if(MSVC)
target_link_libraries(supertuxkart ${PROJECT_SOURCE_DIR}/${DEPENDENCIES}/lib/libcurl.lib)
target_link_libraries(supertuxkart ${PROJECT_SOURCE_DIR}/${DEPENDENCIES}/lib/libeay32.lib)
elseif(MINGW)
target_link_libraries(supertuxkart ${PROJECT_SOURCE_DIR}/${DEPENDENCIES}/lib/libcurldll.a)
target_link_libraries(supertuxkart ${PROJECT_SOURCE_DIR}/${DEPENDENCIES}/lib/libeay32.dll)
else()
find_package(CURL REQUIRED)
find_package(OpenSSL REQUIRED)
include_directories(${CURL_INCLUDE_DIRS})
include_directories(${OpenSSL_INCLUDE_DIRS})
endif()
# Common library dependencies
@ -481,7 +485,7 @@ target_link_libraries(supertuxkart
${FREETYPE_LIBRARIES}
${JPEG_LIBRARIES}
${TURBOJPEG_LIBRARY}
#${VPX_LIBRARIES}
${OPENSSL_CRYPTO_LIBRARY}
)
if(NOT SERVER_ONLY)

View File

@ -158,6 +158,7 @@ LOCAL_CFLAGS := -I../lib/angelscript/include \
-Iobj/libogg/include \
-Iobj/libvorbis/include \
-Iobj/openal/include \
-Iobj/openssl/include \
-I$(call my-dir)/../../sources/android/native_app_glue \
-DUSE_GLES2 \
-DHAVE_OGGVORBIS \

View File

@ -1,5 +1,5 @@
# Modify this file to change the last-modified date when you add/remove a file.
# This will then trigger a new cmake run automatically.
# This will then trigger a new cmake run automatically.
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")

View File

@ -1110,8 +1110,7 @@ int handleCmdLine()
}
else
{
auto cl = LobbyProtocol::create<ClientLobby>();
cl->setAddress(server_addr);
auto cl = LobbyProtocol::create<ClientLobby>(server_addr, server);
cl->requestStart();
}
}

154
src/network/crypto.cpp Normal file
View File

@ -0,0 +1,154 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2018 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/crypto.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp"
#include <openssl/aes.h>
// ============================================================================
std::string Crypto::m_client_key;
std::string Crypto::m_client_iv;
// ============================================================================
bool Crypto::encryptConnectionRequest(BareNetworkString& ns)
{
std::vector<uint8_t> cipher(ns.m_buffer.size() + 4, 0);
int elen;
if (EVP_EncryptInit_ex(m_encrypt, NULL, NULL, NULL, NULL) != 1)
return false;
if (EVP_EncryptUpdate(m_encrypt, cipher.data() + 4, &elen,
ns.m_buffer.data(), ns.m_buffer.size()) != 1)
return false;
if (EVP_EncryptFinal_ex(m_encrypt, cipher.data() + 4 + elen, &elen) != 1)
return false;
if (EVP_CIPHER_CTX_ctrl(m_encrypt, EVP_CTRL_GCM_GET_TAG, 4, cipher.data())
!= 1)
return false;
std::swap(ns.m_buffer, cipher);
return true;
} // encryptConnectionRequest
// ----------------------------------------------------------------------------
bool Crypto::decryptConnectionRequest(BareNetworkString& ns)
{
std::vector<uint8_t> pt(ns.m_buffer.size() - 4, 0);
if (EVP_DecryptInit_ex(m_decrypt, NULL, NULL, NULL, NULL) != 1)
return false;
int dlen;
if (EVP_DecryptUpdate(m_decrypt, pt.data(), &dlen, ns.m_buffer.data() + 4,
ns.m_buffer.size() - 4) != 1)
return false;
if (!EVP_CIPHER_CTX_ctrl(m_decrypt, EVP_CTRL_GCM_SET_TAG, 4,
ns.m_buffer.data()))
return false;
if (!(EVP_DecryptFinal_ex(m_decrypt, pt.data() + dlen, &dlen) > 0))
{
assert(dlen == 0);
return false;
}
std::swap(ns.m_buffer, pt);
return true;
} // decryptConnectionRequest
// ----------------------------------------------------------------------------
ENetPacket* Crypto::encryptSend(BareNetworkString& ns, bool reliable)
{
// 4 bytes counter and 4 bytes tag
ENetPacket* p = enet_packet_create(NULL, ns.m_buffer.size() + 8,
(reliable ? ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED));
if (p == NULL)
return NULL;
std::array<uint8_t, 12> iv = m_iv;
std::unique_lock<std::mutex> ul(m_crypto_mutex);
uint32_t val = NetworkConfig::get()->isClient() ?
m_packet_counter++ : m_packet_counter--;
memcpy(iv.data(), &val, 4);
uint8_t* packet_start = p->data + 8;
if (EVP_EncryptInit_ex(m_encrypt, NULL, NULL, NULL, iv.data()) != 1)
{
enet_packet_destroy(p);
return NULL;
}
int elen;
if (EVP_EncryptUpdate(m_encrypt, packet_start, &elen, ns.m_buffer.data(),
ns.m_buffer.size()) != 1)
{
enet_packet_destroy(p);
return NULL;
}
if (EVP_EncryptFinal_ex(m_encrypt, packet_start, &elen) != 1)
{
enet_packet_destroy(p);
return NULL;
}
if (EVP_CIPHER_CTX_ctrl(m_encrypt, EVP_CTRL_GCM_GET_TAG, 4, p->data + 4)
!= 1)
{
enet_packet_destroy(p);
return NULL;
}
ul.unlock();
memcpy(p->data, iv.data(), 4);
return p;
} // encryptSend
// ----------------------------------------------------------------------------
NetworkString* Crypto::decryptRecieve(ENetPacket* p)
{
int clen = (int)(p->dataLength - 8);
auto ns = std::unique_ptr<NetworkString>(new NetworkString(p->data, clen));
std::array<uint8_t, 12> iv = m_iv;
memcpy(iv.data(), p->data, 4);
uint8_t* packet_start = p->data + 8;
uint8_t* tag = p->data + 4;
if (EVP_DecryptInit_ex(m_decrypt, NULL, NULL, NULL, iv.data()) != 1)
{
throw std::runtime_error("Failed to set IV.");
}
int dlen;
if (EVP_DecryptUpdate(m_decrypt, ns->m_buffer.data(), &dlen,
packet_start, clen) != 1)
{
throw std::runtime_error("Failed to decrypt.");
}
if (!EVP_CIPHER_CTX_ctrl(m_decrypt, EVP_CTRL_GCM_SET_TAG, 4, tag))
{
throw std::runtime_error("Failed to set tag.");
}
if (EVP_DecryptFinal_ex(m_decrypt, ns->m_buffer.data(), &dlen) > 0)
{
assert(dlen == 0);
NetworkString* result = ns.get();
ns.release();
return result;
}
throw std::runtime_error("Failed to finalize decryption.");
} // decryptRecieve

134
src/network/crypto.hpp Normal file
View File

@ -0,0 +1,134 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2018 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_CRYPTO_HPP
#define HEADER_CRYPTO_HPP
#include "utils/string_utils.hpp"
#include <enet/enet.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <algorithm>
#include <array>
#include <cassert>
#include <memory>
#include <mutex>
#include <random>
#include <string>
#include <vector>
class BareNetworkString;
class NetworkString;
class Crypto
{
private:
static std::string m_client_key;
static std::string m_client_iv;
std::array<uint8_t, 12> m_iv;
uint32_t m_packet_counter;
EVP_CIPHER_CTX* m_encrypt;
EVP_CIPHER_CTX* m_decrypt;
std::mutex m_crypto_mutex;
public:
static std::unique_ptr<Crypto> getClientCrypto()
{
assert(!m_client_key.empty());
assert(!m_client_iv.empty());
auto c = std::unique_ptr<Crypto>(new Crypto(
StringUtils::decode64(m_client_key),
StringUtils::decode64(m_client_iv)));
c->m_packet_counter = 1;
return c;
}
// ------------------------------------------------------------------------
static void initClientAES()
{
std::random_device rd;
std::mt19937 g(rd());
// Default key and if RAND_bytes failed
std::vector<uint8_t> key;
for (int i = 0; i < 16; i++)
key.push_back((uint8_t)(g() % 255));
std::vector<uint8_t> iv;
for (int i = 0; i < 12; i++)
iv.push_back((uint8_t)(g() % 255));
if (!RAND_bytes(key.data(), 16))
{
Log::warn("Crypto",
"Failed to generate cryptographically strong key");
}
m_client_key = StringUtils::base64(key);
m_client_iv = StringUtils::base64(iv);
}
// ------------------------------------------------------------------------
static void resetClientAES()
{
m_client_key = "";
m_client_iv = "";
}
// ------------------------------------------------------------------------
static const std::string& getClientKey() { return m_client_key; }
// ------------------------------------------------------------------------
static const std::string& getClientIV() { return m_client_iv; }
// ------------------------------------------------------------------------
Crypto(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv)
{
assert(key.size() == 16);
assert(iv.size() == 12);
std::copy_n(iv.begin(), 12, m_iv.begin());
m_packet_counter = (uint32_t)-1;
m_encrypt = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(m_encrypt);
EVP_EncryptInit_ex(m_encrypt, EVP_aes_128_gcm(), NULL, key.data(),
iv.data());
m_decrypt = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(m_decrypt);
EVP_DecryptInit_ex(m_decrypt, EVP_aes_128_gcm(), NULL, key.data(),
iv.data());
}
// ------------------------------------------------------------------------
~Crypto()
{
EVP_CIPHER_CTX_free(m_encrypt);
EVP_CIPHER_CTX_free(m_decrypt);
}
// ------------------------------------------------------------------------
bool encryptConnectionRequest(BareNetworkString& ns);
// ------------------------------------------------------------------------
bool decryptConnectionRequest(BareNetworkString& ns);
// ------------------------------------------------------------------------
ENetPacket* encryptSend(BareNetworkString& ns, bool reliable);
// ------------------------------------------------------------------------
NetworkString* decryptRecieve(ENetPacket* p);
};
#endif // HEADER_CRYPTO_HPP

View File

@ -18,6 +18,7 @@
#include "network/event.hpp"
#include "network/crypto.hpp"
#include "network/stk_peer.hpp"
#include "utils/log.hpp"
#include "utils/time.hpp"
@ -31,6 +32,7 @@ Event::Event(ENetEvent* event, std::shared_ptr<STKPeer> peer)
{
m_arrival_time = (double)StkTime::getTimeSinceEpoch();
m_pdi = PDI_TIMEOUT;
m_peer = peer;
switch (event->type)
{
@ -50,8 +52,15 @@ Event::Event(ENetEvent* event, std::shared_ptr<STKPeer> peer)
}
if (m_type == EVENT_TYPE_MESSAGE)
{
m_data = new NetworkString(event->packet->data,
(int)event->packet->dataLength);
if (m_peer->getCrypto() && event->channelID == EVENT_CHANNEL_NORMAL)
{
m_data = m_peer->getCrypto()->decryptRecieve(event->packet);
}
else
{
m_data = new NetworkString(event->packet->data,
(int)event->packet->dataLength);
}
}
else
m_data = NULL;
@ -62,7 +71,6 @@ Event::Event(ENetEvent* event, std::shared_ptr<STKPeer> peer)
enet_packet_destroy(event->packet);
}
m_peer = peer;
} // Event(ENetEvent)
// ----------------------------------------------------------------------------
@ -70,9 +78,6 @@ Event::Event(ENetEvent* event, std::shared_ptr<STKPeer> peer)
*/
Event::~Event()
{
// Do not delete m_peer, it's a pointer to the enet data structure
// which is persistent.
m_peer = NULL;
delete m_data;
} // ~Event

View File

@ -44,6 +44,18 @@ enum EVENT_TYPE
EVENT_TYPE_DISCONNECTED,//!< A peer is disconnected
EVENT_TYPE_MESSAGE //!< A message between server and client protocols
};
/*!
* \enum EVENT_CHANNEL
* \brief Represents a list of channels stk used.
*/
enum EVENT_CHANNEL : uint8_t
{
EVENT_CHANNEL_NORMAL = 0, //!< Normal channel (encrypted if supported)
EVENT_CHANNEL_UNENCRYPTED = 1,//!< Unencrypted channel
EVENT_CHANNEL_COUNT = 2
};
enum PeerDisconnectInfo : unsigned int;
/*!

View File

@ -148,11 +148,21 @@ void NetworkConfig::setUserDetails(Online::XMLRequest* r,
const std::string& name)
{
assert(!m_cur_user_token.empty());
r->setApiURL(Online::API::SERVER_PATH, name);
r->setApiURL(Online::API::USER_PATH, name);
r->addParameter("userid", m_cur_user_id);
r->addParameter("token", m_cur_user_token);
} // setUserDetails
// ----------------------------------------------------------------------------
void NetworkConfig::setServerDetails(Online::XMLRequest* r,
const std::string& name)
{
assert(!m_cur_user_token.empty());
r->setApiURL(Online::API::SERVER_PATH, name);
r->addParameter("userid", m_cur_user_id);
r->addParameter("token", m_cur_user_token);
} // setServerDetails
// ----------------------------------------------------------------------------
core::stringw NetworkConfig::getModeName(unsigned id)
{

View File

@ -275,6 +275,8 @@ public:
// ------------------------------------------------------------------------
void setUserDetails(Online::XMLRequest* r, const std::string& name);
// ------------------------------------------------------------------------
void setServerDetails(Online::XMLRequest* r, const std::string& name);
// ------------------------------------------------------------------------
void setServerIdFile(const std::string& id) { m_server_id_file = id; }
// ------------------------------------------------------------------------
const std::string& getServerIdFile() const { return m_server_id_file; }

View File

@ -51,6 +51,7 @@ typedef unsigned char uchar;
class BareNetworkString
{
friend class Crypto;
private:
LEAK_CHECK();

View File

@ -26,6 +26,7 @@
#include "input/device_manager.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/linear_world.hpp"
#include "network/crypto.hpp"
#include "network/event.hpp"
#include "network/game_setup.hpp"
#include "network/network_config.hpp"
@ -33,10 +34,9 @@
#include "network/protocols/game_protocol.hpp"
#include "network/protocols/game_events_protocol.hpp"
#include "network/race_event_manager.hpp"
#include "network/server.hpp"
#include "network/stk_host.hpp"
#include "network/stk_peer.hpp"
#include "online/online_player_profile.hpp"
#include "online/online_profile.hpp"
#include "states_screens/networking_lobby.hpp"
#include "states_screens/network_kart_selection.hpp"
#include "states_screens/race_result_gui.hpp"
@ -66,10 +66,11 @@ engine.
*/
ClientLobby::ClientLobby() : LobbyProtocol(NULL)
ClientLobby::ClientLobby(const TransportAddress& a, std::shared_ptr<Server> s)
: LobbyProtocol(NULL)
{
m_server_address.clear();
m_server_address = a;
m_server = s;
setHandleDisconnections(true);
} // ClientLobby
@ -92,14 +93,6 @@ void ClientLobby::clearPlayers()
}
} // clearPlayers
//-----------------------------------------------------------------------------
/** Sets the address of the server.
*/
void ClientLobby::setAddress(const TransportAddress &address)
{
m_server_address = address;
} // setAddress
//-----------------------------------------------------------------------------
void ClientLobby::setup()
{
@ -263,39 +256,10 @@ void ClientLobby::update(int ticks)
break;
case LINKED:
{
NetworkString *ns = getNetworkString();
NetworkString* ns = getNetworkString();
ns->addUInt8(LE_CONNECTION_REQUESTED)
.addUInt8(NetworkConfig::m_server_version)
.encodeString(NetworkConfig::get()->getPassword());
.addUInt8(NetworkConfig::m_server_version);
assert(!NetworkConfig::get()->isAddingNetworkPlayers());
ns->addUInt8(
(uint8_t)NetworkConfig::get()->getNetworkPlayers().size());
// Only first player has online name and profile
bool first_player = true;
for (auto& p : NetworkConfig::get()->getNetworkPlayers())
{
core::stringw name;
PlayerProfile* player = std::get<1>(p);
if (PlayerManager::getCurrentOnlineState() ==
PlayerProfile::OS_SIGNED_IN && first_player)
{
name = PlayerManager::getCurrentOnlineUserName();
}
else
{
name = player->getName();
}
std::string name_u8 = StringUtils::wideToUtf8(name);
ns->encodeString(name_u8).addFloat(player->getDefaultKartColor());
Online::OnlinePlayerProfile* opp =
dynamic_cast<Online::OnlinePlayerProfile*>(player);
ns->addUInt32(first_player && opp && opp->getProfile() ?
opp->getProfile()->getID() : 0);
// Per-player handicap
ns->addUInt8(std::get<2>(p));
first_player = false;
}
auto all_k = kart_properties_manager->getAllAvailableKarts();
auto all_t = track_manager->getAllTrackIdentifiers();
if (all_k.size() >= 65536)
@ -311,9 +275,40 @@ void ClientLobby::update(int ticks)
{
ns->encodeString(track);
}
assert(!NetworkConfig::get()->isAddingNetworkPlayers());
const uint8_t player_count =
(uint8_t)NetworkConfig::get()->getNetworkPlayers().size();
ns->addUInt8(player_count);
sendToServer(ns);
delete ns;
bool encryption = false;
uint32_t id = PlayerManager::getCurrentOnlineId();
BareNetworkString* rest = new BareNetworkString();
if (m_server->supportsEncryption() && id != 0)
{
ns->addUInt32(id);
encryption = true;
}
else
{
ns->addUInt32(id).addUInt32(0);
if (id != 0)
ns->encodeString(PlayerManager::getCurrentOnlineUserName());
}
rest->encodeString(NetworkConfig::get()->getPassword())
.addUInt8(player_count);
for (auto& p : NetworkConfig::get()->getNetworkPlayers())
{
core::stringw name;
PlayerProfile* player = std::get<1>(p);
rest->encodeString(player->getName()).
addFloat(player->getDefaultKartColor());
// Per-player handicap
rest->addUInt8(std::get<2>(p));
}
finalizeConnectionRequest(ns, rest, encryption);
m_state.store(REQUESTING_CONNECTION);
}
break;
@ -336,6 +331,49 @@ void ClientLobby::update(int ticks)
}
} // update
//-----------------------------------------------------------------------------
void ClientLobby::finalizeConnectionRequest(NetworkString* header,
BareNetworkString* rest,
bool encrypt)
{
if (encrypt)
{
auto crypto = Crypto::getClientCrypto();
Crypto::resetClientAES();
BareNetworkString* result = new BareNetworkString();
if (!crypto->encryptConnectionRequest(*rest))
{
// Failed
result->addUInt32(0);
*result += BareNetworkString(rest->getData(), rest->getTotalSize());
encrypt = false;
}
else
{
Log::info("ClientLobby", "Server will validate this online player.");
result->addUInt32(rest->getTotalSize());
*result += BareNetworkString(rest->getData(), rest->getTotalSize());
}
delete rest;
*header += *result;
delete result;
sendToServer(header);
delete header;
if (encrypt)
{
STKHost::get()->getServerPeerForClient()
->setCrypto(std::move(crypto));
}
}
else
{
*header += *rest;
delete rest;
sendToServer(header);
delete header;
}
} // finalizeConnectionRequest
//-----------------------------------------------------------------------------
void ClientLobby::displayPlayerVote(Event* event)
{
@ -590,6 +628,10 @@ void ClientLobby::connectionRefused(Event* event)
STKHost::get()->setErrorMessage(
_("Connection refused: Server is full."));
break;
case RR_INVALID_PLAYER:
STKHost::get()->setErrorMessage(
_("Connection refused: Invalid player connecting."));
break;
}
STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/);
STKHost::get()->requestShutdown();

View File

@ -6,8 +6,12 @@
#include "utils/cpp2011.hpp"
#include <atomic>
#include <memory>
#include <set>
class BareNetworkString;
class Server;
class ClientLobby : public LobbyProtocol
{
private:
@ -29,6 +33,8 @@ private:
TransportAddress m_server_address;
std::shared_ptr<Server> m_server;
enum ClientState : unsigned int
{
NONE,
@ -51,11 +57,12 @@ private:
bool m_received_server_result = false;
void addAllPlayers(Event* event);
void finalizeConnectionRequest(NetworkString* header,
BareNetworkString* rest, bool encrypt);
public:
ClientLobby();
ClientLobby(const TransportAddress& a, std::shared_ptr<Server> s);
virtual ~ClientLobby();
void setAddress(const TransportAddress &address);
void doneWithResults();
bool receivedServerResult() { return m_received_server_result; }
void startingRaceNow();

View File

@ -19,11 +19,10 @@
#include "network/protocols/connect_to_server.hpp"
#include "config/user_config.hpp"
#include "network/crypto.hpp"
#include "network/event.hpp"
#include "network/network.hpp"
#include "network/network_config.hpp"
#include "network/protocols/get_peer_address.hpp"
#include "network/protocols/hide_public_address.hpp"
#include "network/protocols/request_connection.hpp"
#include "network/protocols/client_lobby.hpp"
#include "network/protocol_manager.hpp"
@ -118,19 +117,18 @@ void ConnectToServer::asynchronousUpdate()
}
servers.clear();
}
if (handleDirectConnect())
if (m_server->supportsEncryption())
{
STKHost::get()->setPublicAddress();
registerWithSTKServer();
}
// Assume official server is firewall-less so give it more time
// to directly connect
if (handleDirectConnect(m_server->isOfficial() ? 5000 : 2000))
return;
STKHost::get()->setPublicAddress();
// Set to DONE will stop STKHost is not connected
m_state = STKHost::get()->getPublicAddress().isUnset() ?
DONE : REGISTER_SELF_ADDRESS;
}
break;
case REGISTER_SELF_ADDRESS:
{
registerWithSTKServer(); // Register us with STK server
m_state = GOT_SERVER_ADDRESS;
DONE : GOT_SERVER_ADDRESS;
}
break;
case GOT_SERVER_ADDRESS:
@ -157,13 +155,10 @@ void ConnectToServer::asynchronousUpdate()
Log::info("ConnectToServer", "Connection request made");
if (m_server_address.isUnset())
{
// server data not correct, hide address and stop
m_state = HIDING_ADDRESS;
// server data not correct, stop
m_state = DONE;
Log::error("ConnectToServer", "Server address is %s",
m_server_address.toString().c_str());
auto hide_address = std::make_shared<HidePublicAddress>();
hide_address->requestStart();
m_current_protocol = hide_address;
return;
}
if (m_tried_connection++ > 7)
@ -221,8 +216,7 @@ void ConnectToServer::asynchronousUpdate()
{
Log::error("ConnectToServer", "Timeout connect to %s",
m_server_address.toString().c_str());
m_state = NetworkConfig::get()->isWAN() ?
HIDING_ADDRESS : DONE;
m_state = DONE;
}
}
break;
@ -230,26 +224,9 @@ void ConnectToServer::asynchronousUpdate()
case CONNECTED:
{
Log::info("ConnectToServer", "Connected");
// LAN networking does not use the stk server tables.
if (NetworkConfig::get()->isWAN() &&
!STKHost::get()->isClientServer() &&
!STKHost::get()->getPublicAddress().isUnset())
{
auto hide_address = std::make_shared<HidePublicAddress>();
hide_address->requestStart();
m_current_protocol = hide_address;
}
m_state = HIDING_ADDRESS;
break;
}
case HIDING_ADDRESS:
// Wait till we have hidden our address
if (!m_current_protocol.expired())
{
return;
}
m_state = DONE;
break;
}
case DONE:
case EXITING:
break;
@ -278,8 +255,8 @@ void ConnectToServer::update(int ticks)
{
// Let main thread create ClientLobby for better
// synchronization with GUI
auto cl = LobbyProtocol::create<ClientLobby>();
cl->setAddress(m_server_address);
auto cl = LobbyProtocol::create<ClientLobby>(m_server_address,
m_server);
cl->requestStart();
}
if (STKHost::get()->getPeerCount() == 0)
@ -311,7 +288,8 @@ bool ConnectToServer::handleDirectConnect(int timeout)
ENetAddress ea;
ea.host = STKHost::HOST_ANY;
ea.port = STKHost::PORT_ANY;
Network* dc = new Network(/*peer_count*/1, /*channel_limit*/2,
Network* dc = new Network(/*peer_count*/1,
/*channel_limit*/EVENT_CHANNEL_COUNT,
/*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea,
true/*change_port_if_bound*/);
assert(dc);
@ -367,13 +345,17 @@ void ConnectToServer::registerWithSTKServer()
// STK server.
const TransportAddress& addr = STKHost::get()->getPublicAddress();
Online::XMLRequest *request = new Online::XMLRequest();
NetworkConfig::get()->setUserDetails(request, "set");
NetworkConfig::get()->setServerDetails(request, "join-server-key");
request->addParameter("server-id", m_server->getServerId());
request->addParameter("address", addr.getIP());
request->addParameter("port", addr.getPort());
request->addParameter("private_port", STKHost::get()->getPrivatePort());
Crypto::initClientAES();
request->addParameter("aes-key", Crypto::getClientKey());
request->addParameter("iv", Crypto::getClientIV());
Log::info("ConnectToServer", "Registering addr %s",
addr.toString().c_str());
addr.toString().c_str());
// This can be done blocking: till we are registered with the
// stk server, there is no need to to react to any other

View File

@ -43,12 +43,10 @@ private:
enum ConnectState : unsigned int
{
SET_PUBLIC_ADDRESS,
REGISTER_SELF_ADDRESS,
GOT_SERVER_ADDRESS,
REQUESTING_CONNECTION,
CONNECTING,
CONNECTED,
HIDING_ADDRESS,
DONE,
EXITING
};
@ -64,7 +62,7 @@ public:
virtual void setup() OVERRIDE;
virtual void asynchronousUpdate() OVERRIDE;
virtual void update(int ticks) OVERRIDE;
bool handleDirectConnect(int timeout = 2000);
bool handleDirectConnect(int timeout);
}; // class ConnectToServer

View File

@ -41,7 +41,7 @@ void GetPeerAddress::setup()
{
m_address.clear();
m_request = new Online::XMLRequest();
NetworkConfig::get()->setUserDetails(m_request, "get");
NetworkConfig::get()->setServerDetails(m_request, "get");
m_request->addParameter("peer_id", m_peer_id);
Online::RequestManager::get()->addRequest(m_request);

View File

@ -41,7 +41,7 @@ void HidePublicAddress::asynchronousUpdate()
if (m_state == NONE)
{
m_request = new Online::XMLRequest();
NetworkConfig::get()->setUserDetails(m_request, "unset");
NetworkConfig::get()->setServerDetails(m_request, "unset");
Online::RequestManager::get()->addRequest(m_request);
m_state = REQUEST_PENDING;
}

View File

@ -20,11 +20,11 @@
#define LOBBY_PROTOCOL_HPP
#include "network/protocol.hpp"
#include "network/network_string.hpp"
class GameSetup;
class NetworkPlayerProfile;
#include <cassert>
#include <memory>
#include <vector>
@ -68,7 +68,8 @@ public:
RR_BANNED = 1,
RR_INCORRECT_PASSWORD = 2,
RR_INCOMPATIBLE_DATA = 3,
RR_TOO_MANY_PLAYERS = 4
RR_TOO_MANY_PLAYERS = 4,
RR_INVALID_PLAYER = 5
};
protected:
@ -83,12 +84,13 @@ protected:
public:
/** Creates either a client or server lobby protocol as a singleton. */
template<typename singleton> static std::shared_ptr<singleton> create()
template<typename Singleton, typename... Types>
static std::shared_ptr<Singleton> create(Types ...args)
{
assert(m_lobby.expired());
auto ret = std::make_shared<singleton>();
auto ret = std::make_shared<Singleton>(args...);
m_lobby = ret;
return std::dynamic_pointer_cast<singleton>(ret);
return std::dynamic_pointer_cast<Singleton>(ret);
} // create
// ------------------------------------------------------------------------

View File

@ -37,7 +37,6 @@ RequestConnection::RequestConnection(std::shared_ptr<Server> server)
: Protocol(PROTOCOL_SILENT)
{
m_server = server;
m_request = NULL;
} // RequestConnection
// ----------------------------------------------------------------------------
@ -132,55 +131,11 @@ void RequestConnection::asynchronousUpdate()
}
}
m_state = DONE;
}
else
{
m_request = new Online::XMLRequest();
NetworkConfig::get()->setUserDetails(m_request,
"request-connection");
m_request->addParameter("server_id", m_server->getServerId());
m_request->queue();
m_state = REQUEST_PENDING;
}
break;
}
case REQUEST_PENDING:
{
if (!m_request->isDone())
return;
const XMLNode * result = m_request->getXMLData();
std::string rec_success;
if(result->get("success", &rec_success))
{
if (rec_success == "yes")
{
Log::debug("RequestConnection",
"Connection Request made successfully.");
}
else
{
Log::error("RequestConnection",
"Fail to make a request to connecto to server %d",
m_server->getServerId());
}
}
else
{
Log::error("RequestConnection", "Fail to make a request.");
}
m_state = DONE;
break;
}
case DONE:
m_state = EXITING;
delete m_request;
m_request = NULL;
requestTerminate();
break;
}
case EXITING:
break;
}

View File

@ -12,14 +12,9 @@ class RequestConnection : public Protocol
protected:
/** Id of the server to join. */
std::shared_ptr<Server> m_server;
/** The request to join a server. */
Online::XMLRequest *m_request;
enum STATE
{
NONE,
REQUEST_PENDING,
DONE,
EXITING
};

View File

@ -21,6 +21,7 @@
#include "config/user_config.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/linear_world.hpp"
#include "network/crypto.hpp"
#include "network/event.hpp"
#include "network/game_setup.hpp"
#include "network/network_config.hpp"
@ -293,6 +294,7 @@ void ServerLobby::asynchronousUpdate()
// Only poll the STK server if this is a WAN server.
if (NetworkConfig::get()->isWAN())
checkIncomingConnectionRequests();
handlePendingConnection();
break;
}
case ERROR_LEAVE:
@ -486,7 +488,7 @@ void ServerLobby::update(int ticks)
void ServerLobby::registerServer()
{
Online::XMLRequest *request = new Online::XMLRequest();
NetworkConfig::get()->setUserDetails(request, "create");
NetworkConfig::get()->setServerDetails(request, "create");
request->addParameter("address", m_server_address.getIP() );
request->addParameter("port", m_server_address.getPort() );
request->addParameter("private_port",
@ -531,7 +533,7 @@ void ServerLobby::registerServer()
void ServerLobby::unregisterServer()
{
Online::XMLRequest* request = new Online::XMLRequest();
NetworkConfig::get()->setUserDetails(request, "stop");
NetworkConfig::get()->setServerDetails(request, "stop");
request->addParameter("address", m_server_address.getIP());
request->addParameter("port", m_server_address.getPort());
@ -671,7 +673,7 @@ void ServerLobby::checkIncomingConnectionRequests()
// Now poll the stk server
last_poll_time = StkTime::getRealTime();
Online::XMLRequest* request = new Online::XMLRequest();
NetworkConfig::get()->setUserDetails(request, "poll-connection-requests");
NetworkConfig::get()->setServerDetails(request, "poll-connection-requests");
const TransportAddress &addr = STKHost::get()->getPublicAddress();
request->addParameter("address", addr.getIP() );
@ -692,13 +694,19 @@ void ServerLobby::checkIncomingConnectionRequests()
// Now start a ConnectToPeer protocol for each connection request
const XMLNode * users_xml = result->getNode("users");
uint32_t id = 0;
for (unsigned int i = 0; i < users_xml->getNumNodes(); i++)
{
uint32_t addr, id;
uint16_t port;
users_xml->getNode(i)->get("ip", &addr);
users_xml->getNode(i)->get("port", &port);
users_xml->getNode(i)->get("id", &id);
Log::debug("ServerLobby",
"User with id %d wants to connect.", id);
std::make_shared<ConnectToPeer>(id)->requestStart();
users_xml->getNode(i)->get("aes-key", &std::get<0>(m_keys[id]));
users_xml->getNode(i)->get("iv", &std::get<1>(m_keys[id]));
users_xml->getNode(i)->get("username", &std::get<2>(m_keys[id]));
std::get<3>(m_keys[id]) = false;
std::make_shared<ConnectToPeer>(TransportAddress(addr, port))
->requestStart();
}
delete request;
} // checkIncomingConnectionRequests
@ -817,25 +825,13 @@ void ServerLobby::clientDisconnected(Event* event)
} // clientDisconnected
//-----------------------------------------------------------------------------
/*! \brief Called when a player asks for a connection.
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1
* ---------------------
* Size | 1 |1| |
* Data | 4 |n| player name |
* ---------------------
*/
void ServerLobby::connectionRequested(Event* event)
{
std::lock_guard<std::mutex> lock(m_connection_mutex);
std::shared_ptr<STKPeer> peer = event->getPeerSP();
NetworkString& data = event->data();
peer->cleanPlayerProfiles();
const NetworkString &data = event->data();
// can we add the player ?
if (m_state != ACCEPTING_CLIENTS ||
m_game_setup->isGrandPrixStarted())
@ -843,7 +839,7 @@ void ServerLobby::connectionRequested(Event* event)
NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_BUSY);
// send only to the peer that made the request and disconect it now
peer->sendPacket(message);
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player refused: selection started");
@ -858,91 +854,13 @@ void ServerLobby::connectionRequested(Event* event)
NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED)
.addUInt8(RR_INCOMPATIBLE_DATA);
peer->sendPacket(message);
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player refused: wrong server version");
return;
}
// Check for password
std::string password;
data.decodeString(&password);
if (password != NetworkConfig::get()->getPassword())
{
NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED)
.addUInt8(RR_INCORRECT_PASSWORD);
peer->sendPacket(message);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player refused: incorrect password");
return;
}
unsigned player_count = data.getUInt8();
if (m_game_setup->getPlayerCount() + player_count >
NetworkConfig::get()->getMaxPlayers())
{
NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_TOO_MANY_PLAYERS);
peer->sendPacket(message);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player refused: too many players");
return;
}
for (unsigned i = 0; i < player_count; i++)
{
std::string name_u8;
data.decodeString(&name_u8);
core::stringw name = StringUtils::utf8ToWide(name_u8);
float default_kart_color = data.getFloat();
uint32_t online_id = data.getUInt32();
PerPlayerDifficulty per_player_difficulty =
(PerPlayerDifficulty)data.getUInt8();
peer->addPlayer(std::make_shared<NetworkPlayerProfile>
(peer, name, peer->getHostId(), default_kart_color, online_id,
per_player_difficulty, (uint8_t)i));
}
bool is_banned = false;
auto ret = m_ban_list.find(peer->getAddress().getIP());
if (ret != m_ban_list.end())
{
// Ban all players
if (ret->second == 0)
{
is_banned = true;
}
else
{
for (auto& p : peer->getPlayerProfiles())
{
if (ret->second == p->getOnlineId())
{
is_banned = true;
break;
}
}
}
}
if (is_banned)
{
NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_BANNED);
peer->cleanPlayerProfiles();
peer->sendPacket(message);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player refused: banned");
return;
}
// Connection accepted.
// ====================
std::set<std::string> client_karts, client_tracks;
const unsigned kart_num = data.getUInt16();
const unsigned track_num = data.getUInt16();
@ -984,7 +902,7 @@ void ServerLobby::connectionRequested(Event* event)
message->addUInt8(LE_CONNECTION_REFUSED)
.addUInt8(RR_INCOMPATIBLE_DATA);
peer->cleanPlayerProfiles();
peer->sendPacket(message);
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player has incompatible karts / tracks");
@ -995,6 +913,95 @@ void ServerLobby::connectionRequested(Event* event)
// disconnects later in lobby it won't affect current players
peer->setAvailableKartsTracks(client_karts, client_tracks);
unsigned player_count = data.getUInt8();
uint32_t online_id = 0;
uint32_t encrypted_size = 0;
online_id = data.getUInt32();
encrypted_size = data.getUInt32();
bool is_banned = false;
auto ret = m_ban_list.find(peer->getAddress().getIP());
if (ret != m_ban_list.end())
{
// Ban all players if ban list is zero or compare it with online id
if (ret->second == 0 || (online_id != 0 && ret->second == online_id))
{
is_banned = true;
}
}
if (is_banned)
{
NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_BANNED);
peer->cleanPlayerProfiles();
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player refused: banned");
return;
}
if (m_game_setup->getPlayerCount() + player_count >
NetworkConfig::get()->getMaxPlayers())
{
NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_TOO_MANY_PLAYERS);
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player refused: too many players");
return;
}
if (encrypted_size != 0)
{
m_pending_connection[peer] = std::make_pair(online_id,
BareNetworkString(data.getCurrentData(), encrypted_size));
}
else
{
core::stringw online_name;
if (online_id > 0)
data.decodeStringW(&online_name);
handleUnencryptedConnection(peer, data, online_id, online_name);
}
} // connectionRequested
//-----------------------------------------------------------------------------
void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
BareNetworkString& data, uint32_t online_id,
const core::stringw& online_name)
{
// Check for password
std::string password;
data.decodeString(&password);
if (password != NetworkConfig::get()->getPassword())
{
NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED)
.addUInt8(RR_INCORRECT_PASSWORD);
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player refused: incorrect password");
return;
}
unsigned player_count = data.getUInt8();
for (unsigned i = 0; i < player_count; i++)
{
core::stringw name;
data.decodeStringW(&name);
float default_kart_color = data.getFloat();
PerPlayerDifficulty per_player_difficulty =
(PerPlayerDifficulty)data.getUInt8();
peer->addPlayer(std::make_shared<NetworkPlayerProfile>
(peer, i == 0 && !online_name.empty() ? online_name : name,
peer->getHostId(), default_kart_color, i == 0 ? online_id : 0,
per_player_difficulty, (uint8_t)i));
}
if (!peer->isClientServerTokenSet())
{
// Now answer to the peer that just connected
@ -1037,7 +1044,7 @@ void ServerLobby::connectionRequested(Event* event)
npp->getOnlineId(), peer->getAddress().toString().c_str());
}
updatePlayerList();
} // connectionRequested
} // handleUnencryptedConnection
//-----------------------------------------------------------------------------
void ServerLobby::updatePlayerList()
@ -1400,3 +1407,56 @@ bool ServerLobby::waitingForPlayers() const
return m_state.load() == ACCEPTING_CLIENTS &&
!m_game_setup->isGrandPrixStarted();
} // waitingForPlayers
//-----------------------------------------------------------------------------
void ServerLobby::handlePendingConnection()
{
for (auto it = m_pending_connection.begin();
it != m_pending_connection.end();)
{
auto peer = it->first.lock();
if (!peer)
{
it = m_pending_connection.erase(it);
}
else
{
const uint32_t online_id = it->second.first;
auto key = m_keys.find(online_id);
if (key != m_keys.end() && std::get<3>(key->second) == false)
{
if (decryptConnectionRequest(peer, it->second.second,
std::get<0>(key->second), std::get<1>(key->second),
online_id, std::get<2>(key->second)))
{
it = m_pending_connection.erase(it);
m_keys.erase(online_id);
continue;
}
else
std::get<3>(key->second) = true;
}
it++;
}
}
} // handlePendingConnection
//-----------------------------------------------------------------------------
bool ServerLobby::decryptConnectionRequest(std::shared_ptr<STKPeer> peer,
BareNetworkString& data, const std::string& key, const std::string& iv,
uint32_t online_id, const core::stringw& online_name)
{
auto crypto = std::unique_ptr<Crypto>(new Crypto(
StringUtils::decode64(key), StringUtils::decode64(iv)));
if (crypto->decryptConnectionRequest(data))
{
peer->setCrypto(std::move(crypto));
std::lock_guard<std::mutex> lock(m_connection_mutex);
Log::info("ServerLobby", "%s validated",
StringUtils::wideToUtf8(online_name).c_str());
handleUnencryptedConnection(peer, data, online_id,
online_name);
return true;
}
return false;
} // decryptConnectionRequest

View File

@ -5,6 +5,9 @@
#include "network/transport_address.hpp"
#include "utils/cpp2011.hpp"
#include "irrString.h"
#include <array>
#include <atomic>
#include <map>
#include <memory>
@ -12,6 +15,7 @@
#include <set>
#include <tuple>
class BareNetworkString;
class STKPeer;
class ServerLobby : public LobbyProtocol
@ -78,6 +82,14 @@ private:
TransportAddress m_server_address;
std::map<uint32_t,
std::tuple</*aes 128 key*/std::string, /*iv*/std::string,
/*genuine player name*/irr::core::stringw, /*tried*/bool> > m_keys;
std::map<std::weak_ptr<STKPeer>,
std::pair<uint32_t, BareNetworkString>,
std::owner_less<std::weak_ptr<STKPeer> > > m_pending_connection;
// connection management
void clientDisconnected(Event* event);
void connectionRequested(Event* event);
@ -124,6 +136,17 @@ private:
}
}
}
void handlePendingConnection();
void handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
BareNetworkString& data,
uint32_t online_id,
const irr::core::stringw& online_name);
bool decryptConnectionRequest(std::shared_ptr<STKPeer> peer,
BareNetworkString& data,
const std::string& key,
const std::string& iv,
uint32_t online_id,
const irr::core::stringw& online_name);
std::tuple<std::string, uint8_t, bool, bool> handleVote();
void stopCurrentRace();

View File

@ -29,7 +29,7 @@
* \param xml The data for one server as received as part of the
* get-all stk-server request.
*/
Server::Server(const XMLNode& xml)
Server::Server(const XMLNode& xml) : m_supports_encrytion(true)
{
assert(xml.getName() == "server");
@ -65,9 +65,9 @@ Server::Server(const XMLNode& xml)
m_server_owner_name = L"-";
// Show owner name as Official right now if official server hoster account
bool official = false;
xml.get("official", &official);
if (official)
m_official = false;
xml.get("official", &m_official);
if (m_official)
{
// I18N: Official means this server is hosted by STK team
m_server_owner_name = _("Official");
@ -117,6 +117,7 @@ Server::Server(const XMLNode& xml)
Server::Server(unsigned server_id, const core::stringw &name, int max_players,
int current_players, unsigned difficulty, unsigned server_mode,
const TransportAddress &address, bool password_protected)
: m_supports_encrytion(false)
{
m_name = name;
m_lower_case_name = StringUtils::toLowerCase(StringUtils::wideToUtf8(name));
@ -132,6 +133,7 @@ Server::Server(unsigned server_id, const core::stringw &name, int max_players,
m_server_mode = server_mode;
m_password_protected = password_protected;
m_distance = 0.0f;
m_official = false;
} // server(server_id, ...)
// ----------------------------------------------------------------------------

View File

@ -80,6 +80,11 @@ protected:
/* WAN server only, distance based on IP latitude and longitude. */
float m_distance;
/* WAN server only, true if hosted officially by stk team. */
bool m_official;
bool m_supports_encrytion;
public:
/** Initialises the object from an XML node. */
@ -123,6 +128,10 @@ public:
{ return m_server_owner_name; }
// ------------------------------------------------------------------------
float getDistance() const { return m_distance; }
// ------------------------------------------------------------------------
bool supportsEncryption() const { return m_supports_encrytion; }
// ------------------------------------------------------------------------
bool isOfficial() const { return m_official; }
}; // Server
#endif // HEADER_SERVER_HPP

View File

@ -275,14 +275,15 @@ STKHost::STKHost(bool server)
addr.port = NetworkConfig::get()->getServerPort();
// Reserve 1 peer to deliver full server message
m_network = new Network(NetworkConfig::get()->getMaxPlayers() + 1,
/*channel_limit*/2, /*max_in_bandwidth*/0,
/*channel_limit*/EVENT_CHANNEL_COUNT, /*max_in_bandwidth*/0,
/*max_out_bandwidth*/ 0, &addr, true/*change_port_if_bound*/);
}
else
{
addr.port = NetworkConfig::get()->getClientPort();
// Client only has 1 peer
m_network = new Network(/*peer_count*/1, /*channel_limit*/2,
m_network = new Network(/*peer_count*/1,
/*channel_limit*/EVENT_CHANNEL_COUNT,
/*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &addr,
true/*change_port_if_bound*/);
}
@ -831,38 +832,16 @@ void STKHost::mainLoop()
if (!stk_event && m_peers.find(event.peer) != m_peers.end())
{
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())))
try
{
// 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();
}
stk_event = new Event(&event, peer);
}
catch (std::exception& e)
{
Log::warn("STKHost", "%s", e.what());
enet_packet_destroy(event.packet);
continue;
}
stk_event = new Event(&event, peer);
}
else if (!stk_event)
{

View File

@ -18,6 +18,8 @@
#include "network/stk_peer.hpp"
#include "config/user_config.hpp"
#include "network/crypto.hpp"
#include "network/event.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp"
#include "network/network_player_profile.hpp"
@ -46,6 +48,11 @@ STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
m_client_server_token.store(0);
} // STKPeer
//-----------------------------------------------------------------------------
STKPeer::~STKPeer()
{
} // ~STKPeer
//-----------------------------------------------------------------------------
void STKPeer::disconnect()
{
@ -84,8 +91,9 @@ void STKPeer::reset()
/** Sends a packet to this host.
* \param data The data to send.
* \param reliable If the data is sent reliable or not.
* \param encrypted If the data is sent encrypted or not.
*/
void STKPeer::sendPacket(NetworkString *data, bool reliable)
void STKPeer::sendPacket(NetworkString *data, bool reliable, bool encrypted)
{
TransportAddress a(m_enet_peer->address);
// Enet will reuse a disconnected peer so we check here to avoid sending
@ -94,14 +102,27 @@ void STKPeer::sendPacket(NetworkString *data, bool reliable)
a != m_peer_address)
return;
data->setToken(m_client_server_token);
Log::verbose("STKPeer", "sending packet of size %d to %s at %f",
data->size(), a.toString().c_str(),StkTime::getRealTime());
ENetPacket* packet = enet_packet_create(data->getData(),
data->getTotalSize(),
(reliable ? ENET_PACKET_FLAG_RELIABLE
: ENET_PACKET_FLAG_UNSEQUENCED));
m_host->addEnetCommand(m_enet_peer, packet, 0, ECT_SEND_PACKET);
ENetPacket* packet = NULL;
if (m_crypto && encrypted)
{
packet = m_crypto->encryptSend(*data, reliable);
}
else
{
packet = enet_packet_create(data->getData(),
data->getTotalSize(), (reliable ?
ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED));
}
if (packet)
{
Log::verbose("STKPeer", "sending packet of size %d to %s at %f",
packet->dataLength, a.toString().c_str(), StkTime::getRealTime());
m_host->addEnetCommand(m_enet_peer, packet,
encrypted ? EVENT_CHANNEL_NORMAL : EVENT_CHANNEL_UNENCRYPTED,
ECT_SEND_PACKET);
}
} // sendPacket
//-----------------------------------------------------------------------------
@ -139,3 +160,9 @@ uint32_t STKPeer::getPing() const
return 0;
return m_enet_peer->roundTripTime;
} // getPing
//-----------------------------------------------------------------------------
void STKPeer::setCrypto(std::unique_ptr<Crypto>&& c)
{
m_crypto = std::move(c);
} // setCrypto

View File

@ -34,6 +34,7 @@
#include <set>
#include <vector>
class Crypto;
class NetworkPlayerProfile;
class NetworkString;
class STKHost;
@ -76,11 +77,15 @@ protected:
/** Available karts and tracks from this peer */
std::pair<std::set<std::string>, std::set<std::string> > m_available_kts;
std::unique_ptr<Crypto> m_crypto;
public:
STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id);
~STKPeer() {}
STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id);
// ------------------------------------------------------------------------
void sendPacket(NetworkString *data, bool reliable = true);
~STKPeer();
// ------------------------------------------------------------------------
void sendPacket(NetworkString *data, bool reliable = true,
bool encrypted = true);
// ------------------------------------------------------------------------
void disconnect();
// ------------------------------------------------------------------------
@ -168,6 +173,10 @@ public:
{ enet_peer_ping_interval(m_enet_peer, interval); }
// ------------------------------------------------------------------------
uint32_t getPing() const;
// ------------------------------------------------------------------------
Crypto* getCrypto() const { return m_crypto.get(); }
// ------------------------------------------------------------------------
void setCrypto(std::unique_ptr<Crypto>&& c);
}; // STKPeer

View File

@ -245,7 +245,8 @@ namespace Online
// List of strings whose values should not be printed. "" is the
// end indicator.
static std::string dont_print[] = { "&password=", "&token=", "&current=",
"&new1=", "&new2=", "&password_confirm=", ""};
"&new1=", "&new2=", "&password_confirm=",
"&aes-key=", "&iv=", ""};
unsigned int j = 0;
while (dont_print[j].size() > 0)
{

View File

@ -273,8 +273,8 @@ void OnlineScreen::eventCallback(Widget* widget, const std::string& name,
m_entered_server_address =
STKHost::get()->getServerPeerForClient()->getAddress();
auto cl = LobbyProtocol::create<ClientLobby>();
cl->setAddress(m_entered_server_address);
auto cl = LobbyProtocol::create<ClientLobby>
(m_entered_server_address, server);
cl->requestStart();
return true;
});

View File

@ -35,6 +35,9 @@
#include <cwchar>
#include <exception>
#include <openssl/hmac.h>
#include <openssl/buffer.h>
namespace StringUtils
{
bool hasSuffix(const std::string& lhs, const std::string &rhs)
@ -896,6 +899,64 @@ namespace StringUtils
return destination;
} //findAndReplace
// ------------------------------------------------------------------------
std::string base64(const std::vector<uint8_t>& input)
{
BIO *bmem, *b64;
BUF_MEM* bptr;
std::string result;
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_set_flags(bmem, BIO_FLAGS_BASE64_NO_NL);
BIO_write(b64, input.data(), input.size());
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
result.resize(bptr->length - 1);
memcpy(&result[0], bptr->data, bptr->length - 1);
BIO_free_all(b64);
return result;
} //base64
// ------------------------------------------------------------------------
inline size_t calcDecodeLength(const std::string& input)
{
// Calculates the length of a decoded string
size_t padding = 0;
const size_t len = input.size();
if (input[len - 1] == '=' && input[len - 2] == '=')
{
// last two chars are =
padding = 2;
}
else if (input[len - 1] == '=')
{
// last char is =
padding = 1;
}
return (len * 3) / 4 - padding;
}
// ------------------------------------------------------------------------
std::vector<uint8_t> decode64(std::string input)
{
BIO *b64, *bmem;
size_t decode_len = calcDecodeLength(input);
std::vector<uint8_t> result(decode_len, 0);
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new_mem_buf(&input[0], input.size());
bmem = BIO_push(b64, bmem);
BIO_set_flags(bmem, BIO_FLAGS_BASE64_NO_NL);
size_t read_l = BIO_read(bmem, result.data(), input.size());
assert(read_l == decode_len);
BIO_free_all(bmem);
return result;
} //decode64
} // namespace StringUtils

View File

@ -233,6 +233,8 @@ namespace StringUtils
std::string wideToUtf8(const wchar_t* input);
std::string wideToUtf8(const irr::core::stringw& input);
std::string findAndReplace(const std::string& source, const std::string& find, const std::string& replace);
std::string base64(const std::vector<uint8_t>& input);
std::vector<uint8_t> decode64(std::string input);
} // namespace StringUtils