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, typename Config>
61 SocketConnection<PhysicalSocket, Config>::SocketConnection(PhysicalSocket&& physicalSocket,
62 const std::function<void(SocketConnection*)>& onDisconnect,
63 const std::shared_ptr<Config>& config)
64 : Super(
65 std::move(physicalSocket),
66 [onDisconnect, this]() {
67 onDisconnect(this);
68 },
69 config) {
70 }
71
72 template <typename PhysicalSocket, typename Config>
73 SSL* SocketConnection<PhysicalSocket, Config>::getSSL() const {
74 return ssl;
75 }
76
77 template <typename PhysicalSocket, typename Config>
78 SSL* SocketConnection<PhysicalSocket, Config>::startSSL(
79 int fd, SSL_CTX* ctx, const utils::Timeval& sslInitTimeout, const utils::Timeval& sslShutdownTimeout, bool closeNotifyIsEOF) {
80 this->sslInitTimeout = sslInitTimeout;
81 this->sslShutdownTimeout = sslShutdownTimeout;
82 if (ctx != nullptr) {
83 ssl = SSL_new(ctx);
84
85 if (ssl != nullptr) {
86 SSL_set_ex_data(ssl, 0, const_cast<std::string*>(&Super::getConnectionName()));
87
88 if (SSL_set_fd(ssl, fd) == 1) {
89 SocketReader::ssl = ssl;
90 SocketWriter::ssl = ssl;
91 SocketReader::closeNotifyIsEOF = closeNotifyIsEOF;
92 SocketWriter::closeNotifyIsEOF = closeNotifyIsEOF;
93 } else {
94 SSL_free(ssl);
95 ssl = nullptr;
96 }
97 }
98 }
99
100 return ssl;
101 }
102
103 template <typename PhysicalSocket, typename Config>
104 void SocketConnection<PhysicalSocket, Config>::stopSSL() {
105 if (ssl != nullptr) {
106 SSL_free(ssl);
107
108 ssl = nullptr;
109 SocketReader::ssl = nullptr;
110 SocketWriter::ssl = nullptr;
111 }
112 }
113
114 template <typename PhysicalSocket, typename Config>
115 bool SocketConnection<PhysicalSocket, Config>::doSSLHandshake(const std::function<void()>& onSuccess,
116 const std::function<void()>& onTimeout,
117 const std::function<void(int)>& onStatus) {
118 if (ssl != nullptr) {
119 if (!SocketReader::isSuspended()) {
120 SocketReader::suspend();
121 }
122 if (!SocketWriter::isSuspended()) {
123 SocketWriter::suspend();
124 }
125
127 Super::getConnectionName(),
128 ssl,
129 [onSuccess, this]() { // onSuccess
130 SocketReader::span();
131 onSuccess();
132 },
133 [onTimeout]() { // onTimeout
134 onTimeout();
135 },
136 [onStatus](int sslErr) { // onStatus
137 onStatus(sslErr);
138 },
140 }
141
142 return ssl != nullptr;
143 }
144
145 template <typename PhysicalSocket, typename Config>
146 void SocketConnection<PhysicalSocket, Config>::doSSLShutdown() {
147 bool resumeSocketReader = false;
148 bool resumeSocketWriter = false;
149
150 if (!SocketReader::isSuspended()) {
151 SocketReader::suspend();
152 resumeSocketReader = true;
153 }
154
155 if (!SocketWriter::isSuspended()) {
156 SocketWriter::suspend();
157 resumeSocketWriter = true;
158 }
159
161 Super::getConnectionName(),
162 ssl,
163 [this, resumeSocketReader, resumeSocketWriter]() { // onSuccess
164 if (resumeSocketReader) {
165 SocketReader::resume();
166 }
167 if (resumeSocketWriter) {
168 SocketWriter::resume();
169 }
170 if (SSL_get_shutdown(ssl) == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN)) {
171 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify received and sent";
172 } else {
173 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify sent";
174
175 if (SSL_get_shutdown(ssl) == SSL_SENT_SHUTDOWN && SocketWriter::closeNotifyIsEOF) {
176 LOG(TRACE) << Super::getConnectionName() << " SSL/TLS: Close_notify is EOF: setting sslShutdownTimeout to "
177 << sslShutdownTimeout << " sec";
178 Super::setTimeout(sslShutdownTimeout);
179 }
180 }
181 },
182 [this, resumeSocketReader, resumeSocketWriter]() { // onTimeout
183 if (resumeSocketReader) {
184 SocketReader::resume();
185 }
186 if (resumeSocketWriter) {
187 SocketWriter::resume();
188 }
189 LOG(ERROR) << Super::getConnectionName() << " SSL/TLS: Shutdown handshake timed out";
190 Super::doWriteShutdown([this]() {
191 SocketConnection::close();
192 });
193 },
194 [this, resumeSocketReader, resumeSocketWriter](int sslErr) { // onStatus
195 if (resumeSocketReader) {
196 SocketReader::resume();
197 }
198 if (resumeSocketWriter) {
199 SocketWriter::resume();
200 }
201 ssl_log(Super::getConnectionName() + " SSL/TLS: Shutdown handshake failed", sslErr);
202 Super::doWriteShutdown([this]() {
203 SocketConnection::close();
204 });
205 },
207 }
208
209 template <typename PhysicalSocket, typename Config>
210 void SocketConnection<PhysicalSocket, Config>::onReadShutdown() {
211 if ((SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) != 0) {
212 if ((SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) != 0) {
213 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify sent and received";
214
215 SocketWriter::shutdownInProgress = false;
216 } else {
217 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Close_notify received";
218
220 }
221 } else {
222 LOG(ERROR) << Super::getConnectionName() << " SSL/TLS: Unexpected EOF error";
223
224 SocketWriter::shutdownInProgress = false;
225 SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
226 }
227 }
228
229 template <typename PhysicalSocket, typename Config>
230 void SocketConnection<PhysicalSocket, Config>::doWriteShutdown(const std::function<void()>& onShutdown) {
231 if ((SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) == 0) {
232 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Send close_notify";
233
235 } else {
236 Super::doWriteShutdown(onShutdown);
237 }
238 }
239
240} // namespace core::socket::stream::tls
SocketConnection(PhysicalSocket &&physicalSocket, const std::function< void(SocketConnection *)> &onDisconnect, const std::shared_ptr< Config > &config)
void doWriteShutdown(const std::function< void()> &onShutdown) final
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
static void doHandshake(const std::string &instanceName, SSL *ssl, const std::function< void(void)> &onSuccess, const std::function< void(void)> &onTimeout, const std::function< void(int)> &onStatus, const utils::Timeval &timeout)
static void doShutdown(const std::string &instanceName, SSL *ssl, const std::function< void(void)> &onSuccess, const std::function< void(void)> &onTimeout, const std::function< void(int)> &onStatus, const utils::Timeval &timeout)
Timeval & operator=(const Timeval &timeVal)
Definition Timeval.cpp:83