From 0f5f1f9cdcdb03901f6589cf98a78d387b2f7b54 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Tue, 1 Mar 2022 13:11:33 +0100 Subject: Moved experimental/frontend.* to frontend/frontend.* --- zenserver/frontend/frontend.cpp | 119 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 zenserver/frontend/frontend.cpp (limited to 'zenserver/frontend/frontend.cpp') diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp new file mode 100644 index 000000000..4bd3ec90a --- /dev/null +++ b/zenserver/frontend/frontend.cpp @@ -0,0 +1,119 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "frontend.h" + +#include +#include + +namespace zen { + +namespace html { + + constexpr std::string_view Index = R"( + + + + + + + + + + +
+
+
+
+__________                  _________  __                           
+\____    /  ____    ____   /   _____/_/  |_   ____  _______   ____  
+  /     / _/ __ \  /    \  \_____  \ \   __\ /  _ \ \_  __ \_/ __ \
+ /     /_ \  ___/ |   |  \ /        \ |  |  (  <_> ) |  | \/\  ___/ 
+/_______ \ \___  >|___|  //_______  / |__|   \____/  |__|    \___  >
+        \/     \/      \/         \/                             \/ 
+				
+
+			
+
+
+
Z$:
+

+		
+
+ + +)"; + +} // namespace html + +HttpFrontendService::HttpFrontendService(std::filesystem::path Directory) : m_Directory(Directory) +{ +} + +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; + + if (m_Directory.empty()) + { + Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, html::Index); + } + else + { + std::string_view Uri = Request.RelativeUri(); + std::filesystem::path RelPath{Uri.empty() ? "index.html" : Uri}; + std::filesystem::path AbsPath = m_Directory / RelPath; + + FileContents File = ReadFile(AbsPath); + + if (!File.ErrorCode) + { + // TODO: Map file extension to MIME type + Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, File.Data[0]); + } + else + { + return Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Ooops!"sv); + } + } +} + +} // namespace zen -- cgit v1.2.3 From 6a955b38cfcc3a566b332f1d1fcc630f7a2f641f Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Wed, 2 Mar 2022 09:19:25 +0100 Subject: Function to find .zip appended to a IoBuffer-wrapped executable file --- zenserver/frontend/frontend.cpp | 97 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) (limited to 'zenserver/frontend/frontend.cpp') diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp index 4bd3ec90a..0611f068c 100644 --- a/zenserver/frontend/frontend.cpp +++ b/zenserver/frontend/frontend.cpp @@ -5,8 +5,105 @@ #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*)(BinBuffer.GetData()); +#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 +# error "needs to be implemented!" +#endif // win/linux/mac + + if (!BinSize || BinSize > BinBuffer.GetSize()) + { + return {}; + } + + return IoBuffer(BinBuffer, BinSize); +} + namespace html { constexpr std::string_view Index = R"( -- cgit v1.2.3 From 27b351c2bac99d06f020f60fd859ecc93d4fd140 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Wed, 2 Mar 2022 10:18:20 +0100 Subject: Implemented zip file locating for fat Mach-o binaries --- zenserver/frontend/frontend.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'zenserver/frontend/frontend.cpp') diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp index 0611f068c..17614f163 100644 --- a/zenserver/frontend/frontend.cpp +++ b/zenserver/frontend/frontend.cpp @@ -2,6 +2,7 @@ #include "frontend.h" +#include #include #include @@ -93,7 +94,38 @@ static IoBuffer FindZipFsInBinary(const IoBuffer& BinBuffer) } } #elif ZEN_PLATFORM_MAC -# error "needs to be implemented!" + 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()) -- cgit v1.2.3 From 9dd7b73739486d4de3206c79f497231b286f53e6 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Wed, 2 Mar 2022 10:30:00 +0100 Subject: Serve the /dashboard endpoint from an embedded .zip file --- zenserver/frontend/frontend.cpp | 162 ++++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 80 deletions(-) (limited to 'zenserver/frontend/frontend.cpp') diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp index 17614f163..b87d7e313 100644 --- a/zenserver/frontend/frontend.cpp +++ b/zenserver/frontend/frontend.cpp @@ -136,113 +136,115 @@ static IoBuffer FindZipFsInBinary(const IoBuffer& BinBuffer) return IoBuffer(BinBuffer, BinSize); } -namespace html { - - constexpr std::string_view Index = R"( - - - - - - - - - - -
-
-
-
-__________                  _________  __                           
-\____    /  ____    ____   /   _____/_/  |_   ____  _______   ____  
-  /     / _/ __ \  /    \  \_____  \ \   __\ /  _ \ \_  __ \_/ __ \
- /     /_ \  ___/ |   |  \ /        \ |  |  (  <_> ) |  | \/\  ___/ 
-/_______ \ \___  >|___|  //_______  / |__|   \____/  |__|    \___  >
-        \/     \/      \/         \/                             \/ 
-				
-
-			
-
-
-
Z$:
-

-		
-
- - -)"; - -} // namespace html - -HttpFrontendService::HttpFrontendService(std::filesystem::path Directory) : m_Directory(Directory) +//////////////////////////////////////////////////////////////////////////////// +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; + for (auto Path = SelfPath.parent_path(); !Path.empty(); Path = Path.parent_path()) + { + if (!std::filesystem::is_regular_file(Path / "xmake.lua", ErrorCode)) + { + continue; + } + + auto HtmlDir = (Path / __FILE__).parent_path() / "html"; + if (std::filesystem::is_directory(HtmlDir, ErrorCode)) + { + m_Directory = HtmlDir; + } + break; + } +#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; - if (m_Directory.empty()) + 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) { - Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, html::Index); + Request.WriteResponse(HttpResponseCode::Forbidden); + return; } - else + + // Map the file extension to a MIME type. To keep things constrained, only a + // small subset of file extensions is allowed. + HttpContentType ContentType = HttpContentType::kCOUNT; + size_t DotIndex = Uri.rfind("."); + if (DotIndex != Uri.npos) { - std::string_view Uri = Request.RelativeUri(); - std::filesystem::path RelPath{Uri.empty() ? "index.html" : Uri}; - std::filesystem::path AbsPath = m_Directory / RelPath; + const std::string_view DotExt = Uri.substr(DotIndex); + if (DotExt == ".html") ContentType = HttpContentType::kHTML; + else if (DotExt == ".js") ContentType = HttpContentType::kJSON; + else if (DotExt == ".css") ContentType = HttpContentType::kCSS; + else if (DotExt == ".png") ContentType = HttpContentType::kPNG; + else if (DotExt == ".ico") ContentType = HttpContentType::kIcon; + } - FileContents File = ReadFile(AbsPath); + if (ContentType == HttpContentType::kCOUNT) + { + Request.WriteResponse(HttpResponseCode::Forbidden); + return; + } + // 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) { - // TODO: Map file extension to MIME type - Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, File.Data[0]); - } - else - { - return Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Ooops!"sv); + Request.WriteResponse(HttpResponseCode::OK, ContentType, File.Data[0]); + return; } } + + IoBuffer FileBuffer = m_ZipFs.GetFile(Uri); + if (FileBuffer) + { + Request.WriteResponse(HttpResponseCode::OK, ContentType, FileBuffer); + return; + } + + Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Not found"sv); } } // namespace zen -- cgit v1.2.3