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