-
Notifications
You must be signed in to change notification settings - Fork 79
Expand file tree
/
Copy pathmDNSMacOSX.c
More file actions
7203 lines (6365 loc) · 301 KB
/
Copy pathmDNSMacOSX.c
File metadata and controls
7203 lines (6365 loc) · 301 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*-
*
* Copyright (c) 2002-2023 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ***************************************************************************
// mDNSMacOSX.c:
// Supporting routines to run mDNS on a CFRunLoop platform
// ***************************************************************************
// For debugging, set LIST_ALL_INTERFACES to 1 to display all found interfaces,
// including ones that mDNSResponder chooses not to use.
#define LIST_ALL_INTERFACES 0
#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above
#include "DNSCommon.h"
#include "uDNS.h"
#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
#include "dns_sd.h" // For mDNSInterface_LocalOnly etc.
#include "dns_sd_internal.h"
#include "PlatformCommon.h"
#include "uds_daemon.h"
#if MDNSRESPONDER_SUPPORTS(APPLE, ANALYTICS)
#include "dnssd_analytics.h"
#endif
#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT)
#include "mdns_trust.h"
#include <os/feature_private.h>
#endif
#if defined(__x86_64__) && __x86_64__
#include <smmintrin.h>
#endif
#include <mdns/power.h>
#include <mdns/tcpinfo.h>
#include <stdio.h>
#include <stdarg.h> // For va_list support
#include <stdlib.h> // For arc4random
#include <net/if.h>
#include <net/if_types.h> // For IFT_ETHER
#include <net/if_dl.h>
#include <net/bpf.h> // For BIOCSETIF etc.
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/event.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <time.h> // platform support for UTC time
#include <arpa/inet.h> // for inet_aton
#include <pthread.h>
#include <netdb.h> // for getaddrinfo
#include <sys/sockio.h> // for SIOCGIFEFLAGS
#include <notify.h>
#include <netinet/in.h> // For IP_RECVTTL
#ifndef IP_RECVTTL
#define IP_RECVTTL 24 // bool; receive reception TTL w/dgram
#endif
#include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
#include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
#include <netinet6/in6_var.h> // For IN6_IFF_TENTATIVE etc.
#include <netinet/tcp.h>
#include "DebugServices.h"
#include "dnsinfo.h"
#include <ifaddrs.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/ps/IOPowerSources.h>
#include <IOKit/ps/IOPowerSourcesPrivate.h>
#include <IOKit/ps/IOPSKeys.h>
#include <mach/mach_error.h>
#include <mach/mach_port.h>
#include <mach/mach_time.h>
#include "helper.h"
#include <SystemConfiguration/SCPrivate.h>
#include <Security/oidsalg.h> // To include the deprecated symbol `CSSMOID_APPLE_X509_BASIC`.
#include "system_utilities.h"
// Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic.
#include <Kernel/IOKit/apple80211/apple80211_var.h>
#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
#include "QuerierSupport.h"
#endif
#ifdef UNIT_TEST
#include "unittest.h"
#endif
#include "mdns_strict.h"
#define mDNS_IOREG_KEY "mDNS_KEY"
#define mDNS_IOREG_VALUE "2009-07-30"
#if !TARGET_OS_WATCH
#define mDNS_IOREG_KA_KEY "mDNS_Keepalive"
#endif
#define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS'
#define DARK_WAKE_TIME 16 // Time we hold an idle sleep assertion for maintenance after a wake notification
// cache the InterfaceID of the AWDL interface
mDNSInterfaceID AWDLInterfaceID;
mDNSInterfaceID WiFiAwareInterfaceID;
// ***************************************************************************
// Globals
// MARK: - Globals
// By default we don't offer sleep proxy service
// If OfferSleepProxyService is set non-zero (typically via command-line switch),
// then we'll offer sleep proxy service on desktop Macs that are set to never sleep.
// We currently do not offer sleep proxy service on laptops, or on machines that are set to go to sleep.
mDNSexport int OfferSleepProxyService = 0;
mDNSexport int DisableSleepProxyClient = 0;
mDNSexport int UseInternalSleepProxy = 1; // Set to non-zero to use internal (in-NIC) Sleep Proxy
mDNSexport int OSXVers, iOSVers;
mDNSexport int KQueueFD;
#ifndef NO_SECURITYFRAMEWORK
static CFArrayRef ServerCerts;
OSStatus SSLSetAllowAnonymousCiphers(SSLContextRef context, Boolean enable);
#endif /* NO_SECURITYFRAMEWORK */
static CFStringRef NetworkChangedKey_IPv4;
static CFStringRef NetworkChangedKey_IPv6;
static CFStringRef NetworkChangedKey_Hostnames;
static CFStringRef NetworkChangedKey_Computername;
static CFStringRef NetworkChangedKey_DNS;
static CFStringRef NetworkChangedKey_StateInterfacePrefix;
static CFStringRef NetworkChangedKey_DynamicDNS = CFSTR("Setup:/Network/DynamicDNS");
static CFStringRef NetworkChangedKey_PowerSettings = CFSTR("State:/IOKit/PowerManagement/CurrentSettings");
static char HINFO_HWstring_buffer[32];
static char *HINFO_HWstring = "Device";
static int HINFO_HWstring_prefixlen = 6;
mDNSexport int WatchDogReportingThreshold = 250;
dispatch_queue_t SSLqueue;
#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
domainname ActiveDirectoryPrimaryDomain;
static int ActiveDirectoryPrimaryDomainLabelCount;
static mDNSAddr ActiveDirectoryPrimaryDomainServer;
#endif
// Don't send triggers too often. We arbitrarily limit it to three minutes.
#define DNS_TRIGGER_INTERVAL (180 * mDNSPlatformOneSecond)
const char dnsprefix[] = "dns:";
// String Array used to write list of private domains to Dynamic Store
static CFArrayRef privateDnsArray = NULL;
// ***************************************************************************
// Functions
// MARK: - Utility Functions
// We only attempt to send and receive multicast packets on interfaces that are
// (a) flagged as multicast-capable
// (b) *not* flagged as point-to-point (e.g. modem)
// Typically point-to-point interfaces are modems (including mobile-phone pseudo-modems), and we don't want
// to run up the user's bill sending multicast traffic over a link where there's only a single device at the
// other end, and that device (e.g. a modem bank) is probably not answering Multicast DNS queries anyway.
#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND)
#define MulticastInterface(i) ((i)->m->BonjourEnabled && \
((i)->ifa_flags & IFF_MULTICAST) && \
!((i)->ifa_flags & IFF_POINTOPOINT))
#else
#define MulticastInterface(i) (((i)->ifa_flags & IFF_MULTICAST) && \
!((i)->ifa_flags & IFF_POINTOPOINT))
#endif
#define SPSInterface(i) ((i)->ifinfo.McastTxRx && !((i)->ifa_flags & IFF_LOOPBACK) && !(i)->D2DInterface)
mDNSlocal void SetNetworkChanged(mDNSs32 delay);
mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg) // Both strings are UTF-8 text
{
// Unless ForceAlerts is defined, we only show these bug report alerts on machines that have a 17.x.x.x address
#if !ForceAlerts
{
// Determine if we're at Apple (17.*.*.*)
NetworkInterfaceInfoOSX *i;
for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && i->ifinfo.ip.ip.v4.b[0] == 17)
break;
if (!i)
return; // If not at Apple, don't show the alert
}
#endif
LogMsg("NotifyOfElusiveBug: %s", title);
LogMsg("NotifyOfElusiveBug: %s", msg);
// If we display our alert early in the boot process, then it vanishes once the desktop appears.
// To avoid this, we don't try to display alerts in the first three minutes after boot.
if ((mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180))
{
LogMsg("Suppressing notification early in boot: %d", mDNSPlatformRawTime());
return;
}
#ifndef NO_CFUSERNOTIFICATION
static int notifyCount = 0; // To guard against excessive display of warning notifications
if (notifyCount < 5)
{
notifyCount++;
mDNSNotify(title, msg);
}
#endif /* NO_CFUSERNOTIFICATION */
}
// Write a syslog message and display an alert, then if ForceAlerts is set, generate a stack trace
#if MDNS_MALLOC_DEBUGGING >= 1
mDNSexport void LogMemCorruption(const char *format, ...)
{
char buffer[512];
va_list ptr;
va_start(ptr,format);
mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
va_end(ptr);
LogMsg("!!!! %s !!!!", buffer);
NotifyOfElusiveBug("Memory Corruption", buffer);
#if ForceAlerts
*(volatile long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want
#endif
}
#endif
// Like LogMemCorruption above, but only display the alert if ForceAlerts is set and we're going to generate a stack trace
// Returns true if it is an AppleTV based hardware running iOS, false otherwise
mDNSlocal mDNSBool IsAppleTV(void)
{
#if TARGET_OS_TV
return mDNStrue;
#else
return mDNSfalse;
#endif
}
mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh)
{
static struct ifaddrs *ifa = NULL;
if (refresh && ifa)
{
freeifaddrs(ifa);
ifa = NULL;
}
if (ifa == NULL)
getifaddrs(&ifa);
return ifa;
}
mDNSlocal void DynamicStoreWrite(enum mDNSDynamicStoreSetConfigKey key, const char* subkey, uintptr_t value, signed long valueCnt)
{
CFStringRef sckey = NULL;
Boolean release_sckey = FALSE;
CFDataRef bytes = NULL;
CFPropertyListRef plist = NULL;
switch (key)
{
case kmDNSMulticastConfig:
sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS);
break;
case kmDNSDynamicConfig:
sckey = CFSTR("State:/Network/DynamicDNS");
break;
case kmDNSPrivateConfig:
sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS);
break;
case kmDNSBackToMyMacConfig:
sckey = CFSTR("State:/Network/BackToMyMac");
break;
case kmDNSSleepProxyServersState:
{
CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringAppend(tmp, CFSTR("State:/Network/Interface/"));
CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8);
CFStringAppend(tmp, CFSTR("/SleepProxyServers"));
sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp);
release_sckey = TRUE;
MDNS_DISPOSE_CF_OBJECT(tmp);
break;
}
case kmDNSDebugState:
sckey = CFSTR("State:/Network/mDNSResponder/DebugState");
break;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
default:
#pragma clang diagnostic pop
LogMsg("unrecognized key %d", key);
goto fin;
}
if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value,
valueCnt, kCFAllocatorNull)))
{
LogMsg("CFDataCreateWithBytesNoCopy of value failed");
goto fin;
}
if (NULL == (plist = CFPropertyListCreateWithData(NULL, bytes, kCFPropertyListImmutable, NULL, NULL)))
{
LogMsg("CFPropertyListCreateWithData of bytes failed");
goto fin;
}
MDNS_DISPOSE_CF_OBJECT(bytes);
SCDynamicStoreSetValue(NULL, sckey, plist);
fin:
MDNS_DISPOSE_CF_OBJECT(bytes);
MDNS_DISPOSE_CF_OBJECT(plist);
if (release_sckey)
MDNS_DISPOSE_CF_OBJECT(sckey);
}
mDNSexport void mDNSDynamicStoreSetConfig(enum mDNSDynamicStoreSetConfigKey key, const char *subkey, CFPropertyListRef value)
{
CFPropertyListRef valueCopy;
char *subkeyCopy = NULL;
if (!value)
return;
// We need to copy the key and value before we dispatch off the block below as the
// caller will free the memory once we return from this function.
valueCopy = CFPropertyListCreateDeepCopy(NULL, value, kCFPropertyListImmutable);
if (!valueCopy)
{
LogMsg("mDNSDynamicStoreSetConfig: ERROR valueCopy NULL");
return;
}
if (subkey)
{
const mDNSu32 len = (mDNSu32)strlen(subkey);
subkeyCopy = mDNSPlatformMemAllocate(len + 1);
if (!subkeyCopy)
{
LogMsg("mDNSDynamicStoreSetConfig: ERROR subkeyCopy NULL");
MDNS_DISPOSE_CF_OBJECT(valueCopy);
return;
}
mDNSPlatformMemCopy(subkeyCopy, subkey, len);
subkeyCopy[len] = 0;
}
dispatch_async(dispatch_get_main_queue(), ^{
CFWriteStreamRef stream = NULL;
CFDataRef bytes = NULL;
CFIndex ret;
KQueueLock();
if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL)))
{
LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCreateWithAllocatedBuffers failed (Object creation failed)");
goto END;
}
CFWriteStreamOpen(stream);
ret = CFPropertyListWrite(valueCopy, stream, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
if (ret == 0)
{
LogMsg("mDNSDynamicStoreSetConfig : CFPropertyListWriteToStream failed (Could not write property list to stream)");
goto END;
}
if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten)))
{
LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCopyProperty failed (Object creation failed) ");
goto END;
}
CFWriteStreamClose(stream);
MDNS_DISPOSE_CF_OBJECT(stream);
const UInt8 * bytes_ptr = CFDataGetBytePtr(bytes);
DynamicStoreWrite(key, subkeyCopy ? subkeyCopy : "", (uintptr_t)bytes_ptr, CFDataGetLength(bytes));
END:;
CFPropertyListRef tmp = valueCopy;
MDNS_DISPOSE_CF_OBJECT(tmp);
if (NULL != stream)
{
CFWriteStreamClose(stream);
MDNS_DISPOSE_CF_OBJECT(stream);
}
MDNS_DISPOSE_CF_OBJECT(bytes);
if (subkeyCopy)
mDNSPlatformMemFree(subkeyCopy);
KQueueUnlock("mDNSDynamicStoreSetConfig");
});
}
// To match *either* a v4 or v6 instance of this interface name, pass AF_UNSPEC for type
mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(const char *ifname, int type)
{
NetworkInterfaceInfoOSX *i;
for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
if (i->Exists && !strcmp(i->ifinfo.ifname, ifname) &&
((type == AF_UNSPEC ) ||
(type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) ||
(type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6))) return(i);
return(NULL);
}
mDNSlocal int myIfIndexToName(u_short ifindex, char *name)
{
struct ifaddrs *ifa;
for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next)
if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_LINK)
if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == ifindex)
{ mdns_strlcpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; }
return -1;
}
mDNSexport NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(mDNSInterfaceID ifindex)
{
mDNS *const m = &mDNSStorage;
mDNSu32 scope_id = (mDNSu32)(uintptr_t)ifindex;
NetworkInterfaceInfoOSX *i;
// Don't get tricked by inactive interfaces
for (i = m->p->InterfaceList; i; i = i->next)
if (i->Registered && i->scope_id == scope_id) return(i);
return mDNSNULL;
}
#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
mDNSexport mdns_interface_monitor_t GetInterfaceMonitorForIndex(uint32_t ifIndex)
{
mDNS *const m = &mDNSStorage;
// We assume that interface should always be real interface, and should never be 0.
if (ifIndex == 0) return NULL;
if (!m->p->InterfaceMonitors)
{
m->p->InterfaceMonitors = CFArrayCreateMutable(kCFAllocatorDefault, 0, &mdns_cfarray_callbacks);
if (!m->p->InterfaceMonitors)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Failed to create InterfaceMonitors array");
return NULL;
}
}
// Search for interface monitor given the interface index.
mdns_interface_monitor_t monitor;
for (CFIndex i = 0, n = CFArrayGetCount(m->p->InterfaceMonitors); i < n; i++)
{
monitor = (mdns_interface_monitor_t) CFArrayGetValueAtIndex(m->p->InterfaceMonitors, i);
if (mdns_interface_monitor_get_interface_index(monitor) == ifIndex) return monitor;
}
// If we come here, it means the interface is a new interface that needs to be monitored.
monitor = mdns_interface_monitor_create(ifIndex);
if (!monitor)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Failed to create an interface monitor for index %u", ifIndex);
return NULL;
}
CFArrayAppendValue(m->p->InterfaceMonitors, monitor);
// Put the monitor into serial queue.
mdns_interface_monitor_set_queue(monitor, dispatch_get_main_queue());
// When the interface configuration is changed, this block will be called.
mdns_interface_monitor_set_update_handler(monitor,
^(mdns_interface_flags_t changeFlags)
{
const mdns_interface_flags_t relevantFlags =
mdns_interface_flag_expensive |
mdns_interface_flag_constrained |
mdns_interface_flag_clat46;
if ((changeFlags & relevantFlags) == 0) return;
KQueueLock();
const CFRange range = CFRangeMake(0, CFArrayGetCount(m->p->InterfaceMonitors));
if (CFArrayContainsValue(m->p->InterfaceMonitors, range, monitor))
{
m->p->if_interface_changed = mDNStrue;
#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Monitored interface changed: %@", monitor);
#endif
// Let mDNSResponder update its network configuration.
mDNS_Lock(m);
SetNetworkChanged((mDNSPlatformOneSecond + 39) / 40); // 25 ms delay
mDNS_Unlock(m);
}
KQueueUnlock("interface monitor update handler");
});
mdns_interface_monitor_set_event_handler(monitor,
^(mdns_event_t event, OSStatus error)
{
switch (event)
{
case mdns_event_invalidated:
mdns_release(monitor);
break;
case mdns_event_error:
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Interface monitor for index %u error: %ld",
mdns_interface_monitor_get_interface_index(monitor), (long) error);
KQueueLock();
if (m->p->InterfaceMonitors)
{
const CFRange range = CFRangeMake(0, CFArrayGetCount(m->p->InterfaceMonitors));
const CFIndex i = CFArrayGetFirstIndexOfValue(m->p->InterfaceMonitors, range, monitor);
if (i >= 0) CFArrayRemoveValueAtIndex(m->p->InterfaceMonitors, i);
}
KQueueUnlock("interface monitor event handler");
mdns_interface_monitor_invalidate(monitor);
break;
default:
break;
}
});
mdns_interface_monitor_activate(monitor);
return monitor;
}
#endif
mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex)
{
(void) m;
if (ifindex == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly);
if (ifindex == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P);
if (ifindex == kDNSServiceInterfaceIndexBLE ) return(mDNSInterface_BLE);
if (ifindex == kDNSServiceInterfaceIndexAny ) return(mDNSNULL);
NetworkInterfaceInfoOSX* ifi = IfindexToInterfaceInfoOSX((mDNSInterfaceID)(uintptr_t)ifindex);
if (!ifi)
{
// Not found. Make sure our interface list is up to date, then try again.
LogInfo("mDNSPlatformInterfaceIDfromInterfaceIndex: InterfaceID for interface index %d not found; Updating interface list", ifindex);
mDNSMacOSXNetworkChanged();
ifi = IfindexToInterfaceInfoOSX((mDNSInterfaceID)(uintptr_t)ifindex);
}
if (!ifi) return(mDNSNULL);
return(ifi->ifinfo.InterfaceID);
}
mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange)
{
NetworkInterfaceInfoOSX *i;
if (id == mDNSInterface_Any ) return(0);
if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly);
if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P);
if (id == mDNSInterface_BLE ) return(kDNSServiceInterfaceIndexBLE);
mDNSu32 scope_id = (mDNSu32)(uintptr_t)id;
// Don't use i->Registered here, because we DO want to find inactive interfaces, which have no Registered set
for (i = m->p->InterfaceList; i; i = i->next)
if (i->scope_id == scope_id) return(i->scope_id);
// If we are supposed to suppress network change, return "id" back
if (suppressNetworkChange) return scope_id;
// Not found. Make sure our interface list is up to date, then try again.
LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id);
mDNSMacOSXNetworkChanged();
for (i = m->p->InterfaceList; i; i = i->next)
if (i->scope_id == scope_id) return(i->scope_id);
return(0);
}
mDNSlocal mDNSBool GetInterfaceSupportsWakeOnLANPacket(mDNSInterfaceID id)
{
NetworkInterfaceInfoOSX *info = IfindexToInterfaceInfoOSX(id);
if (info == NULL)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "GetInterfaceSupportsWakeOnLANPacket: Invalid interface id %p", id);
return mDNSfalse;
}
else
{
return (info->ift_family == IFRTYPE_FAMILY_ETHERNET) ? mDNStrue : mDNSfalse;
}
}
mDNSlocal uint32_t GetIFTFamily(const char * _Nonnull if_name, uint32_t *out_sub_family)
{
uint32_t ift_family = IFRTYPE_FAMILY_ANY;
if (out_sub_family)
{
*out_sub_family = IFRTYPE_SUBFAMILY_ANY;
}
int s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == -1)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "GetIFTFamily: socket() failed: " PUB_S, strerror(errno));
return ift_family;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
mdns_strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFTYPE, (caddr_t)&ifr) == -1)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "GetIFTFamily: SIOCGIFTYPE failed: " PUB_S, strerror(errno));
}
else
{
ift_family = ifr.ifr_type.ift_family;
if (out_sub_family)
{
*out_sub_family = ifr.ifr_type.ift_subfamily;
}
}
close(s);
return ift_family;
}
mDNSlocal uint32_t GetIFRFunctionalType(const char * const _Nonnull if_name)
{
uint32_t type = IFRTYPE_FUNCTIONAL_UNKNOWN;
const int info_socket = socket(AF_INET6, SOCK_DGRAM, 0);
mdns_require_quiet(info_socket != -1, exit);
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
mdns_strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
const int ioctl_ret = ioctl(info_socket, SIOCGIFFUNCTIONALTYPE, (caddr_t)&ifr);
mdns_require_quiet(ioctl_ret != -1, exit);
type = ifr.ifr_functional_type;
exit:
if (info_socket != -1)
{
close(info_socket);
}
return type;
}
// MARK: - UDP & TCP send & receive
// Set traffic class for socket
mDNSlocal void setTrafficClass(int socketfd, mDNSBool useBackgroundTrafficClass)
{
int traffic_class;
if (useBackgroundTrafficClass)
traffic_class = SO_TC_BK_SYS;
else
traffic_class = SO_TC_CTL;
(void) setsockopt(socketfd, SOL_SOCKET, SO_TRAFFIC_CLASS, (void *)&traffic_class, sizeof(traffic_class));
}
#ifdef UNIT_TEST
UNITTEST_SETSOCKOPT
#else
mDNSlocal int mDNSPlatformGetSocktFd(void *sockCxt, mDNSTransport_Type transType, mDNSAddr_Type addrType)
{
if (transType == mDNSTransport_UDP)
{
UDPSocket* sock = (UDPSocket*) sockCxt;
return (addrType == mDNSAddrType_IPv4) ? sock->ss.sktv4 : sock->ss.sktv6;
}
else if (transType == mDNSTransport_TCP)
{
TCPSocket* sock = (TCPSocket*) sockCxt;
return sock->fd;
}
else
{
LogInfo("mDNSPlatformGetSocktFd: invalid transport %d", transType);
return kInvalidSocketRef;
}
}
mDNSexport void mDNSPlatformSetSocktOpt(void *sockCxt, mDNSTransport_Type transType, mDNSAddr_Type addrType, const DNSQuestion *q)
{
int sockfd;
char unenc_name[MAX_ESCAPED_DOMAIN_NAME];
// verify passed-in arguments exist and that sockfd is valid
if (q == mDNSNULL || sockCxt == mDNSNULL || (sockfd = mDNSPlatformGetSocktFd(sockCxt, transType, addrType)) < 0)
return;
if (q->pid)
{
if (setsockopt(sockfd, SOL_SOCKET, SO_DELEGATED, &q->pid, sizeof(q->pid)) == -1)
LogMsg("mDNSPlatformSetSocktOpt: Delegate PID failed %s for PID %d", strerror(errno), q->pid);
}
else
{
if (setsockopt(sockfd, SOL_SOCKET, SO_DELEGATED_UUID, &q->uuid, sizeof(q->uuid)) == -1)
LogMsg("mDNSPlatformSetSocktOpt: Delegate UUID failed %s", strerror(errno));
}
// set the domain on the socket
ConvertDomainNameToCString(&q->qname, unenc_name);
if (!(ne_session_set_socket_attributes(sockfd, unenc_name, NULL)))
LogInfo("mDNSPlatformSetSocktOpt: ne_session_set_socket_attributes()-> setting domain failed for %s", unenc_name);
int nowake = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_NOWAKEFROMSLEEP, &nowake, sizeof(nowake)) == -1)
LogInfo("mDNSPlatformSetSocktOpt: SO_NOWAKEFROMSLEEP failed %s", strerror(errno));
}
#endif // UNIT_TEST
// Note: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket"
// Note: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface"
// OR send via our primary v4 unicast socket
// UPDATE: The UDPSocket *src parameter now allows the caller to specify the source socket
mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end,
mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst,
mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass)
{
NetworkInterfaceInfoOSX *info = mDNSNULL;
struct sockaddr_storage to;
int s = -1;
mStatus result = mStatus_NoError;
ssize_t sentlen;
int sendto_errno;
const DNSMessage *const dns_msg = msg;
if (InterfaceID)
{
info = IfindexToInterfaceInfoOSX(InterfaceID);
if (info == NULL)
{
// We may not have registered interfaces with the "core" as we may not have
// seen any interface notifications yet. This typically happens during wakeup
// where we might try to send DNS requests (non-SuppressUnusable questions internal
// to mDNSResponder) before we receive network notifications.
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_DEFAULT, "mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID);
return mStatus_BadParamErr;
}
}
char *ifa_name = InterfaceID ? info->ifinfo.ifname : "unicast";
if (dst->type == mDNSAddrType_IPv4)
{
struct sockaddr_in *sin_to = (struct sockaddr_in*)&to;
sin_to->sin_len = sizeof(*sin_to);
sin_to->sin_family = AF_INET;
sin_to->sin_port = dstPort.NotAnInteger;
sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger;
s = (src ? src->ss : m->p->permanentsockets).sktv4;
if (!mDNSAddrIsDNSMulticast(dst))
{
#ifdef IP_BOUND_IF
const mDNSu32 ifindex = info ? info->scope_id : IFSCOPE_NONE;
setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex));
#else
static int displayed = 0;
if (displayed < 1000)
{
displayed++;
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_DEFAULT, "[Q%u] IP_BOUND_IF socket option not defined -- cannot specify interface for unicast packets",
mDNSVal16(dns_msg->h.id));
}
#endif
}
else if (info)
{
int err;
#ifdef IP_MULTICAST_IFINDEX
err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IFINDEX, &info->scope_id, sizeof(info->scope_id));
// We get an error when we compile on a machine that supports this option and run the binary on
// a different machine that does not support it
if (err < 0)
{
if (errno != ENOPROTOOPT)
{
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_ERROR, "[Q%u] mDNSPlatformSendUDP: setsockopt: IP_MUTLTICAST_IFINDEX returned %d (" PUB_S ")",
mDNSVal16(dns_msg->h.id), errno, strerror(errno));
}
err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr));
if (err < 0 && !m->NetworkChanged)
{
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_ERROR, "[Q%u] setsockopt - IP_MULTICAST_IF error " PRI_IPv4_ADDR " %d errno %d (" PUB_S ")",
mDNSVal16(dns_msg->h.id), &info->ifa_v4addr, err, errno, strerror(errno));
}
}
#else
err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr));
if (err < 0 && !m->NetworkChanged)
{
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_ERROR, "[Q%u] setsockopt - IP_MULTICAST_IF error " PRI_IPv4_ADDR " %d errno %d (" PUB_S ")",
mDNSVal16(dns_msg->h.id), &info->ifa_v4addr, err, errno, strerror(errno));
}
#endif
}
}
else if (dst->type == mDNSAddrType_IPv6)
{
struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to;
sin6_to->sin6_len = sizeof(*sin6_to);
sin6_to->sin6_family = AF_INET6;
sin6_to->sin6_port = dstPort.NotAnInteger;
sin6_to->sin6_flowinfo = 0;
memcpy(sin6_to->sin6_addr.s6_addr, dst->ip.v6.b, sizeof(sin6_to->sin6_addr.s6_addr));
sin6_to->sin6_scope_id = info ? info->scope_id : 0;
s = (src ? src->ss : m->p->permanentsockets).sktv6;
if (info && mDNSAddrIsDNSMulticast(dst)) // Specify outgoing interface
{
const int err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &info->scope_id, sizeof(info->scope_id));
if (err < 0)
{
const int setsockopt_errno = errno;
char name[IFNAMSIZ];
if (if_indextoname(info->scope_id, name) != NULL)
{
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_ERROR, "[Q%u] setsockopt - IPV6_MULTICAST_IF error %d errno %d (" PUB_S ")",
mDNSVal16(dns_msg->h.id), err, setsockopt_errno, strerror(setsockopt_errno));
}
else
{
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_ERROR, "[Q%u] setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface",
mDNSVal16(dns_msg->h.id), info->scope_id);
}
}
}
#ifdef IPV6_BOUND_IF
if (info) // Specify outgoing interface for non-multicast destination
{
if (!mDNSAddrIsDNSMulticast(dst))
{
if (info->scope_id == 0)
{
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_DEFAULT, "[Q%u] IPV6_BOUND_IF socket option not set -- info %p (" PUB_S ") scope_id is zero",
mDNSVal16(dns_msg->h.id), info, ifa_name);
}
else
{
setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF, &info->scope_id, sizeof(info->scope_id));
}
}
}
#endif
}
else
{
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_FAULT, "[Q%u] mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!", mDNSVal16(dns_msg->h.id));
return mStatus_BadParamErr;
}
if (s >= 0)
{
verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %p %5s/%ld to %#a:%d skt %d",
InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s);
}
else
{
verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %p %5s/%ld (socket of this type not available)",
InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort));
}
// Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet
// If we don't have the corresponding type of socket available, then return mStatus_Invalid
if (s < 0) return(mStatus_Invalid);
// switch to background traffic class for this message if requested
if (useBackgroundTrafficClass)
setTrafficClass(s, useBackgroundTrafficClass);
sentlen = sendto(s, msg, end - (const UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len);
sendto_errno = (sentlen < 0) ? errno : 0;
// set traffic class back to default value
if (useBackgroundTrafficClass)
setTrafficClass(s, mDNSfalse);
if (sentlen < 0)
{
static int MessageCount = 0;
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_ERROR, "[Q%u] mDNSPlatformSendUDP -> sendto(%d) failed to send packet on InterfaceID %p "
PUB_S "/%d to " PRI_IP_ADDR ":%d skt %d error %ld errno %d (" PUB_S ") %u",
mDNSVal16(dns_msg->h.id), s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, (long)sentlen,
sendto_errno, strerror(sendto_errno), (mDNSu32)(m->timenow));
if (!mDNSAddressIsAllDNSLinkGroup(dst))
{
if ((sendto_errno == EHOSTUNREACH) || (sendto_errno == ENETUNREACH)) return(mStatus_HostUnreachErr);
if ((sendto_errno == EHOSTDOWN) || (sendto_errno == ENETDOWN)) return(mStatus_TransientErr);
}
// Don't report EHOSTUNREACH in the first three minutes after boot
// This is because mDNSResponder intentionally starts up early in the boot process (See <rdar://problem/3409090>)
// but this means that sometimes it starts before configd has finished setting up the multicast routing entries.
if (sendto_errno == EHOSTUNREACH && (mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(mStatus_TransientErr);
// Don't report EADDRNOTAVAIL ("Can't assign requested address") if we're in the middle of a network configuration change
if (sendto_errno == EADDRNOTAVAIL && m->NetworkChanged) return(mStatus_TransientErr);
if (sendto_errno == EHOSTUNREACH || sendto_errno == EADDRNOTAVAIL || sendto_errno == ENETDOWN)
{
LogRedact(MDNS_LOG_CATEGORY_NAT, MDNS_LOG_ERROR, "[Q%u] mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p "
PUB_S "/%d to " PRI_IP_ADDR ":%d skt %d error %ld errno %d (" PUB_S ") %u",
mDNSVal16(dns_msg->h.id), s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s,
(long)sentlen, sendto_errno, strerror(sendto_errno), (mDNSu32)(m->timenow));
}
else
{
MessageCount++;
if (MessageCount < 50) // Cap and ensure NO spamming of LogMsgs
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
"[Q%u] mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p " PUB_S "/%d to " PRI_IP_ADDR ":%d skt %d error %ld errno %d (" PUB_S ") %u MessageCount is %d",
mDNSVal16(dns_msg->h.id), s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, (long)sentlen, sendto_errno, strerror(sendto_errno), (mDNSu32)(m->timenow), MessageCount);
}
else // If logging is enabled, remove the cap and log aggressively
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
"[Q%u] mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p " PUB_S "/%d to " PRI_IP_ADDR ":%d skt %d error %ld errno %d (" PUB_S ") %u MessageCount is %d",
mDNSVal16(dns_msg->h.id), s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, (long)sentlen, sendto_errno, strerror(sendto_errno), (mDNSu32)(m->timenow), MessageCount);
}
}
result = mStatus_UnknownErr;
}
return(result);
}
mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
struct sockaddr *const from, socklen_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl)
{
static unsigned int numLogMessages = 0;
struct iovec databuffers = { (char *)buffer, max };
struct msghdr msg;
ssize_t n;
struct cmsghdr *cmPtr;
char ancillary[1024];
*ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be
// Set up the message
msg.msg_name = (caddr_t)from;
msg.msg_namelen = *fromlen;
msg.msg_iov = &databuffers;
msg.msg_iovlen = 1;
msg.msg_control = (caddr_t)&ancillary;
msg.msg_controllen = sizeof(ancillary);
msg.msg_flags = 0;
// Receive the data
n = recvmsg(s, &msg, 0);
if (n<0)
{
if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned error %d errno %d", s, n, errno);
return(-1);
}
if (msg.msg_controllen < (int)sizeof(struct cmsghdr))
{
if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu, errno %d",
s, n, msg.msg_controllen, sizeof(struct cmsghdr), errno);
return(-1);
}
// Note: MSG_TRUNC means the datagram was truncated, while MSG_CTRUNC means that the control data was truncated.
// The mDNS core is capable of handling truncated DNS messages, so MSG_TRUNC isn't checked.
if (msg.msg_flags & MSG_CTRUNC)
{
if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s);
return(-1);