1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/sogou-workflow

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
tutorial-17-dns_cli.md 11 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 01.03.2025 10:20 552780a

Использование workflow для запроса DNS

Как отличный асинхронный программный фреймворк, workflow помогает пользователям управлять множеством деталей, включая разрешение доменных имён. Поэтому в большинстве случаев пользователи не должны беспокоиться о том, как запрашивать сервисы DNS. Как и остальные модули в workflow, модуль разрешения DNS также является полным и элегантным решением. Если вам требуется реализовать задачи разрешения доменных имён, то WFDnsClient и WFDnsTask являются идеальным выбором.

about-dns предоставляет информацию о конфигурации параметров DNS, а данная статья сосредоточена на создании задач DNS и получении результатов разрешения.

tutorial-17-dns_cli.cc

Создание задач с использованием WFDnsClient

WFDnsClient представляет собой высокоуровневый интерфейс, который работает аналогично системному файлу resolv.conf. Он автоматически управляет повторными попытками, объединением списков search, циклическим переключением серверов и другими функциями. Инициализация WFDnsClient может происходить несколькими способами, при успешной инициализации функция вернёт 0.

  • Инициализация с помощью одного IPv4 адреса DNS, следующие два варианта эквивалентны
client.init("8.8.8.8");
// или
client.init("dns://8.8.8.8/");
  • Инициализация с помощью одного IPv6 адреса DNS
client.init("[2402:4e00::]:53");
  • Инициализация с использованием адреса DNS через TLS (DoT), порт по умолчанию равен 853
client.init("dnss://120.53.53.53/");
  • Инициализация с использованием нескольких DNS адресов, разделённых запятой
client.init("dns://8.8.8.8/,119.29.29.29");
  • Инициализация с явным указанием стратегии повторных попыток, пример эквивалентен следующему описанию в resolv.conf
nameserver 8.8.8.8
search sogou.com tencent.com
options nodts:1 attempts:2 rotate
client.init("8.8.8.8", "sogou.com,tencent.com", 1, 2, true);

Задачи, созданные с использованием WFDnsClient, по умолчанию представляют запрос типа DNS_TYPE_A, класса DNS_CLASS_IN и уже имеют установленный рекурсивный флаг task->get_req()->set_rd(1). После того, как вы узнаете способы инициализации WFDnsClient, можно отправить DNS запрос всего за восемь строк кода:

int main()
{
    WFDnsClient client;
    client.init("8.8.8.8");

    WFDnsTask *task = client.create_dns_task("www.sogou.com", dns_callback);
    task->start();

    pause();

    client.deinit();
    return 0;
}

Создание задач с использованием фабричных функций

Если вам не нужны дополнительные возможности WFDnsClient или вы хотите самостоятельно организовать стратегию повторных попыток, вы можете использовать фабричные функции для создания задач.

При использовании фабричных функций для создания задач, вы можете указать имя доменного имени в пути URL. По умолчанию задачи создаются с типом запроса DNS_TYPE_A и классом DNS_CLASS_IN. Эти значения могут быть изменены после создания задачи с помощью методов set_question_type и set_question_class.

std::string url = "dns://8.8.8.8/www.sogou.com";
WFDnsTask *task = WFTaskFactory::create_dns_task(url, 0, dns_callback);
protocol::DnsRequest *req = task->get_req();
req->set_rd(1);
req->set_question_type(DNS_TYPE_AAAA);
req->set_question_class(DNS_CLASS_IN);

Если имя доменного имени не указано при создании задачи (по умолчанию задача будет разрешать корневое имя '.'), его можно установить после создания задачи с помощью метода set_question.

std::string url = "dns://8.8.8.8/";
WFDnsTask *task = WFTaskFactory::create_dns_task(url, 0, dns_callback);
protocol::DnsRequest *req = task->get_req();
req->set_rd(1);
req->set_question("www.zhihu.com", DNS_TYPE_AAAA, DNS_CLASS_IN);

Получение результатов с помощью инструментов

Успешный запрос DNS обычно возвращает полный ответ DNS. Есть два простых интерфейса для получения информации из этого ответа.

DnsUtil::getaddrinfo

Эта функция аналогична системной функции getaddrinfo. При успешном вызове она возвращает ноль и успешно получает набор struct addrinfo. В случае ошибки она возвращает код ошибки типа EAI_*. Успешный вызов этой функции всегда следует за тем, чтобы освободить ресурсы с помощью DnsUtil::freeaddrinfo.

