zephyr/subsys/net/lib/dns/resolve.c

1010 lines
23 KiB
C

/** @file
* @brief DNS resolve API
*
* An API for applications to do DNS query.
*/
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_dns_resolve, CONFIG_DNS_RESOLVER_LOG_LEVEL);
#include <zephyr/types.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <random/rand32.h>
#include <net/net_ip.h>
#include <net/net_pkt.h>
#include <net/dns_resolve.h>
#include "dns_pack.h"
#define DNS_SERVER_COUNT CONFIG_DNS_RESOLVER_MAX_SERVERS
#define SERVER_COUNT (DNS_SERVER_COUNT + DNS_MAX_MCAST_SERVERS)
#define MDNS_IPV4_ADDR "224.0.0.251:5353"
#define MDNS_IPV6_ADDR "[ff02::fb]:5353"
#define LLMNR_IPV4_ADDR "224.0.0.252:5355"
#define LLMNR_IPV6_ADDR "[ff02::1:3]:5355"
#define DNS_BUF_TIMEOUT 500 /* ms */
/* RFC 1035, 3.1. Name space definitions
* To simplify implementations, the total length of a domain name (i.e.,
* label octets and label length octets) is restricted to 255 octets or
* less.
*/
#define DNS_MAX_NAME_LEN 255
#define DNS_QUERY_MAX_SIZE (DNS_MSG_HEADER_SIZE + DNS_MAX_NAME_LEN + \
DNS_QTYPE_LEN + DNS_QCLASS_LEN)
/* This value is recommended by RFC 1035 */
#define DNS_RESOLVER_MAX_BUF_SIZE 512
#define DNS_RESOLVER_MIN_BUF 1
#define DNS_RESOLVER_BUF_CTR (DNS_RESOLVER_MIN_BUF + \
CONFIG_DNS_RESOLVER_ADDITIONAL_BUF_CTR)
/* Compressed RR uses a pointer to another RR. So, min size is 12 bytes without
* considering RR payload.
* See https://tools.ietf.org/html/rfc1035#section-4.1.4
*/
#define DNS_ANSWER_PTR_LEN 12
/* See dns_unpack_answer, and also see:
* https://tools.ietf.org/html/rfc1035#section-4.1.2
*/
#define DNS_QUERY_POS 0x0c
#define DNS_IPV4_LEN sizeof(struct in_addr)
#define DNS_IPV6_LEN sizeof(struct in6_addr)
NET_BUF_POOL_DEFINE(dns_msg_pool, DNS_RESOLVER_BUF_CTR,
DNS_RESOLVER_MAX_BUF_SIZE, 0, NULL);
NET_BUF_POOL_DEFINE(dns_qname_pool, DNS_RESOLVER_BUF_CTR, DNS_MAX_NAME_LEN,
0, NULL);
static struct dns_resolve_context dns_default_ctx;
static int dns_write(struct dns_resolve_context *ctx,
int server_idx,
int query_idx,
struct net_buf *dns_data,
struct net_buf *dns_qname,
int hop_limit);
static bool server_is_mdns(sa_family_t family, struct sockaddr *addr)
{
if (family == AF_INET) {
if (net_ipv4_is_addr_mcast(&net_sin(addr)->sin_addr) &&
net_sin(addr)->sin_addr.s4_addr[3] == 251U) {
return true;
}
return false;
}
if (family == AF_INET6) {
if (net_ipv6_is_addr_mcast(&net_sin6(addr)->sin6_addr) &&
net_sin6(addr)->sin6_addr.s6_addr[15] == 0xfb) {
return true;
}
return false;
}
return false;
}
static bool server_is_llmnr(sa_family_t family, struct sockaddr *addr)
{
if (family == AF_INET) {
if (net_ipv4_is_addr_mcast(&net_sin(addr)->sin_addr) &&
net_sin(addr)->sin_addr.s4_addr[3] == 252U) {
return true;
}
return false;
}
if (family == AF_INET6) {
if (net_ipv6_is_addr_mcast(&net_sin6(addr)->sin6_addr) &&
net_sin6(addr)->sin6_addr.s6_addr[15] == 0x03) {
return true;
}
return false;
}
return false;
}
static void dns_postprocess_server(struct dns_resolve_context *ctx, int idx)
{
struct sockaddr *addr = &ctx->servers[idx].dns_server;
if (addr->sa_family == AF_INET) {
ctx->servers[idx].is_mdns = server_is_mdns(AF_INET, addr);
if (!ctx->servers[idx].is_mdns) {
ctx->servers[idx].is_llmnr =
server_is_llmnr(AF_INET, addr);
}
if (net_sin(addr)->sin_port == 0U) {
if (IS_ENABLED(CONFIG_MDNS_RESOLVER) &&
ctx->servers[idx].is_mdns) {
/* We only use 5353 as a default port
* if mDNS support is enabled. User can
* override this by defining the port
* in config file.
*/
net_sin(addr)->sin_port = htons(5353);
} else if (IS_ENABLED(CONFIG_LLMNR_RESOLVER) &&
ctx->servers[idx].is_llmnr) {
/* We only use 5355 as a default port
* if LLMNR support is enabled. User can
* override this by defining the port
* in config file.
*/
net_sin(addr)->sin_port = htons(5355);
} else {
net_sin(addr)->sin_port = htons(53);
}
}
} else {
ctx->servers[idx].is_mdns = server_is_mdns(AF_INET6, addr);
if (!ctx->servers[idx].is_mdns) {
ctx->servers[idx].is_llmnr =
server_is_llmnr(AF_INET6, addr);
}
if (net_sin6(addr)->sin6_port == 0U) {
if (IS_ENABLED(CONFIG_MDNS_RESOLVER) &&
ctx->servers[idx].is_mdns) {
net_sin6(addr)->sin6_port = htons(5353);
} else if (IS_ENABLED(CONFIG_LLMNR_RESOLVER) &&
ctx->servers[idx].is_llmnr) {
net_sin6(addr)->sin6_port = htons(5355);
} else {
net_sin6(addr)->sin6_port = htons(53);
}
}
}
}
int dns_resolve_init(struct dns_resolve_context *ctx, const char *servers[],
const struct sockaddr *servers_sa[])
{
#if defined(CONFIG_NET_IPV6)
struct sockaddr_in6 local_addr6 = {
.sin6_family = AF_INET6,
.sin6_port = 0,
};
#endif
#if defined(CONFIG_NET_IPV4)
struct sockaddr_in local_addr4 = {
.sin_family = AF_INET,
.sin_port = 0,
};
#endif
struct sockaddr *local_addr = NULL;
socklen_t addr_len = 0;
int i = 0, idx = 0;
int ret, count;
if (!ctx) {
return -ENOENT;
}
if (ctx->is_used) {
return -ENOTEMPTY;
}
(void)memset(ctx, 0, sizeof(*ctx));
if (servers) {
for (i = 0; idx < SERVER_COUNT && servers[i]; i++) {
struct sockaddr *addr = &ctx->servers[idx].dns_server;
(void)memset(addr, 0, sizeof(*addr));
ret = net_ipaddr_parse(servers[i], strlen(servers[i]),
addr);
if (!ret) {
continue;
}
dns_postprocess_server(ctx, idx);
NET_DBG("[%d] %s", i, log_strdup(servers[i]));
idx++;
}
}
if (servers_sa) {
for (i = 0; idx < SERVER_COUNT && servers_sa[i]; i++) {
memcpy(&ctx->servers[idx].dns_server, servers_sa[i],
sizeof(ctx->servers[idx].dns_server));
dns_postprocess_server(ctx, idx);
idx++;
}
}
for (i = 0, count = 0;
i < SERVER_COUNT && ctx->servers[i].dns_server.sa_family; i++) {
if (ctx->servers[i].dns_server.sa_family == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
local_addr = (struct sockaddr *)&local_addr6;
addr_len = sizeof(struct sockaddr_in6);
#else
continue;
#endif
}
if (ctx->servers[i].dns_server.sa_family == AF_INET) {
#if defined(CONFIG_NET_IPV4)
local_addr = (struct sockaddr *)&local_addr4;
addr_len = sizeof(struct sockaddr_in);
#else
continue;
#endif
}
if (!local_addr) {
NET_DBG("Local address not set");
return -EAFNOSUPPORT;
}
ret = net_context_get(ctx->servers[i].dns_server.sa_family,
SOCK_DGRAM, IPPROTO_UDP,
&ctx->servers[i].net_ctx);
if (ret < 0) {
NET_DBG("Cannot get net_context (%d)", ret);
return ret;
}
ret = net_context_bind(ctx->servers[i].net_ctx,
local_addr, addr_len);
if (ret < 0) {
NET_DBG("Cannot bind DNS context (%d)", ret);
return ret;
}
count++;
}
if (count == 0) {
/* No servers defined */
NET_DBG("No DNS servers defined.");
return -EINVAL;
}
ctx->is_used = true;
ctx->buf_timeout = DNS_BUF_TIMEOUT;
return 0;
}
static inline int get_cb_slot(struct dns_resolve_context *ctx)
{
int i;
for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
if (!ctx->queries[i].cb) {
return i;
}
}
return -ENOENT;
}
static inline int get_slot_by_id(struct dns_resolve_context *ctx,
u16_t dns_id)
{
int i;
for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
if (ctx->queries[i].cb && ctx->queries[i].id == dns_id) {
return i;
}
}
return -ENOENT;
}
static int dns_read(struct dns_resolve_context *ctx,
struct net_pkt *pkt,
struct net_buf *dns_data,
u16_t *dns_id,
struct net_buf *dns_cname)
{
struct dns_addrinfo info = { 0 };
/* Helper struct to track the dns msg received from the server */
struct dns_msg_t dns_msg;
u32_t ttl; /* RR ttl, so far it is not passed to caller */
u8_t *src, *addr;
int address_size;
/* index that points to the current answer being analyzed */
int answer_ptr;
int data_len;
int items;
int ret;
int server_idx, query_idx;
data_len = MIN(net_pkt_remaining_data(pkt), DNS_RESOLVER_MAX_BUF_SIZE);
/* TODO: Instead of this temporary copy, just use the net_pkt directly.
*/
ret = net_pkt_read(pkt, dns_data->data, data_len);
if (ret < 0) {
ret = DNS_EAI_MEMORY;
goto quit;
}
dns_msg.msg = dns_data->data;
dns_msg.msg_size = data_len;
/* The dns_unpack_response_header() has design flaw as it expects
* dns id to be given instead of returning the id to the caller.
* In our case we would like to get it returned instead so that we
* can match the DNS query that we sent. When dns_read() is called,
* we do not know what the DNS id is yet.
*/
*dns_id = dns_unpack_header_id(dns_msg.msg);
query_idx = get_slot_by_id(ctx, *dns_id);
if (query_idx < 0) {
ret = DNS_EAI_SYSTEM;
goto quit;
}
if (dns_header_rcode(dns_msg.msg) == DNS_HEADER_REFUSED) {
ret = DNS_EAI_FAIL;
goto quit;
}
ret = dns_unpack_response_header(&dns_msg, *dns_id);
if (ret < 0) {
ret = DNS_EAI_FAIL;
goto quit;
}
if (dns_header_qdcount(dns_msg.msg) != 1) {
ret = DNS_EAI_FAIL;
goto quit;
}
ret = dns_unpack_response_query(&dns_msg);
if (ret < 0) {
ret = DNS_EAI_FAIL;
goto quit;
}
if (ctx->queries[query_idx].query_type == DNS_QUERY_TYPE_A) {
address_size = DNS_IPV4_LEN;
addr = (u8_t *)&net_sin(&info.ai_addr)->sin_addr;
info.ai_family = AF_INET;
info.ai_addr.sa_family = AF_INET;
info.ai_addrlen = sizeof(struct sockaddr_in);
} else if (ctx->queries[query_idx].query_type == DNS_QUERY_TYPE_AAAA) {
/* We cannot resolve IPv6 address if IPv6 is disabled. The reason
* being that "struct sockaddr" does not have enough space for
* IPv6 address in that case.
*/
#if defined(CONFIG_NET_IPV6)
address_size = DNS_IPV6_LEN;
addr = (u8_t *)&net_sin6(&info.ai_addr)->sin6_addr;
info.ai_family = AF_INET6;
info.ai_addr.sa_family = AF_INET6;
info.ai_addrlen = sizeof(struct sockaddr_in6);
#else
ret = DNS_EAI_FAMILY;
goto quit;
#endif
} else {
ret = DNS_EAI_FAMILY;
goto quit;
}
/* while loop to traverse the response */
answer_ptr = DNS_QUERY_POS;
items = 0;
server_idx = 0;
while (server_idx < dns_header_ancount(dns_msg.msg)) {
ret = dns_unpack_answer(&dns_msg, answer_ptr, &ttl);
if (ret < 0) {
ret = DNS_EAI_FAIL;
goto quit;
}
switch (dns_msg.response_type) {
case DNS_RESPONSE_IP:
if (dns_msg.response_length < address_size) {
/* it seems this is a malformed message */
ret = DNS_EAI_FAIL;
goto quit;
}
src = dns_msg.msg + dns_msg.response_position;
memcpy(addr, src, address_size);
ctx->queries[query_idx].cb(DNS_EAI_INPROGRESS, &info,
ctx->queries[query_idx].user_data);
items++;
break;
case DNS_RESPONSE_CNAME_NO_IP:
/* Instead of using the QNAME at DNS_QUERY_POS,
* we will use this CNAME
*/
answer_ptr = dns_msg.response_position;
break;
default:
ret = DNS_EAI_FAIL;
goto quit;
}
/* Update the answer offset to point to the next RR (answer) */
dns_msg.answer_offset += DNS_ANSWER_PTR_LEN;
dns_msg.answer_offset += dns_msg.response_length;
server_idx++;
}
/* No IP addresses were found, so we take the last CNAME to generate
* another query. Number of additional queries is controlled via Kconfig
*/
if (items == 0) {
if (dns_msg.response_type == DNS_RESPONSE_CNAME_NO_IP) {
u16_t pos = dns_msg.response_position;
ret = dns_copy_qname(dns_cname->data, &dns_cname->len,
dns_cname->size, &dns_msg, pos);
if (ret < 0) {
ret = DNS_EAI_SYSTEM;
goto quit;
}
ret = DNS_EAI_AGAIN;
goto finished;
}
}
if (items == 0) {
ret = DNS_EAI_NODATA;
} else {
ret = DNS_EAI_ALLDONE;
}
if (k_delayed_work_remaining_get(&ctx->queries[query_idx].timer) > 0) {
k_delayed_work_cancel(&ctx->queries[query_idx].timer);
}
/* Marks the end of the results */
ctx->queries[query_idx].cb(ret, NULL,
ctx->queries[query_idx].user_data);
ctx->queries[query_idx].cb = NULL;
net_pkt_unref(pkt);
return 0;
finished:
dns_resolve_cancel(ctx, *dns_id);
quit:
net_pkt_unref(pkt);
return ret;
}
static void cb_recv(struct net_context *net_ctx,
struct net_pkt *pkt,
union net_ip_header *ip_hdr,
union net_proto_header *proto_hdr,
int status,
void *user_data)
{
struct dns_resolve_context *ctx = user_data;
struct net_buf *dns_cname = NULL;
struct net_buf *dns_data = NULL;
u16_t dns_id = 0U;
int ret, i;
ARG_UNUSED(net_ctx);
if (status) {
ret = DNS_EAI_SYSTEM;
goto quit;
}
dns_data = net_buf_alloc(&dns_msg_pool, ctx->buf_timeout);
if (!dns_data) {
ret = DNS_EAI_MEMORY;
goto quit;
}
dns_cname = net_buf_alloc(&dns_qname_pool, ctx->buf_timeout);
if (!dns_cname) {
ret = DNS_EAI_MEMORY;
goto quit;
}
ret = dns_read(ctx, pkt, dns_data, &dns_id, dns_cname);
if (!ret) {
/* We called the callback already in dns_read() if there
* was no errors.
*/
goto free_buf;
}
/* Query again if we got CNAME */
if (ret == DNS_EAI_AGAIN) {
int failure = 0;
int j;
i = get_slot_by_id(ctx, dns_id);
if (i < 0) {
goto free_buf;
}
for (j = 0; j < SERVER_COUNT; j++) {
if (!ctx->servers[j].net_ctx) {
continue;
}
ret = dns_write(ctx, j, i, dns_data, dns_cname, 0);
if (ret < 0) {
failure++;
}
}
if (failure) {
NET_DBG("DNS cname query failed %d times", failure);
if (failure == j) {
ret = DNS_EAI_SYSTEM;
goto quit;
}
}
goto free_buf;
}
quit:
i = get_slot_by_id(ctx, dns_id);
if (i < 0) {
goto free_buf;
}
if (k_delayed_work_remaining_get(&ctx->queries[i].timer) > 0) {
k_delayed_work_cancel(&ctx->queries[i].timer);
}
/* Marks the end of the results */
ctx->queries[i].cb(ret, NULL, ctx->queries[i].user_data);
ctx->queries[i].cb = NULL;
free_buf:
if (dns_data) {
net_buf_unref(dns_data);
}
if (dns_cname) {
net_buf_unref(dns_cname);
}
}
static int dns_write(struct dns_resolve_context *ctx,
int server_idx,
int query_idx,
struct net_buf *dns_data,
struct net_buf *dns_qname,
int hop_limit)
{
enum dns_query_type query_type;
struct net_context *net_ctx;
struct sockaddr *server;
int server_addr_len;
u16_t dns_id;
int ret;
net_ctx = ctx->servers[server_idx].net_ctx;
server = &ctx->servers[server_idx].dns_server;
dns_id = ctx->queries[query_idx].id;
query_type = ctx->queries[query_idx].query_type;
ret = dns_msg_pack_query(dns_data->data, &dns_data->len, dns_data->size,
dns_qname->data, dns_qname->len, dns_id,
(enum dns_rr_type)query_type);
if (ret < 0) {
return -EINVAL;
}
if (IS_ENABLED(CONFIG_NET_IPV6) &&
net_context_get_family(net_ctx) == AF_INET6) {
net_context_set_ipv6_hop_limit(net_ctx, hop_limit);
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
net_context_get_family(net_ctx) == AF_INET) {
net_context_set_ipv4_ttl(net_ctx, hop_limit);
}
ret = net_context_recv(net_ctx, cb_recv, K_NO_WAIT, ctx);
if (ret < 0 && ret != -EALREADY) {
NET_DBG("Could not receive from socket (%d)", ret);
return ret;
}
if (server->sa_family == AF_INET) {
server_addr_len = sizeof(struct sockaddr_in);
} else {
server_addr_len = sizeof(struct sockaddr_in6);
}
ret = net_context_sendto(net_ctx, dns_data->data, dns_data->len,
server, server_addr_len, NULL,
K_NO_WAIT, NULL);
if (ret < 0) {
NET_DBG("Cannot send query (%d)", ret);
return ret;
}
ret = k_delayed_work_submit(&ctx->queries[query_idx].timer,
ctx->queries[query_idx].timeout);
if (ret < 0) {
NET_DBG("[%u] cannot submit work to server idx %d for id %u "
"timeout %u ret %d",
query_idx, server_idx, dns_id,
ctx->queries[query_idx].timeout, ret);
return ret;
}
NET_DBG("[%u] submitting work to server idx %d for id %u "
"timeout %u",
query_idx, server_idx, dns_id,
ctx->queries[query_idx].timeout);
return 0;
}
int dns_resolve_cancel(struct dns_resolve_context *ctx, u16_t dns_id)
{
int i;
i = get_slot_by_id(ctx, dns_id);
if (i < 0) {
return -ENOENT;
}
NET_DBG("Cancelling DNS req %u", dns_id);
if (k_delayed_work_remaining_get(&ctx->queries[i].timer) > 0) {
k_delayed_work_cancel(&ctx->queries[i].timer);
}
ctx->queries[i].cb(DNS_EAI_CANCELED, NULL, ctx->queries[i].user_data);
ctx->queries[i].cb = NULL;
return 0;
}
static void query_timeout(struct k_work *work)
{
struct dns_pending_query *pending_query =
CONTAINER_OF(work, struct dns_pending_query, timer);
NET_DBG("Query timeout DNS req %u", pending_query->id);
dns_resolve_cancel(pending_query->ctx, pending_query->id);
}
int dns_resolve_name(struct dns_resolve_context *ctx,
const char *query,
enum dns_query_type type,
u16_t *dns_id,
dns_resolve_cb_t cb,
void *user_data,
s32_t timeout)
{
struct net_buf *dns_data = NULL;
struct net_buf *dns_qname = NULL;
struct sockaddr addr;
int ret, i = -1, j = 0;
int failure = 0;
bool mdns_query = false;
u8_t hop_limit;
if (!ctx || !ctx->is_used || !query || !cb) {
return -EINVAL;
}
/* Timeout cannot be 0 as we cannot resolve name that fast.
*/
if (timeout == K_NO_WAIT) {
return -EINVAL;
}
ret = net_ipaddr_parse(query, strlen(query), &addr);
if (ret) {
/* The query name was already in numeric form, no
* need to continue further.
*/
struct dns_addrinfo info = { 0 };
if (type == DNS_QUERY_TYPE_A) {
memcpy(net_sin(&info.ai_addr), net_sin(&addr),
sizeof(struct sockaddr_in));
info.ai_family = AF_INET;
info.ai_addr.sa_family = AF_INET;
info.ai_addrlen = sizeof(struct sockaddr_in);
} else if (type == DNS_QUERY_TYPE_AAAA) {
#if defined(CONFIG_NET_IPV6)
memcpy(net_sin6(&info.ai_addr), net_sin6(&addr),
sizeof(struct sockaddr_in6));
info.ai_family = AF_INET6;
info.ai_addr.sa_family = AF_INET6;
info.ai_addrlen = sizeof(struct sockaddr_in6);
#else
ret = -EAFNOSUPPORT;
goto quit;
#endif
} else {
goto try_resolve;
}
cb(DNS_EAI_INPROGRESS, &info, user_data);
cb(DNS_EAI_ALLDONE, NULL, user_data);
return 0;
}
try_resolve:
i = get_cb_slot(ctx);
if (i < 0) {
return -EAGAIN;
}
ctx->queries[i].cb = cb;
ctx->queries[i].timeout = timeout;
ctx->queries[i].query = query;
ctx->queries[i].query_type = type;
ctx->queries[i].user_data = user_data;
ctx->queries[i].ctx = ctx;
k_delayed_work_init(&ctx->queries[i].timer, query_timeout);
dns_data = net_buf_alloc(&dns_msg_pool, ctx->buf_timeout);
if (!dns_data) {
ret = -ENOMEM;
goto quit;
}
dns_qname = net_buf_alloc(&dns_qname_pool, ctx->buf_timeout);
if (!dns_qname) {
ret = -ENOMEM;
goto quit;
}
ret = dns_msg_pack_qname(&dns_qname->len, dns_qname->data,
DNS_MAX_NAME_LEN, ctx->queries[i].query);
if (ret < 0) {
goto quit;
}
ctx->queries[i].id = sys_rand32_get();
/* Do this immediately after calculating the Id so that the unit
* test will work properly.
*/
if (dns_id) {
*dns_id = ctx->queries[i].id;
NET_DBG("DNS id will be %u", *dns_id);
}
mdns_query = false;
/* If mDNS is enabled, then send .local queries only to multicast
* address.
*/
if (IS_ENABLED(CONFIG_MDNS_RESOLVER)) {
const char *ptr = strrchr(query, '.');
/* Note that we memcmp() the \0 here too */
if (ptr && !memcmp(ptr, (const void *){ ".local" }, 7)) {
mdns_query = true;
}
}
for (j = 0; j < SERVER_COUNT; j++) {
hop_limit = 0U;
if (!ctx->servers[j].net_ctx) {
continue;
}
/* If mDNS is enabled, then send .local queries only to
* a well known multicast mDNS server address.
*/
if (IS_ENABLED(CONFIG_MDNS_RESOLVER) && mdns_query &&
!ctx->servers[j].is_mdns) {
continue;
}
/* If llmnr is enabled, then all the queries are sent to
* LLMNR multicast address unless it is a mDNS query.
*/
if (!mdns_query && IS_ENABLED(CONFIG_LLMNR_RESOLVER)) {
if (!ctx->servers[j].is_llmnr) {
continue;
}
hop_limit = 1U;
}
ret = dns_write(ctx, j, i, dns_data, dns_qname, hop_limit);
if (ret < 0) {
failure++;
continue;
}
/* Do one concurrent query only for each name resolve.
* TODO: Change the i (query index) to do multiple concurrent
* to each server.
*/
break;
}
if (failure) {
NET_DBG("DNS query failed %d times", failure);
if (failure == j) {
ret = -ENOENT;
goto quit;
}
}
ret = 0;
quit:
if (ret < 0) {
if (i >= 0) {
if (k_delayed_work_remaining_get(
&ctx->queries[i].timer) > 0) {
k_delayed_work_cancel(&ctx->queries[i].timer);
}
ctx->queries[i].cb = NULL;
}
if (dns_id) {
*dns_id = 0U;
}
}
if (dns_data) {
net_buf_unref(dns_data);
}
if (dns_qname) {
net_buf_unref(dns_qname);
}
return ret;
}
int dns_resolve_close(struct dns_resolve_context *ctx)
{
int i;
if (!ctx->is_used) {
return -ENOENT;
}
for (i = 0; i < SERVER_COUNT; i++) {
if (ctx->servers[i].net_ctx) {
net_context_put(ctx->servers[i].net_ctx);
}
}
ctx->is_used = false;
return 0;
}
struct dns_resolve_context *dns_resolve_get_default(void)
{
return &dns_default_ctx;
}
void dns_init_resolver(void)
{
#if defined(CONFIG_DNS_SERVER_IP_ADDRESSES)
static const char *dns_servers[SERVER_COUNT + 1];
int count = DNS_SERVER_COUNT;
int ret;
if (count > 5) {
count = 5;
}
switch (count) {
#if DNS_SERVER_COUNT > 4
case 5:
dns_servers[4] = CONFIG_DNS_SERVER5;
/* fallthrough */
#endif
#if DNS_SERVER_COUNT > 3
case 4:
dns_servers[3] = CONFIG_DNS_SERVER4;
/* fallthrough */
#endif
#if DNS_SERVER_COUNT > 2
case 3:
dns_servers[2] = CONFIG_DNS_SERVER3;
/* fallthrough */
#endif
#if DNS_SERVER_COUNT > 1
case 2:
dns_servers[1] = CONFIG_DNS_SERVER2;
/* fallthrough */
#endif
#if DNS_SERVER_COUNT > 0
case 1:
dns_servers[0] = CONFIG_DNS_SERVER1;
/* fallthrough */
#endif
case 0:
break;
}
#if defined(CONFIG_MDNS_RESOLVER) && (MDNS_SERVER_COUNT > 0)
#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4)
dns_servers[DNS_SERVER_COUNT + 1] = MDNS_IPV6_ADDR;
dns_servers[DNS_SERVER_COUNT] = MDNS_IPV4_ADDR;
#else /* CONFIG_NET_IPV6 && CONFIG_NET_IPV4 */
#if defined(CONFIG_NET_IPV6)
dns_servers[DNS_SERVER_COUNT] = MDNS_IPV6_ADDR;
#endif
#if defined(CONFIG_NET_IPV4)
dns_servers[DNS_SERVER_COUNT] = MDNS_IPV4_ADDR;
#endif
#endif /* CONFIG_NET_IPV6 && CONFIG_NET_IPV4 */
#endif /* MDNS_RESOLVER && MDNS_SERVER_COUNT > 0 */
#if defined(CONFIG_LLMNR_RESOLVER) && (LLMNR_SERVER_COUNT > 0)
#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4)
dns_servers[DNS_SERVER_COUNT + MDNS_SERVER_COUNT + 1] =
LLMNR_IPV6_ADDR;
dns_servers[DNS_SERVER_COUNT + MDNS_SERVER_COUNT] = LLMNR_IPV4_ADDR;
#else /* CONFIG_NET_IPV6 && CONFIG_NET_IPV4 */
#if defined(CONFIG_NET_IPV6)
dns_servers[DNS_SERVER_COUNT + MDNS_SERVER_COUNT] = LLMNR_IPV6_ADDR;
#endif
#if defined(CONFIG_NET_IPV4)
dns_servers[DNS_SERVER_COUNT + MDNS_SERVER_COUNT] = LLMNR_IPV4_ADDR;
#endif
#endif /* CONFIG_NET_IPV6 && CONFIG_NET_IPV4 */
#endif /* LLMNR_RESOLVER && LLMNR_SERVER_COUNT > 0 */
dns_servers[SERVER_COUNT] = NULL;
ret = dns_resolve_init(dns_resolve_get_default(), dns_servers, NULL);
if (ret < 0) {
NET_WARN("Cannot initialize DNS resolver (%d)", ret);
}
#endif
}