diff --git a/include/opendht/dht_proxy_client.h b/include/opendht/dht_proxy_client.h index abb0c0882e3fee7908ccab7827b53cc6be8f66e8..7ac306fd56008843911f095997d124fac76befe7 100644 --- a/include/opendht/dht_proxy_client.h +++ b/include/opendht/dht_proxy_client.h @@ -263,7 +263,7 @@ private: * @return the JSON returned by the proxy */ void getProxyInfos(); - void onProxyInfos(const Json::Value& val); + void onProxyInfos(const Json::Value& val, const sa_family_t& family); SockAddr parsePublicAddress(const Json::Value& val); void opFailed(); @@ -294,7 +294,8 @@ private: NodeStatus statusIpv6_ {NodeStatus::Disconnected}; NodeStats stats4_ {}; NodeStats stats6_ {}; - SockAddr publicAddress_; + SockAddr publicAddressV4_; + SockAddr publicAddressV6_; InfoHash myid {}; diff --git a/src/dht_proxy_client.cpp b/src/dht_proxy_client.cpp index 757d938153d3cc1b6429153f0580127c1589f1ca..e2dde088e5050d656f585108fd95281c4b5f6a98 100644 --- a/src/dht_proxy_client.cpp +++ b/src/dht_proxy_client.cpp @@ -451,39 +451,58 @@ DhtProxyClient::getProxyInfos() statusIpv6_ = NodeStatus::Connecting; } - restbed::Uri uri(HTTP_PROTO + serverHost_ + "/"); - auto req = std::make_shared<restbed::Request>(uri); + // A node can have a Ipv4 and a Ipv6. So, we need to retrieve all public ips + std::string host, service; + auto serviceMarker = serverHost_.find(':'); + if (serviceMarker != std::string::npos) { + host = serverHost_.substr(0, serviceMarker - 1); + service = serverHost_.substr(serviceMarker + 1); + } else { + host = serverHost_; + } + auto resolved_proxies = SockAddr::resolve(host, service); + auto serverHost = serverHost_; // Try to contact the proxy and set the status to connected when done. // will change the connectivity status - statusThread_ = std::thread([this, req]{ - restbed::Http::async(req, - [this](const std::shared_ptr<restbed::Request>&, - const std::shared_ptr<restbed::Response>& reply) { - auto code = reply->get_status_code(); - Json::Value proxyInfos; - if (code == 200) { - restbed::Http::fetch("\n", reply); - std::string body; - reply->get_body(body); + statusThread_ = std::thread([this, resolved_proxies, serverHost]{ + for (const auto& resolved_proxy: resolved_proxies) { + auto server = resolved_proxy.toString(); + if (resolved_proxy.getFamily() == AF_INET6) { + // HACK restbed seems to not correctly handle directly http://[ipv6] + // See https://github.com/Corvusoft/restbed/issues/290. + server = serverHost; + } + restbed::Uri uri(HTTP_PROTO + server + "/"); + auto req = std::make_shared<restbed::Request>(uri); + restbed::Http::async(req, + [this, resolved_proxy](const std::shared_ptr<restbed::Request>&, + const std::shared_ptr<restbed::Response>& reply) { + auto code = reply->get_status_code(); + Json::Value proxyInfos; + if (code == 200) { + restbed::Http::fetch("\n", reply); + std::string body; + reply->get_body(body); - std::string err; - Json::CharReaderBuilder rbuilder; - auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader()); - try { - reader->parse(body.data(), body.data() + body.size(), &proxyInfos, &err); - } catch (...) { + std::string err; + Json::CharReaderBuilder rbuilder; + auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader()); + try { + reader->parse(body.data(), body.data() + body.size(), &proxyInfos, &err); + } catch (...) { + } + onProxyInfos(proxyInfos, resolved_proxy.getFamily()); } - } - onProxyInfos(proxyInfos); - ongoingStatusUpdate_.clear(); - }); + ongoingStatusUpdate_.clear(); + }); + } }); statusThread_.detach(); } void -DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos) +DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, const sa_family_t& family) { std::lock_guard<std::mutex> l(lockCurrentProxyInfos_); @@ -508,7 +527,10 @@ DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos) else statusIpv6_ = NodeStatus::Disconnected; - publicAddress_ = parsePublicAddress(proxyInfos["public_ip"]); + if (family == AF_INET) + publicAddressV4_ = parsePublicAddress(proxyInfos["public_ip"]); + else if (family == AF_INET6) + publicAddressV6_ = parsePublicAddress(proxyInfos["public_ip"]); } catch (...) {} auto newStatus = std::max(statusIpv4_, statusIpv6_); @@ -530,9 +552,9 @@ DhtProxyClient::parsePublicAddress(const Json::Value& val) { auto public_ip = val.asString(); auto endIp = public_ip.find_last_of(':'); - std::string service = public_ip.substr(endIp + 1); - std::string address = public_ip.substr(0, endIp - 1); - auto sa = SockAddr::resolve(address, service); + auto marker = (public_ip.size() > 0 && public_ip[0] == '[') ? 1 : 0; + std::string address = public_ip.substr(marker, endIp - marker * 2); + auto sa = SockAddr::resolve(address); if (sa.empty()) return {}; return sa.front().getMappedIPv4(); } @@ -541,8 +563,10 @@ std::vector<SockAddr> DhtProxyClient::getPublicAddress(sa_family_t family) { std::lock_guard<std::mutex> l(lockCurrentProxyInfos_); - if (not publicAddress_) return {}; - return publicAddress_.getFamily() == family ? std::vector<SockAddr>{publicAddress_} : std::vector<SockAddr>{}; + std::vector<SockAddr> result; + if (publicAddressV6_ && family != AF_INET) result.emplace_back(publicAddressV6_); + if (publicAddressV4_ && family != AF_INET6) result.emplace_back(publicAddressV4_); + return result; } size_t