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,
671 const struct in_addr *addr4,
672 const struct in6_addr *addr6)
673 {
674 bool ret = false;
675
676 if (addr4 != NULL) {
677 struct sockaddr_in sa = { 0 };
678
679 sa.sin_family = AF_INET;
680 sa.sin_addr = *addr4;
681
682 ret = port_in_use_sockaddr(proto, port,
683 (struct sockaddr *)&sa);
684 if (ret) {
685 goto out;
686 }
687 }
688
689 if (addr6 != NULL) {
690 struct sockaddr_in6 sa = { 0 };
691
692 sa.sin6_family = AF_INET6;
693 sa.sin6_addr = *addr6;
694
695 ret = port_in_use_sockaddr(proto, port,
696 (struct sockaddr *)&sa);
697 if (ret) {
698 goto out;
699 }
700 }
701
702 out:
703 return ret;
704 }
705 #else /* CONFIG_NET_TEST */
port_in_use(uint16_t proto,uint16_t port,const struct in_addr * addr4,const struct in6_addr * addr6)706 static inline bool port_in_use(uint16_t proto, uint16_t port, const struct in_addr *addr4,
707 const struct in6_addr *addr6)
708 {
709 ARG_UNUSED(port);
710 ARG_UNUSED(addr4);
711 ARG_UNUSED(addr6);
712 return true;
713 }
714 #endif /* CONFIG_NET_TEST */
715
716
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)717 int dns_sd_handle_ptr_query(const struct dns_sd_rec *inst, const struct in_addr *addr4,
718 const struct in6_addr *addr6, uint8_t *buf, uint16_t buf_size)
719 {
720 /*
721 * RFC 6763 Section 12.1
722 *
723 * When including a DNS-SD Service Instance Enumeration or Selective
724 * Instance Enumeration (subtype) PTR record in a response packet, the
725 * server/responder SHOULD include the following additional records:
726 *
727 * o The SRV record(s) named in the PTR rdata.
728 * o The TXT record(s) named in the PTR rdata.
729 * o All address records (type "A" and "AAAA") named in the SRV rdata.
730 * contain the SRV record(s), the TXT record(s), and the address
731 * records (A or AAAA)
732 */
733
734 uint16_t instance_offset;
735 uint16_t service_offset;
736 uint16_t domain_offset;
737 uint16_t host_offset;
738 uint16_t proto;
739 uint16_t offset = sizeof(struct dns_header);
740 struct dns_header *rsp = (struct dns_header *)buf;
741 uint32_t tmp;
742 int r;
743
744 memset(rsp, 0, sizeof(*rsp));
745
746 if (!rec_is_valid(inst)) {
747 return -EINVAL;
748 }
749
750 if (*(inst->port) == 0) {
751 NET_DBG("Ephemeral port %u for %s.%s.%s.%s not initialized",
752 ntohs(*(inst->port)), inst->instance, inst->service, inst->proto,
753 inst->domain);
754 return -EHOSTDOWN;
755 }
756
757 if (strncmp("_tcp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
758 proto = IPPROTO_TCP;
759 } else if (strncmp("_udp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
760 proto = IPPROTO_UDP;
761 } else {
762 NET_DBG("invalid protocol %s", inst->proto);
763 return -EINVAL;
764 }
765
766 if (!port_in_use(proto, ntohs(*(inst->port)), addr4, addr6)) {
767 /* Service is not yet bound, so do not advertise */
768 return -EHOSTDOWN;
769 }
770
771 /* first add the answer record */
772 r = add_ptr_record(inst, DNS_SD_PTR_TTL, buf, offset, buf_size - offset, &service_offset,
773 &instance_offset, &domain_offset);
774 if (r < 0) {
775 return r; /* LCOV_EXCL_LINE */
776 }
777
778 rsp->ancount++;
779 offset += r;
780
781 /* then add the additional records */
782 r = add_txt_record(inst, DNS_SD_TXT_TTL, instance_offset, buf, offset, buf_size - offset);
783 if (r < 0) {
784 return r; /* LCOV_EXCL_LINE */
785 }
786
787 rsp->arcount++;
788 offset += r;
789
790 r = add_srv_record(inst, DNS_SD_SRV_TTL, instance_offset, domain_offset, buf, offset,
791 buf_size - offset, &host_offset);
792 if (r < 0) {
793 return r; /* LCOV_EXCL_LINE */
794 }
795
796 rsp->arcount++;
797 offset += r;
798
799 if (addr6 != NULL) {
800 r = add_aaaa_record(inst, DNS_SD_AAAA_TTL, host_offset, addr6->s6_addr, buf, offset,
801 buf_size - offset); /* LCOV_EXCL_LINE */
802 if (r < 0) {
803 return r; /* LCOV_EXCL_LINE */
804 }
805
806 rsp->arcount++;
807 offset += r;
808 }
809
810 if (addr4 != NULL) {
811 tmp = htonl(*(addr4->s4_addr32));
812 r = add_a_record(inst, DNS_SD_A_TTL, host_offset, tmp, buf, offset,
813 buf_size - offset);
814 if (r < 0) {
815 return r; /* LCOV_EXCL_LINE */
816 }
817
818 rsp->arcount++;
819 offset += r;
820 }
821
822 /* Set the Response and AA bits */
823 rsp->flags = htons(BIT(15) | BIT(10));
824 rsp->ancount = htons(rsp->ancount);
825 rsp->arcount = htons(rsp->arcount);
826
827 return offset;
828 }
829
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)830 int dns_sd_handle_service_type_enum(const struct dns_sd_rec *inst,
831 const struct in_addr *addr4, const struct in6_addr *addr6,
832 uint8_t *buf, uint16_t buf_size)
833 {
834 static const char query[] = { "\x09_services\x07_dns-sd\x04_udp\x05local" };
835 /* offset of '.local' in the above */
836 uint16_t domain_offset = DNS_SD_PTR_MASK | 35;
837 uint16_t proto;
838 int name_size;
839 uint16_t service_size;
840 uint16_t offset = sizeof(struct dns_header);
841 struct dns_rr *rr;
842 struct dns_header *const rsp = (struct dns_header *)buf;
843
844 if (!rec_is_valid(inst)) {
845 return -EINVAL;
846 }
847
848 if (*(inst->port) == 0) {
849 NET_DBG("Ephemeral port %u for %s.%s.%s.%s "
850 "not initialized",
851 ntohs(*(inst->port)), inst->instance, inst->service, inst->proto,
852 inst->domain);
853 return -EHOSTDOWN;
854 }
855
856 if (strncmp("_tcp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
857 proto = IPPROTO_TCP;
858 } else if (strncmp("_udp", inst->proto, DNS_SD_PROTO_SIZE) == 0) {
859 proto = IPPROTO_UDP;
860 } else {
861 NET_DBG("invalid protocol %s", inst->proto);
862 return -EINVAL;
863 }
864
865 if (!port_in_use(proto, ntohs(*(inst->port)), addr4, addr6)) {
866 /* Service is not yet bound, so do not advertise */
867 NET_DBG("service not bound");
868 return -EHOSTDOWN;
869 }
870
871 service_size = strlen(inst->service);
872 name_size =
873 /* uncompressed. e.g. "._foo._tcp.local." */
874 sizeof(query)
875 + sizeof(*rr)
876 /* compressed e.g. ._googlecast._tcp" followed by (DNS_SD_PTR_MASK | 0x0abc) */
877 + DNS_LABEL_LEN_SIZE + service_size
878 + DNS_LABEL_LEN_SIZE + DNS_SD_PROTO_SIZE
879 + DNS_POINTER_SIZE;
880
881 if (offset > buf_size || name_size >= buf_size - offset) {
882 NET_DBG("Buffer too small. required: %u available: %d", name_size,
883 (int)buf_size - (int)offset);
884 return -ENOSPC;
885 }
886
887 memset(rsp, 0, sizeof(*rsp));
888 memcpy(&buf[offset], query, sizeof(query));
889 offset += sizeof(query);
890
891 rr = (struct dns_rr *)&buf[offset];
892 rr->type = htons(DNS_RR_TYPE_PTR);
893 rr->class_ = htons(DNS_CLASS_IN);
894 rr->ttl = htonl(DNS_SD_PTR_TTL);
895 rr->rdlength = htons(0
896 + DNS_LABEL_LEN_SIZE + service_size
897 + DNS_LABEL_LEN_SIZE + DNS_SD_PROTO_SIZE
898 + DNS_POINTER_SIZE);
899 offset += sizeof(*rr);
900
901 buf[offset++] = service_size;
902 memcpy(&buf[offset], inst->service, service_size);
903 offset += service_size;
904 buf[offset++] = DNS_SD_PROTO_SIZE;
905 memcpy(&buf[offset], inst->proto, DNS_SD_PROTO_SIZE);
906 offset += DNS_SD_PROTO_SIZE;
907 domain_offset = htons(domain_offset);
908 memcpy(&buf[offset], &domain_offset, sizeof(domain_offset));
909 offset += sizeof(domain_offset);
910
911 /* Set the Response and AA bits */
912 rsp->flags = htons(BIT(15) | BIT(10));
913 rsp->ancount = htons(1);
914
915 return offset;
916 }
917
918 /* TODO: dns_sd_handle_srv_query() */
919 /* TODO: dns_sd_handle_txt_query() */
920
dns_sd_rec_match(const struct dns_sd_rec * record,const struct dns_sd_rec * filter)921 bool dns_sd_rec_match(const struct dns_sd_rec *record,
922 const struct dns_sd_rec *filter)
923 {
924 size_t i;
925 const char *rec_label;
926 const char *filt_label;
927
928 static bool (*checkers[])(const char *) = {
929 instance_is_valid,
930 service_is_valid,
931 proto_is_valid,
932 domain_is_valid,
933 };
934
935 static const char *names[] = {
936 "instance",
937 "service",
938 "protocol",
939 "domain",
940 };
941
942 if (!rec_is_valid(record)) {
943 LOG_DBG("DNS SD record at %p is invalid", record);
944 return false;
945 }
946
947 if (filter == NULL) {
948 return false;
949 }
950
951 /* Deref only after it is deemed safe to do so */
952 const char *const pairs[] = {
953 record->instance, filter->instance,
954 record->service, filter->service,
955 record->proto, filter->proto,
956 record->domain, filter->domain,
957 };
958
959 BUILD_ASSERT(ARRAY_SIZE(pairs) == 2 * ARRAY_SIZE(checkers));
960 BUILD_ASSERT(ARRAY_SIZE(names) == ARRAY_SIZE(checkers));
961
962 for (i = 0; i < ARRAY_SIZE(checkers); ++i) {
963 rec_label = pairs[2 * i];
964 filt_label = pairs[2 * i + 1];
965
966 /* check for the "wildcard" pointer */
967 if (filt_label != NULL) {
968 if (!checkers[i](rec_label)) {
969 LOG_WRN("invalid %s label: '%s'",
970 names[i], rec_label);
971 return false;
972 }
973
974 if (strncasecmp(rec_label, filt_label,
975 DNS_LABEL_MAX_SIZE) != 0) {
976 return false;
977 }
978 }
979 }
980
981 /* check for the "wildcard" port */
982 if (filter->port != NULL && *(filter->port) != 0) {
983 if (*(record->port) != *(filter->port)) {
984 return false;
985 }
986 }
987
988 return true;
989 }
990
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)991 int dns_sd_query_extract(const uint8_t *query, size_t query_size, struct dns_sd_rec *record,
992 char **label, size_t *size, size_t *n)
993 {
994 size_t i;
995 size_t offset;
996 size_t qlabels;
997 size_t qsize;
998 const size_t N = (n) ? (*n) : 0;
999
1000 /*
1001 * See RFC 6763, 7.2. Service Name Length Limits
1002 *
1003 * <sn>._tcp.<servicedomain>.<parentdomain>.
1004 * <Instance>.<sn>._tcp.<servicedomain>.<parentdomain>.
1005 * <sub>._sub.<sn>._tcp.<servicedomain>.<parentdomain>.
1006 */
1007 __ASSERT(DNS_SD_MIN_LABELS <= N, "invalid number of labels %zu", N);
1008 __ASSERT(!(query == NULL || label == NULL || size == NULL || n == NULL),
1009 "one or more required arguments are NULL");
1010 __ASSERT(query + query_size >= query, "query %p + query_size %zu wraps NULL", query,
1011 query_size);
1012 __ASSERT(label + N >= label, "label %p + n %zu wraps NULL", label, N);
1013 __ASSERT(size + N >= size, "size %p + n %zu wraps NULL", size, N);
1014 for (i = 0; i < N; ++i) {
1015 if (label[i] == NULL) {
1016 __ASSERT(label[i] != NULL, "label[%zu] is NULL", i);
1017 }
1018 }
1019
1020 if (query_size <= DNS_MSG_HEADER_SIZE) {
1021 NET_DBG("query size %zu is less than DNS_MSG_HEADER_SIZE %d", query_size,
1022 DNS_MSG_HEADER_SIZE);
1023 return -EINVAL;
1024 }
1025
1026 query += DNS_MSG_HEADER_SIZE;
1027 query_size -= DNS_MSG_HEADER_SIZE;
1028 offset = DNS_MSG_HEADER_SIZE;
1029 dns_sd_create_wildcard_filter(record);
1030 /* valid record must have non-NULL port */
1031 record->port = &dns_sd_port_zero;
1032
1033 /* also counts labels */
1034 for (i = 0, qlabels = 0; query_size > 0;) {
1035 qsize = *query;
1036 ++offset;
1037 ++query;
1038 --query_size;
1039
1040 if (qsize == 0) {
1041 break;
1042 }
1043
1044 ++qlabels;
1045 if (qsize >= query_size) {
1046 NET_DBG("claimed query size %zu > query buffer size %zu", qsize,
1047 query_size);
1048 return -EINVAL;
1049 }
1050
1051 if (qsize >= size[i]) {
1052 NET_DBG("qsize %zu >= size[%zu] %zu", qsize, i, size[i]);
1053 return -ENOBUFS;
1054 }
1055
1056 if (i < N) {
1057 /* only extract the label if there is storage for it */
1058 memcpy(label[i], query, qsize);
1059 label[i][qsize] = '\0';
1060 size[i] = qsize;
1061 ++i;
1062 }
1063
1064 offset += qsize;
1065 query += qsize;
1066 query_size -= qsize;
1067 }
1068
1069 /* write-out the actual number of labels in 'n' */
1070 for (*n = i; i < N; ++i) {
1071 label[i] = NULL;
1072 size[i] = 0;
1073 }
1074
1075 if (qlabels > N) {
1076 NET_DBG("too few buffers to extract query: qlabels: %zu, N: %zu",
1077 qlabels, N);
1078 return -ENOBUFS;
1079 }
1080
1081 if (qlabels < DNS_SD_MIN_LABELS) {
1082 NET_DBG("too few labels in query %zu, DNS_SD_MIN_LABELS: %d", qlabels,
1083 DNS_SD_MIN_LABELS);
1084 return -EINVAL;
1085 } else if (qlabels == DNS_SD_MIN_LABELS) {
1086 /* e.g. _zephyr._tcp.local */
1087 record->service = label[0];
1088 record->proto = label[1];
1089 record->domain = label[2];
1090
1091 if (!service_is_valid(record->service)) {
1092 NET_DBG("service '%s' is invalid", record->service);
1093 return -EINVAL;
1094 }
1095
1096 if (!proto_is_valid(record->proto)) {
1097 NET_DBG("proto '%s' is invalid", record->proto);
1098 return -EINVAL;
1099 }
1100
1101 if (!domain_is_valid(record->domain)) {
1102 NET_DBG("domain '%s' is invalid", record->domain);
1103 return -EINVAL;
1104 }
1105 } else if (qlabels > DNS_SD_MIN_LABELS && qlabels < DNS_SD_MAX_LABELS) {
1106 NET_DBG("unsupported number of labels %zu", qlabels);
1107 return -EINVAL;
1108 } else if (qlabels >= DNS_SD_MAX_LABELS) {
1109 /* e.g.
1110 * "Zephyr 42"._zephyr._tcp.local, or
1111 * _domains._dns-sd._udp.local
1112 */
1113 record->instance = label[0];
1114 record->service = label[1];
1115 record->proto = label[2];
1116 record->domain = label[3];
1117
1118 if (!instance_is_valid(record->instance)) {
1119 NET_DBG("service '%s' is invalid", record->instance);
1120 return -EINVAL;
1121 }
1122
1123 if (!service_is_valid(record->service)) {
1124 NET_DBG("service '%s' is invalid", record->service);
1125 return -EINVAL;
1126 }
1127
1128 if (!proto_is_valid(record->proto)) {
1129 NET_DBG("proto '%s' is invalid", record->proto);
1130 return -EINVAL;
1131 }
1132
1133 if (!domain_is_valid(record->domain)) {
1134 NET_DBG("domain '%s' is invalid", record->domain);
1135 return -EINVAL;
1136 }
1137 }
1138
1139 return offset;
1140 }
1141
dns_sd_is_service_type_enumeration(const struct dns_sd_rec * rec)1142 bool dns_sd_is_service_type_enumeration(const struct dns_sd_rec *rec)
1143 {
1144 static const struct dns_sd_rec filter = {
1145 .instance = "_services",
1146 .service = "_dns-sd",
1147 .proto = "_udp",
1148 .domain = "local",
1149 };
1150
1151 return dns_sd_rec_match(rec, &filter);
1152 }
1153
dns_sd_create_wildcard_filter(struct dns_sd_rec * filter)1154 void dns_sd_create_wildcard_filter(struct dns_sd_rec *filter)
1155 {
1156 if (filter != NULL) {
1157 memset(filter, 0, sizeof(*filter));
1158 filter->text = dns_sd_empty_txt;
1159 filter->text_size = sizeof(dns_sd_empty_txt);
1160 }
1161 }
1162