1 /** @file
2  *  @brief DNS Service Discovery
3  */
4 
5 /*
6  * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #ifndef ZEPHYR_INCLUDE_NET_DNS_SD_H_
12 #define ZEPHYR_INCLUDE_NET_DNS_SD_H_
13 
14 #include <stdint.h>
15 #include <zephyr/sys/byteorder.h>
16 
17 #ifdef __cplusplus
18 extern "C" {
19 #endif
20 
21 /**
22  * @brief DNS Service Discovery
23  *
24  * @details This API enables services to be advertised via DNS. To
25  * advertise a service, system or application code should use
26  * @ref DNS_SD_REGISTER_TCP_SERVICE or
27  * @ref DNS_SD_REGISTER_UDP_SERVICE.
28  *
29  * @see <a href="https://tools.ietf.org/html/rfc6763">RFC 6763</a>
30  *
31  * @defgroup dns_sd DNS Service Discovery
32  * @since 2.5
33  * @version 0.8.0
34  * @ingroup networking
35  * @{
36  */
37 
38 /** RFC 1034 Section 3.1 */
39 #define DNS_SD_INSTANCE_MIN_SIZE 1
40 /** RFC 1034 Section 3.1, RFC 6763 Section 7.2 */
41 #define DNS_SD_INSTANCE_MAX_SIZE 63
42 /** RFC 6763 Section 7.2 - inclusive of underscore */
43 #define DNS_SD_SERVICE_MIN_SIZE 2
44 /** RFC 6763 Section 7.2 - inclusive of underscore */
45 #define DNS_SD_SERVICE_MAX_SIZE 16
46 /** RFC 6763 Section 4.1.2 */
47 #define DNS_SD_SERVICE_PREFIX '_'
48 /** RFC 6763 Section 4.1.2 - either _tcp or _udp (case insensitive) */
49 #define DNS_SD_PROTO_SIZE 4
50 /** ICANN Rules for TLD naming */
51 #define DNS_SD_DOMAIN_MIN_SIZE 2
52 /** RFC 1034 Section 3.1, RFC 6763 Section 7.2 */
53 #define DNS_SD_DOMAIN_MAX_SIZE 63
54 
55 /**
56  * Minimum number of segments in a fully-qualified name
57  *
58  * This represents FQN's of the form below
59  * ```
60  * <sn>._tcp.<domain>.
61  * ```
62  * Currently sub-types and service domains are unsupported and only the
63  * "local" domain is supported. Specifically, that excludes the following:
64  * ```
65  * <sub>._sub.<sn>._tcp.<servicedomain>.<parentdomain>.
66  * ```
67  * @see <a href="https://datatracker.ietf.org/doc/html/rfc6763">RFC 6763</a>, Section 7.2.
68  */
69 #define DNS_SD_MIN_LABELS 3
70 /**
71  * Maximum number of segments in a fully-qualified name
72  *
73  * This represents FQN's of the form below
74  * ```
75  * <instance>.<sn>._tcp.<domain>.
76  * ```
77  *
78  * Currently sub-types and service domains are unsupported and only the
79  * "local" domain is supported. Specifically, that excludes the following:
80  * ```
81  * <sub>._sub.<sn>._tcp.<servicedomain>.<parentdomain>.
82  * ```
83  * @see <a href="https://datatracker.ietf.org/doc/html/rfc6763">RFC 6763</a>, Section 7.2.
84  */
85 #define DNS_SD_MAX_LABELS 4
86 
87 /**
88  * @brief Register a service for DNS Service Discovery
89  *
90  * This macro should be used for advanced use cases. Two simple use cases are
91  * when a custom @p _domain or a custom (non-standard) @p _proto is required.
92  *
93  * Another use case is when the port number is not preassigned. That could
94  * be for a number of reasons, but the most common use case would be for
95  * ephemeral port usage - i.e. when the service is bound using port number 0.
96  * In that case, Zephyr (like other OS's) will simply choose an unused port.
97  * When using ephemeral ports, it can be helpful to assign @p _port to the
98  * @ref sockaddr_in.sin_port field of an IPv4 @ref sockaddr_in, or to the
99  * @ref sockaddr_in6.sin6_port field of an IPv6 @ref sockaddr_in6.
100  *
101  * The service can be referenced using the @p _id variable.
102  *
103  * @param _id variable name for the DNS-SD service record
104  * @param _instance name of the service instance such as "My HTTP Server"
105  * @param _service name of the service, such as "_http"
106  * @param _proto protocol used by the service - either "_tcp" or "_udp"
107  * @param _domain the domain of the service, such as "local"
108  * @param _text information for the DNS TXT record
109  * @param _port a pointer to the port number that this service will use
110  */
111 #define DNS_SD_REGISTER_SERVICE(_id, _instance, _service, _proto,	\
112 				_domain, _text, _port)			\
113 	static const STRUCT_SECTION_ITERABLE(dns_sd_rec, _id) = {	\
114 		.instance = _instance,					\
115 		.service = _service,					\
116 		.proto = _proto,					\
117 		.domain = _domain,					\
118 		.text = (const char *)_text,				\
119 		.text_size = sizeof(_text) - 1,				\
120 		.port = _port,						\
121 	}
122 
123 /**
124  * @brief Register a TCP service for DNS Service Discovery
125  *
126  * This macro can be used for service advertisement using DNS-SD.
127  *
128  * The service can be referenced using the @p id variable.
129  *
130  * Example (with TXT):
131  * @code{.c}
132  * #include <zephyr/net/dns_sd.h>
133  * static const bar_txt[] = {
134  *   "\x06" "path=/"
135  *   "\x0f" "this=is the way"
136  *   "\x0e" "foo or=foo not"
137  *   "\x17" "this=has\0embedded\0nulls"
138  *   "\x04" "true"
139  * };
140  * // Possibly use an ephemeral port
141  * // Possibly only assign bar_port when the service is running
142  * static uint16_t bar_port;
143  * DNS_SD_REGISTER_TCP_SERVICE(bar, CONFIG_NET_HOSTNAME,
144  *   "_bar", "local", bar_txt, &bar_port);
145  * @endcode
146  *
147  * TXT records begin with a single length byte (hex-encoded)
148  * and contain key=value pairs. Thus, the length of the key-value pair
149  * must not exceed 255 bytes. Care must be taken to ensure that the
150  * encoded length value is correct.
151  *
152  * For additional rules on TXT encoding, see RFC 6763, Section 6.
153 
154  * @param id variable name for the DNS-SD service record
155  * @param instance name of the service instance such as "My HTTP Server"
156  * @param service name of the service, such as "_http"
157  * @param domain the domain of the service, such as "local"
158  * @param text information for the DNS TXT record
159  * @param port the port number that this service will use
160  *
161  * @see <a href="https://tools.ietf.org/html/rfc6763">RFC 6763</a>
162  */
163 #define DNS_SD_REGISTER_TCP_SERVICE(id, instance, service, domain, text, \
164 				    port)				 \
165 	static const uint16_t id ## _port = sys_cpu_to_be16(port); \
166 	DNS_SD_REGISTER_SERVICE(id, instance, service, "_tcp", domain,	 \
167 				text, &id ## _port)
168 
169 /**
170  * @brief Register a UDP service for DNS Service Discovery
171  *
172  * This macro can be used for service advertisement using DNS-SD.
173  *
174  * The service can be referenced using the @p id variable.
175  *
176  * Example (no TXT):
177  * @code{.c}
178  * #include <zephyr/net/dns_sd.h>
179  * #include <zephyr/sys/byteorder.h>
180  * static const foo_port = sys_cpu_to_be16(4242);
181  * DNS_SD_REGISTER_UDP_SERVICE(foo, CONFIG_NET_HOSTNAME,
182  *   "_foo", DNS_SD_EMPTY_TXT, &foo_port);
183  * @endcode
184  *
185  * @param id variable name for the DNS-SD service record
186  * @param instance name of the service instance such as "My TFTP Server"
187  * @param service name of the service, such as "_tftp"
188  * @param domain the domain of the service, such as "local" or "zephyrproject.org"
189  * @param text information for the DNS TXT record
190  * @param port a pointer to the port number that this service will use
191  *
192  * @see <a href="https://tools.ietf.org/html/rfc6763">RFC 6763</a>
193  */
194 #define DNS_SD_REGISTER_UDP_SERVICE(id, instance, service, domain, text, \
195 				    port)				 \
196 	static const uint16_t id ## _port = sys_cpu_to_be16(port); \
197 	DNS_SD_REGISTER_SERVICE(id, instance, service, "_udp", domain,	 \
198 				text, &id ## _port)
199 
200 /** Empty DNS-SD TXT specifier */
201 #define DNS_SD_EMPTY_TXT dns_sd_empty_txt
202 
203 /**
204  * @brief DNS Service Discovery record
205  *
206  * This structure used in the implementation of RFC 6763 and should not
207  * need to be accessed directly from application code.
208  *
209  * The @a port pointer must be non-NULL. When the value in @a port
210  * is non-zero, the service is advertised as being on that particular
211  * port. When the value in @a port is zero, then the service is not
212  * advertised.
213  *
214  * Thus, it is possible for multiple services to advertise on a
215  * particular port if they hard-code the port.
216  *
217  * @see <a href="https://tools.ietf.org/html/rfc6763">RFC 6763</a>
218  */
219 struct dns_sd_rec {
220 	/** "<Instance>" - e.g. "My HTTP Server" */
221 	const char *instance;
222 	/** Top half of the "<Service>" such as "_http" */
223 	const char *service;
224 	/** Bottom half of the "<Service>" "_tcp" or "_udp" */
225 	const char *proto;
226 	/** "<Domain>" such as "local" or "zephyrproject.org" */
227 	const char *domain;
228 	/** DNS TXT record */
229 	const char *text;
230 	/** Size (in bytes) of the DNS TXT record  */
231 	size_t text_size;
232 	/** A pointer to the port number used by the service */
233 	const uint16_t *port;
234 };
235 
236 /** @cond INTERNAL_HIDDEN */
237 
238 /**
239  * @brief Empty TXT specifier for DNS-SD
240  *
241  * @internal
242  */
243 extern const char dns_sd_empty_txt[1];
244 /**
245  * @brief Wildcard Port specifier for DNS-SD
246  *
247  * @internal
248  */
249 extern const uint16_t dns_sd_port_zero;
250 
251 /** @endcond */
252 
253 /**
254  * @brief Obtain the size of DNS-SD TXT data
255  *
256  * @param rec the record to in question
257  * @return the size of the text field
258  */
dns_sd_txt_size(const struct dns_sd_rec * rec)259 static inline size_t dns_sd_txt_size(const struct dns_sd_rec *rec)
260 {
261 	return rec->text_size;
262 }
263 
264 /**
265  * @brief Check if @a rec is a DNS-SD Service Type Enumeration
266  *
267  * DNS-SD Service Type Enumeration is used by network tooling to
268  * acquire a list of all mDNS-advertised services belonging to a
269  * particular host on a particular domain.
270  *
271  * For example, for the domain '.local', the equivalent query
272  * would be '_services._dns-sd._udp.local'.
273  *
274  * Currently, only the '.local' domain is supported.
275  *
276  * @see <a href="https://datatracker.ietf.org/doc/html/rfc6763#section-9">Service Type Enumeration, RFC 6763</a>.
277  *
278  * @param rec the record to in question
279  * @return true if @a rec is a DNS-SD Service Type Enumeration
280  */
281 bool dns_sd_is_service_type_enumeration(const struct dns_sd_rec *rec);
282 
283 /**
284  * @brief Create a wildcard filter for DNS-SD records
285  *
286  * @param filter a pointer to the filter to use
287  */
288 void dns_sd_create_wildcard_filter(struct dns_sd_rec *filter);
289 
290 /**
291  * @}
292  */
293 
294 #ifdef __cplusplus
295 };
296 #endif
297 
298 #endif /* ZEPHYR_INCLUDE_NET_DNS_SD_H_ */
299