proxygen
BogoShim.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
10 #include <fizz/crypto/Utils.h>
15 #include <folly/String.h>
19 
20 using namespace fizz;
21 using namespace fizz::client;
22 using namespace fizz::server;
23 using namespace folly;
24 using namespace folly::ssl;
25 
26 DEFINE_int32(port, 0, "port to connect to");
27 DEFINE_bool(server, false, "act as a server, otherwise act as a client");
28 DEFINE_string(key_file, "", "key file");
29 DEFINE_string(cert_file, "", "cert file");
30 DEFINE_int32(resume_count, 0, "number of additional connections to open");
31 
32 static constexpr int kUnimplemented = 89;
33 
34 static std::vector<std::string> kKnownFlags{"port",
35  "server",
36  "key_file",
37  "cert_file",
38  "resume_count"};
39 
44  public:
46  EventBase* evb,
47  uint16_t port,
48  std::shared_ptr<FizzServerContext> serverContext,
49  std::shared_ptr<SSLContext> sslContext)
50  : evb_(evb), serverContext_(serverContext), sslContext_(sslContext) {
52  socket_->connect(this, "::", port, 1000);
53  }
54 
55  void connectSuccess() noexcept override {
57  new AsyncFizzServer(std::move(socket_), serverContext_));
58  transport_->accept(this);
59  }
60 
61  void connectErr(const AsyncSocketException& ex) noexcept override {
62  LOG(INFO) << "TCP connect failed: " << ex.what();
63  socket_.reset();
64  success_ = false;
65  }
66 
68  success_ = true;
69  transport_->setReadCB(this);
70  }
71 
74  folly::exception_wrapper ex) noexcept override {
75  LOG(INFO) << "Handshake error: " << ex.what();
76  transport_.reset();
77  success_ = false;
78  }
79 
81  std::unique_ptr<folly::IOBuf> clientHello) override {
82  auto fd = transport_->getUnderlyingTransport<AsyncSocket>()->detachFd();
83  transport_.reset();
84  if (!sslContext_) {
85  unimplemented_ = true;
86  } else {
87  sslSocket_ =
88  AsyncSSLSocket::UniquePtr(new AsyncSSLSocket(sslContext_, evb_, fd));
89  sslSocket_->setPreReceivedData(std::move(clientHello));
90  sslSocket_->sslAccept(this);
91  }
92  }
93 
94  void getReadBuffer(void** /* bufReturn */, size_t* /* lenReturn */) override {
95  throw std::runtime_error("getReadBuffer not implemented");
96  }
97 
98  void readDataAvailable(size_t /* len */) noexcept override {
99  CHECK(false) << "readDataAvailable not implemented";
100  }
101 
102  bool isBufferMovable() noexcept override {
103  return true;
104  }
105 
106  void readBufferAvailable(std::unique_ptr<IOBuf> buf) noexcept override {
107  io::Cursor cursor(buf.get());
108  std::unique_ptr<IOBuf> write = IOBuf::create(0);
109  io::Appender appender(write.get(), 50);
110  while (!cursor.isAtEnd()) {
111  uint8_t byte;
112  cursor.pull(&byte, 1);
113  byte ^= 0xff;
114  appender.push(&byte, 1);
115  }
116  transport_->writeChain(nullptr, std::move(write));
117  }
118 
119  void readEOF() noexcept override {}
120 
121  void readErr(const AsyncSocketException&) noexcept override {}
122 
124  success_ = true;
125  }
126 
129  const folly::AsyncSocketException& ex) noexcept override {
130  LOG(INFO) << "SSL Handshake error: " << ex.what();
131  sslSocket_.reset();
132  success_ = false;
133  }
134 
135  bool unimplemented() const {
136  return unimplemented_;
137  }
138 
139  bool success() const {
140  return *success_;
141  }
142 
143  private:
146 
147  std::shared_ptr<FizzServerContext> serverContext_;
149 
150  std::shared_ptr<SSLContext> sslContext_;
152 
153  bool unimplemented_{false};
155 };
156 
160  public:
162  EventBase* evb,
163  uint16_t port,
164  std::shared_ptr<const FizzClientContext> clientContext)
165  : clientContext_(clientContext) {
167  socket_->connect(this, "::", port, 1000);
168  }
169 
170  void connectSuccess() noexcept override {
173  transport_->connect(
174  this, nullptr, folly::none, std::string("resumption-id"));
175  }
176 
177  void connectErr(const AsyncSocketException& ex) noexcept override {
178  LOG(INFO) << "TCP connect failed: " << ex.what();
179  socket_.reset();
180  success_ = false;
181  }
182 
184  success_ = true;
185  transport_->setReadCB(this);
186  }
187 
190  folly::exception_wrapper ex) noexcept override {
191  LOG(INFO) << "Handshake error: " << ex.what();
192  transport_.reset();
193 
194  // If the server sent us a protocol_version alert assume that
195  if (ex.what().find(
196  "received alert: protocol_version, in state ExpectingServerHello") !=
197  std::string::npos) {
198  unimplemented_ = true;
199  }
200  success_ = false;
201  }
202 
203  void getReadBuffer(void** /* bufReturn */, size_t* /* lenReturn */) override {
204  throw std::runtime_error("getReadBuffer not implemented");
205  }
206 
207  void readDataAvailable(size_t /* len */) noexcept override {
208  CHECK(false) << "readDataAvailable not implemented";
209  }
210 
211  bool isBufferMovable() noexcept override {
212  return true;
213  }
214 
215  void readBufferAvailable(std::unique_ptr<IOBuf> buf) noexcept override {
216  io::Cursor cursor(buf.get());
217  std::unique_ptr<IOBuf> write = IOBuf::create(0);
218  io::Appender appender(write.get(), 50);
219  while (!cursor.isAtEnd()) {
220  uint8_t byte;
221  cursor.pull(&byte, 1);
222  byte ^= 0xff;
223  appender.push(&byte, 1);
224  }
225  transport_->writeChain(nullptr, std::move(write));
226  }
227 
228  void readEOF() noexcept override {}
229 
230  void readErr(const AsyncSocketException&) noexcept override {}
231 
232  bool unimplemented() const {
233  return unimplemented_;
234  }
235 
236  bool success() const {
237  return *success_;
238  }
239 
240  private:
242 
243  std::shared_ptr<const FizzClientContext> clientContext_;
245 
246  bool unimplemented_{false};
248 };
249 
250 class TestRsaCert : public SelfCertImpl<KeyType::RSA> {
251  public:
253  std::string getIdentity() const override {
254  return "testrsacert";
255  }
256 };
257 
258 class TestP256Cert : public SelfCertImpl<KeyType::P256> {
259  public:
261  std::string getIdentity() const override {
262  return "testp256cert";
263  }
264 };
265 
266 std::unique_ptr<SelfCert> readSelfCert() {
267  BioUniquePtr b(BIO_new(BIO_s_file()));
268  BIO_read_filename(b.get(), FLAGS_cert_file.c_str());
269  std::vector<X509UniquePtr> certs;
270  while (true) {
271  X509UniquePtr x509(PEM_read_bio_X509(b.get(), nullptr, nullptr, nullptr));
272  if (!x509) {
273  break;
274  } else {
275  certs.push_back(std::move(x509));
276  }
277  }
278  if (certs.empty()) {
279  throw std::runtime_error("could not read cert");
280  }
281 
282  b.reset(BIO_new(BIO_s_file()));
283  BIO_read_filename(b.get(), FLAGS_key_file.c_str());
284  EvpPkeyUniquePtr key(
285  PEM_read_bio_PrivateKey(b.get(), nullptr, nullptr, nullptr));
286 
287  std::unique_ptr<SelfCert> cert;
288  if (EVP_PKEY_id(key.get()) == EVP_PKEY_RSA) {
289  return std::make_unique<TestRsaCert>(std::move(key), std::move(certs));
290  } else if (EVP_PKEY_id(key.get()) == EVP_PKEY_EC) {
291  return std::make_unique<TestP256Cert>(std::move(key), std::move(certs));
292  } else {
293  throw std::runtime_error("unknown cert type");
294  }
295 }
296 
297 int serverTest() {
298  auto certManager = std::make_unique<CertManager>();
299  certManager->addCert(readSelfCert(), true);
300 
301  auto ticketCipher = std::make_shared<AES128TicketCipher>();
302  auto ticketSeed = RandomGenerator<32>().generateRandom();
303  ticketCipher->setTicketSecrets({{range(ticketSeed)}});
304  ticketCipher->setValidity(std::chrono::seconds(60));
305 
306  auto serverContext = std::make_shared<FizzServerContext>();
307  serverContext->setCertManager(std::move(certManager));
308  serverContext->setTicketCipher(ticketCipher);
309  serverContext->setSupportedAlpns({"h2", "http/1.1"});
310  serverContext->setVersionFallbackEnabled(true);
311 
312  EventBase evb;
313  std::vector<std::unique_ptr<BogoTestServer>> servers;
314  for (size_t i = 0; i <= size_t(FLAGS_resume_count); i++) {
315  servers.push_back(std::make_unique<BogoTestServer>(
316  &evb, FLAGS_port, serverContext, nullptr));
317  }
318  evb.loop();
319  for (const auto& server : servers) {
320  if (server->unimplemented()) {
321  LOG(INFO) << "Testing unimplemented feature.";
322  return kUnimplemented;
323  }
324  }
325  for (const auto& server : servers) {
326  if (!server->success()) {
327  LOG(INFO) << "Connection failed.";
328  return 1;
329  }
330  }
331 
332  return 0;
333 }
334 
335 int clientTest() {
336  auto clientContext = std::make_shared<FizzClientContext>();
337  clientContext->setCompatibilityMode(true);
338 
339  if (!FLAGS_cert_file.empty()) {
340  clientContext->setClientCertificate(readSelfCert());
341  }
342 
343  EventBase evb;
344  if (FLAGS_resume_count >= 1) {
345  return kUnimplemented;
346  }
347  auto client =
348  std::make_unique<BogoTestClient>(&evb, FLAGS_port, clientContext);
349  evb.loop();
350  if (client->unimplemented()) {
351  LOG(INFO) << "Testing unimplemented feature.";
352  return kUnimplemented;
353  }
354  if (!client->success()) {
355  LOG(INFO) << "Connection failed.";
356  return 1;
357  }
358 
359  return 0;
360 }
361 
362 int main(int argc, char** argv) {
363  // Convert "-" in args to "_" so that we can use GFLAGS.
364  for (int i = 1; i < argc; i++) {
365  if (argv[i][0] == '-') {
366  for (char* j = argv[i] + 2; *j; j++) {
367  if (*j == '-') {
368  *j = '_';
369  }
370  }
371  if (std::find(
372  kKnownFlags.begin(),
373  kKnownFlags.end(),
374  std::string(argv[i] + 1)) == kKnownFlags.end()) {
375  LOG(INFO) << "unknown flag: " << argv[i];
376  return kUnimplemented;
377  }
378  }
379  }
380 
381  gflags::ParseCommandLineFlags(&argc, &argv, true);
382  google::InitGoogleLogging(argv[0]);
384 
385  if (FLAGS_port == 0) {
386  throw std::runtime_error("must specify port");
387  }
388 
389  if (FLAGS_server) {
390  return serverTest();
391  } else {
392  return clientTest();
393  }
394 }
std::unique_ptr< SelfCert > readSelfCert()
Definition: BogoShim.cpp:266
int clientTest()
Definition: BogoShim.cpp:335
bool unimplemented() const
Definition: BogoShim.cpp:232
void write(const T &in, folly::io::Appender &appender)
Definition: Types-inl.h:112
std::string getIdentity() const override
Definition: BogoShim.cpp:261
DEFINE_int32(port, 0,"port to connect to")
void handshakeSuc(folly::AsyncSSLSocket *) noexceptoverride
Definition: BogoShim.cpp:123
std::unique_ptr< X509, X509Deleter > X509UniquePtr
char b
bool isBufferMovable() noexceptoverride
Definition: BogoShim.cpp:102
std::unique_ptr< BIO, BioDeleter > BioUniquePtr
void readBufferAvailable(std::unique_ptr< IOBuf > buf) noexceptoverride
Definition: BogoShim.cpp:215
int main(int argc, char **argv)
Definition: BogoShim.cpp:362
Optional< bool > success_
Definition: BogoShim.cpp:154
bool success() const
Definition: BogoShim.cpp:236
void fizzHandshakeSuccess(AsyncFizzClient *) noexceptoverride
Definition: BogoShim.cpp:183
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
void readErr(const AsyncSocketException &) noexceptoverride
Definition: BogoShim.cpp:230
void fizzHandshakeError(AsyncFizzServer *, folly::exception_wrapper ex) noexceptoverride
Definition: BogoShim.cpp:72
void getReadBuffer(void **, size_t *) override
Definition: BogoShim.cpp:203
static void init()
Definition: Utils.cpp:42
Optional< bool > success_
Definition: BogoShim.cpp:247
std::unique_ptr< EVP_PKEY, EvpPkeyDeleter > EvpPkeyUniquePtr
void connectSuccess() noexceptoverride
Definition: BogoShim.cpp:170
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
AsyncSocket::UniquePtr socket_
Definition: BogoShim.cpp:241
requires E e noexcept(noexcept(s.error(std::move(e))))
std::unique_ptr< AsyncFizzClientT, folly::DelayedDestruction::Destructor > UniquePtr
BogoTestServer(EventBase *evb, uint16_t port, std::shared_ptr< FizzServerContext > serverContext, std::shared_ptr< SSLContext > sslContext)
Definition: BogoShim.cpp:45
void connectSuccess() noexceptoverride
Definition: BogoShim.cpp:55
std::shared_ptr< const FizzClientContext > clientContext_
Definition: BogoShim.cpp:243
void connectErr(const AsyncSocketException &ex) noexceptoverride
Definition: BogoShim.cpp:61
void readDataAvailable(size_t) noexceptoverride
Definition: BogoShim.cpp:98
void readEOF() noexceptoverride
Definition: BogoShim.cpp:228
bool unimplemented() const
Definition: BogoShim.cpp:135
static std::vector< std::string > kKnownFlags
Definition: BogoShim.cpp:34
Gen range(Value begin, Value end)
Definition: Base.h:467
std::unique_ptr< AsyncSSLSocket, Destructor > UniquePtr
AsyncFizzClientT< ClientStateMachine > AsyncFizzClient
char ** argv
BogoTestClient(EventBase *evb, uint16_t port, std::shared_ptr< const FizzClientContext > clientContext)
Definition: BogoShim.cpp:161
bool isBufferMovable() noexceptoverride
Definition: BogoShim.cpp:211
void fizzHandshakeSuccess(AsyncFizzServer *) noexceptoverride
Definition: BogoShim.cpp:67
EventBase * evb_
void fizzHandshakeAttemptFallback(std::unique_ptr< folly::IOBuf > clientHello) override
Definition: BogoShim.cpp:80
DEFINE_bool(server, false,"act as a server, otherwise act as a client")
AsyncSSLSocket::UniquePtr sslSocket_
AsyncServerSocket::UniquePtr socket_
EventBase * evb_
Definition: BogoShim.cpp:144
Definition: Actions.h:16
void readDataAvailable(size_t) noexceptoverride
Definition: BogoShim.cpp:207
void getReadBuffer(void **, size_t *) override
Definition: BogoShim.cpp:94
bool success() const
Definition: BogoShim.cpp:139
void readBufferAvailable(std::unique_ptr< IOBuf > buf) noexceptoverride
Definition: BogoShim.cpp:106
void readErr(const AsyncSocketException &) noexceptoverride
Definition: BogoShim.cpp:121
AsyncFizzServer::UniquePtr transport_
Definition: BogoShim.cpp:148
AsyncSocket::UniquePtr socket_
Definition: BogoShim.cpp:145
std::shared_ptr< FizzClientContext > clientContext_
std::shared_ptr< FizzServerContext > serverContext_
Definition: BogoShim.cpp:147
void readEOF() noexceptoverride
Definition: BogoShim.cpp:119
AsyncFizzClient::UniquePtr transport_
Definition: BogoShim.cpp:244
std::unique_ptr< AsyncFizzServerT, folly::DelayedDestruction::Destructor > UniquePtr
const char * string
Definition: Conv.cpp:212
AsyncFizzClient::UniquePtr transport_
int serverTest()
Definition: BogoShim.cpp:297
static constexpr int kUnimplemented
Definition: BogoShim.cpp:32
void handshakeErr(folly::AsyncSSLSocket *, const folly::AsyncSocketException &ex) noexceptoverride
Definition: BogoShim.cpp:127
AsyncFizzServerT< ServerStateMachine > AsyncFizzServer
std::shared_ptr< SSLContext > sslContext_
Definition: BogoShim.cpp:150
void connectErr(const AsyncSocketException &ex) noexceptoverride
Definition: BogoShim.cpp:177
AsyncSSLSocket::UniquePtr sslSocket_
Definition: BogoShim.cpp:151
std::string getIdentity() const override
Definition: BogoShim.cpp:253
std::unique_ptr< AsyncSocket, Destructor > UniquePtr
Definition: AsyncSocket.h:83
DEFINE_string(key_file,"","key file")
constexpr None none
Definition: Optional.h:87
void fizzHandshakeError(AsyncFizzClient *, folly::exception_wrapper ex) noexceptoverride
Definition: BogoShim.cpp:188