Files
terebi/Sources/Core/network_policy.hpp
T

89 lines
3.8 KiB
C++

#pragma once
#include <array>
#include <cstring>
#include <string>
#include <string_view>
#include <arpa/inet.h>
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<char, INET6_ADDRSTRLEN> 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;
}
}