void dns_callback(WFDnsTask *task)
{
    // игнорируем обработку состояний ошибок

    struct addrinfo *res;
    protocol::DnsResponse *resp = task->get_resp();
    int ret = protocol::DnsUtil::getaddrinfo(resp, 80, &res);
    // игнорируем проверку ret == 0

    char ip_str[INET6_ADDRSTRLEN + 1] = { 0 };
    for (struct addrinfo *p = res; p; p = p->ai_next)
    {
        void *addr = nullptr;
        if (p->ai_family == AF_INET)
            addr = &((struct sockaddr_in *)p->ai_addr)->sin_addr;
        else if (p->ai_family == AF_INET6)
            addr = &((struct sockaddr_in6 *)p->ai_addr)->sin6_addr;

        if (addr)
        {
            inet_ntop(p->ai_family, addr, ip_str, p->ai_addrlen);
            printf("ip:%s\n", ip_str);
        }
    }

    protocol::DnsUtil::freeaddrinfo(res);
}

DnsResultCursor

Функция DnsUtil::getaddrinfo обычно используется для получения IPv4 и IPv6 адресов, тогда как использование DnsResultCursor позволяет полностью пройти по всему ответу DNS. Ответ DNS состоит из трёх областей: answer, authority и additional. Обычно основное содержимое находится в области answer. Здесь мы проверяем наличие данных в каждой из этих областей и используем show_result для вывода каждого результата.

void dns_callback(WFDnsTask *task)
{
    // игнорируем обработку состояний ошибок

    protocol::DnsResponse *resp = task->get_resp();
    protocol::DnsResultCursor cursor(resp);

    if(resp->get_ancount() > 0)
    {
        cursor.reset_answer_cursor();
        printf(";; ANSWER SECTION:\n");
        show_result(cursor);
    }
    if(resp->get_nscount() > 0)
    {
        cursor.reset_authority_cursor();
        printf(";; AUTHORITY SECTION\n");
        show_result(cursor);
    }
    if(resp->get_arcount() > 0)
    {
        cursor.reset_additional_cursor();
        printf(";; ADDITIONAL SECTION\n");
        show_result(cursor);
    }
}

В зависимости от типа запроса, данные в ответе могут иметь различные формы. Частыми видами данных являются:

  • DNS_TYPE_A: IPv4 адрес
  • DNS_TYPE_AAAA: IPv6 адрес
  • DNS_TYPE_NS: авторитетный DNS сервер для данного доменного имени
  • DNS_TYPE_CNAME: каноническое имя для данного доменного имени
void show_result(protocol::DnsResultCursor &cursor)
{
    char information[1024];
    const char *info;
    struct dns_record *record;
    struct dns_record_soa *soa;
    struct dns_record_srv *srv;
    struct dns_record_mx *mx;
}    while (cursor.next(&record)) 
    { 
        switch (record->type) 
        { 
        case DNS_TYPE_A: 
            info = inet_ntop(AF_INET, record->rdata, information, 64); 
            break; 
        case DNS_TYPE_AAAA: 
            info = inet_ntop(AF_INET6, record->rdata, information, 64); 
            break; 
        case DNS_TYPE_NS: 
        case DNS_TYPE_CNAME: 
        case DNS_TYPE_PTR: 
            info = (const char *)(record->rdata); 
            break; 
        case DNS_TYPE_SOA: 
            soa = (struct dns_record_soa *)(record->rdata); 
            sprintf(information, "%s %s %u %d %d %d %u", 
                soa->mname, soa->rname, soa->serial, soa->refresh, 
                soa->retry, soa->expire, soa->minimum 
            ); 
            info = information; 
            break; 
        case DNS_TYPE_SRV: 
            srv = (struct dns_record_srv *)(record->rdata); 
            sprintf(information, "%u %u %u %s", 
                srv->priority, srv->weight, srv->port, srv->target 
            ); 
            info = information; 
            break; 
        case DNS_TYPE_MX: 
            mx = (struct dns_record_mx *)(record->rdata); 
            sprintf(information, "%d %s", mx->preference, mx->exchange); 
            info = information; 
            break; 
        default: 
            info = "Неизвестно"; 
        } 



        printf("%s\t%d\t%s\t%s\t%s\n", 
            record->name, record->ttl, 
            dns_class2str(record->rclass), 
            dns_type2str(record->type), 
            info 
        ); 
    } 
    printf("\n"); 
} 

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/sogou-workflow.git
git@api.gitlife.ru:oschina-mirror/sogou-workflow.git
oschina-mirror
sogou-workflow
sogou-workflow
master