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