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 , sslInitTimeout(config->getInitTimeout())
71 , sslShutdownTimeout(config->getShutdownTimeout())
72 , closeNotifyIsEOF(!config->getNoCloseNotifyIsEOF()) {
73 }
74
75 template <typename PhysicalSocket, typename Config>
76 SSL* SocketConnection<PhysicalSocket, Config>::getSSL() const {
77 return ssl;
78 }
79
80 template <typename PhysicalSocket, typename Config>
81 SSL* SocketConnection<PhysicalSocket, Config>::startSSL(int fd, SSL_CTX* ctx) {
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 } else {
92 SSL_free(ssl);
93 ssl = nullptr;
94 }
95 }
96 }
97
98 return ssl;
99 }
100
101 template <typename PhysicalSocket, typename Config>
102 void SocketConnection<PhysicalSocket, Config>::stopSSL() {
103 if (ssl != nullptr) {
104 SSL_free(ssl);
105
106 ssl = nullptr;
107 SocketReader::ssl = nullptr;
108 SocketWriter::ssl = nullptr;
109 }
110 }
111
112 template <typename PhysicalSocket, typename Config>
113 bool SocketConnection<PhysicalSocket, Config>::doSSLHandshake(const std::function<void()>& onSuccess,
114 const std::function<void()>& onTimeout,
115 const std::function<void(int)>& onStatus) {
116 if (ssl != nullptr) {
117 if (!SocketReader::isSuspended()) {
118 SocketReader::suspend();
119 }
120 if (!SocketWriter::isSuspended()) {
121 SocketWriter::suspend();
122 }
123
125 Super::getConnectionName(),
126 ssl,
127 [onSuccess, this]() { // onSuccess
128 SocketReader::span();
129 onSuccess();
130 },
131 [onTimeout]() { // onTimeout
132 onTimeout();
133 },
134 [onStatus](int sslErr) { // onStatus
135 onStatus(sslErr);
136 },
138 }
139
140 return ssl != nullptr;
141 }
142
143 template <typename PhysicalSocket, typename Config>
144 void SocketConnection<PhysicalSocket, Config>::doSSLShutdown() {
145 bool resumeSocketReader = false;
146 bool resumeSocketWriter = false;
147
148 if (!SocketReader::isSuspended()) {
149 SocketReader::suspend();
150 resumeSocketReader = true;
151 }
152
153 if (!SocketWriter::isSuspended()) {
154 SocketWriter::suspend();
155 resumeSocketWriter = true;
156 }
157
159 Super::getConnectionName(),
160 ssl,
161 [this, resumeSocketReader, resumeSocketWriter]() { // onSuccess
162 if (resumeSocketReader) {
163 SocketReader::resume();
164 }
165 if (resumeSocketWriter) {
166 SocketWriter::resume();
167 }
168 if (SSL_get_shutdown(ssl) == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN)) {
169 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Passive close_notify received and sent";
170 } else {
171 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Active close_notify sent";
172 }
173 },
174 [this, resumeSocketReader, resumeSocketWriter]() { // onTimeout
175 if (resumeSocketReader) {
176 SocketReader::resume();
177 }
178 if (resumeSocketWriter) {
179 SocketWriter::resume();
180 }
181 LOG(ERROR) << Super::getConnectionName() << " SSL/TLS: Shutdown handshake timed out";
182 Super::doWriteShutdown([this]() {
183 SocketConnection::close();
184 });
185 },
186 [this, resumeSocketReader, resumeSocketWriter](int sslErr) { // onStatus
187 if (resumeSocketReader) {
188 SocketReader::resume();
189 }
190 if (resumeSocketWriter) {
191 SocketWriter::resume();
192 }
193 ssl_log(Super::getConnectionName() + " SSL/TLS: Shutdown handshake failed", sslErr);
194 Super::doWriteShutdown([this]() {
195 SocketConnection::close();
196 });
197 },
199 }
200
201 template <typename PhysicalSocket, typename Config>
202 void SocketConnection<PhysicalSocket, Config>::onReadShutdown() {
203 if ((SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) != 0) {
204 if ((SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) != 0) {
205 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Active close_notify sent and received";
206 SocketWriter::shutdownInProgress = false;
207
208 if (closeNotifyIsEOF) {
209 this->onReadError(0);
210 }
211 } else {
212 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Passive close_notify received, answering with close_notify";
213
215 }
216 } else {
217 LOG(ERROR) << Super::getConnectionName() << " SSL/TLS: Unexpected EOF error";
218
219 SocketWriter::shutdownInProgress = false;
220 SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
221 }
222 }
223
224 template <typename PhysicalSocket, typename Config>
225 void SocketConnection<PhysicalSocket, Config>::doWriteShutdown(const std::function<void()>& onShutdown) {
226 if ((SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) == 0) {
227 LOG(DEBUG) << Super::getConnectionName() << " SSL/TLS: Active send close_notify";
228
230 } else {
231 Super::doWriteShutdown(onShutdown);
232 }
233 }
234
235} // namespace core::socket::stream::tls
SocketConnection(PhysicalSocket &&physicalSocket, const std::function< void(SocketConnection *)> &onDisconnect, const std::shared_ptr< Config > &config)
typename Super::SocketReader SocketReader
core::socket::stream:: SocketConnectionT< PhysicalSocketT, core::socket::stream::tls::SocketReader, core::socket::stream::tls::SocketWriter, ConfigT > Super
void doWriteShutdown(const std::function< void()> &onShutdown) final
bool doSSLHandshake(const std::function< void()> &onSuccess, const std::function< void()> &onTimeout, const std::function< void(int)> &onStatus) final
typename Super::SocketWriter SocketWriter
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)