]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/mdns.c
mDNSResponder-1096.100.3.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / mdns.c
1 /*
2 * Copyright (c) 2019 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "mdns_private.h"
18
19 #include "mdns_object.h"
20
21 #include <CoreUtils/CoreUtils.h>
22 #include <network_information.h>
23 #include <notify.h>
24 #include <os/log.h>
25 #include <os/object_private.h>
26
27 //======================================================================================================================
28 // MARK: - Kind Declarations
29
30 #define MDNS_STRUCT(NAME) struct mdns_ ## NAME ## _s
31
32 // Note: The last check checks if the base's type is equal to that of the superkind. If it's not, then the pointer
33 // comparison used as the argument to sizeof will cause a "comparison of distinct pointer types" warning, so long as
34 // the warning hasn't been disabled.
35
36 #define MDNS_BASE_CHECK(NAME, SUPER) \
37 check_compile_time(offsetof(MDNS_STRUCT(NAME), base) == 0); \
38 check_compile_time(sizeof_field(MDNS_STRUCT(NAME), base) == sizeof(MDNS_STRUCT(SUPER))); \
39 extern int _mdns_base_type_check[sizeof(&(((mdns_ ## NAME ## _t)0)->base) == ((mdns_ ## SUPER ## _t)0))]
40
41 #define MDNS_OBJECT_SUBKIND_DEFINE(NAME) \
42 static void \
43 _mdns_ ## NAME ## _finalize(mdns_ ## NAME ## _t object); \
44 \
45 static char * \
46 _mdns_ ## NAME ## _copy_description(mdns_ ## NAME ## _t object, bool debug, bool privacy); \
47 \
48 static const struct mdns_kind_s _mdns_ ## NAME ## _kind = { \
49 &_mdns_object_kind, \
50 # NAME, \
51 _mdns_ ## NAME ## _copy_description, \
52 _mdns_ ## NAME ## _finalize \
53 }; \
54 \
55 static mdns_ ## NAME ## _t \
56 _mdns_ ## NAME ## _alloc(void) \
57 { \
58 mdns_ ## NAME ## _t obj = mdns_object_ ## NAME ## _alloc(sizeof(*obj)); \
59 require_quiet(obj, exit); \
60 \
61 const mdns_object_t base = (mdns_object_t)obj; \
62 base->kind = &_mdns_ ## NAME ## _kind; \
63 \
64 exit: \
65 return obj; \
66 } \
67 MDNS_BASE_CHECK(NAME, object)
68
69 typedef char * (*mdns_copy_description_f)(mdns_any_t object, bool debug, bool privacy);
70 typedef void (*mdns_finalize_f)(mdns_any_t object);
71
72 typedef const struct mdns_kind_s * mdns_kind_t;
73 struct mdns_kind_s {
74 mdns_kind_t superkind; // This kind's superkind.
75 const char * name; // Name of this kind.
76 mdns_copy_description_f copy_description; // Creates a textual description of object.
77 mdns_finalize_f finalize; // Releases object's resources right before the object is freed.
78 };
79
80 //======================================================================================================================
81 // MARK: - mdns_object Kind Definition
82
83 struct mdns_object_s {
84 _OS_OBJECT_HEADER(const void *_os_obj_isa, _os_obj_refcnt, _os_obj_xref_cnt);
85 mdns_kind_t kind; // Pointer to an object's kind.
86 };
87
88 static const struct mdns_kind_s _mdns_object_kind = {
89 NULL, // No superkind.
90 "object", // Name.
91 NULL, // No copy_description method.
92 NULL // No finalize method.
93 };
94
95 static const void *
96 _mdns_cf_collection_callback_retain(CFAllocatorRef allocator, const void *object);
97
98 static void
99 _mdns_cf_collection_callback_release(CFAllocatorRef allocator, const void *object);
100
101 static CFStringRef
102 _mdns_cf_collection_callback_copy_description(const void *object);
103
104 const CFArrayCallBacks mdns_cfarray_callbacks = {
105 0, // version
106 _mdns_cf_collection_callback_retain, // retain
107 _mdns_cf_collection_callback_release, // release
108 _mdns_cf_collection_callback_copy_description, // copy description
109 NULL // equal (NULL for pointer equality)
110 };
111
112 //======================================================================================================================
113 // MARK: - mdns_interface_monitor Kind Definition
114
115 struct mdns_interface_monitor_s {
116 struct mdns_object_s base; // Object base.
117 mdns_interface_monitor_t next; // Next monitor in list.
118 dispatch_queue_t user_queue; // User's queue for invoking handlers.
119 nw_path_evaluator_t path_evaluator; // Path evaluator for interface properties.
120 dispatch_source_t update_source; // Data source for triggering user's update handler.
121 mdns_interface_monitor_update_handler_t update_handler; // User's update handler.
122 mdns_event_handler_t event_handler; // User's event handler.
123 char * ifname; // Name of monitored interface.
124 uint32_t ifindex; // Index of monitored interface.
125 mdns_interface_flags_t pending_flags; // The latest interface flags from path updates.
126 mdns_interface_flags_t flags; // The current interface flags made known to user.
127 bool user_activated; // True if user called activate method.
128 bool activated; // True if the monitor has been activated.
129 bool invalidated; // True if the monitor has been invalidated.
130 bool path_evaluator_started; // True if the path evaluator has been started.
131 };
132
133 MDNS_OBJECT_SUBKIND_DEFINE(interface_monitor);
134
135 //======================================================================================================================
136 // MARK: - Local Prototypes
137
138 static dispatch_queue_t
139 _mdns_internal_queue(void);
140
141 static dispatch_queue_t
142 _mdns_nwi_state_mutex_queue(void);
143
144 static void
145 _mdns_interface_monitor_activate_async(mdns_interface_monitor_t monitor);
146
147 static void
148 _mdns_interface_monitor_terminate(mdns_interface_monitor_t me, const OSStatus error);
149
150 static mdns_interface_flags_t
151 _mdns_get_interface_flags_from_nw_path(nw_path_t path, mdns_interface_flags_t current_flags);
152
153 static mdns_interface_flags_t
154 _mdns_get_interface_flags_from_nwi_state(const char *ifname, mdns_interface_flags_t current_flags);
155
156 static int
157 _mdns_snprintf_add(char **ptr, const char *lim, const char *fmt, ...);
158
159 static void
160 _mdns_start_nwi_state_monitoring(void);
161
162 #if !defined(nw_forget)
163 #define nw_forget(X) ForgetCustom(X, nw_release)
164 #endif
165
166 #if !defined(nw_release_null_safe)
167 #define nw_release_null_safe(X) do { if (X) { nw_release(X); } } while (0)
168 #endif
169
170 #if !defined(nwi_state_release_null_safe)
171 #define nwi_state_release_null_safe(X) do { if (X) { nwi_state_release(X); } } while (0)
172 #endif
173
174 //======================================================================================================================
175 // MARK: - Globals
176
177 static mdns_interface_monitor_t g_monitor_list = NULL;
178 static nwi_state_t g_nwi_state = NULL;
179
180 //======================================================================================================================
181 // MARK: - Internals
182
183 static dispatch_queue_t
184 _mdns_internal_queue(void)
185 {
186 static dispatch_once_t s_once = 0;
187 static dispatch_queue_t s_queue = NULL;
188 dispatch_once(&s_once,
189 ^{
190 s_queue = dispatch_queue_create("com.apple.mdns.internal_queue", DISPATCH_QUEUE_SERIAL);
191 });
192 return s_queue;
193 }
194
195 //======================================================================================================================
196
197 static dispatch_queue_t
198 _mdns_nwi_state_mutex_queue(void)
199 {
200 static dispatch_once_t s_once = 0;
201 static dispatch_queue_t s_queue = NULL;
202 dispatch_once(&s_once,
203 ^{
204 s_queue = dispatch_queue_create("com.apple.mdns.nwi_state_mutex", DISPATCH_QUEUE_SERIAL);
205 });
206 return s_queue;
207 }
208
209 //======================================================================================================================
210
211 #define MDNS_LOG_CATEGORY_DEFINE(SHORT_NAME, CATEGORY_STR) \
212 static os_log_t \
213 _mdns_ ## SHORT_NAME ## _log(void) \
214 { \
215 static dispatch_once_t s_once = 0; \
216 static os_log_t s_log = NULL; \
217 dispatch_once(&s_once, \
218 ^{ \
219 s_log = os_log_create("com.apple.mdns", CATEGORY_STR); \
220 }); \
221 return s_log; \
222 } \
223 extern int _mdns_dummy_variable
224
225 MDNS_LOG_CATEGORY_DEFINE(ifmon, "interface_monitor");
226 MDNS_LOG_CATEGORY_DEFINE(nwi, "NWI");
227
228 //======================================================================================================================
229 // MARK: - mdns_object Public Methods
230
231 void
232 mdns_retain(mdns_any_t object)
233 {
234 os_retain(object.base);
235 }
236
237 //======================================================================================================================
238
239 void
240 mdns_release(mdns_any_t object)
241 {
242 os_release(object.base);
243 }
244
245 //======================================================================================================================
246
247 char *
248 mdns_copy_description(mdns_any_t object)
249 {
250 return mdns_object_copy_description(object, false, false);
251 }
252
253 //======================================================================================================================
254 // MARK: - mdns_object Private Methods
255
256 char *
257 mdns_object_copy_description(mdns_any_t object, bool debug, bool privacy)
258 {
259 for (mdns_kind_t kind = object.base->kind; kind; kind = kind->superkind) {
260 if (kind->copy_description) {
261 return kind->copy_description(object, debug, privacy);
262 }
263 }
264 return NULL;
265 }
266
267 //======================================================================================================================
268
269 CFStringRef
270 mdns_object_copy_description_as_cfstring(mdns_any_t object, bool debug, bool privacy)
271 {
272 CFStringRef description = NULL;
273 char *cstring = mdns_object_copy_description(object, debug, privacy);
274 require_quiet(cstring, exit);
275
276 description = CFStringCreateWithCStringNoCopy(NULL, cstring, kCFStringEncodingUTF8, kCFAllocatorMalloc);
277 require_quiet(description, exit);
278 cstring = NULL;
279
280 exit:
281 FreeNullSafe(cstring);
282 return description;
283 }
284
285 //======================================================================================================================
286
287 void
288 mdns_object_finalize(mdns_any_t object)
289 {
290 for (mdns_kind_t kind = object.base->kind; kind; kind = kind->superkind) {
291 if (kind->finalize) {
292 kind->finalize(object);
293 }
294 }
295 }
296
297 //======================================================================================================================
298
299 static const void *
300 _mdns_cf_collection_callback_retain(__unused CFAllocatorRef allocator, const void *object)
301 {
302 mdns_retain((mdns_object_t)object);
303 return object;
304 }
305
306 //======================================================================================================================
307
308 static void
309 _mdns_cf_collection_callback_release(__unused CFAllocatorRef allocator, const void *object)
310 {
311 mdns_release((mdns_object_t)object);
312 }
313
314 //======================================================================================================================
315
316 static CFStringRef
317 _mdns_cf_collection_callback_copy_description(const void *object)
318 {
319 return mdns_object_copy_description_as_cfstring((mdns_object_t)object, false, false);
320 }
321
322 //======================================================================================================================
323 // MARK: - mdns_interface_monitor Public Methods
324
325 mdns_interface_monitor_t
326 mdns_interface_monitor_create(uint32_t interface_index)
327 {
328 mdns_interface_monitor_t monitor = NULL;
329 nw_interface_t interface = NULL;
330 nw_parameters_t params = NULL;
331
332 mdns_interface_monitor_t obj = _mdns_interface_monitor_alloc();
333 require_quiet(obj, exit);
334
335 obj->ifindex = interface_index;
336 char ifname[IF_NAMESIZE + 1];
337 if (if_indextoname(obj->ifindex, ifname) == NULL) {
338 os_log_error(_mdns_ifmon_log(), "if_indextoname returned NULL for index %u", obj->ifindex);
339 goto exit;
340 }
341 obj->ifname = strdup(ifname);
342 require_quiet(obj->ifname, exit);
343
344 interface = nw_interface_create_with_index(obj->ifindex);
345 if (!interface) {
346 os_log_error(_mdns_ifmon_log(), "nw_interface_create_with_index returned NULL for index %u", obj->ifindex);
347 goto exit;
348 }
349
350 params = nw_parameters_create();
351 require_quiet(params, exit);
352
353 nw_parameters_require_interface(params, interface);
354 obj->path_evaluator = nw_path_create_evaluator_for_endpoint(NULL, params);
355 if (!obj->path_evaluator) {
356 os_log_error(_mdns_ifmon_log(), "nw_path_create_evaluator_for_endpoint returned NULL for params: %@", params);
357 goto exit;
358 }
359
360 nw_path_t path = nw_path_evaluator_copy_path(obj->path_evaluator);
361 require_quiet(path, exit);
362
363 obj->pending_flags = _mdns_get_interface_flags_from_nw_path(path, mdns_interface_flag_null);
364 obj->pending_flags = _mdns_get_interface_flags_from_nwi_state(obj->ifname, obj->pending_flags);
365 obj->flags = obj->pending_flags;
366 nw_forget(&path);
367
368 monitor = obj;
369 obj = NULL;
370
371 exit:
372 if (obj) {
373 mdns_release(obj);
374 }
375 nw_release_null_safe(interface);
376 nw_release_null_safe(params);
377 return monitor;
378 }
379
380 //======================================================================================================================
381
382 void
383 mdns_interface_monitor_activate(mdns_interface_monitor_t me)
384 {
385 if (!me->user_activated) {
386 if (me->user_queue) {
387 _mdns_interface_monitor_activate_async(me);
388 }
389 me->user_activated = true;
390 }
391 }
392
393 //======================================================================================================================
394
395 void
396 mdns_interface_monitor_invalidate(mdns_interface_monitor_t me)
397 {
398 mdns_retain(me);
399 dispatch_async(_mdns_internal_queue(),
400 ^{
401 if (!me->invalidated) {
402 _mdns_interface_monitor_terminate(me, kNoErr);
403 me->invalidated = true;
404 }
405 mdns_release(me);
406 });
407 }
408
409 //======================================================================================================================
410
411 void
412 mdns_interface_monitor_set_queue(mdns_interface_monitor_t me, dispatch_queue_t queue)
413 {
414 if (!me->user_activated) {
415 dispatch_retain(queue);
416 dispatch_release_null_safe(me->user_queue);
417 me->user_queue = queue;
418 } else if (!me->user_queue) {
419 me->user_queue = queue;
420 dispatch_retain(me->user_queue);
421 _mdns_interface_monitor_activate_async(me);
422 }
423 }
424
425 //======================================================================================================================
426
427 void
428 mdns_interface_monitor_set_event_handler(mdns_interface_monitor_t me, mdns_event_handler_t handler)
429 {
430 mdns_event_handler_t const new_handler = handler ? Block_copy(handler) : NULL;
431 if (me->event_handler) {
432 Block_release(me->event_handler);
433 }
434 me->event_handler = new_handler;
435 }
436
437 //======================================================================================================================
438
439 void
440 mdns_interface_monitor_set_update_handler(mdns_interface_monitor_t me, mdns_interface_monitor_update_handler_t handler)
441 {
442 mdns_interface_monitor_update_handler_t const new_handler = handler ? Block_copy(handler) : NULL;
443 if (me->update_handler) {
444 Block_release(me->update_handler);
445 }
446 me->update_handler = new_handler;
447 }
448
449 //======================================================================================================================
450
451 uint32_t
452 mdns_interface_monitor_get_interface_index(mdns_interface_monitor_t me)
453 {
454 return me->ifindex;
455 }
456
457 //======================================================================================================================
458
459 bool
460 mdns_interface_monitor_has_ipv4_connectivity(mdns_interface_monitor_t me)
461 {
462 return ((me->flags & mdns_interface_flag_ipv4_connectivity) ? true : false);
463 }
464
465 //======================================================================================================================
466
467 bool
468 mdns_interface_monitor_has_ipv6_connectivity(mdns_interface_monitor_t me)
469 {
470 return ((me->flags & mdns_interface_flag_ipv6_connectivity) ? true : false);
471 }
472
473 //======================================================================================================================
474
475 bool
476 mdns_interface_monitor_is_expensive(mdns_interface_monitor_t me)
477 {
478 return ((me->flags & mdns_interface_flag_expensive) ? true : false);
479 }
480
481 //======================================================================================================================
482
483 bool
484 mdns_interface_monitor_is_constrained(mdns_interface_monitor_t me)
485 {
486 return ((me->flags & mdns_interface_flag_constrained) ? true : false);
487 }
488
489 //======================================================================================================================
490
491 bool
492 mdns_interface_monitor_is_clat46(mdns_interface_monitor_t me)
493 {
494 return ((me->flags & mdns_interface_flag_clat46) ? true : false);
495 }
496
497 //======================================================================================================================
498 // MARK: - mdns_interface_monitor Private Methods
499
500 typedef struct {
501 mdns_interface_flags_t flag;
502 const char * desc;
503 } mdns_interface_flag_description_t;
504
505 const mdns_interface_flag_description_t mdns_interface_flag_descriptions[] = {
506 { mdns_interface_flag_ipv4_connectivity, "IPv4" },
507 { mdns_interface_flag_ipv6_connectivity, "IPv6" },
508 { mdns_interface_flag_expensive, "expensive" },
509 { mdns_interface_flag_constrained, "constrained" },
510 { mdns_interface_flag_clat46, "CLAT46" }
511 };
512
513 static char *
514 _mdns_interface_monitor_copy_description(mdns_interface_monitor_t me, const bool debug, __unused const bool privacy)
515 {
516 char * description = NULL;
517 char buffer[128];
518 char * dst = buffer;
519 const char * const lim = &buffer[countof(buffer)];
520 int n;
521
522 *dst = '\0';
523 if (debug) {
524 n = _mdns_snprintf_add(&dst, lim, "mdns_%s (%p): ", me->base.kind->name, me);
525 require_quiet(n >= 0, exit);
526 }
527 n = _mdns_snprintf_add(&dst, lim, "interface %s (%u): ", me->ifname, me->ifindex);
528 require_quiet(n >= 0, exit);
529
530 const char *separator = "";
531 for (size_t i = 0; i < countof(mdns_interface_flag_descriptions); ++i) {
532 const mdns_interface_flag_description_t * const flag_desc = &mdns_interface_flag_descriptions[i];
533 if (me->flags & flag_desc->flag) {
534 n = _mdns_snprintf_add(&dst, lim, "%s%s", separator, flag_desc->desc);
535 require_quiet(n >= 0, exit);
536 separator = ", ";
537 }
538 }
539 description = strdup(buffer);
540
541 exit:
542 return description;
543 }
544
545 //======================================================================================================================
546
547 static void
548 _mdns_interface_monitor_finalize(mdns_interface_monitor_t me)
549 {
550 dispatch_forget(&me->user_queue);
551 nw_forget(&me->path_evaluator);
552 BlockForget(&me->update_handler);
553 BlockForget(&me->event_handler);
554 ForgetMem(&me->ifname);
555 }
556
557 //======================================================================================================================
558
559 static void
560 _mdns_interface_monitor_activate_internal(mdns_interface_monitor_t monitor);
561
562 static void
563 _mdns_interface_monitor_activate_async(mdns_interface_monitor_t me)
564 {
565 mdns_retain(me);
566 dispatch_async(_mdns_internal_queue(),
567 ^{
568 _mdns_interface_monitor_activate_internal(me);
569 mdns_release(me);
570 });
571 }
572
573 static void
574 _mdns_interface_monitor_activate_internal(mdns_interface_monitor_t me)
575 {
576 OSStatus err;
577 require_action_quiet(!me->activated && !me->invalidated, exit, err = kNoErr);
578 me->activated = true;
579
580 me->update_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_REPLACE, 0, 0, me->user_queue);
581 require_action_quiet(me->update_source, exit, err = kNoResourcesErr);
582
583 mdns_retain(me);
584 const dispatch_source_t update_source = me->update_source;
585 dispatch_source_set_event_handler(me->update_source,
586 ^{
587 const unsigned long data = dispatch_source_get_data(update_source);
588 const mdns_interface_flags_t new_flags = ((mdns_interface_flags_t)data) & ~mdns_interface_flag_reserved;
589 const mdns_interface_flags_t changed_flags = me->flags ^ new_flags;
590 if (changed_flags != 0) {
591 me->flags = new_flags;
592 if (me->update_handler) {
593 me->update_handler(changed_flags);
594 }
595 }
596 });
597 dispatch_source_set_cancel_handler(me->update_source,
598 ^{
599 mdns_release(me);
600 });
601 dispatch_activate(me->update_source);
602
603 mdns_retain(me);
604 nw_path_evaluator_set_update_handler(me->path_evaluator, _mdns_internal_queue(),
605 ^(nw_path_t path)
606 {
607 const mdns_interface_flags_t new_flags = _mdns_get_interface_flags_from_nw_path(path, me->pending_flags);
608 if (new_flags != me->pending_flags) {
609 me->pending_flags = new_flags;
610 if (me->update_source) {
611 // Note: mdns_interface_flag_reserved is used to ensure that the data is non-zero. According to the
612 // dispatch_source_create(3) man page, if the data value is zero, the source handler won't be invoked.
613 dispatch_source_merge_data(me->update_source, me->pending_flags | mdns_interface_flag_reserved);
614 }
615 }
616 });
617 nw_path_evaluator_set_cancel_handler(me->path_evaluator,
618 ^{
619 mdns_release(me);
620 });
621 nw_path_evaluator_start(me->path_evaluator);
622 me->path_evaluator_started = true;
623
624 mdns_interface_monitor_t *p = &g_monitor_list;
625 while (*p != NULL) {
626 p = &(*p)->next;
627 }
628 mdns_retain(me);
629 *p = me;
630
631 // This is called after adding the monitor to the global list to ensure that the initial NWI state check is aware
632 // that the interface monitor exists.
633 _mdns_start_nwi_state_monitoring();
634 err = kNoErr;
635
636 exit:
637 if (err) {
638 _mdns_interface_monitor_terminate(me, err);
639 }
640 }
641
642 //======================================================================================================================
643
644 static void
645 _mdns_interface_monitor_terminate(mdns_interface_monitor_t me, const OSStatus error)
646 {
647 dispatch_source_forget(&me->update_source);
648 if (me->path_evaluator) {
649 if (me->path_evaluator_started) {
650 nw_path_evaluator_cancel(me->path_evaluator);
651 }
652 nw_forget(&me->path_evaluator);
653 }
654 for (mdns_interface_monitor_t *p = &g_monitor_list; *p; p = &(*p)->next) {
655 if (*p == me) {
656 *p = me->next;
657 me->next = NULL;
658 mdns_release(me);
659 break;
660 }
661 }
662 mdns_retain(me);
663 dispatch_async(me->user_queue,
664 ^{
665 if (me->event_handler) {
666 me->event_handler(error ? mdns_event_error : mdns_event_invalidated, error);
667 }
668 mdns_release(me);
669 });
670 }
671
672 //======================================================================================================================
673 // MARK: - NW Path Helpers
674
675 #define MDNS_INTERFACE_FLAGS_FROM_NWPATH \
676 (mdns_interface_flag_ipv4_connectivity | \
677 mdns_interface_flag_ipv6_connectivity | \
678 mdns_interface_flag_expensive | \
679 mdns_interface_flag_constrained)
680
681 static mdns_interface_flags_t
682 _mdns_get_interface_flags_from_nw_path(nw_path_t path, mdns_interface_flags_t current_flags)
683 {
684 mdns_interface_flags_t flags = current_flags & ~MDNS_INTERFACE_FLAGS_FROM_NWPATH;
685 if (nw_path_has_ipv4(path)) {
686 flags |= mdns_interface_flag_ipv4_connectivity;
687 }
688 if (nw_path_has_ipv6(path)) {
689 flags |= mdns_interface_flag_ipv6_connectivity;
690 }
691 if (nw_path_is_expensive(path)) {
692 flags |= mdns_interface_flag_expensive;
693 }
694 if (__builtin_available(macOS 10.15, *)) {
695 if (nw_path_is_constrained(path)) {
696 flags |= mdns_interface_flag_constrained;
697 }
698 }
699 return flags;
700 }
701
702 //======================================================================================================================
703 // MARK: - NWI Helpers
704
705 #if !defined(NWI_IFSTATE_FLAGS_HAS_CLAT46)
706 #define NWI_IFSTATE_FLAGS_HAS_CLAT46 0x0040
707 #endif
708
709 #define MDNS_INTERFACE_FLAGS_FROM_NWI_STATE mdns_interface_flag_clat46
710
711 static mdns_interface_flags_t
712 _mdns_get_interface_flags_from_nwi_state(const char *ifname, mdns_interface_flags_t current_flags)
713 {
714 __block nwi_ifstate_flags ifstate_flags = 0;
715 dispatch_sync(_mdns_nwi_state_mutex_queue(),
716 ^{
717 if (g_nwi_state) {
718 const nwi_ifstate_t ifstate = nwi_state_get_ifstate(g_nwi_state, ifname);
719 if (ifstate) {
720 ifstate_flags = nwi_ifstate_get_flags(ifstate);
721 }
722 }
723 });
724 mdns_interface_flags_t flags = current_flags & ~MDNS_INTERFACE_FLAGS_FROM_NWI_STATE;
725 if (ifstate_flags & NWI_IFSTATE_FLAGS_HAS_CLAT46) {
726 flags |= mdns_interface_flag_clat46;
727 }
728 return flags;
729 }
730
731 //======================================================================================================================
732
733 static void
734 _mdns_nwi_state_update(void);
735
736 static void
737 _mdns_start_nwi_state_monitoring(void)
738 {
739 static int s_nwi_notify_token = NOTIFY_TOKEN_INVALID;
740 if (s_nwi_notify_token == NOTIFY_TOKEN_INVALID) {
741 const uint32_t status = notify_register_dispatch(nwi_state_get_notify_key(), &s_nwi_notify_token,
742 _mdns_internal_queue(),
743 ^(__unused int token)
744 {
745 _mdns_nwi_state_update();
746 });
747 if (s_nwi_notify_token == NOTIFY_TOKEN_INVALID) {
748 os_log_error(_mdns_nwi_log(), "Failed to register for NWI state notifications (status %u)", status);
749 } else {
750 _mdns_nwi_state_update();
751 }
752 }
753 }
754
755 static void
756 _mdns_nwi_state_update(void)
757 {
758 nwi_state_t new_state = nwi_state_copy();
759 if (!new_state) {
760 os_log_error(_mdns_nwi_log(), "Failed to copy NWI state");
761 }
762 __block nwi_state_t old_state;
763 dispatch_sync(_mdns_nwi_state_mutex_queue(),
764 ^{
765 old_state = g_nwi_state;
766 g_nwi_state = new_state;
767 });
768 nwi_state_release_null_safe(old_state);
769 for (mdns_interface_monitor_t m = g_monitor_list; m; m = m->next) {
770 const mdns_interface_flags_t new_flags = _mdns_get_interface_flags_from_nwi_state(m->ifname, m->pending_flags);
771 if (new_flags != m->pending_flags) {
772 m->pending_flags = new_flags;
773 if (m->update_source) {
774 // Note: mdns_interface_flag_reserved is used to ensure that the data is non-zero. According to the
775 // dispatch_source_create(3) man page, if the data value is zero, the source handler won't be invoked.
776 dispatch_source_merge_data(m->update_source, m->pending_flags | mdns_interface_flag_reserved);
777 }
778 }
779 }
780 }
781
782 //======================================================================================================================
783 // MARK: - General Helpers
784
785 static int
786 _mdns_snprintf_add(char **ptr, const char *lim, const char *fmt, ...)
787 {
788 char * const dst = *ptr;
789 const size_t len = (size_t)(lim - dst);
790 int n;
791
792 require_action_quiet(len > 0, exit, n = 0);
793
794 va_list args;
795 va_start(args, fmt);
796 n = vsnprintf(dst, len, fmt, args);
797 va_end(args);
798 require_quiet(n >= 0, exit);
799
800 if (((size_t)n) > len) {
801 n = (int)len;
802 }
803 *ptr = dst + n;
804
805 exit:
806 return n;
807 }