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 << "\""
224 << ",\"signature\":\"" << sig << "\""
225 << ",\"manifest\":\"" << manifest << "\""
226 << ",\"refresh_interval\": " << interval
227 << ",\"version\":" << version << '}';
228 return l.str();
229 };
230 for (auto const& future : futures)
231 {
232 std::string data = "{\"sequence\":" + std::to_string(++sequence) +
233 ",\"effective\":" +
234 std::to_string(future.first.time_since_epoch().count()) +
235 ",\"expiration\":" +
236 std::to_string(future.second.time_since_epoch().count()) +
237 ",\"validators\":[";
238
239 // Use the same set of validators for simplicity
240 for (auto const& val : validators)
241 {
242 data += "{\"validation_public_key\":\"" +
243 strHex(val.masterPublic) + "\",\"manifest\":\"" +
244 val.manifest + "\"},";
245 }
246 data.pop_back();
247 data += "]}";
248 std::string blob = base64_encode(data);
249 auto const sig =
250 strHex(sign(keys.first, keys.second, makeSlice(data)));
251 blobInfo.emplace_back(blob, sig);
252 }
253 getList2_ = [blobInfo, manifest, version](int interval) {
254 // Build the contents of a version 2 format UNL file
255 // Use `version + 1` to get 2 for most tests, but have
256 // a "bad" version number for tests that provide an override.
258 for (auto const& info : blobInfo)
259 {
260 l << "{\"blob\":\"" << info.blob << "\""
261 << ",\"signature\":\"" << info.signature << "\"},";
262 }
263 std::string blobs = l.str();
264 blobs.pop_back();
265 l.str(std::string());
266 l << "{\"blobs_v2\": [ " << blobs << "],\"manifest\":\"" << manifest
267 << "\""
268 << ",\"refresh_interval\": " << interval
269 << ",\"version\":" << (version + 1) << '}';
270 return l.str();
271 };
272
273 if (useSSL_)
274 {
275 // This holds the self-signed certificate used by the server
277 }
278 }
279
280 void
282 {
283 error_code ec;
284 acceptor_.open(ep_.protocol());
285 acceptor_.set_option(
286 boost::asio::ip::tcp::acceptor::reuse_address(true), ec);
287 acceptor_.bind(ep_);
288 acceptor_.listen(boost::asio::socket_base::max_connections);
289 acceptor_.async_accept(
290 sock_,
292 error_code ec) {
293 if (auto p = wp.lock())
294 {
295 p->on_accept(ec);
296 }
297 });
298 }
299
300 void
302 {
303 error_code ec;
304 acceptor_.close(ec);
305 // TODO consider making this join
306 // any running do_peer threads
307 }
308
310 {
311 stop();
312 }
313
316 {
317 return acceptor_.local_endpoint();
318 }
319
320 PublicKey const&
322 {
323 return publisherPublic_;
324 }
325
326 /* CA/self-signed certs :
327 *
328 * The following three methods return certs/keys used by
329 * server and/or client to do the SSL handshake. These strings
330 * were generated using the script below. The server key and cert
331 * are used to configure the server (see load_server_certificate
332 * above). The ca.crt should be used to configure the client
333 * when ssl verification is enabled.
334 *
335 * note:
336 * cert() ==> server.crt
337 * key() ==> server.key
338 * ca_cert() ==> ca.crt
339 * dh() ==> dh.pem
340 ```
341 #!/usr/bin/env bash
342
343 mkdir -p /tmp/__certs__
344 pushd /tmp/__certs__
345 rm *.crt *.key *.pem
346
347 # generate CA
348 openssl genrsa -out ca.key 2048
349 openssl req -new -x509 -nodes -days 10000 -key ca.key -out ca.crt \
350 -subj "/C=US/ST=CA/L=Los
351 Angeles/O=rippled-unit-tests/CN=example.com" # generate private cert
352 openssl genrsa -out server.key 2048
353 # Generate certificate signing request
354 # since our unit tests can run in either ipv4 or ipv6 mode,
355 # we need to use extensions (subjectAltName) so that we can
356 # associate both ipv4 and ipv6 localhost addresses with this cert
357 cat >"extras.cnf" <<EOF
358 [req]
359 req_extensions = v3_req
360 distinguished_name = req_distinguished_name
361
362 [req_distinguished_name]
363
364 [v3_req]
365 subjectAltName = @alt_names
366
367 [alt_names]
368 DNS.1 = localhost
369 IP.1 = ::1
370 EOF
371 openssl req -new -key server.key -out server.csr \
372 -config extras.cnf \
373 -subj "/C=US/ST=California/L=San
374 Francisco/O=rippled-unit-tests/CN=127.0.0.1" \
375
376 # Create public certificate by signing with our CA
377 openssl x509 -req -days 10000 -in server.csr -CA ca.crt -CAkey ca.key
378 -out server.crt \ -extfile extras.cnf -set_serial 01 -extensions v3_req
379
380 # generate DH params for server
381 openssl dhparam -out dh.pem 2048
382 # verify certs
383 openssl verify -CAfile ca.crt server.crt
384 openssl x509 -in server.crt -text -noout
385 popd
386 ```
387 */
388 static std::string const&
390 {
391 static std::string const cert{R"cert(
392-----BEGIN CERTIFICATE-----
393MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEL
394MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw
395bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIyMDIwNTIz
396NDk0M1oXDTQ5MDYyMzIzNDk0M1owazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
397bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs
398ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
399AQEFAAOCAQ8AMIIBCgKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJ
400qTuBUcVcTRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs
401+T/qkwmc/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9f
402n9vrxnWX9LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3
403zVDe7+sxR//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0q
404S1Uj77mhwxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABoyowKDAmBgNV
405HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEL
406BQADggEBAJkUFNS0CeEAKvo0ttzooXnCDH3esj2fwmLJQYLUGsAF8DFrFHTqZEcx
407hFRdr0ftEb/VKpV9dVF6xtSoMU56kHOnhbHEWADyqdKUkCDjrGBet5QdWmEwNV2L
408nYrwGQBAybMt/+1XMUV8HeLFJNHnyxfQYcW0fUsrmNGk8W0kzWuuq88qbhfXZAIx
409KiXrzYpLlM0RlpWXRfYQ6mTdSrRrLnEo5MklizVgNB8HYX78lxa06zP08oReQcfT
410GSGO8NEEq8BTVmp69zD1JyfvQcXzsi7WtkAX+/EOFZ7LesnZ6VsyjZ74wECCaQuD
411X1yu/XxHqchM+DOzzVw6wRKaM7Zsk80=
412-----END CERTIFICATE-----
413)cert"};
414 return cert;
415 }
416
417 static std::string const&
418 key()
420 static std::string const key{R"pkey(
421-----BEGIN RSA PRIVATE KEY-----
422MIIEpAIBAAKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJqTuBUcVc
423TRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs+T/qkwmc
424/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9fn9vrxnWX
4259LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3zVDe7+sx
426R//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0qS1Uj77mh
427wxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABAoIBAEC9MDpOu+quvg8+
428kt4MKSFdIhQuM7WguNaTe5AkSspDrcJzT7SK275mp259QIYCzMxxuA8TSZTb8A1C
429t6dgKbi7k6FaGMCYMRHzzK6NZfMbPi6cj245q9LYlZpdQswuM/FdPpPH1zUxrNYK
430CIaooZ6ZHzlSD/eaRMgkBQEkONHrZZtEinLIvKedwssPCaXkIISmt7MFQTDOlxkf
431K0Mt1mnRREPYbYSfPEEfIyy/KDIiB5AzgGt+uPOn8Oeb1pSqy69jpYcfhSj+bo4S
432UV6qTuTfBd4qkkNI6d/Z7DcDJFFlfloG/vVgGk/beWNnL2e39vzxiebB3w+MQn4F
433Wyx5mCECgYEA22z1/ihqt9LIAWtP42oSS3S/RxlFzpp5d7QfNqFnEoVgeRhQzleP
434pRJIzVXpMYBxexZYqZA/q8xBSggz+2gmRoYnW20VIzl14DsSH378ye3FRwJB0tLy
435dWU8DC7ZB5XQCTvI9UY3voJNToknODw7RCNO1h3V3T1y6JRLdcLskk8CgYEA2OLy
436aE5bvsUaLBSv7W9NFhSuZ0p9Y0pFmRgHI7g8i/AgRZ0BgiE8u8OZSHmPJPMaNs/h
437YIEIrlsgDci1PzwrUYseRp/aiVE1kyev09/ihqRXTPpLQu6h/d63KRe/06W3t5X3
438Dmfj49hH5zGPBI/0y1ECV/n0fwnRhxSv7fNr3RECgYBEuFpOUAAkNApZj29ErNqv
4398Q9ayAp5yx1RpQLFjEUIoub05e2gwgGF1DUiwc43p59iyjvYVwnp1x13fxwwl4yt
440N6Sp2H7vOja1lCp33MB0yVeohodw7InsxFjLA/0KiBvQWH32exhIPOzTNNcooIx7
441KYeuPUfWc0FCn/cGGZcXtwKBgQC1hp1k99CKBuY05suoanOWe5DNGud/ZvaBgD7Z
442gqYKadxY52QPyknOzZNJuZQ5VM8n+S2lW9osNFDLuKUaW/3Vrh6U9c4vCC1TEPB0
4434PnzvzDiWMsNJjWnCfU7C4meVyFBIt84y3NNjAQCWNRe+S3lzdOsVqRwf4NDD+l/
444uzEYQQKBgQCJczIlwobm1Y6O41hbGZhZL/CGMNS6Z0INi2yasV0WDqYlh7XayHMD
445cK55dMILcbHqeIBq/wR6sIhw6IJcaDBfFfrJiKKDilfij2lHxR2FQrEngtTCCRV+
446ZzARzaWhQPvbDqEtLJDWuXZNXfL8/PTIs5NmuKuQ8F4+gQJpkQgwaw==
447-----END RSA PRIVATE KEY-----
448)pkey"};
449 return key;
450 }
451
452 static std::string const&
453 ca_cert()
454 {
455 static std::string const cert{R"cert(
456-----BEGIN CERTIFICATE-----
457MIIDpzCCAo+gAwIBAgIUWc45WqaaNuaSLoFYTMC/Mjfqw/gwDQYJKoZIhvcNAQEL
458BQAwYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQHDAtMb3MgQW5n
459ZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0LXRlc3RzMRQwEgYDVQQDDAtleGFt
460cGxlLmNvbTAeFw0yMjAyMDUyMzQ5MDFaFw00OTA2MjMyMzQ5MDFaMGMxCzAJBgNV
461BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLTG9zIEFuZ2VsZXMxGzAZBgNV
462BAoMEnJpcHBsZWQtdW5pdC10ZXN0czEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEi
463MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0f2JBW2XNW2wT5/ajX2qxmUY+
464aNJGfpV6gZ5CmwdQpbHrPPvJoskxwsCyr3GifzT/GtCpmb1fiu59uUAPxQEYCxiq
465V+HchX4g4Vl27xKJ0P+usxuEED9v7TCteKum9u9eMZ8UDF0fspXcnWGs9fXlyoTj
466uTRP1SBQllk44DPc/KzlrtH+QNXmr9XQnP8XvwWCgJXMx87voxEGiFFOVhkSSAOv
467v+OUGgEuq0NPgwv2LHBlYHSdkoU9F5Z/TmkCAFMShbyoUjldIz2gcWXjN2tespGo
468D6qYvasvPIpmcholBBkc0z8QDt+RNq+Wzrults7epJXy/u+txGK9cHCNlLCpAgMB
469AAGjUzBRMB0GA1UdDgQWBBS1oydh+YyqDNOFKYOvOtVMWKqV4zAfBgNVHSMEGDAW
470gBS1oydh+YyqDNOFKYOvOtVMWKqV4zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
471DQEBCwUAA4IBAQCDPyGKQwQ8Lz0yEgvIl/Uo9BtwAzlvjrLM/39qhStLQqDGSs2Q
472xFIbtjzjuLf5vR3q6OJ62CCvzqXgHkJ+hzVN/tAvyliGTdjJrK+xv1M5a+XipO2f
473c9lb4gRbFL/DyoeoWgb1Rkv3gFf0FlCYH+ZUcYb9ZYCRlGtFgOcxJI2g+T7jSLFp
4748+hSzQ6W5Sp9L6b5iJyCww1vjBvBqzNyZMNeB4gXGtd6z9vMDSvKboTdGD7wcFB+
475mRMyNekaRw+Npy4Hjou5sx272cXHHmPCSF5TjwdaibSaGjx1k0Q50mOf7S9KG5b5
4767X1e3FekJlaD02EBEhtkXURIxogOQALdFncj
477-----END CERTIFICATE-----
478)cert"};
479 return cert;
480 }
481
482 static std::string const&
483 dh()
484 {
485 static std::string const dh{R"dh(
486-----BEGIN DH PARAMETERS-----
487MIIBCAKCAQEAp2I2fWEUZ3sCNfitSRC/MdAhJE/bS+NO0O2tWdIdlvmIFE6B5qhC
488sGW9ojrQT8DTxBvGAcbjr/jagmlE3BV4oSnxyhP37G2mDvMOJ29J3NvFD/ZFAW0d
489BvZJ1RNvMu29NmVCyt6/jgzcqrqnami9uD93aK+zaVrlPsPEYM8xB19HXwqsEYCL
490ux2B7sqXm9Ts74HPg/EV+pcVon9phxNWxxgHlOvFc2QjZ3hXH++kzmJ4vs7N/XDB
491xbEQ+TUZ5jbJGSeBqNFKFeuOUQGJ46Io0jBSYd4rSmKUXkvElQwR+n7KF3jy1uAt
492/8hzd8tHn9TyW7Q2/CPkOA6dCXzltpOSowIBAg==
493-----END DH PARAMETERS-----
494)dh"};
495 return dh;
496 }
497
498private:
499 struct lambda
500 {
501 int id;
502 TrustedPublisherServer& self;
504 boost::asio::executor_work_guard<boost::asio::executor> work;
505 bool ssl;
506
507 lambda(
508 int id_,
509 TrustedPublisherServer& self_,
511 bool ssl_)
512 : id(id_)
513 , self(self_)
514 , sock(std::move(sock_))
515 , work(sock_.get_executor())
516 , ssl(ssl_)
517 {
518 }
519
520 void
521 operator()()
522 {
523 self.do_peer(id, std::move(sock), ssl);
524 }
525 };
526
527 void
529 {
530 if (ec || !acceptor_.is_open())
531 return;
532
533 static int id_ = 0;
534 std::thread{lambda{++id_, *this, std::move(sock_), useSSL_}}.detach();
535 acceptor_.async_accept(
536 sock_,
538 error_code ec) {
539 if (auto p = wp.lock())
540 {
541 p->on_accept(ec);
542 }
543 });
544 }
545
546 void
547 do_peer(int id, socket_type&& s, bool ssl)
548 {
549 using namespace boost::beast;
550 using namespace boost::asio;
551 socket_type sock(std::move(s));
552 flat_buffer sb;
553 error_code ec;
555
556 if (ssl)
557 {
558 // Construct the stream around the socket
559 ssl_stream.emplace(sock, sslCtx_);
560 // Perform the SSL handshake
561 ssl_stream->handshake(ssl::stream_base::server, ec);
562 if (ec)
563 return;
564 }
565
566 for (;;)
567 {
568 resp_type res;
569 req_type req;
570 try
571 {
572 if (ssl)
573 http::read(*ssl_stream, sb, req, ec);
574 else
575 http::read(sock, sb, req, ec);
576
577 if (ec)
578 break;
579
580 std::string_view const path = req.target();
581 res.insert("Server", "TrustedPublisherServer");
582 res.version(req.version());
583 res.keep_alive(req.keep_alive());
584 bool prepare = true;
585
586 if (boost::starts_with(path, "/validators2"))
587 {
588 res.result(http::status::ok);
589 res.insert("Content-Type", "application/json");
590 if (path == "/validators2/bad")
591 res.body() = "{ 'bad': \"2']";
592 else if (path == "/validators2/missing")
593 res.body() = "{\"version\": 2}";
594 else
595 {
596 int refresh = 5;
597 constexpr char const* refreshPrefix =
598 "/validators2/refresh/";
599 if (boost::starts_with(path, refreshPrefix))
600 refresh = boost::lexical_cast<unsigned int>(
601 path.substr(strlen(refreshPrefix)));
602 res.body() = getList2_(refresh);
603 }
604 }
605 else if (boost::starts_with(path, "/validators"))
606 {
607 res.result(http::status::ok);
608 res.insert("Content-Type", "application/json");
609 if (path == "/validators/bad")
610 res.body() = "{ 'bad': \"1']";
611 else if (path == "/validators/missing")
612 res.body() = "{\"version\": 1}";
613 else
614 {
615 int refresh = 5;
616 constexpr char const* refreshPrefix =
617 "/validators/refresh/";
618 if (boost::starts_with(path, refreshPrefix))
619 refresh = boost::lexical_cast<unsigned int>(
620 path.substr(strlen(refreshPrefix)));
621 res.body() = getList_(refresh);
622 }
623 }
624 else if (boost::starts_with(path, "/textfile"))
625 {
626 prepare = false;
627 res.result(http::status::ok);
628 res.insert("Content-Type", "text/example");
629 // if huge was requested, lie about content length
630 std::uint64_t cl =
631 boost::starts_with(path, "/textfile/huge")
633 : 1024;
634 res.content_length(cl);
635 if (req.method() == http::verb::get)
636 {
638 for (auto i = 0; i < 1024; ++i)
639 body << static_cast<char>(rand_int<short>(32, 126)),
640 res.body() = body.str();
641 }
642 }
643 else if (boost::starts_with(path, "/sleep/"))
644 {
645 auto const sleep_sec =
646 boost::lexical_cast<unsigned int>(path.substr(7));
648 std::chrono::seconds(sleep_sec));
649 }
650 else if (boost::starts_with(path, "/redirect"))
651 {
652 if (boost::ends_with(path, "/301"))
653 res.result(http::status::moved_permanently);
654 else if (boost::ends_with(path, "/302"))
655 res.result(http::status::found);
656 else if (boost::ends_with(path, "/307"))
657 res.result(http::status::temporary_redirect);
658 else if (boost::ends_with(path, "/308"))
659 res.result(http::status::permanent_redirect);
660
661 std::stringstream location;
662 if (boost::starts_with(path, "/redirect_to/"))
663 {
664 location << path.substr(13);
665 }
666 else if (!boost::starts_with(path, "/redirect_nolo"))
667 {
668 location
669 << (ssl ? "https://" : "http://")
670 << local_endpoint()
671 << (boost::starts_with(path, "/redirect_forever/")
672 ? path
673 : "/validators");
674 }
675 if (!location.str().empty())
676 res.insert("Location", location.str());
677 }
678 else
679 {
680 // unknown request
681 res.result(boost::beast::http::status::not_found);
682 res.insert("Content-Type", "text/html");
683 res.body() = "The file '" + std::string(path) +
684 "' was not "
685 "found";
686 }
687
688 if (prepare)
689 res.prepare_payload();
690 }
691 catch (std::exception const& e)
692 {
693 res = {};
694 res.result(boost::beast::http::status::internal_server_error);
695 res.version(req.version());
696 res.insert("Server", "TrustedPublisherServer");
697 res.insert("Content-Type", "text/html");
698 res.body() =
699 std::string{"An internal error occurred"} + e.what();
700 res.prepare_payload();
701 }
702
703 if (ssl)
704 write(*ssl_stream, res, ec);
705 else
706 write(sock, res, ec);
707
708 if (ec || req.need_eof())
709 break;
710 }
711
712 // Perform the SSL shutdown
713 if (ssl)
714 ssl_stream->shutdown(ec);
715 }
716};
717
720 boost::asio::io_context& ioc,
722 NetClock::time_point validUntil,
724 futures,
725 bool useSSL = false,
726 int version = 1,
727 bool immediateStart = true,
728 int sequence = 1)
729{
730 auto const r = std::make_shared<TrustedPublisherServer>(
731 ioc, validators, validUntil, futures, useSSL, version, sequence);
732 if (immediateStart)
733 r->start();
734 return r;
735}
736
737} // namespace test
738} // namespace ripple
739#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)
T move(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
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)
char const * getEnvLocalhostAddr()
Definition: envconfig.h:36
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)