results from Boost.Asio resolver differ
Asked Answered
C

3

6

I have a canned reproducer invoking boost::asio::ip::tcp::resolver::resolve() on localhost once every 5 seconds. It counts the number of endpoints returned and compares that value against the previous iteration.

#include <boost/asio.hpp>

#include <iostream>

int main(int argc, char *argv[])
{
    if ( argc < 3 ) {
        std::cerr << argv[0] << " host port" << std::endl;
        exit( EXIT_FAILURE );
    }
    const char* host = argv[1];
    const char* service = argv[2];

    boost::asio::io_service io_service;
    boost::asio::ip::tcp::resolver resolver( io_service );

    size_t previous = 0;
    while ( true ) {
        boost::asio::ip::tcp::resolver::iterator i(
                resolver.resolve(
                    boost::asio::ip::tcp::resolver::query( host, service )
                    )
                );
        size_t count( 0 );
        while ( i != boost::asio::ip::tcp::resolver::iterator() ) {
            std::cout << i->endpoint() << std::endl;
            ++i;
            ++count;
        }

        std::cout << "got " << count << " addresses" << std::endl;
        if ( previous == 0 ) {
            previous = count;
        }
        assert( count == previous );

        sleep( 5 );
    }
}

sample session

~> time ./addrinfo_asio localhost 80

...

127.0.0.1:80
got 1 addresses
[::1]:80
127.0.0.1:80
got 2 addresses
addrinfo_asio: addrinfo_asio.cc:35: int main(int, char**): Assertion `count == previous' failed.
Aborted (core dumped)

real    216m20.515s
user    0m0.181s
sys     0m0.193s
~> 

You can see it found one endpoint (127.0.0.1:80) for about 3.5 hours, then found two (127.0.0.1:80 and [::1]:80). I'm wondering

  1. why the endpoint count changes from one, to two?
  2. what could cause it?

Resolving both ipv4 and ipv6 addresses is intentional, I do not want to limit the query to just ipv4. I realize this behavior is likely not specific to asio, I also have a reproducer invoking getaddrinfo directly that exhibits the same behavior. My platform is ppc64 RHEL 6.2 if that is relevant. I have not tried reproducing elsewhere.

Confront answered 12/6, 2012 at 4:1 Comment(6)
The ::1 address is the IPv6 localhost address. Maybe it takes so long time for the OS to realize it has IPv6 enabled?Christogram
what is the os you are running on ?Cental
@Cental see the last sentence of the question, ppc64 RHEL 6.2Confront
@Sam Miller apologies I missed thatCental
@ Sam Miller were you on an IPv4 only network ? I am just throwing my thoughts out hereCental
the network is both ipv6 and ipv4Confront
M
3

You can limit resolver to IPv4 only:
ip::tcp::resolver::query(ip::tcp::v4(), host, service)

Melancholia answered 12/6, 2012 at 6:36 Comment(4)
AFAIU the question is not how to limit the addresses returned by getaddrinfo, but why the IPv6 address shows up after some time period has elapsed.Krupp
Well, I was under impression that the topic-starter was unaware of the fact that his resolver query contained both ipv4 and ipv6, so he got undesired ipv6 queries. On the other hand, it's well known that ipv6 DNS queries might be painfully slow...Melancholia
@IgorR. I am aware the ::1 address is ipv6-localhost, I've edited my question to reflect that. I do not want to limit my resolve queries to just ipv4.Confront
Thank you so much for this. I have been experiencing very slow connect times (seconds) even when testing on my local machine connecting to another local server. It turns out if I restrict the search to IPv4 it goes really quickly. This is using synchronous boost. I'm running my code under very specific conditions so ipv4 and synchronous do not matter to me. Perhaps this will help someone else in the future.Trypanosomiasis
L
2

If you take a look at Chris Kohlhoff's (the author of asio) presentation on ipv6 and asio at boost con 2011, he does the following trick.

He reorders the list of endpoints so the ipv4 endpoints come first in the list, with the comment "reality check: IPv6 is unlikely to be available yet".

His example code works in the version of asio released in boost 1.47, but it no longer works in the version I am using (asio standalone 1.18.0) due to his habit of constantly changing the asio implementation as the C++ evolves.

Anyway, the following code works in asio 1.18.0 which is included in boost 1.74

auto resolverResults(mResolver.resolve(hostName, serviceName));
std::transform(resolverResults.begin(), resolverResults.end(), std::back_inserter(endpoints), [](const asio::ip::basic_resolver_entry<asio::ip::tcp>& entry) { return entry.endpoint(); });
std::stable_partition(endpoints.begin(), endpoints.end(), [](const asio::ip::tcp::endpoint& endpoint) { return endpoint.protocol() == asio::ip::tcp::v4(); });
auto endpointsWithIP4First = asio::ip::tcp::resolver::results_type::create(endpoints.begin(), endpoints.end(), hostName, serviceName);
Lagasse answered 8/10, 2020 at 23:3 Comment(0)
D
1

Well I'm no boost expert, but a quick browse tells me it seems to be using AI_ADDRCONFIG by default (which is good, it should almost always be used). With that flag it will only return IPv6 addresses if you have at least one global routable IPv6 address configured. Perhaps your IPv6 connection is not always available?

Demagogy answered 9/7, 2012 at 22:28 Comment(1)
Windows-specific caveat: AI_ADDRCONFIG can cause lookups for localhost to fail. See Boost ticket 8503 for details.Lolanthe

© 2022 - 2024 — McMap. All rights reserved.