// Copyright Epic Games, Inc. All Rights Reserved. #include "frontend.h" #include #include #include ZEN_THIRD_PARTY_INCLUDES_START #if ZEN_PLATFORM_WINDOWS # include #endif ZEN_THIRD_PARTY_INCLUDES_END namespace zen { ////////////////////////////////////////////////////////////////////////// static IoBuffer FindZipFsInBinary(const IoBuffer& BinBuffer) { if (BinBuffer.GetSize() < 4) { return {}; } uintptr_t Cursor = uintptr_t(BinBuffer.GetData()); size_t BinSize = 0; uint32_t Magic = *(uint32_t*)(Cursor); #if ZEN_PLATFORM_LINUX if (Magic == 0x464c457f) { struct Elf64Header { char Ident[16]; uint16_t Type; uint16_t Machine; uint32_t Version; uint64_t Entry; uint64_t ProgHeaderOffset; uint64_t SectionHeaderOffset; uint32_t Flags; uint16_t EhSize; uint16_t ProgHeaderEntrySize; uint16_t ProgHeaderCount; uint16_t SectionHeaderEntrySize; uint16_t SectionHeaderCount; uint16_t SectionStringIndex; }; struct SectionHeader { uint32_t NameIndex; uint32_t Type; uint64_t Flags; uint64_t Address; uint64_t Offset; uint64_t Size; uint64_t _Other[3]; }; const auto* Elf = (Elf64Header*)Cursor; if (Elf->Ident[4] != 0x02) // Elf64 { return {}; } const auto* Section = (SectionHeader*)(Cursor + Elf->SectionHeaderOffset); /* size_t BinSize = 0; for (int i = 0, n = Elf->SectionHeaderCount; i < n; ++i, ++Section) { uint32_t SectionEnd = Section->Offset + Section->Size; BinSize = (SectionEnd > BinSize) ? SectionEnd : BinSize; } */ // What if the section headers aren't the last thing in the fiile though? BinSize = Elf->SectionHeaderEntrySize; BinSize *= Elf->SectionHeaderCount; BinSize += Elf->SectionHeaderOffset; } #elif ZEN_PLATFORM_WINDOWS if ((Magic & 0xffff) == 0x5a4d) { const auto* Dos = (IMAGE_DOS_HEADER*)Cursor; const auto* Nt = (IMAGE_NT_HEADERS64*)(Cursor + Dos->e_lfanew); const auto* Section = (IMAGE_SECTION_HEADER*)(uintptr_t(&Nt->OptionalHeader) + Nt->FileHeader.SizeOfOptionalHeader); for (int i = 0, n = Nt->FileHeader.NumberOfSections; i < n; ++i, ++Section) { uint32_t SectionEnd = Section->PointerToRawData + Section->SizeOfRawData; BinSize = (SectionEnd > BinSize) ? SectionEnd : BinSize; } } #elif ZEN_PLATFORM_MAC if (Magic == 0xbebafeca) { struct MachInt32 { operator uint32_t() const { return ByteSwap(Value); } uint32_t Value; }; struct MachFatArch { MachInt32 CpuType; MachInt32 SubType; MachInt32 Offset; MachInt32 Size; MachInt32 Alignment; }; struct MachFatHeader { uint32_t Magic; MachInt32 NumArchs; MachFatArch Archs[]; }; const auto* Header = (MachFatHeader*)Cursor; for (int i = 0, n = Header->NumArchs; i < n; ++i) { const MachFatArch* Arch = Header->Archs + i; uint32_t ArchEnd = Arch->Offset + Arch->Size; BinSize = (ArchEnd > BinSize) ? ArchEnd : BinSize; } } #endif // win/linux/mac if (!BinSize || BinSize > BinBuffer.GetSize()) { return {}; } size_t ZipFsSize = BinBuffer.Size() - BinSize; if (!ZipFsSize) { return {}; } return IoBuffer(BinBuffer, BinSize); } //////////////////////////////////////////////////////////////////////////////// HttpFrontendService::HttpFrontendService(std::filesystem::path Directory) : m_Directory(Directory) { std::filesystem::path SelfPath = GetRunningExecutablePath(); // Locate a .zip file appended onto the end of this binary IoBuffer SelfBuffer = IoBufferBuilder::MakeFromFile(SelfPath); IoBuffer SelfTailBuffer = FindZipFsInBinary(SelfBuffer); if (SelfTailBuffer) { m_ZipFs = ZipFs(std::move(SelfTailBuffer)); } #if ZEN_BUILD_DEBUG if (!Directory.empty()) { return; } std::error_code ErrorCode; auto Path = SelfPath; while (Path.has_parent_path()) { auto ParentPath = Path.parent_path(); if (ParentPath == Path) { break; } if (std::filesystem::is_regular_file(ParentPath / "xmake.lua", ErrorCode)) { if (ErrorCode) { break; } auto HtmlDir = ParentPath / "zenserver" / "frontend" / "html"; if (std::filesystem::is_directory(HtmlDir, ErrorCode)) { m_Directory = HtmlDir; } break; } Path = ParentPath; }; #endif } //////////////////////////////////////////////////////////////////////////////// HttpFrontendService::~HttpFrontendService() { } //////////////////////////////////////////////////////////////////////////////// const char* HttpFrontendService::BaseUri() const { return "/dashboard"; // in order to use the root path we need to remove HttpAddUrlToUrlGroup in HttpSys.cpp } //////////////////////////////////////////////////////////////////////////////// void HttpFrontendService::HandleRequest(zen::HttpServerRequest& Request) { using namespace std::literals; std::string_view Uri = Request.RelativeUri(); for (; Uri[0] == '/'; Uri = Uri.substr(1)) ; if (Uri.empty()) { Uri = "index.html"sv; } // Dismiss if the URI contains .. anywhere to prevent arbitrary file reads if (Uri.find("..") != Uri.npos) { return Request.WriteResponse(HttpResponseCode::Forbidden); } // Map the file extension to a MIME type. To keep things constrained, only a // small subset of file extensions is allowed HttpContentType ContentType = HttpContentType::kUnknownContentType; if (const size_t DotIndex = Uri.rfind("."); DotIndex != Uri.npos) { const std::string_view DotExt = Uri.substr(DotIndex + 1); ContentType = ParseContentType(DotExt); } if (ContentType == HttpContentType::kUnknownContentType) { return Request.WriteResponse(HttpResponseCode::Forbidden); } // The given content directory overrides any zip-fs discovered in the binary if (!m_Directory.empty()) { FileContents File = ReadFile(m_Directory / Uri); if (!File.ErrorCode) { return Request.WriteResponse(HttpResponseCode::OK, ContentType, File.Data[0]); } } if (IoBuffer FileBuffer = m_ZipFs.GetFile(Uri)) { return Request.WriteResponse(HttpResponseCode::OK, ContentType, FileBuffer); } Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Not found"sv); } } // namespace zen