in fizz/experimental/ktls/AsyncKTLSSocket.cpp [192:288]
folly::AsyncSocket::ReadResult AsyncKTLSSocket::processHandshakeData(
folly::ByteRange payload) {
// [note.handshake_fits_in_record_assumption]
//
// It is extremely likely (unless the peer is malicious, or the user
// provides a readCallback that returns extremely small buffers) that any
// post handshake message (of which, there are only a few) sent by the
// peer can fit in one record (and consequently, can be handled directly
// by this call).
//
// We try to parse the handshake message directly off of `payloadBuf`
// first, before we fall back to copying the contents of the buffer into
// an internal IOBufQueue.
auto payloadBuf =
folly::IOBuf::wrapBufferAsValue(payload.data(), payload.size());
VLOG(10) << "AsyncKTLSSocket::processHandshakeData()";
folly::Optional<fizz::Param> handshakeMessage;
// TODO: This can probably be simplified
try {
if (FOLLY_LIKELY(unparsedHandshakeData_ == nullptr)) {
// TODO: Refactor fizz::ReadRecordLayer decoding logic to work on IOBuf
// so we can avoid constructing an iobufqueue with a single element (
// this forces a heap allocation...)
folly::IOBufQueue tmp{folly::IOBufQueue::cacheChainLength()};
tmp.append(payloadBuf);
handshakeMessage = fizz::ReadRecordLayer::decodeHandshakeMessage(tmp);
if (!handshakeMessage) {
unparsedHandshakeData_ = std::make_unique<folly::IOBufQueue>(
folly::IOBufQueue::cacheChainLength());
payloadBuf.makeManaged();
unparsedHandshakeData_->append(payloadBuf.clone());
return ReadResult(READ_BLOCKING);
}
} else {
// We already have data saved from last time
payloadBuf.makeManaged();
unparsedHandshakeData_->append(payloadBuf.clone());
handshakeMessage = fizz::ReadRecordLayer::decodeHandshakeMessage(
*unparsedHandshakeData_);
if (!handshakeMessage) {
return ReadResult(READ_BLOCKING);
} else {
unparsedHandshakeData_.reset();
}
}
} catch (std::exception& ex) {
// TODO: Send a decode_error alert
return ReadResult(
READ_ERROR,
std::make_unique<folly::AsyncSocketException>(
folly::AsyncSocketException::SSL_ERROR,
folly::to<std::string>(
"error decoding handshake data received by ktls: ",
ex.what())));
}
// At this point, `handshakeMessage` is guaranteed to be valid.
//
// However, the *contents* of the handshake struct may be pointing to
// borrowed memory. TLSCallback implementations must unshare() any
// buffers prior to retaining them
//
// TODO is there a less error prone way to enforce this?
DCHECK(handshakeMessage.hasValue());
auto param = std::move(handshakeMessage).value();
switch (param.type()) {
case decltype(param)::Type::KeyUpdate_E:
// The kTLS implementation in the kernel does not currently support
// switching keys.
VLOG(10) << "Received key update on kTLS connection";
return ReadResult(
READ_ERROR,
std::make_unique<folly::AsyncSocketException>(
folly::AsyncSocketException::SSL_ERROR,
"ktls does not support key_updates"));
case decltype(param)::Type::NewSessionTicket_E:
VLOG(10) << "Received NewSessionTicket on KTLS connection";
tlsCallback_->receivedNewSessionTicket(
this, std::move(*param.asNewSessionTicket()));
break;
default:
// Treat any other post-handshake message as an unexpected message
// TODO: Send alert.
return ReadResult(
READ_ERROR,
std::make_unique<folly::AsyncSocketException>(
folly::AsyncSocketException::SSL_ERROR,
"ktls received unexpected handshake content"));
}
// If the socket is still readable, the next record will be processed
// in the next loop iteration. AsyncSocket is level triggered.
return ReadResult(READ_BLOCKING);
}