16 class ssl_error_category :
public std::error_category
19 const char* name() const noexcept
override {
return "ssl"; }
21 std::string
message(
int ev)
const override
24 ERR_error_string_n(
static_cast<unsigned long>(ev), buf,
sizeof(buf));
29 const ssl_error_category& get_ssl_category()
31 static ssl_error_category instance;
35 std::error_code make_ssl_error_code(
int ssl_error)
37 return std::error_code(ssl_error, get_ssl_category());
43 asio::ip::udp::socket socket,
SSL_CTX* ssl_ctx)
46 SSL* ssl = SSL_new(ssl_ctx);
51 "Failed to create SSL object",
52 "dtls_socket::create");
56 BIO* rbio = BIO_new(BIO_s_mem());
57 BIO* wbio = BIO_new(BIO_s_mem());
60 if (rbio) BIO_free(rbio);
61 if (wbio) BIO_free(wbio);
65 "Failed to create BIO objects",
66 "dtls_socket::create");
70 BIO_set_nbio(rbio, 1);
71 BIO_set_nbio(wbio, 1);
74 SSL_set_bio(ssl, rbio, wbio);
76 return ok(std::shared_ptr<dtls_socket>(
81 : socket_(std::move(socket))
102 std::function<
void(std::error_code)> handler) ->
void
104 handshake_type_ = type;
105 handshake_callback_ = std::move(handler);
106 handshake_in_progress_.store(
true);
109 std::lock_guard<std::mutex> lock(ssl_mutex_);
111 if (type == handshake_type::client)
113 SSL_set_connect_state(ssl_);
117 SSL_set_accept_state(ssl_);
125 continue_handshake();
130 if (!handshake_in_progress_.load())
137 std::lock_guard<std::mutex> lock(ssl_mutex_);
138 result = SSL_do_handshake(ssl_);
144 handshake_in_progress_.store(
false);
145 handshake_complete_.store(
true);
150 std::function<void(std::error_code)> callback;
152 std::lock_guard<std::mutex> lock(callback_mutex_);
153 callback = std::move(handshake_callback_);
158 callback(std::error_code{});
165 std::lock_guard<std::mutex> lock(ssl_mutex_);
166 ssl_error = SSL_get_error(ssl_, result);
172 if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE)
179 handshake_in_progress_.store(
false);
181 std::function<void(std::error_code)> callback;
183 std::lock_guard<std::mutex> lock(callback_mutex_);
184 callback = std::move(handshake_callback_);
189 callback(make_ssl_error());
194 std::function<
void(
const std::vector<uint8_t>&,
195 const asio::ip::udp::endpoint&)> callback) ->
void
197 std::lock_guard<std::mutex> lock(callback_mutex_);
198 receive_callback_ = std::move(callback);
202 std::function<
void(std::error_code)> callback) ->
void
204 std::lock_guard<std::mutex> lock(callback_mutex_);
205 error_callback_ = std::move(callback);
210 bool expected =
false;
211 if (is_receiving_.compare_exchange_strong(expected,
true))
219 is_receiving_.store(
false);
224 if (!is_receiving_.load())
229 auto self = shared_from_this();
230 socket_.async_receive_from(
231 asio::buffer(read_buffer_),
233 [
this, self](std::error_code ec, std::size_t length)
235 if (!is_receiving_.load())
242 std::function<void(std::error_code)> callback;
244 std::lock_guard<std::mutex> lock(callback_mutex_);
245 callback = error_callback_;
256 std::vector<uint8_t> data(read_buffer_.begin(),
257 read_buffer_.begin() + length);
258 process_received_data(data, sender_endpoint_);
262 if (is_receiving_.load())
270 const asio::ip::udp::endpoint& sender) ->
void
280 process_received_data(data, sender);
284 const asio::ip::udp::endpoint& sender) ->
void
288 std::lock_guard<std::mutex> lock(ssl_mutex_);
289 int written = BIO_write(rbio_, data.data(),
static_cast<int>(data.size()));
298 if (handshake_in_progress_.load())
300 continue_handshake();
305 if (handshake_complete_.load())
307 std::vector<uint8_t> decrypted;
308 decrypted.resize(65536);
312 std::lock_guard<std::mutex> lock(ssl_mutex_);
313 read_len = SSL_read(ssl_, decrypted.data(),
static_cast<int>(decrypted.size()));
318 decrypted.resize(
static_cast<std::size_t
>(read_len));
320 std::function<void(
const std::vector<uint8_t>&,
const asio::ip::udp::endpoint&)> callback;
322 std::lock_guard<std::mutex> lock(callback_mutex_);
323 callback = receive_callback_;
327 callback(decrypted, sender);
334 std::lock_guard<std::mutex> lock(ssl_mutex_);
335 ssl_error = SSL_get_error(ssl_, read_len);
338 if (ssl_error != SSL_ERROR_WANT_READ && ssl_error != SSL_ERROR_WANT_WRITE)
341 std::function<void(std::error_code)> callback;
343 std::lock_guard<std::mutex> lock(callback_mutex_);
344 callback = error_callback_;
348 callback(make_ssl_error());
358 std::vector<uint8_t> output;
360 std::lock_guard<std::mutex> lock(ssl_mutex_);
361 int pending = BIO_ctrl_pending(wbio_);
367 output.resize(
static_cast<std::size_t
>(pending));
368 int read_len = BIO_read(wbio_, output.data(), pending);
373 output.resize(
static_cast<std::size_t
>(read_len));
377 asio::ip::udp::endpoint target;
379 std::lock_guard<std::mutex> lock(endpoint_mutex_);
380 target = peer_endpoint_;
383 if (target.port() != 0)
385 auto buffer = std::make_shared<std::vector<uint8_t>>(std::move(output));
386 socket_.async_send_to(
387 asio::buffer(*buffer),
389 [buffer](std::error_code , std::size_t )
397 std::vector<uint8_t>&& data,
398 std::function<
void(std::error_code, std::size_t)> handler) ->
void
400 asio::ip::udp::endpoint target;
402 std::lock_guard<std::mutex> lock(endpoint_mutex_);
403 target = peer_endpoint_;
406 async_send_to(std::move(data), target, std::move(handler));
410 std::vector<uint8_t>&& data,
411 const asio::ip::udp::endpoint& endpoint,
412 std::function<
void(std::error_code, std::size_t)> handler) ->
void
414 if (!handshake_complete_.load())
418 handler(std::make_error_code(std::errc::not_connected), 0);
426 std::lock_guard<std::mutex> lock(ssl_mutex_);
427 written = SSL_write(ssl_, data.data(),
static_cast<int>(data.size()));
434 handler(make_ssl_error(), 0);
440 std::vector<uint8_t> encrypted;
442 std::lock_guard<std::mutex> lock(ssl_mutex_);
443 int pending = BIO_ctrl_pending(wbio_);
448 handler(std::make_error_code(std::errc::io_error), 0);
453 encrypted.resize(
static_cast<std::size_t
>(pending));
454 int read_len = BIO_read(wbio_, encrypted.data(), pending);
459 handler(std::make_error_code(std::errc::io_error), 0);
463 encrypted.resize(
static_cast<std::size_t
>(read_len));
467 auto self = shared_from_this();
468 auto buffer = std::make_shared<std::vector<uint8_t>>(std::move(encrypted));
469 auto original_size = data.size();
471 socket_.async_send_to(
472 asio::buffer(*buffer),
474 [handler = std::move(handler), buffer, original_size](
475 std::error_code ec, std::size_t )
480 handler(ec, ec ? 0 : original_size);
487 std::lock_guard<std::mutex> lock(endpoint_mutex_);
488 peer_endpoint_ = endpoint;
493 std::lock_guard<std::mutex> lock(
const_cast<std::mutex&
>(
endpoint_mutex_));
499 unsigned long err = ERR_get_error();
502 return std::make_error_code(std::errc::io_error);
504 return make_ssl_error_code(
static_cast<int>(err));
auto async_send(std::vector< uint8_t > &&data, std::function< void(std::error_code, std::size_t)> handler) -> void
Initiates an asynchronous encrypted send.
auto deliver_encrypted(const std::vector< uint8_t > &data, const asio::ip::udp::endpoint &sender) -> void
Injects an already-received encrypted datagram for DTLS processing.
static Result< std::shared_ptr< dtls_socket > > create(asio::ip::udp::socket socket, SSL_CTX *ssl_ctx)
Constructs a dtls_socket with an existing UDP socket.
dtls_socket(const dtls_socket &)=delete
auto async_handshake(handshake_type type, std::function< void(std::error_code)> handler) -> void
Performs asynchronous DTLS handshake.
asio::ip::udp::endpoint peer_endpoint_
auto make_ssl_error() const -> std::error_code
Creates an OpenSSL error code from the current error state.
handshake_type
Handshake type enumeration.
auto set_receive_callback(std::function< void(const std::vector< uint8_t > &, const asio::ip::udp::endpoint &)> callback) -> void
Sets a callback to receive decrypted inbound datagrams.
auto set_error_callback(std::function< void(std::error_code)> callback) -> void
Sets a callback to handle socket errors.
auto stop_receive() -> void
Stops the receive loop.
auto set_peer_endpoint(const asio::ip::udp::endpoint &endpoint) -> void
Sets the peer endpoint for connected mode.
auto flush_bio_output() -> void
Flushes pending DTLS output to the network.
auto start_receive() -> void
Begins the continuous asynchronous receive loop.
auto process_received_data(const std::vector< uint8_t > &data, const asio::ip::udp::endpoint &sender) -> void
Processes received encrypted data through DTLS.
std::mutex endpoint_mutex_
auto socket() -> asio::ip::udp::socket &
Provides direct access to the underlying UDP socket.
auto peer_endpoint() const -> asio::ip::udp::endpoint
Returns the peer endpoint.
auto continue_handshake() -> void
Continues the handshake process.
auto do_receive() -> void
Internal function to handle the receive logic.
auto async_send_to(std::vector< uint8_t > &&data, const asio::ip::udp::endpoint &endpoint, std::function< void(std::error_code, std::size_t)> handler) -> void
Initiates an asynchronous encrypted send to a specific endpoint.
~dtls_socket()
Destructor. Cleans up OpenSSL resources.
struct ssl_ctx_st SSL_CTX
constexpr int internal_error
@ error
Black hole detected, reset to base.
OpenSSL utilities and version definitions.