#pragma once #include #include #include #include #include namespace playback { inline bool validNetworkURL(const char *text) { if (!text) return false; const std::string_view url(text); if (url.size() > 65536) return false; const std::size_t start = url.starts_with("https://") ? 8 : url.starts_with("http://") ? 7 : 0; if (!start) return false; for (unsigned char value : url) if (value <= 32 || value == 127 || value == '\\') return false; const auto authority = url.substr(start, url.find_first_of("/?#", start) - start); if (authority.empty() || authority.find('@') != std::string_view::npos) return false; auto host = authority; std::string_view port; if (host.front() == '[') { const auto end = host.find(']'); if (end == std::string_view::npos || end == 1) return false; if (end + 1 < host.size()) { if (host[end + 1] != ':') return false; port = host.substr(end + 2); if (port.empty()) return false; } host = host.substr(1, end - 1); std::array address{}; if (host.size() >= address.size()) return false; std::memcpy(address.data(), host.data(), host.size()); in6_addr parsed{}; if (inet_pton(AF_INET6, address.data(), &parsed) != 1) return false; } else { const auto colon = host.find(':'); if (colon != std::string_view::npos) { port = host.substr(colon + 1); host = host.substr(0, colon); if (port.empty()) return false; } if (host.empty()) return false; for (unsigned char value : host) if (!((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || (value >= '0' && value <= '9') || value == '.' || value == '-')) return false; } unsigned number = 0; for (unsigned char value : port) { if (value < '0' || value > '9') return false; number = number * 10 + value - '0'; if (number > 65535) return false; } return port.empty() || number > 0; } inline bool validHeaders(const char *text) { if (!text) return true; const std::string_view headers(text); if (headers.size() > 65536) return false; std::size_t offset = 0; while (offset < headers.size()) { const auto end = headers.find("\r\n", offset); if (end == std::string_view::npos) return false; const auto line = headers.substr(offset, end - offset); const auto colon = line.find(':'); if (colon == std::string_view::npos || colon == 0) return false; for (unsigned char value : line.substr(0, colon)) if (!((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || (value >= '0' && value <= '9') || std::string_view("!#$%&'*+-.^_`|~").find(value) != std::string_view::npos)) return false; for (unsigned char value : line.substr(colon + 1)) if (value < 32 || value == 127) return false; offset = end + 2; } return true; } inline std::string networkOrigin(const char *text) { if (!validNetworkURL(text)) return {}; std::string_view url(text); const auto start = url.starts_with("https://") ? 8u : 7u; std::string authority(url.substr(start, url.find_first_of("/?#", start) - start)); for (char &value : authority) if (value >= 'A' && value <= 'Z') value += 'a' - 'A'; const auto colon = authority.front() == '[' ? authority.find(':', authority.find(']')) : authority.find(':'); if (colon == std::string::npos) authority += start == 8 ? ":443" : ":80"; else authority.replace(colon + 1, std::string::npos, std::to_string(std::stoul(authority.substr(colon + 1)))); return std::string(url.substr(0, start)) + authority; } }