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/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#include "core/socket/stream/SocketConnection.hpp"
43#include "core/socket/stream/tls/SocketConnection.h"
44#include "core/socket/stream/tls/TLSHandshake.h"
45#include "core/socket/stream/tls/TLSShutdown.h"
46
47#ifndef DOXYGEN_SHOULD_SKIP_THIS
48
49#include "core/socket/stream/tls/ssl_utils.h"
50#include "log/Logger.h"
51
52#include <openssl/ssl.h>
53#include <string>
54#include <utility>
55
56#endif // DOXYGEN_SHOULD_SKIP_THIS
57
58namespace core::socket::stream::tls {
59
60 template <typename PhysicalSocket>
61 SocketConnection<PhysicalSocket>::SocketConnection(const std::string& instanceName,
62 PhysicalSocket&& physicalSocket,
63 const std::function<void(SocketConnection*)>& onDisconnect,
64 const std::string& configuredServer,
65 const SocketAddress& localAddress,
66 const SocketAddress& remoteAddress,
67 const utils::Timeval& readTimeout,
68 const utils::Timeval& writeTimeout,
69 std::size_t readBlockSize,
70 std::size_t writeBlockSize,
71 const utils::Timeval& terminateTimeout)
72 : Super(
75 [onDisconnect, this]() {
76 onDisconnect(this);
77 },
86 }
87
88 template <typename PhysicalSocket>
89 SSL* SocketConnection<PhysicalSocket>::getSSL() const {
90 return ssl;
91 }
92
93 template <typename PhysicalSocket>
94 SSL* SocketConnection<PhysicalSocket>::startSSL(
95 int fd, SSL_CTX* ctx, const utils::Timeval& sslInitTimeout, const utils::Timeval& sslShutdownTimeout, bool closeNotifyIsEOF) {
96 this->sslInitTimeout = sslInitTimeout;
97 this->sslShutdownTimeout = sslShutdownTimeout;
98 if (ctx != nullptr) {
99 ssl = SSL_new(ctx);
100
101 if (ssl != nullptr) {
102 SSL_set_ex_data(ssl, 0, const_cast<std::string*>(&Super::getConnectionName()));
103
104 if (SSL_set_fd(ssl, fd) == 1) {
105 SocketReader::ssl = ssl;
106 SocketWriter::ssl = ssl;
107 SocketReader::closeNotifyIsEOF = closeNotifyIsEOF;
108 SocketWriter::closeNotifyIsEOF = closeNotifyIsEOF;
109 } else {
110 SSL_free(ssl);
111 ssl = nullptr;
112 }
113 }
114 }
115
116 return ssl;
117 }
118
119 template <typename PhysicalSocket>
120 void SocketConnection<PhysicalSocket>::stopSSL() {
121 if (ssl != nullptr) {
122 SSL_free(ssl);
123
124 ssl = nullptr;
125 SocketReader::ssl = nullptr;
126 SocketWriter::ssl = nullptr;
127 }
128 }
129
130 template <typename PhysicalSocket>
131 bool SocketConnection<PhysicalSocket>::doSSLHandshake(const std::function<void()>& onSuccess,
132 const std::function<void()>& onTimeout,
133 const std::function<void(int)>& onStatus) {
134 if (ssl != nullptr) {
135 if (!SocketReader::isSuspended()) {
136 SocketReader::suspend();
137 }
138 if (!SocketWriter::isSuspended()) {
139 SocketWriter::suspend();
140 }
141
142 TLSHandshake::doHandshake(
143 Super::getConnectionName(),
144 ssl,
145 [onSuccess, this]() { // onSuccess
146 SocketReader::span();
147 onSuccess();
148 },
149 [onTimeout]() { // onTimeout
150 onTimeout();
151 },
152 [onStatus](int sslErr) { // onStatus
153 onStatus(sslErr);
154 },
155 sslInitTimeout);
156 }
157
158 return ssl != nullptr;
159 }
160
161 template <typename PhysicalSocket>
162 void SocketConnection<PhysicalSocket>::doSSLShutdown() {
163 bool resumeSocketReader = false;
164 bool resumeSocketWriter = false;
165
166 if (!SocketReader::isSuspended()) {
167 SocketReader::suspend();
168 resumeSocketReader = true;
169 }
170
171 if (!SocketWriter::isSuspended()) {
172 SocketWriter::suspend();
173 resumeSocketWriter = true;
174 }
175
176 TLSShutdown::doShutdown(
177 Super::getConnectionName(),
178 ssl,
179 [this, resumeSocketReader, resumeSocketWriter]() { // onSuccess
180 if (resumeSocketReader) {
181 SocketReader::resume();
182 }
183 if (resumeSocketWriter) {
184 SocketWriter::resume();
185 }
186 if (SSL_get_shutdown(ssl) == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN)) {
187 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify received and sent";
188 } else {
189 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify sent";
190
191 if (SSL_get_shutdown(ssl) == SSL_SENT_SHUTDOWN && SocketWriter::closeNotifyIsEOF) {
192 LOG(TRACE) << Super::getConnectionName() << " SSL/TLS: Close_notify is EOF: setting sslShutdownTimeout to "
193 << sslShutdownTimeout << " sec";
194 Super::setTimeout(sslShutdownTimeout);
195 }
196 }
197 },
198 [this, resumeSocketReader, resumeSocketWriter]() { // onTimeout
199 if (resumeSocketReader) {
200 SocketReader::resume();
201 }
202 if (resumeSocketWriter) {
203 SocketWriter::resume();
204 }
205 LOG(ERROR) << Super::getConnectionName() << " SSL/TLS: Shutdown handshake timed out";
206 Super::doWriteShutdown([this]() {
207 SocketConnection::close();
208 });
209 },
210 [this, resumeSocketReader, resumeSocketWriter](int sslErr) { // onStatus
211 if (resumeSocketReader) {
212 SocketReader::resume();
213 }
214 if (resumeSocketWriter) {
215 SocketWriter::resume();
216 }
217 ssl_log(Super::getConnectionName() + " SSL/TLS: Shutdown handshake failed", sslErr);
218 Super::doWriteShutdown([this]() {
219 SocketConnection::close();
220 });
221 },
222 sslShutdownTimeout);
223 }
224
225 template <typename PhysicalSocket>
226 void SocketConnection<PhysicalSocket>::onReadShutdown() {
227 if ((SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) != 0) {
228 if ((SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) != 0) {
229 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify sent and received";
230
231 SocketWriter::shutdownInProgress = false;
232 } else {
233 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify received";
234
236 }
237 } else {
238 LOG(ERROR) << Super::getConnectionName() << " SSL/TLS: Unexpected EOF error";
239
240 SocketWriter::shutdownInProgress = false;
241 SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
242 }
243 }
244
245 template <typename PhysicalSocket>
246 void SocketConnection<PhysicalSocket>::doWriteShutdown(const std::function<void()>& onShutdown) {
247 if ((SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) == 0) {
248 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Send close_notify";
249
251 } else {
252 Super::doWriteShutdown(onShutdown);
253 }
254 }
255
256} // 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:59