2017-07-20 08:01:46 -07:00
|
|
|
//
|
2017-02-06 20:07:03 -05:00
|
|
|
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
2017-07-20 08:01:46 -07:00
|
|
|
//
|
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#ifndef BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
|
|
|
|
|
#define BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
|
|
|
|
|
|
2017-05-10 12:03:00 -07:00
|
|
|
#include <beast/core/type_traits.hpp>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <boost/asio/buffer.hpp>
|
2016-10-28 19:43:30 -04:00
|
|
|
#include <boost/assert.hpp>
|
|
|
|
|
#include <algorithm>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <cstdint>
|
2016-10-28 19:43:30 -04:00
|
|
|
|
2017-07-20 08:01:46 -07:00
|
|
|
namespace beast {
|
|
|
|
|
namespace websocket {
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
2016-10-28 19:43:30 -04:00
|
|
|
/** A UTF8 validator.
|
|
|
|
|
|
|
|
|
|
This validator can be used to check if a buffer containing UTF8 text is
|
|
|
|
|
valid. The write function may be called incrementally with segmented UTF8
|
|
|
|
|
sequences. The finish function determines if all processed text is valid.
|
2017-07-20 08:01:46 -07:00
|
|
|
*/
|
|
|
|
|
template<class = void>
|
|
|
|
|
class utf8_checker_t
|
|
|
|
|
{
|
2016-10-28 19:43:30 -04:00
|
|
|
std::size_t need_ = 0;
|
|
|
|
|
std::uint8_t* p_ = have_;
|
|
|
|
|
std::uint8_t have_[4];
|
2017-07-20 08:01:46 -07:00
|
|
|
|
|
|
|
|
public:
|
2016-10-28 19:43:30 -04:00
|
|
|
/** Prepare to process text as valid utf8
|
|
|
|
|
*/
|
2017-07-20 08:01:46 -07:00
|
|
|
void
|
|
|
|
|
reset();
|
|
|
|
|
|
2016-10-28 19:43:30 -04:00
|
|
|
/** Check that all processed text is valid utf8
|
|
|
|
|
*/
|
2017-07-20 08:01:46 -07:00
|
|
|
bool
|
2016-10-28 19:43:30 -04:00
|
|
|
finish();
|
|
|
|
|
|
|
|
|
|
/** Check if text is valid UTF8
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2016-10-28 19:43:30 -04:00
|
|
|
@return `true` if the text is valid utf8 or false otherwise.
|
|
|
|
|
*/
|
2017-07-20 08:01:46 -07:00
|
|
|
bool
|
2016-10-28 19:43:30 -04:00
|
|
|
write(std::uint8_t const* in, std::size_t size);
|
|
|
|
|
|
|
|
|
|
/** Check if text is valid UTF8
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2016-10-28 19:43:30 -04:00
|
|
|
@return `true` if the text is valid utf8 or false otherwise.
|
|
|
|
|
*/
|
|
|
|
|
template<class ConstBufferSequence>
|
2017-07-20 08:01:46 -07:00
|
|
|
bool
|
2016-10-28 19:43:30 -04:00
|
|
|
write(ConstBufferSequence const& bs);
|
2017-07-20 08:01:46 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<class _>
|
|
|
|
|
void
|
2017-05-10 12:03:00 -07:00
|
|
|
utf8_checker_t<_>::
|
|
|
|
|
reset()
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2016-10-28 19:43:30 -04:00
|
|
|
need_ = 0;
|
|
|
|
|
p_ = have_;
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class _>
|
|
|
|
|
bool
|
2017-05-10 12:03:00 -07:00
|
|
|
utf8_checker_t<_>::
|
|
|
|
|
finish()
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2016-10-28 19:43:30 -04:00
|
|
|
auto const success = need_ == 0;
|
|
|
|
|
reset();
|
|
|
|
|
return success;
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class _>
|
2016-10-28 19:43:30 -04:00
|
|
|
template<class ConstBufferSequence>
|
2017-07-20 08:01:46 -07:00
|
|
|
bool
|
2017-05-10 12:03:00 -07:00
|
|
|
utf8_checker_t<_>::
|
|
|
|
|
write(ConstBufferSequence const& bs)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-06 12:36:40 -07:00
|
|
|
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value,
|
2016-10-28 19:43:30 -04:00
|
|
|
"ConstBufferSequence requirements not met");
|
2017-07-20 08:01:46 -07:00
|
|
|
using boost::asio::buffer_cast;
|
|
|
|
|
using boost::asio::buffer_size;
|
2017-06-13 11:53:06 -07:00
|
|
|
for(boost::asio::const_buffer b : bs)
|
2016-10-28 19:43:30 -04:00
|
|
|
if(! write(buffer_cast<std::uint8_t const*>(b),
|
2017-07-20 08:01:46 -07:00
|
|
|
buffer_size(b)))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class _>
|
|
|
|
|
bool
|
2017-05-10 12:03:00 -07:00
|
|
|
utf8_checker_t<_>::
|
|
|
|
|
write(std::uint8_t const* in, std::size_t size)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2016-10-28 19:43:30 -04:00
|
|
|
auto const valid =
|
2017-06-18 14:57:32 -07:00
|
|
|
[](std::uint8_t const*& p)
|
2016-10-28 19:43:30 -04:00
|
|
|
{
|
2017-06-18 14:57:32 -07:00
|
|
|
if (p[0] < 128)
|
2016-10-28 19:43:30 -04:00
|
|
|
{
|
2017-06-18 14:57:32 -07:00
|
|
|
++p;
|
2016-10-28 19:43:30 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
2017-06-18 14:57:32 -07:00
|
|
|
if ((p[0] & 0x60) == 0x40)
|
2016-10-28 19:43:30 -04:00
|
|
|
{
|
2017-06-18 14:57:32 -07:00
|
|
|
if ((p[1] & 0xc0) != 0x80)
|
2016-11-14 17:21:42 -05:00
|
|
|
return false;
|
2017-06-18 14:57:32 -07:00
|
|
|
p += 2;
|
2016-10-28 19:43:30 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
2017-06-18 14:57:32 -07:00
|
|
|
if ((p[0] & 0xf0) == 0xe0)
|
2016-10-28 19:43:30 -04:00
|
|
|
{
|
2017-06-18 14:57:32 -07:00
|
|
|
if ((p[1] & 0xc0) != 0x80 ||
|
|
|
|
|
(p[2] & 0xc0) != 0x80 ||
|
|
|
|
|
(p[0] == 224 && p[1] < 160) ||
|
|
|
|
|
(p[0] == 237 && p[1] > 159))
|
2016-10-28 19:43:30 -04:00
|
|
|
return false;
|
2017-06-18 14:57:32 -07:00
|
|
|
p += 3;
|
2016-10-28 19:43:30 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
2017-06-18 14:57:32 -07:00
|
|
|
if ((p[0] & 0xf8) == 0xf0)
|
2016-10-28 19:43:30 -04:00
|
|
|
{
|
2017-06-18 14:57:32 -07:00
|
|
|
if (p[0] > 244 ||
|
|
|
|
|
(p[1] & 0xc0) != 0x80 ||
|
|
|
|
|
(p[2] & 0xc0) != 0x80 ||
|
|
|
|
|
(p[3] & 0xc0) != 0x80 ||
|
|
|
|
|
(p[0] == 240 && p[1] < 144) ||
|
|
|
|
|
(p[0] == 244 && p[1] > 143))
|
2016-10-28 19:43:30 -04:00
|
|
|
return false;
|
2017-06-18 14:57:32 -07:00
|
|
|
p += 4;
|
2016-10-28 19:43:30 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
auto const valid_have =
|
|
|
|
|
[&]()
|
|
|
|
|
{
|
|
|
|
|
if ((have_[0] & 0x60) == 0x40)
|
|
|
|
|
return have_[0] <= 223;
|
|
|
|
|
if ((have_[0] & 0xf0) == 0xe0)
|
|
|
|
|
{
|
|
|
|
|
if (p_ - have_ > 1 &&
|
|
|
|
|
((have_[1] & 0xc0) != 0x80 ||
|
|
|
|
|
(have_[0] == 224 && have_[1] < 160) ||
|
|
|
|
|
(have_[0] == 237 && have_[1] > 159)))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if ((have_[0] & 0xf8) == 0xf0)
|
|
|
|
|
{
|
2017-06-18 14:57:32 -07:00
|
|
|
auto const n = p_ - have_;
|
|
|
|
|
if (n > 2 && (have_[2] & 0xc0) != 0x80)
|
2016-11-14 17:21:42 -05:00
|
|
|
return false;
|
2017-06-18 14:57:32 -07:00
|
|
|
if (n > 1 &&
|
2016-10-28 19:43:30 -04:00
|
|
|
((have_[1] & 0xc0) != 0x80 ||
|
|
|
|
|
(have_[0] == 240 && have_[1] < 144) ||
|
|
|
|
|
(have_[0] == 244 && have_[1] > 143)))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
auto const needed =
|
2017-06-18 14:57:32 -07:00
|
|
|
[](std::uint8_t const v)
|
2016-10-28 19:43:30 -04:00
|
|
|
{
|
2017-06-18 14:57:32 -07:00
|
|
|
if (v < 128)
|
2016-10-28 19:43:30 -04:00
|
|
|
return 1;
|
2017-06-18 14:57:32 -07:00
|
|
|
if (v < 194)
|
2016-10-28 19:43:30 -04:00
|
|
|
return 0;
|
2017-06-18 14:57:32 -07:00
|
|
|
if (v < 224)
|
2016-10-28 19:43:30 -04:00
|
|
|
return 2;
|
2017-06-18 14:57:32 -07:00
|
|
|
if (v < 240)
|
2016-10-28 19:43:30 -04:00
|
|
|
return 3;
|
2017-06-18 14:57:32 -07:00
|
|
|
if (v < 245)
|
2016-10-28 19:43:30 -04:00
|
|
|
return 4;
|
|
|
|
|
return 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto const end = in + size;
|
|
|
|
|
if (need_ > 0)
|
|
|
|
|
{
|
2016-11-08 13:03:20 -05:00
|
|
|
auto n = (std::min)(size, need_);
|
2016-10-28 19:43:30 -04:00
|
|
|
size -= n;
|
|
|
|
|
need_ -= n;
|
|
|
|
|
while(n--)
|
|
|
|
|
*p_++ = *in++;
|
|
|
|
|
if(need_ > 0)
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(in == end);
|
|
|
|
|
return valid_have();
|
|
|
|
|
}
|
|
|
|
|
std::uint8_t const* p = &have_[0];
|
|
|
|
|
if (! valid(p))
|
|
|
|
|
return false;
|
|
|
|
|
p_ = have_;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-14 17:50:48 -07:00
|
|
|
if(size <= sizeof(std::size_t))
|
|
|
|
|
goto slow;
|
|
|
|
|
|
|
|
|
|
// align in to sizeof(std::size_t) boundary
|
2016-10-28 19:43:30 -04:00
|
|
|
{
|
2017-06-14 17:50:48 -07:00
|
|
|
auto const in0 = in;
|
|
|
|
|
auto last = reinterpret_cast<std::uint8_t const*>(
|
|
|
|
|
((reinterpret_cast<std::uintptr_t>(in) + sizeof(std::size_t) - 1) /
|
|
|
|
|
sizeof(std::size_t)) * sizeof(std::size_t));
|
|
|
|
|
while(in < last)
|
|
|
|
|
{
|
|
|
|
|
if(*in & 0x80)
|
|
|
|
|
{
|
|
|
|
|
size = size - (in - in0);
|
|
|
|
|
goto slow;
|
|
|
|
|
}
|
|
|
|
|
++in;
|
|
|
|
|
}
|
|
|
|
|
size = size - (in - in0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// fast loop
|
|
|
|
|
{
|
|
|
|
|
auto const in0 = in;
|
|
|
|
|
auto last = in + size - 7;
|
2016-10-28 19:43:30 -04:00
|
|
|
auto constexpr mask = static_cast<
|
2017-06-14 17:50:48 -07:00
|
|
|
std::size_t>(0x8080808080808080 & ~std::size_t{0});
|
|
|
|
|
while(in < last)
|
|
|
|
|
{
|
2017-07-10 08:09:47 -07:00
|
|
|
#if 0
|
|
|
|
|
std::size_t temp;
|
|
|
|
|
std::memcpy(&temp, in, sizeof(temp));
|
|
|
|
|
if((temp & mask) != 0)
|
|
|
|
|
#else
|
|
|
|
|
// Technically UB but works on all known platforms
|
2017-06-14 17:50:48 -07:00
|
|
|
if((*reinterpret_cast<std::size_t const*>(in) & mask) != 0)
|
2017-07-10 08:09:47 -07:00
|
|
|
#endif
|
2017-06-14 17:50:48 -07:00
|
|
|
{
|
|
|
|
|
size = size - (in - in0);
|
|
|
|
|
goto slow;
|
|
|
|
|
}
|
2016-10-28 19:43:30 -04:00
|
|
|
in += sizeof(std::size_t);
|
2017-06-14 17:50:48 -07:00
|
|
|
}
|
|
|
|
|
last += 4;
|
|
|
|
|
while(in < last)
|
|
|
|
|
if(! valid(in))
|
|
|
|
|
return false;
|
|
|
|
|
goto tail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// slow loop: one code point at a time
|
|
|
|
|
slow:
|
|
|
|
|
{
|
|
|
|
|
auto last = in + size - 3;
|
|
|
|
|
while(in < last)
|
|
|
|
|
if(! valid(in))
|
|
|
|
|
return false;
|
2016-10-28 19:43:30 -04:00
|
|
|
}
|
|
|
|
|
|
2017-06-14 17:50:48 -07:00
|
|
|
tail:
|
2016-10-28 19:43:30 -04:00
|
|
|
for(;;)
|
|
|
|
|
{
|
|
|
|
|
auto n = end - in;
|
|
|
|
|
if(! n)
|
|
|
|
|
break;
|
|
|
|
|
auto const need = needed(*in);
|
|
|
|
|
if (need == 0)
|
|
|
|
|
return false;
|
|
|
|
|
if(need <= n)
|
|
|
|
|
{
|
|
|
|
|
if(! valid(in))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
need_ = need - n;
|
|
|
|
|
while(n--)
|
|
|
|
|
*p_++ = *in++;
|
|
|
|
|
return valid_have();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using utf8_checker = utf8_checker_t<>;
|
|
|
|
|
|
|
|
|
|
template<class = void>
|
|
|
|
|
bool
|
|
|
|
|
check_utf8(char const* p, std::size_t n)
|
|
|
|
|
{
|
|
|
|
|
utf8_checker c;
|
2016-10-28 19:43:30 -04:00
|
|
|
if(! c.write(reinterpret_cast<const uint8_t*>(p), n))
|
2017-07-20 08:01:46 -07:00
|
|
|
return false;
|
|
|
|
|
return c.finish();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // detail
|
|
|
|
|
} // websocket
|
|
|
|
|
} // beast
|
|
|
|
|
|
|
|
|
|
#endif
|