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