xrpld
Loading...
Searching...
No Matches
AutoSocket.h
1#pragma once
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/beast/net/IPAddressConversion.h>
5#include <xrpl/beast/net/IPEndpoint.h>
6
7#include <boost/asio.hpp>
8#include <boost/asio/ip/tcp.hpp>
9#include <boost/asio/ssl.hpp>
10#include <boost/beast/core/bind_handler.hpp>
11
12#include <cstddef>
13#include <functional>
14#include <memory>
15#include <string>
16#include <utility>
17#include <vector>
18
19// Socket wrapper that supports both SSL and non-SSL connections.
20// Generally, handle it as you would an SSL connection.
21// To force a non-SSL connection, just don't call async_handshake.
22// To force SSL only inbound, call setSSLOnly.
23
25{
26public:
27 using SslSocket = boost::asio::ssl::stream<boost::asio::ip::tcp::socket>;
28 using EndpointType = boost::asio::ip::tcp::socket::endpoint_type;
30 using PlainSocket = SslSocket::next_layer_type;
31 // NOLINTNEXTLINE(readability-identifier-naming) -- asio stream layer concept name
32 using lowest_layer_type = SslSocket::lowest_layer_type;
33 using HandshakeType = SslSocket::handshake_type;
34 using ErrorCode = boost::system::error_code;
36
37public:
39 boost::asio::io_context& s,
40 boost::asio::ssl::context& c,
41 bool secureOnly,
42 bool plainOnly)
43 : secure_(secureOnly)
44 , buffer_((plainOnly || secureOnly) ? 0 : 4)
45 , j_{beast::Journal::getNullSink()}
46 {
48 }
49
50 AutoSocket(boost::asio::io_context& s, boost::asio::ssl::context& c)
51 : AutoSocket(s, c, false, false)
52 {
53 }
54
55 [[nodiscard]] bool
56 isSecure() const
57 {
58 return secure_;
59 }
62 {
63 return *socket_;
64 }
67 {
68 return socket_->next_layer();
69 }
70
73 {
74 return beast::ip::fromAsio(lowestLayer().local_endpoint());
75 }
76
79 {
80 return beast::ip::fromAsio(lowestLayer().remote_endpoint());
81 }
82
85 {
86 return socket_->lowest_layer();
87 }
88
89 void
90 swap(AutoSocket& s) noexcept
91 {
92 buffer_.swap(s.buffer_);
93 socket_.swap(s.socket_);
94 std::swap(secure_, s.secure_);
95 }
96
97 boost::system::error_code
98 cancel(boost::system::error_code& ec)
99 {
100 return lowestLayer().cancel(ec);
101 }
102
103 void
105 {
106 if ((type == SslSocket::client) || (secure_))
107 {
108 // must be ssl
109 secure_ = true;
110 socket_->async_handshake(type, cbFunc);
111 }
112 else if (buffer_.empty())
113 {
114 // must be plain
115 secure_ = false;
116 post(socket_->get_executor(), boost::beast::bind_handler(cbFunc, ErrorCode()));
117 }
118 else
119 {
120 // autodetect
121 socket_->next_layer().async_receive(
122 boost::asio::buffer(buffer_),
123 boost::asio::socket_base::message_peek,
124 [this, cbFunc](ErrorCode const& ec, size_t bytesTransferred) {
125 handleAutodetect(cbFunc, ec, bytesTransferred);
126 });
127 }
128 }
129
130 template <typename ShutdownHandler>
131 void
132 asyncShutdown(ShutdownHandler handler)
133 {
134 if (isSecure())
135 {
136 socket_->async_shutdown(handler);
137 }
138 else
139 {
140 ErrorCode ec;
141 try
142 {
143 lowestLayer().shutdown(PlainSocket::shutdown_both);
144 }
145 catch (boost::system::system_error const& e)
146 {
147 ec = e.code();
148 }
149 post(socket_->get_executor(), boost::beast::bind_handler(handler, ec));
150 }
151 }
152
153 template <typename Seq, typename Handler>
154 void
155 asyncReadSome(Seq const& buffers, Handler handler)
156 {
157 if (isSecure())
158 {
159 socket_->async_read_some(buffers, handler);
160 }
161 else
162 {
163 plainSocket().async_read_some(buffers, handler);
164 }
165 }
166
167 template <typename Seq, typename Condition, typename Handler>
168 void
169 asyncReadUntil(Seq const& buffers, Condition condition, Handler handler)
170 {
171 if (isSecure())
172 {
173 boost::asio::async_read_until(*socket_, buffers, condition, handler);
174 }
175 else
176 {
177 boost::asio::async_read_until(plainSocket(), buffers, condition, handler);
178 }
179 }
180
181 template <typename Allocator, typename Handler>
182 void
184 boost::asio::basic_streambuf<Allocator>& buffers,
185 std::string const& delim,
186 Handler handler)
187 {
188 if (isSecure())
189 {
190 boost::asio::async_read_until(*socket_, buffers, delim, handler);
191 }
192 else
193 {
194 boost::asio::async_read_until(plainSocket(), buffers, delim, handler);
195 }
196 }
197
198 template <typename Allocator, typename MatchCondition, typename Handler>
199 void
201 boost::asio::basic_streambuf<Allocator>& buffers,
202 MatchCondition cond,
203 Handler handler)
204 {
205 if (isSecure())
206 {
207 boost::asio::async_read_until(*socket_, buffers, cond, handler);
208 }
209 else
210 {
211 boost::asio::async_read_until(plainSocket(), buffers, cond, handler);
212 }
213 }
214
215 template <typename Buf, typename Handler>
216 void
217 asyncWrite(Buf const& buffers, Handler handler)
218 {
219 if (isSecure())
220 {
221 boost::asio::async_write(*socket_, buffers, handler);
222 }
223 else
224 {
225 boost::asio::async_write(plainSocket(), buffers, handler);
226 }
227 }
228
229 template <typename Allocator, typename Handler>
230 void
231 asyncWrite(boost::asio::basic_streambuf<Allocator>& buffers, Handler handler)
232 {
233 if (isSecure())
234 {
235 boost::asio::async_write(*socket_, buffers, handler);
236 }
237 else
238 {
239 boost::asio::async_write(plainSocket(), buffers, handler);
240 }
241 }
242
243 template <typename Buf, typename Condition, typename Handler>
244 void
245 asyncRead(Buf const& buffers, Condition cond, Handler handler)
246 {
247 if (isSecure())
248 {
249 boost::asio::async_read(*socket_, buffers, cond, handler);
250 }
251 else
252 {
253 boost::asio::async_read(plainSocket(), buffers, cond, handler);
254 }
255 }
256
257 template <typename Allocator, typename Condition, typename Handler>
258 void
259 asyncRead(boost::asio::basic_streambuf<Allocator>& buffers, Condition cond, Handler handler)
260 {
261 if (isSecure())
262 {
263 boost::asio::async_read(*socket_, buffers, cond, handler);
264 }
265 else
266 {
267 boost::asio::async_read(plainSocket(), buffers, cond, handler);
268 }
269 }
270
271 template <typename Buf, typename Handler>
272 void
273 asyncRead(Buf const& buffers, Handler handler)
274 {
275 if (isSecure())
276 {
277 boost::asio::async_read(*socket_, buffers, handler);
278 }
279 else
280 {
281 boost::asio::async_read(plainSocket(), buffers, handler);
282 }
283 }
284
285 template <typename Seq, typename Handler>
286 void
287 asyncWriteSome(Seq const& buffers, Handler handler)
288 {
289 if (isSecure())
290 {
291 socket_->async_write_some(buffers, handler);
292 }
293 else
294 {
295 plainSocket().async_write_some(buffers, handler);
296 }
297 }
298
299protected:
300 void
301 handleAutodetect(Callback cbFunc, ErrorCode const& ec, size_t bytesTransferred)
302 {
303 using namespace xrpl;
304
305 if (ec)
306 {
307 JLOG(j_.warn()) << "Handle autodetect error: " << ec;
308 cbFunc(ec);
309 }
310 else if (
311 (buffer_[0] < 127) && (buffer_[0] > 31) &&
312 ((bytesTransferred < 2) || ((buffer_[1] < 127) && (buffer_[1] > 31))) &&
313 ((bytesTransferred < 3) || ((buffer_[2] < 127) && (buffer_[2] > 31))) &&
314 ((bytesTransferred < 4) || ((buffer_[3] < 127) && (buffer_[3] > 31))))
315 {
316 // not ssl
317 JLOG(j_.trace()) << "non-SSL";
318 secure_ = false;
319 cbFunc(ec);
320 }
321 else
322 {
323 // ssl
324 JLOG(j_.trace()) << "SSL";
325 secure_ = true;
326 socket_->async_handshake(SslSocket::server, cbFunc);
327 }
328 }
329
330private:
335};
boost::system::error_code ErrorCode
Definition AutoSocket.h:34
SslSocket::next_layer_type PlainSocket
Definition AutoSocket.h:30
AutoSocket(boost::asio::io_context &s, boost::asio::ssl::context &c, bool secureOnly, bool plainOnly)
Definition AutoSocket.h:38
lowest_layer_type & lowestLayer()
Definition AutoSocket.h:84
beast::Journal j_
Definition AutoSocket.h:334
beast::ip::Endpoint localEndpoint()
Definition AutoSocket.h:72
std::vector< char > buffer_
Definition AutoSocket.h:333
void asyncRead(Buf const &buffers, Condition cond, Handler handler)
Definition AutoSocket.h:245
AutoSocket(boost::asio::io_context &s, boost::asio::ssl::context &c)
Definition AutoSocket.h:50
SslSocket::handshake_type HandshakeType
Definition AutoSocket.h:33
std::function< void(ErrorCode)> Callback
Definition AutoSocket.h:35
void asyncReadUntil(Seq const &buffers, Condition condition, Handler handler)
Definition AutoSocket.h:169
void asyncReadUntil(boost::asio::basic_streambuf< Allocator > &buffers, std::string const &delim, Handler handler)
Definition AutoSocket.h:183
void handleAutodetect(Callback cbFunc, ErrorCode const &ec, size_t bytesTransferred)
Definition AutoSocket.h:301
void asyncHandshake(HandshakeType type, Callback cbFunc)
Definition AutoSocket.h:104
void asyncShutdown(ShutdownHandler handler)
Definition AutoSocket.h:132
void swap(AutoSocket &s) noexcept
Definition AutoSocket.h:90
std::unique_ptr< SslSocket > SocketPtr
Definition AutoSocket.h:29
SocketPtr socket_
Definition AutoSocket.h:331
beast::ip::Endpoint remoteEndpoint()
Definition AutoSocket.h:78
void asyncRead(Buf const &buffers, Handler handler)
Definition AutoSocket.h:273
void asyncWriteSome(Seq const &buffers, Handler handler)
Definition AutoSocket.h:287
void asyncRead(boost::asio::basic_streambuf< Allocator > &buffers, Condition cond, Handler handler)
Definition AutoSocket.h:259
SslSocket::lowest_layer_type lowest_layer_type
Definition AutoSocket.h:32
bool isSecure() const
Definition AutoSocket.h:56
boost::asio::ip::tcp::socket::endpoint_type EndpointType
Definition AutoSocket.h:28
boost::asio::ssl::stream< boost::asio::ip::tcp::socket > SslSocket
Definition AutoSocket.h:27
SslSocket & sslSocket()
Definition AutoSocket.h:61
void asyncReadSome(Seq const &buffers, Handler handler)
Definition AutoSocket.h:155
boost::system::error_code cancel(boost::system::error_code &ec)
Definition AutoSocket.h:98
PlainSocket & plainSocket()
Definition AutoSocket.h:66
void asyncWrite(Buf const &buffers, Handler handler)
Definition AutoSocket.h:217
void asyncReadUntil(boost::asio::basic_streambuf< Allocator > &buffers, MatchCondition cond, Handler handler)
Definition AutoSocket.h:200
void asyncWrite(boost::asio::basic_streambuf< Allocator > &buffers, Handler handler)
Definition AutoSocket.h:231
A generic endpoint for log messages.
Definition Journal.h:44
A version-independent IP address and port combination.
Definition IPEndpoint.h:24
T make_unique(T... args)
Endpoint fromAsio(boost::asio::ip::address const &address)
Convert to Endpoint.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
T swap(T... args)