// // Copyright 2020 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "test/core/util/tls_utils.h" #include #include #include "src/core/lib/gpr/tmpfile.h" #include "src/core/lib/iomgr/load_file.h" #include "src/core/lib/slice/slice_internal.h" namespace grpc_core { namespace testing { TmpFile::TmpFile(absl::string_view data) { name_ = CreateTmpFileAndWriteData(data); GPR_ASSERT(!name_.empty()); } TmpFile::~TmpFile() { GPR_ASSERT(remove(name_.c_str()) == 0); } void TmpFile::RewriteFile(absl::string_view data) { // Create a new file containing new data. std::string new_name = CreateTmpFileAndWriteData(data); GPR_ASSERT(!new_name.empty()); // Remove the old file. GPR_ASSERT(remove(name_.c_str()) == 0); // Rename the new file to the original name. GPR_ASSERT(rename(new_name.c_str(), name_.c_str()) == 0); } std::string TmpFile::CreateTmpFileAndWriteData(absl::string_view data) { char* name = nullptr; FILE* file_descriptor = gpr_tmpfile("test", &name); GPR_ASSERT(fwrite(data.data(), 1, data.size(), file_descriptor) == data.size()); GPR_ASSERT(fclose(file_descriptor) == 0); GPR_ASSERT(file_descriptor != nullptr); GPR_ASSERT(name != nullptr); std::string name_to_return = name; gpr_free(name); return name_to_return; } PemKeyCertPairList MakeCertKeyPairs(absl::string_view private_key, absl::string_view certs) { if (private_key.empty() && certs.empty()) { return {}; } return PemKeyCertPairList{PemKeyCertPair(private_key, certs)}; } std::string GetFileContents(const char* path) { grpc_slice slice = grpc_empty_slice(); GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", grpc_load_file(path, 0, &slice))); std::string data = std::string(StringViewFromSlice(slice)); grpc_slice_unref(slice); return data; } int SyncExternalVerifier::Verify(void* user_data, grpc_tls_custom_verification_check_request*, grpc_tls_on_custom_verification_check_done_cb, void*, grpc_status_code* sync_status, char** sync_error_details) { auto* self = static_cast(user_data); if (self->success_) { *sync_status = GRPC_STATUS_OK; return true; // Synchronous call } *sync_status = GRPC_STATUS_UNAUTHENTICATED; *sync_error_details = gpr_strdup("SyncExternalVerifier failed"); return true; // Synchronous call } void SyncExternalVerifier::Destruct(void* user_data) { auto* self = static_cast(user_data); delete self; } AsyncExternalVerifier::~AsyncExternalVerifier() { // Tell the thread to shut down. { MutexLock lock(&mu_); queue_.push_back(Request{nullptr, nullptr, nullptr, true}); } // Wait for thread to exit. thread_.Join(); grpc_shutdown(); } int AsyncExternalVerifier::Verify( void* user_data, grpc_tls_custom_verification_check_request* request, grpc_tls_on_custom_verification_check_done_cb callback, void* callback_arg, grpc_status_code*, char**) { auto* self = static_cast(user_data); // Add request to queue to be picked up by worker thread. MutexLock lock(&self->mu_); self->queue_.push_back(Request{request, callback, callback_arg, false}); return false; // Asynchronous call } namespace { void DestroyExternalVerifier(void* arg) { auto* verifier = static_cast(arg); delete verifier; } } // namespace void AsyncExternalVerifier::Destruct(void* user_data) { auto* self = static_cast(user_data); // Spawn a detached thread to destroy the verifier, to make sure that we don't // try to join the worker thread from within the worker thread. Thread destroy_thread("DestroyExternalVerifier", DestroyExternalVerifier, self, nullptr, Thread::Options().set_joinable(false)); destroy_thread.Start(); } void AsyncExternalVerifier::WorkerThread(void* arg) { auto* self = static_cast(arg); while (true) { // Check queue for work. bool got_request = false; Request request; { MutexLock lock(&self->mu_); if (!self->queue_.empty()) { got_request = true; request = self->queue_.front(); self->queue_.pop_front(); } } // If nothing found in the queue, sleep for a bit and try again. if (!got_request) { gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(100)); continue; } // If we're being told to shut down, return. if (request.shutdown) { return; } // Process the request. if (self->success_) { request.callback(request.request, request.callback_arg, GRPC_STATUS_OK, ""); } else { request.callback(request.request, request.callback_arg, GRPC_STATUS_UNAUTHENTICATED, "AsyncExternalVerifier failed"); } } } } // namespace testing } // namespace grpc_core