Skip to content

Commit 9392836

Browse files
committed
TCP input TLS - Add Ssl, wrapper around SSL.
1 parent 2b3e950 commit 9392836

File tree

3 files changed

+196
-0
lines changed

3 files changed

+196
-0
lines changed

src/plugins/input/tcp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_library(tcp-input MODULE
1313
src/Acceptor.cpp
1414
src/Plugin.cpp
1515
src/IpxPlugin.cpp
16+
src/tls/Ssl.cpp
1617
)
1718

1819
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_HOST_SYSTEM_NAME STREQUAL "OpenBSD")
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* \file
3+
* \author Jakub Antonín Štigler <[email protected]>
4+
* \brief Wrapper around SSL from OpenSSL. (source file)
5+
* \date 2025
6+
*
7+
* Copyright: (C) 2023 CESNET, z.s.p.o.
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
11+
#include "Ssl.hpp"
12+
13+
namespace tcp_in {
14+
namespace tls {
15+
16+
bool Ssl::accept() {
17+
auto res = SSL_accept(get());
18+
if (res <= 0) {
19+
auto err = SSL_get_error(get(), res);
20+
// Nonblocking client needs to wait.
21+
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
22+
return false;
23+
}
24+
throw_ssl_err(err, "Failed performing TLS handshake with client ({}).");
25+
}
26+
return true;
27+
}
28+
29+
ReadResult Ssl::read_ex(std::vector<std::uint8_t> &buf) {
30+
buf.resize(buf.capacity());
31+
std::size_t len;
32+
auto ret = SSL_read_ex(get(), buf.data(), buf.size(), &len);
33+
34+
if (ret <= 0) {
35+
auto err = SSL_get_error(get(), ret);
36+
buf.resize(0);
37+
switch (err) {
38+
case SSL_ERROR_ZERO_RETURN:
39+
return ReadResult::FINISHED;
40+
case SSL_ERROR_SYSCALL:
41+
return ReadResult::CLOSED;
42+
case SSL_ERROR_WANT_READ:
43+
case SSL_ERROR_WANT_WRITE:
44+
return ReadResult::WAIT;
45+
default:
46+
throw_ssl_err(err, "SSL failed to read data.");
47+
}
48+
}
49+
50+
buf.resize(std::min(len, buf.size()));
51+
return ReadResult::READ;
52+
}
53+
54+
} // namespace tls
55+
} // namespace tcp_in
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**
2+
* \file
3+
* \author Jakub Antonín Štigler <[email protected]>
4+
* \brief Wrapper around SSL from OpenSSL. (header file)
5+
* \date 2025
6+
*
7+
* Copyright: (C) 2023 CESNET, z.s.p.o.
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
11+
#pragma once
12+
13+
#include <cstdint>
14+
#include <memory>
15+
#include <vector>
16+
17+
#include <openssl/ssl.h>
18+
19+
#include "SslBio.hpp"
20+
#include "throw_ssl_err.hpp"
21+
22+
namespace tcp_in {
23+
namespace tls {
24+
25+
namespace dealloc {
26+
27+
/** Deletor for `SSL` from OpenSSL. Can be used for example in `std::unique_ptr` */
28+
class Ssl {
29+
public:
30+
void operator()(SSL *ssl) { SSL_free(ssl); }
31+
};
32+
33+
} // namespace dealloc
34+
35+
/** Describes result of read operation. */
36+
enum class ReadResult {
37+
/** Successufully read some data. */
38+
READ,
39+
/** Non blocking socket needs to wait for more data. */
40+
WAIT,
41+
/** Peer will not send any further data. */
42+
FINISHED,
43+
/** The connection has been closed. (socket cannot read) */
44+
CLOSED,
45+
};
46+
47+
/**
48+
* @brief Wrapper around `SSL` from OpenSSL.
49+
*
50+
* This is basically io stream for data encrypted with TLS.
51+
*/
52+
class Ssl : public std::unique_ptr<SSL, dealloc::Ssl> {
53+
public:
54+
/** Unique pointer of `SSL`. This is base class for `Ssl`. */
55+
using SelfPtr = std::unique_ptr<SSL, dealloc::Ssl>;
56+
57+
/**
58+
* @brief Construct `Ssl` from the given `SSL_CTX`.
59+
* @param ctx Base context for the ssl object.
60+
* @throws If failed to create new bio.
61+
*/
62+
Ssl(SSL_CTX *ctx) : SelfPtr(SSL_new(ctx)) {
63+
if (!get()) {
64+
throw_ssl_err("Failed to create SSL object.");
65+
}
66+
}
67+
68+
/**
69+
* @brief Set the bio object from which this will read data.
70+
* @param bio Bio object from which to read. This will take ownership of the given `SslBio`.
71+
*/
72+
void set_bio(SslBio bio) noexcept {
73+
auto raw_bio = bio.release();
74+
SSL_set_bio(get(), raw_bio, raw_bio);
75+
}
76+
77+
/**
78+
* @brief Set the server name for SNI.
79+
* @param hostname Name of the server.
80+
* @throws On failure.
81+
*/
82+
void set_tlsext_host_name(const char *hostname) {
83+
if (!SSL_set_tlsext_host_name(get(), hostname)) {
84+
throw_ssl_err("Failed to set the SNI hostname.");
85+
}
86+
}
87+
88+
/**
89+
* @brief Set the expected DNS name of the server.
90+
* @param hostname Hostname of the server.
91+
* @throws On failure.
92+
*/
93+
void set1_host(const char *hostname) {
94+
if (!SSL_set1_host(get(), hostname)) {
95+
throw_ssl_err(
96+
"Failed to set the cergificate verification hostname."
97+
);
98+
}
99+
}
100+
101+
/**
102+
* @brief Perform TLS handshake.
103+
*
104+
* In case of nonblocking server, this can be multiple times to continue the tls handshake.
105+
* @returns `true` if TLS handshake is successfully complete. `false` if the tls handshake is
106+
* incomplete because the underlaying socket is nonblocking and it needs to wait for more data.
107+
* @throws On failure.
108+
*/
109+
bool accept();
110+
111+
/**
112+
* @brief Read application data from the TLS connection.
113+
*
114+
* @param [out] buf Will be cleared. The read data will be put here. Up to the capacity of the
115+
* buffer will be used.
116+
* @return result of the read operation:
117+
* - `READ`: Successfully read some data. `buf` shouldn't be empty.
118+
* - `WAIT`: Non blocking socket needs to wait for more data. `buf` should be empty.
119+
* - `FINISHED`: Peer will not send any further data. `buf` should be empty.
120+
* - `CLOSED`: The connection has been closed. (socket cannot read) `buf` should be empty.
121+
* @throws On failure.
122+
*/
123+
ReadResult read_ex(std::vector<std::uint8_t> &buf);
124+
125+
/**
126+
* @brief Shuts the connection down.
127+
* @return `true` on successful shutdown, false if shutdown is in process but not complete yet.
128+
* @throws On failure.
129+
*/
130+
bool shutdown() {
131+
auto ret = SSL_shutdown(get());
132+
if (ret < 0) {
133+
throw_ssl_err("SSL failed to shutdown connection.");
134+
}
135+
return ret == 1;
136+
}
137+
};
138+
139+
} // namespace tls
140+
} // namespace tcp_in

0 commit comments

Comments
 (0)