/* * This file is part of PowerDNS or dnsdist. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "base64.hh" #include "dnsdist-doh-common.hh" #include "dnsdist-rules.hh" #ifdef HAVE_DNS_OVER_HTTPS HTTPHeaderRule::HTTPHeaderRule(const std::string& header, const std::string& regex) : d_header(toLower(header)), d_regex(regex), d_visual("http[" + header + "] ~ " + regex) { } bool HTTPHeaderRule::matches(const DNSQuestion* dq) const { if (!dq->ids.du) { return false; } const auto& headers = dq->ids.du->getHTTPHeaders(); for (const auto& header : headers) { if (header.first == d_header) { return d_regex.match(header.second); } } return false; } string HTTPHeaderRule::toString() const { return d_visual; } HTTPPathRule::HTTPPathRule(std::string path) : d_path(std::move(path)) { } bool HTTPPathRule::matches(const DNSQuestion* dq) const { if (!dq->ids.du) { return false; } const auto path = dq->ids.du->getHTTPPath(); return d_path == path; } string HTTPPathRule::toString() const { return "url path == " + d_path; } HTTPPathRegexRule::HTTPPathRegexRule(const std::string& regex) : d_regex(regex), d_visual("http path ~ " + regex) { } bool HTTPPathRegexRule::matches(const DNSQuestion* dq) const { if (!dq->ids.du) { return false; } return d_regex.match(dq->ids.du->getHTTPPath()); } string HTTPPathRegexRule::toString() const { return d_visual; } void DOHFrontend::rotateTicketsKey(time_t now) { return d_tlsContext.rotateTicketsKey(now); } void DOHFrontend::loadTicketsKeys(const std::string& keyFile) { return d_tlsContext.loadTicketsKeys(keyFile); } void DOHFrontend::handleTicketsKeyRotation() { } std::string DOHFrontend::getNextTicketsKeyRotation() const { return d_tlsContext.getNextTicketsKeyRotation(); } size_t DOHFrontend::getTicketsKeysCount() { return d_tlsContext.getTicketsKeysCount(); } void DOHFrontend::reloadCertificates() { if (isHTTPS()) { d_tlsContext.setupTLS(); } } void DOHFrontend::setup() { if (isHTTPS()) { if (!d_tlsContext.setupTLS()) { throw std::runtime_error("Error setting up TLS context for DoH listener on '" + d_tlsContext.d_addr.toStringWithPort()); } } } #endif /* HAVE_DNS_OVER_HTTPS */ namespace dnsdist::doh { std::optional getPayloadFromPath(const std::string_view& path) { std::optional result{std::nullopt}; if (path.size() <= 5) { return result; } auto pos = path.find("?dns="); if (pos == string::npos) { pos = path.find("&dns="); } if (pos == string::npos) { return result; } // need to base64url decode this string sdns; const size_t payloadSize = path.size() - pos - 5; size_t neededPadding = 0; switch (payloadSize % 4) { case 2: neededPadding = 2; break; case 3: neededPadding = 1; break; } sdns.reserve(payloadSize + neededPadding); sdns = path.substr(pos + 5); for (auto& entry : sdns) { switch (entry) { case '-': entry = '+'; break; case '_': entry = '/'; break; } } if (neededPadding != 0) { // re-add padding that may have been missing sdns.append(neededPadding, '='); } PacketBuffer decoded; /* rough estimate so we hopefully don't need a new allocation later */ /* We reserve at few additional bytes to be able to add EDNS later */ const size_t estimate = ((sdns.size() * 3) / 4); decoded.reserve(estimate); if (B64Decode(sdns, decoded) < 0) { return result; } result = std::move(decoded); return result; } }