diff --git a/src/base/base.cmake b/src/base/base.cmake index 5949c050..8c48fe75 100644 --- a/src/base/base.cmake +++ b/src/base/base.cmake @@ -32,6 +32,7 @@ set(HEADERS_BASE src/base/kernel/interfaces/IConfigListener.h src/base/kernel/interfaces/IConfigTransform.h src/base/kernel/interfaces/IConsoleListener.h + src/base/kernel/interfaces/IDnsBackend.h src/base/kernel/interfaces/IDnsListener.h src/base/kernel/interfaces/ILineListener.h src/base/kernel/interfaces/ILogBackend.h @@ -43,7 +44,11 @@ set(HEADERS_BASE src/base/kernel/Platform.h src/base/kernel/Process.h src/base/net/dns/Dns.h + src/base/net/dns/DnsConfig.h src/base/net/dns/DnsRecord.h + src/base/net/dns/DnsRecords.h + src/base/net/dns/DnsRequest.h + src/base/net/dns/DnsUvBackend.h src/base/net/http/Http.h src/base/net/http/HttpListener.h src/base/net/stratum/BaseClient.h @@ -99,7 +104,10 @@ set(SOURCES_BASE src/base/kernel/Platform.cpp src/base/kernel/Process.cpp src/base/net/dns/Dns.cpp + src/base/net/dns/DnsConfig.cpp src/base/net/dns/DnsRecord.cpp + src/base/net/dns/DnsRecords.cpp + src/base/net/dns/DnsUvBackend.cpp src/base/net/http/Http.cpp src/base/net/stratum/BaseClient.cpp src/base/net/stratum/Client.cpp diff --git a/src/base/kernel/config/BaseConfig.cpp b/src/base/kernel/config/BaseConfig.cpp index 1f2b09cb..37982999 100644 --- a/src/base/kernel/config/BaseConfig.cpp +++ b/src/base/kernel/config/BaseConfig.cpp @@ -23,6 +23,7 @@ #include "base/io/log/Log.h" #include "base/io/log/Tags.h" #include "base/kernel/interfaces/IJsonReader.h" +#include "base/net/dns/Dns.h" #include "version.h" @@ -105,6 +106,8 @@ bool xmrig::BaseConfig::read(const IJsonReader &reader, const char *fileName) m_http.load(reader.getObject(kHttp)); m_pools.load(reader); + Dns::set(reader.getObject(DnsConfig::kField)); + return m_pools.active() > 0; } diff --git a/src/base/kernel/config/BaseTransform.cpp b/src/base/kernel/config/BaseTransform.cpp index 55e082b6..3cd48d5a 100644 --- a/src/base/kernel/config/BaseTransform.cpp +++ b/src/base/kernel/config/BaseTransform.cpp @@ -33,6 +33,7 @@ #include "base/kernel/config/BaseConfig.h" #include "base/kernel/interfaces/IConfig.h" #include "base/kernel/Process.h" +#include "base/net/dns/DnsConfig.h" #include "base/net/stratum/Pool.h" #include "base/net/stratum/Pools.h" #include "core/config/Config_platform.h" @@ -244,6 +245,7 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch case IConfig::HttpPort: /* --http-port */ case IConfig::DonateLevelKey: /* --donate-level */ case IConfig::DaemonPollKey: /* --daemon-poll-interval */ + case IConfig::DnsTtlKey: /* --dns-ttl */ return transformUint64(doc, key, static_cast(strtol(arg, nullptr, 10))); case IConfig::BackgroundKey: /* --background */ @@ -256,6 +258,7 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch case IConfig::DaemonKey: /* --daemon */ case IConfig::SubmitToOriginKey: /* --submit-to-origin */ case IConfig::VerboseKey: /* --verbose */ + case IConfig::DnsIPv6Key: /* --dns-ipv6 */ return transformBoolean(doc, key, true); case IConfig::ColorKey: /* --no-color */ @@ -316,6 +319,9 @@ void xmrig::BaseTransform::transformBoolean(rapidjson::Document &doc, int key, b case IConfig::NoTitleKey: /* --no-title */ return set(doc, BaseConfig::kTitle, enable); + case IConfig::DnsIPv6Key: /* --dns-ipv6 */ + return set(doc, DnsConfig::kField, DnsConfig::kIPv6, enable); + default: break; } @@ -344,6 +350,9 @@ void xmrig::BaseTransform::transformUint64(rapidjson::Document &doc, int key, ui case IConfig::PrintTimeKey: /* --print-time */ return set(doc, BaseConfig::kPrintTime, arg); + case IConfig::DnsTtlKey: /* --dns-ttl */ + return set(doc, DnsConfig::kField, DnsConfig::kTTL, arg); + # ifdef XMRIG_FEATURE_HTTP case IConfig::DaemonPollKey: /* --daemon-poll-interval */ return add(doc, Pools::kPools, Pool::kDaemonPollInterval, arg); diff --git a/src/base/kernel/interfaces/IConfig.h b/src/base/kernel/interfaces/IConfig.h index 7a7f657e..ec3d8510 100644 --- a/src/base/kernel/interfaces/IConfig.h +++ b/src/base/kernel/interfaces/IConfig.h @@ -82,6 +82,8 @@ public: HugePageSizeKey = 1050, PauseOnActiveKey = 1051, SubmitToOriginKey = 1052, + DnsIPv6Key = 1053, + DnsTtlKey = 1054, // xmrig common CPUPriorityKey = 1021, diff --git a/src/base/kernel/interfaces/IDnsBackend.h b/src/base/kernel/interfaces/IDnsBackend.h new file mode 100644 index 00000000..ca676f32 --- /dev/null +++ b/src/base/kernel/interfaces/IDnsBackend.h @@ -0,0 +1,54 @@ +/* XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + +#ifndef XMRIG_IDNSBACKEND_H +#define XMRIG_IDNSBACKEND_H + + +#include "base/tools/Object.h" + + +#include + + +namespace xmrig { + + +class DnsRecords; +class DnsRequest; +class IDnsListener; +class String; + + +class IDnsBackend +{ +public: + XMRIG_DISABLE_COPY_MOVE(IDnsBackend) + + IDnsBackend() = default; + virtual ~IDnsBackend() = default; + + virtual const DnsRecords &records() const = 0; + virtual std::shared_ptr resolve(const String &host, IDnsListener *listener, uint64_t ttl) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_IDNSBACKEND_H diff --git a/src/base/kernel/interfaces/IDnsListener.h b/src/base/kernel/interfaces/IDnsListener.h index 7d0e14e3..b9d20efa 100644 --- a/src/base/kernel/interfaces/IDnsListener.h +++ b/src/base/kernel/interfaces/IDnsListener.h @@ -1,6 +1,6 @@ /* XMRig - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig , + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ namespace xmrig { -class Dns; +class DnsRecords; class IDnsListener @@ -37,7 +37,7 @@ public: IDnsListener() = default; virtual ~IDnsListener() = default; - virtual void onResolved(const Dns &dns, int status) = 0; + virtual void onResolved(const DnsRecords &records, int status, const char *error) = 0; }; diff --git a/src/base/net/dns/Dns.cpp b/src/base/net/dns/Dns.cpp index ef50b20d..85590bb7 100644 --- a/src/base/net/dns/Dns.cpp +++ b/src/base/net/dns/Dns.cpp @@ -1,6 +1,6 @@ /* XMRig - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig , + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,136 +18,24 @@ #include "base/net/dns/Dns.h" -#include "base/kernel/interfaces/IDnsListener.h" +#include "base/net/dns/DnsUvBackend.h" namespace xmrig { - Storage Dns::m_storage; - static const DnsRecord defaultRecord; -} -xmrig::Dns::Dns(IDnsListener *listener) : - m_listener(listener) +DnsConfig Dns::m_config; +std::map > Dns::m_backends; + + +} // namespace xmrig + + +std::shared_ptr xmrig::Dns::resolve(const String &host, IDnsListener *listener, uint64_t ttl) { - m_key = m_storage.add(this); + if (m_backends.find(host) == m_backends.end()) { + m_backends.insert({ host, std::make_shared() }); + } - m_resolver = new uv_getaddrinfo_t; - m_resolver->data = m_storage.ptr(m_key); - - m_hints.ai_family = AF_UNSPEC; - m_hints.ai_socktype = SOCK_STREAM; - m_hints.ai_protocol = IPPROTO_TCP; -} - - -xmrig::Dns::~Dns() -{ - m_storage.release(m_key); - - delete m_resolver; -} - - -bool xmrig::Dns::resolve(const String &host) -{ - if (m_host != host) { - m_host = host; - - clear(); - } - - m_status = uv_getaddrinfo(uv_default_loop(), m_resolver, Dns::onResolved, m_host.data(), nullptr, &m_hints); - - return m_status == 0; -} - - -const char *xmrig::Dns::error() const -{ - return uv_strerror(m_status); -} - - -const xmrig::DnsRecord &xmrig::Dns::get(DnsRecord::Type prefered) const -{ - if (count() == 0) { - return defaultRecord; - } - - const size_t ipv4 = m_ipv4.size(); - const size_t ipv6 = m_ipv6.size(); - - if (ipv6 && (prefered == DnsRecord::AAAA || !ipv4)) { - return m_ipv6[ipv6 == 1 ? 0 : static_cast(rand()) % ipv6]; - } - - if (ipv4) { - return m_ipv4[ipv4 == 1 ? 0 : static_cast(rand()) % ipv4]; - } - - return defaultRecord; -} - - -size_t xmrig::Dns::count(DnsRecord::Type type) const -{ - if (type == DnsRecord::A) { - return m_ipv4.size(); - } - - if (type == DnsRecord::AAAA) { - return m_ipv6.size(); - } - - return m_ipv4.size() + m_ipv6.size(); -} - - -void xmrig::Dns::clear() -{ - m_ipv4.clear(); - m_ipv6.clear(); -} - - -void xmrig::Dns::onResolved(int status, addrinfo *res) -{ - m_status = status; - - if (m_status < 0) { - return m_listener->onResolved(*this, status); - } - - clear(); - - addrinfo *ptr = res; - while (ptr != nullptr) { - if (ptr->ai_family == AF_INET) { - m_ipv4.emplace_back(ptr); - } - - if (ptr->ai_family == AF_INET6) { - m_ipv6.emplace_back(ptr); - } - - ptr = ptr->ai_next; - } - - if (isEmpty()) { - m_status = UV_EAI_NONAME; - } - - m_listener->onResolved(*this, m_status); -} - - -void xmrig::Dns::onResolved(uv_getaddrinfo_t *req, int status, addrinfo *res) -{ - Dns *dns = m_storage.get(req->data); - if (dns) { - dns->onResolved(status, res); - } - - uv_freeaddrinfo(res); + return m_backends.at(host)->resolve(host, listener, ttl == 0 ? m_config.ttl() : ttl); } diff --git a/src/base/net/dns/Dns.h b/src/base/net/dns/Dns.h index 86f90145..cf054390 100644 --- a/src/base/net/dns/Dns.h +++ b/src/base/net/dns/Dns.h @@ -1,6 +1,6 @@ /* XMRig - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig , + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,55 +20,34 @@ #define XMRIG_DNS_H -#include -#include - - -#include "base/net/dns/DnsRecord.h" -#include "base/net/tools/Storage.h" -#include "base/tools/Object.h" +#include "base/net/dns/DnsConfig.h" #include "base/tools/String.h" +#include +#include + + namespace xmrig { +class DnsConfig; +class DnsRequest; +class IDnsBackend; class IDnsListener; class Dns { public: - XMRIG_DISABLE_COPY_MOVE_DEFAULT(Dns) + inline static const DnsConfig &config() { return m_config; } + inline static void set(const DnsConfig &config) { m_config = config; } - Dns(IDnsListener *listener); - ~Dns(); - - inline bool isEmpty() const { return m_ipv4.empty() && m_ipv6.empty(); } - inline const String &host() const { return m_host; } - inline int status() const { return m_status; } - - bool resolve(const String &host); - const char *error() const; - const DnsRecord &get(DnsRecord::Type prefered = DnsRecord::A) const; - size_t count(DnsRecord::Type type = DnsRecord::Unknown) const; + static std::shared_ptr resolve(const String &host, IDnsListener *listener, uint64_t ttl = 0); private: - void clear(); - void onResolved(int status, addrinfo *res); - - static void onResolved(uv_getaddrinfo_t *req, int status, addrinfo *res); - - addrinfo m_hints{}; - IDnsListener *m_listener; - int m_status = 0; - std::vector m_ipv4; - std::vector m_ipv6; - String m_host; - uintptr_t m_key; - uv_getaddrinfo_t *m_resolver = nullptr; - - static Storage m_storage; + static DnsConfig m_config; + static std::map > m_backends; }; diff --git a/src/base/net/dns/DnsConfig.cpp b/src/base/net/dns/DnsConfig.cpp new file mode 100644 index 00000000..c2446c2a --- /dev/null +++ b/src/base/net/dns/DnsConfig.cpp @@ -0,0 +1,57 @@ +/* XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + + +#include "base/net/dns/DnsConfig.h" +#include "3rdparty/rapidjson/document.h" +#include "base/io/json/Json.h" + + +#include + + +namespace xmrig { + + +const char *DnsConfig::kField = "dns"; +const char *DnsConfig::kIPv6 = "ipv6"; +const char *DnsConfig::kTTL = "ttl"; + + +} // namespace xmrig + + +xmrig::DnsConfig::DnsConfig(const rapidjson::Value &value) +{ + m_ipv6 = Json::getBool(value, kIPv6, m_ipv6); + m_ttl = std::max(Json::getUint(value, kTTL, m_ttl), 1U); +} + + +rapidjson::Value xmrig::DnsConfig::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + + auto &allocator = doc.GetAllocator(); + Value obj(kObjectType); + + obj.AddMember(StringRef(kIPv6), m_ipv6, allocator); + obj.AddMember(StringRef(kTTL), m_ttl, allocator); + + return obj; +} diff --git a/src/base/net/dns/DnsConfig.h b/src/base/net/dns/DnsConfig.h new file mode 100644 index 00000000..d42c8ad8 --- /dev/null +++ b/src/base/net/dns/DnsConfig.h @@ -0,0 +1,54 @@ +/* XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + +#ifndef XMRIG_DNSCONFIG_H +#define XMRIG_DNSCONFIG_H + + +#include "3rdparty/rapidjson/fwd.h" + + +namespace xmrig { + + +class DnsConfig +{ +public: + static const char *kField; + static const char *kIPv6; + static const char *kTTL; + + DnsConfig() = default; + DnsConfig(const rapidjson::Value &object); + + inline bool isIPv6() const { return m_ipv6; } + inline uint32_t ttl() const { return m_ttl * 1000U; } + + rapidjson::Value toJSON(rapidjson::Document &doc) const; + + +private: + bool m_ipv6 = false; + uint32_t m_ttl = 30U; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_DNSCONFIG_H */ diff --git a/src/base/net/dns/DnsRecord.cpp b/src/base/net/dns/DnsRecord.cpp index 1667c715..bfa84613 100644 --- a/src/base/net/dns/DnsRecord.cpp +++ b/src/base/net/dns/DnsRecord.cpp @@ -1,6 +1,6 @@ /* XMRig - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig , + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,38 +24,34 @@ xmrig::DnsRecord::DnsRecord(const addrinfo *addr) : - m_type(addr->ai_family == AF_INET6 ? AAAA : A) + m_type(addr->ai_family == AF_INET6 ? AAAA : (addr->ai_family == AF_INET ? A : Unknown)) +{ + static_assert(sizeof(m_data) >= sizeof(sockaddr_in6), "Not enough storage for IPv6 address."); + + memcpy(m_data, addr->ai_addr, m_type == AAAA ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)); +} + + +const sockaddr *xmrig::DnsRecord::addr(uint16_t port) const +{ + reinterpret_cast(m_data)->sin_port = htons(port); + + return reinterpret_cast(m_data); +} + + +xmrig::String xmrig::DnsRecord::ip() const { char *buf = nullptr; if (m_type == AAAA) { buf = new char[45](); - uv_ip6_name(reinterpret_cast(addr->ai_addr), buf, 45); + uv_ip6_name(reinterpret_cast(m_data), buf, 45); } else { buf = new char[16](); - uv_ip4_name(reinterpret_cast(addr->ai_addr), buf, 16); + uv_ip4_name(reinterpret_cast(m_data), buf, 16); } - m_ip = buf; -} - - -sockaddr *xmrig::DnsRecord::addr(uint16_t port) const -{ - if (m_type == A) { - auto addr = new sockaddr_in(); - uv_ip4_addr(m_ip.data(), port, addr); - - return reinterpret_cast(addr); - } - - if (m_type == AAAA) { - auto addr = new sockaddr_in6(); - uv_ip6_addr(m_ip.data(), port, addr); - - return reinterpret_cast(addr); - } - - return nullptr; + return buf; } diff --git a/src/base/net/dns/DnsRecord.h b/src/base/net/dns/DnsRecord.h index cf6c2598..7a68ea2f 100644 --- a/src/base/net/dns/DnsRecord.h +++ b/src/base/net/dns/DnsRecord.h @@ -1,6 +1,6 @@ /* XMRig - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig , + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ namespace xmrig { class DnsRecord { public: - enum Type { + enum Type : uint32_t { Unknown, A, AAAA @@ -42,15 +42,15 @@ public: DnsRecord() {} DnsRecord(const addrinfo *addr); - sockaddr *addr(uint16_t port = 0) const; + const sockaddr *addr(uint16_t port = 0) const; + String ip() const; inline bool isValid() const { return m_type != Unknown; } - inline const String &ip() const { return m_ip; } inline Type type() const { return m_type; } private: - Type m_type = Unknown; - String m_ip; + mutable uint8_t m_data[28]{}; + const Type m_type = Unknown; }; diff --git a/src/base/net/dns/DnsRecords.cpp b/src/base/net/dns/DnsRecords.cpp new file mode 100644 index 00000000..fb232f27 --- /dev/null +++ b/src/base/net/dns/DnsRecords.cpp @@ -0,0 +1,108 @@ +/* XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + + +#include + + +#include "base/net/dns/DnsRecords.h" +#include "base/net/dns/Dns.h" + + +const xmrig::DnsRecord &xmrig::DnsRecords::get(DnsRecord::Type prefered) const +{ + static const DnsRecord defaultRecord; + + if (isEmpty()) { + return defaultRecord; + } + + const size_t ipv4 = m_ipv4.size(); + const size_t ipv6 = m_ipv6.size(); + + if (ipv6 && (prefered == DnsRecord::AAAA || Dns::config().isIPv6() || !ipv4)) { + return m_ipv6[ipv6 == 1 ? 0 : static_cast(rand()) % ipv6]; + } + + if (ipv4) { + return m_ipv4[ipv4 == 1 ? 0 : static_cast(rand()) % ipv4]; + } + + return defaultRecord; +} + + +size_t xmrig::DnsRecords::count(DnsRecord::Type type) const +{ + if (type == DnsRecord::A) { + return m_ipv4.size(); + } + + if (type == DnsRecord::AAAA) { + return m_ipv6.size(); + } + + return m_ipv4.size() + m_ipv6.size(); +} + + +void xmrig::DnsRecords::clear() +{ + m_ipv4.clear(); + m_ipv6.clear(); +} + + +void xmrig::DnsRecords::parse(addrinfo *res) +{ + clear(); + + addrinfo *ptr = res; + size_t ipv4 = 0; + size_t ipv6 = 0; + + while (ptr != nullptr) { + if (ptr->ai_family == AF_INET) { + ++ipv4; + } + else if (ptr->ai_family == AF_INET6) { + ++ipv6; + } + + ptr = ptr->ai_next; + } + + if (ipv4 == 0 && ipv6 == 0) { + return; + } + + m_ipv4.reserve(ipv4); + m_ipv6.reserve(ipv6); + + ptr = res; + while (ptr != nullptr) { + if (ptr->ai_family == AF_INET) { + m_ipv4.emplace_back(ptr); + } + else if (ptr->ai_family == AF_INET6) { + m_ipv6.emplace_back(ptr); + } + + ptr = ptr->ai_next; + } +} diff --git a/src/base/net/dns/DnsRecords.h b/src/base/net/dns/DnsRecords.h new file mode 100644 index 00000000..cfa19217 --- /dev/null +++ b/src/base/net/dns/DnsRecords.h @@ -0,0 +1,48 @@ +/* XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + +#ifndef XMRIG_DNSRECORDS_H +#define XMRIG_DNSRECORDS_H + + +#include "base/net/dns/DnsRecord.h" + + +namespace xmrig { + + +class DnsRecords +{ +public: + inline bool isEmpty() const { return m_ipv4.empty() && m_ipv6.empty(); } + + const DnsRecord &get(DnsRecord::Type prefered = DnsRecord::Unknown) const; + size_t count(DnsRecord::Type type = DnsRecord::Unknown) const; + void clear(); + void parse(addrinfo *res); + +private: + std::vector m_ipv4; + std::vector m_ipv6; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_DNSRECORDS_H */ diff --git a/src/base/net/dns/DnsRequest.h b/src/base/net/dns/DnsRequest.h new file mode 100644 index 00000000..036eaa34 --- /dev/null +++ b/src/base/net/dns/DnsRequest.h @@ -0,0 +1,50 @@ +/* XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + +#ifndef XMRIG_DNSREQUEST_H +#define XMRIG_DNSREQUEST_H + + +#include "base/tools/Object.h" + + +#include + + +namespace xmrig { + + +class IDnsListener; + + +class DnsRequest +{ +public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(DnsRequest) + + DnsRequest(IDnsListener *listener) : listener(listener) {} + ~DnsRequest() = default; + + IDnsListener *listener; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_DNSREQUEST_H */ diff --git a/src/base/net/dns/DnsUvBackend.cpp b/src/base/net/dns/DnsUvBackend.cpp new file mode 100644 index 00000000..51ee2193 --- /dev/null +++ b/src/base/net/dns/DnsUvBackend.cpp @@ -0,0 +1,130 @@ +/* XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + + +#include + + +#include "base/net/dns/DnsUvBackend.h" +#include "base/kernel/interfaces/IDnsListener.h" +#include "base/net/dns/DnsRequest.h" +#include "base/tools/Chrono.h" + + +namespace xmrig { + + +Storage DnsUvBackend::m_storage; + +static addrinfo hints{}; + + +} // namespace xmrig + + +xmrig::DnsUvBackend::DnsUvBackend() +{ + if (!hints.ai_protocol) { + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + } + + m_key = m_storage.add(this); +} + + +xmrig::DnsUvBackend::~DnsUvBackend() +{ + m_storage.release(m_key); +} + + +std::shared_ptr xmrig::DnsUvBackend::resolve(const String &host, IDnsListener *listener, uint64_t ttl) +{ + auto req = std::make_shared(listener); + + if (Chrono::currentMSecsSinceEpoch() - m_ts <= ttl && !m_records.isEmpty()) { + req->listener->onResolved(m_records, 0, nullptr); + } else { + m_queue.emplace(req); + } + + if (m_queue.size() == 1 && !resolve(host)) { + done(); + } + + return req; +} + + +bool xmrig::DnsUvBackend::resolve(const String &host) +{ + m_req = std::make_shared(); + m_req->data = m_storage.ptr(m_key); + + m_status = uv_getaddrinfo(uv_default_loop(), m_req.get(), DnsUvBackend::onResolved, host.data(), nullptr, &hints); + + return m_status == 0; +} + + +void xmrig::DnsUvBackend::done() +{ + const char *error = m_status < 0 ? uv_strerror(m_status) : nullptr; + + while (!m_queue.empty()) { + auto req = std::move(m_queue.front()).lock(); + if (req) { + req->listener->onResolved(m_records, m_status, error); + } + + m_queue.pop(); + } + + m_req.reset(); +} + + +void xmrig::DnsUvBackend::onResolved(int status, addrinfo *res) +{ + m_ts = Chrono::currentMSecsSinceEpoch(); + + if ((m_status = status) < 0) { + return done(); + } + + m_records.parse(res); + + if (m_records.isEmpty()) { + m_status = UV_EAI_NONAME; + } + + done(); +} + + +void xmrig::DnsUvBackend::onResolved(uv_getaddrinfo_t *req, int status, addrinfo *res) +{ + auto backend = m_storage.get(req->data); + if (backend) { + backend->onResolved(status, res); + } + + uv_freeaddrinfo(res); +} diff --git a/src/base/net/dns/DnsUvBackend.h b/src/base/net/dns/DnsUvBackend.h new file mode 100644 index 00000000..f4e6949b --- /dev/null +++ b/src/base/net/dns/DnsUvBackend.h @@ -0,0 +1,71 @@ +/* XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + +#ifndef XMRIG_DNSUVBACKEND_H +#define XMRIG_DNSUVBACKEND_H + + +#include "base/kernel/interfaces/IDnsBackend.h" +#include "base/net/dns/DnsRecords.h" +#include "base/net/tools/Storage.h" + + +#include + + +using uv_getaddrinfo_t = struct uv_getaddrinfo_s; + + +namespace xmrig { + + +class DnsUvBackend : public IDnsBackend +{ +public: + XMRIG_DISABLE_COPY_MOVE(DnsUvBackend) + + DnsUvBackend(); + ~DnsUvBackend() override; + +protected: + inline const DnsRecords &records() const override { return m_records; } + + std::shared_ptr resolve(const String &host, IDnsListener *listener, uint64_t ttl) override; + +private: + bool resolve(const String &host); + void done(); + void onResolved(int status, addrinfo *res); + + static void onResolved(uv_getaddrinfo_t *req, int status, addrinfo *res); + + DnsRecords m_records; + int m_status = 0; + std::queue > m_queue; + std::shared_ptr m_req; + uint64_t m_ts = 0; + uintptr_t m_key; + + static Storage m_storage; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_DNSUVBACKEND_H */ diff --git a/src/base/net/http/HttpClient.cpp b/src/base/net/http/HttpClient.cpp index ea0d68c8..96f3f556 100644 --- a/src/base/net/http/HttpClient.cpp +++ b/src/base/net/http/HttpClient.cpp @@ -23,11 +23,13 @@ #include "base/io/log/Log.h" #include "base/kernel/Platform.h" #include "base/net/dns/Dns.h" +#include "base/net/dns/DnsRecords.h" #include "base/net/tools/NetBuffer.h" #include "base/tools/Timer.h" #include +#include namespace xmrig { @@ -48,7 +50,6 @@ xmrig::HttpClient::HttpClient(const char *tag, FetchRequest &&req, const std::we url = std::move(m_req.path); body = std::move(m_req.body); headers = std::move(m_req.headers); - m_dns = std::make_shared(this); if (m_req.timeout) { m_timer = std::make_shared(this, m_req.timeout, 0); @@ -58,30 +59,29 @@ xmrig::HttpClient::HttpClient(const char *tag, FetchRequest &&req, const std::we bool xmrig::HttpClient::connect() { - return m_dns->resolve(m_req.host); + m_dns = Dns::resolve(m_req.host, this); + + return true; } -void xmrig::HttpClient::onResolved(const Dns &dns, int status) +void xmrig::HttpClient::onResolved(const DnsRecords &records, int status, const char *error) { this->status = status; + m_dns.reset(); - if (status < 0 && dns.isEmpty()) { + if (status < 0 && records.isEmpty()) { if (!isQuiet()) { - LOG_ERR("%s " RED("DNS error: ") RED_BOLD("\"%s\""), tag(), uv_strerror(status)); + LOG_ERR("%s " RED("DNS error: ") RED_BOLD("\"%s\""), tag(), error); } return; } - sockaddr *addr = dns.get().addr(port()); - auto req = new uv_connect_t; req->data = this; - uv_tcp_connect(req, m_tcp, addr, onConnect); - - delete addr; + uv_tcp_connect(req, m_tcp, records.get().addr(port()), onConnect); } diff --git a/src/base/net/http/HttpClient.h b/src/base/net/http/HttpClient.h index d012d26f..ccda2e56 100644 --- a/src/base/net/http/HttpClient.h +++ b/src/base/net/http/HttpClient.h @@ -32,7 +32,7 @@ namespace xmrig { -class String; +class DnsRequest; class HttpClient : public HttpContext, public IDnsListener, public ITimerListener @@ -51,7 +51,7 @@ public: bool connect(); protected: - void onResolved(const Dns &dns, int status) override; + void onResolved(const DnsRecords &records, int status, const char *error) override; void onTimer(const Timer *timer) override; virtual void handshake(); @@ -65,7 +65,7 @@ private: const char *m_tag; FetchRequest m_req; - std::shared_ptr m_dns; + std::shared_ptr m_dns; std::shared_ptr m_timer; }; diff --git a/src/base/net/stratum/Client.cpp b/src/base/net/stratum/Client.cpp index 5327867e..fd4d13a7 100644 --- a/src/base/net/stratum/Client.cpp +++ b/src/base/net/stratum/Client.cpp @@ -48,10 +48,11 @@ #include "base/io/log/Log.h" #include "base/kernel/interfaces/IClientListener.h" #include "base/net/dns/Dns.h" +#include "base/net/dns/DnsRecords.h" #include "base/net/stratum/Socks5.h" #include "base/net/tools/NetBuffer.h" -#include "base/tools/Cvt.h" #include "base/tools/Chrono.h" +#include "base/tools/Cvt.h" #include "net/JobResult.h" @@ -86,13 +87,11 @@ xmrig::Client::Client(int id, const char *agent, IClientListener *listener) : { m_reader.setListener(this); m_key = m_storage.add(this); - m_dns = new Dns(this); } xmrig::Client::~Client() { - delete m_dns; delete m_socket; } @@ -295,22 +294,24 @@ void xmrig::Client::tick(uint64_t now) } -void xmrig::Client::onResolved(const Dns &dns, int status) +void xmrig::Client::onResolved(const DnsRecords &records, int status, const char *error) { + m_dns.reset(); + assert(m_listener != nullptr); if (!m_listener) { return reconnect(); } - if (status < 0 && dns.isEmpty()) { + if (status < 0 && records.isEmpty()) { if (!isQuiet()) { - LOG_ERR("%s " RED("DNS error: ") RED_BOLD("\"%s\""), tag(), uv_strerror(status)); + LOG_ERR("%s " RED("DNS error: ") RED_BOLD("\"%s\""), tag(), error); } return reconnect(); } - const auto &record = dns.get(); + const auto &record = records.get(); m_ip = record.ip(); connect(record.addr(m_socks5 ? m_pool.proxy().port() : m_pool.port())); @@ -524,13 +525,7 @@ int xmrig::Client::resolve(const String &host) m_failures = 0; } - if (!m_dns->resolve(host)) { - if (!isQuiet()) { - LOG_ERR("%s " RED("getaddrinfo error: ") RED_BOLD("\"%s\""), tag(), uv_strerror(m_dns->status())); - } - - return 1; - } + m_dns = Dns::resolve(host, this); return 0; } @@ -566,7 +561,7 @@ int64_t xmrig::Client::send(size_t size) } -void xmrig::Client::connect(sockaddr *addr) +void xmrig::Client::connect(const sockaddr *addr) { setState(ConnectingState); @@ -584,8 +579,6 @@ void xmrig::Client::connect(sockaddr *addr) # endif uv_tcp_connect(req, m_socket, addr, onConnect); - - delete addr; } diff --git a/src/base/net/stratum/Client.h b/src/base/net/stratum/Client.h index 33e3fd8f..e6508c65 100644 --- a/src/base/net/stratum/Client.h +++ b/src/base/net/stratum/Client.h @@ -50,6 +50,7 @@ using BIO = struct bio_st; namespace xmrig { +class DnsRequest; class IClientListener; class JobResult; @@ -79,7 +80,7 @@ protected: void deleteLater() override; void tick(uint64_t now) override; - void onResolved(const Dns &dns, int status) override; + void onResolved(const DnsRecords &records, int status, const char *error) override; inline bool hasExtension(Extension extension) const noexcept override { return m_extensions.test(extension); } inline const char *mode() const override { return "pool"; } @@ -108,7 +109,7 @@ private: bool write(const uv_buf_t &buf); int resolve(const String &host); int64_t send(size_t size); - void connect(sockaddr *addr); + void connect(const sockaddr *addr); void handshake(); void parse(char *line, size_t len); void parseExtensions(const rapidjson::Value &result); @@ -131,10 +132,10 @@ private: static inline Client *getClient(void *data) { return m_storage.get(data); } const char *m_agent; - Dns *m_dns; LineReader m_reader; Socks5 *m_socks5 = nullptr; std::bitset m_extensions; + std::shared_ptr m_dns; std::vector m_sendBuf; String m_rpcId; Tls *m_tls = nullptr; diff --git a/src/base/net/stratum/benchmark/BenchClient.cpp b/src/base/net/stratum/benchmark/BenchClient.cpp index 0e1093a1..11b0d2ba 100644 --- a/src/base/net/stratum/benchmark/BenchClient.cpp +++ b/src/base/net/stratum/benchmark/BenchClient.cpp @@ -27,6 +27,7 @@ #include "base/io/log/Tags.h" #include "base/kernel/interfaces/IClientListener.h" #include "base/net/dns/Dns.h" +#include "base/net/dns/DnsRecords.h" #include "base/net/http/Fetch.h" #include "base/net/http/HttpData.h" #include "base/net/http/HttpListener.h" @@ -185,16 +186,18 @@ void xmrig::BenchClient::onHttpData(const HttpData &data) } -void xmrig::BenchClient::onResolved(const Dns &dns, int status) +void xmrig::BenchClient::onResolved(const DnsRecords &records, int status, const char *error) { # ifdef XMRIG_FEATURE_HTTP assert(!m_httpListener); + m_dns.reset(); + if (status < 0) { - return setError(dns.error(), "DNS error"); + return setError(error, "DNS error"); } - m_ip = dns.get().ip(); + m_ip = records.get().ip(); m_httpListener = std::make_shared(this, tag()); if (m_mode == ONLINE_BENCH) { @@ -307,11 +310,7 @@ void xmrig::BenchClient::onGetReply(const rapidjson::Value &value) void xmrig::BenchClient::resolve() { - m_dns = std::make_shared(this); - - if (!m_dns->resolve(BenchConfig::kApiHost)) { - setError(m_dns->error(), "getaddrinfo error"); - } + m_dns = Dns::resolve(BenchConfig::kApiHost, this); } diff --git a/src/base/net/stratum/benchmark/BenchClient.h b/src/base/net/stratum/benchmark/BenchClient.h index 7eac1ee8..a4cedf61 100644 --- a/src/base/net/stratum/benchmark/BenchClient.h +++ b/src/base/net/stratum/benchmark/BenchClient.h @@ -70,7 +70,7 @@ protected: void onBenchDone(uint64_t result, uint64_t diff, uint64_t ts) override; void onBenchReady(uint64_t ts, uint32_t threads, const IBackend *backend) override; void onHttpData(const HttpData &data) override; - void onResolved(const Dns &dns, int status) override; + void onResolved(const DnsRecords &records, int status, const char *error) override; private: enum Mode : uint32_t { @@ -110,7 +110,7 @@ private: Pool m_pool; Request m_request = NO_REQUEST; std::shared_ptr m_benchmark; - std::shared_ptr m_dns; + std::shared_ptr m_dns; std::shared_ptr m_httpListener; String m_ip; String m_token; diff --git a/src/core/config/Config.cpp b/src/core/config/Config.cpp index 2b27ae58..3924123f 100644 --- a/src/core/config/Config.cpp +++ b/src/core/config/Config.cpp @@ -28,6 +28,7 @@ #include "backend/cpu/Cpu.h" #include "base/io/log/Log.h" #include "base/kernel/interfaces/IJsonReader.h" +#include "base/net/dns/Dns.h" #include "crypto/common/Assembly.h" @@ -295,6 +296,7 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const doc.AddMember(StringRef(kTls), m_tls.toJSON(doc), allocator); # endif + doc.AddMember(StringRef(DnsConfig::kField), Dns::config().toJSON(doc), allocator); doc.AddMember(StringRef(kUserAgent), m_userAgent.toJSON(), allocator); doc.AddMember(StringRef(kVerbose), Log::verbose(), allocator); doc.AddMember(StringRef(kWatch), m_watch, allocator); diff --git a/src/core/config/Config_platform.h b/src/core/config/Config_platform.h index d5429975..f92a3158 100644 --- a/src/core/config/Config_platform.h +++ b/src/core/config/Config_platform.h @@ -93,7 +93,9 @@ static const option options[] = { { "title", 1, nullptr, IConfig::TitleKey }, { "no-title", 0, nullptr, IConfig::NoTitleKey }, { "pause-on-battery", 0, nullptr, IConfig::PauseOnBatteryKey }, - { "pause-on-active", 1, nullptr, IConfig::PauseOnActiveKey }, + { "pause-on-active", 1, nullptr, IConfig::PauseOnActiveKey }, + { "dns-ipv6", 0, nullptr, IConfig::DnsIPv6Key }, + { "dns-ttl", 1, nullptr, IConfig::DnsTtlKey }, # ifdef XMRIG_FEATURE_BENCHMARK { "stress", 0, nullptr, IConfig::StressKey }, { "bench", 1, nullptr, IConfig::BenchKey }, diff --git a/src/core/config/usage.h b/src/core/config/usage.h index 57374620..f16124cb 100644 --- a/src/core/config/usage.h +++ b/src/core/config/usage.h @@ -59,6 +59,9 @@ static inline const std::string &usage() u += " --tls-fingerprint=HEX pool TLS certificate fingerprint for strict certificate pinning\n"; # endif + u += " --dns-ipv6 prefer IPv6 records from DNS responses\n"; + u += " --dns-ttl=N N seconds (default: 30) TTL for internal DNS cache\n"; + # ifdef XMRIG_FEATURE_HTTP u += " --daemon use daemon RPC instead of pool for solo mining\n"; u += " --daemon-poll-interval=N daemon poll interval in milliseconds (default: 1000)\n";