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 ssl_socket = boost::asio::ssl::stream<boost::asio::ip::tcp::socket>;
28 using endpoint_type = boost::asio::ip::tcp::socket::endpoint_type;
30 using plain_socket = ssl_socket::next_layer_type;
31 using lowest_layer_type = ssl_socket::lowest_layer_type;
32 using handshake_type = ssl_socket::handshake_type;
33 using error_code = boost::system::error_code;
35
36public:
38 boost::asio::io_context& s,
39 boost::asio::ssl::context& c,
40 bool secureOnly,
41 bool plainOnly)
42 : secure_(secureOnly)
43 , buffer_((plainOnly || secureOnly) ? 0 : 4)
44 , j_{beast::Journal::getNullSink()}
45 {
47 }
48
49 AutoSocket(boost::asio::io_context& s, boost::asio::ssl::context& c)
50 : AutoSocket(s, c, false, false)
51 {
52 }
53
54 [[nodiscard]] bool
55 isSecure() const
56 {
57 return secure_;
58 }
61 {
62 return *socket_;
63 }
66 {
67 return socket_->next_layer();
68 }
69
72 {
73 return beast::ip::fromAsio(lowestLayer().local_endpoint());
74 }
75
78 {
79 return beast::ip::fromAsio(lowestLayer().remote_endpoint());
80 }
81
84 {
85 return socket_->lowest_layer();
86 }
87
88 void
89 swap(AutoSocket& s) noexcept
90 {
91 buffer_.swap(s.buffer_);
92 socket_.swap(s.socket_);
93 std::swap(secure_, s.secure_);
94 }
95
96 boost::system::error_code
97 cancel(boost::system::error_code& ec)
98 {
99 return lowestLayer().cancel(ec);
100 }
101
102 void
104 {
105 if ((type == ssl_socket::client) || (secure_))
106 {
107 // must be ssl
108 secure_ = true;
109 socket_->async_handshake(type, cbFunc);
110 }
111 else if (buffer_.empty())
112 {
113 // must be plain
114 secure_ = false;
115 post(socket_->get_executor(), boost::beast::bind_handler(cbFunc, error_code()));
116 }
117 else
118 {
119 // autodetect
120 socket_->next_layer().async_receive(
121 boost::asio::buffer(buffer_),
122 boost::asio::socket_base::message_peek,
123 [this, cbFunc](error_code const& ec, size_t bytesTransferred) {
124 handleAutodetect(cbFunc, ec, bytesTransferred);
125 });
126 }
127 }
128
129 template <typename ShutdownHandler>
130 void
131 asyncShutdown(ShutdownHandler handler)
132 {
133 if (isSecure())
134 {
135 socket_->async_shutdown(handler);
136 }
137 else
138 {
139 error_code ec;
140 try
141 {
142 lowestLayer().shutdown(plain_socket::shutdown_both);
143 }
144 catch (boost::system::system_error const& e)
145 {
146 ec = e.code();
147 }
148 post(socket_->get_executor(), boost::beast::bind_handler(handler, ec));
149 }
150 }
151
152 template <typename Seq, typename Handler>
153 void
154 asyncReadSome(Seq const& buffers, Handler handler)
155 {
156 if (isSecure())
157 {
158 socket_->async_read_some(buffers, handler);
159 }
160 else
161 {
162 plainSocket().async_read_some(buffers, handler);
163 }
164 }
165
166 template <typename Seq, typename Condition, typename Handler>
167 void
168 asyncReadUntil(Seq const& buffers, Condition condition, Handler handler)
169 {
170 if (isSecure())
171 {
172 boost::asio::async_read_until(*socket_, buffers, condition, handler);
173 }
174 else
175 {
176 boost::asio::async_read_until(plainSocket(), buffers, condition, handler);
177 }
178 }
179
180 template <typename Allocator, typename Handler>
181 void
183 boost::asio::basic_streambuf<Allocator>& buffers,
184 std::string const& delim,
185 Handler handler)
186 {
187 if (isSecure())
188 {
189 boost::asio::async_read_until(*socket_, buffers, delim, handler);
190 }
191 else
192 {
193 boost::asio::async_read_until(plainSocket(), buffers, delim, handler);
194 }
195 }
196
197 template <typename Allocator, typename MatchCondition, typename Handler>
198 void
200 boost::asio::basic_streambuf<Allocator>& buffers,
201 MatchCondition cond,
202 Handler handler)
203 {
204 if (isSecure())
205 {
206 boost::asio::async_read_until(*socket_, buffers, cond, handler);
207 }
208 else
209 {
210 boost::asio::async_read_until(plainSocket(), buffers, cond, handler);
211 }
212 }
213
214 template <typename Buf, typename Handler>
215 void
216 asyncWrite(Buf const& buffers, Handler handler)
217 {
218 if (isSecure())
219 {
220 boost::asio::async_write(*socket_, buffers, handler);
221 }
222 else
223 {
224 boost::asio::async_write(plainSocket(), buffers, handler);
225 }
226 }
227
228 template <typename Allocator, typename Handler>
229 void
230 asyncWrite(boost::asio::basic_streambuf<Allocator>& buffers, Handler handler)
231 {
232 if (isSecure())
233 {
234 boost::asio::async_write(*socket_, buffers, handler);
235 }
236 else
237 {
238 boost::asio::async_write(plainSocket(), buffers, handler);
239 }
240 }
241
242 template <typename Buf, typename Condition, typename Handler>
243 void
244 asyncRead(Buf const& buffers, Condition cond, Handler handler)
245 {
246 if (isSecure())
247 {
248 boost::asio::async_read(*socket_, buffers, cond, handler);
249 }
250 else
251 {
252 boost::asio::async_read(plainSocket(), buffers, cond, handler);
253 }
254 }
255
256 template <typename Allocator, typename Condition, typename Handler>
257 void
258 asyncRead(boost::asio::basic_streambuf<Allocator>& buffers, Condition cond, Handler handler)
259 {
260 if (isSecure())
261 {
262 boost::asio::async_read(*socket_, buffers, cond, handler);
263 }
264 else
265 {
266 boost::asio::async_read(plainSocket(), buffers, cond, handler);
267 }
268 }
269
270 template <typename Buf, typename Handler>
271 void
272 asyncRead(Buf const& buffers, Handler handler)
273 {
274 if (isSecure())
275 {
276 boost::asio::async_read(*socket_, buffers, handler);
277 }
278 else
279 {
280 boost::asio::async_read(plainSocket(), buffers, handler);
281 }
282 }
283
284 template <typename Seq, typename Handler>
285 void
286 asyncWriteSome(Seq const& buffers, Handler handler)
287 {
288 if (isSecure())
289 {
290 socket_->async_write_some(buffers, handler);
291 }
292 else
293 {
294 plainSocket().async_write_some(buffers, handler);
295 }
296 }
297
298protected:
299 void
300 handleAutodetect(callback cbFunc, error_code const& ec, size_t bytesTransferred)
301 {
302 using namespace xrpl;
303
304 if (ec)
305 {
306 JLOG(j_.warn()) << "Handle autodetect error: " << ec;
307 cbFunc(ec);
308 }
309 else if (
310 (buffer_[0] < 127) && (buffer_[0] > 31) &&
311 ((bytesTransferred < 2) || ((buffer_[1] < 127) && (buffer_[1] > 31))) &&
312 ((bytesTransferred < 3) || ((buffer_[2] < 127) && (buffer_[2] > 31))) &&
313 ((bytesTransferred < 4) || ((buffer_[3] < 127) && (buffer_[3] > 31))))
314 {
315 // not ssl
316 JLOG(j_.trace()) << "non-SSL";
317 secure_ = false;
318 cbFunc(ec);
319 }
320 else
321 {
322 // ssl
323 JLOG(j_.trace()) << "SSL";
324 secure_ = true;
325 socket_->async_handshake(ssl_socket::server, cbFunc);
326 }
327 }
328
329private:
334};
std::function< void(error_code)> callback
Definition AutoSocket.h:34
AutoSocket(boost::asio::io_context &s, boost::asio::ssl::context &c, bool secureOnly, bool plainOnly)
Definition AutoSocket.h:37
ssl_socket::handshake_type handshake_type
Definition AutoSocket.h:32
ssl_socket::lowest_layer_type lowest_layer_type
Definition AutoSocket.h:31
ssl_socket::next_layer_type plain_socket
Definition AutoSocket.h:30
boost::asio::ip::tcp::socket::endpoint_type endpoint_type
Definition AutoSocket.h:28
lowest_layer_type & lowestLayer()
Definition AutoSocket.h:83
beast::Journal j_
Definition AutoSocket.h:333
plain_socket & plainSocket()
Definition AutoSocket.h:65
beast::ip::Endpoint localEndpoint()
Definition AutoSocket.h:71
boost::system::error_code error_code
Definition AutoSocket.h:33
std::vector< char > buffer_
Definition AutoSocket.h:332
void asyncRead(Buf const &buffers, Condition cond, Handler handler)
Definition AutoSocket.h:244
AutoSocket(boost::asio::io_context &s, boost::asio::ssl::context &c)
Definition AutoSocket.h:49
std::unique_ptr< ssl_socket > socket_ptr
Definition AutoSocket.h:29
void asyncReadUntil(Seq const &buffers, Condition condition, Handler handler)
Definition AutoSocket.h:168
void asyncReadUntil(boost::asio::basic_streambuf< Allocator > &buffers, std::string const &delim, Handler handler)
Definition AutoSocket.h:182
socket_ptr socket_
Definition AutoSocket.h:330
void asyncShutdown(ShutdownHandler handler)
Definition AutoSocket.h:131
void swap(AutoSocket &s) noexcept
Definition AutoSocket.h:89
void handleAutodetect(callback cbFunc, error_code const &ec, size_t bytesTransferred)
Definition AutoSocket.h:300
beast::ip::Endpoint remoteEndpoint()
Definition AutoSocket.h:77
void asyncRead(Buf const &buffers, Handler handler)
Definition AutoSocket.h:272
void asyncWriteSome(Seq const &buffers, Handler handler)
Definition AutoSocket.h:286
void asyncRead(boost::asio::basic_streambuf< Allocator > &buffers, Condition cond, Handler handler)
Definition AutoSocket.h:258
bool isSecure() const
Definition AutoSocket.h:55
void asyncReadSome(Seq const &buffers, Handler handler)
Definition AutoSocket.h:154
boost::system::error_code cancel(boost::system::error_code &ec)
Definition AutoSocket.h:97
ssl_socket & sslSocket()
Definition AutoSocket.h:60
boost::asio::ssl::stream< boost::asio::ip::tcp::socket > ssl_socket
Definition AutoSocket.h:27
void asyncWrite(Buf const &buffers, Handler handler)
Definition AutoSocket.h:216
void asyncHandshake(handshake_type type, callback cbFunc)
Definition AutoSocket.h:103
void asyncReadUntil(boost::asio::basic_streambuf< Allocator > &buffers, MatchCondition cond, Handler handler)
Definition AutoSocket.h:199
void asyncWrite(boost::asio::basic_streambuf< Allocator > &buffers, Handler handler)
Definition AutoSocket.h:230
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)