Search code examples
c++linuxdnsgetaddrinfogetaddrinfo-a

gai_cancel() takes a really long time to succeed


I am trying to look up domain asynchronously in c++. The reason is I want to be able to effectively add a time out period in case the system can't look up the domain. I came across the getaddrinfo_a() command so I decided to give it a try. However cancelling any dns look up that will not succeed (such as when there is no internet connection) will never take less than 20 seconds on my machine. Here is a simple example of this:

#include <iostream>
#include <netdb.h>
#include <string.h>
#include <unistd.h>

using namespace std;

int main() {


        int ret;
        gaicb* reqs;

        reqs = new gaicb;

        memset(reqs, 0, sizeof (gaicb));
        reqs->ar_name = "google.com";

        ret = getaddrinfo_a(GAI_NOWAIT, &reqs, 1, NULL);
        if (ret != 0) {
                cout << "something went wrong" << endl;
                return false;
        }

        while (1) {
                ret = gai_cancel(reqs);
                if (ret == EAI_CANCELED || ret == EAI_ALLDONE) {
                        break;
                }
                usleep(100 * 1000); //sleep for 100 milliseconds
        }

        cout << "finished cancellation" << endl;


        return 0;
}

Compile like this:

g++ -o main main.cpp -lanl

Then run the command on your linux based system without an internet connection like so:

time ./main

You will find that the program always takes about 20 seconds to close. Any help would be greatly appreciated!


Solution

  • Okay the answer is to not use getaddrinfo_a(3) if you need asynchronous dns lookup shorter than 20 seconds. Also per Martin Sustrik's second comment on http://sourceware-org.1504.n7.nabble.com/getaddrinfo-a-memory-leaks-td233794.html as well as my experience here it looks like getaddrinfo_a is fairly experimental and shouldn't be used anyways. I actually ended up using dns.c (https://github.com/wahern/dns). Here's a sample for anyone looking:

    #include "dns.c"
    
    uint8_t getaddrinfo_k(
    uint32_t* ip_addr, const char* dns_address, uint32_t timeout) {
        int32_t                 rc;
        struct addrinfo         hints;
        struct dns_resolv_conf* dns_conf;
        struct dns_hosts*       dns_hosts;
        struct dns_hints*       dns_hints;
        struct dns_resolver*    resolver;
        struct dns_addrinfo*    ai;
        struct addrinfo*        it;
    
        dns_conf    = dns_resconf_local(&rc);
        assert(dns_conf);
        dns_hosts   = dns_hosts_local(&rc);
        assert(dns_hosts);
        dns_hints   = dns_hints_local(dns_conf, &rc);
        assert(dns_hints);
        resolver    = dns_res_open(
        dns_conf, dns_hosts, dns_hints, NULL, dns_opts(), &rc);
        assert(resolver);
        it          = NULL;
    
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = PF_INET;
    
        ai = dns_ai_open(dns_address, "80", DNS_T_A, &hints, resolver, &rc);
        assert(ai);
    
        while (timeout-- > 0) {
            rc = dns_ai_nextent(&it, ai);
            switch(rc) {
                    case 0:
                            *ip_addr = (
                            (struct sockaddr_in *) it->ai_addr)->sin_addr.s_addr;
                            free(it);
                            goto exit_loop;
                    case EAGAIN:
                            rc = dns_ai_poll(ai, 1);
                            assert(rc == 0);
                            break;
                    default:
                            goto exit_loop;
            }
            usleep(100 * 1000);
        }
    
        exit_loop:
    
        dns_ai_close        (ai);
        dns_res_close       (resolver);
        dns_hints_close     (dns_hints);
        dns_hosts_close     (dns_hosts);
        dns_resconf_close   (dns_conf);
    
        switch(rc) {
            case 0:
                return 1;
            case EAGAIN:
                printf("DNS_WRAPPER: timed out\n");
                break;
            case ENOENT:
                printf("DNS_WRAPPER: file doesn't exist\n");
                break;
            default:
                printf("DNS_WRAPPER: unknown error\n");
                break;
        }
    
        return 0;
    }