rippled
Loading...
Searching...
No Matches
TrustedPublisherServer.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright 2017 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19#ifndef RIPPLE_TEST_TRUSTED_PUBLISHER_SERVER_H_INCLUDED
20#define RIPPLE_TEST_TRUSTED_PUBLISHER_SERVER_H_INCLUDED
21
22#include <test/jtx/envconfig.h>
23#include <xrpl/basics/base64.h>
24#include <xrpl/basics/random.h>
25#include <xrpl/basics/strHex.h>
26#include <xrpl/protocol/PublicKey.h>
27#include <xrpl/protocol/SecretKey.h>
28#include <xrpl/protocol/Sign.h>
29#include <boost/algorithm/string/predicate.hpp>
30#include <boost/asio.hpp>
31#include <boost/asio/ip/tcp.hpp>
32#include <boost/asio/ssl/stream.hpp>
33#include <boost/beast/http.hpp>
34#include <boost/beast/ssl.hpp>
35#include <boost/beast/version.hpp>
36#include <boost/lexical_cast.hpp>
37
38#include <memory>
39#include <thread>
40
41namespace ripple {
42namespace test {
43
45 : public std::enable_shared_from_this<TrustedPublisherServer>
46{
47 using endpoint_type = boost::asio::ip::tcp::endpoint;
48 using address_type = boost::asio::ip::address;
49 using socket_type = boost::asio::ip::tcp::socket;
50
51 using req_type =
52 boost::beast::http::request<boost::beast::http::string_body>;
53 using resp_type =
54 boost::beast::http::response<boost::beast::http::string_body>;
55 using error_code = boost::system::error_code;
56
59 boost::asio::ip::tcp::acceptor acceptor_;
60 // Generates a version 1 validator list, using the int parameter as the
61 // actual version.
63 // Generates a version 2 validator list, using the int parameter as the
64 // actual version.
66
67 // The SSL context is required, and holds certificates
68 bool useSSL_;
69 boost::asio::ssl::context sslCtx_{boost::asio::ssl::context::tlsv12};
70
73
74 // Load a signed certificate into the ssl context, and configure
75 // the context for use with a server.
76 inline void
78 {
79 sslCtx_.set_password_callback(
80 [](std::size_t, boost::asio::ssl::context_base::password_purpose) {
81 return "test";
82 });
83
84 sslCtx_.set_options(
85 boost::asio::ssl::context::default_workarounds |
86 boost::asio::ssl::context::no_sslv2 |
87 boost::asio::ssl::context::single_dh_use);
88
89 sslCtx_.use_certificate_chain(
90 boost::asio::buffer(cert().data(), cert().size()));
91
92 sslCtx_.use_private_key(
93 boost::asio::buffer(key().data(), key().size()),
94 boost::asio::ssl::context::file_format::pem);
95
96 sslCtx_.use_tmp_dh(boost::asio::buffer(dh().data(), dh().size()));
97 }
98
99 struct BlobInfo
100 {
102 {
103 }
104
105 // base-64 encoded JSON containing the validator list.
107 // hex-encoded signature of the blob using the publisher's signing key
109 };
110
111public:
113 {
117 };
118
119 static std::string
121 PublicKey const& pk,
122 SecretKey const& sk,
123 PublicKey const& spk,
124 SecretKey const& ssk,
125 int seq)
126 {
128 st[sfSequence] = seq;
129 st[sfPublicKey] = pk;
130 st[sfSigningPubKey] = spk;
131
132 sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk);
133 sign(
134 st,
136 *publicKeyType(pk),
137 sk,
138 sfMasterSignature);
139
140 Serializer s;
141 st.add(s);
142
143 return base64_encode(
144 std::string(static_cast<char const*>(s.data()), s.size()));
145 }
146
147 static Validator
149 {
150 auto const secret = randomSecretKey();
151 auto const masterPublic = derivePublicKey(KeyType::ed25519, secret);
152 auto const signingKeys = randomKeyPair(KeyType::secp256k1);
153 return {
154 masterPublic,
155 signingKeys.first,
157 masterPublic,
158 secret,
159 signingKeys.first,
160 signingKeys.second,
161 1)};
162 }
163
164 // TrustedPublisherServer must be accessed through a shared_ptr.
165 // This constructor is only public so std::make_shared has access.
166 // The function`make_TrustedPublisherServer` should be used to create
167 // instances.
168 // The `futures` member is expected to be structured as
169 // effective / expiration time point pairs for use in version 2 UNLs
171 boost::asio::io_context& ioc,
172 std::vector<Validator> const& validators,
173 NetClock::time_point validUntil,
176 futures,
177 bool useSSL = false,
178 int version = 1,
179 bool immediateStart = true,
180 int sequence = 1)
181 : sock_{ioc}
182 , ep_{beast::IP::Address::from_string(
183 ripple::test::getEnvLocalhostAddr()),
184 // 0 means let OS pick the port based on what's available
185 0}
186 , acceptor_{ioc}
187 , useSSL_{useSSL}
190 {
191 auto const keys = randomKeyPair(KeyType::secp256k1);
192 auto const manifest = makeManifestString(
193 publisherPublic_, publisherSecret_, keys.first, keys.second, 1);
194
195 std::vector<BlobInfo> blobInfo;
196 blobInfo.reserve(futures.size() + 1);
197 auto const [data, blob] = [&]() -> std::pair<std::string, std::string> {
198 // Builds the validator list, then encodes it into a blob.
199 std::string data = "{\"sequence\":" + std::to_string(sequence) +
200 ",\"expiration\":" +
201 std::to_string(validUntil.time_since_epoch().count()) +
202 ",\"validators\":[";
203
204 for (auto const& val : validators)
205 {
206 data += "{\"validation_public_key\":\"" +
207 strHex(val.masterPublic) + "\",\"manifest\":\"" +
208 val.manifest + "\"},";
209 }
210 data.pop_back();
211 data += "]}";
212 std::string blob = base64_encode(data);
213 return std::make_pair(data, blob);
214 }();
215 auto const sig = strHex(sign(keys.first, keys.second, makeSlice(data)));
216 blobInfo.emplace_back(blob, sig);
217 getList_ = [blob = blob, sig, manifest, version](int interval) {
218 // Build the contents of a version 1 format UNL file
220 l << "{\"blob\":\"" << blob << "\"" << ",\"signature\":\"" << sig
221 << "\"" << ",\"manifest\":\"" << manifest << "\""
222 << ",\"refresh_interval\": " << interval
223 << ",\"version\":" << version << '}';
224 return l.str();
225 };
226 for (auto const& future : futures)
227 {
228 std::string data = "{\"sequence\":" + std::to_string(++sequence) +
229 ",\"effective\":" +
230 std::to_string(future.first.time_since_epoch().count()) +
231 ",\"expiration\":" +
232 std::to_string(future.second.time_since_epoch().count()) +
233 ",\"validators\":[";
234
235 // Use the same set of validators for simplicity
236 for (auto const& val : validators)
237 {
238 data += "{\"validation_public_key\":\"" +
239 strHex(val.masterPublic) + "\",\"manifest\":\"" +
240 val.manifest + "\"},";
241 }
242 data.pop_back();
243 data += "]}";
244 std::string blob = base64_encode(data);
245 auto const sig =
246 strHex(sign(keys.first, keys.second, makeSlice(data)));
247 blobInfo.emplace_back(blob, sig);
248 }
249 getList2_ = [blobInfo, manifest, version](int interval) {
250 // Build the contents of a version 2 format UNL file
251 // Use `version + 1` to get 2 for most tests, but have
252 // a "bad" version number for tests that provide an override.
254 for (auto const& info : blobInfo)
255 {
256 l << "{\"blob\":\"" << info.blob << "\"" << ",\"signature\":\""
257 << info.signature << "\"},";
258 }
259 std::string blobs = l.str();
260 blobs.pop_back();
261 l.str(std::string());
262 l << "{\"blobs_v2\": [ " << blobs << "],\"manifest\":\"" << manifest
263 << "\"" << ",\"refresh_interval\": " << interval
264 << ",\"version\":" << (version + 1) << '}';
265 return l.str();
266 };
267
268 if (useSSL_)
269 {
270 // This holds the self-signed certificate used by the server
272 }
273 }
274
275 void
277 {
278 error_code ec;
279 acceptor_.open(ep_.protocol());
280 acceptor_.set_option(
281 boost::asio::ip::tcp::acceptor::reuse_address(true), ec);
282 acceptor_.bind(ep_);
283 acceptor_.listen(boost::asio::socket_base::max_connections);
284 acceptor_.async_accept(
285 sock_,
287 error_code ec) {
288 if (auto p = wp.lock())
289 {
290 p->on_accept(ec);
291 }
292 });
293 }
294
295 void
297 {
298 error_code ec;
299 acceptor_.close(ec);
300 // TODO consider making this join
301 // any running do_peer threads
302 }
303
305 {
306 stop();
307 }
308
311 {
312 return acceptor_.local_endpoint();
313 }
314
315 PublicKey const&
317 {
318 return publisherPublic_;
319 }
320
321 /* CA/self-signed certs :
322 *
323 * The following three methods return certs/keys used by
324 * server and/or client to do the SSL handshake. These strings
325 * were generated using the script below. The server key and cert
326 * are used to configure the server (see load_server_certificate
327 * above). The ca.crt should be used to configure the client
328 * when ssl verification is enabled.
329 *
330 * note:
331 * cert() ==> server.crt
332 * key() ==> server.key
333 * ca_cert() ==> ca.crt
334 * dh() ==> dh.pem
335 ```
336 #!/usr/bin/env bash
337
338 mkdir -p /tmp/__certs__
339 pushd /tmp/__certs__
340 rm *.crt *.key *.pem
341
342 # generate CA
343 openssl genrsa -out ca.key 2048
344 openssl req -new -x509 -nodes -days 10000 -key ca.key -out ca.crt \
345 -subj "/C=US/ST=CA/L=Los
346 Angeles/O=rippled-unit-tests/CN=example.com" # generate private cert
347 openssl genrsa -out server.key 2048
348 # Generate certificate signing request
349 # since our unit tests can run in either ipv4 or ipv6 mode,
350 # we need to use extensions (subjectAltName) so that we can
351 # associate both ipv4 and ipv6 localhost addresses with this cert
352 cat >"extras.cnf" <<EOF
353 [req]
354 req_extensions = v3_req
355 distinguished_name = req_distinguished_name
356
357 [req_distinguished_name]
358
359 [v3_req]
360 subjectAltName = @alt_names
361
362 [alt_names]
363 DNS.1 = localhost
364 IP.1 = ::1
365 EOF
366 openssl req -new -key server.key -out server.csr \
367 -config extras.cnf \
368 -subj "/C=US/ST=California/L=San
369 Francisco/O=rippled-unit-tests/CN=127.0.0.1" \
370
371 # Create public certificate by signing with our CA
372 openssl x509 -req -days 10000 -in server.csr -CA ca.crt -CAkey ca.key
373 -out server.crt \ -extfile extras.cnf -set_serial 01 -extensions v3_req
374
375 # generate DH params for server
376 openssl dhparam -out dh.pem 2048
377 # verify certs
378 openssl verify -CAfile ca.crt server.crt
379 openssl x509 -in server.crt -text -noout
380 popd
381 ```
382 */
383 static std::string const&
385 {
386 static std::string const cert{R"cert(
387-----BEGIN CERTIFICATE-----
388MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEL
389MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw
390bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIyMDIwNTIz
391NDk0M1oXDTQ5MDYyMzIzNDk0M1owazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
392bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs
393ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
394AQEFAAOCAQ8AMIIBCgKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJ
395qTuBUcVcTRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs
396+T/qkwmc/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9f
397n9vrxnWX9LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3
398zVDe7+sxR//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0q
399S1Uj77mhwxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABoyowKDAmBgNV
400HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEL
401BQADggEBAJkUFNS0CeEAKvo0ttzooXnCDH3esj2fwmLJQYLUGsAF8DFrFHTqZEcx
402hFRdr0ftEb/VKpV9dVF6xtSoMU56kHOnhbHEWADyqdKUkCDjrGBet5QdWmEwNV2L
403nYrwGQBAybMt/+1XMUV8HeLFJNHnyxfQYcW0fUsrmNGk8W0kzWuuq88qbhfXZAIx
404KiXrzYpLlM0RlpWXRfYQ6mTdSrRrLnEo5MklizVgNB8HYX78lxa06zP08oReQcfT
405GSGO8NEEq8BTVmp69zD1JyfvQcXzsi7WtkAX+/EOFZ7LesnZ6VsyjZ74wECCaQuD
406X1yu/XxHqchM+DOzzVw6wRKaM7Zsk80=
407-----END CERTIFICATE-----
408)cert"};
409 return cert;
410 }
411
412 static std::string const&
413 key()
415 static std::string const key{R"pkey(
416-----BEGIN RSA PRIVATE KEY-----
417MIIEpAIBAAKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJqTuBUcVc
418TRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs+T/qkwmc
419/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9fn9vrxnWX
4209LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3zVDe7+sx
421R//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0qS1Uj77mh
422wxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABAoIBAEC9MDpOu+quvg8+
423kt4MKSFdIhQuM7WguNaTe5AkSspDrcJzT7SK275mp259QIYCzMxxuA8TSZTb8A1C
424t6dgKbi7k6FaGMCYMRHzzK6NZfMbPi6cj245q9LYlZpdQswuM/FdPpPH1zUxrNYK
425CIaooZ6ZHzlSD/eaRMgkBQEkONHrZZtEinLIvKedwssPCaXkIISmt7MFQTDOlxkf
426K0Mt1mnRREPYbYSfPEEfIyy/KDIiB5AzgGt+uPOn8Oeb1pSqy69jpYcfhSj+bo4S
427UV6qTuTfBd4qkkNI6d/Z7DcDJFFlfloG/vVgGk/beWNnL2e39vzxiebB3w+MQn4F
428Wyx5mCECgYEA22z1/ihqt9LIAWtP42oSS3S/RxlFzpp5d7QfNqFnEoVgeRhQzleP
429pRJIzVXpMYBxexZYqZA/q8xBSggz+2gmRoYnW20VIzl14DsSH378ye3FRwJB0tLy
430dWU8DC7ZB5XQCTvI9UY3voJNToknODw7RCNO1h3V3T1y6JRLdcLskk8CgYEA2OLy
431aE5bvsUaLBSv7W9NFhSuZ0p9Y0pFmRgHI7g8i/AgRZ0BgiE8u8OZSHmPJPMaNs/h
432YIEIrlsgDci1PzwrUYseRp/aiVE1kyev09/ihqRXTPpLQu6h/d63KRe/06W3t5X3
433Dmfj49hH5zGPBI/0y1ECV/n0fwnRhxSv7fNr3RECgYBEuFpOUAAkNApZj29ErNqv
4348Q9ayAp5yx1RpQLFjEUIoub05e2gwgGF1DUiwc43p59iyjvYVwnp1x13fxwwl4yt
435N6Sp2H7vOja1lCp33MB0yVeohodw7InsxFjLA/0KiBvQWH32exhIPOzTNNcooIx7
436KYeuPUfWc0FCn/cGGZcXtwKBgQC1hp1k99CKBuY05suoanOWe5DNGud/ZvaBgD7Z
437gqYKadxY52QPyknOzZNJuZQ5VM8n+S2lW9osNFDLuKUaW/3Vrh6U9c4vCC1TEPB0
4384PnzvzDiWMsNJjWnCfU7C4meVyFBIt84y3NNjAQCWNRe+S3lzdOsVqRwf4NDD+l/
439uzEYQQKBgQCJczIlwobm1Y6O41hbGZhZL/CGMNS6Z0INi2yasV0WDqYlh7XayHMD
440cK55dMILcbHqeIBq/wR6sIhw6IJcaDBfFfrJiKKDilfij2lHxR2FQrEngtTCCRV+
441ZzARzaWhQPvbDqEtLJDWuXZNXfL8/PTIs5NmuKuQ8F4+gQJpkQgwaw==
442-----END RSA PRIVATE KEY-----
443)pkey"};
444 return key;
445 }
446
447 static std::string const&
448 ca_cert()
449 {
450 static std::string const cert{R"cert(
451-----BEGIN CERTIFICATE-----
452MIIDpzCCAo+gAwIBAgIUWc45WqaaNuaSLoFYTMC/Mjfqw/gwDQYJKoZIhvcNAQEL
453BQAwYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQHDAtMb3MgQW5n
454ZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0LXRlc3RzMRQwEgYDVQQDDAtleGFt
455cGxlLmNvbTAeFw0yMjAyMDUyMzQ5MDFaFw00OTA2MjMyMzQ5MDFaMGMxCzAJBgNV
456BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLTG9zIEFuZ2VsZXMxGzAZBgNV
457BAoMEnJpcHBsZWQtdW5pdC10ZXN0czEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEi
458MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0f2JBW2XNW2wT5/ajX2qxmUY+
459aNJGfpV6gZ5CmwdQpbHrPPvJoskxwsCyr3GifzT/GtCpmb1fiu59uUAPxQEYCxiq
460V+HchX4g4Vl27xKJ0P+usxuEED9v7TCteKum9u9eMZ8UDF0fspXcnWGs9fXlyoTj
461uTRP1SBQllk44DPc/KzlrtH+QNXmr9XQnP8XvwWCgJXMx87voxEGiFFOVhkSSAOv
462v+OUGgEuq0NPgwv2LHBlYHSdkoU9F5Z/TmkCAFMShbyoUjldIz2gcWXjN2tespGo
463D6qYvasvPIpmcholBBkc0z8QDt+RNq+Wzrults7epJXy/u+txGK9cHCNlLCpAgMB
464AAGjUzBRMB0GA1UdDgQWBBS1oydh+YyqDNOFKYOvOtVMWKqV4zAfBgNVHSMEGDAW
465gBS1oydh+YyqDNOFKYOvOtVMWKqV4zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
466DQEBCwUAA4IBAQCDPyGKQwQ8Lz0yEgvIl/Uo9BtwAzlvjrLM/39qhStLQqDGSs2Q
467xFIbtjzjuLf5vR3q6OJ62CCvzqXgHkJ+hzVN/tAvyliGTdjJrK+xv1M5a+XipO2f
468c9lb4gRbFL/DyoeoWgb1Rkv3gFf0FlCYH+ZUcYb9ZYCRlGtFgOcxJI2g+T7jSLFp
4698+hSzQ6W5Sp9L6b5iJyCww1vjBvBqzNyZMNeB4gXGtd6z9vMDSvKboTdGD7wcFB+
470mRMyNekaRw+Npy4Hjou5sx272cXHHmPCSF5TjwdaibSaGjx1k0Q50mOf7S9KG5b5
4717X1e3FekJlaD02EBEhtkXURIxogOQALdFncj
472-----END CERTIFICATE-----
473)cert"};
474 return cert;
475 }
476
477 static std::string const&
478 dh()
479 {
480 static std::string const dh{R"dh(
481-----BEGIN DH PARAMETERS-----
482MIIBCAKCAQEAp2I2fWEUZ3sCNfitSRC/MdAhJE/bS+NO0O2tWdIdlvmIFE6B5qhC
483sGW9ojrQT8DTxBvGAcbjr/jagmlE3BV4oSnxyhP37G2mDvMOJ29J3NvFD/ZFAW0d
484BvZJ1RNvMu29NmVCyt6/jgzcqrqnami9uD93aK+zaVrlPsPEYM8xB19HXwqsEYCL
485ux2B7sqXm9Ts74HPg/EV+pcVon9phxNWxxgHlOvFc2QjZ3hXH++kzmJ4vs7N/XDB
486xbEQ+TUZ5jbJGSeBqNFKFeuOUQGJ46Io0jBSYd4rSmKUXkvElQwR+n7KF3jy1uAt
487/8hzd8tHn9TyW7Q2/CPkOA6dCXzltpOSowIBAg==
488-----END DH PARAMETERS-----
489)dh"};
490 return dh;
491 }
492
493private:
494 struct lambda
495 {
496 int id;
497 TrustedPublisherServer& self;
499 boost::asio::executor_work_guard<boost::asio::executor> work;
500 bool ssl;
501
502 lambda(
503 int id_,
504 TrustedPublisherServer& self_,
506 bool ssl_)
507 : id(id_)
508 , self(self_)
509 , sock(std::move(sock_))
510 , work(sock_.get_executor())
511 , ssl(ssl_)
512 {
513 }
514
515 void
516 operator()()
517 {
518 self.do_peer(id, std::move(sock), ssl);
519 }
520 };
521
522 void
524 {
525 if (ec || !acceptor_.is_open())
526 return;
527
528 static int id_ = 0;
529 std::thread{lambda{++id_, *this, std::move(sock_), useSSL_}}.detach();
530 acceptor_.async_accept(
531 sock_,
533 error_code ec) {
534 if (auto p = wp.lock())
535 {
536 p->on_accept(ec);
537 }
538 });
539 }
540
541 void
542 do_peer(int id, socket_type&& s, bool ssl)
543 {
544 using namespace boost::beast;
545 using namespace boost::asio;
546 socket_type sock(std::move(s));
547 flat_buffer sb;
548 error_code ec;
550
551 if (ssl)
552 {
553 // Construct the stream around the socket
554 ssl_stream.emplace(sock, sslCtx_);
555 // Perform the SSL handshake
556 ssl_stream->handshake(ssl::stream_base::server, ec);
557 if (ec)
558 return;
559 }
560
561 for (;;)
562 {
563 resp_type res;
564 req_type req;
565 try
566 {
567 if (ssl)
568 http::read(*ssl_stream, sb, req, ec);
569 else
570 http::read(sock, sb, req, ec);
571
572 if (ec)
573 break;
574
575 std::string_view const path = req.target();
576 res.insert("Server", "TrustedPublisherServer");
577 res.version(req.version());
578 res.keep_alive(req.keep_alive());
579 bool prepare = true;
580
581 if (boost::starts_with(path, "/validators2"))
582 {
583 res.result(http::status::ok);
584 res.insert("Content-Type", "application/json");
585 if (path == "/validators2/bad")
586 res.body() = "{ 'bad': \"2']";
587 else if (path == "/validators2/missing")
588 res.body() = "{\"version\": 2}";
589 else
590 {
591 int refresh = 5;
592 constexpr char const* refreshPrefix =
593 "/validators2/refresh/";
594 if (boost::starts_with(path, refreshPrefix))
595 refresh = boost::lexical_cast<unsigned int>(
596 path.substr(strlen(refreshPrefix)));
597 res.body() = getList2_(refresh);
598 }
599 }
600 else if (boost::starts_with(path, "/validators"))
601 {
602 res.result(http::status::ok);
603 res.insert("Content-Type", "application/json");
604 if (path == "/validators/bad")
605 res.body() = "{ 'bad': \"1']";
606 else if (path == "/validators/missing")
607 res.body() = "{\"version\": 1}";
608 else
609 {
610 int refresh = 5;
611 constexpr char const* refreshPrefix =
612 "/validators/refresh/";
613 if (boost::starts_with(path, refreshPrefix))
614 refresh = boost::lexical_cast<unsigned int>(
615 path.substr(strlen(refreshPrefix)));
616 res.body() = getList_(refresh);
617 }
618 }
619 else if (boost::starts_with(path, "/textfile"))
620 {
621 prepare = false;
622 res.result(http::status::ok);
623 res.insert("Content-Type", "text/example");
624 // if huge was requested, lie about content length
625 std::uint64_t cl =
626 boost::starts_with(path, "/textfile/huge")
628 : 1024;
629 res.content_length(cl);
630 if (req.method() == http::verb::get)
631 {
633 for (auto i = 0; i < 1024; ++i)
634 body << static_cast<char>(rand_int<short>(32, 126)),
635 res.body() = body.str();
636 }
637 }
638 else if (boost::starts_with(path, "/sleep/"))
639 {
640 auto const sleep_sec =
641 boost::lexical_cast<unsigned int>(path.substr(7));
643 std::chrono::seconds(sleep_sec));
644 }
645 else if (boost::starts_with(path, "/redirect"))
646 {
647 if (boost::ends_with(path, "/301"))
648 res.result(http::status::moved_permanently);
649 else if (boost::ends_with(path, "/302"))
650 res.result(http::status::found);
651 else if (boost::ends_with(path, "/307"))
652 res.result(http::status::temporary_redirect);
653 else if (boost::ends_with(path, "/308"))
654 res.result(http::status::permanent_redirect);
655
656 std::stringstream location;
657 if (boost::starts_with(path, "/redirect_to/"))
658 {
659 location << path.substr(13);
660 }
661 else if (!boost::starts_with(path, "/redirect_nolo"))
662 {
663 location
664 << (ssl ? "https://" : "http://")
665 << local_endpoint()
666 << (boost::starts_with(path, "/redirect_forever/")
667 ? path
668 : "/validators");
669 }
670 if (!location.str().empty())
671 res.insert("Location", location.str());
672 }
673 else
674 {
675 // unknown request
676 res.result(boost::beast::http::status::not_found);
677 res.insert("Content-Type", "text/html");
678 res.body() = "The file '" + std::string(path) +
679 "' was not "
680 "found";
681 }
682
683 if (prepare)
684 res.prepare_payload();
685 }
686 catch (std::exception const& e)
687 {
688 res = {};
689 res.result(boost::beast::http::status::internal_server_error);
690 res.version(req.version());
691 res.insert("Server", "TrustedPublisherServer");
692 res.insert("Content-Type", "text/html");
693 res.body() =
694 std::string{"An internal error occurred"} + e.what();
695 res.prepare_payload();
696 }
697
698 if (ssl)
699 write(*ssl_stream, res, ec);
700 else
701 write(sock, res, ec);
702
703 if (ec || req.need_eof())
704 break;
705 }
706
707 // Perform the SSL shutdown
708 if (ssl)
709 ssl_stream->shutdown(ec);
710 }
711};
712
715 boost::asio::io_context& ioc,
717 NetClock::time_point validUntil,
719 futures,
720 bool useSSL = false,
721 int version = 1,
722 bool immediateStart = true,
723 int sequence = 1)
724{
725 auto const r = std::make_shared<TrustedPublisherServer>(
726 ioc, validators, validUntil, futures, useSSL, version, sequence);
727 if (immediateStart)
728 r->start();
729 return r;
730}
731
732} // namespace test
733} // namespace ripple
734#endif
A public key.
Definition: PublicKey.h:62
void add(Serializer &s) const override
Definition: STObject.cpp:111
A secret key.
Definition: SecretKey.h:37
std::size_t size() const noexcept
Definition: Serializer.h:72
void const * data() const noexcept
Definition: Serializer.h:78
boost::beast::http::request< boost::beast::http::string_body > req_type
void do_peer(int id, socket_type &&s, bool ssl)
TrustedPublisherServer(boost::asio::io_context &ioc, std::vector< Validator > const &validators, NetClock::time_point validUntil, std::vector< std::pair< NetClock::time_point, NetClock::time_point > > const &futures, bool useSSL=false, int version=1, bool immediateStart=true, int sequence=1)
boost::asio::ip::tcp::socket socket_type
boost::beast::http::response< boost::beast::http::string_body > resp_type
std::function< std::string(int)> getList2_
boost::asio::ip::tcp::acceptor acceptor_
std::function< std::string(int)> getList_
boost::asio::ip::tcp::endpoint endpoint_type
static std::string makeManifestString(PublicKey const &pk, SecretKey const &sk, PublicKey const &spk, SecretKey const &ssk, int seq)
Set the regular signature on a JTx.
Definition: sig.h:35
T emplace_back(T... args)
T emplace(T... args)
T insert(T... args)
T make_pair(T... args)
T max(T... args)
void write(nudb::detail::ostream &os, std::size_t t)
Definition: varint.h:133
void sign(Json::Value &jv, Account const &account)
Sign automatically.
Definition: utility.cpp:46
const char * getEnvLocalhostAddr()
Definition: envconfig.h:36
std::shared_ptr< TrustedPublisherServer > make_TrustedPublisherServer(boost::asio::io_context &ioc, std::vector< TrustedPublisherServer::Validator > const &validators, NetClock::time_point validUntil, std::vector< std::pair< NetClock::time_point, NetClock::time_point > > const &futures, bool useSSL=false, int version=1, bool immediateStart=true, int sequence=1)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
SField const sfGeneric
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
Definition: SecretKey.cpp:313
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:207
bool from_string(RangeSet< T > &rs, std::string const &s)
Convert the given styled string to a RangeSet.
Definition: RangeSet.h:124
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:243
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
Definition: SecretKey.cpp:281
KeyType
Definition: KeyType.h:28
std::string base64_encode(std::uint8_t const *data, std::size_t len)
Definition: base64.cpp:236
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
Definition: SecretKey.cpp:368
@ manifest
Manifest.
STL namespace.
T pop_back(T... args)
T reserve(T... args)
T sleep_for(T... args)
T str(T... args)
T strlen(T... args)
boost::asio::executor_work_guard< boost::asio::executor > work
lambda(int id_, TrustedPublisherServer &self_, socket_type &&sock_, bool ssl_)
Set the sequence number on a JTx.
Definition: seq.h:34
T time_since_epoch(T... args)
T to_string(T... args)
T what(T... args)