Как отличный асинхронный программный фреймворк, workflow помогает пользователям управлять множеством деталей, включая разрешение доменных имён. Поэтому в большинстве случаев пользователи не должны беспокоиться о том, как запрашивать сервисы DNS. Как и остальные модули в workflow, модуль разрешения DNS также является полным и элегантным решением. Если вам требуется реализовать задачи разрешения доменных имён, то WFDnsClient и WFDnsTask являются идеальным выбором.
about-dns предоставляет информацию о конфигурации параметров DNS, а данная статья сосредоточена на создании задач DNS и получении результатов разрешения.
WFDnsClient представляет собой высокоуровневый интерфейс, который работает аналогично системному файлу resolv.conf
. Он автоматически управляет повторными попытками, объединением списков search, циклическим переключением серверов и другими функциями. Инициализация WFDnsClient может происходить несколькими способами, при успешной инициализации функция вернёт 0.
client.init("8.8.8.8");
// или
client.init("dns://8.8.8.8/");
client.init("[2402:4e00::]:53");
client.init("dnss://120.53.53.53/");
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. Есть два простых интерфейса для получения информации из этого ответа.
Эта функция аналогична системной функции 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);
}
Функция 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);
}
}
В зависимости от типа запроса, данные в ответе могут иметь различные формы. Частыми видами данных являются:
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 )