19 using namespace folly;
28 <<
"Usage: s_client args\n" 30 <<
"Supported arguments:\n" 31 <<
" -host host (use connect instead)\n" 32 <<
" -port port (use connect instead)\n" 33 <<
" -connect host:port (set the address to connect to. Default: localhost:4433)\n" 34 <<
" -verify (enable server cert verification. Default: false)\n" 35 <<
" -cert cert (PEM format client certificate to send if requested. Default: none)\n" 36 <<
" -key key (PEM format private key for client certificate. Default: none)\n" 37 <<
" -pass password (private key password. Default: none)\n" 38 <<
" -capaths d1:... (colon-separated paths to directories of CA certs used for verification)\n" 39 <<
" -cafile file (path to bundle of CA certs used for verification)\n" 40 <<
" -reconnect (after connecting, open another connection using a psk. Default: false)\n" 41 <<
" -servername name (server name to send in SNI. Default: same as host)\n" 42 <<
" -alpn alpn1,... (comma-separated list of ALPNs to send. Default: none)\n" 43 <<
" -early (enables sending early data during resumption. Default: false)\n" 44 <<
" -quiet (hide informational logging. Default: false)\n";
52 public InputHandlerCallback {
56 std::shared_ptr<FizzClientContext> clientContext,
58 std::shared_ptr<const CertificateVerifier> verifier,
68 sock_->connect(
this, addr);
71 void close()
override {
80 LOG(ERROR) <<
"Connect error: " << ex.what();
84 void connectSuccess()
noexcept override {
85 LOG(INFO) << (
willResume_ ?
"Initial connection" :
"Connection")
94 printHandshakeSuccess();
96 LOG(INFO) <<
"Early handshake success.";
102 void fizzHandshakeError(
105 LOG(ERROR) <<
"Handshake error: " << ex.what();
109 void getReadBuffer(
void** ,
size_t* )
override {
110 throw std::runtime_error(
"getReadBuffer not implemented");
113 void readDataAvailable(
size_t ) noexcept
override {
114 throw std::runtime_error(
"readDataAvailable not implemented");
117 bool isBufferMovable() noexcept
override {
121 void readBufferAvailable(std::unique_ptr<IOBuf> buf) noexcept
override {
125 void readEOF() noexcept
override {
126 LOG(INFO) << (
willResume_ ?
"Initial EOF" :
"EOF");
133 LOG(ERROR) <<
"Read error: " << ex.what();
137 void onReplaySafe()
override {
138 printHandshakeSuccess();
141 bool connected()
const override {
145 void write(std::unique_ptr<IOBuf> msg)
override {
152 void printHandshakeSuccess() {
156 LOG(INFO) << (
willResume_ ?
"Initial handshake" :
"Handshake")
160 LOG(INFO) <<
" Named Group: " 162 LOG(INFO) <<
" Signature Scheme: " 165 LOG(INFO) <<
" PSK Mode: " 167 LOG(INFO) <<
" Key Exchange Type: " <<
toString(*
state.keyExchangeType());
169 LOG(INFO) <<
" Server Identity: " 171 LOG(INFO) <<
" Client Identity: " 175 if (!PEM_write_bio_X509(bio.get(),
serverCert->getX509().get())) {
176 LOG(ERROR) <<
" Couldn't convert server certificate to PEM: " 177 << SSLContext::getErrors();
179 BUF_MEM* bptr =
nullptr;
180 BIO_get_mem_ptr(bio.get(), &bptr);
181 LOG(INFO) <<
" Server Certificate:\n" 188 if (!PEM_write_bio_X509(bio.get(),
clientCert->getX509().get())) {
189 LOG(ERROR) <<
" Couldn't convert client certificate to PEM: " 190 << SSLContext::getErrors();
192 BUF_MEM* bptr =
nullptr;
193 BIO_get_mem_ptr(bio.get(), &bptr);
194 LOG(INFO) <<
" Client Certificate:\n" 198 LOG(INFO) <<
" ALPN: " <<
state.alpn().value_or(
"(none)");
239 bool reconnect =
false;
241 std::vector<std::string> alpns;
246 {
"-host", {
true, [&host](
const std::string& arg) { host = arg; }}},
250 {
"-connect", {
true, [&host, &port](
const std::string& arg) {
251 size_t colonIdx = arg.find(
':');
252 if (colonIdx == std::string::npos) {
253 throw std::runtime_error(
"-connect requires a host:port pair.");
255 host = arg.substr(0, colonIdx);
259 {
"-cert", {
true, [&certPath](
const std::string& arg) { certPath = arg; }}},
260 {
"-key", {
true, [&keyPath](
const std::string& arg) { keyPath = arg; }}},
261 {
"-pass", {
true, [&keyPass](
const std::string& arg) { keyPass = arg; }}},
262 {
"-capath", {
true, [&caPath](
const std::string& arg) { caPath = arg; }}},
263 {
"-cafile", {
true, [&caFile](
const std::string& arg) { caFile = arg; }}},
264 {
"-reconnect", {
false, [&reconnect](
const std::string&) {
267 {
"-servername", {
true, [&customSNI](
const std::string& arg) {
270 {
"-alpn", {
true, [&alpns](
const std::string& arg) {
272 auto remainder = arg;
273 for (
auto commaPos = remainder.find(
',');
274 commaPos != std::string::npos;
275 commaPos = remainder.find(
',')) {
276 alpns.push_back(remainder.substr(0, commaPos));
277 remainder = remainder.substr(commaPos+1);
280 alpns.push_back(remainder);
282 {
"-early", {
false, [&early](
const std::string&) { early =
true; }}},
284 FLAGS_minloglevel = google::GLOG_ERROR;
294 }
catch (
const std::exception& e) {
295 LOG(ERROR) <<
"Error: " << e.what();
300 if (certPath.empty() != keyPath.empty()) {
301 LOG(ERROR) <<
"-cert and -key are both required when specified";
306 std::shared_ptr<const CertificateVerifier> verifier;
307 auto clientContext = std::make_shared<FizzClientContext>();
309 if (!alpns.empty()) {
310 clientContext->setSupportedAlpns(
std::move(alpns));
313 clientContext->setSupportedVersions(
315 clientContext->setSendEarlyData(early);
320 if (!caPath.empty() || !caFile.empty()) {
321 storePtr.reset(X509_STORE_new());
322 auto caFilePtr = caFile.empty() ?
nullptr : caFile.c_str();
323 auto caPathPtr = caPath.empty() ?
nullptr : caPath.c_str();
325 if (X509_STORE_load_locations(storePtr.get(), caFilePtr, caPathPtr) ==
327 LOG(ERROR) <<
"Failed to load CA certificates";
332 verifier = std::make_shared<const DefaultCertificateVerifier>(
336 if (!certPath.empty()) {
339 if (!
readFile(certPath.c_str(), certData)) {
340 LOG(ERROR) <<
"Failed to read certificate";
342 }
else if (!
readFile(keyPath.c_str(), keyData)) {
343 LOG(ERROR) <<
"Failed to read private key";
347 std::unique_ptr<SelfCert> cert;
348 if (!keyPass.empty()) {
353 clientContext->setClientCertificate(
std::move(cert));
358 auto sni = customSNI.
empty() ? host : customSNI;
359 Connection conn(&evb, clientContext,
sni, verifier, reconnect);
360 Connection resumptionConn(&evb, clientContext,
sni, verifier,
false);
361 Connection* inputTarget = &conn;
363 auto pskCache = std::make_shared<ResumptionPskCache>(
364 &evb, [&conn, &resumptionConn,
addr]() {
366 resumptionConn.connect(addr);
368 clientContext->setPskCache(pskCache);
369 inputTarget = &resumptionConn;
374 }
catch (
const std::exception& e) {
375 LOG(ERROR) <<
"Error: " << e.what();
folly::StringPiece toString(StateEnum state)
bool readFile(int fd, Container &out, size_t num_bytes=std::numeric_limits< size_t >::max())
void write(const T &in, folly::io::Appender &appender)
int connect(NetworkSocket s, const sockaddr *name, socklen_t namelen)
static std::unique_ptr< SelfCert > makeSelfCert(std::string certData, std::string keyData, const std::vector< std::shared_ptr< CertificateCompressor >> &compressors={})
std::unique_ptr< BIO, BioDeleter > BioUniquePtr
constexpr detail::Map< Move > move
—— Concurrent Priority Queue Implementation ——
requires E e noexcept(noexcept(s.error(std::move(e))))
std::unique_ptr< AsyncFizzClientT, folly::DelayedDestruction::Destructor > UniquePtr
void putPsk(const std::string &identity, CachedPsk psk) override
AsyncFizzClientT< ClientStateMachine > AsyncFizzClient
constexpr bool empty() const
void runInLoop(LoopCallback *callback, bool thisIteration=false)
Optional< std::string > sni_
std::unique_ptr< X509_STORE, X509StoreDeleter > X509StoreUniquePtr
AsyncSocket::UniquePtr sock_
std::shared_ptr< const Cert > serverCert
std::shared_ptr< FizzClientContext > clientContext_
AsyncFizzClient::UniquePtr transport_
std::shared_ptr< const CertificateVerifier > verifier_
std::shared_ptr< const Cert > clientCert
folly::Function< void()> callback_
Range< const char * > StringPiece
int close(NetworkSocket s)
ThreadPoolListHook * addr
std::unique_ptr< AsyncSocket, Destructor > UniquePtr