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