ScopeExit make_scope_exit(Fun &&fun) noexcept(noexcept(ScopeExit(ScopeExit((Fun &&) fun)))) { return ScopeExit((Fun &&) fun); }
Result<SslFd> SslFd::init(SocketFd fd, CSlice host, CSlice cert_file, VerifyPeer verify_peer) { #if TD_WINDOWS return Status::Error("TODO"); #else static bool init_openssl = [] { #if OPENSSL_VERSION_NUMBER >= 0x10100000L return OPENSSL_init_ssl(0, nullptr) != 0; #else OpenSSL_add_all_algorithms(); SSL_load_error_strings(); return OpenSSL_add_ssl_algorithms() != 0; #endif }(); CHECK(init_openssl); openssl_clear_errors("Before SslFd::init"); CHECK(!fd.empty()); auto ssl_method = #if OPENSSL_VERSION_NUMBER >= 0x10100000L TLS_client_method(); #else SSLv23_client_method(); #endif if (ssl_method == nullptr) { return create_openssl_error(-6, "Failed to create an SSL client method"); } auto ssl_ctx = SSL_CTX_new(ssl_method); if (ssl_ctx == nullptr) { return create_openssl_error(-7, "Failed to create an SSL context"); } auto ssl_ctx_guard = ScopeExit() + [&]() { SSL_CTX_free(ssl_ctx); }; long options = 0; #ifdef SSL_OP_NO_SSLv2 options |= SSL_OP_NO_SSLv2; #endif #ifdef SSL_OP_NO_SSLv3 options |= SSL_OP_NO_SSLv3; #endif SSL_CTX_set_options(ssl_ctx, options); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE); if (cert_file.empty()) { SSL_CTX_set_default_verify_paths(ssl_ctx); } else { if (SSL_CTX_load_verify_locations(ssl_ctx, cert_file.c_str(), nullptr) == 0) { return create_openssl_error(-8, "Failed to set custom cert file"); } } if (VERIFY_PEER && verify_peer == VerifyPeer::On) { SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback); if (VERIFY_DEPTH != -1) { SSL_CTX_set_verify_depth(ssl_ctx, VERIFY_DEPTH); } } else { SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, nullptr); } // TODO(now): cipher list string cipher_list; if (SSL_CTX_set_cipher_list(ssl_ctx, cipher_list.empty() ? "DEFAULT" : cipher_list.c_str()) == 0) { return create_openssl_error(-9, PSLICE("Failed to set cipher list \"%s\"", cipher_list.c_str())); } auto ssl_handle = SSL_new(ssl_ctx); if (ssl_handle == nullptr) { return create_openssl_error(-13, "Failed to create an SSL handle"); } auto ssl_handle_guard = ScopeExit() + [&]() { do_ssl_shutdown(ssl_handle); SSL_free(ssl_handle); }; #if OPENSSL_VERSION_NUMBER >= 0x10002000L X509_VERIFY_PARAM *param = SSL_get0_param(ssl_handle); /* Enable automatic hostname checks */ // TODO: X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS X509_VERIFY_PARAM_set_hostflags(param, 0); X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0); #else #warning DANGEROUS! HTTPS HOST WILL NOT BE CHECKED. INSTALL OPENSSL >= 1.0.2 OR IMPLEMENT HTTPS HOST CHECK MANUALLY #endif if (!SSL_set_fd(ssl_handle, fd.get_fd().get_native_fd())) { return create_openssl_error(-14, "Failed to set fd"); } #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) auto host_str = host.str(); SSL_set_tlsext_host_name(ssl_handle, MutableCSlice(host_str).begin()); #endif SSL_set_connect_state(ssl_handle); ssl_ctx_guard.dismiss(); ssl_handle_guard.dismiss(); return SslFd(std::move(fd), ssl_handle, ssl_ctx); #endif }