///////////////////////////////////////////////////////
| 330 | |
| 331 | //////////////////////////////////////////////////////////// |
| 332 | std::optional<IpAddress> IpAddress::getPublicAddress(Time timeout, std::optional<Type> type, bool secure) |
| 333 | { |
| 334 | // The trick here is more complicated, because the only way |
| 335 | // to get our public IP address is to get it from a distant computer. |
| 336 | |
| 337 | // Our own DNS implementation does not support DNSSEC, DNS-over-TLS or DNS-over-HTTPS |
| 338 | // Both DoT and DoH require communication over a TLS connection anyway |
| 339 | // so there is no advantage when using DNS to perform secure queries instead of HTTPS |
| 340 | if (!secure) |
| 341 | { |
| 342 | // Try first via DNS query |
| 343 | if (!type.has_value()) |
| 344 | { |
| 345 | // No type preference, try IPv6 then IPv4 |
| 346 | if (const auto addressViaDns = Dns::getPublicAddress(timeout, IpAddress::Type::IpV6); addressViaDns) |
| 347 | return addressViaDns; |
| 348 | |
| 349 | if (const auto addressViaDns = Dns::getPublicAddress(timeout, IpAddress::Type::IpV4); addressViaDns) |
| 350 | return addressViaDns; |
| 351 | } |
| 352 | else |
| 353 | { |
| 354 | if (const auto addressViaDns = Dns::getPublicAddress(timeout, *type); addressViaDns) |
| 355 | return addressViaDns; |
| 356 | } |
| 357 | } |
| 358 | |
| 359 | // DNS query wasn't successful, try via HTTP |
| 360 | // Here we get the web page from e.g. http://ifconfig.co/ip |
| 361 | // and parse the result to extract our IP address |
| 362 | // (not very hard: the web page contains only our IP address). |
| 363 | for (const auto provider : {"ifconfig.co"sv, "ifconfig.me"sv, "icanhazip.com"sv, "ipinfo.io"sv, "v6.ipinfo.io"sv}) |
| 364 | { |
| 365 | if (type.has_value()) |
| 366 | { |
| 367 | // Make sure the provider has addresses that match the query type |
| 368 | if (const auto providerAddresses = Dns::resolve(provider); |
| 369 | providerAddresses.has_value() && |
| 370 | (std::find_if(providerAddresses->begin(), |
| 371 | providerAddresses->end(), |
| 372 | [&type](const auto& address) { return address.getType() == type; }) == |
| 373 | providerAddresses->end())) |
| 374 | continue; |
| 375 | } |
| 376 | |
| 377 | const Http server((secure ? "https://" : "http://") + std::string(provider), 0, type); |
| 378 | const Http::Request request("/ip", Http::Request::Method::Get); |
| 379 | const Http::Response page = server.sendRequest(request, timeout, secure); |
| 380 | const Http::Response::Status status = page.getStatus(); |
| 381 | |
| 382 | if (status == Http::Response::Status::Ok) |
| 383 | { |
| 384 | static constexpr std::array charset = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', |
| 385 | 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', '.', ':'}; |
| 386 | auto body = page.getBody(); |
| 387 | body.erase(std::remove_if(body.begin(), |
| 388 | body.end(), |
| 389 | [](char c) { |