added handshake for dos protection
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
### BREAKING CHANGES
|
### BREAKING CHANGES
|
||||||
|
|
||||||
* Remote mounts must be upgraded to v2.1.0+ to support new authentication scheme
|
* Remote mounts must be upgraded to v2.1.0+ to support new authentication scheme
|
||||||
|
* Protocol handshake added for DoS protection
|
||||||
|
|
||||||
### Issues
|
### Issues
|
||||||
|
|
||||||
|
|||||||
77
repertory/librepertory/include/comm/packet/common.hpp
Normal file
77
repertory/librepertory/include/comm/packet/common.hpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
|
||||||
|
#define REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
|
||||||
|
|
||||||
|
#include "utils/common.hpp"
|
||||||
|
|
||||||
|
namespace repertory::comm {
|
||||||
|
constexpr const std::uint8_t max_read_attempts{5U};
|
||||||
|
constexpr const std::uint16_t packet_nonce_size{256U};
|
||||||
|
|
||||||
|
struct non_blocking_guard final {
|
||||||
|
boost::asio::ip::tcp::socket &sock;
|
||||||
|
bool non_blocking{};
|
||||||
|
|
||||||
|
non_blocking_guard(const non_blocking_guard &) = delete;
|
||||||
|
non_blocking_guard(non_blocking_guard &&) = delete;
|
||||||
|
|
||||||
|
auto operator=(const non_blocking_guard &) -> non_blocking_guard & = delete;
|
||||||
|
auto operator=(non_blocking_guard &&) -> non_blocking_guard & = delete;
|
||||||
|
|
||||||
|
explicit non_blocking_guard(boost::asio::ip::tcp::socket &sock_)
|
||||||
|
: sock(sock_), non_blocking(sock_.non_blocking()) {
|
||||||
|
boost::system::error_code err;
|
||||||
|
[[maybe_unused]] auto ret = sock_.non_blocking(true, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
~non_blocking_guard() {
|
||||||
|
if (not sock.is_open()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_code err;
|
||||||
|
[[maybe_unused]] auto ret = sock.non_blocking(non_blocking, err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock)
|
||||||
|
-> bool;
|
||||||
|
|
||||||
|
void connect_with_deadline(
|
||||||
|
boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock,
|
||||||
|
boost::asio::ip::basic_resolver<boost::asio::ip::tcp>::results_type
|
||||||
|
&endpoints,
|
||||||
|
std::chrono::milliseconds deadline);
|
||||||
|
|
||||||
|
void read_exact_with_deadline(boost::asio::io_context &io_ctx,
|
||||||
|
boost::asio::ip::tcp::socket &sock,
|
||||||
|
boost::asio::mutable_buffer buf,
|
||||||
|
std::chrono::milliseconds deadline);
|
||||||
|
|
||||||
|
void write_all_with_deadline(boost::asio::io_context &io_ctx,
|
||||||
|
boost::asio::ip::tcp::socket &sock,
|
||||||
|
boost::asio::mutable_buffer buf,
|
||||||
|
std::chrono::milliseconds deadline);
|
||||||
|
} // namespace repertory::comm
|
||||||
|
|
||||||
|
#endif // REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
|
||||||
@@ -200,7 +200,7 @@ public:
|
|||||||
|
|
||||||
void encode_top(remote::file_info val);
|
void encode_top(remote::file_info val);
|
||||||
|
|
||||||
void encrypt(std::string_view token);
|
void encrypt(std::string_view token, bool include_size = true);
|
||||||
|
|
||||||
[[nodiscard]] auto get_size() const -> std::uint32_t {
|
[[nodiscard]] auto get_size() const -> std::uint32_t {
|
||||||
return static_cast<std::uint32_t>(buffer_.size());
|
return static_cast<std::uint32_t>(buffer_.size());
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#define REPERTORY_INCLUDE_COMM_PACKET_PACKET_SERVER_HPP_
|
#define REPERTORY_INCLUDE_COMM_PACKET_PACKET_SERVER_HPP_
|
||||||
|
|
||||||
#include "comm/packet/client_pool.hpp"
|
#include "comm/packet/client_pool.hpp"
|
||||||
|
#include "comm/packet/common.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
|
|
||||||
using namespace boost::asio;
|
using namespace boost::asio;
|
||||||
@@ -61,14 +62,16 @@ private:
|
|||||||
std::string client_id;
|
std::string client_id;
|
||||||
std::string nonce;
|
std::string nonce;
|
||||||
|
|
||||||
void generate_nonce() { nonce = utils::generate_random_string(256U); }
|
void generate_nonce() {
|
||||||
|
nonce = utils::generate_random_string(comm::packet_nonce_size);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string encryption_token_;
|
std::string encryption_token_;
|
||||||
closed_callback closed_;
|
closed_callback closed_;
|
||||||
message_handler_callback message_handler_;
|
message_handler_callback message_handler_;
|
||||||
io_context io_context_;
|
mutable io_context io_context_;
|
||||||
std::unique_ptr<std::thread> server_thread_;
|
std::unique_ptr<std::thread> server_thread_;
|
||||||
std::vector<std::thread> service_threads_;
|
std::vector<std::thread> service_threads_;
|
||||||
std::recursive_mutex connection_mutex_;
|
std::recursive_mutex connection_mutex_;
|
||||||
|
|||||||
191
repertory/librepertory/src/comm/packet/common.cpp
Normal file
191
repertory/librepertory/src/comm/packet/common.cpp
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "comm/packet/common.hpp"
|
||||||
|
|
||||||
|
#include "events/event_system.hpp"
|
||||||
|
#include "events/types/packet_client_timeout.hpp"
|
||||||
|
|
||||||
|
namespace repertory::comm {
|
||||||
|
auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock) -> bool {
|
||||||
|
if (not sock.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
non_blocking_guard guard{sock};
|
||||||
|
|
||||||
|
boost::system::error_code err{};
|
||||||
|
std::array<std::uint8_t, 1> tmp{};
|
||||||
|
auto available = sock.receive(boost::asio::buffer(tmp),
|
||||||
|
boost::asio::socket_base::message_peek, err);
|
||||||
|
|
||||||
|
if (err == boost::asio::error::would_block ||
|
||||||
|
err == boost::asio::error::try_again) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == boost::asio::error::eof ||
|
||||||
|
err == boost::asio::error::connection_reset ||
|
||||||
|
err == boost::asio::error::operation_aborted ||
|
||||||
|
err == boost::asio::error::not_connected ||
|
||||||
|
err == boost::asio::error::bad_descriptor ||
|
||||||
|
err == boost::asio::error::network_down) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not err && available == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not err && available > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class op_t>
|
||||||
|
void run_with_deadline(boost::asio::io_context &io_ctx,
|
||||||
|
boost::asio::ip::tcp::socket &sock, op_t &&operation,
|
||||||
|
std::chrono::milliseconds deadline,
|
||||||
|
std::string_view event_name,
|
||||||
|
std::string_view function_name) {
|
||||||
|
deadline = std::max(deadline, std::chrono::milliseconds{250});
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
bool timed_out = false;
|
||||||
|
|
||||||
|
boost::asio::steady_timer timer{io_ctx};
|
||||||
|
timer.expires_after(deadline);
|
||||||
|
timer.async_wait([&done, &sock, &timed_out](auto &&err_) {
|
||||||
|
if (not err_ && not done) {
|
||||||
|
timed_out = true;
|
||||||
|
sock.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
boost::system::error_code err{};
|
||||||
|
operation([&done, &err](auto &&err_) {
|
||||||
|
err = err_;
|
||||||
|
done = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_ctx.restart();
|
||||||
|
while (not done) {
|
||||||
|
io_ctx.run_one();
|
||||||
|
}
|
||||||
|
timer.cancel();
|
||||||
|
|
||||||
|
if (timed_out) {
|
||||||
|
repertory::event_system::instance().raise<repertory::packet_client_timeout>(
|
||||||
|
std::string(event_name), std::string(function_name));
|
||||||
|
throw std::runtime_error(std::string(event_name) + " timed-out");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
throw std::runtime_error(std::string(event_name) + " failed|err|" +
|
||||||
|
err.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void connect_with_deadline(
|
||||||
|
boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock,
|
||||||
|
boost::asio::ip::basic_resolver<boost::asio::ip::tcp>::results_type
|
||||||
|
&endpoints,
|
||||||
|
std::chrono::milliseconds deadline) {
|
||||||
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
run_with_deadline(
|
||||||
|
io_ctx, sock,
|
||||||
|
[&sock, &endpoints](auto &&handler) {
|
||||||
|
boost::asio::async_connect(
|
||||||
|
sock, endpoints, [handler](auto &&err, auto &&) { handler(err); });
|
||||||
|
},
|
||||||
|
deadline, "connect", function_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_exact_with_deadline(boost::asio::io_context &io_ctx,
|
||||||
|
boost::asio::ip::tcp::socket &sock,
|
||||||
|
boost::asio::mutable_buffer buf,
|
||||||
|
std::chrono::milliseconds deadline) {
|
||||||
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
auto *base = static_cast<std::uint8_t *>(buf.data());
|
||||||
|
|
||||||
|
std::size_t total = buf.size();
|
||||||
|
std::size_t offset = 0U;
|
||||||
|
|
||||||
|
while (offset < total) {
|
||||||
|
std::size_t bytes_read = 0U;
|
||||||
|
|
||||||
|
run_with_deadline(
|
||||||
|
io_ctx, sock,
|
||||||
|
[&](auto &&handler) {
|
||||||
|
sock.async_read_some(
|
||||||
|
boost::asio::buffer(base + offset, total - offset),
|
||||||
|
[&bytes_read, handler](auto &&err, auto &&count) {
|
||||||
|
bytes_read = count;
|
||||||
|
handler(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deadline, "read", function_name);
|
||||||
|
|
||||||
|
if (bytes_read == 0U) {
|
||||||
|
throw std::runtime_error("0 bytes read");
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += bytes_read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_all_with_deadline(boost::asio::io_context &io_ctx,
|
||||||
|
boost::asio::ip::tcp::socket &sock,
|
||||||
|
boost::asio::mutable_buffer buf,
|
||||||
|
std::chrono::milliseconds deadline) {
|
||||||
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
auto *base = static_cast<std::uint8_t *>(buf.data());
|
||||||
|
std::size_t total = buf.size();
|
||||||
|
std::size_t offset = 0U;
|
||||||
|
|
||||||
|
while (offset < total) {
|
||||||
|
std::size_t bytes_written = 0U;
|
||||||
|
|
||||||
|
run_with_deadline(
|
||||||
|
io_ctx, sock,
|
||||||
|
[&](auto &&handler) {
|
||||||
|
sock.async_write_some(
|
||||||
|
boost::asio::buffer(base + offset, total - offset),
|
||||||
|
[&bytes_written, handler](auto &&err, auto &&count) {
|
||||||
|
bytes_written = count;
|
||||||
|
handler(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deadline, "write", function_name);
|
||||||
|
|
||||||
|
if (bytes_written == 0U) {
|
||||||
|
throw std::runtime_error("0 bytes written");
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += bytes_written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace repertory::comm
|
||||||
@@ -518,14 +518,16 @@ void packet::encode_top(remote::file_info val) {
|
|||||||
encode_top(&val, sizeof(val), true);
|
encode_top(&val, sizeof(val), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void packet::encrypt(std::string_view token) {
|
void packet::encrypt(std::string_view token, bool include_size) {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data_buffer result;
|
data_buffer result;
|
||||||
utils::encryption::encrypt_data(token, buffer_, result);
|
utils::encryption::encrypt_data(token, buffer_, result);
|
||||||
buffer_ = std::move(result);
|
buffer_ = std::move(result);
|
||||||
encode_top(static_cast<std::uint32_t>(buffer_.size()));
|
if (include_size) {
|
||||||
|
encode_top(static_cast<std::uint32_t>(buffer_.size()));
|
||||||
|
}
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
utils::error::raise_error(function_name, e, "exception occurred");
|
utils::error::raise_error(function_name, e, "exception occurred");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "comm/packet/packet_client.hpp"
|
#include "comm/packet/packet_client.hpp"
|
||||||
|
|
||||||
#include "events/event_system.hpp"
|
#include "comm/packet/common.hpp"
|
||||||
#include "events/types/packet_client_timeout.hpp"
|
|
||||||
#include "platform/platform.hpp"
|
#include "platform/platform.hpp"
|
||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
#include "utils/collection.hpp"
|
#include "utils/collection.hpp"
|
||||||
@@ -30,199 +29,7 @@
|
|||||||
#include "utils/error_utils.hpp"
|
#include "utils/error_utils.hpp"
|
||||||
#include "version.hpp"
|
#include "version.hpp"
|
||||||
|
|
||||||
namespace {
|
using namespace repertory::comm;
|
||||||
constexpr std::uint8_t max_attempts{5U};
|
|
||||||
|
|
||||||
struct non_blocking_guard final {
|
|
||||||
boost::asio::ip::tcp::socket &sock;
|
|
||||||
bool non_blocking{};
|
|
||||||
|
|
||||||
non_blocking_guard(const non_blocking_guard &) = delete;
|
|
||||||
non_blocking_guard(non_blocking_guard &&) = delete;
|
|
||||||
|
|
||||||
auto operator=(const non_blocking_guard &) -> non_blocking_guard & = delete;
|
|
||||||
auto operator=(non_blocking_guard &&) -> non_blocking_guard & = delete;
|
|
||||||
|
|
||||||
explicit non_blocking_guard(boost::asio::ip::tcp::socket &sock_)
|
|
||||||
: sock(sock_), non_blocking(sock_.non_blocking()) {
|
|
||||||
boost::system::error_code err;
|
|
||||||
[[maybe_unused]] auto ret = sock_.non_blocking(true, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
~non_blocking_guard() {
|
|
||||||
if (not sock.is_open()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::system::error_code err;
|
|
||||||
[[maybe_unused]] auto ret = sock.non_blocking(non_blocking, err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock)
|
|
||||||
-> bool {
|
|
||||||
if (not sock.is_open()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
non_blocking_guard guard{sock};
|
|
||||||
|
|
||||||
boost::system::error_code err{};
|
|
||||||
std::array<std::uint8_t, 1> tmp{};
|
|
||||||
auto available = sock.receive(boost::asio::buffer(tmp),
|
|
||||||
boost::asio::socket_base::message_peek, err);
|
|
||||||
|
|
||||||
if (err == boost::asio::error::would_block ||
|
|
||||||
err == boost::asio::error::try_again) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err == boost::asio::error::eof ||
|
|
||||||
err == boost::asio::error::connection_reset ||
|
|
||||||
err == boost::asio::error::operation_aborted ||
|
|
||||||
err == boost::asio::error::not_connected ||
|
|
||||||
err == boost::asio::error::bad_descriptor ||
|
|
||||||
err == boost::asio::error::network_down) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (not err && available == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (not err && available > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class op_t>
|
|
||||||
void run_with_deadline(boost::asio::io_context &io_ctx,
|
|
||||||
boost::asio::ip::tcp::socket &sock, op_t &&operation,
|
|
||||||
std::chrono::milliseconds deadline,
|
|
||||||
std::string_view event_name,
|
|
||||||
std::string_view function_name) {
|
|
||||||
deadline = std::max(deadline, std::chrono::milliseconds{250});
|
|
||||||
|
|
||||||
bool done = false;
|
|
||||||
bool timed_out = false;
|
|
||||||
|
|
||||||
boost::asio::steady_timer timer{io_ctx};
|
|
||||||
timer.expires_after(deadline);
|
|
||||||
timer.async_wait([&done, &sock, &timed_out](auto &&err_) {
|
|
||||||
if (not err_ && not done) {
|
|
||||||
timed_out = true;
|
|
||||||
sock.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
boost::system::error_code err{};
|
|
||||||
operation([&done, &err](auto &&err_) {
|
|
||||||
err = err_;
|
|
||||||
done = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
io_ctx.restart();
|
|
||||||
while (not done) {
|
|
||||||
io_ctx.run_one();
|
|
||||||
}
|
|
||||||
timer.cancel();
|
|
||||||
|
|
||||||
if (timed_out) {
|
|
||||||
repertory::event_system::instance().raise<repertory::packet_client_timeout>(
|
|
||||||
std::string(event_name), std::string(function_name));
|
|
||||||
throw std::runtime_error(std::string(event_name) + " timed-out");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
throw std::runtime_error(std::string(event_name) + " failed|err|" +
|
|
||||||
err.message());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void connect_with_deadline(boost::asio::io_context &io_ctx,
|
|
||||||
boost::asio::ip::tcp::socket &sock,
|
|
||||||
const auto &endpoints,
|
|
||||||
std::chrono::milliseconds deadline) {
|
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
|
||||||
|
|
||||||
run_with_deadline(
|
|
||||||
io_ctx, sock,
|
|
||||||
[&sock, &endpoints](auto &&handler) {
|
|
||||||
boost::asio::async_connect(
|
|
||||||
sock, endpoints, [handler](auto &&err, auto &&) { handler(err); });
|
|
||||||
},
|
|
||||||
deadline, "connect", function_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_exact_with_deadline(boost::asio::io_context &io_ctx,
|
|
||||||
boost::asio::ip::tcp::socket &sock,
|
|
||||||
boost::asio::mutable_buffer buf,
|
|
||||||
std::chrono::milliseconds deadline) {
|
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
|
||||||
|
|
||||||
auto *base = static_cast<std::uint8_t *>(buf.data());
|
|
||||||
|
|
||||||
std::size_t total = buf.size();
|
|
||||||
std::size_t offset = 0U;
|
|
||||||
|
|
||||||
while (offset < total) {
|
|
||||||
std::size_t bytes_read = 0U;
|
|
||||||
|
|
||||||
run_with_deadline(
|
|
||||||
io_ctx, sock,
|
|
||||||
[&](auto &&handler) {
|
|
||||||
sock.async_read_some(
|
|
||||||
boost::asio::buffer(base + offset, total - offset),
|
|
||||||
[&bytes_read, handler](auto &&err, auto &&count) {
|
|
||||||
bytes_read = count;
|
|
||||||
handler(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
deadline, "read", function_name);
|
|
||||||
|
|
||||||
if (bytes_read == 0U) {
|
|
||||||
throw std::runtime_error("0 bytes read");
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += bytes_read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_all_with_deadline(boost::asio::io_context &io_ctx,
|
|
||||||
boost::asio::ip::tcp::socket &sock,
|
|
||||||
boost::asio::mutable_buffer buf,
|
|
||||||
std::chrono::milliseconds deadline) {
|
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
|
||||||
|
|
||||||
auto *base = static_cast<std::uint8_t *>(buf.data());
|
|
||||||
std::size_t total = buf.size();
|
|
||||||
std::size_t offset = 0U;
|
|
||||||
|
|
||||||
while (offset < total) {
|
|
||||||
std::size_t bytes_written = 0U;
|
|
||||||
|
|
||||||
run_with_deadline(
|
|
||||||
io_ctx, sock,
|
|
||||||
[&](auto &&handler) {
|
|
||||||
sock.async_write_some(
|
|
||||||
boost::asio::buffer(base + offset, total - offset),
|
|
||||||
[&bytes_written, handler](auto &&err, auto &&count) {
|
|
||||||
bytes_written = count;
|
|
||||||
handler(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
deadline, "write", function_name);
|
|
||||||
|
|
||||||
if (bytes_written == 0U) {
|
|
||||||
throw std::runtime_error("0 bytes written");
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += bytes_written;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
packet_client::packet_client(remote::remote_config cfg)
|
packet_client::packet_client(remote::remote_config cfg)
|
||||||
@@ -318,38 +125,21 @@ auto packet_client::handshake(client &cli) const -> bool {
|
|||||||
data_buffer buffer;
|
data_buffer buffer;
|
||||||
{
|
{
|
||||||
packet tmp;
|
packet tmp;
|
||||||
tmp.encode_top(cli.nonce);
|
tmp.encode_top(utils::generate_random_string(packet_nonce_size));
|
||||||
tmp.to_buffer(buffer);
|
tmp.to_buffer(buffer);
|
||||||
}
|
}
|
||||||
auto to_read{buffer.size()};
|
|
||||||
|
|
||||||
std::uint32_t total_read{};
|
read_exact_with_deadline(io_context_, cli.socket,
|
||||||
while ((total_read < to_read) && cli.socket.is_open()) {
|
boost::asio::buffer(buffer),
|
||||||
auto bytes_read = boost::asio::read(
|
std::chrono::milliseconds(cfg_.recv_timeout_ms));
|
||||||
cli.socket,
|
packet response(buffer);
|
||||||
boost::asio::buffer(&buffer[total_read], buffer.size() - total_read));
|
response.encrypt(cfg_.encryption_token, false);
|
||||||
if (bytes_read <= 0) {
|
response.to_buffer(buffer);
|
||||||
throw std::runtime_error("0 bytes read");
|
|
||||||
}
|
|
||||||
|
|
||||||
total_read += static_cast<std::uint32_t>(bytes_read);
|
write_all_with_deadline(io_context_, cli.socket,
|
||||||
}
|
boost::asio::buffer(buffer),
|
||||||
|
std::chrono::milliseconds(cfg_.send_timeout_ms));
|
||||||
if (total_read == to_read) {
|
return true;
|
||||||
packet response(buffer);
|
|
||||||
response.encrypt(cfg_.encryption_token);
|
|
||||||
response.to_buffer(buffer);
|
|
||||||
|
|
||||||
auto written = boost::asio::write(
|
|
||||||
cli.socket, boost::asio::buffer(boost::asio::buffer(buffer)));
|
|
||||||
if (written == to_read) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("failed to send handshake");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("failed to read handshake");
|
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
repertory::utils::error::raise_error(function_name, e, "handlshake failed");
|
repertory::utils::error::raise_error(function_name, e, "handlshake failed");
|
||||||
}
|
}
|
||||||
@@ -427,7 +217,8 @@ auto packet_client::send(std::string_view method, packet &request,
|
|||||||
request.encode_top(std::string{project_get_version()});
|
request.encode_top(std::string{project_get_version()});
|
||||||
|
|
||||||
for (std::uint8_t retry = 1U;
|
for (std::uint8_t retry = 1U;
|
||||||
allow_connections_ && not success && (retry <= max_attempts); ++retry) {
|
allow_connections_ && not success && (retry <= max_read_attempts);
|
||||||
|
++retry) {
|
||||||
auto current_client = get_client();
|
auto current_client = get_client();
|
||||||
if (current_client) {
|
if (current_client) {
|
||||||
try {
|
try {
|
||||||
@@ -456,7 +247,7 @@ auto packet_client::send(std::string_view method, packet &request,
|
|||||||
utils::error::raise_error(function_name, e, "send failed");
|
utils::error::raise_error(function_name, e, "send failed");
|
||||||
|
|
||||||
close(*current_client);
|
close(*current_client);
|
||||||
if (allow_connections_ && (retry < max_attempts)) {
|
if (allow_connections_ && (retry < max_read_attempts)) {
|
||||||
std::this_thread::sleep_for(1s);
|
std::this_thread::sleep_for(1s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "comm/packet/packet_server.hpp"
|
#include "comm/packet/packet_server.hpp"
|
||||||
|
|
||||||
|
#include "comm/packet/common.hpp"
|
||||||
#include "comm/packet/packet.hpp"
|
#include "comm/packet/packet.hpp"
|
||||||
#include "events/event_system.hpp"
|
#include "events/event_system.hpp"
|
||||||
#include "events/types/service_start_begin.hpp"
|
#include "events/types/service_start_begin.hpp"
|
||||||
@@ -31,9 +32,10 @@
|
|||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
#include "utils/error_utils.hpp"
|
#include "utils/error_utils.hpp"
|
||||||
|
|
||||||
namespace repertory {
|
using namespace repertory::comm;
|
||||||
using std::thread;
|
using std::thread;
|
||||||
|
|
||||||
|
namespace repertory {
|
||||||
packet_server::packet_server(std::uint16_t port, std::string token,
|
packet_server::packet_server(std::uint16_t port, std::string token,
|
||||||
std::uint8_t pool_size, closed_callback closed,
|
std::uint8_t pool_size, closed_callback closed,
|
||||||
message_handler_callback message_handler)
|
message_handler_callback message_handler)
|
||||||
@@ -89,49 +91,30 @@ auto packet_server::handshake(std::shared_ptr<connection> conn) const -> bool {
|
|||||||
packet request;
|
packet request;
|
||||||
request.encode_top(conn->nonce);
|
request.encode_top(conn->nonce);
|
||||||
request.to_buffer(buffer);
|
request.to_buffer(buffer);
|
||||||
auto to_read{buffer.size()};
|
auto to_read{buffer.size() + utils::encryption::encryption_header_size};
|
||||||
|
|
||||||
auto written = boost::asio::write(
|
write_all_with_deadline(io_context_, conn->socket,
|
||||||
conn->socket, boost::asio::buffer(boost::asio::buffer(buffer)));
|
boost::asio::buffer(buffer),
|
||||||
if (written == to_read) {
|
std::chrono::milliseconds(3000U));
|
||||||
conn->buffer.resize(to_read);
|
|
||||||
|
|
||||||
std::uint32_t total_read{};
|
conn->buffer.resize(to_read);
|
||||||
while ((total_read < to_read) && conn->socket.is_open()) {
|
read_exact_with_deadline(io_context_, conn->socket,
|
||||||
auto bytes_read = boost::asio::read(
|
boost::asio::buffer(conn->buffer),
|
||||||
conn->socket,
|
std::chrono::milliseconds(3000U));
|
||||||
boost::asio::buffer(&conn->buffer[total_read],
|
packet response(conn->buffer);
|
||||||
conn->buffer.size() - total_read));
|
if (response.decrypt(encryption_token_) == 0) {
|
||||||
if (bytes_read <= 0) {
|
std::string nonce;
|
||||||
throw std::runtime_error("0 bytes read");
|
if (response.decode(nonce) == 0) {
|
||||||
|
if (nonce == conn->nonce) {
|
||||||
|
conn->generate_nonce();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
total_read += static_cast<std::uint32_t>(bytes_read);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (total_read == to_read) {
|
throw std::runtime_error("invalid nonce");
|
||||||
packet response(conn->buffer);
|
|
||||||
if (response.decrypt(encryption_token_) == 0) {
|
|
||||||
std::string nonce;
|
|
||||||
if (response.decode(nonce) == 0) {
|
|
||||||
if (nonce == conn->nonce) {
|
|
||||||
conn->generate_nonce();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("invalid nonce");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("invalid nonce");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("decryption failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("invalid handshake");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("failed to send handshake");
|
throw std::runtime_error("decryption failed");
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
repertory::utils::error::raise_error(function_name, e, "handlshake failed");
|
repertory::utils::error::raise_error(function_name, e, "handlshake failed");
|
||||||
}
|
}
|
||||||
@@ -169,10 +152,16 @@ void packet_server::initialize(const uint16_t &port, uint8_t pool_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void packet_server::listen_for_connection(tcp::acceptor &acceptor) {
|
void packet_server::listen_for_connection(tcp::acceptor &acceptor) {
|
||||||
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
auto conn =
|
auto conn =
|
||||||
std::make_shared<packet_server::connection>(io_context_, acceptor);
|
std::make_shared<packet_server::connection>(io_context_, acceptor);
|
||||||
acceptor.async_accept(conn->socket, [this, conn](auto &&err) {
|
acceptor.async_accept(conn->socket, [this, conn](auto &&err) {
|
||||||
on_accept(conn, std::forward<decltype(err)>(err));
|
try {
|
||||||
|
on_accept(conn, std::forward<decltype(err)>(err));
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
utils::error::raise_error(function_name, e, "exception occurred");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,9 +320,10 @@ void packet_server::send_response(std::shared_ptr<connection> conn,
|
|||||||
if (err) {
|
if (err) {
|
||||||
remove_client(*conn);
|
remove_client(*conn);
|
||||||
utils::error::raise_error(function_name, err.message());
|
utils::error::raise_error(function_name, err.message());
|
||||||
} else {
|
return;
|
||||||
read_header(conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
read_header(conn);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
|||||||
Reference in New Issue
Block a user