///////////////////////////////////////////////////////
| 892 | |
| 893 | //////////////////////////////////////////////////////////// |
| 894 | std::optional<IpAddress> Dns::getPublicAddress(std::optional<Time> timeout, IpAddress::Type type) |
| 895 | { |
| 896 | std::optional<IpAddress> address; |
| 897 | |
| 898 | const auto handler = |
| 899 | [&](const char* data, std::size_t length, const char*, std::uint16_t responseType, std::uint16_t responseClass) |
| 900 | { |
| 901 | // No need to handle this response if we already have an address |
| 902 | if (address.has_value()) |
| 903 | return; |
| 904 | |
| 905 | // AAAA, IN |
| 906 | if ((responseType == 28) && (responseClass == 1) && (length == 16)) |
| 907 | { |
| 908 | std::array<std::uint8_t, 16> bytes{}; |
| 909 | std::memcpy(bytes.data(), data, 16); |
| 910 | if (std::any_of(bytes.begin(), bytes.end(), [](auto b) { return b != 0; })) |
| 911 | address.emplace(bytes); |
| 912 | } |
| 913 | |
| 914 | // A, IN |
| 915 | if ((responseType == 1) && (responseClass == 1)) |
| 916 | { |
| 917 | if (const auto bytes = readU32(data, data + length); bytes != 0) |
| 918 | address.emplace(bytes); |
| 919 | } |
| 920 | |
| 921 | // TXT, IN or CH |
| 922 | if ((responseType == 16) && ((responseClass == 1) || (responseClass == 3))) |
| 923 | { |
| 924 | if (const auto txtLength = readU8(data, data + length); (std::size_t{1} + txtLength) == length) |
| 925 | { |
| 926 | std::string str(data, txtLength); |
| 927 | str.erase(std::remove(str.begin(), str.end(), '"'), str.end()); // Remove quotes if any |
| 928 | if (const auto ipAddress = IpAddress::fromString(str); ipAddress) |
| 929 | address.emplace(*ipAddress); |
| 930 | } |
| 931 | } |
| 932 | }; |
| 933 | |
| 934 | const auto filter = [&type](auto filteredAddresses) |
| 935 | { |
| 936 | filteredAddresses.erase(std::remove_if(filteredAddresses.begin(), |
| 937 | filteredAddresses.end(), |
| 938 | [&type](const auto& filteredAddress) |
| 939 | { return filteredAddress.getType() != type; }), |
| 940 | filteredAddresses.end()); |
| 941 | return filteredAddresses; |
| 942 | }; |
| 943 | |
| 944 | const auto addressQueryType = static_cast<std::uint16_t>((type == IpAddress::Type::IpV4) ? 1 : 28); // A/AAAA |
| 945 | |
| 946 | std::vector<Query> queries; |
| 947 | |
| 948 | // Attempt to query Cloudflare and OpenDNS first since those don't require their server hostnames to be looked up and resolved |
| 949 | queries.emplace_back(Query{filter(cloudflareAddresses), 16, 3, "whoami.cloudflare"}); // TXT, CH |
| 950 | queries.emplace_back(Query{filter(opendnsAddresses), addressQueryType, 1, "myip.opendns.com"}); // A/AAAA, IN |
| 951 |