10#include <condition_variable>
17 std::string_view client_id,
bool verify_cert)
18 : client_id_(client_id)
19 , verify_cert_(verify_cert)
38 if (lifecycle_.is_running())
42 "Client is already running",
43 "secure_messaging_udp_client::start_client");
50 "Host cannot be empty",
51 "secure_messaging_udp_client::start_client");
54 if (!lifecycle_.try_start())
58 "Client is already starting",
59 "secure_messaging_udp_client::start_client");
62 is_connected_.store(
false, std::memory_order_release);
65 auto result = do_start_impl(host, port);
68 lifecycle_.mark_stopped();
76 if (!lifecycle_.is_running())
81 if (!lifecycle_.prepare_stop())
86 is_connected_.store(
false, std::memory_order_release);
89 auto result = do_stop_impl();
92 lifecycle_.mark_stopped();
95 invoke_disconnected_callback();
102 lifecycle_.wait_for_stop();
126 if (!is_connected_.load(std::memory_order_acquire))
131 "secure_messaging_udp_client::send_packet");
138 "Data cannot be empty",
139 "secure_messaging_udp_client::send_packet");
142 return do_send_impl(std::move(data));
146 std::vector<uint8_t>&& data,
147 std::function<
void(std::error_code, std::size_t)> handler) ->
VoidResult
152 "Client not connected",
153 "secure_messaging_udp_client::send_packet_with_handler");
159 "Socket not available",
160 "secure_messaging_udp_client::send_packet_with_handler");
163 socket_->async_send(std::move(data), std::move(handler));
174 std::lock_guard<std::mutex> lock(udp_callback_mutex_);
175 udp_receive_callback_ = std::move(callback);
180 callbacks_.set<
to_index(callback_index::receive)>(std::move(callback));
185 callbacks_.set<
to_index(callback_index::connected)>(std::move(callback));
190 callbacks_.set<
to_index(callback_index::disconnected)>(std::move(callback));
195 callbacks_.set<
to_index(callback_index::error)>(std::move(callback));
204 is_connected_.store(
connected, std::memory_order_release);
209 callbacks_.invoke<
to_index(callback_index::receive)>(data);
214 callbacks_.invoke<
to_index(callback_index::connected)>();
219 callbacks_.invoke<
to_index(callback_index::disconnected)>();
224 callbacks_.invoke<
to_index(callback_index::error)>(ec);
234 ssl_ctx_ = SSL_CTX_new(DTLS_client_method());
238 "Failed to create DTLS context",
239 "secure_messaging_udp_client::init_ssl_context");
243 SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
248 SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_PEER,
nullptr);
250 SSL_CTX_set_default_verify_paths(ssl_ctx_);
254 SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_NONE,
nullptr);
261 std::string_view host,
unsigned short port) ->
VoidResult
264 auto ssl_result = init_ssl_context();
265 if (!ssl_result.is_ok())
271 io_context_ = std::make_unique<asio::io_context>();
274 asio::ip::udp::resolver resolver(*io_context_);
275 std::error_code resolve_ec;
276 auto endpoints = resolver.resolve(
279 std::to_string(port),
282 if (resolve_ec || endpoints.empty())
284 SSL_CTX_free(ssl_ctx_);
287 "Failed to resolve host: " + std::string(host),
288 "secure_messaging_udp_client::do_start_impl");
292 std::lock_guard<std::mutex> lock(endpoint_mutex_);
293 target_endpoint_ = *endpoints.begin();
297 asio::ip::udp::socket udp_socket(*io_context_, asio::ip::udp::v4());
300 socket_ = std::make_shared<internal::dtls_socket>(
301 std::move(udp_socket), ssl_ctx_);
305 std::lock_guard<std::mutex> lock(endpoint_mutex_);
306 socket_->set_peer_endpoint(target_endpoint_);
310 auto self = shared_from_this();
311 socket_->set_receive_callback(
312 [
this, self](
const std::vector<uint8_t>& data,
const asio::ip::udp::endpoint& sender)
316 std::lock_guard<std::mutex> lock(udp_callback_mutex_);
317 callback = udp_receive_callback_;
321 callback(data, sender);
325 socket_->set_error_callback(
326 [
this, self](std::error_code ec)
328 invoke_error_callback(ec);
335 thread_pool_ = std::make_shared<integration::basic_thread_pool>(std::thread::hardware_concurrency());
339 io_context_future_ = thread_pool_->
submit(
342 auto work_guard = asio::make_work_guard(*io_context_);
347 auto handshake_result = do_handshake();
348 if (!handshake_result.is_ok())
350 return handshake_result;
357 invoke_connected_callback();
364 std::mutex handshake_mutex;
365 std::condition_variable handshake_cv;
366 bool handshake_done =
false;
367 std::error_code handshake_error;
369 socket_->async_handshake(
371 [&](std::error_code ec)
374 std::lock_guard<std::mutex> lock(handshake_mutex);
375 handshake_done =
true;
376 handshake_error = ec;
378 handshake_cv.notify_one();
383 std::unique_lock<std::mutex> lock(handshake_mutex);
384 if (!handshake_cv.wait_for(lock, std::chrono::seconds(30),
385 [&] { return handshake_done; }))
388 "DTLS handshake timeout",
389 "secure_messaging_udp_client::do_handshake");
396 "DTLS handshake failed: " + handshake_error.message(),
397 "secure_messaging_udp_client::do_handshake");
408 socket_->stop_receive();
419 if (io_context_future_.valid())
421 io_context_future_.wait();
427 SSL_CTX_free(ssl_ctx_);
439 "Socket not available",
440 "secure_messaging_udp_client::do_send_impl");
443 socket_->async_send(std::move(data),
nullptr);
std::shared_ptr< kcenon::network::integration::thread_pool_interface > get_thread_pool()
Get current thread pool.
static network_context & instance()
Get the singleton instance.
auto invoke_disconnected_callback() -> void
Invokes the disconnected callback.
auto set_error_callback(error_callback_t callback) -> void
Sets the callback for errors.
std::atomic< bool > is_connected_
auto set_udp_receive_callback(udp_receive_callback_t callback) -> void
Sets a UDP-specific callback to handle received decrypted datagrams.
auto send_packet_with_handler(std::vector< uint8_t > &&data, std::function< void(std::error_code, std::size_t)> handler) -> VoidResult
Sends an encrypted datagram with a completion handler.
auto set_disconnected_callback(disconnected_callback_t callback) -> void
Sets the callback for disconnection.
auto do_start_impl(std::string_view host, unsigned short port) -> VoidResult
DTLS-specific implementation of client start.
auto init_ssl_context() -> VoidResult
Initializes the SSL context for DTLS.
auto do_send_impl(std::vector< uint8_t > &&data) -> VoidResult
DTLS-specific implementation of data send.
auto start_client(std::string_view host, unsigned short port) -> VoidResult
Starts the client and connects to the specified host and port.
std::function< void(const std::vector< uint8_t > &)> receive_callback_t
Callback type for received data.
~secure_messaging_udp_client() noexcept
Destructor. Automatically calls stop_client() if still running.
auto invoke_connected_callback() -> void
Invokes the connected callback.
auto do_stop_impl() -> VoidResult
DTLS-specific implementation of client stop.
std::function< void()> connected_callback_t
Callback type for connection established.
secure_messaging_udp_client(std::string_view client_id, bool verify_cert=true)
Constructs a secure_messaging_udp_client with an identifier.
auto stop_client() -> VoidResult
Stops the client and disconnects.
auto invoke_error_callback(std::error_code ec) -> void
Invokes the error callback.
auto invoke_receive_callback(const std::vector< uint8_t > &data) -> void
Invokes the receive callback.
std::function< void()> disconnected_callback_t
Callback type for disconnection.
auto client_id() const -> const std::string &
Returns the client identifier.
utils::lifecycle_manager lifecycle_
auto is_connected() const noexcept -> bool
Checks if the client is connected to the server.
auto send_packet(std::vector< uint8_t > &&data) -> VoidResult
Sends an encrypted datagram to the server.
auto is_running() const noexcept -> bool
Checks if the client is currently running.
auto set_receive_callback(receive_callback_t callback) -> void
Sets the callback for received data.
auto set_connected(bool connected) -> void
Sets the connected state.
auto wait_for_stop() -> void
Blocks until stop_client() is called.
std::function< void(const std::vector< uint8_t > &, const asio::ip::udp::endpoint &)> udp_receive_callback_t
UDP-specific receive callback type with sender endpoint.
std::function< void(std::error_code)> error_callback_t
Callback type for errors.
auto do_handshake() -> VoidResult
Performs the DTLS handshake.
auto set_connected_callback(connected_callback_t callback) -> void
Sets the callback for connection established.
virtual std::future< void > submit(std::function< void()> task)=0
Submit a task to the thread pool.
auto is_running() const -> bool
Checks if the component is currently running.
constexpr int invalid_argument
constexpr int internal_error
constexpr int already_exists
constexpr int connection_closed
constexpr int connection_timeout
constexpr int connection_failed
constexpr auto to_index(E e) noexcept -> std::size_t
Helper to convert enum to std::size_t for callback_manager access.
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
Global context for shared network system resources.
DTLS client class for encrypted UDP communication.