1 /*
2  * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <ctype.h>
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <strings.h>
14 
15 #include <zephyr/net/net_context.h>
16 #include <zephyr/net/net_core.h>
17 #include <zephyr/net/dns_sd.h>
18 #include <zephyr/sys/util.h>
19 #include <zephyr/kernel.h>
20 
21 #include "dns_pack.h"
22 #include "dns_sd.h"
23 
24 #include <zephyr/logging/log.h>
25 LOG_MODULE_REGISTER(net_dns_sd, CONFIG_DNS_SD_LOG_LEVEL);
26 
27 const char dns_sd_empty_txt[1];
28 const uint16_t dns_sd_port_zero;
29 
30 #ifndef CONFIG_NET_TEST
31 
32 static size_t service_proto_size(const struct dns_sd_rec *inst);
33 static bool label_is_valid(const char *label, size_t label_size);
34 static int add_a_record(const struct dns_sd_rec *inst, uint32_t ttl,
35 			uint16_t host_offset, uint32_t addr,
36 			uint8_t *buf,
37 			uint16_t buf_offset, uint16_t buf_size);
38 static int add_ptr_record(const struct dns_sd_rec *inst, uint32_t ttl,
39 			  uint8_t *buf, uint16_t buf_offset,
40 			  uint16_t buf_size,
41 			  uint16_t *service_offset,
42 			  uint16_t *instance_offset,
43 			  uint16_t *domain_offset);
44 static int add_txt_record(const struct dns_sd_rec *inst, uint32_t ttl,
45 			  uint16_t instance_offset, uint8_t *buf,
46 			  uint16_t buf_offset, uint16_t buf_size);
47 static int add_aaaa_record(const struct dns_sd_rec *inst, uint32_t ttl,
48 			   uint16_t host_offset, const uint8_t addr[16],
49 			   uint8_t *buf, uint16_t buf_offset,
50 			   uint16_t buf_size);
51 static int add_srv_record(const struct dns_sd_rec *inst, uint32_t ttl,
52 			  uint16_t instance_offset,
53 			  uint16_t domain_offset,
54 			  uint8_t *buf, uint16_t buf_offset,
55 			  uint16_t buf_size,
56 			  uint16_t *host_offset);
57 static bool rec_is_valid(const struct dns_sd_rec *inst);
58 
59 #endif /* CONFIG_NET_TEST */
60 
61 /**
62  * Calculate the size of a DNS-SD service
63  *
64  * This macro calculates the size of the DNS-SD service for a DNS
65  * Resource Record (RR).
66  *
67  * For example, if there is a service called 'My Foo'._http._tcp.local.,
68  * then the returned size is 18. That is broken down as shown below.
69  *
70  * - 1 byte for the size of "_http"
71  * - 5 bytes for the value of "_http"
72  * - 1 byte for the size of "_tcp"
73  * - 4 bytes for the value of "_tcp"
74  * - 1 byte for the size of "local"
75  * - 5 bytes for the value of "local"
76  * - 1 byte for the trailing NUL terminator '\0'
77  *
78  * @param ref the DNS-SD record
79  * @return the size of the DNS-SD service for a DNS Resource Record
80  */
service_proto_size(const struct dns_sd_rec * ref)81 size_t service_proto_size(const struct dns_sd_rec *ref)
82 {
83 	return 0
84 	       + DNS_LABEL_LEN_SIZE + strlen(ref->service)
85 	       + DNS_LABEL_LEN_SIZE + strlen(ref->proto)
86 	       + DNS_LABEL_LEN_SIZE + strlen(ref->domain)
87 	       + DNS_LABEL_LEN_SIZE
88 	;
89 }
90 
91 /**
92  * Check Label Validity according to RFC 1035, Section 3.5
93  *
94  * <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
95  * <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
96  * <let-dig-hyp> ::= <let-dig> | -
97  * <let-dig> ::= <letter> | <digit>
98  * <letter> ::= [a-zA-Z]
99  * <digit> ::= [0-9]
100  */
label_is_valid(const char * label,size_t label_size)101 bool label_is_valid(const char *label, size_t label_size)
102 {
103 	size_t i;
104 
105 	if (label == NULL) {
106 		NET_DBG("label is NULL");
107 		return false;
108 	}
109 
110 	if (label_size == 0) {
111 		/* automatically calculate the length of the string */
112 		label_size = strlen(label);
113 	}
114 
115 	if (label_size < DNS_LABEL_MIN_SIZE ||
116 	    label_size > DNS_LABEL_MAX_SIZE) {
117 		NET_DBG("label invalid size (%zu, min: %u, max: %u)",
118 			 label_size,
119 			 DNS_LABEL_MIN_SIZE,
120 			 DNS_LABEL_MAX_SIZE);
121 		return false;
122 	}
123 
124 	for (i = 0; i < label_size; ++i) {
125 		if (isalpha((int)label[i]) != 0) {
126 			continue;
127 		}
128 
129 		if (i > 0) {
130 			if (isdigit((int)label[i]) != 0) {
131 				continue;
132 			}
133 
134 			if ('-' == label[i]) {
135 				continue;
136 			}
137 		}
138 
139 		NET_DBG("label '%s' contains illegal byte 0x%02x", label, label[i]);
140 		return false;
141 	}
142 
143 	return true;
144 }
145 
instance_is_valid(const char * instance)146 static bool instance_is_valid(const char *instance)
147 {
148 	size_t i;
149 	size_t instance_size;
150 
151 	if (instance == NULL) {
152 		NET_DBG("instance is NULL");
153 		return false;
154 	}
155 
156 	instance_size = strlen(instance);
157 	if (instance_size < DNS_SD_INSTANCE_MIN_SIZE) {
158 		NET_DBG("instance '%s' is too small (%zu, min: %u)",
159 			instance, instance_size,
160 			DNS_SD_INSTANCE_MIN_SIZE);
161 		return false;
162 	}
163 
164 	if (instance_size > DNS_SD_INSTANCE_MAX_SIZE) {
165 		NET_DBG("instance '%s' is too big (%zu, max: %u)",
166 			instance, instance_size,
167 			DNS_SD_INSTANCE_MAX_SIZE);
168 		return false;
169 	}
170 
171 	for (i = 0; i < instance_size; ++i) {
172 		/* RFC 6763 Section 4.1.1 */
173 		if (instance[i] <= 0x1f ||
174 		    instance[i] == 0x7f) {
175 			NET_DBG(
176 				"instance '%s' contains illegal byte 0x%02x",
177 				instance, instance[i]);
178 			return false;
179 		}
180 	}
181 
182 	return instance_size;
183 }
184 
service_is_valid(const char * service)185 static bool service_is_valid(const char *service)
186 {
187 	size_t service_size;
188 
189 	if (service == NULL) {
190 		NET_DBG("service is NULL");
191 		return false;
192 	}
193 
194 	service_size = strlen(service);
195 	if (service_size < DNS_SD_SERVICE_MIN_SIZE) {
196 		NET_DBG("service '%s' is too small (%zu, min: %u)",
197 			service, service_size, DNS_SD_SERVICE_MIN_SIZE);
198 		return false;
199 	}
200 
201 	if (service_size > DNS_SD_SERVICE_MAX_SIZE) {
202 		NET_DBG("service '%s' is too big (%zu, max: %u)",
203 			service, service_size, DNS_SD_SERVICE_MAX_SIZE);
204 		return false;
205 	}
206 
207 	if (service[0] != DNS_SD_SERVICE_PREFIX) {
208 		NET_DBG("service '%s' invalid (no leading underscore)",
209 			service);
210 		return false;
211 	}
212 
213 	if (!label_is_valid(&service[1], service_size - 1)) {
214 		NET_DBG("service '%s' contains invalid characters",
215 			service);
216 		return false;
217 	}
218 
219 	return service_size;
220 }
221 
proto_is_valid(const char * proto)222 static bool proto_is_valid(const char *proto)
223 {
224 	size_t proto_size;
225 
226 	if (proto == NULL) {
227 		NET_DBG("proto is NULL");
228 		return false;
229 	}
230 
231 	proto_size = strlen(proto);
232 	if (proto_size != DNS_SD_PROTO_SIZE) {
233 		NET_DBG("proto '%s' wrong size (%zu, exp: %u)",
234 			proto, proto_size, DNS_SD_PROTO_SIZE);
235 		return false;
236 	}
237 
238 	if (!(strncasecmp("_tcp", proto, DNS_SD_PROTO_SIZE) == 0 ||
239 	      strncasecmp("_udp", proto, DNS_SD_PROTO_SIZE) == 0)) {
240 		/* RFC 1034 Section 3.1 */
241 		NET_DBG("proto '%s' is invalid (not _tcp or _udp)",
242 			proto);
243 		return false;
244 	}
245 
246 	return proto_size;
247 }
248 
domain_is_valid(const char * domain)249 static bool domain_is_valid(const char *domain)
250 {
251 	size_t domain_size;
252 
253 	if (domain == NULL) {
254 		NET_DBG("domain is NULL");
255 		return false;
256 	}
257 
258 	domain_size = strlen(domain);
259 	if (domain_size < DNS_SD_DOMAIN_MIN_SIZE) {
260 		NET_DBG("domain '%s' is too small (%zu, min: %u)",
261 			domain, domain_size, DNS_SD_DOMAIN_MIN_SIZE);
262 		return false;
263 	}
264 
265 	if (domain_size > DNS_SD_DOMAIN_MAX_SIZE) {
266 		NET_DBG("domain '%s' is too big (%zu, max: %u)",
267 			domain, domain_size, DNS_SD_DOMAIN_MAX_SIZE);
268 		return false;
269 	}
270 
271 	if (!label_is_valid(domain, domain_size)) {
272 		NET_DBG("domain '%s' contains invalid characters",
273 			domain);
274 		return false;
275 	}
276 
277 	return domain_size;
278 }
279 
280 /**
281  * Check DNS SD Record for validity
282  *
283  * Our records are in the form <Instance>.<Service>.<Proto>.<Domain>
284  *
285  * Currently, <Subdomain>.<Domain> services are not supported.
286  */
rec_is_valid(const struct dns_sd_rec * inst)287 bool rec_is_valid(const struct dns_sd_rec *inst)
288 {
289 	return true
290 	       && inst != NULL
291 	       && instance_is_valid(inst->instance)
292 	       && service_is_valid(inst->service)
293 	       && proto_is_valid(inst->proto)
294 	       && domain_is_valid(inst->domain)
295 	       && inst->text != NULL
296 	       && inst->port != NULL
297 	;
298 }
299 
add_a_record(const struct dns_sd_rec * inst,uint32_t ttl,uint16_t host_offset,uint32_t addr,uint8_t * buf,uint16_t buf_offset,uint16_t buf_size)300 int add_a_record(const struct dns_sd_rec *inst, uint32_t ttl,
301 		 uint16_t host_offset, uint32_t addr, uint8_t *buf,
302 		 uint16_t buf_offset, uint16_t buf_size)
303 {
304 	uint16_t total_size;
305 	struct dns_rr *rr;
306 	struct dns_a_rdata *rdata;
307 	uint16_t inst_offs;
308 	uint16_t offset = buf_offset;
309 
310 	if ((DNS_SD_PTR_MASK & host_offset) != 0) {
311 		NET_DBG("offset %u too big for message compression",
312 			host_offset);
313 		return -E2BIG;
314 	}
315 
316 	/* First, calculate that there is enough space in the buffer */
317 	total_size =
318 		/* pointer to .<Instance>.local. */
319 		2 + sizeof(*rr) + sizeof(*rdata);
320 
321 	if (offset > buf_size || total_size >= buf_size - offset) {
322 		NET_DBG("Buffer too small. required: %u available: %d",
323 			total_size, (int)buf_size - (int)offset);
324 		return -ENOSPC;
325 	}
326 
327 	/* insert a pointer to the instance + service name */
328 	inst_offs = host_offset;
329 	inst_offs |= DNS_SD_PTR_MASK;
330 	inst_offs = htons(inst_offs);
331 	memcpy(&buf[offset], &inst_offs, sizeof(inst_offs));
332 	offset += sizeof(inst_offs);
333 
334 	rr = (struct dns_rr *)&buf[offset];
335 	rr->type = htons(DNS_RR_TYPE_A);
336 	rr->class_ = htons(DNS_CLASS_IN | DNS_CLASS_FLUSH);
337 	rr->ttl = htonl(ttl);
338 	rr->rdlength = htons(sizeof(*rdata));
339 	offset += sizeof(*rr);
340 
341 	rdata = (struct dns_a_rdata *)&buf[offset];
342 	rdata->address = htonl(addr);
343 	offset += sizeof(*rdata);
344 
345 	__ASSERT_NO_MSG(total_size == offset - buf_offset);
346 
347 	return offset - buf_offset;
348 }
349 
add_ptr_record(const struct dns_sd_rec * inst,uint32_t ttl,uint8_t * buf,uint16_t buf_offset,uint16_t buf_size,uint16_t * service_offset,uint16_t * instance_offset,uint16_t * domain_offset)350 int add_ptr_record(const struct dns_sd_rec *inst, uint32_t ttl,
351 		   uint8_t *buf, uint16_t buf_offset, uint16_t buf_size,
352 		   uint16_t *service_offset, uint16_t *instance_offset,
353 		   uint16_t *domain_offset)
354 {
355 	uint8_t i;
356 	int name_size;
357 	struct dns_rr *rr;
358 	uint16_t svc_offs;
359 	uint16_t inst_offs;
360 	uint16_t dom_offs;
361 	size_t label_size;
362 	uint16_t sp_size;
363 	uint16_t offset = buf_offset;
364 	const char *labels[] = {
365 		inst->instance,
366 		inst->service,
367 		inst->proto,
368 		inst->domain,
369 	};
370 
371 	/* First, ensure that labels and full name are within spec */
372 	if (!rec_is_valid(inst)) {
373 		return -EINVAL;
374 	}
375 
376 	sp_size = service_proto_size(inst);
377 
378 	/*
379 	 * Next, calculate that there is enough space in the buffer.
380 	 *
381 	 * We require that this is the first time names will appear in the
382 	 * DNS message. Message Compression is used in subsequent
383 	 * calculations.
384 	 *
385 	 * That is the reason there is an output variable for
386 	 * service_offset and instance_offset.
387 	 *
388 	 * For more information on DNS Message Compression, see
389 	 * RFC 1035, Section 4.1.4.
390 	 */
391 	name_size =
392 		/* uncompressed. e.g. "._foo._tcp.local." */
393 		sp_size +
394 		sizeof(*rr)
395 		/* compressed e.g. .My Foo" followed by (DNS_SD_PTR_MASK | 0x0abc) */
396 		+ 1 + strlen(inst->instance) + 2;
397 
398 	if (offset > buf_size || name_size >= buf_size - offset) {
399 		NET_DBG("Buffer too small. required: %u available: %d",
400 			name_size, (int)buf_size - (int)offset);
401 		return -ENOSPC;
402 	}
403 
404 	svc_offs = offset;
405 	if ((svc_offs & DNS_SD_PTR_MASK) != 0) {
406 		NET_DBG("offset %u too big for message compression",
407 			svc_offs);
408 		return -E2BIG;
409 	}
410 
411 	inst_offs = offset + sp_size + sizeof(*rr);
412 	if ((inst_offs & DNS_SD_PTR_MASK) != 0) {
413 		NET_DBG("offset %u too big for message compression",
414 			inst_offs);
415 		return -E2BIG;
416 	}
417 
418 	dom_offs = offset + sp_size - 1 -
419 		   strlen(inst->domain) - 1;
420 
421 	/* Finally, write output with confidence that doing so is safe */
422 
423 	*service_offset = svc_offs;
424 	*instance_offset = inst_offs;
425 	*domain_offset = dom_offs;
426 
427 	/* copy the service name. e.g. "._foo._tcp.local." */
428 	for (i = 1; i < ARRAY_SIZE(labels); ++i) {
429 		label_size = strlen(labels[i]);
430 		buf[offset++] = strlen(labels[i]);
431 		memcpy(&buf[offset], labels[i], label_size);
432 		offset += label_size;
433 		if (i == ARRAY_SIZE(labels) - 1) {
434 			/* terminator */
435 			buf[offset++] = '\0';
436 		}
437 	}
438 
439 	__ASSERT_NO_MSG(svc_offs + sp_size == offset);
440 
441 	rr = (struct dns_rr *)&buf[offset];
442 	rr->type = htons(DNS_RR_TYPE_PTR);
443 	rr->class_ = htons(DNS_CLASS_IN);
444 	rr->ttl = htonl(ttl);
445 	rr->rdlength = htons(
446 		DNS_LABEL_LEN_SIZE +
447 		strlen(inst->instance)
448 		+ DNS_POINTER_SIZE);
449 	offset += sizeof(*rr);
450 
451 	__ASSERT_NO_MSG(inst_offs == offset);
452 
453 	/* copy the instance size, value, and add a pointer */
454 	label_size = strlen(inst->instance);
455 	buf[offset++] = label_size;
456 	memcpy(&buf[offset], inst->instance, label_size);
457 	offset += label_size;
458 
459 	svc_offs |= DNS_SD_PTR_MASK;
460 	svc_offs = htons(svc_offs);
461 	memcpy(&buf[offset], &svc_offs, sizeof(svc_offs));
462 	offset += sizeof(svc_offs);
463 
464 	__ASSERT_NO_MSG(name_size == offset - buf_offset);
465 
466 	return offset - buf_offset;
467 }
468 
add_txt_record(const struct dns_sd_rec * inst,uint32_t ttl,uint16_t instance_offset,uint8_t * buf,uint16_t buf_offset,uint16_t buf_size)469 int add_txt_record(const struct dns_sd_rec *inst, uint32_t ttl,
470 		   uint16_t instance_offset, uint8_t *buf,
471 		   uint16_t buf_offset, uint16_t buf_size)
472 {
473 	uint16_t total_size;
474 	struct dns_rr *rr;
475 	uint16_t inst_offs;
476 	uint16_t offset = buf_offset;
477 
478 	if ((DNS_SD_PTR_MASK & instance_offset) != 0) {
479 		NET_DBG("offset %u too big for message compression",
480 			instance_offset);
481 		return -E2BIG;
482 	}
483 
484 	/* First, calculate that there is enough space in the buffer */
485 	total_size =
486 		/* pointer to .<Instance>.<Service>.<Protocol>.local. */
487 		DNS_POINTER_SIZE + sizeof(*rr) + dns_sd_txt_size(inst);
488 
489 	if (offset > buf_size || total_size >= buf_size - offset) {
490 		NET_DBG("Buffer too small. required: %u available: %d",
491 			total_size, (int)buf_size - (int)offset);
492 		return -ENOSPC;
493 	}
494 
495 	/* insert a pointer to the instance + service name */
496 	inst_offs = instance_offset;
497 	inst_offs |= DNS_SD_PTR_MASK;
498 	inst_offs = htons(inst_offs);
499 	memcpy(&buf[offset], &inst_offs, sizeof(inst_offs));
500 	offset += sizeof(inst_offs);
501 
502 	rr = (struct dns_rr *)&buf[offset];
503 	rr->type = htons(DNS_RR_TYPE_TXT);
504 	rr->class_ = htons(DNS_CLASS_IN | DNS_CLASS_FLUSH);
505 	rr->ttl = htonl(ttl);
506 	rr->rdlength = htons(dns_sd_txt_size(inst));
507 	offset += sizeof(*rr);
508 
509 	memcpy(&buf[offset], inst->text, dns_sd_txt_size(inst));
510 	offset += dns_sd_txt_size(inst);
511 
512 	__ASSERT_NO_MSG(total_size == offset - buf_offset);
513 
514 	return offset - buf_offset;
515 }
516 
add_aaaa_record(const struct dns_sd_rec * inst,uint32_t ttl,uint16_t host_offset,const uint8_t addr[16],uint8_t * buf,uint16_t buf_offset,uint16_t buf_size)517 int add_aaaa_record(const struct dns_sd_rec *inst, uint32_t ttl,
518 		    uint16_t host_offset, const uint8_t addr[16],
519 		    uint8_t *buf, uint16_t buf_offset, uint16_t buf_size)
520 {
521 	uint16_t total_size;
522 	struct dns_rr *rr;
523 	struct dns_aaaa_rdata *rdata;
524 	uint16_t inst_offs;
525 	uint16_t offset = buf_offset;
526 
527 	if ((DNS_SD_PTR_MASK & host_offset) != 0) {
528 		NET_DBG("offset %u too big for message compression",
529 			host_offset);
530 		return -E2BIG;
531 	}
532 
533 	/* First, calculate that there is enough space in the buffer */
534 	total_size =
535 		/* pointer to .<Instance>.local. */
536 		DNS_POINTER_SIZE + sizeof(*rr) + sizeof(*rdata);
537 
538 	if (offset > buf_size || total_size >= buf_size - offset) {
539 		NET_DBG("Buffer too small. required: %u available: %d",
540 			total_size, (int)buf_size - (int)offset);
541 		return -ENOSPC;
542 	}
543 
544 	/* insert a pointer to the instance + service name */
545 	inst_offs = host_offset;
546 	inst_offs |= DNS_SD_PTR_MASK;
547 	inst_offs = htons(inst_offs);
548 	memcpy(&buf[offset], &inst_offs, sizeof(inst_offs));
549 	offset += sizeof(inst_offs);
550 
551 	rr = (struct dns_rr *)&buf[offset];
552 	rr->type = htons(DNS_RR_TYPE_AAAA);
553 	rr->class_ = htons(DNS_CLASS_IN | DNS_CLASS_FLUSH);
554 	rr->ttl = htonl(ttl);
555 	rr->rdlength = htons(sizeof(*rdata));
556 	offset += sizeof(*rr);
557 
558 	rdata = (struct dns_aaaa_rdata *)&buf[offset];
559 	memcpy(rdata->address, addr, sizeof(*rdata));
560 	offset += sizeof(*rdata);
561 
562 	__ASSERT_NO_MSG(total_size == offset - buf_offset);
563 
564 	return offset - buf_offset;
565 }
566 
add_srv_record(const struct dns_sd_rec * inst,uint32_t ttl,uint16_t instance_offset,uint16_t domain_offset,uint8_t * buf,uint16_t buf_offset,uint16_t buf_size,uint16_t * host_offset)567 int add_srv_record(const struct dns_sd_rec *inst, uint32_t ttl,
568 		   uint16_t instance_offset, uint16_t domain_offset,
569 		   uint8_t *buf, uint16_t buf_offset, uint16_t buf_size,
570 		   uint16_t *host_offset)
571 {
572 	uint16_t total_size;
573 	struct dns_rr *rr;
574 	struct dns_srv_rdata *rdata;
575 	size_t label_size;
576 	uint16_t inst_offs;
577 	uint16_t offset = buf_offset;
578 
579 	if ((DNS_SD_PTR_MASK & instance_offset) != 0) {
580 		NET_DBG("offset %u too big for message compression",
581 			instance_offset);
582 		return -E2BIG;
583 	}
584 
585 	if ((DNS_SD_PTR_MASK & domain_offset) != 0) {
586 		NET_DBG("offset %u too big for message compression",
587 			domain_offset);
588 		return -E2BIG;
589 	}
590 
591 	/* First, calculate that there is enough space in the buffer */
592 	total_size =
593 		/* pointer to .<Instance>.<Service>.<Protocol>.local. */
594 		DNS_POINTER_SIZE + sizeof(*rr)
595 		+ sizeof(*rdata)
596 		/* .<Instance> */
597 		+ DNS_LABEL_LEN_SIZE
598 		+ strlen(inst->instance)
599 		/* pointer to .local. */
600 		+ DNS_POINTER_SIZE;
601 
602 	if (offset > buf_size || total_size >= buf_size - offset) {
603 		NET_DBG("Buffer too small. required: %u available: %d",
604 			total_size, (int)buf_size - (int)offset);
605 		return -ENOSPC;
606 	}
607 
608 	/* insert a pointer to the instance + service name */
609 	inst_offs = instance_offset;
610 	inst_offs |= DNS_SD_PTR_MASK;
611 	inst_offs = htons(inst_offs);
612 	memcpy(&buf[offset], &inst_offs, sizeof(inst_offs));
613 	offset += sizeof(inst_offs);
614 
615 	rr = (struct dns_rr *)&buf[offset];
616 	rr->type = htons(DNS_RR_TYPE_SRV);
617 	rr->class_ = htons(DNS_CLASS_IN | DNS_CLASS_FLUSH);
618 	rr->ttl = htonl(ttl);
619 	/* .<Instance>.local. */
620 	rr->rdlength = htons(sizeof(*rdata) + DNS_LABEL_LEN_SIZE
621 			     + strlen(inst->instance) +
622 			     DNS_POINTER_SIZE);
623 	offset += sizeof(*rr);
624 
625 	rdata = (struct dns_srv_rdata *)&buf[offset];
626 	rdata->priority = 0;
627 	rdata->weight = 0;
628 	rdata->port = *(inst->port);
629 	offset += sizeof(*rdata);
630 
631 	*host_offset = offset;
632 
633 	label_size = strlen(inst->instance);
634 	buf[offset++] = label_size;
635 	memcpy(&buf[offset], inst->instance, label_size);
636 	offset += label_size;
637 
638 	domain_offset |= DNS_SD_PTR_MASK;
639 	domain_offset = htons(domain_offset);
640 	memcpy(&buf[offset], &domain_offset, sizeof(domain_offset));
641 	offset += sizeof(domain_offset);
642 
643 	__ASSERT_NO_MSG(total_size == offset - buf_offset);
644 
645 	return offset - buf_offset;
646 }
647 
648 #ifndef CONFIG_NET_TEST
port_in_use_sockaddr(uint16_t proto,uint16_t port,const struct sockaddr * addr)649 static bool port_in_use_sockaddr(uint16_t proto, uint16_t port,
650 	const struct sockaddr *addr)
651 {
652 	const struct sockaddr_in any = {
653 		.sin_family = AF_INET,
654 		.sin_addr.s_addr = INADDR_ANY,
655 	};
656 	const struct sockaddr_in6 any6 = {
657 		.sin6_family = AF_INET6,
658 		.sin6_addr = in6addr_any,
659 	};
660 	const struct sockaddr *anyp =
661 		(addr->sa_family == AF_INET)
662 		? (const struct sockaddr *) &any
663 		: (const struct sockaddr *) &any6;
664 
665 	return
666 		net_context_port_in_use(proto, port, addr)
667 		|| net_context_port_in_use(proto, port, anyp);
668 }
669 
port_in_use(uint16_t proto,uint16_t port,const struct in_addr * addr4,const struct in6_addr * addr6)670 static bool port_in_use(uint16_t proto, uint16_t port, const struct in_addr *addr4,
671 	const struct in6_addr *addr6)
672 {
673 	bool r;
674 	struct sockaddr sa;
675 
676 	if (addr4 != NULL) {
677 		net_sin(&sa)->sin_family = AF_INET;
678 		net_sin(&sa)->sin_addr = *addr4;
679 
680 		r = port_in_use_sockaddr(proto, port, &sa);
681 		if (r) {
682 			return true;
683 		}
684 	}
685 
686 	if (addr6 != NULL) {
687 		net_sin6(&sa)->sin6_family = AF_INET6;
688 		net_sin6(&sa)->sin6_addr = *addr6;
689 
690 		r = port_in_use_sockaddr(proto, port, &sa);
691 		if (r) {
692 			return true;
693 		}
694 	}
695 
696 	return false;
697 }
698 #else /* CONFIG_NET_TEST */
port_in_use(uint16_t proto,uint16_t port,const struct in_addr * addr4,const struct in6_addr * addr6)699 static inline bool port_in_use(uint16_t proto, uint16_t port, const struct in_addr *addr4,
700 	const struct in6_addr *addr6)
701 {
702 	ARG_UNUSED(port);
703 	ARG_UNUSED(addr4);
704 	ARG_UNUSED(addr6);
705 	return true;
706 }
707 #endif /* CONFIG_NET_TEST */
708 
709 
dns_sd_handle_ptr_query(const struct dns_sd_rec * inst,const struct in_addr * addr4,const struct in6_addr * addr6,uint8_t * buf,uint16_t buf_size)710 int dns_sd_handle_ptr_query(const struct dns_sd_rec *inst, const struct in_addr *addr4,
711 			    const struct in6_addr *addr6, uint8_t *buf, uint16_t buf_size)
712 {
713 	/*
714 	 * RFC 6763 Section 12.1
715 	 *
716 	 * When including a DNS-SD Service Instance Enumeration or Selective
717 	 * Instance Enumeration (subtype) PTR record in a response packet, the
718 	 * server/responder SHOULD include the following additional records:
719 	 *
720 	 * o  The SRV record(s) named in the PTR rdata.
721 	 * o  The TXT record(s) named in the PTR rdata.
722 	 * o  All address records (type "A" and "AAAA") named in the SRV rdata.
723 	 *	contain the SRV record(s), the TXT record(s), and the address
724 	 *      records (A or AAAA)
725 	 */
726 
727 	uint16_t instance_offset;
728 	uint16_t service_offset;
729 	uint16_t domain_offset;
730 	uint16_t host_offset;
731 	uint16_t proto;
732 	uint16_t offset = sizeof(struct dns_header);
733 	struct dns_header *rsp = (struct dns_header *)buf;
734 	uint32_t tmp;
735 	int r;
736 
737 	memset(rsp, 0, sizeof(*rsp));
738 
739 	if (!rec_is_valid(inst)) {
740 		return -EINVAL;
741 	}
742 
743 	if (*(inst->port) == 0) {
744 		NET_DBG("Ephemeral port %u for %s.%s.%s.%s not initialized",
745 			ntohs(*(inst->port)), inst->instance, inst->service, inst->proto,
746 			inst->domain);
747 		return -EHOSTDOWN;
748 	}
749 
750 	if (strncmp("_tcp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
751 		proto = IPPROTO_TCP;
752 	} else if (strncmp("_udp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
753 		proto = IPPROTO_UDP;
754 	} else {
755 		NET_DBG("invalid protocol %s", inst->proto);
756 		return -EINVAL;
757 	}
758 
759 	if (!port_in_use(proto, ntohs(*(inst->port)), addr4, addr6)) {
760 		/* Service is not yet bound, so do not advertise */
761 		return -EHOSTDOWN;
762 	}
763 
764 	/* first add the answer record */
765 	r = add_ptr_record(inst, DNS_SD_PTR_TTL, buf, offset, buf_size - offset, &service_offset,
766 			   &instance_offset, &domain_offset);
767 	if (r < 0) {
768 		return r; /* LCOV_EXCL_LINE */
769 	}
770 
771 	rsp->ancount++;
772 	offset += r;
773 
774 	/* then add the additional records */
775 	r = add_txt_record(inst, DNS_SD_TXT_TTL, instance_offset, buf, offset, buf_size - offset);
776 	if (r < 0) {
777 		return r; /* LCOV_EXCL_LINE */
778 	}
779 
780 	rsp->arcount++;
781 	offset += r;
782 
783 	r = add_srv_record(inst, DNS_SD_SRV_TTL, instance_offset, domain_offset, buf, offset,
784 			   buf_size - offset, &host_offset);
785 	if (r < 0) {
786 		return r; /* LCOV_EXCL_LINE */
787 	}
788 
789 	rsp->arcount++;
790 	offset += r;
791 
792 	if (addr6 != NULL) {
793 		r = add_aaaa_record(inst, DNS_SD_AAAA_TTL, host_offset, addr6->s6_addr, buf, offset,
794 				    buf_size - offset); /* LCOV_EXCL_LINE */
795 		if (r < 0) {
796 			return r; /* LCOV_EXCL_LINE */
797 		}
798 
799 		rsp->arcount++;
800 		offset += r;
801 	}
802 
803 	if (addr4 != NULL) {
804 		tmp = htonl(*(addr4->s4_addr32));
805 		r = add_a_record(inst, DNS_SD_A_TTL, host_offset, tmp, buf, offset,
806 				 buf_size - offset);
807 		if (r < 0) {
808 			return r; /* LCOV_EXCL_LINE */
809 		}
810 
811 		rsp->arcount++;
812 		offset += r;
813 	}
814 
815 	/* Set the Response and AA bits */
816 	rsp->flags = htons(BIT(15) | BIT(10));
817 	rsp->ancount = htons(rsp->ancount);
818 	rsp->arcount = htons(rsp->arcount);
819 
820 	return offset;
821 }
822 
dns_sd_handle_service_type_enum(const struct dns_sd_rec * inst,const struct in_addr * addr4,const struct in6_addr * addr6,uint8_t * buf,uint16_t buf_size)823 int dns_sd_handle_service_type_enum(const struct dns_sd_rec *inst,
824 				    const struct in_addr *addr4, const struct in6_addr *addr6,
825 				    uint8_t *buf, uint16_t buf_size)
826 {
827 	static const char query[] = { "\x09_services\x07_dns-sd\x04_udp\x05local" };
828 	/* offset of '.local' in the above */
829 	uint16_t domain_offset = DNS_SD_PTR_MASK | 35;
830 	uint16_t proto;
831 	int name_size;
832 	uint16_t service_size;
833 	uint16_t offset = sizeof(struct dns_header);
834 	struct dns_rr *rr;
835 	struct dns_header *const rsp = (struct dns_header *)buf;
836 
837 	if (!rec_is_valid(inst)) {
838 		return -EINVAL;
839 	}
840 
841 	if (*(inst->port) == 0) {
842 		NET_DBG("Ephemeral port %u for %s.%s.%s.%s "
843 			"not initialized",
844 			ntohs(*(inst->port)), inst->instance, inst->service, inst->proto,
845 			inst->domain);
846 		return -EHOSTDOWN;
847 	}
848 
849 	if (strncmp("_tcp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
850 		proto = IPPROTO_TCP;
851 	} else if (strncmp("_udp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
852 		proto = IPPROTO_UDP;
853 	} else {
854 		NET_DBG("invalid protocol %s", inst->proto);
855 		return -EINVAL;
856 	}
857 
858 	if (!port_in_use(proto, ntohs(*(inst->port)), addr4, addr6)) {
859 		/* Service is not yet bound, so do not advertise */
860 		return -EHOSTDOWN;
861 	}
862 
863 	service_size = strlen(inst->service);
864 	name_size =
865 		/* uncompressed. e.g. "._foo._tcp.local." */
866 		sizeof(query)
867 		+ sizeof(*rr)
868 		/* compressed e.g. ._googlecast._tcp" followed by (DNS_SD_PTR_MASK | 0x0abc) */
869 		+ DNS_LABEL_LEN_SIZE + service_size
870 		+ DNS_LABEL_LEN_SIZE + DNS_SD_PROTO_SIZE
871 		+ DNS_POINTER_SIZE;
872 
873 	if (offset > buf_size || name_size >= buf_size - offset) {
874 		NET_DBG("Buffer too small. required: %u available: %d", name_size,
875 			(int)buf_size - (int)offset);
876 		return -ENOSPC;
877 	}
878 
879 	memset(rsp, 0, sizeof(*rsp));
880 	memcpy(&buf[offset], query, sizeof(query));
881 	offset += sizeof(query);
882 
883 	rr = (struct dns_rr *)&buf[offset];
884 	rr->type = htons(DNS_RR_TYPE_PTR);
885 	rr->class_ = htons(DNS_CLASS_IN);
886 	rr->ttl = htonl(DNS_SD_PTR_TTL);
887 	rr->rdlength = htons(0
888 		+ DNS_LABEL_LEN_SIZE + service_size
889 		+ DNS_LABEL_LEN_SIZE + DNS_SD_PROTO_SIZE
890 		+ DNS_POINTER_SIZE);
891 	offset += sizeof(*rr);
892 
893 	buf[offset++] = service_size;
894 	memcpy(&buf[offset], inst->service, service_size);
895 	offset += service_size;
896 	buf[offset++] = DNS_SD_PROTO_SIZE;
897 	memcpy(&buf[offset], inst->proto, DNS_SD_PROTO_SIZE);
898 	offset += DNS_SD_PROTO_SIZE;
899 	domain_offset = htons(domain_offset);
900 	memcpy(&buf[offset], &domain_offset, sizeof(domain_offset));
901 	offset += sizeof(domain_offset);
902 
903 	/* Set the Response and AA bits */
904 	rsp->flags = htons(BIT(15) | BIT(10));
905 	rsp->ancount = htons(1);
906 
907 	return offset;
908 }
909 
910 /* TODO: dns_sd_handle_srv_query() */
911 /* TODO: dns_sd_handle_txt_query() */
912 
dns_sd_rec_match(const struct dns_sd_rec * record,const struct dns_sd_rec * filter)913 bool dns_sd_rec_match(const struct dns_sd_rec *record,
914 		      const struct dns_sd_rec *filter)
915 {
916 	size_t i;
917 	const char *rec_label;
918 	const char *filt_label;
919 
920 	static bool (*checkers[])(const char *) = {
921 		instance_is_valid,
922 		service_is_valid,
923 		proto_is_valid,
924 		domain_is_valid,
925 	};
926 
927 	static const char *names[] = {
928 		"instance",
929 		"service",
930 		"protocol",
931 		"domain",
932 	};
933 
934 	if (!rec_is_valid(record)) {
935 		LOG_DBG("DNS SD record at %p is invalid", record);
936 		return false;
937 	}
938 
939 	if (filter == NULL) {
940 		return false;
941 	}
942 
943 	/* Deref only after it is deemed safe to do so */
944 	const char *const pairs[] = {
945 		record->instance, filter->instance,
946 		record->service, filter->service,
947 		record->proto, filter->proto,
948 		record->domain, filter->domain,
949 	};
950 
951 	BUILD_ASSERT(ARRAY_SIZE(pairs) == 2 * ARRAY_SIZE(checkers));
952 	BUILD_ASSERT(ARRAY_SIZE(names) == ARRAY_SIZE(checkers));
953 
954 	for (i = 0; i < ARRAY_SIZE(checkers); ++i) {
955 		rec_label = pairs[2 * i];
956 		filt_label = pairs[2 * i + 1];
957 
958 		/* check for the "wildcard" pointer */
959 		if (filt_label != NULL) {
960 			if (!checkers[i](rec_label)) {
961 				LOG_WRN("invalid %s label: '%s'",
962 					names[i], rec_label);
963 				return false;
964 			}
965 
966 			if (strncasecmp(rec_label, filt_label,
967 					DNS_LABEL_MAX_SIZE) != 0) {
968 				return false;
969 			}
970 		}
971 	}
972 
973 	/* check for the "wildcard" port */
974 	if (filter->port != NULL && *(filter->port) != 0) {
975 		if (*(record->port) != *(filter->port)) {
976 			return false;
977 		}
978 	}
979 
980 	return true;
981 }
982 
dns_sd_query_extract(const uint8_t * query,size_t query_size,struct dns_sd_rec * record,char ** label,size_t * size,size_t * n)983 int dns_sd_query_extract(const uint8_t *query, size_t query_size, struct dns_sd_rec *record,
984 			 char **label, size_t *size, size_t *n)
985 {
986 	size_t i;
987 	size_t offset;
988 	size_t qlabels;
989 	size_t qsize;
990 	const size_t N = (n) ? (*n) : 0;
991 
992 	/*
993 	 * See RFC 6763, 7.2. Service Name Length Limits
994 	 *
995 	 *            <sn>._tcp.<servicedomain>.<parentdomain>.
996 	 * <Instance>.<sn>._tcp.<servicedomain>.<parentdomain>.
997 	 * <sub>._sub.<sn>._tcp.<servicedomain>.<parentdomain>.
998 	 */
999 	__ASSERT(DNS_SD_MIN_LABELS <= N, "invalid number of labels %zu", N);
1000 	__ASSERT(!(query == NULL || label == NULL || size == NULL || n == NULL),
1001 		 "one or more required arguments are NULL");
1002 	__ASSERT(query + query_size >= query, "query %p + query_size %zu  wraps NULL", query,
1003 		 query_size);
1004 	__ASSERT(label + N >= label, "label %p + n %zu  wraps NULL", label, N);
1005 	__ASSERT(size + N >= size, "size %p + n %zu  wraps NULL", size, N);
1006 	for (i = 0; i < N; ++i) {
1007 		if (label[i] == NULL) {
1008 			__ASSERT(label[i] != NULL, "label[%zu] is NULL", i);
1009 		}
1010 	}
1011 
1012 	if (query_size <= DNS_MSG_HEADER_SIZE) {
1013 		NET_DBG("query size %zu is less than DNS_MSG_HEADER_SIZE %d", query_size,
1014 			DNS_MSG_HEADER_SIZE);
1015 		return -EINVAL;
1016 	}
1017 
1018 	query += DNS_MSG_HEADER_SIZE;
1019 	query_size -= DNS_MSG_HEADER_SIZE;
1020 	offset = DNS_MSG_HEADER_SIZE;
1021 	dns_sd_create_wildcard_filter(record);
1022 	/* valid record must have non-NULL port */
1023 	record->port = &dns_sd_port_zero;
1024 
1025 	/* also counts labels */
1026 	for (i = 0, qlabels = 0; query_size > 0;) {
1027 		qsize = *query;
1028 		++offset;
1029 		++query;
1030 		--query_size;
1031 
1032 		if (qsize == 0) {
1033 			break;
1034 		}
1035 
1036 		++qlabels;
1037 		if (qsize >= query_size) {
1038 			NET_DBG("claimed query size %zu > query buffer size %zu", qsize,
1039 				query_size);
1040 			return -EINVAL;
1041 		}
1042 
1043 		if (qsize > size[i]) {
1044 			NET_DBG("qsize %zu > size[%zu] %zu", qsize, i, size[i]);
1045 			return -ENOBUFS;
1046 		}
1047 
1048 		if (i < N) {
1049 			/* only extract the label if there is storage for it */
1050 			memcpy(label[i], query, qsize);
1051 			label[i][qsize] = '\0';
1052 			size[i] = qsize;
1053 			++i;
1054 		}
1055 
1056 		offset += qsize;
1057 		query += qsize;
1058 		query_size -= qsize;
1059 	}
1060 
1061 	/* write-out the actual number of labels in 'n' */
1062 	for (*n = i; i < N; ++i) {
1063 		label[i] = NULL;
1064 		size[i] = 0;
1065 	}
1066 
1067 	if (qlabels > N) {
1068 		NET_DBG("too few buffers to extract query: qlabels: %zu, N: %zu",
1069 			qlabels, N);
1070 		return -ENOBUFS;
1071 	}
1072 
1073 	if (qlabels < DNS_SD_MIN_LABELS) {
1074 		NET_DBG("too few labels in query %zu, DNS_SD_MIN_LABELS: %d", qlabels,
1075 			DNS_SD_MIN_LABELS);
1076 		return -EINVAL;
1077 	} else if (qlabels == DNS_SD_MIN_LABELS) {
1078 		/* e.g. _zephyr._tcp.local */
1079 		record->service = label[0];
1080 		record->proto = label[1];
1081 		record->domain = label[2];
1082 
1083 		if (!service_is_valid(record->service)) {
1084 			NET_DBG("service '%s' is invalid", record->service);
1085 			return -EINVAL;
1086 		}
1087 
1088 		if (!proto_is_valid(record->proto)) {
1089 			NET_DBG("proto '%s' is invalid", record->proto);
1090 			return -EINVAL;
1091 		}
1092 
1093 		if (!domain_is_valid(record->domain)) {
1094 			NET_DBG("domain '%s' is invalid", record->domain);
1095 			return -EINVAL;
1096 		}
1097 	} else if (qlabels > DNS_SD_MIN_LABELS && qlabels < DNS_SD_MAX_LABELS) {
1098 		NET_DBG("unsupported number of labels %zu", qlabels);
1099 		return -EINVAL;
1100 	} else if (qlabels >= DNS_SD_MAX_LABELS) {
1101 		/* e.g.
1102 		 * "Zephyr 42"._zephyr._tcp.local, or
1103 		 * _domains._dns-sd._udp.local
1104 		 */
1105 		record->instance = label[0];
1106 		record->service = label[1];
1107 		record->proto = label[2];
1108 		record->domain = label[3];
1109 
1110 		if (!instance_is_valid(record->instance)) {
1111 			NET_DBG("service '%s' is invalid", record->instance);
1112 			return -EINVAL;
1113 		}
1114 
1115 		if (!service_is_valid(record->service)) {
1116 			NET_DBG("service '%s' is invalid", record->service);
1117 			return -EINVAL;
1118 		}
1119 
1120 		if (!proto_is_valid(record->proto)) {
1121 			NET_DBG("proto '%s' is invalid", record->proto);
1122 			return -EINVAL;
1123 		}
1124 
1125 		if (!domain_is_valid(record->domain)) {
1126 			NET_DBG("domain '%s' is invalid", record->domain);
1127 			return -EINVAL;
1128 		}
1129 	}
1130 
1131 	return offset;
1132 }
1133 
dns_sd_extract_service_proto_domain(const uint8_t * query,size_t query_size,struct dns_sd_rec * record,char * service,size_t service_size,char * proto,size_t proto_size,char * domain,size_t domain_size)1134 int dns_sd_extract_service_proto_domain(const uint8_t *query, size_t query_size,
1135 					struct dns_sd_rec *record, char *service,
1136 					size_t service_size, char *proto, size_t proto_size,
1137 					char *domain, size_t domain_size)
1138 {
1139 	char instance[DNS_SD_INSTANCE_MAX_SIZE + 1];
1140 	char *label[4];
1141 	size_t size[] = {
1142 		ARRAY_SIZE(instance),
1143 		service_size,
1144 		proto_size,
1145 		domain_size,
1146 	};
1147 	size_t n = ARRAY_SIZE(label);
1148 
1149 	BUILD_ASSERT(ARRAY_SIZE(label) == ARRAY_SIZE(size),
1150 		"label and size arrays are different size");
1151 
1152 	/*
1153 	 * work around for bug in compliance scripts which say that the array
1154 	 * should be static const (incorrect)
1155 	 */
1156 	label[0] = instance;
1157 	label[1] = service;
1158 	label[2] = proto;
1159 	label[3] = domain;
1160 
1161 	return dns_sd_query_extract(query, query_size, record, label, size, &n);
1162 }
1163 
dns_sd_is_service_type_enumeration(const struct dns_sd_rec * rec)1164 bool dns_sd_is_service_type_enumeration(const struct dns_sd_rec *rec)
1165 {
1166 	static const struct dns_sd_rec filter = {
1167 		.instance = "_services",
1168 		.service = "_dns-sd",
1169 		.proto = "_udp",
1170 		.domain = "local",
1171 	};
1172 
1173 	return dns_sd_rec_match(rec, &filter);
1174 }
1175 
dns_sd_create_wildcard_filter(struct dns_sd_rec * filter)1176 void dns_sd_create_wildcard_filter(struct dns_sd_rec *filter)
1177 {
1178 	if (filter != NULL) {
1179 		memset(filter, 0, sizeof(*filter));
1180 		filter->text = dns_sd_empty_txt;
1181 		filter->text_size = sizeof(dns_sd_empty_txt);
1182 	}
1183 }
1184