LINUX.ORG.RU

Вопрос по применению c-ares

 


0

1

Всем привет. Есть такой пример кода:

/*
 *  gcc -o c-ares_test c-ares.c -static -I/usr/pkg/include -L/usr/pkg/lib -lcares_static -lm -std=c11
 *  gcc -o c-ares_test c-ares.c -I/usr/pkg/include -L/usr/pkg/lib -lcares -lm -std=c11
*/

#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ares.h>

void dns_callback (void* arg, int status, int timeouts, struct hostent* host)
{
    if(status == ARES_SUCCESS) {
        //memcpy(host1, &host, sizeof(host));
        printf("%s\n", host->h_name);
        char ip[INET6_ADDRSTRLEN];
        for (int i = 0; host->h_addr_list[i]; ++i) {
            inet_ntop(host->h_addrtype, host->h_addr_list[i], ip, sizeof(ip));
            printf("%s\n", ip);
        }
    }
    else {
        printf("lookup failed: %s\n", ares_strerror(status));
    }
}

static void ares_loop(ares_channel channel) {
    int nfds, count;
    fd_set readers, writers;
    struct timeval tv, *tvp;
    while (1) {
        FD_ZERO(&readers);
        FD_ZERO(&writers);
        nfds = ares_fds(channel, &readers, &writers);
        if (nfds == 0)break;
        tvp = ares_timeout(channel, NULL, &tv);
        count = select(nfds, &readers, &writers, NULL, tvp);
        ares_process(channel, &readers, &writers);
     }

}

int main(int argc, char **argv) {
    int status;
    ares_channel channel;

    status = ares_library_init(ARES_LIB_INIT_ALL);
    if (status != ARES_SUCCESS){
        printf("ares_library_init: %s\n", ares_strerror(status));
        return 1;
    }
    if((status = ares_init(&channel)) != ARES_SUCCESS) {
        printf("ares feiled:  %s\n", ares_strerror(status));
        return 1;
    }
    ares_gethostbyname(channel, "google.com", AF_INET6, dns_callback, NULL);
    ares_gethostbyname(channel, "google.com", AF_INET, dns_callback, NULL);
    ares_loop(channel);
    ares_destroy(channel);
    ares_library_cleanup();
     
    return 0;
}

Как правильно вытащить результаты днс резольвинга в функцию main для дальнейшего использования в программе? Подозреваю, что нужно прокинуть указатель на структуру struct hostent* в callback функцию через void *arg, создать копию struct hostent* host, скопировать в нее данные и связать с указателем arg. Но так у меня не получилось - может не знаю как правильно, а может вообще не так надо... В общем, нужен ликбез по использованию библиотеки.



Последнее исправление: sirius (всего исправлений: 1)

В arg можно прокидывать любой указатель. главное чтобы на момент конца ресолвинга этот указатель указывал на всё ещё актуальную память.

mittorn ★★★★★
()

Перенеси char ip[INET6_ADDRSTRLEN]; в main и через arg передавай указатель в колбек. Используй его при вызове inet_ntop.

Мимопроходящий c++ боярин.

ox55ff ★★★★★
()

mittorn, ox55ff - спасибо за ответы. Получилось вытащить результат inet_ntop как было предложено. Уже кое-что… Осталось разобраться, как вытаскивать всю структуру hostent. Хотя я кажется понял, в чем была проблема - необходимо глубокое копирование struct hostent. Я так не делал и ожидаемо получал сегфолт(т.к. при завершени callback функции память, выделенная struct hostent* host освобождается).

sirius
() автор топика
Ответ на: комментарий от sirius

получал сегфолт

В поиске таких ошибок может помочь AddressSanitizer. Он уже довольно давно встроен в GCC. Просто добавь в параметры компиляции -fsanitize=address.

вытаскивать всю структуру

Возможно, ты просто используешь библиотеку не по назначению. Она же для асинхронного разрешения имён. Предполагается, что ты будешь выполнять работу в колбеках, а не пытаться сделать из асинхронной модели синхронную. Какой смысл в асинхронности, если ты в основном потоке ты всё равно кроме ожидания ответа ничего не делаешь? Проще будет getaddrinfo или gethostbyname использовать, они уже синхронные.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

-fsanitize=address

Спасибо, добавлю.

Возможно, ты просто используешь библиотеку не по назначению.

Ага скорее всего так и есть, но как ее правильно использовать, пока не понимаю. Куда-нибудь сохранить результат в коллбэк функции еще ладно, но всю логику приложения туда пихать на мой взгляд тоже не решение.

Проще будет getaddrinfo или gethostbyname

Ага, проще. Но при их использовании, например при сборке с glibc в debian(на BSD все «ок») есть проблемы со статической сборкой. Ну и тяжелые они. Помимо днс еще много где смотрят.

sirius
() автор топика