SNode.C
Loading...
Searching...
No Matches
SocketConnection.hpp
Go to the documentation of this file.
1/*
2 * SNode.C - a slim toolkit for network communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "core/socket/stream/SocketConnection.hpp"
21#include "core/socket/stream/tls/SocketConnection.h"
22#include "core/socket/stream/tls/TLSHandshake.h"
23#include "core/socket/stream/tls/TLSShutdown.h"
24
25#ifndef DOXYGEN_SHOULD_SKIP_THIS
26
27#include "core/socket/stream/tls/ssl_utils.h"
28#include "log/Logger.h"
29
30#include <openssl/ssl.h>
31#include <string>
32#include <utility>
33
34#endif // DOXYGEN_SHOULD_SKIP_THIS
35
36namespace core::socket::stream::tls {
37
38 template <typename PhysicalSocket>
39 SocketConnection<PhysicalSocket>::SocketConnection(const std::string& instanceName,
40 PhysicalSocket&& physicalSocket,
41 const std::function<void(SocketConnection*)>& onDisconnect,
42 const std::string& configuredServer,
43 const SocketAddress& localAddress,
44 const SocketAddress& remoteAddress,
45 const utils::Timeval& readTimeout,
46 const utils::Timeval& writeTimeout,
47 std::size_t readBlockSize,
48 std::size_t writeBlockSize,
49 const utils::Timeval& terminateTimeout)
50 : Super(
53 [onDisconnect, this]() {
54 onDisconnect(this);
55 },
64 }
65
66 template <typename PhysicalSocket>
67 SSL* SocketConnection<PhysicalSocket>::getSSL() const {
68 return ssl;
69 }
70
71 template <typename PhysicalSocket>
72 SSL* SocketConnection<PhysicalSocket>::startSSL(
73 int fd, SSL_CTX* ctx, const utils::Timeval& sslInitTimeout, const utils::Timeval& sslShutdownTimeout, bool closeNotifyIsEOF) {
74 this->sslInitTimeout = sslInitTimeout;
75 this->sslShutdownTimeout = sslShutdownTimeout;
76 if (ctx != nullptr) {
77 ssl = SSL_new(ctx);
78
79 if (ssl != nullptr) {
80 SSL_set_ex_data(ssl, 0, const_cast<std::string*>(&Super::getConnectionName()));
81
82 if (SSL_set_fd(ssl, fd) == 1) {
83 SocketReader::ssl = ssl;
84 SocketWriter::ssl = ssl;
85 SocketReader::closeNotifyIsEOF = closeNotifyIsEOF;
86 SocketWriter::closeNotifyIsEOF = closeNotifyIsEOF;
87 } else {
88 SSL_free(ssl);
89 ssl = nullptr;
90 }
91 }
92 }
93
94 return ssl;
95 }
96
97 template <typename PhysicalSocket>
98 void SocketConnection<PhysicalSocket>::stopSSL() {
99 if (ssl != nullptr) {
100 SSL_free(ssl);
101
102 ssl = nullptr;
103 SocketReader::ssl = nullptr;
104 SocketWriter::ssl = nullptr;
105 }
106 }
107
108 template <typename PhysicalSocket>
109 bool SocketConnection<PhysicalSocket>::doSSLHandshake(const std::function<void()>& onSuccess,
110 const std::function<void()>& onTimeout,
111 const std::function<void(int)>& onStatus) {
112 if (ssl != nullptr) {
113 if (!SocketReader::isSuspended()) {
114 SocketReader::suspend();
115 }
116 if (!SocketWriter::isSuspended()) {
117 SocketWriter::suspend();
118 }
119
120 TLSHandshake::doHandshake(
121 Super::getConnectionName(),
122 ssl,
123 [onSuccess, this]() { // onSuccess
124 SocketReader::span();
125 onSuccess();
126 },
127 [onTimeout]() { // onTimeout
128 onTimeout();
129 },
130 [onStatus](int sslErr) { // onStatus
131 onStatus(sslErr);
132 },
133 sslInitTimeout);
134 }
135
136 return ssl != nullptr;
137 }
138
139 template <typename PhysicalSocket>
140 void SocketConnection<PhysicalSocket>::doSSLShutdown() {
141 bool resumeSocketReader = false;
142 bool resumeSocketWriter = false;
143
144 if (!SocketReader::isSuspended()) {
145 SocketReader::suspend();
146 resumeSocketReader = true;
147 }
148
149 if (!SocketWriter::isSuspended()) {
150 SocketWriter::suspend();
151 resumeSocketWriter = true;
152 }
153
154 TLSShutdown::doShutdown(
155 Super::getConnectionName(),
156 ssl,
157 [this, resumeSocketReader, resumeSocketWriter]() { // onSuccess
158 if (resumeSocketReader) {
159 SocketReader::resume();
160 }
161 if (resumeSocketWriter) {
162 SocketWriter::resume();
163 }
164 if (SSL_get_shutdown(ssl) == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN)) {
165 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify received and sent";
166 } else {
167 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify sent";
168
169 if (SSL_get_shutdown(ssl) == SSL_SENT_SHUTDOWN && SocketWriter::closeNotifyIsEOF) {
170 LOG(TRACE) << Super::getConnectionName() << " SSL/TLS: Close_notify is EOF: setting sslShutdownTimeout to "
171 << sslShutdownTimeout << " sec";
172 Super::setTimeout(sslShutdownTimeout);
173 }
174 }
175 },
176 [this, resumeSocketReader, resumeSocketWriter]() { // onTimeout
177 if (resumeSocketReader) {
178 SocketReader::resume();
179 }
180 if (resumeSocketWriter) {
181 SocketWriter::resume();
182 }
183 LOG(ERROR) << Super::getConnectionName() << " SSL/TLS: Shutdown handshake timed out";
184 Super::doWriteShutdown([this]() {
185 SocketConnection::close();
186 });
187 },
188 [this, resumeSocketReader, resumeSocketWriter](int sslErr) { // onStatus
189 if (resumeSocketReader) {
190 SocketReader::resume();
191 }
192 if (resumeSocketWriter) {
193 SocketWriter::resume();
194 }
195 ssl_log(Super::getConnectionName() + " SSL/TLS: Shutdown handshake failed", sslErr);
196 Super::doWriteShutdown([this]() {
197 SocketConnection::close();
198 });
199 },
200 sslShutdownTimeout);
201 }
202
203 template <typename PhysicalSocket>
204 void SocketConnection<PhysicalSocket>::onReadShutdown() {
205 if ((SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) != 0) {
206 if ((SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) != 0) {
207 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify sent and received";
208
209 SocketWriter::shutdownInProgress = false;
210 } else {
211 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify received";
212
214 }
215 } else {
216 LOG(ERROR) << Super::getConnectionName() << " SSL/TLS: Unexpected EOF error";
217
218 SocketWriter::shutdownInProgress = false;
219 SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
220 }
221 }
222
223 template <typename PhysicalSocket>
224 void SocketConnection<PhysicalSocket>::doWriteShutdown(const std::function<void()>& onShutdown) {
225 if ((SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) == 0) {
226 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Send close_notify";
227
229 } else {
230 Super::doWriteShutdown(onShutdown);
231 }
232 }
233
234} // namespace core::socket::stream::tls
SocketConnection(const std::string &instanceName, PhysicalSocket &&physicalSocket, const std::function< void(SocketConnection *)> &onDisconnect, const std::string &configuredServer, const SocketAddress &localAddress, const SocketAddress &remoteAddress, const utils::Timeval &readTimeout, const utils::Timeval &writeTimeout, std::size_t readBlockSize, std::size_t writeBlockSize, const utils::Timeval &terminateTimeout)
SSL * startSSL(int fd, SSL_CTX *ctx, const utils::Timeval &sslInitTimeout, const utils::Timeval &sslShutdownTimeout, bool closeNotifyIsEOF)
bool doSSLHandshake(const std::function< void()> &onSuccess, const std::function< void()> &onTimeout, const std::function< void(int)> &onStatus) final
void doWriteShutdown(const std::function< void()> &onShutdown) final
Definition Config.h:37