diff options
Diffstat (limited to '')
-rw-r--r-- | dnsdist-lua-ffi.cc | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/dnsdist-lua-ffi.cc b/dnsdist-lua-ffi.cc new file mode 100644 index 0000000..25e36c2 --- /dev/null +++ b/dnsdist-lua-ffi.cc @@ -0,0 +1,583 @@ +/* + * 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 "dnsdist-lua-ffi.hh" +#include "dnsdist-lua.hh" +#include "dnsdist-ecs.hh" + +uint16_t dnsdist_ffi_dnsquestion_get_qtype(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->qtype; +} + +uint16_t dnsdist_ffi_dnsquestion_get_qclass(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->qclass; +} + +static void dnsdist_ffi_comboaddress_to_raw(const ComboAddress& ca, const void** addr, size_t* addrSize) +{ + if (ca.isIPv4()) { + *addr = &ca.sin4.sin_addr.s_addr; + *addrSize = sizeof(ca.sin4.sin_addr.s_addr); + } + else { + *addr = &ca.sin6.sin6_addr.s6_addr; + *addrSize = sizeof(ca.sin6.sin6_addr.s6_addr); + } +} + +void dnsdist_ffi_dnsquestion_get_localaddr(const dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize) +{ + dnsdist_ffi_comboaddress_to_raw(*dq->dq->local, addr, addrSize); +} + +void dnsdist_ffi_dnsquestion_get_remoteaddr(const dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize) +{ + dnsdist_ffi_comboaddress_to_raw(*dq->dq->remote, addr, addrSize); +} + +void dnsdist_ffi_dnsquestion_get_masked_remoteaddr(dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize, uint8_t bits) +{ + dq->maskedRemote = Netmask(*dq->dq->remote, bits).getMaskedNetwork(); + dnsdist_ffi_comboaddress_to_raw(dq->maskedRemote, addr, addrSize); +} + +uint16_t dnsdist_ffi_dnsquestion_get_local_port(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->local->getPort(); +} + +uint16_t dnsdist_ffi_dnsquestion_get_remote_port(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->remote->getPort(); +} + +void dnsdist_ffi_dnsquestion_get_qname_raw(const dnsdist_ffi_dnsquestion_t* dq, const char** qname, size_t* qnameSize) +{ + const auto& storage = dq->dq->qname->getStorage(); + *qname = storage.data(); + *qnameSize = storage.size(); +} + +size_t dnsdist_ffi_dnsquestion_get_qname_hash(const dnsdist_ffi_dnsquestion_t* dq, size_t init) +{ + return dq->dq->qname->hash(init); +} + +int dnsdist_ffi_dnsquestion_get_rcode(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->getHeader()->rcode; +} + +void* dnsdist_ffi_dnsquestion_get_header(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->getHeader(); +} + +uint16_t dnsdist_ffi_dnsquestion_get_len(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->getData().size(); +} + +size_t dnsdist_ffi_dnsquestion_get_size(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->getData().size(); +} + +bool dnsdist_ffi_dnsquestion_set_size(dnsdist_ffi_dnsquestion_t* dq, size_t newSize) +{ + try { + dq->dq->getMutableData().resize(newSize); + return true; + } + catch (const std::exception& e) { + return false; + } +} + +uint8_t dnsdist_ffi_dnsquestion_get_opcode(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->getHeader()->opcode; +} + +bool dnsdist_ffi_dnsquestion_get_tcp(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->overTCP(); +} + +bool dnsdist_ffi_dnsquestion_get_skip_cache(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->skipCache; +} + +bool dnsdist_ffi_dnsquestion_get_use_ecs(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->useECS; +} + +bool dnsdist_ffi_dnsquestion_get_add_xpf(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->addXPF; +} + +bool dnsdist_ffi_dnsquestion_get_ecs_override(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->ecsOverride; +} + +uint16_t dnsdist_ffi_dnsquestion_get_ecs_prefix_length(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->ecsPrefixLength; +} + +bool dnsdist_ffi_dnsquestion_is_temp_failure_ttl_set(const dnsdist_ffi_dnsquestion_t* dq) +{ + return dq->dq->tempFailureTTL != boost::none; +} + +uint32_t dnsdist_ffi_dnsquestion_get_temp_failure_ttl(const dnsdist_ffi_dnsquestion_t* dq) +{ + if (dq->dq->tempFailureTTL) { + return *dq->dq->tempFailureTTL; + } + return 0; +} + +bool dnsdist_ffi_dnsquestion_get_do(const dnsdist_ffi_dnsquestion_t* dq) +{ + return getEDNSZ(*dq->dq) & EDNS_HEADER_FLAG_DO; +} + +void dnsdist_ffi_dnsquestion_get_sni(const dnsdist_ffi_dnsquestion_t* dq, const char** sni, size_t* sniSize) +{ + *sniSize = dq->dq->sni.size(); + *sni = dq->dq->sni.c_str(); +} + +const char* dnsdist_ffi_dnsquestion_get_tag(const dnsdist_ffi_dnsquestion_t* dq, const char* label) +{ + const char * result = nullptr; + + if (dq->dq->qTag != nullptr) { + const auto it = dq->dq->qTag->find(label); + if (it != dq->dq->qTag->cend()) { + result = it->second.c_str(); + } + } + + return result; +} + +const char* dnsdist_ffi_dnsquestion_get_http_path(dnsdist_ffi_dnsquestion_t* dq) +{ + if (!dq->httpPath) { + if (dq->dq->du == nullptr) { + return nullptr; + } +#ifdef HAVE_DNS_OVER_HTTPS + dq->httpPath = dq->dq->du->getHTTPPath(); +#endif /* HAVE_DNS_OVER_HTTPS */ + } + if (dq->httpPath) { + return dq->httpPath->c_str(); + } + return nullptr; +} + +const char* dnsdist_ffi_dnsquestion_get_http_query_string(dnsdist_ffi_dnsquestion_t* dq) +{ + if (!dq->httpQueryString) { + if (dq->dq->du == nullptr) { + return nullptr; + } +#ifdef HAVE_DNS_OVER_HTTPS + dq->httpQueryString = dq->dq->du->getHTTPQueryString(); +#endif /* HAVE_DNS_OVER_HTTPS */ + } + if (dq->httpQueryString) { + return dq->httpQueryString->c_str(); + } + return nullptr; +} + +const char* dnsdist_ffi_dnsquestion_get_http_host(dnsdist_ffi_dnsquestion_t* dq) +{ + if (!dq->httpHost) { + if (dq->dq->du == nullptr) { + return nullptr; + } +#ifdef HAVE_DNS_OVER_HTTPS + dq->httpHost = dq->dq->du->getHTTPHost(); +#endif /* HAVE_DNS_OVER_HTTPS */ + } + if (dq->httpHost) { + return dq->httpHost->c_str(); + } + return nullptr; +} + +const char* dnsdist_ffi_dnsquestion_get_http_scheme(dnsdist_ffi_dnsquestion_t* dq) +{ + if (!dq->httpScheme) { + if (dq->dq->du == nullptr) { + return nullptr; + } +#ifdef HAVE_DNS_OVER_HTTPS + dq->httpScheme = dq->dq->du->getHTTPScheme(); +#endif /* HAVE_DNS_OVER_HTTPS */ + } + if (dq->httpScheme) { + return dq->httpScheme->c_str(); + } + return nullptr; +} + +static void fill_edns_option(const EDNSOptionViewValue& value, dnsdist_ffi_ednsoption_t& option) +{ + option.len = value.size; + option.data = nullptr; + + if (value.size > 0) { + option.data = value.content; + } +} + +// returns the length of the resulting 'out' array. 'out' is not set if the length is 0 +size_t dnsdist_ffi_dnsquestion_get_edns_options(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_ednsoption_t** out) +{ + if (dq->dq->ednsOptions == nullptr) { + parseEDNSOptions(*(dq->dq)); + + if (dq->dq->ednsOptions == nullptr) { + return 0; + } + } + + size_t totalCount = 0; + for (const auto& option : *dq->dq->ednsOptions) { + totalCount += option.second.values.size(); + } + + dq->ednsOptionsVect.clear(); + dq->ednsOptionsVect.resize(totalCount); + size_t pos = 0; + for (const auto& option : *dq->dq->ednsOptions) { + for (const auto& entry : option.second.values) { + fill_edns_option(entry, dq->ednsOptionsVect.at(pos)); + dq->ednsOptionsVect.at(pos).optionCode = option.first; + pos++; + } + } + + if (totalCount > 0) { + *out = dq->ednsOptionsVect.data(); + } + + return totalCount; +} + +size_t dnsdist_ffi_dnsquestion_get_http_headers(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_http_header_t** out) +{ + if (dq->dq->du == nullptr) { + return 0; + } + +#ifdef HAVE_DNS_OVER_HTTPS + dq->httpHeaders = dq->dq->du->getHTTPHeaders(); + dq->httpHeadersVect.clear(); + dq->httpHeadersVect.resize(dq->httpHeaders.size()); + size_t pos = 0; + for (const auto& header : dq->httpHeaders) { + dq->httpHeadersVect.at(pos).name = header.first.c_str(); + dq->httpHeadersVect.at(pos).value = header.second.c_str(); + ++pos; + } + + if (!dq->httpHeadersVect.empty()) { + *out = dq->httpHeadersVect.data(); + } + + return dq->httpHeadersVect.size(); +#else + return 0; +#endif +} + +size_t dnsdist_ffi_dnsquestion_get_tag_array(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_tag_t** out) +{ + if (dq->dq->qTag == nullptr || dq->dq->qTag->size() == 0) { + return 0; + } + + dq->tagsVect.clear(); + dq->tagsVect.resize(dq->dq->qTag->size()); + size_t pos = 0; + + for (const auto& tag : *dq->dq->qTag) { + auto& entry = dq->tagsVect.at(pos); + entry.name = tag.first.c_str(); + entry.value = tag.second.c_str(); + ++pos; + } + + + if (!dq->tagsVect.empty()) { + *out = dq->tagsVect.data(); + } + + return dq->tagsVect.size(); +} + +void dnsdist_ffi_dnsquestion_set_result(dnsdist_ffi_dnsquestion_t* dq, const char* str, size_t strSize) +{ + dq->result = std::string(str, strSize); +} + +void dnsdist_ffi_dnsquestion_set_http_response(dnsdist_ffi_dnsquestion_t* dq, uint16_t statusCode, const char* body, size_t bodyLen, const char* contentType) +{ + if (dq->dq->du == nullptr) { + return; + } + +#ifdef HAVE_DNS_OVER_HTTPS + PacketBuffer bodyVect(body, body + bodyLen); + dq->dq->du->setHTTPResponse(statusCode, std::move(bodyVect), contentType); + dq->dq->getHeader()->qr = true; +#endif +} + +void dnsdist_ffi_dnsquestion_set_rcode(dnsdist_ffi_dnsquestion_t* dq, int rcode) +{ + dq->dq->getHeader()->rcode = rcode; + dq->dq->getHeader()->qr = true; +} + +void dnsdist_ffi_dnsquestion_set_len(dnsdist_ffi_dnsquestion_t* dq, uint16_t len) +{ + dq->dq->getMutableData().resize(len); +} + +void dnsdist_ffi_dnsquestion_set_skip_cache(dnsdist_ffi_dnsquestion_t* dq, bool skipCache) +{ + dq->dq->skipCache = skipCache; +} + +void dnsdist_ffi_dnsquestion_set_use_ecs(dnsdist_ffi_dnsquestion_t* dq, bool useECS) +{ + dq->dq->useECS = useECS; +} + +void dnsdist_ffi_dnsquestion_set_ecs_override(dnsdist_ffi_dnsquestion_t* dq, bool ecsOverride) +{ + dq->dq->ecsOverride = ecsOverride; +} + +void dnsdist_ffi_dnsquestion_set_ecs_prefix_length(dnsdist_ffi_dnsquestion_t* dq, uint16_t ecsPrefixLength) +{ + dq->dq->ecsPrefixLength = ecsPrefixLength; +} + +void dnsdist_ffi_dnsquestion_set_temp_failure_ttl(dnsdist_ffi_dnsquestion_t* dq, uint32_t tempFailureTTL) +{ + dq->dq->tempFailureTTL = tempFailureTTL; +} + +void dnsdist_ffi_dnsquestion_unset_temp_failure_ttl(dnsdist_ffi_dnsquestion_t* dq) +{ + dq->dq->tempFailureTTL = boost::none; +} + +void dnsdist_ffi_dnsquestion_set_tag(dnsdist_ffi_dnsquestion_t* dq, const char* label, const char* value) +{ + dq->dq->setTag(label, value); +} + +size_t dnsdist_ffi_dnsquestion_get_trailing_data(dnsdist_ffi_dnsquestion_t* dq, const char** out) +{ + dq->trailingData = dq->dq->getTrailingData(); + if (!dq->trailingData.empty()) { + *out = dq->trailingData.data(); + } + + return dq->trailingData.size(); +} + +bool dnsdist_ffi_dnsquestion_set_trailing_data(dnsdist_ffi_dnsquestion_t* dq, const char* data, size_t dataLen) +{ + return dq->dq->setTrailingData(std::string(data, dataLen)); +} + +void dnsdist_ffi_dnsquestion_send_trap(dnsdist_ffi_dnsquestion_t* dq, const char* reason, size_t reasonLen) +{ + if (g_snmpAgent && g_snmpTrapsEnabled) { + g_snmpAgent->sendDNSTrap(*dq->dq, std::string(reason, reasonLen)); + } +} + +void dnsdist_ffi_dnsquestion_spoof_raw(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_raw_value_t* values, size_t valuesCount) +{ + std::vector<std::string> data; + data.reserve(valuesCount); + + for (size_t idx = 0; idx < valuesCount; idx++) { + data.emplace_back(values[idx].value, values[idx].size); + } + + std::string result; + SpoofAction sa(data); + sa(dq->dq, &result); +} + +void dnsdist_ffi_dnsquestion_spoof_addrs(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_raw_value_t* values, size_t valuesCount) +{ + std::vector<ComboAddress> data; + data.reserve(valuesCount); + + for (size_t idx = 0; idx < valuesCount; idx++) { + if (values[idx].size == 4) { + sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_port = 0; + memcpy(&sin.sin_addr.s_addr, values[idx].value, sizeof(sin.sin_addr.s_addr)); + data.emplace_back(&sin); + } + else if (values[idx].size == 16) { + sockaddr_in6 sin6; + sin6.sin6_family = AF_INET6; + sin6.sin6_port = 0; + sin6.sin6_scope_id = 0; + sin6.sin6_flowinfo = 0; + memcpy(&sin6.sin6_addr.s6_addr, values[idx].value, sizeof(sin6.sin6_addr.s6_addr)); + data.emplace_back(&sin6); + } + } + + std::string result; + SpoofAction sa(data); + sa(dq->dq, &result); +} + +size_t dnsdist_ffi_servers_list_get_count(const dnsdist_ffi_servers_list_t* list) +{ + return list->ffiServers.size(); +} + +void dnsdist_ffi_servers_list_get_server(const dnsdist_ffi_servers_list_t* list, size_t idx, const dnsdist_ffi_server_t** out) +{ + *out = &list->ffiServers.at(idx); +} + +static size_t dnsdist_ffi_servers_get_index_from_server(const ServerPolicy::NumberedServerVector& servers, const std::shared_ptr<DownstreamState>& server) +{ + for (const auto& pair : servers) { + if (pair.second == server) { + return pair.first - 1; + } + } + throw std::runtime_error("Unable to find servers in server list"); +} + +size_t dnsdist_ffi_servers_list_chashed(const dnsdist_ffi_servers_list_t* list, const dnsdist_ffi_dnsquestion_t* dq, size_t hash) +{ + auto server = chashedFromHash(list->servers, hash); + return dnsdist_ffi_servers_get_index_from_server(list->servers, server); +} + +size_t dnsdist_ffi_servers_list_whashed(const dnsdist_ffi_servers_list_t* list, const dnsdist_ffi_dnsquestion_t* dq, size_t hash) +{ + auto server = whashedFromHash(list->servers, hash); + return dnsdist_ffi_servers_get_index_from_server(list->servers, server); +} + +uint64_t dnsdist_ffi_server_get_outstanding(const dnsdist_ffi_server_t* server) +{ + return server->server->outstanding; +} + +int dnsdist_ffi_server_get_weight(const dnsdist_ffi_server_t* server) +{ + return server->server->weight; +} + +int dnsdist_ffi_server_get_order(const dnsdist_ffi_server_t* server) +{ + return server->server->order; +} + +double dnsdist_ffi_server_get_latency(const dnsdist_ffi_server_t* server) +{ + return server->server->latencyUsec; +} + +bool dnsdist_ffi_server_is_up(const dnsdist_ffi_server_t* server) +{ + return server->server->isUp(); +} + +const char* dnsdist_ffi_server_get_name(const dnsdist_ffi_server_t* server) +{ + return server->server->getName().c_str(); +} + +const char* dnsdist_ffi_server_get_name_with_addr(const dnsdist_ffi_server_t* server) +{ + return server->server->getNameWithAddr().c_str(); +} + +const std::string& getLuaFFIWrappers() +{ + static const std::string interface = +#include "dnsdist-lua-ffi-interface.inc" + ; + static const std::string code = R"FFICodeContent( + local ffi = require("ffi") + local C = ffi.C + + ffi.cdef[[ +)FFICodeContent" + interface + R"FFICodeContent( + ]] + +)FFICodeContent"; + return code; +} + +void setupLuaLoadBalancingContext(LuaContext& luaCtx) +{ + setupLuaBindings(luaCtx, true); + setupLuaBindingsDNSQuestion(luaCtx); + setupLuaBindingsKVS(luaCtx, true); + setupLuaVars(luaCtx); + +#ifdef LUAJIT_VERSION + luaCtx.executeCode(getLuaFFIWrappers()); +#endif +} + +void setupLuaFFIPerThreadContext(LuaContext& luaCtx) +{ + setupLuaVars(luaCtx); + +#ifdef LUAJIT_VERSION + luaCtx.executeCode(getLuaFFIWrappers()); +#endif +} |