Search code examples
c++linuxboostboost-asioip-address

ipv4 and ipv6 from any valid address


I'm trying to get both the ipv4 and ipv6 address from any string address, be it by ipv4, ipv6, or DNS address.

I can create my own function to do so, but I'm trying to take expert advice and utilize built-in capabilities.

Is there a way to input an address string of any format, and have both ipv4 and ipv6 boost addresses returned?


Solution

  • Getting an address from the DNS name involves... querying a naming server (DNS!). If you want to enumerate the results, use a resolver in asio:

    Simple example:

    #include <boost/asio.hpp>
    #include <boost/function_output_iterator.hpp>
    #include <set>
    
    using boost::asio::ip::address;
    
    std::set<address> unique_endpoints(std::string const& ip)
    {
        using resolver = boost::asio::ip::tcp::resolver;
        boost::asio::io_service ios; // TODO use existing service / resolver
        resolver r(ios);
    
        std::set<address> unique;
        for (auto it = r.resolve({ip, ""}); it != resolver::iterator {}; ++it)
        {
            //std::cout << "Resolved: " << it->host_name() << " -> " << it->endpoint() << " " << it->service_name() << "\n";
            address a = it->endpoint().address();
            if (a.is_v4())
                unique.insert(boost::asio::ip::address_v6::v4_mapped(a.to_v4()));
            else
                unique.insert(a);
        }
    
        return unique;
    }
    
    template <typename S>
    bool endpoints_overlap(S const& a, S const& b)
    {
        bool matching_found = false;
    
        std::set_intersection(
                a.begin(), a.end(), b.begin(), b.end(),
                boost::make_function_output_iterator([&](address const&) { matching_found = true; }));
    
        return matching_found;
    }
    
    int main()
    {
        auto h = unique_endpoints("bbs2.fritz.box");
        auto a = unique_endpoints("192.168.2.111");
        auto b = unique_endpoints("::ffff:192.168.2.111");
        auto c = unique_endpoints("::ffff:c0a8:026f");
    
        assert(endpoints_overlap(a, b));
        assert(endpoints_overlap(a, c));
        assert(endpoints_overlap(b, c));
    
        assert(endpoints_overlap(h, a));
        assert(endpoints_overlap(h, b));
        assert(endpoints_overlap(h, c));
    }
    

    Note that this test will decide that endpoints overlap when one of the DNS responses matches. This might not always be true in a cluster setting (? no expert there) and you might also want to detect broadcast addresses before using this algorithm (not tested).

    Note also, I don't think there's a way to detect equivalence of actual hosts (meaning, if a host has several physical/logical NICs, they will appear as separate servers on the transport level).

    Finally, in a real-world application you will want to do the resolving asynchronously (use async_resolve)