1 /*
2  * Copyright (c) 2016 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef _DNS_PACK_H_
8 #define _DNS_PACK_H_
9 
10 #include <zephyr/net/net_ip.h>
11 #include <zephyr/net_buf.h>
12 
13 #include <zephyr/types.h>
14 #include <stddef.h>
15 #include <errno.h>
16 
17 /* See RFC 1035, 4.1.1 Header section format
18  * DNS Message Header is always 12 bytes
19  */
20 #define DNS_MSG_HEADER_SIZE	12
21 
22 /* This is the label's length octet, see 4.1.2. Question section format */
23 #define DNS_LABEL_LEN_SIZE	1
24 #define DNS_POINTER_SIZE	2
25 #define DNS_LABEL_MIN_SIZE	1
26 #define DNS_LABEL_MAX_SIZE	63
27 #define DNS_NAME_MAX_SIZE       255
28 #define DNS_ANSWER_MIN_SIZE	12
29 #define DNS_COMMON_UINT_SIZE	2
30 
31 #define DNS_HEADER_ID_LEN	2
32 #define DNS_HEADER_FLAGS_LEN	2
33 #define DNS_QTYPE_LEN		2
34 #define DNS_QCLASS_LEN		2
35 #define DNS_QDCOUNT_LEN		2
36 #define DNS_ANCOUNT_LEN		2
37 #define DNS_NSCOUNT_LEN		2
38 #define DNS_ARCOUNT_LEN		2
39 #define DNS_TTL_LEN		4
40 #define DNS_RDLENGTH_LEN	2
41 
42 #define NS_CMPRSFLGS    0xc0   /* DNS name compression */
43 
44 /* RFC 1035 '4.1.1. Header section format' defines the following flags:
45  * QR, Opcode, AA, TC, RD, RA, Z and RCODE.
46  * This implementation only uses RD (Recursion Desired).
47  */
48 #define DNS_RECURSION		1
49 
50 /* These two defines represent the 3rd and 4th bytes of the DNS msg header.
51  * See RFC 1035, 4.1.1. Header section format.
52  */
53 #define DNS_FLAGS1		DNS_RECURSION	/* QR, Opcode, AA, and TC = 0 */
54 #define DNS_FLAGS2		0		/* RA, Z and RCODE = 0 */
55 
56 /**
57  * DNS message structure for DNS responses
58  *
59  * Structure that points to the buffer containing the DNS message. It also
60  * contains some decodified message's properties that can not be recovered
61  * easily:
62  * - cname_offset
63  * - query_offset
64  * - answer_offset:
65  *     + response_type: It indicates the response's content type. It could be
66  *       an IP address, a CNAME with IP (two answers), a CNAME with no IP
67  *       address. See enum dns_response_type for more details.
68  *     + response_position: this is an offset. It holds the starting byte of
69  *       the field containing the desired info. For example an IPv4 address.
70  *     + response_length: this is an offset. It holds the response's length.
71  */
72 struct dns_msg_t {
73 	uint8_t *msg;
74 
75 	int response_type;
76 	uint16_t response_position;
77 	uint16_t response_length;
78 
79 	uint16_t query_offset;
80 	uint16_t answer_offset;
81 	uint16_t msg_size;
82 };
83 
84 #define DNS_MSG_INIT(b, s)	{.msg = b, .msg_size = s,	\
85 				 .response_type = -EINVAL}
86 
87 
88 enum dns_rr_type {
89 	DNS_RR_TYPE_INVALID = 0,
90 	DNS_RR_TYPE_A	= 1,		/* IPv4  */
91 	DNS_RR_TYPE_CNAME = 5,		/* CNAME */
92 	DNS_RR_TYPE_PTR = 12,		/* PTR   */
93 	DNS_RR_TYPE_TXT = 16,		/* TXT   */
94 	DNS_RR_TYPE_AAAA = 28,		/* IPv6  */
95 	DNS_RR_TYPE_SRV = 33,		/* SRV   */
96 	DNS_RR_TYPE_HTTPS = 65,		/* HTTPS */
97 	DNS_RR_TYPE_ANY = 0xff,		/* ANY (all records)   */
98 };
99 
100 enum dns_response_type {
101 	DNS_RESPONSE_INVALID = -EINVAL,
102 	DNS_RESPONSE_IP = 1,
103 	DNS_RESPONSE_DATA,
104 	DNS_RESPONSE_TXT,
105 	DNS_RESPONSE_SRV,
106 	DNS_RESPONSE_CNAME_WITH_IP,
107 	DNS_RESPONSE_CNAME_NO_IP
108 };
109 
110 enum dns_class {
111 	DNS_CLASS_INVALID = 0,
112 	DNS_CLASS_IN,
113 	DNS_CLASS_FLUSH = BIT(15)
114 };
115 
116 enum dns_msg_type {
117 	DNS_QUERY = 0,
118 	DNS_RESPONSE
119 };
120 
121 enum dns_header_rcode {
122 	DNS_HEADER_NOERROR = 0,
123 	DNS_HEADER_FORMATERROR,
124 	DNS_HEADER_SERVERFAILURE,
125 	DNS_HEADER_NAMEERROR,
126 	DNS_HEADER_NOTIMPLEMENTED,
127 	DNS_HEADER_REFUSED
128 };
129 
130 struct dns_header {
131 	/** Transaction ID */
132 	uint16_t id;
133 	/**
134 	 * | Name | Bit Position | Width | Description |
135 	 * |------|--------------|-------|-------------|
136 	 * | RCODE | 0 | 4 | Response / Error code |
137 	 * | CD | 4 | 1 | |
138 	 * | AD | 5 | 1 | Authenticated Data. 0 := Unacceptable, 1 := Acceptable |
139 	 * | Z | 6 | 1 | Reserved (WZ/RAZ) |
140 	 * | RA | 7 | 1 | Recursion Available. 0 := Unavailable, 1 := Available |
141 	 * | RD | 8 | 1 | Recursion Desired. 0 := No Recursion, 1 := Recursion |
142 	 * | TC | 9 | 1 | 0 := Not Truncated, 1 := Truncated |
143 	 * | AA | 10 | 1 | Answer Authenticated / Answer Authoritative. 0 := Not Authenticated, 1 := Authenticated|
144 	 * | Opcode | 11 | 4 | See @ref dns_opcode |
145 	 * | QR | 15 | 1 | 0 := Query, 1 := Response |
146 	 */
147 	uint16_t flags;
148 	/** Query count */
149 	uint16_t qdcount;
150 	/** Answer count */
151 	uint16_t ancount;
152 	/** Authority count */
153 	uint16_t nscount;
154 	/** Additional information count */
155 	uint16_t arcount;
156 	/** Flexible array member for records */
157 	uint8_t data[];
158 } __packed;
159 
160 struct dns_query {
161 	uint16_t type;
162 	uint16_t class_;
163 } __packed;
164 
165 struct dns_rr {
166 	uint16_t type;
167 	uint16_t class_;
168 	uint32_t ttl;
169 	uint16_t rdlength;
170 	uint8_t rdata[];
171 } __packed;
172 
173 struct dns_srv_rdata {
174 	uint16_t priority;
175 	uint16_t weight;
176 	uint16_t port;
177 } __packed;
178 
179 struct dns_a_rdata {
180 	uint32_t address;
181 } __packed;
182 
183 struct dns_aaaa_rdata {
184 	uint8_t address[16];
185 } __packed;
186 
187 /** It returns the ID field in the DNS msg header	*/
dns_header_id(uint8_t * header)188 static inline int dns_header_id(uint8_t *header)
189 {
190 	return net_htons(UNALIGNED_GET((uint16_t *)(header)));
191 }
192 
193 /* inline unpack routines are used to unpack data from network
194  * order to cpu. Similar routines without the unpack prefix are
195  * used for cpu to network order.
196  */
dns_unpack_header_id(uint8_t * header)197 static inline int dns_unpack_header_id(uint8_t *header)
198 {
199 	return net_ntohs(UNALIGNED_GET((uint16_t *)(header)));
200 }
201 
202 /** It returns the QR field in the DNS msg header	*/
dns_header_qr(uint8_t * header)203 static inline int dns_header_qr(uint8_t *header)
204 {
205 	return ((*(header + 2)) & 0x80) ? 1 : 0;
206 }
207 
208 /** It returns the OPCODE field in the DNS msg header	*/
dns_header_opcode(uint8_t * header)209 static inline int dns_header_opcode(uint8_t *header)
210 {
211 	return ((*(header + 2)) & 0x70) >> 1;
212 }
213 
214 /** It returns the AA field in the DNS msg header	*/
dns_header_aa(uint8_t * header)215 static inline int dns_header_aa(uint8_t *header)
216 {
217 	return ((*(header + 2)) & 0x04) ? 1 : 0;
218 }
219 
220 /** It returns the TC field in the DNS msg header	*/
dns_header_tc(uint8_t * header)221 static inline int dns_header_tc(uint8_t *header)
222 {
223 	return ((*(header + 2)) & 0x02) ? 1 : 0;
224 }
225 
226 /** It returns the RD field in the DNS msg header	*/
dns_header_rd(uint8_t * header)227 static inline int dns_header_rd(uint8_t *header)
228 {
229 	return ((*(header + 2)) & 0x01) ? 1 : 0;
230 }
231 
232 /** It returns the RA field in the DNS msg header	*/
dns_header_ra(uint8_t * header)233 static inline int dns_header_ra(uint8_t *header)
234 {
235 	return ((*(header + 3)) & 0x80) >> 7;
236 }
237 
238 /** It returns the Z field in the DNS msg header	*/
dns_header_z(uint8_t * header)239 static inline int dns_header_z(uint8_t *header)
240 {
241 	return ((*(header + 3)) & 0x70) >> 4;
242 }
243 
244 /** It returns the RCODE field in the DNS msg header	*/
dns_header_rcode(uint8_t * header)245 static inline int dns_header_rcode(uint8_t *header)
246 {
247 	return ((*(header + 3)) & 0x0F);
248 }
249 
250 /** It returns the QDCOUNT field in the DNS msg header	*/
dns_header_qdcount(uint8_t * header)251 static inline int dns_header_qdcount(uint8_t *header)
252 {
253 	return net_htons(UNALIGNED_GET((uint16_t *)(header + 4)));
254 }
255 
dns_unpack_header_qdcount(uint8_t * header)256 static inline int dns_unpack_header_qdcount(uint8_t *header)
257 {
258 	return net_ntohs(UNALIGNED_GET((uint16_t *)(header + 4)));
259 }
260 
261 /** It returns the ANCOUNT field in the DNS msg header	*/
dns_header_ancount(uint8_t * header)262 static inline int dns_header_ancount(uint8_t *header)
263 {
264 	return net_htons(UNALIGNED_GET((uint16_t *)(header + 6)));
265 }
266 
dns_unpack_header_ancount(uint8_t * header)267 static inline int dns_unpack_header_ancount(uint8_t *header)
268 {
269 	return net_ntohs(UNALIGNED_GET((uint16_t *)(header + 6)));
270 }
271 
272 /** It returns the NSCOUNT field in the DNS msg header	*/
dns_header_nscount(uint8_t * header)273 static inline int dns_header_nscount(uint8_t *header)
274 {
275 	return net_htons(UNALIGNED_GET((uint16_t *)(header + 8)));
276 }
277 
278 /** It returns the ARCOUNT field in the DNS msg header	*/
dns_header_arcount(uint8_t * header)279 static inline int dns_header_arcount(uint8_t *header)
280 {
281 	return net_htons(UNALIGNED_GET((uint16_t *)(header + 10)));
282 }
283 
dns_query_qtype(uint8_t * question)284 static inline int dns_query_qtype(uint8_t *question)
285 {
286 	return net_htons(UNALIGNED_GET((uint16_t *)(question + 0)));
287 }
288 
dns_unpack_query_qtype(const uint8_t * question)289 static inline int dns_unpack_query_qtype(const uint8_t *question)
290 {
291 	return net_ntohs(UNALIGNED_GET((uint16_t *)(question + 0)));
292 }
293 
dns_query_qclass(uint8_t * question)294 static inline int dns_query_qclass(uint8_t *question)
295 {
296 	return net_htons(UNALIGNED_GET((uint16_t *)(question + 2)));
297 }
298 
dns_unpack_query_qclass(const uint8_t * question)299 static inline int dns_unpack_query_qclass(const uint8_t *question)
300 {
301 	return net_ntohs(UNALIGNED_GET((uint16_t *)(question + 2)));
302 }
303 
dns_answer_type(uint16_t dname_size,uint8_t * answer)304 static inline int dns_answer_type(uint16_t dname_size, uint8_t *answer)
305 {
306 	/* 4.1.3. Resource record format */
307 	return net_ntohs(UNALIGNED_GET((uint16_t *)(answer + dname_size + 0)));
308 }
309 
dns_answer_class(uint16_t dname_size,uint8_t * answer)310 static inline int dns_answer_class(uint16_t dname_size, uint8_t *answer)
311 {
312 	/* 4.1.3. Resource record format */
313 	return net_ntohs(UNALIGNED_GET((uint16_t *)(answer + dname_size + 2)));
314 }
315 
dns_answer_ttl(uint16_t dname_size,uint8_t * answer)316 static inline int dns_answer_ttl(uint16_t dname_size, uint8_t *answer)
317 {
318 	return net_ntohl(UNALIGNED_GET((uint32_t *)(answer + dname_size + 4)));
319 }
320 
dns_answer_rdlength(uint16_t dname_size,uint8_t * answer)321 static inline int dns_answer_rdlength(uint16_t dname_size,
322 					     uint8_t *answer)
323 {
324 	return net_ntohs(UNALIGNED_GET((uint16_t *)(answer + dname_size + 8)));
325 }
326 
dns_unpack_srv_priority(const uint8_t * srv)327 static inline int dns_unpack_srv_priority(const uint8_t *srv)
328 {
329 	return net_ntohs(UNALIGNED_GET((uint16_t *)(srv + 0)));
330 }
331 
dns_unpack_srv_weight(const uint8_t * srv)332 static inline int dns_unpack_srv_weight(const uint8_t *srv)
333 {
334 	return net_ntohs(UNALIGNED_GET((uint16_t *)(srv + 2)));
335 }
336 
dns_unpack_srv_port(const uint8_t * srv)337 static inline int dns_unpack_srv_port(const uint8_t *srv)
338 {
339 	return net_ntohs(UNALIGNED_GET((uint16_t *)(srv + 4)));
340 }
341 
342 /**
343  * @brief Packs a QNAME
344  *
345  * @param len Bytes used by this function
346  * @param buf Buffer
347  * @param sizeof Buffer's size
348  * @param domain_name Something like www.example.com
349  * @retval 0 on success
350  * @retval -ENOMEM if there is no enough space to store the resultant QNAME
351  * @retval -EINVAL if an invalid parameter was passed as an argument
352  */
353 int dns_msg_pack_qname(uint16_t *len, uint8_t *buf, uint16_t size,
354 		       const char *domain_name);
355 
356 /**
357  * @brief Unpacks an answer message
358  *
359  * @param dns_msg Structure
360  * @param dname_ptr An index to the previous CNAME. For example for the
361  *        first answer, ptr must be 0x0c, the DNAME at the question.
362  * @param ttl TTL answer parameter.
363  * @param type Answer type parameter.
364  * @retval 0 on success
365  * @retval -ENOMEM on error
366  */
367 int dns_unpack_answer(struct dns_msg_t *dns_msg, int dname_ptr, uint32_t *ttl,
368 		      enum dns_rr_type *type);
369 
370 /**
371  * @brief Unpacks the header's response.
372  *
373  * @param msg Structure containing the response.
374  * @param src_id Transaction id, it must match the id used in the query
375  *        datagram sent to the DNS server.
376  * @retval 0 on success
377  * @retval -ENOMEM if the buffer in msg has no enough space to store the header.
378  *         The header is always 12 bytes length.
379  * @retval -EINVAL if the src_id does not match the header's id, or if the
380  *         header's QR value is not DNS_RESPONSE or if the header's OPCODE
381  *         value is not DNS_QUERY, or if the header's Z value is not 0 or if
382  *         the question counter is not 1 or the answer counter is less than 1.
383  * @retval RFC 1035 RCODEs (> 0) 1 Format error, 2 Server failure, 3 Name Error,
384  *         4 Not Implemented and 5 Refused.
385  */
386 int dns_unpack_response_header(struct dns_msg_t *msg, int src_id);
387 
388 /**
389  * @brief Packs the query message
390  *
391  * @param buf Buffer that will contain the resultant query
392  * @param len Number of bytes used to encode the query
393  * @param size Buffer size
394  * @param qname Domain name represented as a sequence of labels.
395  *        See RFC 1035, 4.1.2. Question section format.
396  * @param qname_len Number of octets in qname.
397  * @param id Transaction Identifier
398  * @param qtype Query type: AA, AAAA. See enum dns_rr_type
399  * @retval 0 on success
400  * @retval On error, a negative value is returned.
401  *         See: dns_msg_pack_query_header and  dns_msg_pack_qname.
402  */
403 int dns_msg_pack_query(uint8_t *buf, uint16_t *len, uint16_t size,
404 		       uint8_t *qname, uint16_t qname_len, uint16_t id,
405 		       enum dns_rr_type qtype);
406 
407 /**
408  * @brief Unpacks the response's query.
409  *
410  * @details RFC 1035 states that the response's query comes after the first
411  *          12 bytes i.e., after the message's header. This function computes
412  *          the answer_offset field.
413  *
414  * @param dns_msg Structure containing the message.
415  * @retval 0 on success
416  * @retval -ENOMEM if the null label is not found after traversing the buffer
417  *         or if QCLASS and QTYPE are not found.
418  * @retval -EINVAL if QTYPE is not "A" (IPv4) or "AAAA" (IPv6) or if QCLASS
419  *         is not "IN".
420  */
421 int dns_unpack_response_query(struct dns_msg_t *dns_msg);
422 
423 /**
424  * @brief Copies the qname from dns_msg to buf
425  *
426  * @details This routine implements the algorithm described in RFC 1035, 4.1.4.
427  *          Message compression to copy the qname (perhaps containing pointers
428  *          with offset) to the linear buffer buf. Pointers are removed and
429  *          only the "true" labels are copied.
430  *
431  * @param buf Output buffer
432  * @param len Output buffer's length
433  * @param size Output buffer's size
434  * @param dns_msg Structure containing the message
435  * @param pos QNAME's position in dns_msg->msg
436  * @retval 0 on success
437  * @retval -EINVAL if an invalid parameter was passed as an argument
438  * @retval -ENOMEM if the label's size is corrupted
439  */
440 int dns_copy_qname(uint8_t *buf, uint16_t *len, uint16_t size,
441 		   struct dns_msg_t *dns_msg, uint16_t pos);
442 
443 /**
444  * @brief Unpacks the mDNS query. This is special version for multicast DNS
445  *        as it skips checks to various fields as described in RFC 6762
446  *        chapter 18.
447  *
448  * @param msg Structure containing the response.
449  * @param src_id Transaction id, this is returned to the caller.
450  * @retval 0 on success, <0 if error
451  * @retval -ENOMEM if the buffer in msg has no enough space to store the header.
452  *         The header is always 12 bytes length.
453  * @retval -EINVAL if the src_id does not match the header's id, or if the
454  *         header's QR value is not DNS_RESPONSE or if the header's OPCODE
455  *         value is not DNS_QUERY, or if the header's Z value is not 0 or if
456  *         the question counter is not 1 or the answer counter is less than 1.
457  * @retval RFC 1035 RCODEs (> 0) 1 Format error, 2 Server failure, 3 Name Error,
458  *         4 Not Implemented and 5 Refused.
459  */
460 int mdns_unpack_query_header(struct dns_msg_t *msg, uint16_t *src_id);
461 
llmnr_unpack_query_header(struct dns_msg_t * msg,uint16_t * src_id)462 static inline int llmnr_unpack_query_header(struct dns_msg_t *msg,
463 					    uint16_t *src_id)
464 {
465 	return mdns_unpack_query_header(msg, src_id);
466 }
467 
468 /**
469  * @brief Unpacks the query.
470  *
471  * @param dns_msg Structure containing the message.
472  * @param buf Result buf
473  * @param qtype Query type is returned to caller
474  * @param qclass Query class is returned to caller
475  * @retval 0 on success
476  * @retval -ENOMEM if the null label is not found after traversing the buffer
477  *         or if QCLASS and QTYPE are not found.
478  * @retval -EINVAL if QTYPE is not "A" (IPv4) or "AAAA" (IPv6) or if QCLASS
479  *         is not "IN".
480  */
481 int dns_unpack_query(struct dns_msg_t *dns_msg, struct net_buf *buf,
482 		     enum dns_rr_type *qtype,
483 		     enum dns_class *qclass);
484 
485 /**
486  * @brief Map query type number to a string.
487  *
488  * @param qtype Query type
489  *
490  * @return Printable query type name.
491  */
492 const char *dns_qtype_to_str(enum dns_rr_type qtype);
493 
494 int dns_unpack_name(const uint8_t *msg, int maxlen, const uint8_t *src,
495 		    struct net_buf *buf, const uint8_t **eol);
496 
497 #endif
498