#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "TeamServerApi.grpc.pb.h" namespace fs = std::filesystem; namespace { class ScopedPath { public: explicit ScopedPath(fs::path path) : m_path(std::move(path)) { } ~ScopedPath() { std::error_code ec; fs::remove_all(m_path, ec); } const fs::path& path() const { return m_path; } private: fs::path m_path; }; class ScopedServerProcess { public: explicit ScopedServerProcess(fs::path runtimeRoot) : m_runtimeRoot(std::move(runtimeRoot)) { } ~ScopedServerProcess() { stop(); } void start() { assert(m_pid == -1); fs::path teamServerDir = m_runtimeRoot / "TeamServer"; fs::path teamServerBinary = teamServerDir / "TeamServer"; assert(fs::exists(teamServerBinary)); m_pid = ::fork(); assert(m_pid >= 0); if (m_pid == 0) { const int chdirResult = ::chdir(teamServerDir.c_str()); if (chdirResult != 0) _exit(126); ::execl(teamServerBinary.c_str(), teamServerBinary.c_str(), "TeamServerConfig.json", static_cast(nullptr)); _exit(127); } } bool isRunning() const { if (m_pid <= 0) return false; int status = 0; pid_t result = ::waitpid(m_pid, &status, WNOHANG); return result == 0; } void stop() { if (m_pid <= 0) return; if (isRunning()) { ::kill(m_pid, SIGTERM); for (int attempt = 0; attempt < 20; ++attempt) { int status = 0; pid_t result = ::waitpid(m_pid, &status, WNOHANG); if (result == m_pid) { m_pid = -1; return; } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } ::kill(m_pid, SIGKILL); } int status = 0; ::waitpid(m_pid, &status, 0); m_pid = -1; } private: fs::path m_runtimeRoot; pid_t m_pid = -1; }; std::string readFile(const fs::path& filePath) { std::ifstream input(filePath, std::ios::binary); return std::string(std::istreambuf_iterator(input), {}); } int reserveTcpPort() { int fd = ::socket(AF_INET, SOCK_STREAM, 0); assert(fd >= 0); sockaddr_in address {}; address.sin_family = AF_INET; address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); address.sin_port = htons(0); int bindResult = ::bind(fd, reinterpret_cast(&address), sizeof(address)); assert(bindResult == 0); socklen_t addressLength = sizeof(address); int nameResult = ::getsockname(fd, reinterpret_cast(&address), &addressLength); assert(nameResult == 0); int port = ntohs(address.sin_port); ::close(fd); return port; } fs::path makeRuntimeCopy(const fs::path& stagedRuntimeRoot) { fs::path tempRoot = fs::temp_directory_path() / ("c2teamserver-integration-" + std::to_string(::getpid())); fs::remove_all(tempRoot); fs::create_directories(tempRoot); fs::path runtimeCopy = tempRoot / "runtime"; fs::copy(stagedRuntimeRoot, runtimeCopy, fs::copy_options::recursive); return runtimeCopy; } void rewriteRuntimeConfig(const fs::path& runtimeRoot, int grpcPort) { fs::path configFile = runtimeRoot / "TeamServer" / "TeamServerConfig.json"; std::ifstream input(configFile); nlohmann::json config = nlohmann::json::parse(input); config["ServerGRPCAdd"] = "127.0.0.1"; config["ServerGRPCPort"] = std::to_string(grpcPort); std::ofstream output(configFile); output << config.dump(4); } std::unique_ptr makeStub(const fs::path& runtimeRoot, int grpcPort) { grpc::SslCredentialsOptions credentialsOptions; credentialsOptions.pem_root_certs = readFile(runtimeRoot / "TeamServer" / "rootCA.crt"); grpc::ChannelArguments channelArguments; channelArguments.SetSslTargetNameOverride("localhost"); channelArguments.SetMaxReceiveMessageSize(512 * 1024 * 1024); channelArguments.SetMaxSendMessageSize(512 * 1024 * 1024); auto channel = grpc::CreateCustomChannel( "127.0.0.1:" + std::to_string(grpcPort), grpc::SslCredentials(credentialsOptions), channelArguments); return teamserverapi::TeamServerApi::NewStub(channel); } std::string authenticate(teamserverapi::TeamServerApi::Stub& stub) { grpc::ClientContext context; teamserverapi::AuthRequest request; request.set_username("admin"); request.set_password("admin"); teamserverapi::AuthResponse response; grpc::Status status = stub.Authenticate(&context, request, &response); assert(status.ok()); assert(response.status() == teamserverapi::OK); assert(!response.token().empty()); return response.token(); } void waitForServerReady(teamserverapi::TeamServerApi::Stub& stub, ScopedServerProcess& process) { for (int attempt = 0; attempt < 50; ++attempt) { assert(process.isRunning()); grpc::ClientContext context; teamserverapi::AuthRequest request; request.set_username("admin"); request.set_password("admin"); teamserverapi::AuthResponse response; grpc::Status status = stub.Authenticate(&context, request, &response); if (status.ok() && response.status() == teamserverapi::OK && !response.token().empty()) return; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } assert(false && "TeamServer did not become ready in time"); } void testStagedRuntimeSupportsGrpcAuthenticationAndStableRpc() { fs::path stagedRuntimeRoot = fs::path(C2_INTEGRATION_STAGING_DIR) / "Release"; assert(fs::exists(stagedRuntimeRoot / "TeamServer" / "TeamServer")); assert(fs::exists(stagedRuntimeRoot / "Client" / "c2client_protocol" / "TeamServerApi_pb2.py")); ScopedPath runtimeCopy(makeRuntimeCopy(stagedRuntimeRoot)); const int grpcPort = reserveTcpPort(); rewriteRuntimeConfig(runtimeCopy.path(), grpcPort); ScopedServerProcess process(runtimeCopy.path()); process.start(); auto stub = makeStub(runtimeCopy.path(), grpcPort); waitForServerReady(*stub, process); const std::string token = authenticate(*stub); grpc::ClientContext listenersContext; listenersContext.AddMetadata("authorization", "Bearer " + token); listenersContext.AddMetadata("clientid", "integration-test"); teamserverapi::Empty empty; std::unique_ptr> listeners = stub->ListListeners(&listenersContext, empty); teamserverapi::Listener listener; std::vector streamedListeners; while (listeners->Read(&listener)) { streamedListeners.push_back(listener); } grpc::Status listenersStatus = listeners->Finish(); assert(listenersStatus.ok()); assert(streamedListeners.empty()); grpc::ClientContext sessionsContext; sessionsContext.AddMetadata("authorization", "Bearer " + token); sessionsContext.AddMetadata("clientid", "integration-test"); std::unique_ptr> sessions = stub->ListSessions(&sessionsContext, empty); teamserverapi::Session session; std::vector streamedSessions; while (sessions->Read(&session)) { streamedSessions.push_back(session); } grpc::Status sessionsStatus = sessions->Finish(); assert(sessionsStatus.ok()); assert(streamedSessions.empty()); } } // namespace int main() { testStagedRuntimeSupportsGrpcAuthenticationAndStableRpc(); return 0; }