1 /*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <string.h>
8 #include <zephyr/sys/bitarray.h>
9 #include <zephyr/net/dns_resolve.h>
10 #include <zephyr/net_buf.h>
11
12 #include "dns_pack.h"
13
14 #include "dns_internal.h"
15
dns_strlen(const char * str)16 static inline uint16_t dns_strlen(const char *str)
17 {
18 if (str == NULL) {
19 return 0;
20 }
21 return (uint16_t)strlen(str);
22 }
23
dns_msg_pack_qname(uint16_t * len,uint8_t * buf,uint16_t size,const char * domain_name)24 int dns_msg_pack_qname(uint16_t *len, uint8_t *buf, uint16_t size,
25 const char *domain_name)
26 {
27 uint16_t dn_size;
28 uint16_t lb_start;
29 uint16_t lb_index;
30 uint16_t lb_size;
31 uint16_t i;
32
33 lb_start = 0U;
34 lb_index = 1U;
35 lb_size = 0U;
36
37 dn_size = dns_strlen(domain_name);
38 if (dn_size == 0U) {
39 return -EINVAL;
40 }
41
42 /* traverse the domain name str, including the null-terminator :) */
43 for (i = 0U; i < dn_size + 1; i++) {
44 if (lb_index >= size) {
45 return -ENOMEM;
46 }
47
48 switch (domain_name[i]) {
49 default:
50 buf[lb_index] = domain_name[i];
51 lb_size += 1U;
52 break;
53 case '.':
54 buf[lb_start] = lb_size;
55 lb_size = 0U;
56 lb_start = lb_index;
57 break;
58 case '\0':
59 buf[lb_start] = lb_size;
60 buf[lb_index] = 0U;
61 break;
62 }
63 lb_index += 1U;
64 }
65
66 *len = lb_index;
67
68 return 0;
69 }
70
set_dns_msg_response(struct dns_msg_t * dns_msg,int type,uint16_t pos,uint16_t len)71 static inline void set_dns_msg_response(struct dns_msg_t *dns_msg, int type,
72 uint16_t pos, uint16_t len)
73 {
74 dns_msg->response_type = type;
75 dns_msg->response_position = pos;
76 dns_msg->response_length = len;
77 }
78
79 /*
80 * Skip encoded FQDN in DNS message.
81 * Returns size in bytes of encoded FQDN, or negative error code.
82 */
skip_fqdn(uint8_t * answer,int buf_sz)83 static int skip_fqdn(uint8_t *answer, int buf_sz)
84 {
85 int i = 0;
86
87 while (1) {
88 if (i >= buf_sz) {
89 return -EINVAL;
90 }
91
92 if (answer[i] == 0) {
93 i += 1;
94 break;
95 } else if (answer[i] >= 0xc0) {
96 i += 2;
97 if (i > buf_sz) {
98 return -EINVAL;
99 }
100 break;
101 } else if (answer[i] < DNS_LABEL_MAX_SIZE) {
102 i += answer[i] + 1;
103 } else {
104 return -EINVAL;
105 }
106 }
107
108 return i;
109 }
110
dns_unpack_answer(struct dns_msg_t * dns_msg,int dname_ptr,uint32_t * ttl,enum dns_rr_type * type)111 int dns_unpack_answer(struct dns_msg_t *dns_msg, int dname_ptr, uint32_t *ttl,
112 enum dns_rr_type *type)
113 {
114 int dname_len;
115 uint16_t rem_size;
116 uint16_t pos;
117 uint16_t len;
118 uint8_t *answer;
119
120 answer = dns_msg->msg + dns_msg->answer_offset;
121
122 dname_len = skip_fqdn(answer,
123 dns_msg->msg_size - dns_msg->answer_offset);
124 if (dname_len < 0) {
125 return dname_len;
126 }
127
128 /*
129 * We need to be sure this buffer has enough space
130 * to contain the answer.
131 *
132 * size: dname_size + type + class + ttl + rdlength + rdata
133 * 2 + 2 + 2 + 4 + 2 + ?
134 *
135 * So, answer size >= 12
136 *
137 * See RFC-1035 4.1.3. Resource record format
138 */
139 rem_size = dns_msg->msg_size - dns_msg->answer_offset - dname_len;
140 if (rem_size < 2 + 2 + 4 + 2) {
141 return -EINVAL;
142 }
143
144 /* Only DNS_CLASS_IN answers. If mDNS is enabled, strip away the
145 * Cache-Flush bit (highest one).
146 */
147 if ((dns_answer_class(dname_len, answer) &
148 (IS_ENABLED(CONFIG_MDNS_RESOLVER) ? 0x7fff : 0xffff))
149 != DNS_CLASS_IN) {
150 return -EINVAL;
151 }
152
153 /* TTL value */
154 *ttl = dns_answer_ttl(dname_len, answer);
155 len = dns_answer_rdlength(dname_len, answer);
156 pos = dns_msg->answer_offset + dname_len +
157 DNS_COMMON_UINT_SIZE + /* class length */
158 DNS_COMMON_UINT_SIZE + /* type length */
159 DNS_TTL_LEN +
160 DNS_RDLENGTH_LEN;
161 *type = dns_answer_type(dname_len, answer);
162
163 switch (*type) {
164 case DNS_RR_TYPE_A:
165 case DNS_RR_TYPE_AAAA:
166 set_dns_msg_response(dns_msg, DNS_RESPONSE_IP, pos, len);
167 return 0;
168
169 case DNS_RR_TYPE_PTR:
170 set_dns_msg_response(dns_msg, DNS_RESPONSE_DATA, pos, len);
171 return 0;
172
173 case DNS_RR_TYPE_CNAME:
174 set_dns_msg_response(dns_msg, DNS_RESPONSE_CNAME_NO_IP,
175 pos, len);
176 return 0;
177
178 default:
179 /* malformed dns answer */
180 return -EINVAL;
181 }
182
183 return 0;
184 }
185
dns_unpack_response_header(struct dns_msg_t * msg,int src_id)186 int dns_unpack_response_header(struct dns_msg_t *msg, int src_id)
187 {
188 uint8_t *dns_header;
189 uint16_t size;
190 int qdcount;
191 int ancount;
192 int rc;
193
194 dns_header = msg->msg;
195 size = msg->msg_size;
196
197 if (size < DNS_MSG_HEADER_SIZE) {
198 return -ENOMEM;
199 }
200
201 if (dns_unpack_header_id(dns_header) != src_id) {
202 return -EINVAL;
203 }
204
205 if (dns_header_qr(dns_header) != DNS_RESPONSE) {
206 return -EINVAL;
207 }
208
209 if (dns_header_opcode(dns_header) != DNS_QUERY) {
210 return -EINVAL;
211 }
212
213 if (dns_header_z(dns_header) != 0) {
214 return -EINVAL;
215 }
216
217 rc = dns_header_rcode(dns_header);
218 switch (rc) {
219 case DNS_HEADER_NOERROR:
220 break;
221 default:
222 return rc;
223
224 }
225
226 qdcount = dns_unpack_header_qdcount(dns_header);
227 ancount = dns_unpack_header_ancount(dns_header);
228
229 /* For mDNS (when src_id == 0) the query count is 0 so accept
230 * the packet in that case.
231 */
232 if ((qdcount < 1 && src_id > 0) || ancount < 1) {
233 return -EINVAL;
234 }
235
236 return 0;
237 }
238
dns_msg_pack_query_header(uint8_t * buf,uint16_t size,uint16_t id)239 static int dns_msg_pack_query_header(uint8_t *buf, uint16_t size, uint16_t id)
240 {
241 uint16_t offset;
242
243 if (size < DNS_MSG_HEADER_SIZE) {
244 return -ENOMEM;
245 }
246
247 UNALIGNED_PUT(htons(id), (uint16_t *)(buf));
248
249 /* RD = 1, TC = 0, AA = 0, Opcode = 0, QR = 0 <-> 0x01 (1B)
250 * RCode = 0, Z = 0, RA = 0 <-> 0x00 (1B)
251 *
252 * QDCOUNT = 1 <-> 0x0001 (2B)
253 */
254
255 offset = DNS_HEADER_ID_LEN;
256 /* Split the following assignments just in case we need to alter
257 * the flags in future releases
258 */
259 *(buf + offset) = DNS_FLAGS1; /* QR, Opcode, AA, TC and RD */
260 *(buf + offset + 1) = DNS_FLAGS2; /* RA, Z and RCODE */
261
262 offset += DNS_HEADER_FLAGS_LEN;
263 /* set question counter */
264 UNALIGNED_PUT(htons(1), (uint16_t *)(buf + offset));
265
266 offset += DNS_QDCOUNT_LEN;
267 /* set answer and ns rr */
268 UNALIGNED_PUT(0, (uint32_t *)(buf + offset));
269
270 offset += DNS_ANCOUNT_LEN + DNS_NSCOUNT_LEN;
271 /* set the additional records */
272 UNALIGNED_PUT(0, (uint16_t *)(buf + offset));
273
274 return 0;
275 }
276
dns_msg_pack_query(uint8_t * buf,uint16_t * len,uint16_t size,uint8_t * qname,uint16_t qname_len,uint16_t id,enum dns_rr_type qtype)277 int dns_msg_pack_query(uint8_t *buf, uint16_t *len, uint16_t size,
278 uint8_t *qname, uint16_t qname_len, uint16_t id,
279 enum dns_rr_type qtype)
280 {
281 uint16_t msg_size;
282 uint16_t offset;
283 int rc;
284
285 msg_size = DNS_MSG_HEADER_SIZE + DNS_QTYPE_LEN + DNS_QCLASS_LEN;
286 if (msg_size + qname_len > size) {
287 return -ENOMEM;
288 }
289
290 rc = dns_msg_pack_query_header(buf, size, id);
291 if (rc != 0) {
292 return rc;
293 }
294
295 offset = DNS_MSG_HEADER_SIZE;
296 memcpy(buf + offset, qname, qname_len);
297
298 offset += qname_len;
299
300 /* QType */
301 UNALIGNED_PUT(htons(qtype), (uint16_t *)(buf + offset + 0));
302 offset += DNS_QTYPE_LEN;
303
304 /* QClass */
305 UNALIGNED_PUT(htons(DNS_CLASS_IN), (uint16_t *)(buf + offset));
306
307 *len = offset + DNS_QCLASS_LEN;
308
309 return 0;
310 }
311
dns_find_null(int * qname_size,uint8_t * buf,uint16_t size)312 static int dns_find_null(int *qname_size, uint8_t *buf, uint16_t size)
313 {
314 *qname_size = 0;
315 while (*qname_size < size) {
316 if (buf[(*qname_size)++] == 0x00) {
317 return 0;
318 }
319 }
320
321 return -ENOMEM;
322 }
323
dns_unpack_response_query(struct dns_msg_t * dns_msg)324 int dns_unpack_response_query(struct dns_msg_t *dns_msg)
325 {
326 uint8_t *dns_query;
327 uint8_t *buf;
328 int remaining_size;
329 int qname_size;
330 int offset;
331 int rc;
332
333 dns_msg->query_offset = DNS_MSG_HEADER_SIZE;
334 dns_query = dns_msg->msg + dns_msg->query_offset;
335 remaining_size = dns_msg->msg_size - dns_msg->query_offset;
336
337 rc = dns_find_null(&qname_size, dns_query, remaining_size);
338 if (rc != 0) {
339 return rc;
340 }
341
342 /* header already parsed + qname size */
343 offset = dns_msg->query_offset + qname_size;
344
345 /* 4 bytes more due to qtype and qclass */
346 offset += DNS_QTYPE_LEN + DNS_QCLASS_LEN;
347 if (offset >= dns_msg->msg_size) {
348 return -ENOMEM;
349 }
350
351 buf = dns_query + qname_size;
352 if (dns_unpack_query_qtype(buf) != DNS_RR_TYPE_A &&
353 dns_unpack_query_qtype(buf) != DNS_RR_TYPE_AAAA &&
354 dns_unpack_query_qtype(buf) != DNS_RR_TYPE_PTR) {
355 return -EINVAL;
356 }
357
358 if (dns_unpack_query_qclass(buf) != DNS_CLASS_IN) {
359 return -EINVAL;
360 }
361
362 offset = dns_msg->query_offset + qname_size +
363 DNS_QTYPE_LEN + DNS_QCLASS_LEN;
364
365 if (offset >= dns_msg->msg_size) {
366 return -ENOMEM;
367 }
368
369 dns_msg->answer_offset = offset;
370
371 return 0;
372 }
373
dns_copy_qname(uint8_t * buf,uint16_t * len,uint16_t size,struct dns_msg_t * dns_msg,uint16_t pos)374 int dns_copy_qname(uint8_t *buf, uint16_t *len, uint16_t size,
375 struct dns_msg_t *dns_msg, uint16_t pos)
376 {
377 SYS_BITARRAY_DEFINE(visited, DNS_RESOLVER_MAX_BUF_SIZE);
378 uint16_t msg_size = dns_msg->msg_size;
379 uint8_t *msg = dns_msg->msg;
380 uint16_t lb_size;
381 int rc = -EINVAL, ret, prev;
382
383 *len = 0U;
384
385 while (1) {
386 if (pos >= msg_size) {
387 rc = -ENOMEM;
388 break;
389 }
390
391 lb_size = msg[pos];
392
393 /* pointer */
394 if ((lb_size & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
395 uint8_t mask = DNS_LABEL_MAX_SIZE;
396
397 if (pos + 1 >= msg_size) {
398 rc = -ENOMEM;
399 break;
400 }
401
402 /* See: RFC 1035, 4.1.4. Message compression */
403 pos = ((msg[pos] & mask) << 8) + msg[pos + 1];
404
405 ret = sys_bitarray_test_and_set_bit(&visited, pos, &prev);
406 if (ret < 0) {
407 rc = -EINVAL;
408 break;
409 }
410
411 if (prev) {
412 rc = -ELOOP;
413 break;
414 }
415
416 continue;
417 } else if (lb_size & NS_CMPRSFLGS) {
418 rc = -EINVAL;
419 break;
420 }
421
422 /* validate that the label (i.e. size + elements),
423 * fits the current msg buffer
424 */
425 if (DNS_LABEL_LEN_SIZE + lb_size > MIN(size - *len, msg_size - pos)) {
426 rc = -ENOMEM;
427 break;
428 }
429
430 /* copy the lb_size value and label elements */
431 memcpy(buf + *len, msg + pos, DNS_LABEL_LEN_SIZE + lb_size);
432 /* update destination buffer len */
433 *len += DNS_LABEL_LEN_SIZE + lb_size;
434 /* update msg ptr position */
435 pos += DNS_LABEL_LEN_SIZE + lb_size;
436
437 /* The domain name terminates with the zero length octet
438 * for the null label of the root
439 */
440 if (lb_size == 0U) {
441 rc = 0;
442 break;
443 }
444 }
445
446 return rc;
447 }
448
mdns_unpack_query_header(struct dns_msg_t * msg,uint16_t * src_id)449 int mdns_unpack_query_header(struct dns_msg_t *msg, uint16_t *src_id)
450 {
451 uint8_t *dns_header;
452 uint16_t size;
453 int qdcount;
454
455 dns_header = msg->msg;
456 size = msg->msg_size;
457
458 if (size < DNS_MSG_HEADER_SIZE) {
459 return -ENOMEM;
460 }
461
462 if (dns_header_qr(dns_header) != DNS_QUERY) {
463 return -EINVAL;
464 }
465
466 if (dns_header_opcode(dns_header) != DNS_QUERY) {
467 return -EINVAL;
468 }
469
470 if (dns_header_rcode(dns_header) != 0) {
471 return -EINVAL;
472 }
473
474 qdcount = dns_unpack_header_qdcount(dns_header);
475 if (qdcount < 1) {
476 /* Discard the message if query count is 0. RFC 6804 ch. 2 */
477 return -ENOENT;
478 }
479
480 if (src_id) {
481 *src_id = dns_unpack_header_id(dns_header);
482 }
483
484 msg->query_offset = DNS_MSG_HEADER_SIZE;
485
486 return qdcount;
487 }
488
489 /* Returns the length of the unpacked name */
dns_unpack_name(const uint8_t * msg,int maxlen,const uint8_t * src,struct net_buf * buf,const uint8_t ** eol)490 int dns_unpack_name(const uint8_t *msg, int maxlen, const uint8_t *src,
491 struct net_buf *buf, const uint8_t **eol)
492 {
493 int dest_size = net_buf_tailroom(buf);
494 const uint8_t *end_of_label = NULL;
495 const uint8_t *curr_src = src;
496 int loop_check = 0, len = -1;
497 int label_len;
498 int val;
499
500 if (curr_src < msg || curr_src >= (msg + maxlen)) {
501 return -EMSGSIZE;
502 }
503
504 while ((val = *curr_src++)) {
505 if ((val & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
506 /* Follow pointer */
507 int pos;
508
509 if (curr_src >= (msg + maxlen)) {
510 return -EMSGSIZE;
511 }
512
513 if (len < 0) {
514 len = curr_src - src + 1;
515 }
516
517 end_of_label = curr_src + 1;
518
519 /* Strip compress bits from length calculation */
520 pos = ((val & 0x3f) << 8) | (*curr_src & 0xff);
521
522 curr_src = msg + pos;
523 if (curr_src >= (msg + maxlen)) {
524 return -EMSGSIZE;
525 }
526
527 loop_check += 2;
528 if (loop_check >= maxlen) {
529 return -EMSGSIZE;
530 }
531 } else {
532 /* Max label length is 64 bytes (because 2 bits are
533 * used for pointer)
534 */
535 label_len = val;
536 if (label_len > 63) {
537 return -EMSGSIZE;
538 }
539
540 if (((buf->data + label_len + 1) >=
541 (buf->data + dest_size)) ||
542 ((curr_src + label_len) >= (msg + maxlen))) {
543 return -EMSGSIZE;
544 }
545
546 loop_check += label_len + 1;
547
548 net_buf_add_u8(buf, '.');
549 net_buf_add_mem(buf, curr_src, label_len);
550
551 curr_src += label_len;
552 }
553 }
554
555 buf->data[buf->len] = '\0';
556
557 if (eol) {
558 if (!end_of_label) {
559 end_of_label = curr_src;
560 }
561
562 *eol = end_of_label;
563 }
564
565 return buf->len;
566 }
567
dns_qtype_to_str(enum dns_rr_type qtype)568 const char *dns_qtype_to_str(enum dns_rr_type qtype)
569 {
570 switch (qtype) {
571 case DNS_RR_TYPE_A:
572 return "A";
573 case DNS_RR_TYPE_CNAME:
574 return "CNAME";
575 case DNS_RR_TYPE_PTR:
576 return "PTR";
577 case DNS_RR_TYPE_TXT:
578 return "TXT";
579 case DNS_RR_TYPE_AAAA:
580 return "AAAA";
581 case DNS_RR_TYPE_SRV:
582 return "SRV";
583 case DNS_RR_TYPE_ANY:
584 return "ANY";
585 default:
586 break;
587 }
588
589 return "<unknown>";
590 }
591
dns_unpack_query(struct dns_msg_t * dns_msg,struct net_buf * buf,enum dns_rr_type * qtype,enum dns_class * qclass)592 int dns_unpack_query(struct dns_msg_t *dns_msg, struct net_buf *buf,
593 enum dns_rr_type *qtype, enum dns_class *qclass)
594 {
595 const uint8_t *end_of_label;
596 uint8_t *dns_query;
597 int ret;
598 int query_type, query_class;
599
600 dns_query = dns_msg->msg + dns_msg->query_offset;
601
602 ret = dns_unpack_name(dns_msg->msg, dns_msg->msg_size, dns_query,
603 buf, &end_of_label);
604 if (ret < 0) {
605 return ret;
606 }
607
608 query_type = dns_unpack_query_qtype(end_of_label);
609 if (query_type != DNS_RR_TYPE_A && query_type != DNS_RR_TYPE_AAAA
610 && query_type != DNS_RR_TYPE_PTR
611 && query_type != DNS_RR_TYPE_SRV
612 && query_type != DNS_RR_TYPE_TXT
613 && query_type != DNS_RR_TYPE_HTTPS
614 && query_type != DNS_RR_TYPE_ANY) {
615 return -EINVAL;
616 }
617
618 query_class = dns_unpack_query_qclass(end_of_label);
619 if ((query_class & DNS_CLASS_IN) != DNS_CLASS_IN) {
620 return -EINVAL;
621 }
622
623 if (qtype) {
624 *qtype = query_type;
625 }
626
627 if (qclass) {
628 *qclass = query_class;
629 }
630
631 dns_msg->query_offset = end_of_label - dns_msg->msg + 2 + 2;
632
633 return ret;
634 }
635