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,
103 DNS_RESPONSE_DATA,
104 DNS_RESPONSE_CNAME_WITH_IP,
105 DNS_RESPONSE_CNAME_NO_IP
106 };
107
108 enum dns_class {
109 DNS_CLASS_INVALID = 0,
110 DNS_CLASS_IN,
111 DNS_CLASS_FLUSH = BIT(15)
112 };
113
114 enum dns_msg_type {
115 DNS_QUERY = 0,
116 DNS_RESPONSE
117 };
118
119 enum dns_header_rcode {
120 DNS_HEADER_NOERROR = 0,
121 DNS_HEADER_FORMATERROR,
122 DNS_HEADER_SERVERFAILURE,
123 DNS_HEADER_NAMEERROR,
124 DNS_HEADER_NOTIMPLEMENTED,
125 DNS_HEADER_REFUSED
126 };
127
128 struct dns_header {
129 /** Transaction ID */
130 uint16_t id;
131 /**
132 * | Name | Bit Position | Width | Description |
133 * |------|--------------|-------|-------------|
134 * | RCODE | 0 | 4 | Response / Error code |
135 * | CD | 4 | 1 | |
136 * | AD | 5 | 1 | Authenticated Data. 0 := Unacceptable, 1 := Acceptable |
137 * | Z | 6 | 1 | Reserved (WZ/RAZ) |
138 * | RA | 7 | 1 | Recursion Available. 0 := Unavailable, 1 := Available |
139 * | RD | 8 | 1 | Recursion Desired. 0 := No Recursion, 1 := Recursion |
140 * | TC | 9 | 1 | 0 := Not Truncated, 1 := Truncated |
141 * | AA | 10 | 1 | Answer Authenticated / Answer Authoritative. 0 := Not Authenticated, 1 := Authenticated|
142 * | Opcode | 11 | 4 | See @ref dns_opcode |
143 * | QR | 15 | 1 | 0 := Query, 1 := Response |
144 */
145 uint16_t flags;
146 /** Query count */
147 uint16_t qdcount;
148 /** Answer count */
149 uint16_t ancount;
150 /** Authority count */
151 uint16_t nscount;
152 /** Additional information count */
153 uint16_t arcount;
154 /** Flexible array member for records */
155 uint8_t data[];
156 } __packed;
157
158 struct dns_query {
159 uint16_t type;
160 uint16_t class_;
161 } __packed;
162
163 struct dns_rr {
164 uint16_t type;
165 uint16_t class_;
166 uint32_t ttl;
167 uint16_t rdlength;
168 uint8_t rdata[];
169 } __packed;
170
171 struct dns_srv_rdata {
172 uint16_t priority;
173 uint16_t weight;
174 uint16_t port;
175 } __packed;
176
177 struct dns_a_rdata {
178 uint32_t address;
179 } __packed;
180
181 struct dns_aaaa_rdata {
182 uint8_t address[16];
183 } __packed;
184
185 /** It returns the ID field in the DNS msg header */
dns_header_id(uint8_t * header)186 static inline int dns_header_id(uint8_t *header)
187 {
188 return htons(UNALIGNED_GET((uint16_t *)(header)));
189 }
190
191 /* inline unpack routines are used to unpack data from network
192 * order to cpu. Similar routines without the unpack prefix are
193 * used for cpu to network order.
194 */
dns_unpack_header_id(uint8_t * header)195 static inline int dns_unpack_header_id(uint8_t *header)
196 {
197 return ntohs(UNALIGNED_GET((uint16_t *)(header)));
198 }
199
200 /** It returns the QR field in the DNS msg header */
dns_header_qr(uint8_t * header)201 static inline int dns_header_qr(uint8_t *header)
202 {
203 return ((*(header + 2)) & 0x80) ? 1 : 0;
204 }
205
206 /** It returns the OPCODE field in the DNS msg header */
dns_header_opcode(uint8_t * header)207 static inline int dns_header_opcode(uint8_t *header)
208 {
209 return ((*(header + 2)) & 0x70) >> 1;
210 }
211
212 /** It returns the AA field in the DNS msg header */
dns_header_aa(uint8_t * header)213 static inline int dns_header_aa(uint8_t *header)
214 {
215 return ((*(header + 2)) & 0x04) ? 1 : 0;
216 }
217
218 /** It returns the TC field in the DNS msg header */
dns_header_tc(uint8_t * header)219 static inline int dns_header_tc(uint8_t *header)
220 {
221 return ((*(header + 2)) & 0x02) ? 1 : 0;
222 }
223
224 /** It returns the RD field in the DNS msg header */
dns_header_rd(uint8_t * header)225 static inline int dns_header_rd(uint8_t *header)
226 {
227 return ((*(header + 2)) & 0x01) ? 1 : 0;
228 }
229
230 /** It returns the RA field in the DNS msg header */
dns_header_ra(uint8_t * header)231 static inline int dns_header_ra(uint8_t *header)
232 {
233 return ((*(header + 3)) & 0x80) >> 7;
234 }
235
236 /** It returns the Z field in the DNS msg header */
dns_header_z(uint8_t * header)237 static inline int dns_header_z(uint8_t *header)
238 {
239 return ((*(header + 3)) & 0x70) >> 4;
240 }
241
242 /** It returns the RCODE field in the DNS msg header */
dns_header_rcode(uint8_t * header)243 static inline int dns_header_rcode(uint8_t *header)
244 {
245 return ((*(header + 3)) & 0x0F);
246 }
247
248 /** It returns the QDCOUNT field in the DNS msg header */
dns_header_qdcount(uint8_t * header)249 static inline int dns_header_qdcount(uint8_t *header)
250 {
251 return htons(UNALIGNED_GET((uint16_t *)(header + 4)));
252 }
253
dns_unpack_header_qdcount(uint8_t * header)254 static inline int dns_unpack_header_qdcount(uint8_t *header)
255 {
256 return ntohs(UNALIGNED_GET((uint16_t *)(header + 4)));
257 }
258
259 /** It returns the ANCOUNT field in the DNS msg header */
dns_header_ancount(uint8_t * header)260 static inline int dns_header_ancount(uint8_t *header)
261 {
262 return htons(UNALIGNED_GET((uint16_t *)(header + 6)));
263 }
264
dns_unpack_header_ancount(uint8_t * header)265 static inline int dns_unpack_header_ancount(uint8_t *header)
266 {
267 return ntohs(UNALIGNED_GET((uint16_t *)(header + 6)));
268 }
269
270 /** It returns the NSCOUNT field in the DNS msg header */
dns_header_nscount(uint8_t * header)271 static inline int dns_header_nscount(uint8_t *header)
272 {
273 return htons(UNALIGNED_GET((uint16_t *)(header + 8)));
274 }
275
276 /** It returns the ARCOUNT field in the DNS msg header */
dns_header_arcount(uint8_t * header)277 static inline int dns_header_arcount(uint8_t *header)
278 {
279 return htons(UNALIGNED_GET((uint16_t *)(header + 10)));
280 }
281
dns_query_qtype(uint8_t * question)282 static inline int dns_query_qtype(uint8_t *question)
283 {
284 return htons(UNALIGNED_GET((uint16_t *)(question + 0)));
285 }
286
dns_unpack_query_qtype(const uint8_t * question)287 static inline int dns_unpack_query_qtype(const uint8_t *question)
288 {
289 return ntohs(UNALIGNED_GET((uint16_t *)(question + 0)));
290 }
291
dns_query_qclass(uint8_t * question)292 static inline int dns_query_qclass(uint8_t *question)
293 {
294 return htons(UNALIGNED_GET((uint16_t *)(question + 2)));
295 }
296
dns_unpack_query_qclass(const uint8_t * question)297 static inline int dns_unpack_query_qclass(const uint8_t *question)
298 {
299 return ntohs(UNALIGNED_GET((uint16_t *)(question + 2)));
300 }
301
dns_answer_type(uint16_t dname_size,uint8_t * answer)302 static inline int dns_answer_type(uint16_t dname_size, uint8_t *answer)
303 {
304 /* 4.1.3. Resource record format */
305 return ntohs(UNALIGNED_GET((uint16_t *)(answer + dname_size + 0)));
306 }
307
dns_answer_class(uint16_t dname_size,uint8_t * answer)308 static inline int dns_answer_class(uint16_t dname_size, uint8_t *answer)
309 {
310 /* 4.1.3. Resource record format */
311 return ntohs(UNALIGNED_GET((uint16_t *)(answer + dname_size + 2)));
312 }
313
dns_answer_ttl(uint16_t dname_size,uint8_t * answer)314 static inline int dns_answer_ttl(uint16_t dname_size, uint8_t *answer)
315 {
316 return ntohl(UNALIGNED_GET((uint32_t *)(answer + dname_size + 4)));
317 }
318
dns_answer_rdlength(uint16_t dname_size,uint8_t * answer)319 static inline int dns_answer_rdlength(uint16_t dname_size,
320 uint8_t *answer)
321 {
322 return ntohs(UNALIGNED_GET((uint16_t *)(answer + dname_size + 8)));
323 }
324
325 /**
326 * @brief Packs a QNAME
327 *
328 * @param len Bytes used by this function
329 * @param buf Buffer
330 * @param sizeof Buffer's size
331 * @param domain_name Something like www.example.com
332 * @retval 0 on success
333 * @retval -ENOMEM if there is no enough space to store the resultant QNAME
334 * @retval -EINVAL if an invalid parameter was passed as an argument
335 */
336 int dns_msg_pack_qname(uint16_t *len, uint8_t *buf, uint16_t size,
337 const char *domain_name);
338
339 /**
340 * @brief Unpacks an answer message
341 *
342 * @param dns_msg Structure
343 * @param dname_ptr An index to the previous CNAME. For example for the
344 * first answer, ptr must be 0x0c, the DNAME at the question.
345 * @param ttl TTL answer parameter.
346 * @param type Answer type parameter.
347 * @retval 0 on success
348 * @retval -ENOMEM on error
349 */
350 int dns_unpack_answer(struct dns_msg_t *dns_msg, int dname_ptr, uint32_t *ttl,
351 enum dns_rr_type *type);
352
353 /**
354 * @brief Unpacks the header's response.
355 *
356 * @param msg Structure containing the response.
357 * @param src_id Transaction id, it must match the id used in the query
358 * datagram sent to the DNS server.
359 * @retval 0 on success
360 * @retval -ENOMEM if the buffer in msg has no enough space to store the header.
361 * The header is always 12 bytes length.
362 * @retval -EINVAL if the src_id does not match the header's id, or if the
363 * header's QR value is not DNS_RESPONSE or if the header's OPCODE
364 * value is not DNS_QUERY, or if the header's Z value is not 0 or if
365 * the question counter is not 1 or the answer counter is less than 1.
366 * @retval RFC 1035 RCODEs (> 0) 1 Format error, 2 Server failure, 3 Name Error,
367 * 4 Not Implemented and 5 Refused.
368 */
369 int dns_unpack_response_header(struct dns_msg_t *msg, int src_id);
370
371 /**
372 * @brief Packs the query message
373 *
374 * @param buf Buffer that will contain the resultant query
375 * @param len Number of bytes used to encode the query
376 * @param size Buffer size
377 * @param qname Domain name represented as a sequence of labels.
378 * See RFC 1035, 4.1.2. Question section format.
379 * @param qname_len Number of octets in qname.
380 * @param id Transaction Identifier
381 * @param qtype Query type: AA, AAAA. See enum dns_rr_type
382 * @retval 0 on success
383 * @retval On error, a negative value is returned.
384 * See: dns_msg_pack_query_header and dns_msg_pack_qname.
385 */
386 int dns_msg_pack_query(uint8_t *buf, uint16_t *len, uint16_t size,
387 uint8_t *qname, uint16_t qname_len, uint16_t id,
388 enum dns_rr_type qtype);
389
390 /**
391 * @brief Unpacks the response's query.
392 *
393 * @details RFC 1035 states that the response's query comes after the first
394 * 12 bytes i.e., after the message's header. This function computes
395 * the answer_offset field.
396 *
397 * @param dns_msg Structure containing the message.
398 * @retval 0 on success
399 * @retval -ENOMEM if the null label is not found after traversing the buffer
400 * or if QCLASS and QTYPE are not found.
401 * @retval -EINVAL if QTYPE is not "A" (IPv4) or "AAAA" (IPv6) or if QCLASS
402 * is not "IN".
403 */
404 int dns_unpack_response_query(struct dns_msg_t *dns_msg);
405
406 /**
407 * @brief Copies the qname from dns_msg to buf
408 *
409 * @details This routine implements the algorithm described in RFC 1035, 4.1.4.
410 * Message compression to copy the qname (perhaps containing pointers
411 * with offset) to the linear buffer buf. Pointers are removed and
412 * only the "true" labels are copied.
413 *
414 * @param buf Output buffer
415 * @param len Output buffer's length
416 * @param size Output buffer's size
417 * @param dns_msg Structure containing the message
418 * @param pos QNAME's position in dns_msg->msg
419 * @retval 0 on success
420 * @retval -EINVAL if an invalid parameter was passed as an argument
421 * @retval -ENOMEM if the label's size is corrupted
422 */
423 int dns_copy_qname(uint8_t *buf, uint16_t *len, uint16_t size,
424 struct dns_msg_t *dns_msg, uint16_t pos);
425
426 /**
427 * @brief Unpacks the mDNS query. This is special version for multicast DNS
428 * as it skips checks to various fields as described in RFC 6762
429 * chapter 18.
430 *
431 * @param msg Structure containing the response.
432 * @param src_id Transaction id, this is returned to the caller.
433 * @retval 0 on success, <0 if error
434 * @retval -ENOMEM if the buffer in msg has no enough space to store the header.
435 * The header is always 12 bytes length.
436 * @retval -EINVAL if the src_id does not match the header's id, or if the
437 * header's QR value is not DNS_RESPONSE or if the header's OPCODE
438 * value is not DNS_QUERY, or if the header's Z value is not 0 or if
439 * the question counter is not 1 or the answer counter is less than 1.
440 * @retval RFC 1035 RCODEs (> 0) 1 Format error, 2 Server failure, 3 Name Error,
441 * 4 Not Implemented and 5 Refused.
442 */
443 int mdns_unpack_query_header(struct dns_msg_t *msg, uint16_t *src_id);
444
llmnr_unpack_query_header(struct dns_msg_t * msg,uint16_t * src_id)445 static inline int llmnr_unpack_query_header(struct dns_msg_t *msg,
446 uint16_t *src_id)
447 {
448 return mdns_unpack_query_header(msg, src_id);
449 }
450
451 /**
452 * @brief Unpacks the query.
453 *
454 * @param dns_msg Structure containing the message.
455 * @param buf Result buf
456 * @param qtype Query type is returned to caller
457 * @param qclass Query class is returned to caller
458 * @retval 0 on success
459 * @retval -ENOMEM if the null label is not found after traversing the buffer
460 * or if QCLASS and QTYPE are not found.
461 * @retval -EINVAL if QTYPE is not "A" (IPv4) or "AAAA" (IPv6) or if QCLASS
462 * is not "IN".
463 */
464 int dns_unpack_query(struct dns_msg_t *dns_msg, struct net_buf *buf,
465 enum dns_rr_type *qtype,
466 enum dns_class *qclass);
467
468 /**
469 * @brief Map query type number to a string.
470 *
471 * @param qtype Query type
472 *
473 * @return Printable query type name.
474 */
475 const char *dns_qtype_to_str(enum dns_rr_type qtype);
476
477 int dns_unpack_name(const uint8_t *msg, int maxlen, const uint8_t *src,
478 struct net_buf *buf, const uint8_t **eol);
479
480 #endif
481