diff --git a/td/telegram/net/ConnectionCreator.cpp b/td/telegram/net/ConnectionCreator.cpp index 242e29dd4..c3aa932b9 100644 --- a/td/telegram/net/ConnectionCreator.cpp +++ b/td/telegram/net/ConnectionCreator.cpp @@ -455,7 +455,8 @@ void ConnectionCreator::ping_proxy(int32 proxy_id, Promise promise) { return promise.set_error(Status::Error(400, "Unknown proxy identifier")); } const Proxy &proxy = it->second; - send_closure(get_host_by_name_actor_, &GetHostByNameActor::run, proxy.server().str(), proxy.port(), + bool prefer_ipv6 = G()->shared_config().get_option_boolean("prefer_ipv6"); + send_closure(get_host_by_name_actor_, &GetHostByNameActor::run, proxy.server().str(), proxy.port(), prefer_ipv6, PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise), proxy_id](Result result) mutable { if (result.is_error()) { @@ -1230,8 +1231,9 @@ void ConnectionCreator::loop() { if (resolve_proxy_query_token_ == 0) { resolve_proxy_query_token_ = next_token(); const Proxy &proxy = proxies_[active_proxy_id_]; + bool prefer_ipv6 = G()->shared_config().get_option_boolean("prefer_ipv6"); send_closure( - get_host_by_name_actor_, &GetHostByNameActor::run, proxy.server().str(), proxy.port(), + get_host_by_name_actor_, &GetHostByNameActor::run, proxy.server().str(), proxy.port(), prefer_ipv6, PromiseCreator::lambda([actor_id = create_reference(resolve_proxy_query_token_)](Result result) { send_closure(std::move(actor_id), &ConnectionCreator::on_proxy_resolved, std::move(result), false); })); diff --git a/tdnet/td/net/GetHostByNameActor.cpp b/tdnet/td/net/GetHostByNameActor.cpp index f2b9313f6..ed6d8ce7e 100644 --- a/tdnet/td/net/GetHostByNameActor.cpp +++ b/tdnet/td/net/GetHostByNameActor.cpp @@ -15,13 +15,13 @@ GetHostByNameActor::GetHostByNameActor(int32 ok_timeout, int32 error_timeout) : ok_timeout_(ok_timeout), error_timeout_(error_timeout) { } -void GetHostByNameActor::run(std::string host, int port, td::Promise promise) { - auto r_ip = load_ip(std::move(host), port); +void GetHostByNameActor::run(std::string host, int port, bool prefer_ipv6, td::Promise promise) { + auto r_ip = load_ip(std::move(host), port, prefer_ipv6); promise.set_result(std::move(r_ip)); } -Result GetHostByNameActor::load_ip(string host, int port) { - auto &value = cache_.emplace(host, Value{{}, 0}).first->second; +Result GetHostByNameActor::load_ip(string host, int port, bool prefer_ipv6) { + auto &value = cache_[prefer_ipv6].emplace(host, Value{{}, 0}).first->second; auto begin_time = td::Time::now(); if (value.expire_at > begin_time) { auto ip = value.ip.clone(); @@ -33,7 +33,7 @@ Result GetHostByNameActor::load_ip(string host, int port) { } td::IPAddress ip; - auto status = ip.init_host_port(host, port); + auto status = ip.init_host_port(host, port, prefer_ipv6); auto end_time = td::Time::now(); LOG(WARNING) << "Init host = " << host << ", port = " << port << " in " << end_time - begin_time << " seconds to " << ip; diff --git a/tdnet/td/net/GetHostByNameActor.h b/tdnet/td/net/GetHostByNameActor.h index dabed07f6..4061927af 100644 --- a/tdnet/td/net/GetHostByNameActor.h +++ b/tdnet/td/net/GetHostByNameActor.h @@ -17,7 +17,7 @@ namespace td { class GetHostByNameActor final : public td::Actor { public: explicit GetHostByNameActor(int32 ok_timeout = CACHE_TIME, int32 error_timeout = ERROR_CACHE_TIME); - void run(std::string host, int port, td::Promise promise); + void run(std::string host, int port, bool prefer_ipv6, td::Promise promise); private: struct Value { @@ -27,12 +27,13 @@ class GetHostByNameActor final : public td::Actor { Value(Result ip, double expire_at) : ip(std::move(ip)), expire_at(expire_at) { } }; - std::unordered_map cache_; + std::unordered_map cache_[2]; static constexpr int32 CACHE_TIME = 60 * 29; // 29 minutes static constexpr int32 ERROR_CACHE_TIME = 60 * 5; // 5 minutes int32 ok_timeout_; int32 error_timeout_; - Result load_ip(string host, int port) TD_WARN_UNUSED_RESULT; + + Result load_ip(string host, int port, bool prefer_ipv6) TD_WARN_UNUSED_RESULT; }; } // namespace td diff --git a/tdutils/td/utils/port/IPAddress.cpp b/tdutils/td/utils/port/IPAddress.cpp index 748966750..41f5340ec 100644 --- a/tdutils/td/utils/port/IPAddress.cpp +++ b/tdutils/td/utils/port/IPAddress.cpp @@ -289,12 +289,12 @@ Status IPAddress::init_ipv4_port(CSlice ipv4, int port) { return Status::OK(); } -Status IPAddress::init_host_port(CSlice host, int port) { +Status IPAddress::init_host_port(CSlice host, int port, bool prefer_ipv6) { auto str_port = to_string(port); - return init_host_port(host, str_port); + return init_host_port(host, str_port, prefer_ipv6); } -Status IPAddress::init_host_port(CSlice host, CSlice port) { +Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) { if (host.empty()) { return Status::Error("Host is empty"); } @@ -327,14 +327,19 @@ Status IPAddress::init_host_port(CSlice host, CSlice port) { addrinfo *best_info = nullptr; for (auto *ptr = info; ptr != nullptr; ptr = ptr->ai_next) { - if (ptr->ai_family == AF_INET) { - // just use first IPv4 address + if (ptr->ai_family == AF_INET && (!prefer_ipv6 || best_info == nullptr)) { + // just use first IPv4 address if there is no IPv6 and it isn't preferred best_info = ptr; - break; + if (!prefer_ipv6) { + break; + } } - if (ptr->ai_family == AF_INET6 && best_info == nullptr) { - // or first IPv6 address if there is no IPv4 + if (ptr->ai_family == AF_INET6 && (prefer_ipv6 || best_info == nullptr)) { + // or first IPv6 address if there is no IPv4 and it isn't preferred best_info = ptr; + if (prefer_ipv6) { + break; + } } } if (best_info == nullptr) { diff --git a/tdutils/td/utils/port/IPAddress.h b/tdutils/td/utils/port/IPAddress.h index c9993dbb0..ca8dafb47 100644 --- a/tdutils/td/utils/port/IPAddress.h +++ b/tdutils/td/utils/port/IPAddress.h @@ -46,8 +46,8 @@ class IPAddress { Status init_ipv6_port(CSlice ipv6, int port) TD_WARN_UNUSED_RESULT; Status init_ipv6_as_ipv4_port(CSlice ipv4, int port) TD_WARN_UNUSED_RESULT; Status init_ipv4_port(CSlice ipv4, int port) TD_WARN_UNUSED_RESULT; - Status init_host_port(CSlice host, int port) TD_WARN_UNUSED_RESULT; - Status init_host_port(CSlice host, CSlice port) TD_WARN_UNUSED_RESULT; + Status init_host_port(CSlice host, int port, bool prefer_ipv6 = false) TD_WARN_UNUSED_RESULT; + Status init_host_port(CSlice host, CSlice port, bool prefer_ipv6 = false) TD_WARN_UNUSED_RESULT; Status init_host_port(CSlice host_port) TD_WARN_UNUSED_RESULT; Status init_socket_address(const SocketFd &socket_fd) TD_WARN_UNUSED_RESULT; Status init_peer_address(const SocketFd &socket_fd) TD_WARN_UNUSED_RESULT;