proxygen
AsyncFizzClient-inl.h
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 
9 namespace fizz {
10 namespace client {
11 
12 template <typename SM>
15  std::shared_ptr<const FizzClientContext> fizzContext,
16  const std::shared_ptr<ClientExtensions>& extensions)
17  : AsyncFizzBase(std::move(socket)),
18  fizzContext_(std::move(fizzContext)),
19  extensions_(extensions),
20  visitor_(*this),
21  fizzClient_(state_, transportReadBuf_, visitor_, this) {}
22 
23 template <typename SM>
25  folly::EventBase* eventBase,
26  std::shared_ptr<const FizzClientContext> fizzContext,
27  const std::shared_ptr<ClientExtensions>& extensions)
28  : AsyncFizzBase(
29  folly::AsyncSocket::UniquePtr(new folly::AsyncSocket(eventBase))),
30  fizzContext_(std::move(fizzContext)),
31  extensions_(extensions),
32  visitor_(*this),
34 
35 template <typename SM>
37  HandshakeCallback* callback,
39  std::chrono::milliseconds timeout) {
40  auto pskIdentity = hostname;
41  connect(
42  callback,
43  std::make_shared<DefaultCertificateVerifier>(VerificationContext::Client),
44  std::move(hostname),
45  std::move(pskIdentity),
46  std::move(timeout));
47 }
48 
49 template <typename SM>
51  HandshakeCallback* callback,
52  std::shared_ptr<const CertificateVerifier> verifier,
54  folly::Optional<std::string> pskIdentity,
55  std::chrono::milliseconds timeout) {
56  DelayedDestruction::DestructorGuard dg(this);
57 
58  CHECK(callback);
59  CHECK(!callback_);
60  callback_ = callback;
61 
62  if (!transport_->good()) {
65  "handshake connect called but socket isn't open");
66  deliverAllErrors(ase, false);
67  return;
68  }
69 
70  sni_ = sni;
71  pskIdentity_ = pskIdentity;
72 
73  if (timeout != std::chrono::milliseconds::zero()) {
74  startHandshakeTimeout(timeout);
75  }
76 
78 
80  if (pskIdentity) {
81  cachedPsk = fizzContext_->getPsk(*pskIdentity);
82  }
83  fizzClient_.connect(
85  std::move(verifier),
86  std::move(sni),
87  std::move(cachedPsk),
88  extensions_);
89 }
90 
91 template <typename SM>
93  const folly::SocketAddress& connectAddr,
95  std::shared_ptr<const CertificateVerifier> verifier,
97  folly::Optional<std::string> pskIdentity,
98  std::chrono::milliseconds totalTimeout,
99  std::chrono::milliseconds socketTimeout,
100  const folly::AsyncSocket::OptionMap& options,
101  const folly::SocketAddress& bindAddr) {
102  DelayedDestruction::DestructorGuard dg(this);
103 
104  CHECK(callback);
105  CHECK(!callback_);
106  callback_ = callback;
107 
108  verifier_ = std::move(verifier);
109  sni_ = sni;
110  pskIdentity_ = pskIdentity;
111 
112  if (totalTimeout != std::chrono::milliseconds::zero()) {
113  startHandshakeTimeout(std::move(totalTimeout));
114  }
115 
116  auto underlyingSocket =
117  transport_->getUnderlyingTransport<folly::AsyncSocket>();
118  if (underlyingSocket) {
119  underlyingSocket->disableTransparentTls();
120  underlyingSocket->connect(
121  this,
122  connectAddr,
123  static_cast<int>(socketTimeout.count()),
124  options,
125  bindAddr);
126  } else {
129  "could not find underlying socket");
130  deliverAllErrors(ase, false);
131  }
132 }
133 
134 template <typename SM>
136  return !error() && transport_->good();
137 }
138 
139 template <typename SM>
141  return transport_->readable();
142 }
143 
144 template <typename SM>
146  return callback_ || transport_->connecting();
147 }
148 
149 template <typename SM>
151  return transport_->error() || fizzClient_.inErrorState();
152 }
153 
154 template <typename SM>
157  if (serverCert) {
158  return serverCert->getX509();
159  } else {
160  return nullptr;
161  }
162 }
163 
164 template <typename SM>
166  auto cert = getSelfCertificate();
167  if (cert) {
168  return cert->getX509().get();
169  } else {
170  return nullptr;
171  }
172 }
173 
174 template <typename SM>
176  return earlyDataState_ ? getState().earlyDataParams()->serverCert.get()
177  : getState().serverCert().get();
178 }
179 
180 template <typename SM>
182  return earlyDataState_ ? getState().earlyDataParams()->clientCert.get()
183  : getState().clientCert().get();
184 }
185 
186 template <typename SM>
188  return !earlyDataState_.hasValue();
189 }
190 
191 template <typename SM>
194  DCHECK(!callback || !isReplaySafe());
195  replaySafetyCallback_ = callback;
196 }
197 
198 template <typename SM>
200  if (earlyDataState_) {
201  if (getState().earlyDataParams()->alpn) {
202  return *getState().earlyDataParams()->alpn;
203  } else {
204  return "";
205  }
206  } else {
207  if (getState().alpn()) {
208  return *getState().alpn();
209  } else {
210  return "";
211  }
212  }
213 }
214 
215 template <typename SM>
217  if (transport_->good()) {
218  fizzClient_.appClose();
219  } else {
220  DelayedDestruction::DestructorGuard dg(this);
222  folly::AsyncSocketException::END_OF_FILE, "socket closed locally");
223  deliverAllErrors(ase, false);
224  transport_->close();
225  }
226 }
227 
228 template <typename SM>
230  DelayedDestruction::DestructorGuard dg(this);
231  if (transport_->good()) {
232  fizzClient_.appClose();
233  }
235  folly::AsyncSocketException::END_OF_FILE, "socket closed locally");
236  deliverAllErrors(ase, false);
237  transport_->closeWithReset();
238 }
239 
240 template <typename SM>
242  DelayedDestruction::DestructorGuard dg(this);
243  if (transport_->good()) {
244  fizzClient_.appClose();
245  }
247  folly::AsyncSocketException::END_OF_FILE, "socket closed locally");
248  deliverAllErrors(ase, false);
249  transport_->closeNow();
250 }
251 
252 template <typename SM>
255 
257  if (pskIdentity_) {
258  cachedPsk = fizzContext_->getPsk(*pskIdentity_);
259  }
260  fizzClient_.connect(
261  fizzContext_,
263  sni_,
264  std::move(cachedPsk),
265  extensions_);
266 }
267 
268 template <typename SM>
271  deliverAllErrors(ex, false);
272 }
273 
274 template <typename SM>
277  std::unique_ptr<folly::IOBuf>&& buf,
279  if (error()) {
280  if (callback) {
281  callback->writeErr(
282  0,
285  "fizz app write in error state"));
286  }
287  return;
288  }
289 
290  if (earlyDataState_) {
291  auto size = buf->computeChainDataLength();
292  if (!earlyDataState_->pendingAppWrites.empty() ||
293  size > earlyDataState_->remainingEarlyData) {
294  AppWrite w;
295  w.callback = callback;
296  w.data = std::move(buf);
297  w.flags = flags;
298 
299  earlyDataState_->remainingEarlyData = 0;
300  earlyDataState_->pendingAppWrites.push_back(std::move(w));
301  } else {
302  EarlyAppWrite w;
303  w.callback = callback;
304  w.data = std::move(buf);
305  w.flags = flags;
306 
309  // We need to call unshare() to make a copy of the data here since we
310  // may need to resend it after we've already called writeSuccess().
311  // Particularly when using the write and writev interfaces, the
312  // application is allowed to delete the underlying buffer after getting
313  // the write callback.
314  auto writeCopy = w.data->clone();
315  writeCopy->unshare();
316  earlyDataState_->resendBuffer.append(std::move(writeCopy));
317  }
318 
319  earlyDataState_->remainingEarlyData -= size;
320  fizzClient_.earlyAppWrite(std::move(w));
321  }
322  } else {
323  AppWrite w;
324  w.callback = callback;
325  w.data = std::move(buf);
326  w.flags = flags;
327  fizzClient_.appWrite(std::move(w));
328  }
329 }
330 
331 template <typename SM>
333  const folly::AsyncSocketException& ex) {
334  DelayedDestruction::DestructorGuard dg(this);
335  deliverAllErrors(ex);
336 }
337 
338 template <typename SM>
340  fizzClient_.newTransportData();
341 }
342 
343 template <typename SM>
345  const folly::AsyncSocketException& ex,
346  bool closeTransport) {
347  DelayedDestruction::DestructorGuard dg(this);
349 
350  if (replaySafetyCallback_) {
351  replaySafetyCallback_ = nullptr;
352  }
353 
354  while (earlyDataState_ && !earlyDataState_->pendingAppWrites.empty()) {
355  auto w = std::move(earlyDataState_->pendingAppWrites.front());
356  earlyDataState_->pendingAppWrites.pop_front();
357  if (w.callback) {
358  w.callback->writeErr(0, ex);
359  }
360  }
361  fizzClient_.moveToErrorState(ex);
362  deliverError(ex, closeTransport);
363 }
364 
365 template <typename SM>
367  if (callback_) {
369  auto cb = *callback_;
372  cb,
373  [this, &ex](HandshakeCallback* callback) {
374  callback->fizzHandshakeError(this, std::move(ex));
375  },
376  [&ex](folly::AsyncSocket::ConnectCallback* callback) {
377  ex.handle(
378  [callback](const folly::AsyncSocketException& ase) {
379  callback->connectErr(ase);
380  },
381  [callback](const std::exception& stdEx) {
384  callback->connectErr(ase);
385  },
386  [callback](...) {
388  folly::AsyncSocketException::SSL_ERROR, "unknown error");
389  callback->connectErr(ase);
390  });
391  });
392  }
393 }
394 
395 template <typename SM>
397  client_.deliverAppData(std::move(data.data));
398 }
399 
400 template <typename SM>
402  DCHECK(!data.contents.empty());
403  Buf allData = std::move(data.contents.front().data);
404  for (size_t i = 1; i < data.contents.size(); ++i) {
405  allData->prependChain(std::move(data.contents[i].data));
406  }
407  client_.transport_->writeChain(data.callback, std::move(allData), data.flags);
408 }
409 
410 template <typename SM>
412  ReportEarlyHandshakeSuccess& earlySuccess) {
413  client_.earlyDataState_ = EarlyDataState();
414  client_.earlyDataState_->remainingEarlyData = earlySuccess.maxEarlyDataSize;
415  if (client_.callback_) {
416  auto cb = *client_.callback_;
417  client_.callback_ = folly::none;
419  cb,
420  [this](HandshakeCallback* callback) {
421  callback->fizzHandshakeSuccess(&client_);
422  },
424  callback->connectSuccess();
425  });
426  }
427 }
428 
429 template <typename SM>
432  switch (earlyDataRejectionPolicy_) {
436  "fizz early data rejected");
437  }
440  if (!earlyDataState_->resendBuffer.empty()) {
441  AppWrite resend;
442  resend.data = earlyDataState_->resendBuffer.move();
443  fizzClient_.appWrite(std::move(resend));
444  }
445  } else {
448  "fizz early data rejected, could not be resent");
449  }
450  break;
451  }
452  }
453  return folly::none;
454 }
455 
456 template <typename SM>
458  ReportHandshakeSuccess& success) {
459  client_.cancelHandshakeTimeout();
460  if (client_.earlyDataState_) {
461  if (!success.earlyDataAccepted) {
462  auto ex = client_.handleEarlyReject();
463  if (ex) {
464  if (client_.pskIdentity_) {
465  client_.fizzContext_->removePsk(*client_.pskIdentity_);
466  }
467  client_.deliverAllErrors(*ex, false);
468  client_.transport_->closeNow();
469  return;
470  }
471  }
472 
473  while (!client_.earlyDataState_->pendingAppWrites.empty()) {
474  auto w = std::move(client_.earlyDataState_->pendingAppWrites.front());
475  client_.earlyDataState_->pendingAppWrites.pop_front();
476  client_.fizzClient_.appWrite(std::move(w));
477  }
478  client_.earlyDataState_.clear();
479  }
480  if (client_.callback_) {
481  auto cb = *client_.callback_;
482  client_.callback_ = folly::none;
484  cb,
485  [this](HandshakeCallback* callback) {
486  callback->fizzHandshakeSuccess(&client_);
487  },
489  callback->connectSuccess();
490  });
491  }
492  if (client_.replaySafetyCallback_) {
493  auto callback = client_.replaySafetyCallback_;
494  client_.replaySafetyCallback_ = nullptr;
495  callback->onReplaySafe();
496  }
497 }
498 
499 template <typename SM>
502  // If the state machine reports that an early write happened after early data
503  // was already rejected, we need to invoke some write callback so that the
504  // write isn't leaked. For now we just call writeSuccess and let the actual
505  // rejection or early data get sorted out after full handshake success.
506  //
507  // TODO: buffer these callbacks until full handshake success, and call
508  // writeSuccess/writeErr depending on whether we are treating rejection
509  // as a fatal error.
510  if (write.write.callback) {
511  write.write.callback->writeSuccess();
512  }
513 }
514 
515 template <typename SM>
519  client_.deliverHandshakeError(std::move(error.error));
520  client_.deliverAllErrors(ase);
521 }
522 
523 template <typename SM>
525  client_.fizzClient_.waitForData();
526 
527  if (client_.callback_) {
528  // Make sure that the read callback is installed.
529  client_.startTransportReads();
530  }
531 }
532 
533 template <typename SM>
535  mutator(client_.state_);
536 }
537 
538 template <typename SM>
540  NewCachedPsk& newCachedPsk) {
541  if (client_.pskIdentity_) {
542  client_.fizzContext_->putPsk(
543  *client_.pskIdentity_, std::move(newCachedPsk.psk));
544  }
545 }
546 
547 template <typename SM>
549  return getState().cipher();
550 }
551 
552 template <typename SM>
553 std::vector<SignatureScheme> AsyncFizzClientT<SM>::getSupportedSigSchemes()
554  const {
556 }
557 
558 template <typename SM>
561  const Buf& context,
562  uint16_t length) const {
563  return fizzClient_.getEkm(label, context, length);
564 }
565 
566 template <typename SM>
569  const Buf& context,
570  uint16_t length) const {
571  return fizzClient_.getEarlyEkm(label, context, length);
572 }
573 
574 template <typename SM>
576  return getState().pskMode().has_value();
577 }
578 } // namespace client
579 } // namespace fizz
folly::fbstring what() const
std::unique_ptr< folly::IOBuf > data
Definition: Params.h:48
std::shared_ptr< const FizzClientContext > fizzContext_
folly::WriteFlags flags
Definition: Actions.h:43
virtual void deliverError(const folly::AsyncSocketException &ex, bool closeTransport=true)
folly::IOBufQueue transportReadBuf_
flags
Definition: http_parser.h:127
Buf getEarlyEkm(folly::StringPiece label, const Buf &context, uint16_t length) const
EarlyDataRejectionPolicy earlyDataRejectionPolicy_
void handle(CatchFns...fns)
folly::Optional< CipherSuite > getCipher() const override
std::unique_ptr< X509, X509Deleter > X509UniquePtr
void connectSuccess() noexceptoverride
void connectErr(const folly::AsyncSocketException &ex) noexceptoverride
context
Definition: CMakeCache.txt:563
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
folly::small_vector< TLSContent, 4 > contents
Definition: Actions.h:41
StringPiece alpn
STL namespace.
virtual void fizzHandshakeError(AsyncFizzClientT *transport, folly::exception_wrapper ex) noexcept=0
bool isReplaySafe() const override
FOLLY_CPP14_CONSTEXPR bool has_value() const noexcept
Definition: Optional.h:296
void transportError(const folly::AsyncSocketException &ex) override
virtual void startHandshakeTimeout(std::chrono::milliseconds)
folly::Optional< PskKeyExchangeMode > pskMode() const
Definition: State.h:142
folly::AsyncTransport::ReplaySafetyCallback * replaySafetyCallback_
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
std::unique_ptr< AsyncFizzClientT, folly::DelayedDestruction::Destructor > UniquePtr
folly::Optional< CipherSuite > cipher() const
Definition: State.h:113
const folly::Optional< std::string > & alpn() const
Definition: State.h:170
virtual void cancelHandshakeTimeout()
std::shared_ptr< const Cert > clientCert() const
Definition: State.h:84
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
folly::Optional< folly::AsyncSocketException > handleEarlyReject()
std::map< OptionKey, int > OptionMap
Definition: AsyncSocket.h:376
std::unique_ptr< AsyncTransportWrapper, Destructor > UniquePtr
void writeAppData(folly::AsyncTransportWrapper::WriteCallback *callback, std::unique_ptr< folly::IOBuf > &&buf, folly::WriteFlags flags=folly::WriteFlags::NONE) override
void setReplaySafetyCallback(folly::AsyncTransport::ReplaySafetyCallback *callback) override
FizzClient< ActionMoveVisitor, SM > fizzClient_
void write(folly::AsyncTransportWrapper::WriteCallback *callback, const void *buf, size_t bytes, folly::WriteFlags flags=folly::WriteFlags::NONE) override
folly::Optional< boost::variant< HandshakeCallback *, folly::AsyncSocket::ConnectCallback * > > callback_
const folly::Optional< EarlyDataParams > & earlyDataParams() const
Definition: State.h:163
folly::WriteFlags flags
Definition: Params.h:49
std::shared_ptr< ClientExtensions > extensions_
const Cert * getSelfCertificate() const override
std::basic_string< E, T, A > toStdString() const
Definition: FBString.h:1227
void deliverAllErrors(const folly::AsyncSocketException &ex, bool closeTransport=true)
folly::exception_wrapper error
Definition: Actions.h:50
bool earlyParametersMatch(const State &state)
Definition: Actions.h:16
void disableTransparentTls()
Definition: AsyncSocket.h:789
virtual void fizzHandshakeSuccess(AsyncFizzClientT *transport) noexcept=0
NetworkSocket socket(int af, int type, int protocol)
Definition: NetOps.cpp:412
std::shared_ptr< const Cert > serverCert
virtual void startTransportReads()
folly::AsyncTransportWrapper::WriteCallback * callback
Definition: Actions.h:37
folly::Optional< EarlyDataState > earlyDataState_
std::shared_ptr< const CertificateVerifier > verifier_
void deliverHandshakeError(folly::exception_wrapper ex)
const auto & getSupportedSigSchemes() const
std::vector< SignatureScheme > getSupportedSigSchemes() const override
StringPiece sni
const char * string
Definition: Conv.cpp:212
Buf getEkm(folly::StringPiece label, const Buf &context, uint16_t length) const override
std::unique_ptr< folly::IOBuf > Buf
Definition: Types.h:22
const X509 * getSelfCert() const override
std::shared_ptr< const Cert > serverCert() const
Definition: State.h:77
const
Definition: upload.py:398
folly::ssl::X509UniquePtr getPeerCert() const override
const FizzClientContext * context() const
Definition: State.h:70
virtual void writeSuccess() noexcept=0
decltype(auto) variant_match(Variant &&variant, Cases &&...cases)
Definition: Overload.h:74
const Cert * getPeerCertificate() const override
folly::Optional< std::string > pskIdentity_
virtual void writeErr(size_t bytesWritten, const AsyncSocketException &ex) noexcept=0
folly::Optional< std::string > sni_
virtual void connect(HandshakeCallback *callback, std::shared_ptr< const CertificateVerifier > verifier, folly::Optional< std::string > sni, folly::Optional< std::string > pskIdentity, std::chrono::milliseconds=std::chrono::milliseconds(0))
static constexpr uint64_t data[1]
Definition: Fingerprint.cpp:43
const State & getState() const
std::unique_ptr< folly::IOBuf > data
Definition: Params.h:54
folly::AsyncTransportWrapper::WriteCallback * callback
Definition: Params.h:47
constexpr None none
Definition: Optional.h:87
std::string getApplicationProtocol() const noexceptoverride
AsyncFizzClientT(folly::AsyncTransportWrapper::UniquePtr socket, std::shared_ptr< const FizzClientContext > fizzContext, const std::shared_ptr< ClientExtensions > &extensions=nullptr)
StringPiece label
std::unique_ptr< folly::IOBuf > data
Definition: Actions.h:23
folly::WriteFlags flags
Definition: Params.h:55
folly::AsyncTransportWrapper::WriteCallback * callback
Definition: Params.h:53