Core/PacketIO: Add a validating string helper class for use in packet classes

(cherry picked from commit f2f47f774f)
This commit is contained in:
Shauren
2020-03-18 23:20:11 +01:00
parent def97385cc
commit 3ebfa8cc64
6 changed files with 192 additions and 7 deletions

View File

@@ -0,0 +1,34 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* 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 2 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef Tuples_h__
#define Tuples_h__
#include <tuple>
namespace Trinity
{
template <typename T, typename Tuple>
struct has_type;
template <typename T, typename... Us>
struct has_type<T, std::tuple<Us...>> : std::disjunction<std::is_same<T, Us>...>
{
};
}
#endif // Tuples_h__

View File

@@ -16,9 +16,48 @@
*/
#include "PacketUtilities.h"
#include "Hyperlinks.h"
#include "Errors.h"
#include <utf8.h>
#include <sstream>
WorldPackets::InvalidStringValueException::InvalidStringValueException(std::string const& value) : ByteBufferInvalidValueException("string", value.c_str())
{
}
WorldPackets::InvalidUtf8ValueException::InvalidUtf8ValueException(std::string const& value) : InvalidStringValueException(value)
{
}
WorldPackets::InvalidHyperlinkException::InvalidHyperlinkException(std::string const& value) : InvalidStringValueException(value)
{
}
WorldPackets::IllegalHyperlinkException::IllegalHyperlinkException(std::string const& value) : InvalidStringValueException(value)
{
}
bool WorldPackets::Strings::Utf8::Validate(std::string const& value)
{
if (!utf8::is_valid(value.begin(), value.end()))
throw InvalidUtf8ValueException(value);
return true;
}
bool WorldPackets::Strings::Hyperlinks::Validate(std::string const& value)
{
if (!Trinity::Hyperlinks::CheckAllLinks(value))
throw InvalidHyperlinkException(value);
return true;
}
bool WorldPackets::Strings::NoHyperlinks::Validate(std::string const& value)
{
if (value.find('|') != std::string::npos)
throw IllegalHyperlinkException(value);
return true;
}
WorldPackets::PacketArrayMaxCapacityException::PacketArrayMaxCapacityException(std::size_t requestedSize, std::size_t sizeLimit)
{
std::ostringstream builder;

View File

@@ -20,11 +20,107 @@
#include "ByteBuffer.h"
#include "Duration.h"
#include "Tuples.h"
#include <boost/container/static_vector.hpp>
#include <ctime>
namespace WorldPackets
{
class InvalidStringValueException : public ByteBufferInvalidValueException
{
public:
InvalidStringValueException(std::string const& value);
std::string const& GetInvalidValue() const { return _value; }
private:
std::string _value;
};
class InvalidUtf8ValueException : public InvalidStringValueException
{
public:
InvalidUtf8ValueException(std::string const& value);
};
class InvalidHyperlinkException : public InvalidStringValueException
{
public:
InvalidHyperlinkException(std::string const& value);
};
class IllegalHyperlinkException : public InvalidStringValueException
{
public:
IllegalHyperlinkException(std::string const& value);
};
namespace Strings
{
struct RawBytes { static bool Validate(std::string const& /*value*/) { return true; } };
template<std::size_t MaxBytesWithoutNullTerminator>
struct ByteSize { static bool Validate(std::string const& value) { return value.size() <= MaxBytesWithoutNullTerminator; } };
struct Utf8 { static bool Validate(std::string const& value); };
struct Hyperlinks { static bool Validate(std::string const& value); };
struct NoHyperlinks { static bool Validate(std::string const& value); };
}
/**
* Utility class for automated prevention of invalid strings in client packets
*/
template<std::size_t MaxBytesWithoutNullTerminator, typename... Validators>
class String
{
using ValidatorList = std::conditional_t<!Trinity::has_type<Strings::RawBytes, std::tuple<Validators...>>::value,
std::tuple<Strings::ByteSize<MaxBytesWithoutNullTerminator>, Strings::Utf8, Validators...>,
std::tuple<Strings::ByteSize<MaxBytesWithoutNullTerminator>, Validators...>>;
public:
bool empty() const { return _storage.empty(); }
char const* c_str() const { return _storage.c_str(); }
operator std::string&() { return _storage; }
operator std::string const&() const { return _storage; }
std::string&& Move() { return std::move(_storage); }
friend ByteBuffer& operator>>(ByteBuffer& data, String& value)
{
std::string string = data.ReadCString(false);
Validate(string);
value._storage = std::move(string);
return data;
}
String& operator=(std::string const& value)
{
Validate(value);
_storage = value;
return *this;
}
String& operator=(std::string&& value)
{
Validate(value);
_storage = std::move(value);
return *this;
}
private:
static bool Validate(std::string const& value)
{
return ValidateNth(value, std::make_index_sequence<std::tuple_size_v<ValidatorList>>{});
}
template<std::size_t... indexes>
static bool ValidateNth(std::string const& value, std::index_sequence<indexes...>)
{
return (std::tuple_element_t<indexes, ValidatorList>::Validate(value) && ...);
}
std::string _storage;
};
class PacketArrayMaxCapacityException : public ByteBufferException
{
public:

View File

@@ -425,6 +425,22 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
break;
}
}
catch (WorldPackets::InvalidHyperlinkException const& ihe)
{
TC_LOG_ERROR("network", "%s sent %s with an invalid link:\n%s", GetPlayerInfo().c_str(),
GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str(), ihe.GetInvalidValue().c_str());
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
KickPlayer();
}
catch (WorldPackets::IllegalHyperlinkException const& ihe)
{
TC_LOG_ERROR("network", "%s sent %s which illegally contained a hyperlink:\n%s", GetPlayerInfo().c_str(),
GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str(), ihe.GetInvalidValue().c_str());
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
KickPlayer();
}
catch (WorldPackets::PacketArrayMaxCapacityException const& pamce)
{
TC_LOG_ERROR("network", "PacketArrayMaxCapacityException: %s while parsing %s from %s.",

View File

@@ -39,16 +39,16 @@ ByteBufferPositionException::ByteBufferPositionException(size_t pos, size_t size
message().assign(ss.str());
}
ByteBufferInvalidValueException::ByteBufferInvalidValueException(char const* type, size_t pos)
ByteBufferInvalidValueException::ByteBufferInvalidValueException(char const* type, char const* value)
{
message().assign(Trinity::StringFormat("Invalid %s value found in ByteBuffer at pos " SZFMTD, type, pos));
message().assign(Trinity::StringFormat("Invalid %s value (%s) found in ByteBuffer", type, value));
}
ByteBuffer& ByteBuffer::operator>>(float& value)
{
value = read<float>();
if (!std::isfinite(value))
throw ByteBufferInvalidValueException("float", _rpos - sizeof(float));
throw ByteBufferInvalidValueException("float", "infinity");
return *this;
}
@@ -56,7 +56,7 @@ ByteBuffer& ByteBuffer::operator>>(double& value)
{
value = read<double>();
if (!std::isfinite(value))
throw ByteBufferInvalidValueException("double", _rpos - sizeof(double));
throw ByteBufferInvalidValueException("double", "infinity");
return *this;
}
@@ -71,7 +71,7 @@ std::string ByteBuffer::ReadCString(bool requireValidUtf8 /*= true*/)
value += c;
}
if (requireValidUtf8 && !utf8::is_valid(value.begin(), value.end()))
throw ByteBufferInvalidValueException("string", _rpos - value.length() - 1);
throw ByteBufferInvalidValueException("string", value.c_str());
return value;
}
@@ -87,7 +87,7 @@ std::string ByteBuffer::ReadString(uint32 length, bool requireValidUtf8 /*= true
std::string value(reinterpret_cast<char const*>(&_storage[_rpos]), length);
_rpos += length;
if (requireValidUtf8 && !utf8::is_valid(value.begin(), value.end()))
throw ByteBufferInvalidValueException("string", _rpos - value.length() - 1);
throw ByteBufferInvalidValueException("string", value.c_str());
return value;
}

View File

@@ -53,7 +53,7 @@ public:
class TC_SHARED_API ByteBufferInvalidValueException : public ByteBufferException
{
public:
ByteBufferInvalidValueException(char const* type, size_t pos);
ByteBufferInvalidValueException(char const* type, char const* value);
~ByteBufferInvalidValueException() noexcept = default;
};