Navigation Menu

Skip to content

Commit

Permalink
net: dns: dns-sd: support dns service discovery
Browse files Browse the repository at this point in the history
This change adds support for DNS Service Discovery (DNS-SD)
as described in RFC 6763.

Fixes #29099

Signed-off-by: Christopher Friedt <chrisfriedt@gmail.com>
  • Loading branch information
cfriedt authored and andrewboie committed Nov 10, 2020
1 parent 5c69149 commit e7e5843
Show file tree
Hide file tree
Showing 8 changed files with 1,436 additions and 2 deletions.
2 changes: 1 addition & 1 deletion CODEOWNERS
Validating CODEOWNERS rules …
Expand Up @@ -532,7 +532,7 @@
/subsys/net/buf.c @jukkar @jhedberg @tbursztyka @pfalcon
/subsys/net/ip/ @jukkar @tbursztyka @pfalcon
/subsys/net/lib/ @jukkar @tbursztyka @pfalcon
/subsys/net/lib/dns/ @jukkar @tbursztyka @pfalcon
/subsys/net/lib/dns/ @jukkar @tbursztyka @pfalcon @cfriedt
/subsys/net/lib/lwm2m/ @rlubos
/subsys/net/lib/config/ @jukkar @tbursztyka @pfalcon
/subsys/net/lib/mqtt/ @jukkar @tbursztyka @rlubos
Expand Down
4 changes: 4 additions & 0 deletions include/linker/common-rom.ld
Expand Up @@ -134,6 +134,10 @@
} GROUP_LINK_IN(ROMABLE_REGION)
#endif /* CONFIG_EMUL */

#if defined(CONFIG_DNS_SD)
Z_ITERABLE_SECTION_ROM(dns_sd_rec, 4)
#endif

SECTION_DATA_PROLOGUE(log_const_sections,,)
{
__log_const_start = .;
Expand Down
234 changes: 234 additions & 0 deletions include/net/dns_sd.h
@@ -0,0 +1,234 @@
/** @file
* @brief DNS Service Discovery
*/

/*
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_NET_DNS_SD_H_
#define ZEPHYR_INCLUDE_NET_DNS_SD_H_

#include <stdint.h>
#include <sys/byteorder.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief DNS Service Discovery
*
* @details This API enables services to be advertised via DNS. To
* advvertise a service, system or application code should use
* @ref DNS_SD_REGISTER_TCP_SERVICE or
* @ref DNS_SD_REGISTER_UDP_SERVICE.
*
* @see <a href="https://tools.ietf.org/html/rfc6763">RFC 6763</a>
*
* @defgroup dns_sd DNS Service Discovery
* @ingroup networking
* @{
*/

/** RFC 1034 Section 3.1 */
#define DNS_SD_INSTANCE_MIN_SIZE 1
/** RFC 1034 Section 3.1, RFC 6763 Section 7.2 */
#define DNS_SD_INSTANCE_MAX_SIZE 63
/** RFC 6763 Section 7.2 - inclusive of underscore */
#define DNS_SD_SERVICE_MIN_SIZE 2
/** RFC 6763 Section 7.2 - inclusive of underscore */
#define DNS_SD_SERVICE_MAX_SIZE 16
/** RFC 6763 Section 4.1.2 */
#define DNS_SD_SERVICE_PREFIX '_'
/** RFC 6763 Section 4.1.2 - either _tcp or _udp (case insensitive) */
#define DNS_SD_PROTO_SIZE 4
/** ICANN Rules for TLD naming */
#define DNS_SD_DOMAIN_MIN_SIZE 2
/** RFC 1034 Section 3.1, RFC 6763 Section 7.2 */
#define DNS_SD_DOMAIN_MAX_SIZE 63

/**
* @brief Register a service for DNS Service Discovery
*
* This macro should be used for advanced use cases. Two simple use cases are
* when a custom @p domain or a custom (non-standard) @p proto is required.
*
* Another use case is when the port number is not preassigned. That could
* be for a number of reasons, but the most common use case would be for
* ephemeral port usage - i.e. when the service is bound using port number 0.
* In that case, Zephyr (like other OS's) will simply choose an unused port.
* When using ephemeral ports, it can be helpful to assign @p port to the
* @ref sockaddr_in.sin_port field of an IPv4 @ref sockaddr_in, or to the
* @ref sockaddr_in6.sin6_port field of an IPv6 @ref sockaddr_in6.
*
* The service can be referenced using the @p id variable.
*
* @param id variable name for the DNS-SD service record
* @param instance name of the service instance such as "My HTTP Server"
* @param service name of the service, such as "_http"
* @param proto protocol used by the service - either "_tcp" or "_udp"
* @param domain the domain of the service, such as "local"
* @param text information for the DNS TXT record
* @param port a pointer to the port number that this service will use
*/
#define DNS_SD_REGISTER_SERVICE(id, instance, service, proto, domain, \
text, port) \
static const Z_STRUCT_SECTION_ITERABLE(dns_sd_rec, id) = { \
instance, \
service, \
proto, \
domain, \
(const char *)text, \
sizeof(text) - 1, \
port \
}

