added handshake for dos protection
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Remote mounts must be upgraded to v2.1.0+ to support new authentication scheme
|
||||
* Protocol handshake added for DoS protection
|
||||
|
||||
### 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 encrypt(std::string_view token);
|
||||
void encrypt(std::string_view token, bool include_size = true);
|
||||
|
||||
[[nodiscard]] auto get_size() const -> std::uint32_t {
|
||||
return static_cast<std::uint32_t>(buffer_.size());
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#define REPERTORY_INCLUDE_COMM_PACKET_PACKET_SERVER_HPP_
|
||||
|
||||
#include "comm/packet/client_pool.hpp"
|
||||
#include "comm/packet/common.hpp"
|
||||
#include "utils/common.hpp"
|
||||
|
||||
using namespace boost::asio;
|
||||
@@ -61,14 +62,16 @@ private:
|
||||
std::string client_id;
|
||||
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:
|
||||
std::string encryption_token_;
|
||||
closed_callback closed_;
|
||||
message_handler_callback message_handler_;
|
||||
io_context io_context_;
|
||||
mutable io_context io_context_;
|
||||
std::unique_ptr<std::thread> server_thread_;
|
||||
std::vector<std::thread> service_threads_;
|
||||
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);
|
||||
}
|
||||
|
||||
void packet::encrypt(std::string_view token) {
|
||||
void packet::encrypt(std::string_view token, bool include_size) {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(token, buffer_, 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) {
|
||||
utils::error::raise_error(function_name, e, "exception occurred");
|
||||
}
|
||||
|
@@ -21,8 +21,7 @@
|
||||
*/
|
||||
#include "comm/packet/packet_client.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/packet_client_timeout.hpp"
|
||||
#include "comm/packet/common.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/collection.hpp"
|
||||
@@ -30,199 +29,7 @@
|
||||
#include "utils/error_utils.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
namespace {
|
||||
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
|
||||
using namespace repertory::comm;
|
||||
|
||||
namespace repertory {
|
||||
packet_client::packet_client(remote::remote_config cfg)
|
||||
@@ -318,38 +125,21 @@ auto packet_client::handshake(client &cli) const -> bool {
|
||||
data_buffer buffer;
|
||||
{
|
||||
packet tmp;
|
||||
tmp.encode_top(cli.nonce);
|
||||
tmp.encode_top(utils::generate_random_string(packet_nonce_size));
|
||||
tmp.to_buffer(buffer);
|
||||
}
|
||||
auto to_read{buffer.size()};
|
||||
|
||||
std::uint32_t total_read{};
|
||||
while ((total_read < to_read) && cli.socket.is_open()) {
|
||||
auto bytes_read = boost::asio::read(
|
||||
cli.socket,
|
||||
boost::asio::buffer(&buffer[total_read], buffer.size() - total_read));
|
||||
if (bytes_read <= 0) {
|
||||
throw std::runtime_error("0 bytes read");
|
||||
}
|
||||
read_exact_with_deadline(io_context_, cli.socket,
|
||||
boost::asio::buffer(buffer),
|
||||
std::chrono::milliseconds(cfg_.recv_timeout_ms));
|
||||
packet response(buffer);
|
||||
response.encrypt(cfg_.encryption_token, false);
|
||||
response.to_buffer(buffer);
|
||||
|
||||
total_read += static_cast<std::uint32_t>(bytes_read);
|
||||
}
|
||||
|
||||
if (total_read == to_read) {
|
||||
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");
|
||||
write_all_with_deadline(io_context_, cli.socket,
|
||||
boost::asio::buffer(buffer),
|
||||
std::chrono::milliseconds(cfg_.send_timeout_ms));
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
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()});
|
||||
|
||||
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();
|
||||
if (current_client) {
|
||||
try {
|
||||
@@ -456,7 +247,7 @@ auto packet_client::send(std::string_view method, packet &request,
|
||||
utils::error::raise_error(function_name, e, "send failed");
|
||||
|
||||
close(*current_client);
|
||||
if (allow_connections_ && (retry < max_attempts)) {
|
||||
if (allow_connections_ && (retry < max_read_attempts)) {
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
#include "comm/packet/packet_server.hpp"
|
||||
|
||||
#include "comm/packet/common.hpp"
|
||||
#include "comm/packet/packet.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/service_start_begin.hpp"
|
||||
@@ -31,9 +32,10 @@
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
using namespace repertory::comm;
|
||||
using std::thread;
|
||||
|
||||
namespace repertory {
|
||||
packet_server::packet_server(std::uint16_t port, std::string token,
|
||||
std::uint8_t pool_size, closed_callback closed,
|
||||
message_handler_callback message_handler)
|
||||
@@ -89,49 +91,30 @@ auto packet_server::handshake(std::shared_ptr<connection> conn) const -> bool {
|
||||
packet request;
|
||||
request.encode_top(conn->nonce);
|
||||
request.to_buffer(buffer);
|
||||
auto to_read{buffer.size()};
|
||||
auto to_read{buffer.size() + utils::encryption::encryption_header_size};
|
||||
|
||||
auto written = boost::asio::write(
|
||||
conn->socket, boost::asio::buffer(boost::asio::buffer(buffer)));
|
||||
if (written == to_read) {
|
||||
conn->buffer.resize(to_read);
|
||||
write_all_with_deadline(io_context_, conn->socket,
|
||||
boost::asio::buffer(buffer),
|
||||
std::chrono::milliseconds(3000U));
|
||||
|
||||
std::uint32_t total_read{};
|
||||
while ((total_read < to_read) && conn->socket.is_open()) {
|
||||
auto bytes_read = boost::asio::read(
|
||||
conn->socket,
|
||||
boost::asio::buffer(&conn->buffer[total_read],
|
||||
conn->buffer.size() - total_read));
|
||||
if (bytes_read <= 0) {
|
||||
throw std::runtime_error("0 bytes read");
|
||||
conn->buffer.resize(to_read);
|
||||
read_exact_with_deadline(io_context_, conn->socket,
|
||||
boost::asio::buffer(conn->buffer),
|
||||
std::chrono::milliseconds(3000U));
|
||||
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;
|
||||
}
|
||||
|
||||
total_read += static_cast<std::uint32_t>(bytes_read);
|
||||
}
|
||||
|
||||
if (total_read == to_read) {
|
||||
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("invalid nonce");
|
||||
}
|
||||
|
||||
throw std::runtime_error("failed to send handshake");
|
||||
throw std::runtime_error("decryption failed");
|
||||
} catch (const std::exception &e) {
|
||||
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) {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
auto conn =
|
||||
std::make_shared<packet_server::connection>(io_context_, acceptor);
|
||||
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) {
|
||||
remove_client(*conn);
|
||||
utils::error::raise_error(function_name, err.message());
|
||||
} else {
|
||||
read_header(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
read_header(conn);
|
||||
});
|
||||
}
|
||||
} // namespace repertory
|
||||
|
Reference in New Issue
Block a user