net: dns: Allow using resolver and responder at the same time
Allow mDNS resolver and responder to to be used at the same
time so that both can use the port 5353. This requires
a DNS traffic dispatcher which affects also the normal DNS
resolver.
Fixes #72553
Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
diff --git a/include/zephyr/net/dns_resolve.h b/include/zephyr/net/dns_resolve.h
index f68a15e..36983f0 100644
--- a/include/zephyr/net/dns_resolve.h
+++ b/include/zephyr/net/dns_resolve.h
@@ -46,14 +46,36 @@
/** @cond INTERNAL_HIDDEN */
+#define DNS_BUF_TIMEOUT K_MSEC(500) /* ms */
+
+/* This value is recommended by RFC 1035 */
+#define DNS_RESOLVER_MAX_BUF_SIZE 512
+
/* Make sure that we can compile things even if CONFIG_DNS_RESOLVER
* is not enabled.
*/
-#if !defined(CONFIG_DNS_RESOLVER_MAX_SERVERS)
-#define CONFIG_DNS_RESOLVER_MAX_SERVERS 1
+#if defined(CONFIG_DNS_RESOLVER_MAX_SERVERS)
+#define DNS_RESOLVER_MAX_SERVERS CONFIG_DNS_RESOLVER_MAX_SERVERS
+#else
+#define DNS_RESOLVER_MAX_SERVERS 0
#endif
-#if !defined(CONFIG_DNS_NUM_CONCUR_QUERIES)
-#define CONFIG_DNS_NUM_CONCUR_QUERIES 1
+
+#if defined(CONFIG_DNS_NUM_CONCUR_QUERIES)
+#define DNS_NUM_CONCUR_QUERIES CONFIG_DNS_NUM_CONCUR_QUERIES
+#else
+#define DNS_NUM_CONCUR_QUERIES 1
+#endif
+
+#if defined(CONFIG_NET_IF_MAX_IPV6_COUNT)
+#define MAX_IPV6_IFACE_COUNT CONFIG_NET_IF_MAX_IPV6_COUNT
+#else
+#define MAX_IPV6_IFACE_COUNT 1
+#endif
+
+#if defined(CONFIG_NET_IF_MAX_IPV4_COUNT)
+#define MAX_IPV4_IFACE_COUNT CONFIG_NET_IF_MAX_IPV4_COUNT
+#else
+#define MAX_IPV4_IFACE_COUNT 1
#endif
/* If mDNS is enabled, then add some extra well known multicast servers to the
@@ -84,6 +106,139 @@
#define DNS_MAX_MCAST_SERVERS (MDNS_SERVER_COUNT + LLMNR_SERVER_COUNT)
+#if defined(CONFIG_MDNS_RESPONDER)
+#if defined(CONFIG_NET_IPV6)
+#define MDNS_MAX_IPV6_IFACE_COUNT CONFIG_NET_IF_MAX_IPV6_COUNT
+#else
+#define MDNS_MAX_IPV6_IFACE_COUNT 0
+#endif /* CONFIG_NET_IPV6 */
+
+#if defined(CONFIG_NET_IPV4)
+#define MDNS_MAX_IPV4_IFACE_COUNT CONFIG_NET_IF_MAX_IPV4_COUNT
+#else
+#define MDNS_MAX_IPV4_IFACE_COUNT 0
+#endif /* CONFIG_NET_IPV4 */
+
+#define MDNS_MAX_POLL (MDNS_MAX_IPV4_IFACE_COUNT + MDNS_MAX_IPV6_IFACE_COUNT)
+#else
+#define MDNS_MAX_POLL 0
+#endif /* CONFIG_MDNS_RESPONDER */
+
+#if defined(CONFIG_LLMNR_RESPONDER)
+#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4)
+#define LLMNR_MAX_POLL 2
+#else
+#define LLMNR_MAX_POLL 1
+#endif
+#else
+#define LLMNR_MAX_POLL 0
+#endif /* CONFIG_LLMNR_RESPONDER */
+
+#define DNS_RESOLVER_MAX_POLL (DNS_RESOLVER_MAX_SERVERS + DNS_MAX_MCAST_SERVERS)
+
+/** How many sockets the dispatcher is able to poll. */
+#define DNS_DISPATCHER_MAX_POLL (DNS_RESOLVER_MAX_POLL + MDNS_MAX_POLL + LLMNR_MAX_POLL)
+
+#if defined(CONFIG_NET_SOCKETS_POLL_MAX)
+BUILD_ASSERT(CONFIG_NET_SOCKETS_POLL_MAX >= DNS_DISPATCHER_MAX_POLL,
+ "CONFIG_NET_SOCKETS_POLL_MAX must be larger than " STRINGIFY(DNS_DISPATCHER_MAX_POLL));
+#endif
+
+/** @brief What is the type of the socket given to DNS socket dispatcher,
+ * resolver or responder.
+ */
+enum dns_socket_type {
+ DNS_SOCKET_RESOLVER = 1, /**< Socket is used for resolving (client type) */
+ DNS_SOCKET_RESPONDER = 2 /**< Socket is used for responding (server type) */
+};
+
+struct dns_resolve_context;
+struct mdns_responder_context;
+
+/**
+ * @typedef dns_socket_dispatcher_cb
+ * @brief Callback used when the DNS socket dispatcher has found a handler for
+ * this type of socket.
+ *
+ * @param ctx DNS resolve or mDNS responder context.
+ * @param sock Socket which is seeing traffic.
+ * @param addr Socket address of the peer that sent the DNS packet.
+ * @param addrlen Length of the socket address.
+ * @param buf Pointer to data buffer containing the DNS message.
+ * @param data_len Length of the data in buffer chain.
+ *
+ * @return 0 if ok, <0 if error
+ */
+typedef int (*dns_socket_dispatcher_cb)(void *ctx, int sock,
+ struct sockaddr *addr, size_t addrlen,
+ struct net_buf *buf, size_t data_len);
+
+/** @brief DNS socket dispatcher context. */
+struct dns_socket_dispatcher {
+ /** slist node for the different dispatcher contexts */
+ sys_snode_t node;
+ /** Socket service for this dispatcher instance */
+ const struct net_socket_service_desc *svc;
+ /** DNS resolver context that contains information needed by the
+ * resolver/responder handler, or mDNS responder context.
+ */
+ union {
+ void *ctx;
+ struct dns_resolve_context *resolve_ctx;
+ struct mdns_responder_context *mdns_ctx;
+ };
+
+ /** Type of the socket (resolver / responder) */
+ enum dns_socket_type type;
+ /** Local endpoint address (used when binding the socket) */
+ struct sockaddr local_addr;
+ /** DNS socket dispatcher callback is called for incoming traffic */
+ dns_socket_dispatcher_cb cb;
+ /** Socket descriptors to poll */
+ struct zsock_pollfd *fds;
+ /** Length of the poll array */
+ int fds_len;
+ /** Local socket to dispatch */
+ int sock;
+ /** There can be two contexts to dispatch. This points to the other
+ * context if sharing the socket between resolver / responder.
+ */
+ struct dns_socket_dispatcher *pair;
+ /** Mutex lock protecting access to this dispatcher context */
+ struct k_mutex lock;
+ /** Buffer allocation timeout */
+ k_timeout_t buf_timeout;
+};
+
+struct mdns_responder_context {
+ struct sockaddr server_addr;
+ struct dns_socket_dispatcher dispatcher;
+ struct zsock_pollfd fds[1];
+ int sock;
+};
+
+/**
+ * @brief Register a DNS dispatcher socket. Each code wanting to use
+ * the dispatcher needs to create the dispatcher context and call
+ * this function.
+ *
+ * @param ctx DNS socket dispatcher context.
+ *
+ * @return 0 if ok, <1 if error
+ */
+int dns_dispatcher_register(struct dns_socket_dispatcher *ctx);
+
+/**
+ * @brief Unregister a DNS dispatcher socket. Called when the
+ * resolver/responder no longer needs to receive traffic for the
+ * socket.
+ *
+ * @param ctx DNS socket dispatcher context.
+ *
+ * @return 0 if ok, <1 if error
+ */
+int dns_dispatcher_unregister(struct dns_socket_dispatcher *ctx);
+
/** @endcond */
/**
@@ -178,7 +333,7 @@
*/
struct dns_resolve_context {
/** List of configured DNS servers */
- struct {
+ struct dns_server {
/** DNS server information */
struct sockaddr dns_server;
@@ -190,10 +345,14 @@
/** Is this server LLMNR one */
uint8_t is_llmnr : 1;
- } servers[CONFIG_DNS_RESOLVER_MAX_SERVERS + DNS_MAX_MCAST_SERVERS];
/** @cond INTERNAL_HIDDEN */
-#define DNS_RESOLVER_MAX_POLL (CONFIG_DNS_RESOLVER_MAX_SERVERS + DNS_MAX_MCAST_SERVERS)
+ /** Dispatch DNS data between resolver and responder */
+ struct dns_socket_dispatcher dispatcher;
+/** @endcond */
+ } servers[DNS_RESOLVER_MAX_POLL];
+
+/** @cond INTERNAL_HIDDEN */
/** Socket polling for each server connection */
struct zsock_pollfd fds[DNS_RESOLVER_MAX_POLL];
/** @endcond */
@@ -257,7 +416,7 @@
* cannot be used to find correct pending query.
*/
uint16_t query_hash;
- } queries[CONFIG_DNS_NUM_CONCUR_QUERIES];
+ } queries[DNS_NUM_CONCUR_QUERIES];
/** Is this context in use */
enum dns_resolve_context_state state;
diff --git a/subsys/net/ip/net_core.c b/subsys/net/ip/net_core.c
index cc2356c..982fc413 100644
--- a/subsys/net/ip/net_core.c
+++ b/subsys/net/ip/net_core.c
@@ -561,7 +561,10 @@
net_dhcpv4_server_init();
+ dns_dispatcher_init();
dns_init_resolver();
+ mdns_init_responder();
+
websocket_init();
net_coap_init();
diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h
index cb9d8dc..853e502 100644
--- a/subsys/net/ip/net_private.h
+++ b/subsys/net/ip/net_private.h
@@ -122,6 +122,18 @@
}
#endif
+#if defined(CONFIG_DNS_SOCKET_DISPATCHER)
+extern void dns_dispatcher_init(void);
+#else
+static inline void dns_dispatcher_init(void) { }
+#endif
+
+#if defined(CONFIG_MDNS_RESPONDER)
+extern void mdns_init_responder(void);
+#else
+static inline void mdns_init_responder(void) { }
+#endif /* CONFIG_MDNS_RESPONDER */
+
#if defined(CONFIG_NET_NATIVE)
enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback);
enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback);
diff --git a/subsys/net/lib/dns/CMakeLists.txt b/subsys/net/lib/dns/CMakeLists.txt
index 974e162..5a6f74e 100644
--- a/subsys/net/lib/dns/CMakeLists.txt
+++ b/subsys/net/lib/dns/CMakeLists.txt
@@ -5,6 +5,7 @@
zephyr_library()
zephyr_library_sources(dns_pack.c)
+zephyr_library_sources_ifdef(CONFIG_DNS_SOCKET_DISPATCHER dispatcher.c)
zephyr_library_sources_ifdef(CONFIG_DNS_RESOLVER resolve.c)
zephyr_library_sources_ifdef(CONFIG_DNS_SD dns_sd.c)
zephyr_library_sources_ifdef(CONFIG_DNS_RESOLVER_CACHE dns_cache.c)
diff --git a/subsys/net/lib/dns/Kconfig b/subsys/net/lib/dns/Kconfig
index 9b15408..03ca268 100644
--- a/subsys/net/lib/dns/Kconfig
+++ b/subsys/net/lib/dns/Kconfig
@@ -34,16 +34,6 @@
to LLMNR well known multicast address 224.0.0.252:5355 or
[ff02::1:3]:5355 and other DNS server addresses are ignored.
-
-config DNS_RESOLVER_ADDITIONAL_BUF_CTR
- int "Additional DNS buffers"
- default 0
- help
- Number of additional buffers available for the DNS resolver.
- The DNS resolver requires at least one buffer. This option
- enables additional buffers required for multiple concurrent
- DNS connections.
-
config DNS_RESOLVER_ADDITIONAL_QUERIES
int "Additional DNS queries"
range 0 2
@@ -294,3 +284,35 @@
source "subsys/net/Kconfig.template.log_config.net"
endif # DNS_SD
+
+# Note that we enable the DNS socket dispatcher always if either responder or
+# resolver support is enabed to simplify things. Strictly speaking the
+# dispatcher is really needed for supporting resolver and responder at the same
+# time.
+config DNS_SOCKET_DISPATCHER
+ bool
+ depends on (DNS_RESOLVER || MDNS_RESPONDER)
+ select NET_SOCKETS_SERVICE
+ default y
+ help
+ A DNS socket dispatcher that allows both the DNS resolver and
+ mDNS responder to be used at the same time.
+
+if DNS_SOCKET_DISPATCHER
+
+config DNS_RESOLVER_ADDITIONAL_BUF_CTR
+ int "Additional DNS buffers"
+ default 1
+ help
+ Number of additional buffers available for the DNS resolver.
+ The DNS resolver requires at least one buffer. This option
+ enables additional buffers required for multiple concurrent
+ DNS connections.
+
+module = DNS_SOCKET_DISPATCHER
+module-dep = NET_LOG
+module-str = Log level for DNS socket dispatcher
+module-help = Enables DNS socket dispatcher code to output debug messages.
+source "subsys/net/Kconfig.template.log_config.net"
+
+endif # DNS_SOCKET_DISPATCHER
diff --git a/subsys/net/lib/dns/dispatcher.c b/subsys/net/lib/dns/dispatcher.c
new file mode 100644
index 0000000..e4e6a7b
--- /dev/null
+++ b/subsys/net/lib/dns/dispatcher.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2024 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(net_dns_dispatcher, CONFIG_DNS_SOCKET_DISPATCHER_LOG_LEVEL);
+
+#include <zephyr/kernel.h>
+#include <zephyr/sys/check.h>
+#include <zephyr/sys/slist.h>
+#include <zephyr/net/buf.h>
+#include <zephyr/net/dns_resolve.h>
+#include <zephyr/net/socket_service.h>
+
+#include "dns_pack.h"
+
+static K_MUTEX_DEFINE(lock);
+
+static sys_slist_t sockets;
+
+#define DNS_RESOLVER_MIN_BUF 1
+#define DNS_RESOLVER_BUF_CTR (DNS_RESOLVER_MIN_BUF + \
+ CONFIG_DNS_RESOLVER_ADDITIONAL_BUF_CTR)
+
+NET_BUF_POOL_DEFINE(dns_msg_pool, DNS_RESOLVER_BUF_CTR,
+ DNS_RESOLVER_MAX_BUF_SIZE, 0, NULL);
+
+static int dns_dispatch(struct dns_socket_dispatcher *dispatcher,
+ int sock, struct sockaddr *addr, size_t addrlen,
+ struct net_buf *dns_data, size_t buf_len)
+{
+ /* Helper struct to track the dns msg received from the server */
+ struct dns_msg_t dns_msg;
+ bool is_query;
+ int data_len;
+ int ret;
+
+ data_len = MIN(buf_len, DNS_RESOLVER_MAX_BUF_SIZE);
+
+ dns_msg.msg = dns_data->data;
+ dns_msg.msg_size = data_len;
+
+ /* Make sure that we can read DNS id, flags and rcode */
+ if (dns_msg.msg_size < (sizeof(uint16_t) + sizeof(uint16_t))) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (dns_header_rcode(dns_msg.msg) == DNS_HEADER_REFUSED) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ is_query = (dns_header_qr(dns_msg.msg) == DNS_QUERY);
+ if (is_query) {
+ if (dispatcher->type == DNS_SOCKET_RESPONDER) {
+ /* Call the responder callback */
+ ret = dispatcher->cb(dispatcher->ctx, sock,
+ addr, addrlen,
+ dns_data, data_len);
+ } else if (dispatcher->pair) {
+ ret = dispatcher->pair->cb(dispatcher->pair->ctx, sock,
+ addr, addrlen,
+ dns_data, data_len);
+ } else {
+ /* Discard the message as it was a query and there are none
+ * expecting a query.
+ */
+ ret = -ENOENT;
+ }
+ } else {
+ /* So this was an answer to a query that was made by resolver.
+ */
+ if (dispatcher->type == DNS_SOCKET_RESOLVER) {
+ /* Call the resolver callback */
+ ret = dispatcher->cb(dispatcher->ctx, sock,
+ addr, addrlen,
+ dns_data, data_len);
+ } else if (dispatcher->pair) {
+ ret = dispatcher->pair->cb(dispatcher->pair->ctx, sock,
+ addr, addrlen,
+ dns_data, data_len);
+ } else {
+ /* Discard the message as it was not a query reply and
+ * we were a reply.
+ */
+ ret = -ENOENT;
+ }
+ }
+
+done:
+ return ret;
+}
+
+static int recv_data(struct net_socket_service_event *pev)
+{
+ struct dns_socket_dispatcher *dispatcher = pev->user_data;
+ socklen_t optlen = sizeof(int);
+ struct net_buf *dns_data = NULL;
+ struct sockaddr addr;
+ size_t addrlen;
+ int family, sock_error;
+ int ret = 0, len;
+
+ k_mutex_lock(&dispatcher->lock, K_FOREVER);
+
+ (void)zsock_getsockopt(pev->event.fd, SOL_SOCKET,
+ SO_DOMAIN, &family, &optlen);
+
+ if ((pev->event.revents & ZSOCK_POLLERR) ||
+ (pev->event.revents & ZSOCK_POLLNVAL)) {
+ (void)zsock_getsockopt(pev->event.fd, SOL_SOCKET,
+ SO_ERROR, &sock_error, &optlen);
+ NET_ERR("Receiver IPv%d socket error (%d)",
+ family == AF_INET ? 4 : 6, sock_error);
+ ret = DNS_EAI_SYSTEM;
+ goto unlock;
+ }
+
+ addrlen = (family == AF_INET) ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+
+ dns_data = net_buf_alloc(&dns_msg_pool, dispatcher->buf_timeout);
+ if (!dns_data) {
+ ret = DNS_EAI_MEMORY;
+ goto unlock;
+ }
+
+ ret = zsock_recvfrom(pev->event.fd, dns_data->data,
+ net_buf_max_len(dns_data), 0,
+ (struct sockaddr *)&addr, &addrlen);
+ if (ret < 0) {
+ ret = -errno;
+ NET_ERR("recv failed on IPv%d socket (%d)",
+ family == AF_INET ? 4 : 6, -ret);
+ goto free_buf;
+ }
+
+ len = ret;
+
+ ret = dns_dispatch(dispatcher, pev->event.fd,
+ (struct sockaddr *)&addr, addrlen,
+ dns_data, len);
+free_buf:
+ if (dns_data) {
+ net_buf_unref(dns_data);
+ }
+
+unlock:
+ k_mutex_unlock(&dispatcher->lock);
+
+ return ret;
+}
+
+void dns_dispatcher_svc_handler(struct k_work *work)
+{
+ struct net_socket_service_event *pev =
+ CONTAINER_OF(work, struct net_socket_service_event, work);
+ int ret;
+
+ ret = recv_data(pev);
+ if (ret < 0 && ret != DNS_EAI_ALLDONE) {
+ NET_ERR("DNS recv error (%d)", ret);
+ }
+}
+
+int dns_dispatcher_register(struct dns_socket_dispatcher *ctx)
+{
+ struct dns_socket_dispatcher *entry, *next, *found = NULL;
+ sys_snode_t *prev_node = NULL;
+ bool dup = false;
+ size_t addrlen;
+ int ret = 0;
+
+ k_mutex_lock(&lock, K_FOREVER);
+
+ if (sys_slist_find(&sockets, &ctx->node, &prev_node)) {
+ ret = -EALREADY;
+ goto out;
+ }
+
+ SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sockets, entry, next, node) {
+ /* Refuse to register context if we have identical context
+ * already registered.
+ */
+ if (ctx->type == entry->type &&
+ ctx->local_addr.sa_family == entry->local_addr.sa_family) {
+ if (net_sin(&entry->local_addr)->sin_port ==
+ net_sin(&ctx->local_addr)->sin_port) {
+ dup = true;
+ continue;
+ }
+ }
+
+ /* Then check if there is an entry with same family and
+ * port already in the list. If there is then we can act
+ * as a dispatcher for the given socket. Do not break
+ * from the loop even if we found an entry so that we
+ * can catch possible duplicates.
+ */
+ if (found == NULL && ctx->type != entry->type &&
+ ctx->local_addr.sa_family == entry->local_addr.sa_family) {
+ if (net_sin(&entry->local_addr)->sin_port ==
+ net_sin(&ctx->local_addr)->sin_port) {
+ found = entry;
+ continue;
+ }
+ }
+ }
+
+ if (dup) {
+ /* Found a duplicate */
+ ret = -EALREADY;
+ goto out;
+ }
+
+ if (found != NULL) {
+ entry = found;
+
+ if (entry->pair != NULL) {
+ NET_DBG("Already paired connection found.");
+ ret = -EALREADY;
+ goto out;
+ }
+
+ entry->pair = ctx;
+
+ /* Basically we are now done. If there is incoming data to
+ * the socket, the dispatcher will then pass it to the correct
+ * recipient.
+ */
+ ret = 0;
+ goto out;
+ }
+
+ ctx->buf_timeout = DNS_BUF_TIMEOUT;
+
+ if (ctx->local_addr.sa_family == AF_INET) {
+ addrlen = sizeof(struct sockaddr_in);
+ } else {
+ addrlen = sizeof(struct sockaddr_in6);
+ }
+
+ /* Bind and then register a socket service with this combo */
+ ret = zsock_bind(ctx->sock, &ctx->local_addr, addrlen);
+ if (ret < 0) {
+ ret = -errno;
+ NET_DBG("Cannot bind DNS socket %d (%d)", ctx->sock, ret);
+ goto out;
+ }
+
+ ctx->pair = NULL;
+
+ ret = net_socket_service_register(ctx->svc, ctx->fds, ctx->fds_len, ctx);
+ if (ret < 0) {
+ NET_DBG("Cannot register socket service (%d)", ret);
+ goto out;
+ }
+
+ sys_slist_prepend(&sockets, &ctx->node);
+
+out:
+ k_mutex_unlock(&lock);
+
+ return ret;
+}
+
+int dns_dispatcher_unregister(struct dns_socket_dispatcher *ctx)
+{
+ k_mutex_lock(&lock, K_FOREVER);
+
+ (void)sys_slist_find_and_remove(&sockets, &ctx->node);
+
+ k_mutex_unlock(&lock);
+
+ return 0;
+}
+
+void dns_dispatcher_init(void)
+{
+ sys_slist_init(&sockets);
+}
diff --git a/subsys/net/lib/dns/mdns_responder.c b/subsys/net/lib/dns/mdns_responder.c
index ab4e8e4..568899c 100644
--- a/subsys/net/lib/dns/mdns_responder.c
+++ b/subsys/net/lib/dns/mdns_responder.c
@@ -35,22 +35,24 @@
#include "net_private.h"
+extern void dns_dispatcher_svc_handler(struct k_work *work);
+
#define MDNS_LISTEN_PORT 5353
#define MDNS_TTL CONFIG_MDNS_RESPONDER_TTL /* In seconds */
#if defined(CONFIG_NET_IPV4)
-#define MAX_IPV4_IFACE_COUNT CONFIG_NET_IF_MAX_IPV4_COUNT
-static int ipv4[MAX_IPV4_IFACE_COUNT];
-static struct sockaddr_in local_addr4;
-#else
-#define MAX_IPV4_IFACE_COUNT 0
+static struct mdns_responder_context v4_ctx[MAX_IPV4_IFACE_COUNT];
+
+NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(v4_svc, NULL, dns_dispatcher_svc_handler,
+ MDNS_MAX_IPV4_IFACE_COUNT);
#endif
+
#if defined(CONFIG_NET_IPV6)
-#define MAX_IPV6_IFACE_COUNT CONFIG_NET_IF_MAX_IPV6_COUNT
-static int ipv6[MAX_IPV6_IFACE_COUNT];
-#else
-#define MAX_IPV6_IFACE_COUNT 0
+static struct mdns_responder_context v6_ctx[MAX_IPV6_IFACE_COUNT];
+
+NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(v6_svc, NULL, dns_dispatcher_svc_handler,
+ MDNS_MAX_IPV6_IFACE_COUNT);
#endif
static struct net_mgmt_event_callback mgmt_cb;
@@ -59,25 +61,15 @@
#define BUF_ALLOC_TIMEOUT K_MSEC(100)
-/* This value is recommended by RFC 1035 */
-#define DNS_RESOLVER_MAX_BUF_SIZE 512
-#define DNS_RESOLVER_MIN_BUF 2
-#define DNS_RESOLVER_BUF_CTR (DNS_RESOLVER_MIN_BUF + \
- CONFIG_MDNS_RESOLVER_ADDITIONAL_BUF_CTR)
-
-#define MDNS_MAX_POLL (MAX_IPV4_IFACE_COUNT + MAX_IPV6_IFACE_COUNT)
-
-static void svc_handler(struct k_work *work);
-NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(svc_mdns, NULL, svc_handler, MDNS_MAX_POLL);
-
-/* Socket polling for each server connection */
-static struct zsock_pollfd fds[MDNS_MAX_POLL];
-
#ifndef CONFIG_NET_TEST
static int setup_dst_addr(int sock, sa_family_t family,
struct sockaddr *dst, socklen_t *dst_len);
#endif /* CONFIG_NET_TEST */
+#define DNS_RESOLVER_MIN_BUF 2
+#define DNS_RESOLVER_BUF_CTR (DNS_RESOLVER_MIN_BUF + \
+ CONFIG_MDNS_RESOLVER_ADDITIONAL_BUF_CTR)
+
NET_BUF_POOL_DEFINE(mdns_msg_pool, DNS_RESOLVER_BUF_CTR,
DNS_RESOLVER_MAX_BUF_SIZE, 0, NULL);
@@ -106,11 +98,14 @@
{
if (mgmt_event == NET_EVENT_IF_UP) {
#if defined(CONFIG_NET_IPV4)
- int ret = net_ipv4_igmp_join(iface, &local_addr4.sin_addr, NULL);
-
- if (ret < 0) {
- NET_DBG("Cannot add IPv4 multicast address to iface %d",
- net_if_get_by_iface(iface));
+ ARRAY_FOR_EACH(v4_ctx, i) {
+ int ret = net_ipv4_igmp_join(iface,
+ &net_sin(&v4_ctx[i].dispatcher.local_addr)->sin_addr,
+ NULL);
+ if (ret < 0) {
+ NET_DBG("Cannot add IPv4 multicast address to iface %d",
+ net_if_get_by_iface(iface));
+ }
}
#endif /* defined(CONFIG_NET_IPV4) */
}
@@ -156,27 +151,7 @@
ret = zsock_socket(family, SOCK_DGRAM, IPPROTO_UDP);
if (ret < 0) {
ret = -errno;
- NET_DBG("Cannot get context (%d)", ret);
- }
-
- return ret;
-}
-
-static int bind_ctx(int sock,
- struct sockaddr *local_addr,
- socklen_t addrlen)
-{
- int ret;
-
- if (sock < 0) {
- return -EINVAL;
- }
-
- ret = zsock_bind(sock, local_addr, addrlen);
- if (ret < 0) {
- NET_DBG("Cannot bind to %s %s port (%d)", "mDNS",
- local_addr->sa_family == AF_INET ?
- "IPv4" : "IPv6", ret);
+ NET_DBG("Cannot get socket (%d)", ret);
}
return ret;
@@ -354,19 +329,6 @@
return ret;
}
-static const char *qtype_to_string(int qtype)
-{
- switch (qtype) {
- case DNS_RR_TYPE_A: return "A";
- case DNS_RR_TYPE_CNAME: return "CNAME";
- case DNS_RR_TYPE_PTR: return "PTR";
- case DNS_RR_TYPE_TXT: return "TXT";
- case DNS_RR_TYPE_AAAA: return "AAAA";
- case DNS_RR_TYPE_SRV: return "SRV";
- default: return "<unknown type>";
- }
-}
-
static void send_sd_response(int sock,
sa_family_t family,
struct sockaddr *src_addr,
@@ -528,18 +490,16 @@
struct net_buf *dns_data,
size_t len,
struct sockaddr *src_addr,
- size_t addrlen,
- struct dns_addrinfo *info)
+ size_t addrlen)
{
/* Helper struct to track the dns msg received from the server */
const char *hostname = net_hostname_get();
int hostname_len = strlen(hostname);
+ int family = src_addr->sa_family;
struct net_buf *result;
struct dns_msg_t dns_msg;
- socklen_t optlen;
int data_len;
int queries;
- int family;
int ret;
data_len = MIN(len, DNS_RESOLVER_MAX_BUF_SIZE);
@@ -564,9 +524,6 @@
queries = ret;
- optlen = sizeof(int);
- (void)zsock_getsockopt(sock, SOL_SOCKET, SO_DOMAIN, &family, &optlen);
-
NET_DBG("Received %d %s from %s", queries,
queries > 1 ? "queries" : "query",
net_sprint_addr(family,
@@ -594,7 +551,7 @@
}
NET_DBG("[%d] query %s/%s label %s (%d bytes)", queries,
- qtype_to_string(qtype), "IN",
+ dns_qtype_to_str(qtype), "IN",
result->data, ret);
/* If the query matches to our hostname, then send reply.
@@ -626,71 +583,6 @@
return ret;
}
-static int recv_data(struct net_socket_service_event *pev)
-{
- COND_CODE_1(IS_ENABLED(CONFIG_NET_IPV6),
- (struct sockaddr_in6), (struct sockaddr_in)) addr;
- struct net_buf *dns_data = NULL;
- struct dns_addrinfo info = { 0 };
- size_t addrlen = sizeof(addr);
- int ret, family, sock_error, len;
- socklen_t optlen;
-
- if ((pev->event.revents & ZSOCK_POLLERR) ||
- (pev->event.revents & ZSOCK_POLLNVAL)) {
- (void)zsock_getsockopt(pev->event.fd, SOL_SOCKET,
- SO_DOMAIN, &family, &optlen);
- (void)zsock_getsockopt(pev->event.fd, SOL_SOCKET,
- SO_ERROR, &sock_error, &optlen);
- NET_ERR("Receiver IPv%d socket error (%d)",
- family == AF_INET ? 4 : 6, sock_error);
- ret = DNS_EAI_SYSTEM;
- goto quit;
- }
-
- dns_data = net_buf_alloc(&mdns_msg_pool, BUF_ALLOC_TIMEOUT);
- if (!dns_data) {
- ret = -ENOENT;
- goto quit;
- }
-
- ret = zsock_recvfrom(pev->event.fd, dns_data->data,
- net_buf_max_len(dns_data), 0,
- (struct sockaddr *)&addr, &addrlen);
- if (ret < 0) {
- ret = -errno;
- NET_ERR("recv failed on IPv%d socket (%d)",
- family == AF_INET ? 4 : 6, -ret);
- goto free_buf;
- }
-
- len = ret;
-
- ret = dns_read(pev->event.fd, dns_data, len,
- (struct sockaddr *)&addr, addrlen, &info);
- if (ret < 0 && ret != -EINVAL) {
- NET_DBG("%s read failed (%d)", "mDNS", ret);
- }
-
-free_buf:
- net_buf_unref(dns_data);
-
-quit:
- return ret;
-}
-
-static void svc_handler(struct k_work *work)
-{
- struct net_socket_service_event *pev =
- CONTAINER_OF(work, struct net_socket_service_event, work);
- int ret;
-
- ret = recv_data(pev);
- if (ret < 0) {
- NET_ERR("DNS recv error (%d)", ret);
- }
-}
-
#if defined(CONFIG_NET_IPV6)
static void iface_ipv6_cb(struct net_if *iface, void *user_data)
{
@@ -754,9 +646,53 @@
#define INTERFACE_NAME_LEN 0
#endif
+static int dispatcher_cb(void *my_ctx, int sock,
+ struct sockaddr *addr, size_t addrlen,
+ struct net_buf *dns_data, size_t len)
+{
+ int ret;
+
+ ARG_UNUSED(my_ctx);
+
+ ret = dns_read(sock, dns_data, len, addr, addrlen);
+ if (ret < 0 && ret != -EINVAL) {
+ NET_DBG("%s read failed (%d)", "mDNS", ret);
+ }
+
+ net_buf_unref(dns_data);
+
+ return ret;
+}
+
+static int register_dispatcher(struct mdns_responder_context *ctx,
+ const struct net_socket_service_desc *svc,
+ struct sockaddr *local)
+{
+ ctx->dispatcher.type = DNS_SOCKET_RESPONDER;
+ ctx->dispatcher.cb = dispatcher_cb;
+ ctx->dispatcher.fds = ctx->fds;
+ ctx->dispatcher.fds_len = ARRAY_SIZE(ctx->fds);
+ ctx->dispatcher.sock = ctx->sock;
+ ctx->dispatcher.svc = svc;
+ ctx->dispatcher.mdns_ctx = ctx;
+ ctx->dispatcher.pair = NULL;
+
+ if (IS_ENABLED(CONFIG_NET_IPV6) && local->sa_family == AF_INET6) {
+ memcpy(&ctx->dispatcher.local_addr, local,
+ sizeof(struct sockaddr_in6));
+ } else if (IS_ENABLED(CONFIG_NET_IPV4) && local->sa_family == AF_INET) {
+ memcpy(&ctx->dispatcher.local_addr, local,
+ sizeof(struct sockaddr_in));
+ } else {
+ return -ENOTSUP;
+ }
+
+ return dns_dispatcher_register(&ctx->dispatcher);
+}
+
static int init_listener(void)
{
- int ret, ok = 0, i, ifindex;
+ int ret, ok = 0, ifindex;
char name[INTERFACE_NAME_LEN + 1];
struct ifreq if_req;
struct net_if *iface;
@@ -774,21 +710,22 @@
MAX_IPV6_IFACE_COUNT), iface_count);
}
- ARRAY_FOR_EACH(fds, j) {
- fds[j].fd = -1;
- }
-
#if defined(CONFIG_NET_IPV6)
struct sockaddr_in6 local_addr6;
int v6;
setup_ipv6_addr(&local_addr6);
- for (i = 0; i < MAX_IPV6_IFACE_COUNT; i++) {
+ ARRAY_FOR_EACH(v6_ctx, i) {
+ ARRAY_FOR_EACH(v6_ctx[i].fds, j) {
+ v6_ctx[i].fds[j].fd = -1;
+ }
+
v6 = get_socket(AF_INET6);
if (v6 < 0) {
- NET_ERR("Cannot get %s socket out of %d. Max sockets is %d",
- "IPv6", MAX_IPV6_IFACE_COUNT, CONFIG_NET_MAX_CONTEXTS);
+ NET_ERR("Cannot get %s socket (%d %s interfaces). Max sockets is %d",
+ "IPv6", MAX_IPV6_IFACE_COUNT,
+ "IPv6", CONFIG_NET_MAX_CONTEXTS);
continue;
}
@@ -816,24 +753,18 @@
}
}
- ret = bind_ctx(v6, (struct sockaddr *)&local_addr6,
- sizeof(local_addr6));
- if (ret < 0) {
- zsock_close(v6);
- goto ipv6_out;
- }
+ v6_ctx[i].sock = v6;
+ ret = -1;
- ret = -ENOENT;
-
- ARRAY_FOR_EACH(fds, j) {
- if (fds[j].fd == v6) {
+ ARRAY_FOR_EACH(v6_ctx[i].fds, j) {
+ if (v6_ctx[i].fds[j].fd == v6) {
ret = 0;
break;
}
- if (fds[j].fd < 0) {
- fds[j].fd = v6;
- fds[j].events = ZSOCK_POLLIN;
+ if (v6_ctx[i].fds[j].fd < 0) {
+ v6_ctx[i].fds[j].fd = v6;
+ v6_ctx[i].fds[j].events = ZSOCK_POLLIN;
ret = 0;
break;
}
@@ -845,32 +776,33 @@
continue;
}
- ret = net_socket_service_register(&svc_mdns, fds, ARRAY_SIZE(fds), NULL);
+ ret = register_dispatcher(&v6_ctx[i], &v6_svc, (struct sockaddr *)&local_addr6);
if (ret < 0) {
NET_DBG("Cannot register %s %s socket service (%d)",
"IPv6", "mDNS", ret);
zsock_close(v6);
} else {
- ipv6[i] = v6;
ok++;
}
}
-ipv6_out:
- ; /* Added ";" to avoid clang compile error because of
- * the "struct net_context *v4" after it.
- */
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
+ struct sockaddr_in local_addr4;
int v4;
setup_ipv4_addr(&local_addr4);
- for (i = 0; i < MAX_IPV4_IFACE_COUNT; i++) {
+ ARRAY_FOR_EACH(v4_ctx, i) {
+ ARRAY_FOR_EACH(v4_ctx[i].fds, j) {
+ v4_ctx[i].fds[j].fd = -1;
+ }
+
v4 = get_socket(AF_INET);
if (v4 < 0) {
- NET_ERR("Cannot get %s socket out of %d. Max sockets is %d",
- "IPv4", MAX_IPV4_IFACE_COUNT, CONFIG_NET_MAX_CONTEXTS);
+ NET_ERR("Cannot get %s socket (%d %s interfaces). Max sockets is %d",
+ "IPv4", MAX_IPV4_IFACE_COUNT,
+ "IPv4", CONFIG_NET_MAX_CONTEXTS);
continue;
}
@@ -898,24 +830,18 @@
}
}
- ret = bind_ctx(v4, (struct sockaddr *)&local_addr4,
- sizeof(local_addr4));
- if (ret < 0) {
- zsock_close(v4);
- goto ipv4_out;
- }
+ v4_ctx[i].sock = v4;
+ ret = -1;
- ret = -ENOENT;
-
- ARRAY_FOR_EACH(fds, j) {
- if (fds[j].fd == v4) {
+ ARRAY_FOR_EACH(v4_ctx[i].fds, j) {
+ if (v4_ctx[i].fds[j].fd == v4) {
ret = 0;
break;
}
- if (fds[j].fd < 0) {
- fds[j].fd = v4;
- fds[j].events = ZSOCK_POLLIN;
+ if (v4_ctx[i].fds[j].fd < 0) {
+ v4_ctx[i].fds[j].fd = v4;
+ v4_ctx[i].fds[j].events = ZSOCK_POLLIN;
ret = 0;
break;
}
@@ -927,17 +853,15 @@
continue;
}
- ret = net_socket_service_register(&svc_mdns, fds, ARRAY_SIZE(fds), NULL);
+ ret = register_dispatcher(&v4_ctx[i], &v4_svc, (struct sockaddr *)&local_addr4);
if (ret < 0) {
NET_DBG("Cannot register %s %s socket service (%d)",
"IPv4", "mDNS", ret);
zsock_close(v4);
} else {
- ipv4[i] = v4;
ok++;
}
}
-ipv4_out:
#endif /* CONFIG_NET_IPV4 */
if (!ok) {
@@ -972,4 +896,7 @@
return 0;
}
-SYS_INIT(mdns_responder_init, APPLICATION, CONFIG_MDNS_RESPONDER_INIT_PRIO);
+void mdns_init_responder(void)
+{
+ (void)mdns_responder_init();
+}
diff --git a/subsys/net/lib/dns/resolve.c b/subsys/net/lib/dns/resolve.c
index d866b81..ec25773 100644
--- a/subsys/net/lib/dns/resolve.c
+++ b/subsys/net/lib/dns/resolve.c
@@ -33,23 +33,20 @@
#define DNS_SERVER_COUNT CONFIG_DNS_RESOLVER_MAX_SERVERS
#define SERVER_COUNT (DNS_SERVER_COUNT + DNS_MAX_MCAST_SERVERS)
+extern void dns_dispatcher_svc_handler(struct k_work *work);
+
+NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(resolve_svc, NULL, dns_dispatcher_svc_handler,
+ DNS_RESOLVER_MAX_POLL);
+
#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 K_MSEC(500) /* ms */
-
#define DNS_QUERY_MAX_SIZE (DNS_MSG_HEADER_SIZE + CONFIG_DNS_RESOLVER_MAX_QUERY_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
@@ -64,21 +61,22 @@
#define DNS_IPV4_LEN sizeof(struct in_addr)
#define DNS_IPV6_LEN sizeof(struct in6_addr)
-static void svc_handler(struct k_work *work);
-static int init_called;
-
-NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(svc, NULL, svc_handler, DNS_RESOLVER_MAX_POLL);
+#define DNS_RESOLVER_MIN_BUF 1
+#define DNS_RESOLVER_BUF_CTR (DNS_RESOLVER_MIN_BUF + \
+ CONFIG_DNS_RESOLVER_ADDITIONAL_BUF_CTR)
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, CONFIG_DNS_RESOLVER_MAX_QUERY_LEN,
+NET_BUF_POOL_DEFINE(dns_qname_pool, DNS_RESOLVER_BUF_CTR,
+ CONFIG_DNS_RESOLVER_MAX_QUERY_LEN,
0, NULL);
#ifdef CONFIG_DNS_RESOLVER_CACHE
DNS_CACHE_DEFINE(dns_cache, CONFIG_DNS_RESOLVER_CACHE_MAX_ENTRIES);
#endif /* CONFIG_DNS_RESOLVER_CACHE */
+static int init_called;
static struct dns_resolve_context dns_default_ctx;
/* Must be invoked with context lock held */
@@ -88,6 +86,18 @@
uint8_t *buf, size_t buf_len, size_t max_len,
struct net_buf *dns_qname,
int hop_limit);
+static int dns_read(struct dns_resolve_context *ctx,
+ struct net_buf *dns_data, size_t buf_len,
+ uint16_t *dns_id,
+ struct net_buf *dns_cname,
+ uint16_t *query_hash);
+static inline int get_slot_by_id(struct dns_resolve_context *ctx,
+ uint16_t dns_id,
+ uint16_t query_hash);
+static inline void invoke_query_callback(int status,
+ struct dns_addrinfo *info,
+ struct dns_pending_query *pending_query);
+static void release_query(struct dns_pending_query *pending_query);
static bool server_is_mdns(sa_family_t family, struct sockaddr *addr)
{
@@ -188,6 +198,129 @@
}
}
+static int dispatcher_cb(void *my_ctx, int sock,
+ struct sockaddr *addr, size_t addrlen,
+ struct net_buf *dns_data, size_t len)
+{
+ struct dns_resolve_context *ctx = my_ctx;
+ struct net_buf *dns_cname = NULL;
+ uint16_t query_hash = 0U;
+ uint16_t dns_id = 0U;
+ int ret = 0, i;
+
+ k_mutex_lock(&ctx->lock, K_FOREVER);
+
+ if (ctx->state != DNS_RESOLVE_CONTEXT_ACTIVE) {
+ goto unlock;
+ }
+
+ dns_cname = net_buf_alloc(&dns_qname_pool, ctx->buf_timeout);
+ if (!dns_cname) {
+ ret = DNS_EAI_MEMORY;
+ goto free_buf;
+ }
+
+ ret = dns_read(ctx, dns_data, len, &dns_id, dns_cname, &query_hash);
+ if (!ret) {
+ /* We called the callback already in dns_read() if there
+ * were 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, query_hash);
+ if (i < 0) {
+ goto free_buf;
+ }
+
+ for (j = 0; j < SERVER_COUNT; j++) {
+ if (ctx->servers[j].sock < 0) {
+ continue;
+ }
+
+ ret = dns_write(ctx, j, i, dns_data->data, len,
+ net_buf_max_len(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, query_hash);
+ if (i < 0) {
+ goto free_buf;
+ }
+
+ invoke_query_callback(ret, NULL, &ctx->queries[i]);
+
+ /* Marks the end of the results */
+ release_query(&ctx->queries[i]);
+
+free_buf:
+ if (dns_data) {
+ net_buf_unref(dns_data);
+ }
+
+ if (dns_cname) {
+ net_buf_unref(dns_cname);
+ }
+
+unlock:
+ k_mutex_unlock(&ctx->lock);
+
+ return ret;
+}
+
+static int register_dispatcher(struct dns_resolve_context *ctx,
+ const struct net_socket_service_desc *svc,
+ struct dns_server *server,
+ struct sockaddr *local,
+ const struct in6_addr *addr6,
+ const struct in_addr *addr4)
+{
+ server->dispatcher.type = DNS_SOCKET_RESOLVER;
+ server->dispatcher.cb = dispatcher_cb;
+ server->dispatcher.fds = ctx->fds;
+ server->dispatcher.fds_len = ARRAY_SIZE(ctx->fds);
+ server->dispatcher.sock = server->sock;
+ server->dispatcher.svc = svc;
+ server->dispatcher.resolve_ctx = ctx;
+
+ if (IS_ENABLED(CONFIG_NET_IPV6) &&
+ server->dns_server.sa_family == AF_INET6) {
+ memcpy(&server->dispatcher.local_addr,
+ local,
+ sizeof(struct sockaddr_in6));
+ } else if (IS_ENABLED(CONFIG_NET_IPV4) &&
+ server->dns_server.sa_family == AF_INET) {
+ memcpy(&server->dispatcher.local_addr,
+ local,
+ sizeof(struct sockaddr_in));
+ } else {
+ return -ENOTSUP;
+ }
+
+ return dns_dispatcher_register(&server->dispatcher);
+}
+
/* Must be invoked with context lock held */
static int dns_resolve_init_locked(struct dns_resolve_context *ctx,
const char *servers[],
@@ -208,6 +341,8 @@
struct sockaddr *local_addr = NULL;
socklen_t addr_len = 0;
int i = 0, idx = 0;
+ const struct in6_addr *addr6 = NULL;
+ const struct in_addr *addr4 = NULL;
struct net_if *iface;
int ret, count;
@@ -307,18 +442,44 @@
ctx->servers[i].sock = ret;
- ret = zsock_bind(ctx->servers[i].sock, local_addr, addr_len);
- if (ret < 0) {
- NET_DBG("Cannot bind DNS context (%d)", ret);
- goto fail;
- }
-
if (ctx->servers[i].dns_server.sa_family == AF_INET6) {
iface = net_if_ipv6_select_src_iface(
&net_sin6(&ctx->servers[i].dns_server)->sin6_addr);
+ addr6 = net_if_ipv6_select_src_addr(iface,
+ &net_sin6(&ctx->servers[i].dns_server)->sin6_addr);
} else {
iface = net_if_ipv4_select_src_iface(
&net_sin(&ctx->servers[i].dns_server)->sin_addr);
+ addr4 = net_if_ipv4_select_src_addr(iface,
+ &net_sin(&ctx->servers[i].dns_server)->sin_addr);
+ }
+
+ ARRAY_FOR_EACH(ctx->fds, j) {
+ if (ctx->fds[j].fd == ctx->servers[i].sock) {
+ /* There was query to this server already */
+ ret = 0;
+ break;
+ }
+
+ if (ctx->fds[j].fd < 0) {
+ ctx->fds[j].fd = ctx->servers[i].sock;
+ ctx->fds[j].events = ZSOCK_POLLIN;
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ NET_DBG("Cannot set %s to socket (%d)", "polling", ret);
+ zsock_close(ctx->servers[i].sock);
+ continue;
+ }
+
+ ret = register_dispatcher(ctx, &resolve_svc, &ctx->servers[i], local_addr,
+ addr6, addr4);
+ if (ret < 0) {
+ NET_DBG("Cannot register dispatcher for %s (%d)", "mDNS", ret);
+ goto fail;
}
if (IS_ENABLED(CONFIG_NET_MGMT_EVENT_INFO)) {
@@ -758,7 +919,7 @@
goto finished;
}
- if (ret < 0 || query_idx < 0 ||
+ if ((ret < 0 && ret != DNS_EAI_ALLDONE) || query_idx < 0 ||
query_idx > CONFIG_DNS_NUM_CONCUR_QUERIES) {
goto quit;
}
@@ -778,140 +939,6 @@
return ret;
}
-static int recv_data(struct net_socket_service_event *pev)
-{
- struct dns_resolve_context *ctx = pev->user_data;
- socklen_t optlen = sizeof(int);
- struct net_buf *dns_cname = NULL;
- struct net_buf *dns_data = NULL;
- uint16_t query_hash = 0U;
- uint16_t dns_id = 0U;
- int family = AF_UNSPEC, sock_error;
- int ret = 0, i, len;
-
- k_mutex_lock(&ctx->lock, K_FOREVER);
-
- if (ctx->state != DNS_RESOLVE_CONTEXT_ACTIVE) {
- goto unlock;
- }
-
- if ((pev->event.revents & ZSOCK_POLLERR) ||
- (pev->event.revents & ZSOCK_POLLNVAL)) {
- (void)zsock_getsockopt(pev->event.fd, SOL_SOCKET,
- SO_DOMAIN, &family, &optlen);
- (void)zsock_getsockopt(pev->event.fd, SOL_SOCKET,
- SO_ERROR, &sock_error, &optlen);
- NET_ERR("Receiver IPv%d socket error (%d)",
- family == AF_INET ? 4 : 6, sock_error);
- ret = DNS_EAI_SYSTEM;
- goto unlock;
- }
-
- dns_data = net_buf_alloc(&dns_msg_pool, ctx->buf_timeout);
- if (!dns_data) {
- ret = DNS_EAI_MEMORY;
- goto free_buf;
- }
-
- dns_cname = net_buf_alloc(&dns_qname_pool, ctx->buf_timeout);
- if (!dns_cname) {
- ret = DNS_EAI_MEMORY;
- goto free_buf;
- }
-
- ret = zsock_recvfrom(pev->event.fd, dns_data->data,
- net_buf_max_len(dns_data), 0,
- NULL, NULL);
- if (ret < 0) {
- ret = -errno;
- NET_ERR("recv failed on IPv%d socket (%d)",
- family == AF_INET ? 4 : 6, -ret);
- goto free_buf;
- }
-
- len = ret;
-
- ret = dns_read(ctx, dns_data, len, &dns_id, dns_cname, &query_hash);
- 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, query_hash);
- if (i < 0) {
- goto free_buf;
- }
-
- for (j = 0; j < SERVER_COUNT; j++) {
- if (ctx->servers[j].sock < 0) {
- continue;
- }
-
- ret = dns_write(ctx, j, i, dns_data->data, len,
- net_buf_max_len(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, query_hash);
- if (i < 0) {
- goto free_buf;
- }
-
- invoke_query_callback(ret, NULL, &ctx->queries[i]);
-
- /* Marks the end of the results */
- release_query(&ctx->queries[i]);
-
-free_buf:
- if (dns_data) {
- net_buf_unref(dns_data);
- }
-
- if (dns_cname) {
- net_buf_unref(dns_cname);
- }
-
-unlock:
- k_mutex_unlock(&ctx->lock);
-
- return ret;
-}
-
-static void svc_handler(struct k_work *work)
-{
- struct net_socket_service_event *pev =
- CONTAINER_OF(work, struct net_socket_service_event, work);
- int ret;
-
- ret = recv_data(pev);
- if (ret < 0 && ret != DNS_EAI_ALLDONE) {
- NET_ERR("DNS recv error (%d)", ret);
- }
-}
-
static int set_ttl_hop_limit(int sock, int level, int option, int new_limit)
{
return zsock_setsockopt(sock, level, option, &new_limit, sizeof(new_limit));
@@ -995,12 +1022,6 @@
return ret;
}
- ret = net_socket_service_register(&svc, ctx->fds, ARRAY_SIZE(ctx->fds), ctx);
- if (ret < 0) {
- NET_DBG("Cannot register socket service (%d)", ret);
- return ret;
- }
-
if (family == AF_INET) {
server_addr_len = sizeof(struct sockaddr_in);
} else {
@@ -1021,7 +1042,7 @@
ret = zsock_sendto(sock, buf, len, 0, server, server_addr_len);
if (ret < 0) {
- NET_DBG("Cannot send query (%d)", ret);
+ NET_DBG("Cannot send query (%d)", -errno);
return ret;
}
@@ -1455,6 +1476,8 @@
continue;
}
+ (void)dns_dispatcher_unregister(&ctx->servers[i].dispatcher);
+
if (ctx->servers[i].dns_server.sa_family == AF_INET6) {
iface = net_if_ipv6_select_src_iface(
&net_sin6(&ctx->servers[i].dns_server)->sin6_addr);
@@ -1482,11 +1505,12 @@
}
}
+ (void)dns_dispatcher_unregister(&ctx->servers[i].dispatcher);
+
ctx->servers[i].sock = -1;
}
if (--init_called <= 0) {
- (void)net_socket_service_unregister(&svc);
init_called = 0;
}