/**
* @brief Register a TCP service for DNS Service Discovery
*
* This macro can be used for service advertisement using DNS-SD.
*
* The service can be referenced using the @p id variable.
*
* Example (with TXT):
* @code{c}
* #include <net/dns_sd.h>
* static const bar_txt[] = {
* "\x06" "path=/"
* "\x0f" "this=is the way"
* "\x0e" "foo or=foo not"
* "\x17" "this=has\0embedded\0nulls"
* "\x04" "true"
* };
* // Possibly use an ephemeral port
* // Possibly only assign bar_port when the service is running
* static uint16_t bar_port;
* DNS_SD_REGISTER_TCP_SERVICE(bar, CONFIG_NET_HOSTNAME,
* "_bar", "local", bar_txt, &bar_port);
* @endcode{c}
*
* TXT records begin with a single length byte (hex-encoded)
* and contain key=value pairs. Thus, the length of the key-value pair
* must not exceed 255 bytes. Care must be taken to ensure that the
* encoded length value is correct.
*
* For additional rules on TXT encoding, see RFC 6763, Section 6.
* @param id variable name for the DNS-SD service record
* @param instance name of the service instance such as "My HTTP Server"
* @param service name of the service, such as "_http"
* @param domain the domain of the service, such as "local"
* @param text information for the DNS TXT record
* @param port the port number that this service will use
*
* @see <a href="https://tools.ietf.org/html/rfc6763">RFC 6763</a>
*/
#define DNS_SD_REGISTER_TCP_SERVICE(id, instance, service, domain, text, \
port) \
static const uint16_t id ## _port = sys_cpu_to_be16(port); \
DNS_SD_REGISTER_SERVICE(id, instance, service, "_tcp", domain, \
text, &id ## _port)

/**
* @brief Register a UDP service for DNS Service Discovery
*
* This macro can be used for service advertisement using DNS-SD.
*
* The service can be referenced using the @p id variable.
*
* Example (no TXT):
* @code{c}
* #include <net/dns_sd.h>
* #include <sys/byteorder.h>
* static const foo_port = sys_cpu_to_be16(4242);
* DNS_SD_REGISTER_UDP_SERVICE(foo, CONFIG_NET_HOSTNAME,
* "_foo", DNS_SD_EMPTY_TXT, &foo_port);
* @endcode{c}
*
* @param id variable name for the DNS-SD service record
* @param instance name of the service instance such as "My TFTP Server"
* @param service name of the service, such as "_tftp"
* @param domain the domain of the service, such as "local" or "zephyrproject.org"
* @param text information for the DNS TXT record
* @param port a pointer to the port number that this service will use
*
* @see <a href="https://tools.ietf.org/html/rfc6763">RFC 6763</a>
*/
#define DNS_SD_REGISTER_UDP_SERVICE(id, instance, service, domain, text, \
port) \
static const uint16_t id ## _port = sys_cpu_to_be16(port); \
DNS_SD_REGISTER_SERVICE(id, instance, service, "_udp", domain, \
text, &id ## _port)

/** Empty DNS-SD TXT specifier */
#define DNS_SD_EMPTY_TXT dns_sd_empty_txt

/** @cond INTERNAL_HIDDEN */

/**
* @brief DNS Service Discovery record
*
* This structure used in the implementation of RFC 6763 and should not
* need to be accessed directly from application code.
*
* The @a port pointer must be non-NULL. When the value in @a port
* is non-zero, the service is advertized as being on that particular
* port. When the value in @a port is zero, then the service is not
* advertised.
*
* Thus, it is possible for multiple services to advertise on a
* particular port if they hard-code the port.
*
* @internal
*
* @see <a href="https://tools.ietf.org/html/rfc6763">RFC 6763</a>
*/
struct dns_sd_rec {
/** <Instance> - e.g. "My HTTP Server" */
const char *instance;
/** Top half of the <Service> such as "_http" */
const char *service;
/** Bottom half of the <Service> "_tcp" or "_udp" */
const char *proto;
/** <Domain> such as "local" or "zephyrproject.org" */
const char *domain;
/** DNS TXT record */
const char *text;
/** Size (in bytes) of the DNS TXT record */
size_t text_size;
/** A pointer to the port number used by the service */
const uint16_t *port;
};

/**
* @brief Empty TXT specifier for DNS-SD
*
* @internal
*/
extern const char dns_sd_empty_txt[1];

/** @endcond */

/**
* @brief Obtain the size of DNS-SD TXT data
*
* @param rec the record to in question
* @return the size of the text field
*/
static inline size_t dns_sd_txt_size(const struct dns_sd_rec *rec)
{
return rec->text_size;
}

/**
* @}
*/

#ifdef __cplusplus
};
#endif

