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 - 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 dns_msg->answer_offset = dns_msg->query_offset + qname_size +
356 DNS_QTYPE_LEN + DNS_QCLASS_LEN;
357
358 return 0;
359 }
360
dns_copy_qname(uint8_t * buf,uint16_t * len,uint16_t size,struct dns_msg_t * dns_msg,uint16_t pos)361 int dns_copy_qname(uint8_t *buf, uint16_t *len, uint16_t size,
362 struct dns_msg_t *dns_msg, uint16_t pos)
363 {
364 uint16_t msg_size = dns_msg->msg_size;
365 uint8_t *msg = dns_msg->msg;
366 uint16_t lb_size;
367 int rc = -EINVAL;
368
369 *len = 0U;
370
371 while (1) {
372 if (pos >= msg_size) {
373 rc = -ENOMEM;
374 break;
375 }
376
377 lb_size = msg[pos];
378
379 /* pointer */
380 if (lb_size > DNS_LABEL_MAX_SIZE) {
381 uint8_t mask = DNS_LABEL_MAX_SIZE;
382
383 if (pos + 1 >= msg_size) {
384 rc = -ENOMEM;
385 break;
386 }
387
388 /* See: RFC 1035, 4.1.4. Message compression */
389 pos = ((msg[pos] & mask) << 8) + msg[pos + 1];
390
391 continue;
392 }
393
394 /* validate that the label (i.e. size + elements),
395 * fits the current msg buffer
396 */
397 if (DNS_LABEL_LEN_SIZE + lb_size > size - *len) {
398 rc = -ENOMEM;
399 break;
400 }
401
402 /* copy the lb_size value and label elements */
403 memcpy(buf + *len, msg + pos, DNS_LABEL_LEN_SIZE + lb_size);
404 /* update destination buffer len */
405 *len += DNS_LABEL_LEN_SIZE + lb_size;
406 /* update msg ptr position */
407 pos += DNS_LABEL_LEN_SIZE + lb_size;
408
409 /* The domain name terminates with the zero length octet
410 * for the null label of the root
411 */
412 if (lb_size == 0U) {
413 rc = 0;
414 break;
415 }
416 }
417
418 return rc;
419 }
420
mdns_unpack_query_header(struct dns_msg_t * msg,uint16_t * src_id)421 int mdns_unpack_query_header(struct dns_msg_t *msg, uint16_t *src_id)
422 {
423 uint8_t *dns_header;
424 uint16_t size;
425 int qdcount;
426
427 dns_header = msg->msg;
428 size = msg->msg_size;
429
430 if (size < DNS_MSG_HEADER_SIZE) {
431 return -ENOMEM;
432 }
433
434 if (dns_header_qr(dns_header) != DNS_QUERY) {
435 return -EINVAL;
436 }
437
438 if (dns_header_opcode(dns_header) != DNS_QUERY) {
439 return -EINVAL;
440 }
441
442 if (dns_header_opcode(dns_header) != 0) {
443 return -EINVAL;
444 }
445
446 if (dns_header_rcode(dns_header) != 0) {
447 return -EINVAL;
448 }
449
450 qdcount = dns_unpack_header_qdcount(dns_header);
451 if (qdcount < 1) {
452 return -EINVAL;
453 }
454
455 if (src_id) {
456 *src_id = dns_unpack_header_id(dns_header);
457 }
458
459 msg->query_offset = DNS_MSG_HEADER_SIZE;
460
461 return qdcount;
462 }
463
464 /* 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)465 static int dns_unpack_name(const uint8_t *msg, int maxlen, const uint8_t *src,
466 struct net_buf *buf, const uint8_t **eol)
467 {
468 int dest_size = net_buf_tailroom(buf);
469 const uint8_t *end_of_label = NULL;
470 const uint8_t *curr_src = src;
471 int loop_check = 0, len = -1;
472 int label_len;
473 int val;
474
475 if (curr_src < msg || curr_src >= (msg + maxlen)) {
476 return -EMSGSIZE;
477 }
478
479 while ((val = *curr_src++)) {
480 if (val & NS_CMPRSFLGS) {
481 /* Follow pointer */
482 int pos;
483
484 if (curr_src >= (msg + maxlen)) {
485 return -EMSGSIZE;
486 }
487
488 if (len < 0) {
489 len = curr_src - src + 1;
490 }
491
492 end_of_label = curr_src + 1;
493
494 /* Strip compress bits from length calculation */
495 pos = ((val & 0x3f) << 8) | (*curr_src & 0xff);
496
497 curr_src = msg + pos;
498 if (curr_src >= (msg + maxlen)) {
499 return -EMSGSIZE;
500 }
501
502 loop_check += 2;
503 if (loop_check >= maxlen) {
504 return -EMSGSIZE;
505 }
506 } else {
507 /* Max label length is 64 bytes (because 2 bits are
508 * used for pointer)
509 */
510 label_len = val;
511 if (label_len > 63) {
512 return -EMSGSIZE;
513 }
514
515 if (((buf->data + label_len + 1) >=
516 (buf->data + dest_size)) ||
517 ((curr_src + label_len) >= (msg + maxlen))) {
518 return -EMSGSIZE;
519 }
520
521 loop_check += label_len + 1;
522
523 net_buf_add_u8(buf, '.');
524 net_buf_add_mem(buf, curr_src, label_len);
525
526 curr_src += label_len;
527 }
528 }
529
530 buf->data[buf->len] = '\0';
531
532 if (eol) {
533 if (!end_of_label) {
534 end_of_label = curr_src;
535 }
536
537 *eol = end_of_label;
538 }
539
540 return buf->len;
541 }
542
dns_unpack_query(struct dns_msg_t * dns_msg,struct net_buf * buf,enum dns_rr_type * qtype,enum dns_class * qclass)543 int dns_unpack_query(struct dns_msg_t *dns_msg, struct net_buf *buf,
544 enum dns_rr_type *qtype, enum dns_class *qclass)
545 {
546 const uint8_t *end_of_label;
547 uint8_t *dns_query;
548 int ret;
549 int query_type, query_class;
550
551 dns_query = dns_msg->msg + dns_msg->query_offset;
552
553 ret = dns_unpack_name(dns_msg->msg, dns_msg->msg_size, dns_query,
554 buf, &end_of_label);
555 if (ret < 0) {
556 return ret;
557 }
558
559 query_type = dns_unpack_query_qtype(end_of_label);
560 if (query_type != DNS_RR_TYPE_A && query_type != DNS_RR_TYPE_AAAA
561 && query_type != DNS_RR_TYPE_PTR
562 && query_type != DNS_RR_TYPE_SRV
563 && query_type != DNS_RR_TYPE_TXT) {
564 return -EINVAL;
565 }
566
567 query_class = dns_unpack_query_qclass(end_of_label);
568 if (query_class != DNS_CLASS_IN) {
569 return -EINVAL;
570 }
571
572 if (qtype) {
573 *qtype = query_type;
574 }
575
576 if (qclass) {
577 *qclass = query_class;
578 }
579
580 dns_msg->query_offset = end_of_label - dns_msg->msg + 2 + 2;
581
582 return ret;
583 }
584