#endif /* ZEPHYR_INCLUDE_NET_DNS_SD_H_ */
1 change: 1 addition & 0 deletions subsys/net/lib/dns/CMakeLists.txt
Expand Up @@ -6,6 +6,7 @@ zephyr_library()
zephyr_library_sources(dns_pack.c)

zephyr_library_sources_ifdef(CONFIG_DNS_RESOLVER resolve.c)
zephyr_library_sources_ifdef(CONFIG_DNS_SD dns_sd.c)

if(CONFIG_MDNS_RESPONDER)
zephyr_library_sources(mdns_responder.c)
Expand Down
18 changes: 18 additions & 0 deletions subsys/net/lib/dns/Kconfig
Expand Up @@ -198,3 +198,21 @@ config LLMNR_RESOLVER_ADDITIONAL_BUF_CTR
Number of additional buffers available for the LLMNR responder.

endif # LLMNR_RESPONDER

config DNS_SD
bool "Enable DNS Service Discovery"
help
This option enables DNS Service Discovery for Zephyr. It can
be enabled for virtually any network service with only a few
lines of code and works for both Unicast and Multicast DNS.
See RFC 6763 for more details about DNS-SD.

if DNS_SD

module = DNS_SD
module-dep = NET_LOG
module-str = Log level for DNS-SD
module-help = Enables DNS Service Discovery code to output debug messages.
source "subsys/net/Kconfig.template.log_config.net"

endif # DNS_SD
66 changes: 65 additions & 1 deletion subsys/net/lib/dns/dns_pack.h
Expand Up @@ -21,7 +21,10 @@

/* This is the label's length octet, see 4.1.2. Question section format */
#define DNS_LABEL_LEN_SIZE 1
#define DNS_POINTER_SIZE 2
#define DNS_LABEL_MIN_SIZE 1
#define DNS_LABEL_MAX_SIZE 63
#define DNS_NAME_MAX_SIZE 255
#define DNS_ANSWER_MIN_SIZE 12
#define DNS_COMMON_UINT_SIZE 2

Expand Down Expand Up @@ -86,7 +89,10 @@ enum dns_rr_type {
DNS_RR_TYPE_INVALID = 0,
DNS_RR_TYPE_A = 1, /* IPv4 */
DNS_RR_TYPE_CNAME = 5, /* CNAME */
DNS_RR_TYPE_AAAA = 28 /* IPv6 */
DNS_RR_TYPE_PTR = 12, /* PTR */
DNS_RR_TYPE_TXT = 16, /* TXT */
DNS_RR_TYPE_AAAA = 28, /* IPv6 */
DNS_RR_TYPE_SRV = 33, /* SRV */
};

enum dns_response_type {
Expand All @@ -99,6 +105,7 @@ enum dns_response_type {
enum dns_class {
DNS_CLASS_INVALID = 0,
DNS_CLASS_IN,
DNS_CLASS_FLUSH = BIT(15)
};

enum dns_msg_type {
Expand All @@ -115,6 +122,63 @@ enum dns_header_rcode {
DNS_HEADER_REFUSED
};

struct dns_header {
/** Transaction ID */
uint16_t id;
/**
* | Name | Bit Position | Width | Description |
* |------|--------------|-------|-------------|
* | RCODE | 0 | 4 | Response / Error code |
* | CD | 4 | 1 | |
* | AD | 5 | 1 | Authenticated Data. 0 := Unacceptable, 1 := Acceptable |
* | Z | 6 | 1 | Reserved (WZ/RAZ) |
* | RA | 7 | 1 | Recursion Available. 0 := Unavailable, 1 := Available |
* | RD | 8 | 1 | Recursion Desired. 0 := No Recursion, 1 := Recursion |
* | TC | 9 | 1 | 0 := Not Truncated, 1 := Truncated |
* | AA | 10 | 1 | Answer Authenticated / Answer Authoritative. 0 := Not Authenticated, 1 := Authenticated|
* | Opcode | 11 | 4 | See @ref dns_opcode |
* | QR | 15 | 1 | 0 := Query, 1 := Response |
*/
uint16_t flags;
/** Query count */
uint16_t qdcount;
/** Answer count */
uint16_t ancount;
/** Authority count */
uint16_t nscount;
/** Additional information count */
uint16_t arcount;
/** Flexible array member for records */
uint8_t data[];
} __packed;

struct dns_query {
uint16_t type;
uint16_t class_;
} __packed;

struct dns_rr {
uint16_t type;
uint16_t class_;
uint32_t ttl;
uint16_t rdlength;
uint8_t rdata[];
} __packed;

struct dns_srv_rdata {
uint16_t priority;
uint16_t weight;
uint16_t port;
} __packed;

struct dns_a_rdata {
uint32_t address;
} __packed;

struct dns_aaaa_rdata {
uint8_t address[16];
} __packed;

/** It returns the ID field in the DNS msg header */
static inline int dns_header_id(uint8_t *header)
{
Expand Down

0 comments on commit e7e5843

Please sign in to comment.