1 /* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*-
3 * Copyright (c) 2002-2019 Apple Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 // ***************************************************************************
20 // Supporting routines to run mDNS on a CFRunLoop platform
21 // ***************************************************************************
23 // For debugging, set LIST_ALL_INTERFACES to 1 to display all found interfaces,
24 // including ones that mDNSResponder chooses not to use.
25 #define LIST_ALL_INTERFACES 0
27 #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above
28 #include "DNSCommon.h"
30 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
31 #include "dns_sd.h" // For mDNSInterface_LocalOnly etc.
32 #include "dns_sd_internal.h"
33 #include "PlatformCommon.h"
34 #include "uds_daemon.h"
35 #include "CryptoSupport.h"
38 #include <stdarg.h> // For va_list support
39 #include <stdlib.h> // For arc4random
41 #include <net/if_types.h> // For IFT_ETHER
42 #include <net/if_dl.h>
43 #include <net/bpf.h> // For BIOCSETIF etc.
45 #include <sys/param.h>
46 #include <sys/socket.h>
47 #include <sys/sysctl.h>
48 #include <sys/event.h>
50 #include <sys/ioctl.h>
51 #include <time.h> // platform support for UTC time
52 #include <arpa/inet.h> // for inet_aton
54 #include <netdb.h> // for getaddrinfo
55 #include <sys/sockio.h> // for SIOCGIFEFLAGS
57 #include <netinet/in.h> // For IP_RECVTTL
59 #define IP_RECVTTL 24 // bool; receive reception TTL w/dgram
62 #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
63 #include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
64 #include <netinet6/in6_var.h> // For IN6_IFF_TENTATIVE etc.
66 #include <netinet/tcp.h>
68 #include <DebugServices.h>
73 #include <IOKit/IOKitLib.h>
74 #include <IOKit/IOMessage.h>
76 #include <IOKit/ps/IOPowerSources.h>
77 #include <IOKit/ps/IOPowerSourcesPrivate.h>
78 #include <IOKit/ps/IOPSKeys.h>
80 #include <mach/mach_error.h>
81 #include <mach/mach_port.h>
82 #include <mach/mach_time.h>
84 #include "P2PPacketFilter.h"
86 #include <SystemConfiguration/SCPrivate.h>
89 #include <MobileWiFi/WiFiManagerClient.h> // For WiFiManagerClientRef etc, declarations.
91 #endif // TARGET_OS_IPHONE
93 #if MDNSRESPONDER_SUPPORTS(APPLE, IGNORE_HOSTS_FILE)
94 #include "system_utilities.h" // For os_variant_has_internal_diagnostics().
97 // Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic.
98 #include <Kernel/IOKit/apple80211/apple80211_var.h>
100 #if APPLE_OSX_mDNSResponder
101 #include <ne_session.h> // for ne_session_set_socket_attributes()
104 #if APPLE_OSX_mDNSResponder && TARGET_OS_OSX
105 #include <IOKit/platform/IOPlatformSupportPrivate.h>
109 #include "unittest.h"
112 #define kInterfaceSpecificOption "interface="
114 #define mDNS_IOREG_KEY "mDNS_KEY"
115 #define mDNS_IOREG_VALUE "2009-07-30"
116 #define mDNS_IOREG_KA_KEY "mDNS_Keepalive"
117 #define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS'
119 #define DARK_WAKE_TIME 16 // Time we hold an idle sleep assertion for maintenance after a wake notification
121 // cache the InterfaceID of the AWDL interface
122 mDNSInterfaceID AWDLInterfaceID
;
124 // ***************************************************************************
127 #if COMPILER_LIKES_PRAGMA_MARK
128 #pragma mark - Globals
131 // By default we don't offer sleep proxy service
132 // If OfferSleepProxyService is set non-zero (typically via command-line switch),
133 // then we'll offer sleep proxy service on desktop Macs that are set to never sleep.
134 // We currently do not offer sleep proxy service on laptops, or on machines that are set to go to sleep.
135 mDNSexport
int OfferSleepProxyService
= 0;
136 mDNSexport
int DisableSleepProxyClient
= 0;
137 mDNSexport
int UseInternalSleepProxy
= 1; // Set to non-zero to use internal (in-NIC) Sleep Proxy
139 mDNSexport
int OSXVers
, iOSVers
;
140 mDNSexport
int KQueueFD
;
142 #ifndef NO_SECURITYFRAMEWORK
143 static CFArrayRef ServerCerts
;
144 OSStatus
SSLSetAllowAnonymousCiphers(SSLContextRef context
, Boolean enable
);
145 #endif /* NO_SECURITYFRAMEWORK */
147 static CFStringRef NetworkChangedKey_IPv4
;
148 static CFStringRef NetworkChangedKey_IPv6
;
149 static CFStringRef NetworkChangedKey_Hostnames
;
150 static CFStringRef NetworkChangedKey_Computername
;
151 static CFStringRef NetworkChangedKey_DNS
;
152 static CFStringRef NetworkChangedKey_StateInterfacePrefix
;
153 static CFStringRef NetworkChangedKey_DynamicDNS
= CFSTR("Setup:/Network/DynamicDNS");
154 static CFStringRef NetworkChangedKey_PowerSettings
= CFSTR("State:/IOKit/PowerManagement/CurrentSettings");
156 static char HINFO_HWstring_buffer
[32];
157 static char *HINFO_HWstring
= "Device";
158 static int HINFO_HWstring_prefixlen
= 6;
160 mDNSexport
int WatchDogReportingThreshold
= 250;
162 dispatch_queue_t SSLqueue
;
164 #if APPLE_OSX_mDNSResponder
165 static mDNSu8 SPMetricPortability
= 99;
166 static mDNSu8 SPMetricMarginalPower
= 99;
167 static mDNSu8 SPMetricTotalPower
= 99;
168 static mDNSu8 SPMetricFeatures
= 1; /* The current version supports TCP Keep Alive Feature */
169 #endif // APPLE_OSX_mDNSResponder
171 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
172 domainname ActiveDirectoryPrimaryDomain
;
173 static int ActiveDirectoryPrimaryDomainLabelCount
;
174 static mDNSAddr ActiveDirectoryPrimaryDomainServer
;
177 // Don't send triggers too often. We arbitrarily limit it to three minutes.
178 #define DNS_TRIGGER_INTERVAL (180 * mDNSPlatformOneSecond)
180 const char dnsprefix
[] = "dns:";
182 // String Array used to write list of private domains to Dynamic Store
183 static CFArrayRef privateDnsArray
= NULL
;
185 // ***************************************************************************
188 #if COMPILER_LIKES_PRAGMA_MARK
190 #pragma mark - Utility Functions
193 // We only attempt to send and receive multicast packets on interfaces that are
194 // (a) flagged as multicast-capable
195 // (b) *not* flagged as point-to-point (e.g. modem)
196 // Typically point-to-point interfaces are modems (including mobile-phone pseudo-modems), and we don't want
197 // to run up the user's bill sending multicast traffic over a link where there's only a single device at the
198 // other end, and that device (e.g. a modem bank) is probably not answering Multicast DNS queries anyway.
200 #if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND)
201 #define MulticastInterface(i) ((i)->m->BonjourEnabled && ((i)->ifa_flags & IFF_MULTICAST) && !((i)->ifa_flags & IFF_POINTOPOINT))
203 #define MulticastInterface(i) (((i)->ifa_flags & IFF_MULTICAST) && !((i)->ifa_flags & IFF_POINTOPOINT))
205 #define SPSInterface(i) ((i)->ifinfo.McastTxRx && !((i)->ifa_flags & IFF_LOOPBACK) && !(i)->D2DInterface)
207 mDNSlocal
void SetNetworkChanged(mDNSs32 delay
);
209 mDNSexport
void NotifyOfElusiveBug(const char *title
, const char *msg
) // Both strings are UTF-8 text
211 // Unless ForceAlerts is defined, we only show these bug report alerts on machines that have a 17.x.x.x address
214 // Determine if we're at Apple (17.*.*.*)
215 NetworkInterfaceInfoOSX
*i
;
216 for (i
= mDNSStorage
.p
->InterfaceList
; i
; i
= i
->next
)
217 if (i
->ifinfo
.ip
.type
== mDNSAddrType_IPv4
&& i
->ifinfo
.ip
.ip
.v4
.b
[0] == 17)
220 return; // If not at Apple, don't show the alert
224 LogMsg("NotifyOfElusiveBug: %s", title
);
225 LogMsg("NotifyOfElusiveBug: %s", msg
);
227 // If we display our alert early in the boot process, then it vanishes once the desktop appears.
228 // To avoid this, we don't try to display alerts in the first three minutes after boot.
229 if ((mDNSu32
)(mDNSPlatformRawTime()) < (mDNSu32
)(mDNSPlatformOneSecond
* 180))
231 LogMsg("Suppressing notification early in boot: %d", mDNSPlatformRawTime());
235 #ifndef NO_CFUSERNOTIFICATION
236 static int notifyCount
= 0; // To guard against excessive display of warning notifications
240 mDNSNotify(title
, msg
);
242 #endif /* NO_CFUSERNOTIFICATION */
246 // Write a syslog message and display an alert, then if ForceAlerts is set, generate a stack trace
247 #if MDNS_MALLOC_DEBUGGING >= 1
248 mDNSexport
void LogMemCorruption(const char *format
, ...)
252 va_start(ptr
,format
);
253 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
255 LogMsg("!!!! %s !!!!", buffer
);
256 NotifyOfElusiveBug("Memory Corruption", buffer
);
258 *(volatile long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want
263 // Like LogMemCorruption above, but only display the alert if ForceAlerts is set and we're going to generate a stack trace
264 #if APPLE_OSX_mDNSResponder
265 mDNSexport
void LogFatalError(const char *format
, ...)
269 va_start(ptr
,format
);
270 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
272 LogMsg("!!!! %s !!!!", buffer
);
274 NotifyOfElusiveBug("Fatal Error. See /Library/Logs/DiagnosticReports", buffer
);
275 *(volatile long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want
280 // Returns true if it is an AppleTV based hardware running iOS, false otherwise
281 mDNSlocal mDNSBool
IsAppleTV(void)
290 mDNSlocal
struct ifaddrs
*myGetIfAddrs(int refresh
)
292 static struct ifaddrs
*ifa
= NULL
;
305 mDNSlocal
void DynamicStoreWrite(int key
, const char* subkey
, uintptr_t value
, signed long valueCnt
)
307 CFStringRef sckey
= NULL
;
308 Boolean release_sckey
= FALSE
;
309 CFDataRef bytes
= NULL
;
310 CFPropertyListRef plist
= NULL
;
312 switch ((enum mDNSDynamicStoreSetConfigKey
)key
)
314 case kmDNSMulticastConfig
:
315 sckey
= CFSTR("State:/Network/" kDNSServiceCompMulticastDNS
);
317 case kmDNSDynamicConfig
:
318 sckey
= CFSTR("State:/Network/DynamicDNS");
320 case kmDNSPrivateConfig
:
321 sckey
= CFSTR("State:/Network/" kDNSServiceCompPrivateDNS
);
323 case kmDNSBackToMyMacConfig
:
324 sckey
= CFSTR("State:/Network/BackToMyMac");
326 case kmDNSSleepProxyServersState
:
328 CFMutableStringRef tmp
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
329 CFStringAppend(tmp
, CFSTR("State:/Network/Interface/"));
330 CFStringAppendCString(tmp
, subkey
, kCFStringEncodingUTF8
);
331 CFStringAppend(tmp
, CFSTR("/SleepProxyServers"));
332 sckey
= CFStringCreateCopy(kCFAllocatorDefault
, tmp
);
333 release_sckey
= TRUE
;
337 case kmDNSDebugState
:
338 sckey
= CFSTR("State:/Network/mDNSResponder/DebugState");
341 LogMsg("unrecognized key %d", key
);
344 if (NULL
== (bytes
= CFDataCreateWithBytesNoCopy(NULL
, (void *)value
,
345 valueCnt
, kCFAllocatorNull
)))
347 LogMsg("CFDataCreateWithBytesNoCopy of value failed");
350 if (NULL
== (plist
= CFPropertyListCreateWithData(NULL
, bytes
, kCFPropertyListImmutable
, NULL
, NULL
)))
352 LogMsg("CFPropertyListCreateWithData of bytes failed");
357 SCDynamicStoreSetValue(NULL
, sckey
, plist
);
364 if (release_sckey
&& sckey
)
368 mDNSexport
void mDNSDynamicStoreSetConfig(int key
, const char *subkey
, CFPropertyListRef value
)
370 CFPropertyListRef valueCopy
;
371 char *subkeyCopy
= NULL
;
375 // We need to copy the key and value before we dispatch off the block below as the
376 // caller will free the memory once we return from this function.
377 valueCopy
= CFPropertyListCreateDeepCopy(NULL
, value
, kCFPropertyListImmutable
);
380 LogMsg("mDNSDynamicStoreSetConfig: ERROR valueCopy NULL");
385 int len
= strlen(subkey
);
386 subkeyCopy
= mDNSPlatformMemAllocate(len
+ 1);
389 LogMsg("mDNSDynamicStoreSetConfig: ERROR subkeyCopy NULL");
390 CFRelease(valueCopy
);
393 mDNSPlatformMemCopy(subkeyCopy
, subkey
, len
);
397 dispatch_async(dispatch_get_main_queue(), ^{
398 CFWriteStreamRef stream
= NULL
;
399 CFDataRef bytes
= NULL
;
403 if (NULL
== (stream
= CFWriteStreamCreateWithAllocatedBuffers(NULL
, NULL
)))
405 LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCreateWithAllocatedBuffers failed (Object creation failed)");
408 CFWriteStreamOpen(stream
);
409 ret
= CFPropertyListWrite(valueCopy
, stream
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
412 LogMsg("mDNSDynamicStoreSetConfig : CFPropertyListWriteToStream failed (Could not write property list to stream)");
415 if (NULL
== (bytes
= CFWriteStreamCopyProperty(stream
, kCFStreamPropertyDataWritten
)))
417 LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCopyProperty failed (Object creation failed) ");
420 CFWriteStreamClose(stream
);
423 DynamicStoreWrite(key
, subkeyCopy
? subkeyCopy
: "", (uintptr_t)CFDataGetBytePtr(bytes
), CFDataGetLength(bytes
));
426 CFRelease(valueCopy
);
429 CFWriteStreamClose(stream
);
435 mDNSPlatformMemFree(subkeyCopy
);
437 KQueueUnlock("mDNSDynamicStoreSetConfig");
441 // To match *either* a v4 or v6 instance of this interface name, pass AF_UNSPEC for type
442 mDNSlocal NetworkInterfaceInfoOSX
*SearchForInterfaceByName(const char *ifname
, int type
)
444 NetworkInterfaceInfoOSX
*i
;
445 for (i
= mDNSStorage
.p
->InterfaceList
; i
; i
= i
->next
)
446 if (i
->Exists
&& !strcmp(i
->ifinfo
.ifname
, ifname
) &&
447 ((type
== AF_UNSPEC
) ||
448 (type
== AF_INET
&& i
->ifinfo
.ip
.type
== mDNSAddrType_IPv4
) ||
449 (type
== AF_INET6
&& i
->ifinfo
.ip
.type
== mDNSAddrType_IPv6
))) return(i
);
453 mDNSlocal
int myIfIndexToName(u_short ifindex
, char *name
)
456 for (ifa
= myGetIfAddrs(0); ifa
; ifa
= ifa
->ifa_next
)
457 if (ifa
->ifa_addr
&& ifa
->ifa_addr
->sa_family
== AF_LINK
)
458 if (((struct sockaddr_dl
*)ifa
->ifa_addr
)->sdl_index
== ifindex
)
459 { strlcpy(name
, ifa
->ifa_name
, IF_NAMESIZE
); return 0; }
463 mDNSexport NetworkInterfaceInfoOSX
*IfindexToInterfaceInfoOSX(mDNSInterfaceID ifindex
)
465 mDNS
*const m
= &mDNSStorage
;
466 mDNSu32 scope_id
= (mDNSu32
)(uintptr_t)ifindex
;
467 NetworkInterfaceInfoOSX
*i
;
469 // Don't get tricked by inactive interfaces
470 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
)
471 if (i
->Registered
&& i
->scope_id
== scope_id
) return(i
);
476 mDNSexport mdns_interface_monitor_t
GetInterfaceMonitorForIndex(uint32_t ifIndex
)
478 mDNS
*const m
= &mDNSStorage
;
480 // We assume that interface should always be real interface, and should never be 0.
481 if (ifIndex
== 0) return NULL
;
483 if (!m
->p
->InterfaceMonitors
)
485 m
->p
->InterfaceMonitors
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &mdns_cfarray_callbacks
);
486 if (!m
->p
->InterfaceMonitors
)
488 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
, "Failed to create InterfaceMonitors array");
493 // Search for interface monitor given the interface index.
494 mdns_interface_monitor_t monitor
;
495 for (CFIndex i
= 0, n
= CFArrayGetCount(m
->p
->InterfaceMonitors
); i
< n
; i
++)
497 monitor
= (mdns_interface_monitor_t
) CFArrayGetValueAtIndex(m
->p
->InterfaceMonitors
, i
);
498 if (mdns_interface_monitor_get_interface_index(monitor
) == ifIndex
) return monitor
;
501 // If we come here, it means the interface is a new interface that needs to be monitored.
502 monitor
= mdns_interface_monitor_create(ifIndex
);
505 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
, "Failed to create an interface monitor for index %u", ifIndex
);
508 CFArrayAppendValue(m
->p
->InterfaceMonitors
, monitor
);
510 // Put the monitor into serial queue.
511 mdns_interface_monitor_set_queue(monitor
, dispatch_get_main_queue());
513 // When the interface configuration is changed, this block will be called.
514 mdns_interface_monitor_set_update_handler(monitor
,
515 ^(mdns_interface_flags_t changeFlags
)
517 const mdns_interface_flags_t relevantFlags
=
518 mdns_interface_flag_expensive
|
519 mdns_interface_flag_constrained
|
520 mdns_interface_flag_clat46
;
521 if ((changeFlags
& relevantFlags
) == 0) return;
524 const CFRange range
= CFRangeMake(0, CFArrayGetCount(m
->p
->InterfaceMonitors
));
525 if (CFArrayContainsValue(m
->p
->InterfaceMonitors
, range
, monitor
))
527 m
->p
->if_interface_changed
= mDNStrue
;
529 #if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
530 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
, "Monitored interface changed: %@", monitor
);
532 // Let mDNSResponder update its network configuration.
534 SetNetworkChanged((mDNSPlatformOneSecond
+ 39) / 40); // 25 ms delay
537 KQueueUnlock("interface monitor update handler");
540 mdns_interface_monitor_set_event_handler(monitor
,
541 ^(mdns_event_t event
, OSStatus error
)
545 case mdns_event_invalidated
:
546 mdns_release(monitor
);
549 case mdns_event_error
:
550 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
, "Interface monitor for index %u error: %ld",
551 mdns_interface_monitor_get_interface_index(monitor
), (long) error
);
553 if (m
->p
->InterfaceMonitors
)
555 const CFRange range
= CFRangeMake(0, CFArrayGetCount(m
->p
->InterfaceMonitors
));
556 const CFIndex i
= CFArrayGetFirstIndexOfValue(m
->p
->InterfaceMonitors
, range
, monitor
);
557 if (i
>= 0) CFArrayRemoveValueAtIndex(m
->p
->InterfaceMonitors
, i
);
559 KQueueUnlock("interface monitor event handler");
560 mdns_interface_monitor_invalidate(monitor
);
567 mdns_interface_monitor_activate(monitor
);
572 mDNSexport mDNSInterfaceID
mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS
*const m
, mDNSu32 ifindex
)
575 if (ifindex
== kDNSServiceInterfaceIndexLocalOnly
) return(mDNSInterface_LocalOnly
);
576 if (ifindex
== kDNSServiceInterfaceIndexP2P
) return(mDNSInterface_P2P
);
577 if (ifindex
== kDNSServiceInterfaceIndexBLE
) return(mDNSInterface_BLE
);
578 if (ifindex
== kDNSServiceInterfaceIndexAny
) return(mDNSNULL
);
580 NetworkInterfaceInfoOSX
* ifi
= IfindexToInterfaceInfoOSX((mDNSInterfaceID
)(uintptr_t)ifindex
);
583 // Not found. Make sure our interface list is up to date, then try again.
584 LogInfo("mDNSPlatformInterfaceIDfromInterfaceIndex: InterfaceID for interface index %d not found; Updating interface list", ifindex
);
585 mDNSMacOSXNetworkChanged();
586 ifi
= IfindexToInterfaceInfoOSX((mDNSInterfaceID
)(uintptr_t)ifindex
);
589 if (!ifi
) return(mDNSNULL
);
591 return(ifi
->ifinfo
.InterfaceID
);
595 mDNSexport mDNSu32
mDNSPlatformInterfaceIndexfromInterfaceID(mDNS
*const m
, mDNSInterfaceID id
, mDNSBool suppressNetworkChange
)
597 NetworkInterfaceInfoOSX
*i
;
598 if (id
== mDNSInterface_Any
) return(0);
599 if (id
== mDNSInterface_LocalOnly
) return(kDNSServiceInterfaceIndexLocalOnly
);
600 if (id
== mDNSInterface_P2P
) return(kDNSServiceInterfaceIndexP2P
);
601 if (id
== mDNSInterface_BLE
) return(kDNSServiceInterfaceIndexBLE
);
603 mDNSu32 scope_id
= (mDNSu32
)(uintptr_t)id
;
605 // Don't use i->Registered here, because we DO want to find inactive interfaces, which have no Registered set
606 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
)
607 if (i
->scope_id
== scope_id
) return(i
->scope_id
);
609 // If we are supposed to suppress network change, return "id" back
610 if (suppressNetworkChange
) return scope_id
;
612 // Not found. Make sure our interface list is up to date, then try again.
613 LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id
);
614 mDNSMacOSXNetworkChanged();
615 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
)
616 if (i
->scope_id
== scope_id
) return(i
->scope_id
);
621 #if COMPILER_LIKES_PRAGMA_MARK
623 #pragma mark - UDP & TCP send & receive
626 mDNSlocal mDNSBool
AddrRequiresPPPConnection(const struct sockaddr
*addr
)
628 mDNSBool result
= mDNSfalse
;
629 SCNetworkConnectionFlags flags
;
630 CFDataRef remote_addr
;
631 CFMutableDictionaryRef options
;
632 SCNetworkReachabilityRef ReachRef
= NULL
;
634 options
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
635 remote_addr
= CFDataCreate(NULL
, (const UInt8
*)addr
, addr
->sa_len
);
636 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
, remote_addr
);
637 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionServerBypass
, kCFBooleanTrue
);
638 ReachRef
= SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault
, options
);
640 CFRelease(remote_addr
);
644 LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithOptions");
647 if (!SCNetworkReachabilityGetFlags(ReachRef
, &flags
))
649 LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags");
652 result
= flags
& kSCNetworkFlagsConnectionRequired
;
660 // Set traffic class for socket
661 mDNSlocal
void setTrafficClass(int socketfd
, mDNSBool useBackgroundTrafficClass
)
665 if (useBackgroundTrafficClass
)
666 traffic_class
= SO_TC_BK_SYS
;
668 traffic_class
= SO_TC_CTL
;
670 (void) setsockopt(socketfd
, SOL_SOCKET
, SO_TRAFFIC_CLASS
, (void *)&traffic_class
, sizeof(traffic_class
));
676 mDNSlocal
int mDNSPlatformGetSocktFd(void *sockCxt
, mDNSTransport_Type transType
, mDNSAddr_Type addrType
)
678 if (transType
== mDNSTransport_UDP
)
680 UDPSocket
* sock
= (UDPSocket
*) sockCxt
;
681 return (addrType
== mDNSAddrType_IPv4
) ? sock
->ss
.sktv4
: sock
->ss
.sktv6
;
683 else if (transType
== mDNSTransport_TCP
)
685 TCPSocket
* sock
= (TCPSocket
*) sockCxt
;
690 LogInfo("mDNSPlatformGetSocktFd: invalid transport %d", transType
);
691 return kInvalidSocketRef
;
695 mDNSexport
void mDNSPlatformSetSocktOpt(void *sockCxt
, mDNSTransport_Type transType
, mDNSAddr_Type addrType
, const DNSQuestion
*q
)
698 char unenc_name
[MAX_ESCAPED_DOMAIN_NAME
];
700 // verify passed-in arguments exist and that sockfd is valid
701 if (q
== mDNSNULL
|| sockCxt
== mDNSNULL
|| (sockfd
= mDNSPlatformGetSocktFd(sockCxt
, transType
, addrType
)) < 0)
706 if (setsockopt(sockfd
, SOL_SOCKET
, SO_DELEGATED
, &q
->pid
, sizeof(q
->pid
)) == -1)
707 LogMsg("mDNSPlatformSetSocktOpt: Delegate PID failed %s for PID %d", strerror(errno
), q
->pid
);
711 if (setsockopt(sockfd
, SOL_SOCKET
, SO_DELEGATED_UUID
, &q
->uuid
, sizeof(q
->uuid
)) == -1)
712 LogMsg("mDNSPlatformSetSocktOpt: Delegate UUID failed %s", strerror(errno
));
715 // set the domain on the socket
716 ConvertDomainNameToCString(&q
->qname
, unenc_name
);
717 if (!(ne_session_set_socket_attributes(sockfd
, unenc_name
, NULL
)))
718 LogInfo("mDNSPlatformSetSocktOpt: ne_session_set_socket_attributes()-> setting domain failed for %s", unenc_name
);
721 if (setsockopt(sockfd
, SOL_SOCKET
, SO_NOWAKEFROMSLEEP
, &nowake
, sizeof(nowake
)) == -1)
722 LogInfo("mDNSPlatformSetSocktOpt: SO_NOWAKEFROMSLEEP failed %s", strerror(errno
));
726 // Note: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket"
727 // Note: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface"
728 // OR send via our primary v4 unicast socket
729 // UPDATE: The UDPSocket *src parameter now allows the caller to specify the source socket
730 mDNSexport mStatus
mDNSPlatformSendUDP(const mDNS
*const m
, const void *const msg
, const mDNSu8
*const end
,
731 mDNSInterfaceID InterfaceID
, UDPSocket
*src
, const mDNSAddr
*dst
,
732 mDNSIPPort dstPort
, mDNSBool useBackgroundTrafficClass
)
734 NetworkInterfaceInfoOSX
*info
= mDNSNULL
;
735 struct sockaddr_storage to
;
737 mStatus result
= mStatus_NoError
;
739 const DNSMessage
*const dns_msg
= msg
;
743 info
= IfindexToInterfaceInfoOSX(InterfaceID
);
746 // We may not have registered interfaces with the "core" as we may not have
747 // seen any interface notifications yet. This typically happens during wakeup
748 // where we might try to send DNS requests (non-SuppressUnusable questions internal
749 // to mDNSResponder) before we receive network notifications.
750 LogInfo("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID
);
751 return mStatus_BadParamErr
;
755 char *ifa_name
= InterfaceID
? info
->ifinfo
.ifname
: "unicast";
757 if (dst
->type
== mDNSAddrType_IPv4
)
759 struct sockaddr_in
*sin_to
= (struct sockaddr_in
*)&to
;
760 sin_to
->sin_len
= sizeof(*sin_to
);
761 sin_to
->sin_family
= AF_INET
;
762 sin_to
->sin_port
= dstPort
.NotAnInteger
;
763 sin_to
->sin_addr
.s_addr
= dst
->ip
.v4
.NotAnInteger
;
764 s
= (src
? src
->ss
: m
->p
->permanentsockets
).sktv4
;
766 if (!mDNSAddrIsDNSMulticast(dst
))
769 const mDNSu32 ifindex
= info
? info
->scope_id
: IFSCOPE_NONE
;
770 setsockopt(s
, IPPROTO_IP
, IP_BOUND_IF
, &ifindex
, sizeof(ifindex
));
772 static int displayed
= 0;
773 if (displayed
< 1000)
776 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
777 "[Q%u] IP_BOUND_IF socket option not defined -- cannot specify interface for unicast packets",
778 mDNSVal16(dns_msg
->h
.id
));
784 #ifdef IP_MULTICAST_IFINDEX
785 err
= setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IFINDEX
, &info
->scope_id
, sizeof(info
->scope_id
));
786 // We get an error when we compile on a machine that supports this option and run the binary on
787 // a different machine that does not support it
790 if (errno
!= ENOPROTOOPT
)
792 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
,
793 "[Q%u] mDNSPlatformSendUDP: setsockopt: IP_MUTLTICAST_IFINDEX returned %d",
794 mDNSVal16(dns_msg
->h
.id
), errno
);
796 err
= setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
, &info
->ifa_v4addr
, sizeof(info
->ifa_v4addr
));
797 if (err
< 0 && !m
->NetworkChanged
)
799 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
,
800 "[Q%u] setsockopt - IP_MULTICAST_IF error " PRI_IPv4_ADDR
" %d errno %d (" PUB_S
")",
801 mDNSVal16(dns_msg
->h
.id
), &info
->ifa_v4addr
, err
, errno
, strerror(errno
));
805 err
= setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
, &info
->ifa_v4addr
, sizeof(info
->ifa_v4addr
));
806 if (err
< 0 && !m
->NetworkChanged
)
808 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
,
809 "[Q%u] setsockopt - IP_MULTICAST_IF error " PRI_IPv4_ADDR
" %d errno %d (" PUB_S
")",
810 mDNSVal16(dns_msg
->h
.id
), &info
->ifa_v4addr
, err
, errno
, strerror(errno
));
815 else if (dst
->type
== mDNSAddrType_IPv6
)
817 struct sockaddr_in6
*sin6_to
= (struct sockaddr_in6
*)&to
;
818 sin6_to
->sin6_len
= sizeof(*sin6_to
);
819 sin6_to
->sin6_family
= AF_INET6
;
820 sin6_to
->sin6_port
= dstPort
.NotAnInteger
;
821 sin6_to
->sin6_flowinfo
= 0;
822 sin6_to
->sin6_addr
= *(struct in6_addr
*)&dst
->ip
.v6
;
823 sin6_to
->sin6_scope_id
= info
? info
->scope_id
: 0;
824 s
= (src
? src
->ss
: m
->p
->permanentsockets
).sktv6
;
825 if (info
&& mDNSAddrIsDNSMulticast(dst
)) // Specify outgoing interface
827 err
= setsockopt(s
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, &info
->scope_id
, sizeof(info
->scope_id
));
830 const int setsockopt_errno
= errno
;
832 if (if_indextoname(info
->scope_id
, name
) != NULL
)
834 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
,
835 "[Q%u] setsockopt - IPV6_MULTICAST_IF error %d errno %d (" PUB_S
")",
836 mDNSVal16(dns_msg
->h
.id
), err
, setsockopt_errno
, strerror(setsockopt_errno
));
840 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
,
841 "[Q%u] setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface",
842 mDNSVal16(dns_msg
->h
.id
), info
->scope_id
);
847 if (info
) // Specify outgoing interface for non-multicast destination
849 if (!mDNSAddrIsDNSMulticast(dst
))
851 if (info
->scope_id
== 0)
853 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
854 "[Q%u] IPV6_BOUND_IF socket option not set -- info %p (" PUB_S
") scope_id is zero",
855 mDNSVal16(dns_msg
->h
.id
), info
, ifa_name
);
859 setsockopt(s
, IPPROTO_IPV6
, IPV6_BOUND_IF
, &info
->scope_id
, sizeof(info
->scope_id
));
867 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_FAULT
,
868 "[Q%u] mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!", mDNSVal16(dns_msg
->h
.id
));
869 return mStatus_BadParamErr
;
874 verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %p %5s/%ld to %#a:%d skt %d",
875 InterfaceID
, ifa_name
, dst
->type
, dst
, mDNSVal16(dstPort
), s
);
879 verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %p %5s/%ld (socket of this type not available)",
880 InterfaceID
, ifa_name
, dst
->type
, dst
, mDNSVal16(dstPort
));
883 // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet
884 // If we don't have the corresponding type of socket available, then return mStatus_Invalid
885 if (s
< 0) return(mStatus_Invalid
);
887 // switch to background traffic class for this message if requested
888 if (useBackgroundTrafficClass
)
889 setTrafficClass(s
, useBackgroundTrafficClass
);
891 err
= sendto(s
, msg
, (UInt8
*)end
- (UInt8
*)msg
, 0, (struct sockaddr
*)&to
, to
.ss_len
);
892 sendto_errno
= (err
< 0) ? errno
: 0;
894 // set traffic class back to default value
895 if (useBackgroundTrafficClass
)
896 setTrafficClass(s
, mDNSfalse
);
900 static int MessageCount
= 0;
901 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
,
902 "[Q%u] mDNSPlatformSendUDP -> sendto(%d) failed to send packet on InterfaceID %p " PUB_S
"/%d to " PRI_IP_ADDR
":%d skt %d error %d errno %d (" PUB_S
") %u",
903 mDNSVal16(dns_msg
->h
.id
), s
, InterfaceID
, ifa_name
, dst
->type
, dst
, mDNSVal16(dstPort
), s
, err
, sendto_errno
, strerror(sendto_errno
), (mDNSu32
)(m
->timenow
));
904 if (!mDNSAddressIsAllDNSLinkGroup(dst
))
906 if ((sendto_errno
== EHOSTUNREACH
) || (sendto_errno
== ENETUNREACH
)) return(mStatus_HostUnreachErr
);
907 if ((sendto_errno
== EHOSTDOWN
) || (sendto_errno
== ENETDOWN
)) return(mStatus_TransientErr
);
909 // Don't report EHOSTUNREACH in the first three minutes after boot
910 // This is because mDNSResponder intentionally starts up early in the boot process (See <rdar://problem/3409090>)
911 // but this means that sometimes it starts before configd has finished setting up the multicast routing entries.
912 if (sendto_errno
== EHOSTUNREACH
&& (mDNSu32
)(mDNSPlatformRawTime()) < (mDNSu32
)(mDNSPlatformOneSecond
* 180)) return(mStatus_TransientErr
);
913 // Don't report EADDRNOTAVAIL ("Can't assign requested address") if we're in the middle of a network configuration change
914 if (sendto_errno
== EADDRNOTAVAIL
&& m
->NetworkChanged
) return(mStatus_TransientErr
);
915 if (sendto_errno
== EHOSTUNREACH
|| sendto_errno
== EADDRNOTAVAIL
|| sendto_errno
== ENETDOWN
)
917 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
,
918 "[Q%u] mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p " PUB_S
"/%d to " PRI_IP_ADDR
":%d skt %d error %d errno %d (" PUB_S
") %u",
919 mDNSVal16(dns_msg
->h
.id
), s
, InterfaceID
, ifa_name
, dst
->type
, dst
, mDNSVal16(dstPort
), s
, err
, sendto_errno
, strerror(sendto_errno
), (mDNSu32
)(m
->timenow
));
924 if (MessageCount
< 50) // Cap and ensure NO spamming of LogMsgs
926 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
,
927 "[Q%u] mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p " PUB_S
"/%d to " PRI_IP_ADDR
":%d skt %d error %d errno %d (" PUB_S
") %u MessageCount is %d",
928 mDNSVal16(dns_msg
->h
.id
), s
, InterfaceID
, ifa_name
, dst
->type
, dst
, mDNSVal16(dstPort
), s
, err
, sendto_errno
, strerror(sendto_errno
), (mDNSu32
)(m
->timenow
), MessageCount
);
930 else // If logging is enabled, remove the cap and log aggressively
932 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
933 "[Q%u] mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p " PUB_S
"/%d to " PRI_IP_ADDR
":%d skt %d error %d errno %d (" PUB_S
") %u MessageCount is %d",
934 mDNSVal16(dns_msg
->h
.id
), s
, InterfaceID
, ifa_name
, dst
->type
, dst
, mDNSVal16(dstPort
), s
, err
, sendto_errno
, strerror(sendto_errno
), (mDNSu32
)(m
->timenow
), MessageCount
);
938 result
= mStatus_UnknownErr
;
944 mDNSexport ssize_t
myrecvfrom(const int s
, void *const buffer
, const size_t max
,
945 struct sockaddr
*const from
, size_t *const fromlen
, mDNSAddr
*dstaddr
, char ifname
[IF_NAMESIZE
], mDNSu8
*ttl
)
947 static unsigned int numLogMessages
= 0;
948 struct iovec databuffers
= { (char *)buffer
, max
};
951 struct cmsghdr
*cmPtr
;
952 char ancillary
[1024];
954 *ttl
= 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be
956 // Set up the message
957 msg
.msg_name
= (caddr_t
)from
;
958 msg
.msg_namelen
= *fromlen
;
959 msg
.msg_iov
= &databuffers
;
961 msg
.msg_control
= (caddr_t
)&ancillary
;
962 msg
.msg_controllen
= sizeof(ancillary
);
966 n
= recvmsg(s
, &msg
, 0);
969 if (errno
!= EWOULDBLOCK
&& numLogMessages
++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned error %d errno %d", s
, n
, errno
);
972 if (msg
.msg_controllen
< (int)sizeof(struct cmsghdr
))
974 if (numLogMessages
++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu, errno %d",
975 s
, n
, msg
.msg_controllen
, sizeof(struct cmsghdr
), errno
);
978 if (msg
.msg_flags
& MSG_CTRUNC
)
980 if (numLogMessages
++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s
);
984 *fromlen
= msg
.msg_namelen
;
986 // Parse each option out of the ancillary data.
987 for (cmPtr
= CMSG_FIRSTHDR(&msg
); cmPtr
; cmPtr
= CMSG_NXTHDR(&msg
, cmPtr
))
989 // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type);
990 if (cmPtr
->cmsg_level
== IPPROTO_IP
&& cmPtr
->cmsg_type
== IP_RECVDSTADDR
)
992 dstaddr
->type
= mDNSAddrType_IPv4
;
993 dstaddr
->ip
.v4
= *(mDNSv4Addr
*)CMSG_DATA(cmPtr
);
994 //LogMsg("mDNSMacOSX.c: recvmsg IP_RECVDSTADDR %.4a", &dstaddr->ip.v4);
996 if (cmPtr
->cmsg_level
== IPPROTO_IP
&& cmPtr
->cmsg_type
== IP_RECVIF
)
998 struct sockaddr_dl
*sdl
= (struct sockaddr_dl
*)CMSG_DATA(cmPtr
);
999 if (sdl
->sdl_nlen
< IF_NAMESIZE
)
1001 mDNSPlatformMemCopy(ifname
, sdl
->sdl_data
, sdl
->sdl_nlen
);
1002 ifname
[sdl
->sdl_nlen
] = 0;
1003 // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen);
1006 if (cmPtr
->cmsg_level
== IPPROTO_IP
&& cmPtr
->cmsg_type
== IP_RECVTTL
)
1007 *ttl
= *(u_char
*)CMSG_DATA(cmPtr
);
1008 if (cmPtr
->cmsg_level
== IPPROTO_IPV6
&& cmPtr
->cmsg_type
== IPV6_PKTINFO
)
1010 struct in6_pktinfo
*ip6_info
= (struct in6_pktinfo
*)CMSG_DATA(cmPtr
);
1011 dstaddr
->type
= mDNSAddrType_IPv6
;
1012 dstaddr
->ip
.v6
= *(mDNSv6Addr
*)&ip6_info
->ipi6_addr
;
1013 myIfIndexToName(ip6_info
->ipi6_ifindex
, ifname
);
1015 if (cmPtr
->cmsg_level
== IPPROTO_IPV6
&& cmPtr
->cmsg_type
== IPV6_HOPLIMIT
)
1016 *ttl
= *(int*)CMSG_DATA(cmPtr
);
1022 // What is this for, and why does it use xor instead of a simple equality check? -- SC
1023 mDNSlocal mDNSInterfaceID
FindMyInterface(const mDNSAddr
*addr
)
1025 NetworkInterfaceInfo
*intf
;
1027 if (addr
->type
== mDNSAddrType_IPv4
)
1029 for (intf
= mDNSStorage
.HostInterfaces
; intf
; intf
= intf
->next
)
1031 if (intf
->ip
.type
== addr
->type
&& intf
->McastTxRx
)
1033 if ((intf
->ip
.ip
.v4
.NotAnInteger
^ addr
->ip
.v4
.NotAnInteger
) == 0)
1035 return(intf
->InterfaceID
);
1041 if (addr
->type
== mDNSAddrType_IPv6
)
1043 for (intf
= mDNSStorage
.HostInterfaces
; intf
; intf
= intf
->next
)
1045 if (intf
->ip
.type
== addr
->type
&& intf
->McastTxRx
)
1047 if (((intf
->ip
.ip
.v6
.l
[0] ^ addr
->ip
.v6
.l
[0]) == 0) &&
1048 ((intf
->ip
.ip
.v6
.l
[1] ^ addr
->ip
.v6
.l
[1]) == 0) &&
1049 ((intf
->ip
.ip
.v6
.l
[2] ^ addr
->ip
.v6
.l
[2]) == 0) &&
1050 (((intf
->ip
.ip
.v6
.l
[3] ^ addr
->ip
.v6
.l
[3]) == 0)))
1052 return(intf
->InterfaceID
);
1057 return(mDNSInterface_Any
);
1060 mDNSexport
void myKQSocketCallBack(int s1
, short filter
, void *context
, mDNSBool encounteredEOF
)
1062 KQSocketSet
*const ss
= (KQSocketSet
*)context
;
1063 mDNS
*const m
= ss
->m
;
1064 int err
= 0, count
= 0, closed
= 0, recvfrom_errno
= 0;
1066 if (filter
!= EVFILT_READ
)
1067 LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter
, EVFILT_READ
);
1069 if (s1
!= ss
->sktv4
&& s1
!= ss
->sktv6
)
1071 LogMsg("myKQSocketCallBack: native socket %d", s1
);
1072 LogMsg("myKQSocketCallBack: sktv4 %d sktv6 %d", ss
->sktv4
, ss
->sktv6
);
1077 LogMsg("myKQSocketCallBack: socket %d is no longer readable (EOF)", s1
);
1078 if (s1
== ss
->sktv4
)
1080 ss
->sktv4EOF
= mDNStrue
;
1081 KQueueSet(ss
->sktv4
, EV_DELETE
, EVFILT_READ
, &ss
->kqsv4
);
1083 else if (s1
== ss
->sktv6
)
1085 ss
->sktv6EOF
= mDNStrue
;
1086 KQueueSet(ss
->sktv6
, EV_DELETE
, EVFILT_READ
, &ss
->kqsv6
);
1093 mDNSAddr senderAddr
, destAddr
= zeroAddr
;
1094 mDNSIPPort senderPort
;
1095 struct sockaddr_storage from
;
1096 size_t fromlen
= sizeof(from
);
1097 char packetifname
[IF_NAMESIZE
] = "";
1099 err
= myrecvfrom(s1
, &m
->imsg
, sizeof(m
->imsg
), (struct sockaddr
*)&from
, &fromlen
, &destAddr
, packetifname
, &ttl
);
1102 recvfrom_errno
= errno
;
1106 if ((destAddr
.type
== mDNSAddrType_IPv4
&& (destAddr
.ip
.v4
.b
[0] & 0xF0) == 0xE0) ||
1107 (destAddr
.type
== mDNSAddrType_IPv6
&& (destAddr
.ip
.v6
.b
[0] == 0xFF))) m
->p
->num_mcasts
++;
1110 if (from
.ss_family
== AF_INET
)
1112 struct sockaddr_in
*s
= (struct sockaddr_in
*)&from
;
1113 senderAddr
.type
= mDNSAddrType_IPv4
;
1114 senderAddr
.ip
.v4
.NotAnInteger
= s
->sin_addr
.s_addr
;
1115 senderPort
.NotAnInteger
= s
->sin_port
;
1116 //LogInfo("myKQSocketCallBack received IPv4 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname);
1118 else if (from
.ss_family
== AF_INET6
)
1120 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&from
;
1121 senderAddr
.type
= mDNSAddrType_IPv6
;
1122 senderAddr
.ip
.v6
= *(mDNSv6Addr
*)&sin6
->sin6_addr
;
1123 senderPort
.NotAnInteger
= sin6
->sin6_port
;
1124 //LogInfo("myKQSocketCallBack received IPv6 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname);
1128 LogMsg("myKQSocketCallBack from is unknown address family %d", from
.ss_family
);
1132 // Note: When handling multiple packets in a batch, MUST reset InterfaceID before handling each packet
1133 mDNSInterfaceID InterfaceID
= mDNSNULL
;
1134 NetworkInterfaceInfoOSX
*intf
= m
->p
->InterfaceList
;
1137 if (intf
->Exists
&& !strcmp(intf
->ifinfo
.ifname
, packetifname
))
1142 // When going to sleep we deregister all our interfaces, but if the machine
1143 // takes a few seconds to sleep we may continue to receive multicasts
1144 // during that time, which would confuse mDNSCoreReceive, because as far
1145 // as it's concerned, we should have no active interfaces any more.
1146 // Hence we ignore multicasts for which we can find no matching InterfaceID.
1148 InterfaceID
= intf
->ifinfo
.InterfaceID
;
1149 else if (mDNSAddrIsDNSMulticast(&destAddr
))
1154 InterfaceID
= FindMyInterface(&destAddr
);
1157 // LogMsg("myKQSocketCallBack got packet from %#a to %#a on interface %#a/%s",
1158 // &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifinfo.ifname);
1160 // mDNSCoreReceive may close the socket we're reading from. We must break out of our
1161 // loop when that happens, or we may try to read from an invalid FD. We do this by
1162 // setting the closeFlag pointer in the socketset, so CloseSocketSet can inform us
1163 // if it closes the socketset.
1164 ss
->closeFlag
= &closed
;
1168 m
->p
->UDPProxyCallback(&m
->p
->UDPProxy
, &m
->imsg
.m
, (unsigned char*)&m
->imsg
+ err
, &senderAddr
,
1169 senderPort
, &destAddr
, ss
->port
, InterfaceID
, NULL
);
1173 mDNSCoreReceive(m
, &m
->imsg
.m
, (unsigned char*)&m
->imsg
+ err
, &senderAddr
, senderPort
, &destAddr
, ss
->port
, InterfaceID
);
1176 // if we didn't close, we can safely dereference the socketset, and should to
1177 // reset the closeFlag, since it points to something on the stack
1178 if (!closed
) ss
->closeFlag
= mDNSNULL
;
1181 // If a client application's sockets are marked as defunct
1182 // sockets we have delegated to it with SO_DELEGATED will also go defunct.
1183 // We get an ENOTCONN error for defunct sockets and should just close the socket in that case.
1184 if (err
< 0 && recvfrom_errno
== ENOTCONN
)
1186 LogInfo("myKQSocketCallBack: ENOTCONN, closing socket");
1191 if (err
< 0 && (recvfrom_errno
!= EWOULDBLOCK
|| count
== 0))
1193 // Something is busted here.
1194 // kqueue says there is a packet, but myrecvfrom says there is not.
1195 // Try calling select() to get another opinion.
1196 // Find out about other socket parameter that can help understand why select() says the socket is ready for read
1197 // All of this is racy, as data may have arrived after the call to select()
1198 static unsigned int numLogMessages
= 0;
1204 struct timeval timeout
;
1207 FD_SET(s1
, &readfds
);
1209 timeout
.tv_usec
= 0;
1210 selectresult
= select(s1
+1, &readfds
, NULL
, NULL
, &timeout
);
1211 solen
= (socklen_t
)sizeof(so_error
);
1212 if (getsockopt(s1
, SOL_SOCKET
, SO_ERROR
, &so_error
, &solen
) == -1)
1213 LogMsg("myKQSocketCallBack getsockopt(SO_ERROR) error %d", errno
);
1214 solen
= (socklen_t
)sizeof(so_nread
);
1215 if (getsockopt(s1
, SOL_SOCKET
, SO_NREAD
, &so_nread
, &solen
) == -1)
1216 LogMsg("myKQSocketCallBack getsockopt(SO_NREAD) error %d", errno
);
1217 if (ioctl(s1
, FIONREAD
, &fionread
) == -1)
1218 LogMsg("myKQSocketCallBack ioctl(FIONREAD) error %d", errno
);
1219 if (numLogMessages
++ < 100)
1220 LogMsg("myKQSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d",
1221 s1
, err
, recvfrom_errno
, strerror(recvfrom_errno
), selectresult
, FD_ISSET(s1
, &readfds
) ? "" : "*NO* ", so_error
, so_nread
, fionread
, count
);
1222 if (numLogMessages
> 5)
1223 NotifyOfElusiveBug("Flaw in Kernel (select/recvfrom mismatch)",
1224 "Congratulations, you've reproduced an elusive bug.\r"
1225 "Please send email to radar-3387020@group.apple.com.)\r"
1226 "If possible, please leave your machine undisturbed so that someone can come to investigate the problem.");
1228 sleep(1); // After logging this error, rate limit so we don't flood syslog
1232 mDNSlocal
void doTcpSocketCallback(TCPSocket
*sock
)
1234 mDNSBool c
= !sock
->connected
;
1235 if (!sock
->connected
&& sock
->err
== mStatus_NoError
)
1237 sock
->connected
= mDNStrue
;
1239 sock
->callback(sock
, sock
->context
, c
, sock
->err
);
1240 // Note: the callback may call CloseConnection here, which frees the context structure!
1243 #ifndef NO_SECURITYFRAMEWORK
1245 mDNSlocal OSStatus
tlsWriteSock(SSLConnectionRef connection
, const void *data
, size_t *dataLength
)
1247 int ret
= send(((TCPSocket
*)connection
)->fd
, data
, *dataLength
, 0);
1248 if (ret
>= 0 && (size_t)ret
< *dataLength
) { *dataLength
= ret
; return(errSSLWouldBlock
); }
1249 if (ret
>= 0) { *dataLength
= ret
; return(noErr
); }
1251 if (errno
== EAGAIN
) return(errSSLWouldBlock
);
1252 if (errno
== ENOENT
) return(errSSLClosedGraceful
);
1253 if (errno
== EPIPE
|| errno
== ECONNRESET
) return(errSSLClosedAbort
);
1254 LogMsg("ERROR: tlsWriteSock: %d error %d (%s)\n", ((TCPSocket
*)connection
)->fd
, errno
, strerror(errno
));
1255 return(errSSLClosedAbort
);
1258 mDNSlocal OSStatus
tlsReadSock(SSLConnectionRef connection
, void *data
, size_t *dataLength
)
1260 int ret
= recv(((TCPSocket
*)connection
)->fd
, data
, *dataLength
, 0);
1261 if (ret
> 0 && (size_t)ret
< *dataLength
) { *dataLength
= ret
; return(errSSLWouldBlock
); }
1262 if (ret
> 0) { *dataLength
= ret
; return(noErr
); }
1264 if (ret
== 0 || errno
== ENOENT
) return(errSSLClosedGraceful
);
1265 if ( errno
== EAGAIN
) return(errSSLWouldBlock
);
1266 if ( errno
== ECONNRESET
) return(errSSLClosedAbort
);
1267 LogMsg("ERROR: tlsSockRead: error %d (%s)\n", errno
, strerror(errno
));
1268 return(errSSLClosedAbort
);
1271 mDNSlocal OSStatus
tlsSetupSock(TCPSocket
*sock
, SSLProtocolSide pside
, SSLConnectionType ctype
)
1273 char domname_cstr
[MAX_ESCAPED_DOMAIN_NAME
];
1275 sock
->tlsContext
= SSLCreateContext(kCFAllocatorDefault
, pside
, ctype
);
1276 if (!sock
->tlsContext
)
1278 LogMsg("ERROR: tlsSetupSock: SSLCreateContext failed");
1279 return(mStatus_UnknownErr
);
1282 mStatus err
= SSLSetIOFuncs(sock
->tlsContext
, tlsReadSock
, tlsWriteSock
);
1285 LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err
);
1289 err
= SSLSetConnection(sock
->tlsContext
, (SSLConnectionRef
) sock
);
1292 LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err
);
1296 // Set the default ciphersuite configuration
1297 err
= SSLSetSessionConfig(sock
->tlsContext
, CFSTR("default"));
1300 LogMsg("ERROR: tlsSetupSock: SSLSetSessionConfig failed with error code: %d", err
);
1304 // We already checked for NULL in hostname and this should never happen. Hence, returning -1
1305 // (error not in OSStatus space) is okay.
1306 if (!sock
->hostname
|| !sock
->hostname
->c
[0])
1308 LogMsg("ERROR: tlsSetupSock: hostname NULL");
1313 ConvertDomainNameToCString(sock
->hostname
, domname_cstr
);
1314 err
= SSLSetPeerDomainName(sock
->tlsContext
, domname_cstr
, strlen(domname_cstr
));
1317 LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr
, err
);
1324 if (sock
->tlsContext
)
1325 CFRelease(sock
->tlsContext
);
1329 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1330 mDNSlocal
void doSSLHandshake(TCPSocket
*sock
)
1332 mStatus err
= SSLHandshake(sock
->tlsContext
);
1334 //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is
1335 //defined, KQueueLock is a noop. Hence we need to serialize here
1337 //NOTE: We just can't serialize doTcpSocketCallback alone on the main queue.
1338 //We need the rest of the logic also. Otherwise, we can enable the READ
1339 //events below, dispatch a doTcpSocketCallback on the main queue. Assume it is
1340 //ConnFailed which means we are going to free the tcpInfo. While it
1341 //is waiting to be dispatched, another read event can come into tcpKQSocketCallback
1342 //and potentially call doTCPCallback with error which can close the fd and free the
1343 //tcpInfo. Later when the thread gets dispatched it will crash because the tcpInfo
1346 dispatch_async(dispatch_get_main_queue(), ^{
1348 LogInfo("doSSLHandshake %p: got lock", sock
); // Log *after* we get the lock
1350 if (sock
->handshake
== handshake_to_be_closed
)
1352 LogInfo("SSLHandshake completed after close");
1353 mDNSPlatformTCPCloseConnection(sock
);
1357 if (sock
->fd
!= -1) KQueueSet(sock
->fd
, EV_ADD
, EVFILT_READ
, sock
->kqEntry
);
1358 else LogMsg("doSSLHandshake: sock->fd is -1");
1360 if (err
== errSSLWouldBlock
)
1361 sock
->handshake
= handshake_required
;
1366 LogMsg("SSLHandshake failed: %d%s", err
, err
== errSSLPeerInternalError
? " (server busy)" : "");
1367 CFRelease(sock
->tlsContext
);
1368 sock
->tlsContext
= NULL
;
1371 sock
->err
= err
? mStatus_ConnFailed
: 0;
1372 sock
->handshake
= handshake_completed
;
1374 LogInfo("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock
, sock
->fd
);
1375 doTcpSocketCallback(sock
);
1379 LogInfo("SSLHandshake %p: dropping lock for fd %d", sock
, sock
->fd
);
1383 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1384 mDNSlocal
void *doSSLHandshake(TCPSocket
*sock
)
1386 // Warning: Touching sock without the kqueue lock!
1387 // We're protected because sock->handshake == handshake_in_progress
1388 mStatus err
= SSLHandshake(sock
->tlsContext
);
1391 debugf("doSSLHandshake %p: got lock", sock
); // Log *after* we get the lock
1393 if (sock
->handshake
== handshake_to_be_closed
)
1395 LogInfo("SSLHandshake completed after close");
1396 mDNSPlatformTCPCloseConnection(sock
);
1400 if (sock
->fd
!= -1) KQueueSet(sock
->fd
, EV_ADD
, EVFILT_READ
, &sock
->kqEntry
);
1401 else LogMsg("doSSLHandshake: sock->fd is -1");
1403 if (err
== errSSLWouldBlock
)
1404 sock
->handshake
= handshake_required
;
1409 LogMsg("SSLHandshake failed: %d%s", err
, err
== errSSLPeerInternalError
? " (server busy)" : "");
1410 CFRelease(sock
->tlsContext
);
1411 sock
->tlsContext
= NULL
;
1414 sock
->err
= err
? mStatus_ConnFailed
: 0;
1415 sock
->handshake
= handshake_completed
;
1417 debugf("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock
, sock
->fd
);
1418 doTcpSocketCallback(sock
);
1422 debugf("SSLHandshake %p: dropping lock for fd %d", sock
, sock
->fd
);
1423 KQueueUnlock("doSSLHandshake");
1426 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1428 mDNSlocal
void spawnSSLHandshake(TCPSocket
* sock
)
1430 debugf("spawnSSLHandshake %p: entry", sock
);
1432 if (sock
->handshake
!= handshake_required
) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock
->handshake
);
1433 sock
->handshake
= handshake_in_progress
;
1434 KQueueSet(sock
->fd
, EV_DELETE
, EVFILT_READ
, &sock
->kqEntry
);
1436 // Dispatch it on a separate queue to help avoid blocking other threads/queues, and
1437 // to limit the number of threads used for SSLHandshake
1438 dispatch_async(SSLqueue
, ^{doSSLHandshake(sock
);});
1440 debugf("spawnSSLHandshake %p: done for %d", sock
, sock
->fd
);
1443 #endif /* NO_SECURITYFRAMEWORK */
1445 mDNSlocal
void tcpKQSocketCallback(__unused
int fd
, short filter
, void *context
, __unused mDNSBool encounteredEOF
)
1447 TCPSocket
*sock
= context
;
1448 sock
->err
= mStatus_NoError
;
1450 //if (filter == EVFILT_READ ) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_READ", filter);
1451 //if (filter == EVFILT_WRITE) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_WRITE", filter);
1452 // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it here with EV_DELETE
1453 if (filter
== EVFILT_WRITE
)
1455 // sock->connected gets set by doTcpSocketCallback(), which may be called from here, or may be called
1456 // from the TLS connect code. If we asked for a writability test, we are connecting
1457 // (sock->connected == mDNSFalse).
1458 if (sock
->connected
)
1460 LogInfo("ERROR: TCPConnectCallback called with write event when socket is connected.");
1465 socklen_t len
= (socklen_t
)sizeof(result
);
1466 if (getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &result
, &len
) < 0)
1468 LogInfo("ERROR: TCPConnectCallback - unable to get connect error: socket %d: Error %d (%s)",
1469 sock
->fd
, errno
, strerror(errno
));
1470 sock
->err
= mStatus_ConnFailed
;
1476 sock
->err
= mStatus_ConnFailed
;
1477 if (result
== EHOSTUNREACH
|| result
== EADDRNOTAVAIL
|| result
== ENETDOWN
)
1479 LogInfo("ERROR: TCPConnectCallback - connect failed: socket %d: Error %d (%s)",
1480 sock
->fd
, result
, strerror(result
));
1484 LogMsg("ERROR: TCPConnectCallback - connect failed: socket %d: Error %d (%s)",
1485 sock
->fd
, result
, strerror(result
));
1490 KQueueSet(sock
->fd
, EV_DELETE
, EVFILT_WRITE
, &sock
->kqEntry
);
1492 // If we set the EVFILT_READ event in mDNSPlatformTCPConnect, it's possible to get a read event
1493 // before the write event--apparently the socket is both readable and writable once that happens,
1494 // even if the connect fails. If we set it here, after we've gotten a successful connection, then
1495 // we shouldn't run into that problem.
1496 if (sock
->err
== mStatus_NoError
&&
1497 KQueueSet(sock
->fd
, EV_ADD
, EVFILT_READ
, &sock
->kqEntry
))
1499 // And of course if that fails, we can't use the connection even though we have it.
1500 LogMsg("ERROR: tcpKQSocketCallback - KQueueSet failed");
1501 sock
->err
= mStatus_TransientErr
;
1505 if (sock
->flags
& kTCPSocketFlags_UseTLS
)
1507 #ifndef NO_SECURITYFRAMEWORK
1508 // Don't try to set up TLS if the connect failed.
1509 if (sock
->err
== mStatus_NoError
) {
1512 sock
->setup
= mDNStrue
;
1513 sock
->err
= tlsSetupSock(sock
, kSSLClientSide
, kSSLStreamType
);
1516 LogMsg("ERROR: tcpKQSocketCallback: tlsSetupSock failed with error code: %d", sock
->err
);
1520 if (sock
->handshake
== handshake_required
)
1522 spawnSSLHandshake(sock
);
1525 else if (sock
->handshake
== handshake_in_progress
|| sock
->handshake
== handshake_to_be_closed
)
1529 else if (sock
->handshake
!= handshake_completed
)
1532 sock
->err
= mStatus_UnknownErr
;
1533 LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock
->handshake
);
1536 #else /* NO_SECURITYFRAMEWORK */
1537 sock
->err
= mStatus_UnsupportedErr
;
1538 #endif /* NO_SECURITYFRAMEWORK */
1541 doTcpSocketCallback(sock
);
1544 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1545 mDNSexport
int KQueueSet(int fd
, u_short flags
, short filter
, KQueueEntry
*const entryRef
)
1547 dispatch_queue_t queue
= dispatch_get_main_queue();
1548 dispatch_source_t source
;
1549 if (flags
== EV_DELETE
)
1551 if (filter
== EVFILT_READ
)
1553 dispatch_source_cancel(entryRef
->readSource
);
1554 dispatch_release(entryRef
->readSource
);
1555 entryRef
->readSource
= mDNSNULL
;
1556 debugf("KQueueSet: source cancel for read %p, %p", entryRef
->readSource
, entryRef
->writeSource
);
1558 else if (filter
== EVFILT_WRITE
)
1560 dispatch_source_cancel(entryRef
->writeSource
);
1561 dispatch_release(entryRef
->writeSource
);
1562 entryRef
->writeSource
= mDNSNULL
;
1563 debugf("KQueueSet: source cancel for write %p, %p", entryRef
->readSource
, entryRef
->writeSource
);
1566 LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_DELETE", filter
);
1569 if (flags
!= EV_ADD
) LogMsg("KQueueSet: Invalid flags %d", flags
);
1571 if (filter
== EVFILT_READ
)
1573 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
, fd
, 0, queue
);
1575 else if (filter
== EVFILT_WRITE
)
1577 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE
, fd
, 0, queue
);
1581 LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_ADD", filter
);
1584 if (!source
) return -1;
1585 dispatch_source_set_event_handler(source
, ^{
1587 mDNSs32 stime
= mDNSPlatformRawTime();
1588 entryRef
->KQcallback(fd
, filter
, entryRef
->KQcontext
);
1589 mDNSs32 etime
= mDNSPlatformRawTime();
1590 if (etime
- stime
>= WatchDogReportingThreshold
)
1591 LogInfo("KQEntryCallback Block: WARNING: took %dms to complete", etime
- stime
);
1593 // Trigger the event delivery to the application. Even though we trigger the
1594 // event completion after handling every event source, these all will hopefully
1596 TriggerEventCompletion();
1599 dispatch_source_set_cancel_handler(source
, ^{
1600 if (entryRef
->fdClosed
)
1602 //LogMsg("CancelHandler: closing fd %d", fd);
1606 dispatch_resume(source
);
1607 if (filter
== EVFILT_READ
)
1608 entryRef
->readSource
= source
;
1610 entryRef
->writeSource
= source
;
1615 mDNSexport
void KQueueLock()
1618 mDNSexport
void KQueueUnlock(const char const *task
)
1620 (void)task
; //unused
1624 mDNSexport
int KQueueSet(int fd
, u_short flags
, short filter
, const KQueueEntry
*const entryRef
)
1626 struct kevent new_event
;
1627 EV_SET(&new_event
, fd
, filter
, flags
, 0, 0, (void*)entryRef
);
1628 return (kevent(KQueueFD
, &new_event
, 1, NULL
, 0, NULL
) < 0) ? errno
: 0;
1631 mDNSexport
void KQueueLock()
1633 mDNS
*const m
= &mDNSStorage
;
1634 pthread_mutex_lock(&m
->p
->BigMutex
);
1635 m
->p
->BigMutexStartTime
= mDNSPlatformRawTime();
1638 mDNSexport
void KQueueUnlock(const char* task
)
1640 mDNS
*const m
= &mDNSStorage
;
1641 mDNSs32 end
= mDNSPlatformRawTime();
1643 if (end
- m
->p
->BigMutexStartTime
>= WatchDogReportingThreshold
)
1645 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_WARNING
,
1646 "WARNING: " PUB_S
" took %d ms to complete", task
, end
- m
->p
->BigMutexStartTime
);
1649 pthread_mutex_unlock(&m
->p
->BigMutex
);
1652 if (send(m
->p
->WakeKQueueLoopFD
, &wake
, sizeof(wake
), 0) == -1)
1653 LogMsg("ERROR: KQueueWake: send failed with error code: %d (%s)", errno
, strerror(errno
));
1657 mDNSexport
void mDNSPlatformCloseFD(KQueueEntry
*kq
, int fd
)
1659 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1663 dispatch_source_cancel(kq
->readSource
);
1664 kq
->readSource
= mDNSNULL
;
1666 if (kq
->writeSource
)
1668 dispatch_source_cancel(kq
->writeSource
);
1669 kq
->writeSource
= mDNSNULL
;
1671 // Close happens in the cancellation handler
1672 debugf("mDNSPlatformCloseFD: resetting sources for %d", fd
);
1673 kq
->fdClosed
= mDNStrue
;
1680 mDNSlocal mStatus
SetupTCPSocket(TCPSocket
*sock
, mDNSAddr_Type addrtype
, mDNSIPPort
*port
, mDNSBool useBackgroundTrafficClass
)
1685 if (!mDNSPosixTCPSocketSetup(&skt
, addrtype
, port
, &sock
->port
))
1687 if (skt
!= -1) close(skt
);
1688 return mStatus_UnknownErr
;
1691 // for TCP sockets, the traffic class is set once and not changed
1692 setTrafficClass(skt
, useBackgroundTrafficClass
);
1695 sock
->kqEntry
.KQcallback
= tcpKQSocketCallback
;
1696 sock
->kqEntry
.KQcontext
= sock
;
1697 sock
->kqEntry
.KQtask
= "mDNSPlatformTCPSocket";
1698 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1699 sock
->kqEntry
.readSource
= mDNSNULL
;
1700 sock
->kqEntry
.writeSource
= mDNSNULL
;
1701 sock
->kqEntry
.fdClosed
= mDNSfalse
;
1703 return mStatus_NoError
;
1706 mDNSexport TCPSocket
*mDNSPlatformTCPSocket(TCPSocketFlags flags
, mDNSAddr_Type addrtype
, mDNSIPPort
*port
, domainname
*hostname
, mDNSBool useBackgroundTrafficClass
)
1709 mDNSu32 lowWater
= 16384;
1710 size_t len
= sizeof (TCPSocket
);
1712 len
+= sizeof (domainname
);
1715 TCPSocket
*sock
= (TCPSocket
*) callocL("TCPSocket/mDNSPlatformTCPSocket", len
);
1716 if (!sock
) { LogMsg("mDNSPlatformTCPSocket: memory allocation failure"); return(mDNSNULL
); }
1720 sock
->hostname
= (domainname
*)(sock
+ 1); // Allocated together so can be freed together
1721 debugf("mDNSPlatformTCPSocket: hostname %##s", hostname
->c
);
1722 AssignDomainName(sock
->hostname
, hostname
);
1725 err
= SetupTCPSocket(sock
, addrtype
, port
, useBackgroundTrafficClass
);
1729 LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock
->fd
, errno
, strerror(errno
));
1730 freeL("TCPSocket/mDNSPlatformTCPSocket", sock
);
1734 if (setsockopt(sock
->fd
, IPPROTO_TCP
, TCP_NOTSENT_LOWAT
, &lowWater
, sizeof lowWater
) < 0)
1736 LogMsg("mDNSPlatformTCPSocket: TCP_NOTSENT_LOWAT returned %d", errno
);
1737 mDNSPlatformTCPCloseConnection(sock
);
1741 sock
->callback
= mDNSNULL
;
1742 sock
->flags
= flags
;
1743 sock
->context
= mDNSNULL
;
1744 sock
->setup
= mDNSfalse
;
1745 sock
->connected
= mDNSfalse
;
1746 sock
->handshake
= handshake_required
;
1747 sock
->m
= &mDNSStorage
;
1748 sock
->err
= mStatus_NoError
;
1753 mDNSexport mStatus
mDNSPlatformTCPConnect(TCPSocket
*sock
, const mDNSAddr
*dst
, mDNSOpaque16 dstport
, mDNSInterfaceID InterfaceID
, TCPConnectionCallback callback
, void *context
)
1755 mStatus err
= mStatus_NoError
;
1756 struct sockaddr_storage ss
;
1758 sock
->callback
= callback
;
1759 sock
->context
= context
;
1760 sock
->setup
= mDNSfalse
;
1761 sock
->connected
= mDNSfalse
;
1762 sock
->handshake
= handshake_required
;
1763 sock
->err
= mStatus_NoError
;
1765 if (dst
->type
== mDNSAddrType_IPv4
)
1767 struct sockaddr_in
*saddr
= (struct sockaddr_in
*)&ss
;
1768 mDNSPlatformMemZero(saddr
, sizeof(*saddr
));
1769 saddr
->sin_family
= AF_INET
;
1770 saddr
->sin_port
= dstport
.NotAnInteger
;
1771 saddr
->sin_len
= sizeof(*saddr
);
1772 saddr
->sin_addr
.s_addr
= dst
->ip
.v4
.NotAnInteger
;
1776 struct sockaddr_in6
*saddr6
= (struct sockaddr_in6
*)&ss
;
1777 mDNSPlatformMemZero(saddr6
, sizeof(*saddr6
));
1778 saddr6
->sin6_family
= AF_INET6
;
1779 saddr6
->sin6_port
= dstport
.NotAnInteger
;
1780 saddr6
->sin6_len
= sizeof(*saddr6
);
1781 saddr6
->sin6_addr
= *(struct in6_addr
*)&dst
->ip
.v6
;
1784 // Watch for connect complete (write is ready)
1785 // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it in tcpKQSocketCallback using EV_DELETE
1786 if (KQueueSet(sock
->fd
, EV_ADD
/* | EV_ONESHOT */, EVFILT_WRITE
, &sock
->kqEntry
))
1788 LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed");
1792 if (fcntl(sock
->fd
, F_SETFL
, fcntl(sock
->fd
, F_GETFL
, 0) | O_NONBLOCK
) < 0) // set non-blocking
1794 LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno
));
1795 return mStatus_UnknownErr
;
1798 // We bind to the interface and all subsequent packets including the SYN will be sent out
1799 // on this interface
1801 // Note: If we are in Active Directory domain, we may try TCP (if the response can't fit in
1805 NetworkInterfaceInfoOSX
*info
= IfindexToInterfaceInfoOSX(InterfaceID
);
1806 if (dst
->type
== mDNSAddrType_IPv4
)
1809 if (info
) setsockopt(sock
->fd
, IPPROTO_IP
, IP_BOUND_IF
, &info
->scope_id
, sizeof(info
->scope_id
));
1810 else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID
); return mStatus_BadParamErr
; }
1812 (void)InterfaceID
; // Unused
1813 (void)info
; // Unused
1818 #ifdef IPV6_BOUND_IF
1819 if (info
) setsockopt(sock
->fd
, IPPROTO_IPV6
, IPV6_BOUND_IF
, &info
->scope_id
, sizeof(info
->scope_id
));
1820 else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID
); return mStatus_BadParamErr
; }
1822 (void)InterfaceID
; // Unused
1823 (void)info
; // Unused
1828 // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address
1829 // from which we can infer the destination address family. Hence we need to remember that here.
1830 // Instead of remembering the address family, we remember the right fd.
1831 sock
->fd
= sock
->fd
;
1832 // initiate connection wth peer
1833 if (connect(sock
->fd
, (struct sockaddr
*)&ss
, ss
.ss_len
) < 0)
1835 if (errno
== EINPROGRESS
) return mStatus_ConnPending
;
1836 if (errno
== EHOSTUNREACH
|| errno
== EADDRNOTAVAIL
|| errno
== ENETDOWN
)
1837 LogInfo("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s)", sock
->fd
, errno
, strerror(errno
));
1839 LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s) length %d", sock
->fd
, errno
, strerror(errno
), ss
.ss_len
);
1840 return mStatus_ConnFailed
;
1843 LogMsg("NOTE: mDNSPlatformTCPConnect completed synchronously");
1844 // kQueue should notify us, but this LogMsg is to help track down if it doesn't
1845 // Experimentation shows that even a connection to a local listener returns EINPROGRESS, so this
1846 // will likely never happen.
1851 // Replace the existing socket callback with a new one, or establish a callback where none was present.
1852 mDNSexport mStatus
mDNSPlatformTCPSocketSetCallback(TCPSocket
*sock
, TCPConnectionCallback callback
, void *context
)
1854 sock
->callback
= callback
;
1855 sock
->context
= context
;
1857 // dnsextd currently reaches into the TCPSocket structure layer to do its own thing; this won't work for
1858 // any code (e.g., the Discovery Proxy or Discovery Relay) that actually uses the mDNSPlatform layer as
1859 // an opaque layer. So for that code, we have this. dnsextd should probably be platformized if it's
1861 if (!sock
->callback
) {
1862 // Watch for incoming data
1863 if (KQueueSet(sock
->fd
, EV_ADD
, EVFILT_READ
, &sock
->kqEntry
))
1865 LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed");
1866 return mStatus_UnknownErr
;
1870 sock
->kqEntry
.KQcallback
= tcpKQSocketCallback
;
1871 sock
->kqEntry
.KQcontext
= sock
;
1872 sock
->kqEntry
.KQtask
= "mDNSPlatformTCPSocket";
1873 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1874 sock
->kqEntry
.readSource
= mDNSNULL
;
1875 sock
->kqEntry
.writeSource
= mDNSNULL
;
1876 sock
->kqEntry
.fdClosed
= mDNSfalse
;
1878 return mStatus_NoError
;
1881 // Why doesn't mDNSPlatformTCPAccept actually call accept() ?
1882 // mDNSPlatformTCPAccept is only called by dnsextd.c. It's called _after_ accept has returned
1883 // a connected socket. The purpose appears to be to allocate and initialize the TCPSocket structure
1884 // and set up TLS, if required for this connection. dnsextd appears to be the only thing in mDNSResponder
1885 // that accepts incoming TLS connections.
1886 mDNSexport TCPSocket
*mDNSPlatformTCPAccept(TCPSocketFlags flags
, int fd
)
1888 mStatus err
= mStatus_NoError
;
1890 TCPSocket
*sock
= (TCPSocket
*) callocL("TCPSocket/mDNSPlatformTCPAccept", sizeof(*sock
));
1891 if (!sock
) return(mDNSNULL
);
1894 sock
->flags
= flags
;
1896 if (flags
& kTCPSocketFlags_UseTLS
)
1898 #ifndef NO_SECURITYFRAMEWORK
1899 if (!ServerCerts
) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err
= mStatus_UnknownErr
; goto exit
; }
1901 err
= tlsSetupSock(sock
, kSSLServerSide
, kSSLStreamType
);
1902 if (err
) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err
); goto exit
; }
1904 err
= SSLSetCertificate(sock
->tlsContext
, ServerCerts
);
1905 if (err
) { LogMsg("ERROR: mDNSPlatformTCPAccept: SSLSetCertificate failed with error code: %d", err
); goto exit
; }
1907 err
= mStatus_UnsupportedErr
;
1908 #endif /* NO_SECURITYFRAMEWORK */
1910 #ifndef NO_SECURITYFRAMEWORK
1914 if (err
) { freeL("TCPSocket/mDNSPlatformTCPAccept", sock
); return(mDNSNULL
); }
1918 mDNSlocal
void tcpListenCallback(int fd
, __unused
short filter
, void *context
, __unused mDNSBool encounteredEOF
)
1920 TCPListener
*listener
= context
;
1923 sock
= mDNSPosixDoTCPListenCallback(fd
, listener
->addressType
, listener
->socketFlags
,
1924 listener
->callback
, listener
->context
);
1926 if (sock
!= mDNSNULL
)
1928 KQueueSet(sock
->fd
, EV_ADD
, EVFILT_READ
, &sock
->kqEntry
);
1930 sock
->kqEntry
.KQcallback
= tcpKQSocketCallback
;
1931 sock
->kqEntry
.KQcontext
= sock
;
1932 sock
->kqEntry
.KQtask
= "mDNSPlatformTCPListen";
1933 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1934 sock
->kqEntry
.readSource
= mDNSNULL
;
1935 sock
->kqEntry
.writeSource
= mDNSNULL
;
1936 sock
->kqEntry
.fdClosed
= mDNSfalse
;
1941 mDNSexport TCPListener
*mDNSPlatformTCPListen(mDNSAddr_Type addrtype
, mDNSIPPort
*port
, mDNSAddr
*addr
,
1942 TCPSocketFlags socketFlags
, mDNSBool reuseAddr
, int queueLength
,
1943 TCPAcceptedCallback callback
, void *context
)
1948 if (!mDNSPosixTCPListen(&fd
, addrtype
, port
, addr
, reuseAddr
, queueLength
)) {
1955 // Allocate a listener structure
1956 ret
= (TCPListener
*) mDNSPlatformMemAllocateClear(sizeof *ret
);
1957 if (ret
== mDNSNULL
)
1959 LogMsg("mDNSPlatformTCPListen: no memory for TCPListener struct.");
1964 ret
->callback
= callback
;
1965 ret
->context
= context
;
1966 ret
->socketFlags
= socketFlags
;
1968 // Watch for incoming data
1969 KQueueSet(ret
->fd
, EV_ADD
, EVFILT_READ
, &ret
->kqEntry
);
1970 ret
->kqEntry
.KQcallback
= tcpListenCallback
;
1971 ret
->kqEntry
.KQcontext
= ret
;
1972 ret
->kqEntry
.KQtask
= "mDNSPlatformTCPListen";
1973 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1974 ret
->kqEntry
.readSource
= mDNSNULL
;
1975 ret
->kqEntry
.writeSource
= mDNSNULL
;
1976 ret
->kqEntry
.fdClosed
= mDNSfalse
;
1981 mDNSexport mDNSu16
mDNSPlatformGetUDPPort(UDPSocket
*sock
)
1988 port
= sock
->ss
.port
.NotAnInteger
;
1993 mDNSlocal
void CloseSocketSet(KQSocketSet
*ss
)
1995 if (ss
->sktv4
!= -1)
1997 mDNSPlatformCloseFD(&ss
->kqsv4
, ss
->sktv4
);
2000 if (ss
->sktv6
!= -1)
2002 mDNSPlatformCloseFD(&ss
->kqsv6
, ss
->sktv6
);
2005 if (ss
->closeFlag
) *ss
->closeFlag
= 1;
2008 mDNSexport
void mDNSPlatformTCPCloseConnection(TCPSocket
*sock
)
2012 #ifndef NO_SECURITYFRAMEWORK
2013 if (sock
->tlsContext
)
2015 if (sock
->handshake
== handshake_in_progress
) // SSLHandshake thread using this sock (esp. tlsContext)
2017 LogInfo("mDNSPlatformTCPCloseConnection: called while handshake in progress");
2018 // When we come back from SSLHandshake, we will notice that a close was here and
2019 // call this function again which will do the cleanup then.
2020 sock
->handshake
= handshake_to_be_closed
;
2024 SSLClose(sock
->tlsContext
);
2025 CFRelease(sock
->tlsContext
);
2026 sock
->tlsContext
= NULL
;
2028 #endif /* NO_SECURITYFRAMEWORK */
2029 if (sock
->fd
!= -1) {
2030 shutdown(sock
->fd
, 2);
2031 mDNSPlatformCloseFD(&sock
->kqEntry
, sock
->fd
);
2035 freeL("TCPSocket/mDNSPlatformTCPCloseConnection", sock
);
2039 mDNSexport
long mDNSPlatformReadTCP(TCPSocket
*sock
, void *buf
, unsigned long buflen
, mDNSBool
*closed
)
2042 *closed
= mDNSfalse
;
2044 // We can get here if the caller set up a TCP connection but didn't check the status when it got the
2046 if (!sock
->connected
) {
2047 return mStatus_DefunctConnection
;
2050 if (sock
->flags
& kTCPSocketFlags_UseTLS
)
2052 #ifndef NO_SECURITYFRAMEWORK
2053 if (sock
->handshake
== handshake_required
) { LogMsg("mDNSPlatformReadTCP called while handshake required"); return 0; }
2054 else if (sock
->handshake
== handshake_in_progress
) return 0;
2055 else if (sock
->handshake
!= handshake_completed
) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock
->handshake
);
2057 //LogMsg("Starting SSLRead %d %X", sock->fd, fcntl(sock->fd, F_GETFL, 0));
2058 mStatus err
= SSLRead(sock
->tlsContext
, buf
, buflen
, (size_t *)&nread
);
2059 //LogMsg("SSLRead returned %d (%d) nread %d buflen %d", err, errSSLWouldBlock, nread, buflen);
2060 if (err
== errSSLClosedGraceful
) { nread
= 0; *closed
= mDNStrue
; }
2061 else if (err
&& err
!= errSSLWouldBlock
)
2062 { LogMsg("ERROR: mDNSPlatformReadTCP - SSLRead: %d", err
); nread
= -1; *closed
= mDNStrue
; }
2066 #endif /* NO_SECURITYFRAMEWORK */
2070 nread
= mDNSPosixReadTCP(sock
->fd
, buf
, buflen
, closed
);
2076 mDNSexport
long mDNSPlatformWriteTCP(TCPSocket
*sock
, const char *msg
, unsigned long len
)
2080 if (!sock
->connected
) {
2081 return mStatus_DefunctConnection
;
2084 if (sock
->flags
& kTCPSocketFlags_UseTLS
)
2086 #ifndef NO_SECURITYFRAMEWORK
2088 if (sock
->handshake
== handshake_required
) { LogMsg("mDNSPlatformWriteTCP called while handshake required"); return 0; }
2089 if (sock
->handshake
== handshake_in_progress
) return 0;
2090 else if (sock
->handshake
!= handshake_completed
) LogMsg("mDNSPlatformWriteTCP called with unexpected SSLHandshake status: %d", sock
->handshake
);
2092 mStatus err
= SSLWrite(sock
->tlsContext
, msg
, len
, &processed
);
2094 if (!err
) nsent
= (int) processed
;
2095 else if (err
== errSSLWouldBlock
) nsent
= 0;
2096 else { LogMsg("ERROR: mDNSPlatformWriteTCP - SSLWrite returned %d", err
); nsent
= -1; }
2099 #endif /* NO_SECURITYFRAMEWORK */
2103 nsent
= mDNSPosixWriteTCP(sock
->fd
, msg
, len
);
2108 mDNSexport
int mDNSPlatformTCPGetFD(TCPSocket
*sock
)
2113 // This function checks to see if the socket is writable. It will be writable if the kernel TCP output
2114 // buffer is less full than TCP_NOTSENT_LOWAT. This should be half or less of the actual kernel buffer
2115 // size. This check is done in cases where data should be written if there's space, for example in the
2116 // Discovery Relay code, where we may be receiving mDNS messages at arbitrary times, and generally there
2117 // should be buffer space to relay them, but in exceptional cases there might not be. In this case it's
2119 mDNSexport mDNSBool
mDNSPlatformTCPWritable(TCPSocket
*sock
)
2122 struct kevent kin
, kout
;
2128 LogMsg("ERROR: kqueue failed: %m");
2133 EV_SET(&kin
, sock
->fd
, EVFILT_WRITE
, EV_ADD
, 0, 0, 0);
2134 count
= kevent(kfd
, &kin
, 1, &kout
, 1, &ts
);
2136 if (count
== 1 && (int)kout
.ident
== sock
->fd
&& kout
.filter
== EVFILT_WRITE
)
2143 // If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface
2144 // If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries
2145 mDNSlocal mStatus
SetupSocket(KQSocketSet
*cp
, const mDNSIPPort port
, u_short sa_family
, mDNSIPPort
*const outport
)
2147 int *s
= (sa_family
== AF_INET
) ? &cp
->sktv4
: &cp
->sktv6
;
2148 KQueueEntry
*k
= (sa_family
== AF_INET
) ? &cp
->kqsv4
: &cp
->kqsv6
;
2150 const int twofivefive
= 255;
2151 mStatus err
= mStatus_NoError
;
2152 char *errstr
= mDNSNULL
;
2156 cp
->closeFlag
= mDNSNULL
;
2158 int skt
= socket(sa_family
, SOCK_DGRAM
, IPPROTO_UDP
);
2159 if (skt
< 3) { if (errno
!= EAFNOSUPPORT
) LogMsg("SetupSocket: socket error %d errno %d (%s)", skt
, errno
, strerror(errno
));return(skt
); }
2161 // set default traffic class
2162 setTrafficClass(skt
, mDNSfalse
);
2164 #ifdef SO_RECV_ANYIF
2165 // Enable inbound packets on IFEF_AWDL interface.
2166 // Only done for multicast sockets, since we don't expect unicast socket operations
2167 // on the IFEF_AWDL interface. Operation is a no-op for other interface types.
2168 if (mDNSSameIPPort(port
, MulticastDNSPort
))
2170 err
= setsockopt(skt
, SOL_SOCKET
, SO_RECV_ANYIF
, &on
, sizeof(on
));
2171 if (err
< 0) { errstr
= "setsockopt - SO_RECV_ANYIF"; goto fail
; }
2173 #endif // SO_RECV_ANYIF
2175 // ... with a shared UDP port, if it's for multicast receiving
2176 if (mDNSSameIPPort(port
, MulticastDNSPort
) || mDNSSameIPPort(port
, NATPMPAnnouncementPort
))
2178 err
= setsockopt(skt
, SOL_SOCKET
, SO_REUSEPORT
, &on
, sizeof(on
));
2179 if (err
< 0) { errstr
= "setsockopt - SO_REUSEPORT"; goto fail
; }
2182 // Don't want to wake from sleep for inbound packets on the mDNS sockets
2183 if (mDNSSameIPPort(port
, MulticastDNSPort
))
2186 if (setsockopt(skt
, SOL_SOCKET
, SO_NOWAKEFROMSLEEP
, &nowake
, sizeof(nowake
)) == -1)
2187 LogInfo("SetupSocket: SO_NOWAKEFROMSLEEP failed %s", strerror(errno
));
2190 if (sa_family
== AF_INET
)
2192 // We want to receive destination addresses
2193 err
= setsockopt(skt
, IPPROTO_IP
, IP_RECVDSTADDR
, &on
, sizeof(on
));
2194 if (err
< 0) { errstr
= "setsockopt - IP_RECVDSTADDR"; goto fail
; }
2196 // We want to receive interface identifiers
2197 err
= setsockopt(skt
, IPPROTO_IP
, IP_RECVIF
, &on
, sizeof(on
));
2198 if (err
< 0) { errstr
= "setsockopt - IP_RECVIF"; goto fail
; }
2200 // We want to receive packet TTL value so we can check it
2201 err
= setsockopt(skt
, IPPROTO_IP
, IP_RECVTTL
, &on
, sizeof(on
));
2202 if (err
< 0) { errstr
= "setsockopt - IP_RECVTTL"; goto fail
; }
2204 // Send unicast packets with TTL 255
2205 err
= setsockopt(skt
, IPPROTO_IP
, IP_TTL
, &twofivefive
, sizeof(twofivefive
));
2206 if (err
< 0) { errstr
= "setsockopt - IP_TTL"; goto fail
; }
2208 // And multicast packets with TTL 255 too
2209 err
= setsockopt(skt
, IPPROTO_IP
, IP_MULTICAST_TTL
, &twofivefive
, sizeof(twofivefive
));
2210 if (err
< 0) { errstr
= "setsockopt - IP_MULTICAST_TTL"; goto fail
; }
2212 // And start listening for packets
2213 struct sockaddr_in listening_sockaddr
;
2214 listening_sockaddr
.sin_family
= AF_INET
;
2215 listening_sockaddr
.sin_port
= port
.NotAnInteger
; // Pass in opaque ID without any byte swapping
2216 listening_sockaddr
.sin_addr
.s_addr
= mDNSSameIPPort(port
, NATPMPAnnouncementPort
) ? AllHosts_v4
.NotAnInteger
: 0;
2217 err
= bind(skt
, (struct sockaddr
*) &listening_sockaddr
, sizeof(listening_sockaddr
));
2218 if (err
) { errstr
= "bind"; goto fail
; }
2219 if (outport
) outport
->NotAnInteger
= listening_sockaddr
.sin_port
;
2221 else if (sa_family
== AF_INET6
)
2223 // NAT-PMP Announcements make no sense on IPv6, and we don't support IPv6 for PCP, so bail early w/o error
2224 if (mDNSSameIPPort(port
, NATPMPAnnouncementPort
)) { if (outport
) *outport
= zeroIPPort
; close(skt
); return mStatus_NoError
; }
2226 // We want to receive destination addresses and receive interface identifiers
2227 err
= setsockopt(skt
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &on
, sizeof(on
));
2228 if (err
< 0) { errstr
= "setsockopt - IPV6_RECVPKTINFO"; goto fail
; }
2230 // We want to receive packet hop count value so we can check it
2231 err
= setsockopt(skt
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &on
, sizeof(on
));
2232 if (err
< 0) { errstr
= "setsockopt - IPV6_RECVHOPLIMIT"; goto fail
; }
2234 // We want to receive only IPv6 packets. Without this option we get IPv4 packets too,
2235 // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address
2236 err
= setsockopt(skt
, IPPROTO_IPV6
, IPV6_V6ONLY
, &on
, sizeof(on
));
2237 if (err
< 0) { errstr
= "setsockopt - IPV6_V6ONLY"; goto fail
; }
2239 // Send unicast packets with TTL 255
2240 err
= setsockopt(skt
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &twofivefive
, sizeof(twofivefive
));
2241 if (err
< 0) { errstr
= "setsockopt - IPV6_UNICAST_HOPS"; goto fail
; }
2243 // And multicast packets with TTL 255 too
2244 err
= setsockopt(skt
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &twofivefive
, sizeof(twofivefive
));
2245 if (err
< 0) { errstr
= "setsockopt - IPV6_MULTICAST_HOPS"; goto fail
; }
2247 // Want to receive our own packets
2248 err
= setsockopt(skt
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &on
, sizeof(on
));
2249 if (err
< 0) { errstr
= "setsockopt - IPV6_MULTICAST_LOOP"; goto fail
; }
2251 // Disable default option to send mDNSv6 packets at min IPv6 MTU: RFC 3542, Sec 11
2252 err
= setsockopt(skt
, IPPROTO_IPV6
, IPV6_USE_MIN_MTU
, &mtu
, sizeof(mtu
));
2253 if (err
< 0) // Since it is an optimization if we fail just log the err, no need to close the skt
2254 LogMsg("SetupSocket: setsockopt - IPV6_USE_MIN_MTU: IP6PO_MINMTU_DISABLE socket %d err %d errno %d (%s)",
2255 skt
, err
, errno
, strerror(errno
));
2257 // And start listening for packets
2258 struct sockaddr_in6 listening_sockaddr6
;
2259 mDNSPlatformMemZero(&listening_sockaddr6
, sizeof(listening_sockaddr6
));
2260 listening_sockaddr6
.sin6_len
= sizeof(listening_sockaddr6
);
2261 listening_sockaddr6
.sin6_family
= AF_INET6
;
2262 listening_sockaddr6
.sin6_port
= port
.NotAnInteger
; // Pass in opaque ID without any byte swapping
2263 listening_sockaddr6
.sin6_flowinfo
= 0;
2264 listening_sockaddr6
.sin6_addr
= in6addr_any
; // Want to receive multicasts AND unicasts on this socket
2265 listening_sockaddr6
.sin6_scope_id
= 0;
2266 err
= bind(skt
, (struct sockaddr
*) &listening_sockaddr6
, sizeof(listening_sockaddr6
));
2267 if (err
) { errstr
= "bind"; goto fail
; }
2268 if (outport
) outport
->NotAnInteger
= listening_sockaddr6
.sin6_port
;
2271 fcntl(skt
, F_SETFL
, fcntl(skt
, F_GETFL
, 0) | O_NONBLOCK
); // set non-blocking
2272 fcntl(skt
, F_SETFD
, 1); // set close-on-exec
2274 k
->KQcallback
= myKQSocketCallBack
;
2276 k
->KQtask
= "UDP packet reception";
2277 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2278 k
->readSource
= mDNSNULL
;
2279 k
->writeSource
= mDNSNULL
;
2280 k
->fdClosed
= mDNSfalse
;
2282 KQueueSet(*s
, EV_ADD
, EVFILT_READ
, k
);
2284 return(mStatus_NoError
);
2287 saved_errno
= errno
;
2288 // For "bind" failures, only write log messages for our shared mDNS port, or for binding to zero
2289 if (strcmp(errstr
, "bind") || mDNSSameIPPort(port
, MulticastDNSPort
) || mDNSIPPortIsZero(port
))
2290 LogMsg("%s skt %d port %d error %d errno %d (%s)", errstr
, skt
, mDNSVal16(port
), err
, saved_errno
, strerror(saved_errno
));
2292 // If we got a "bind" failure of EADDRINUSE, inform the caller as it might need to try another random port
2293 if (!strcmp(errstr
, "bind") && saved_errno
== EADDRINUSE
)
2296 if (mDNSSameIPPort(port
, MulticastDNSPort
))
2297 NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed",
2298 "Congratulations, you've reproduced an elusive bug.\r"
2299 "Please contact the current assignee of <rdar://problem/3814904>.\r"
2300 "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r"
2301 "If possible, please leave your machine undisturbed so that someone can come to investigate the problem.");
2304 mDNSPlatformCloseFD(k
, skt
);
2308 mDNSexport UDPSocket
*mDNSPlatformUDPSocket(const mDNSIPPort requestedport
)
2311 mDNSIPPort port
= requestedport
;
2312 mDNSBool randomizePort
= mDNSIPPortIsZero(requestedport
);
2313 int i
= 10000; // Try at most 10000 times to get a unique random port
2314 UDPSocket
*p
= (UDPSocket
*) callocL("UDPSocket", sizeof(*p
));
2315 if (!p
) { LogMsg("mDNSPlatformUDPSocket: memory exhausted"); return(mDNSNULL
); }
2316 p
->ss
.port
= zeroIPPort
;
2317 p
->ss
.m
= &mDNSStorage
;
2320 p
->ss
.proxy
= mDNSfalse
;
2324 // The kernel doesn't do cryptographically strong random port allocation, so we do it ourselves here
2325 if (randomizePort
) port
= mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF));
2326 err
= SetupSocket(&p
->ss
, port
, AF_INET
, &p
->ss
.port
);
2329 err
= SetupSocket(&p
->ss
, port
, AF_INET6
, &p
->ss
.port
);
2330 if (err
) { mDNSPlatformCloseFD(&p
->ss
.kqsv4
, p
->ss
.sktv4
); p
->ss
.sktv4
= -1; }
2333 } while (err
== EADDRINUSE
&& randomizePort
&& i
);
2337 // In customer builds we don't want to log failures with port 5351, because this is a known issue
2338 // of failing to bind to this port when Internet Sharing has already bound to it
2339 // We also don't want to log about port 5350, due to a known bug when some other
2340 // process is bound to it.
2341 if (mDNSSameIPPort(requestedport
, NATPMPPort
) || mDNSSameIPPort(requestedport
, NATPMPAnnouncementPort
))
2342 LogInfo("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport
), err
, errno
, strerror(errno
));
2343 else LogMsg("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport
), err
, errno
, strerror(errno
));
2344 freeL("UDPSocket", p
);
2353 mDNSexport
void mDNSPlatformUDPClose(UDPSocket
*sock
)
2355 CloseSocketSet(&sock
->ss
);
2356 freeL("UDPSocket", sock
);
2360 mDNSexport mDNSBool
mDNSPlatformUDPSocketEncounteredEOF(const UDPSocket
*sock
)
2362 return (sock
->ss
.sktv4EOF
|| sock
->ss
.sktv6EOF
);
2365 #if COMPILER_LIKES_PRAGMA_MARK
2367 #pragma mark - BPF Raw packet sending/receiving
2370 #if APPLE_OSX_mDNSResponder
2372 mDNSexport
void mDNSPlatformSendRawPacket(const void *const msg
, const mDNSu8
*const end
, mDNSInterfaceID InterfaceID
)
2374 if (!InterfaceID
) { LogMsg("mDNSPlatformSendRawPacket: No InterfaceID specified"); return; }
2375 NetworkInterfaceInfoOSX
*info
;
2377 info
= IfindexToInterfaceInfoOSX(InterfaceID
);
2380 LogMsg("mDNSPlatformSendRawPacket: Invalid interface index %p", InterfaceID
);
2383 if (info
->BPF_fd
< 0)
2384 LogMsg("mDNSPlatformSendRawPacket: %s BPF_fd %d not ready", info
->ifinfo
.ifname
, info
->BPF_fd
);
2387 //LogMsg("mDNSPlatformSendRawPacket %d bytes on %s", end - (mDNSu8 *)msg, info->ifinfo.ifname);
2388 if (write(info
->BPF_fd
, msg
, end
- (mDNSu8
*)msg
) < 0)
2389 LogMsg("mDNSPlatformSendRawPacket: BPF write(%d) failed %d (%s)", info
->BPF_fd
, errno
, strerror(errno
));
2393 mDNSexport
void mDNSPlatformSetLocalAddressCacheEntry(const mDNSAddr
*const tpa
, const mDNSEthAddr
*const tha
, mDNSInterfaceID InterfaceID
)
2395 if (!InterfaceID
) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: No InterfaceID specified"); return; }
2396 NetworkInterfaceInfoOSX
*info
;
2397 info
= IfindexToInterfaceInfoOSX(InterfaceID
);
2398 if (info
== NULL
) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID
); return; }
2399 // Manually inject an entry into our local ARP cache.
2400 // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.)
2401 if (!mDNS_AddressIsLocalSubnet(&mDNSStorage
, InterfaceID
, tpa
))
2402 LogSPS("Don't need address cache entry for %s %#a %.6a", info
->ifinfo
.ifname
, tpa
, tha
);
2405 int result
= mDNSSetLocalAddressCacheEntry(info
->scope_id
, tpa
->type
, tpa
->ip
.v6
.b
, tha
->b
);
2406 if (result
) LogMsg("Set local address cache entry for %s %#a %.6a failed: %d", info
->ifinfo
.ifname
, tpa
, tha
, result
);
2407 else LogSPS("Set local address cache entry for %s %#a %.6a", info
->ifinfo
.ifname
, tpa
, tha
);
2411 mDNSlocal
void CloseBPF(NetworkInterfaceInfoOSX
*const i
)
2413 LogSPS("%s closing BPF fd %d", i
->ifinfo
.ifname
, i
->BPF_fd
);
2414 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2415 // close will happen in the cancel handler
2416 dispatch_source_cancel(i
->BPF_source
);
2419 // Note: MUST NOT close() the underlying native BSD sockets.
2420 // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately, because
2421 // it first has to unhook the sockets from its select() call on its other thread, before it can safely close them.
2422 CFRunLoopRemoveSource(CFRunLoopGetMain(), i
->BPF_rls
, kCFRunLoopDefaultMode
);
2423 CFRelease(i
->BPF_rls
);
2424 CFSocketInvalidate(i
->BPF_cfs
);
2425 CFRelease(i
->BPF_cfs
);
2428 if (i
->BPF_mcfd
>= 0) { close(i
->BPF_mcfd
); i
->BPF_mcfd
= -1; }
2431 mDNSlocal
void bpf_callback_common(NetworkInterfaceInfoOSX
*info
)
2435 // Now we've got the lock, make sure the kqueue thread didn't close the fd out from under us (will not be a problem once the OS X
2436 // kernel has a mechanism for dispatching all events to a single thread, but for now we have to guard against this race condition).
2437 if (info
->BPF_fd
< 0) goto exit
;
2439 ssize_t n
= read(info
->BPF_fd
, &info
->m
->imsg
, info
->BPF_len
);
2440 const mDNSu8
*ptr
= (const mDNSu8
*)&info
->m
->imsg
;
2441 const mDNSu8
*end
= (const mDNSu8
*)&info
->m
->imsg
+ n
;
2442 debugf("%3d: bpf_callback got %d bytes on %s", info
->BPF_fd
, n
, info
->ifinfo
.ifname
);
2446 /* <rdar://problem/10287386>
2447 * sometimes there can be a race condition btw when the bpf socket
2448 * gets data and the callback get scheduled and when we call BIOCSETF (which
2449 * clears the socket). this can cause the read to hang for a really long time
2450 * and effectively prevent us from responding to requests for long periods of time.
2451 * to prevent this make the socket non blocking and just bail if we dont get anything
2453 if (errno
== EAGAIN
)
2455 LogMsg("bpf_callback got EAGAIN bailing");
2458 LogMsg("Closing %s BPF fd %d due to error %d (%s)", info
->ifinfo
.ifname
, info
->BPF_fd
, errno
, strerror(errno
));
2465 const struct bpf_hdr
*const bh
= (const struct bpf_hdr
*)ptr
;
2466 debugf("%3d: bpf_callback ptr %p bh_hdrlen %d data %p bh_caplen %4d bh_datalen %4d next %p remaining %4d",
2467 info
->BPF_fd
, ptr
, bh
->bh_hdrlen
, ptr
+ bh
->bh_hdrlen
, bh
->bh_caplen
, bh
->bh_datalen
,
2468 ptr
+ BPF_WORDALIGN(bh
->bh_hdrlen
+ bh
->bh_caplen
), end
- (ptr
+ BPF_WORDALIGN(bh
->bh_hdrlen
+ bh
->bh_caplen
)));
2469 // Note that BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header.
2470 // Given that An Ethernet header is 14 bytes, this means that if the network layer header (e.g. IP header,
2471 // ARP message, etc.) is 4-byte aligned, then necessarily the Ethernet header will be NOT be 4-byte aligned.
2472 mDNSCoreReceiveRawPacket(info
->m
, ptr
+ bh
->bh_hdrlen
, ptr
+ bh
->bh_hdrlen
+ bh
->bh_caplen
, info
->ifinfo
.InterfaceID
);
2473 ptr
+= BPF_WORDALIGN(bh
->bh_hdrlen
+ bh
->bh_caplen
);
2476 KQueueUnlock("bpf_callback");
2478 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2479 mDNSlocal
void bpf_callback_dispatch(NetworkInterfaceInfoOSX
*const info
)
2481 bpf_callback_common(info
);
2484 mDNSlocal
void bpf_callback(const CFSocketRef cfs
, const CFSocketCallBackType CallBackType
, const CFDataRef address
, const void *const data
, void *const context
)
2490 bpf_callback_common((NetworkInterfaceInfoOSX
*)context
);
2494 mDNSexport
void mDNSPlatformSendKeepalive(mDNSAddr
*sadd
, mDNSAddr
*dadd
, mDNSIPPort
*lport
, mDNSIPPort
*rport
, mDNSu32 seq
, mDNSu32 ack
, mDNSu16 win
)
2496 LogMsg("mDNSPlatformSendKeepalive called\n");
2497 mDNSSendKeepalive(sadd
->ip
.v6
.b
, dadd
->ip
.v6
.b
, lport
->NotAnInteger
, rport
->NotAnInteger
, seq
, ack
, win
);
2500 mDNSexport mStatus
mDNSPlatformClearSPSData(void)
2502 CFStringRef spsAddressKey
= NULL
;
2503 CFStringRef ownerOPTRecKey
= NULL
;
2504 SCDynamicStoreRef addrStore
= SCDynamicStoreCreate(NULL
, CFSTR("mDNSResponder:SPSAddresses"), NULL
, NULL
);
2505 SCDynamicStoreRef optStore
= SCDynamicStoreCreate(NULL
, CFSTR("mDNSResponder:SPSOPTRecord"), NULL
, NULL
);
2507 spsAddressKey
= SCDynamicStoreKeyCreateNetworkInterfaceEntity (kCFAllocatorDefault
, kSCDynamicStoreDomainState
, kSCCompAnyRegex
, CFSTR("BonjourSleepProxyAddress"));
2508 if (spsAddressKey
!= NULL
)
2510 CFArrayRef keyList
= SCDynamicStoreCopyKeyList(addrStore
, spsAddressKey
);
2511 if (keyList
!= NULL
)
2513 if (SCDynamicStoreSetMultiple(addrStore
, NULL
, keyList
, NULL
) == false)
2514 LogSPS("mDNSPlatformClearSPSData: Unable to remove %s : error %s", CFStringGetCStringPtr( spsAddressKey
, kCFStringEncodingASCII
), SCErrorString(SCError()));
2516 if (keyList
) CFRelease(keyList
);
2518 ownerOPTRecKey
= SCDynamicStoreKeyCreateNetworkInterfaceEntity (kCFAllocatorDefault
, kSCDynamicStoreDomainState
, kSCCompAnyRegex
, CFSTR("BonjourSleepProxyOPTRecord"));
2519 if(ownerOPTRecKey
!= NULL
)
2521 CFArrayRef keyList
= SCDynamicStoreCopyKeyList(addrStore
, ownerOPTRecKey
);
2522 if (keyList
!= NULL
)
2524 if (SCDynamicStoreSetMultiple(optStore
, NULL
, keyList
, NULL
) == false)
2525 LogSPS("mDNSPlatformClearSPSData: Unable to remove %s : error %s", CFStringGetCStringPtr(ownerOPTRecKey
, kCFStringEncodingASCII
), SCErrorString(SCError()));
2527 if (keyList
) CFRelease(keyList
);
2530 if (addrStore
) CFRelease(addrStore
);
2531 if (optStore
) CFRelease(optStore
);
2532 if (spsAddressKey
) CFRelease(spsAddressKey
);
2533 if (ownerOPTRecKey
) CFRelease(ownerOPTRecKey
);
2534 return KERN_SUCCESS
;
2537 mDNSlocal
int getMACAddress(int family
, v6addr_t raddr
, v6addr_t gaddr
, int *gfamily
, ethaddr_t eth
)
2541 struct rt_msghdr m_rtm
;
2545 struct rt_msghdr
*rtm
= &(m_rtmsg
.m_rtm
);
2546 char *cp
= m_rtmsg
.m_space
;
2547 int seq
= 6367, sock
, rlen
, i
;
2548 struct sockaddr_in
*sin
= NULL
;
2549 struct sockaddr_in6
*sin6
= NULL
;
2550 struct sockaddr_dl
*sdl
= NULL
;
2551 struct sockaddr_storage sins
;
2552 struct sockaddr_dl sdl_m
;
2554 #define NEXTADDR(w, s, len) \
2555 if (rtm->rtm_addrs & (w)) \
2557 bcopy((char *)s, cp, len); \
2561 bzero(&sins
, sizeof(struct sockaddr_storage
));
2562 bzero(&sdl_m
, sizeof(struct sockaddr_dl
));
2563 bzero((char *)&m_rtmsg
, sizeof(m_rtmsg
));
2565 sock
= socket(PF_ROUTE
, SOCK_RAW
, 0);
2568 const int socket_errno
= errno
;
2569 LogMsg("getMACAddress: Can not open the socket - %s", strerror(socket_errno
));
2570 return socket_errno
;
2573 rtm
->rtm_addrs
|= RTA_DST
| RTA_GATEWAY
;
2574 rtm
->rtm_type
= RTM_GET
;
2576 rtm
->rtm_version
= RTM_VERSION
;
2577 rtm
->rtm_seq
= ++seq
;
2579 sdl_m
.sdl_len
= sizeof(sdl_m
);
2580 sdl_m
.sdl_family
= AF_LINK
;
2581 if (family
== AF_INET
)
2583 sin
= (struct sockaddr_in
*)&sins
;
2584 sin
->sin_family
= AF_INET
;
2585 sin
->sin_len
= sizeof(struct sockaddr_in
);
2586 memcpy(&sin
->sin_addr
, raddr
, sizeof(struct in_addr
));
2587 NEXTADDR(RTA_DST
, sin
, sin
->sin_len
);
2589 else if (family
== AF_INET6
)
2591 sin6
= (struct sockaddr_in6
*)&sins
;
2592 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
2593 sin6
->sin6_family
= AF_INET6
;
2594 memcpy(&sin6
->sin6_addr
, raddr
, sizeof(struct in6_addr
));
2595 NEXTADDR(RTA_DST
, sin6
, sin6
->sin6_len
);
2597 NEXTADDR(RTA_GATEWAY
, &sdl_m
, sdl_m
.sdl_len
);
2598 rtm
->rtm_msglen
= rlen
= cp
- (char *)&m_rtmsg
;
2600 if (write(sock
, (char *)&m_rtmsg
, rlen
) < 0)
2602 const int write_errno
= errno
;
2603 LogMsg("getMACAddress: writing to routing socket: %s", strerror(write_errno
));
2610 rlen
= read(sock
, (char *)&m_rtmsg
, sizeof(m_rtmsg
));
2612 while (rlen
> 0 && (rtm
->rtm_seq
!= seq
|| rtm
->rtm_pid
!= getpid()));
2615 LogMsg("getMACAddress: Read from routing socket failed");
2617 if (family
== AF_INET
)
2619 sin
= (struct sockaddr_in
*) (rtm
+ 1);
2620 sdl
= (struct sockaddr_dl
*) (sin
->sin_len
+ (char *) sin
);
2622 else if (family
== AF_INET6
)
2624 sin6
= (struct sockaddr_in6
*) (rtm
+1);
2625 sdl
= (struct sockaddr_dl
*) (sin6
->sin6_len
+ (char *) sin6
);
2630 LogMsg("getMACAddress: sdl is NULL for family %d", family
);
2635 // If the address is not on the local net, we get the IP address of the gateway.
2636 // We would have to repeat the process to get the MAC address of the gateway
2637 *gfamily
= sdl
->sdl_family
;
2638 if (sdl
->sdl_family
== AF_INET
)
2642 struct sockaddr_in
*new_sin
= (struct sockaddr_in
*)(sin
->sin_len
+(char*) sin
);
2643 memcpy(gaddr
, &new_sin
->sin_addr
, sizeof(struct in_addr
));
2647 LogMsg("getMACAddress: sin is NULL");
2652 else if (sdl
->sdl_family
== AF_INET6
)
2656 struct sockaddr_in6
*new_sin6
= (struct sockaddr_in6
*)(sin6
->sin6_len
+(char*) sin6
);
2657 memcpy(gaddr
, &new_sin6
->sin6_addr
, sizeof(struct in6_addr
));
2661 LogMsg("getMACAddress: sin6 is NULL");
2667 unsigned char *ptr
= (unsigned char *)LLADDR(sdl
);
2668 for (i
= 0; i
< ETHER_ADDR_LEN
; i
++)
2669 (eth
)[i
] = *(ptr
+i
);
2673 return KERN_SUCCESS
;
2676 mDNSlocal
int GetRemoteMacinternal(int family
, v6addr_t raddr
, ethaddr_t eth
)
2685 ret
= getMACAddress(family
, raddr
, gateway
, &gfamily
, eth
);
2688 memcpy(raddr
, gateway
, (gfamily
== AF_INET
) ? 4 : 16);
2693 while ((ret
== -1) && (count
< 5));
2697 mDNSlocal
int StoreSPSMACAddressinternal(int family
, v6addr_t spsaddr
, const char *ifname
)
2700 char spsip
[INET6_ADDRSTRLEN
];
2702 CFStringRef sckey
= NULL
;
2703 SCDynamicStoreRef store
= SCDynamicStoreCreate(NULL
, CFSTR("mDNSResponder:StoreSPSMACAddress"), NULL
, NULL
);
2704 SCDynamicStoreRef ipstore
= SCDynamicStoreCreate(NULL
, CFSTR("mDNSResponder:GetIPv6Addresses"), NULL
, NULL
);
2705 CFMutableDictionaryRef dict
= NULL
;
2706 CFStringRef entityname
= NULL
;
2707 CFDictionaryRef ipdict
= NULL
;
2708 CFArrayRef addrs
= NULL
;
2710 if ((store
== NULL
) || (ipstore
== NULL
))
2712 LogMsg("StoreSPSMACAddressinternal: Unable to accesss SC Dynamic Store");
2717 // Get the MAC address of the Sleep Proxy Server
2718 memset(eth
, 0, sizeof(eth
));
2719 ret
= GetRemoteMacinternal(family
, spsaddr
, eth
);
2722 LogMsg("StoreSPSMACAddressinternal: Failed to determine the MAC address");
2726 // Create/Update the dynamic store entry for the specified interface
2727 sckey
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s%s%s"), "State:/Network/Interface/", ifname
, "/BonjourSleepProxyAddress");
2728 dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2731 LogMsg("StoreSPSMACAddressinternal: SPSCreateDict() Could not create CFDictionary dict");
2736 CFStringRef macaddr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%02x:%02x:%02x:%02x:%02x:%02x"), eth
[0], eth
[1], eth
[2], eth
[3], eth
[4], eth
[5]);
2737 CFDictionarySetValue(dict
, CFSTR("MACAddress"), macaddr
);
2738 if (NULL
!= macaddr
)
2741 if( NULL
== inet_ntop(family
, (void *)spsaddr
, spsip
, sizeof(spsip
)))
2743 LogMsg("StoreSPSMACAddressinternal: inet_ntop failed: %s", strerror(errno
));
2748 CFStringRef ipaddr
= CFStringCreateWithCString(NULL
, spsip
, kCFStringEncodingUTF8
);
2749 CFDictionarySetValue(dict
, CFSTR("IPAddress"), ipaddr
);
2753 // Get the current IPv6 addresses on this interface and store them so NAs can be sent on wakeup
2754 if ((entityname
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("State:/Network/Interface/%s/IPv6"), ifname
)) != NULL
)
2756 if ((ipdict
= SCDynamicStoreCopyValue(ipstore
, entityname
)) != NULL
)
2758 if((addrs
= CFDictionaryGetValue(ipdict
, CFSTR("Addresses"))) != NULL
)
2760 addrs
= CFRetain(addrs
);
2761 CFDictionarySetValue(dict
, CFSTR("RegisteredAddresses"), addrs
);
2765 SCDynamicStoreSetValue(store
, sckey
, dict
);
2768 if (store
) CFRelease(store
);
2769 if (ipstore
) CFRelease(ipstore
);
2770 if (sckey
) CFRelease(sckey
);
2771 if (dict
) CFRelease(dict
);
2772 if (ipdict
) CFRelease(ipdict
);
2773 if (entityname
) CFRelease(entityname
);
2774 if (addrs
) CFRelease(addrs
);
2779 mDNSlocal
void mDNSStoreSPSMACAddress(int family
, v6addr_t spsaddr
, char *ifname
)
2787 mDNSPlatformMemCopy(addr
.saddr
, spsaddr
, sizeof(v6addr_t
));
2789 err
= StoreSPSMACAddressinternal(family
, (uint8_t *)addr
.saddr
, ifname
);
2791 LogMsg("mDNSStoreSPSMACAddress : failed");
2794 mDNSexport mStatus
mDNSPlatformStoreSPSMACAddr(mDNSAddr
*spsaddr
, char *ifname
)
2796 int family
= (spsaddr
->type
== mDNSAddrType_IPv4
) ? AF_INET
: AF_INET6
;
2798 LogInfo("mDNSPlatformStoreSPSMACAddr : Storing %#a on interface %s", spsaddr
, ifname
);
2799 mDNSStoreSPSMACAddress(family
, spsaddr
->ip
.v6
.b
, ifname
);
2801 return KERN_SUCCESS
;
2805 mDNSexport mStatus
mDNSPlatformStoreOwnerOptRecord(char *ifname
, DNSMessage
* msg
, int length
)
2808 CFStringRef sckey
= NULL
;
2809 SCDynamicStoreRef store
= SCDynamicStoreCreate(NULL
, CFSTR("mDNSResponder:StoreOwnerOPTRecord"), NULL
, NULL
);
2810 CFMutableDictionaryRef dict
= NULL
;
2814 LogMsg("mDNSPlatformStoreOwnerOptRecord: Unable to accesss SC Dynamic Store");
2819 // Create/Update the dynamic store entry for the specified interface
2820 sckey
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s%s%s"), "State:/Network/Interface/", ifname
, "/BonjourSleepProxyOPTRecord");
2821 dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2824 LogMsg("mDNSPlatformStoreOwnerOptRecord: Could not create CFDictionary dictionary to store OPT Record");
2829 CFDataRef optRec
= NULL
;
2830 optRec
= CFDataCreate(NULL
, (const uint8_t *)msg
, (CFIndex
)length
);
2831 CFDictionarySetValue(dict
, CFSTR("OwnerOPTRecord"), optRec
);
2832 if (NULL
!= optRec
) CFRelease(optRec
);
2834 SCDynamicStoreSetValue(store
, sckey
, dict
);
2837 if (NULL
!= store
) CFRelease(store
);
2838 if (NULL
!= sckey
) CFRelease(sckey
);
2839 if (NULL
!= dict
) CFRelease(dict
);
2843 mDNSlocal
void mDNSGet_RemoteMAC(int family
, v6addr_t raddr
)
2846 IPAddressMACMapping
*addrMapping
;
2847 int kr
= KERN_FAILURE
;
2853 bzero(eth
, sizeof(ethaddr_t
));
2854 mDNSPlatformMemCopy(dst
.addr
, raddr
, sizeof(v6addr_t
));
2856 kr
= GetRemoteMacinternal(family
, (uint8_t *)dst
.addr
, eth
);
2858 // If the call to get the remote MAC address succeeds, allocate and copy
2859 // the values and schedule a task to update the MAC address in the TCP Keepalive record.
2862 addrMapping
= (IPAddressMACMapping
*) mDNSPlatformMemAllocateClear(sizeof(*addrMapping
));
2863 // This memory allocation is not checked for failure
2864 // It also shoudn’t need to be a memory allocation at all -- why not just use a stack variable? -- SC
2865 snprintf(addrMapping
->ethaddr
, sizeof(addrMapping
->ethaddr
), "%02x:%02x:%02x:%02x:%02x:%02x",
2866 eth
[0], eth
[1], eth
[2], eth
[3], eth
[4], eth
[5]);
2867 // Why is the address represented using text? The UpdateRMAC routine just parses it back into a six-byte MAC address. -- SC
2868 if (family
== AF_INET
)
2870 addrMapping
->ipaddr
.type
= mDNSAddrType_IPv4
;
2871 mDNSPlatformMemCopy(addrMapping
->ipaddr
.ip
.v4
.b
, raddr
, sizeof(v6addr_t
));
2872 // This is the wrong size. It’s using sizeof(v6addr_t) for an IPv4 address -- SC
2876 addrMapping
->ipaddr
.type
= mDNSAddrType_IPv6
;
2877 mDNSPlatformMemCopy(addrMapping
->ipaddr
.ip
.v6
.b
, raddr
, sizeof(v6addr_t
));
2879 UpdateRMAC(&mDNSStorage
, addrMapping
);
2883 mDNSexport mStatus
mDNSPlatformGetRemoteMacAddr(mDNSAddr
*raddr
)
2885 int family
= (raddr
->type
== mDNSAddrType_IPv4
) ? AF_INET
: AF_INET6
;
2887 LogInfo("mDNSPlatformGetRemoteMacAddr calling mDNSGet_RemoteMAC");
2888 mDNSGet_RemoteMAC(family
, raddr
->ip
.v6
.b
);
2890 return KERN_SUCCESS
;
2893 mDNSexport mStatus
mDNSPlatformRetrieveTCPInfo(mDNSAddr
*laddr
, mDNSIPPort
*lport
, mDNSAddr
*raddr
, mDNSIPPort
*rport
, mDNSTCPInfo
*mti
)
2897 int family
= (laddr
->type
== mDNSAddrType_IPv4
) ? AF_INET
: AF_INET6
;
2899 error
= mDNSRetrieveTCPInfo(family
, laddr
->ip
.v6
.b
, lport
->NotAnInteger
, raddr
->ip
.v6
.b
, rport
->NotAnInteger
, (uint32_t *)&(mti
->seq
), (uint32_t *)&(mti
->ack
), (uint16_t *)&(mti
->window
), (int32_t*)&intfid
);
2900 if (error
!= KERN_SUCCESS
)
2902 LogMsg("%s: mDNSRetrieveTCPInfo returned : %d", __func__
, error
);
2905 mti
->IntfId
= mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage
, intfid
);
2909 #define BPF_SetOffset(from, cond, to) (from)->cond = (to) - 1 - (from)
2911 mDNSlocal
int CountProxyTargets(NetworkInterfaceInfoOSX
*x
, int *p4
, int *p6
)
2913 int numv4
= 0, numv6
= 0;
2916 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
2917 if (rr
->resrec
.InterfaceID
== x
->ifinfo
.InterfaceID
&& rr
->AddressProxy
.type
== mDNSAddrType_IPv4
)
2919 if (p4
) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.4a", x
->BPF_fd
, x
->ifinfo
.ifname
, numv4
, &rr
->AddressProxy
.ip
.v4
);
2923 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
2924 if (rr
->resrec
.InterfaceID
== x
->ifinfo
.InterfaceID
&& rr
->AddressProxy
.type
== mDNSAddrType_IPv6
)
2926 if (p6
) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.16a", x
->BPF_fd
, x
->ifinfo
.ifname
, numv6
, &rr
->AddressProxy
.ip
.v6
);
2930 if (p4
) *p4
= numv4
;
2931 if (p6
) *p6
= numv6
;
2932 return(numv4
+ numv6
);
2935 mDNSexport
void mDNSPlatformUpdateProxyList(const mDNSInterfaceID InterfaceID
)
2937 mDNS
*const m
= &mDNSStorage
;
2938 NetworkInterfaceInfoOSX
*x
;
2940 // Note: We can't use IfIndexToInterfaceInfoOSX because that looks for Registered also.
2941 for (x
= m
->p
->InterfaceList
; x
; x
= x
->next
) if ((x
->ifinfo
.InterfaceID
== InterfaceID
) && (x
->BPF_fd
>= 0)) break;
2943 if (!x
) { LogMsg("mDNSPlatformUpdateProxyList: ERROR InterfaceID %p not found", InterfaceID
); return; }
2945 #define MAX_BPF_ADDRS 250
2946 int numv4
= 0, numv6
= 0;
2948 if (CountProxyTargets(x
, &numv4
, &numv6
) > MAX_BPF_ADDRS
)
2950 LogMsg("mDNSPlatformUpdateProxyList: ERROR Too many address proxy records v4 %d v6 %d", numv4
, numv6
);
2951 if (numv4
> MAX_BPF_ADDRS
) numv4
= MAX_BPF_ADDRS
;
2952 numv6
= MAX_BPF_ADDRS
- numv4
;
2955 LogSPS("mDNSPlatformUpdateProxyList: fd %d %-7s MAC %.6a %d v4 %d v6", x
->BPF_fd
, x
->ifinfo
.ifname
, &x
->ifinfo
.MAC
, numv4
, numv6
);
2957 // Caution: This is a static structure, so we need to be careful that any modifications we make to it
2958 // are done in such a way that they work correctly when mDNSPlatformUpdateProxyList is called multiple times
2959 static struct bpf_insn filter
[17 + MAX_BPF_ADDRS
] =
2961 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, 12), // 0 Read Ethertype (bytes 12,13)
2963 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0x0806, 0, 1), // 1 If Ethertype == ARP goto next, else 3
2964 BPF_STMT(BPF_RET
+ BPF_K
, 42), // 2 Return 42-byte ARP
2966 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0x0800, 4, 0), // 3 If Ethertype == IPv4 goto 8 (IPv4 address list check) else next
2968 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0x86DD, 0, 9), // 4 If Ethertype == IPv6 goto next, else exit
2969 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, 20), // 5 Read Protocol and Hop Limit (bytes 20,21)
2970 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0x3AFF, 0, 9), // 6 If (Prot,TTL) == (3A,FF) goto next, else IPv6 address list check
2971 BPF_STMT(BPF_RET
+ BPF_K
, 86), // 7 Return 86-byte ND
2973 // Is IPv4 packet; check if it's addressed to any IPv4 address we're proxying for
2974 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, 30), // 8 Read IPv4 Dst (bytes 30,31,32,33)
2977 // Special filter program to use when there are no address proxy records
2978 static struct bpf_insn nullfilter
[] =
2980 BPF_STMT(BPF_RET
| BPF_K
, 0) // 0 Match no packets and return size 0
2983 struct bpf_program prog
;
2984 if (!numv4
&& !numv6
)
2986 LogSPS("mDNSPlatformUpdateProxyList: No need for filter");
2987 if (m
->timenow
== 0) LogMsg("mDNSPlatformUpdateProxyList: m->timenow == 0");
2989 // Cancel any previous ND group memberships we had
2990 if (x
->BPF_mcfd
>= 0)
2996 // Schedule check to see if we can close this BPF_fd now
2997 if (!m
->NetworkChanged
) m
->NetworkChanged
= NonZeroTime(m
->timenow
+ mDNSPlatformOneSecond
* 2);
2998 if (x
->BPF_fd
< 0) return; // If we've already closed our BPF_fd, no need to generate an error message below
3000 prog
.bf_insns
= nullfilter
;
3004 struct bpf_insn
*pc
= &filter
[9];
3005 struct bpf_insn
*chk6
= pc
+ numv4
+ 1; // numv4 address checks, plus a "return 0"
3006 struct bpf_insn
*fail
= chk6
+ 1 + numv6
; // Get v6 Dst LSW, plus numv6 address checks
3007 struct bpf_insn
*ret4
= fail
+ 1;
3008 struct bpf_insn
*ret6
= ret4
+ 4;
3010 static const struct bpf_insn rf
= BPF_STMT(BPF_RET
+ BPF_K
, 0); // No match: Return nothing
3012 static const struct bpf_insn g6
= BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, 50); // Read IPv6 Dst LSW (bytes 50,51,52,53)
3014 static const struct bpf_insn r4a
= BPF_STMT(BPF_LDX
+ BPF_B
+ BPF_MSH
, 14); // Get IP Header length (normally 20)
3015 static const struct bpf_insn r4b
= BPF_STMT(BPF_LD
+ BPF_IMM
, 54); // A = 54 (14-byte Ethernet plus 20-byte TCP + 20 bytes spare)
3016 static const struct bpf_insn r4c
= BPF_STMT(BPF_ALU
+ BPF_ADD
+ BPF_X
, 0); // A += IP Header length
3017 static const struct bpf_insn r4d
= BPF_STMT(BPF_RET
+ BPF_A
, 0); // Success: Return Ethernet + IP + TCP + 20 bytes spare (normally 74)
3019 static const struct bpf_insn r6a
= BPF_STMT(BPF_RET
+ BPF_K
, 94); // Success: Return Eth + IPv6 + TCP + 20 bytes spare
3021 BPF_SetOffset(&filter
[4], jf
, fail
); // If Ethertype not ARP, IPv4, or IPv6, fail
3022 BPF_SetOffset(&filter
[6], jf
, chk6
); // If IPv6 but not ICMPv6, go to IPv6 address list check
3024 // BPF Byte-Order Note
3025 // The BPF API designers apparently thought that programmers would not be smart enough to use htons
3026 // and htonl correctly to convert numeric values to network byte order on little-endian machines,
3027 // so instead they chose to make the API implicitly byte-swap *ALL* values, even literal byte strings
3028 // that shouldn't be byte-swapped, like ASCII text, Ethernet addresses, IP addresses, etc.
3029 // As a result, if we put Ethernet addresses and IP addresses in the right byte order, the BPF API
3030 // will byte-swap and make them backwards, and then our filter won't work. So, we have to arrange
3031 // that on little-endian machines we deliberately put addresses in memory with the bytes backwards,
3032 // so that when the BPF API goes through and swaps them all, they end up back as they should be.
3033 // In summary, if we byte-swap all the non-numeric fields that shouldn't be swapped, and we *don't*
3034 // swap any of the numeric values that *should* be byte-swapped, then the filter will work correctly.
3036 // IPSEC capture size notes:
3037 // 8 bytes UDP header
3038 // 4 bytes Non-ESP Marker
3039 // 28 bytes IKE Header
3041 // 40 Total. Capturing TCP Header + 20 gets us enough bytes to receive the IKE Header in a UDP-encapsulated IKE packet.
3044 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3045 if (rr
->resrec
.InterfaceID
== InterfaceID
&& rr
->AddressProxy
.type
== mDNSAddrType_IPv4
)
3047 mDNSv4Addr a
= rr
->AddressProxy
.ip
.v4
;
3048 pc
->code
= BPF_JMP
+ BPF_JEQ
+ BPF_K
;
3049 BPF_SetOffset(pc
, jt
, ret4
);
3051 pc
->k
= (bpf_u_int32
)a
.b
[0] << 24 | (bpf_u_int32
)a
.b
[1] << 16 | (bpf_u_int32
)a
.b
[2] << 8 | (bpf_u_int32
)a
.b
[3];
3056 if (pc
!= chk6
) LogMsg("mDNSPlatformUpdateProxyList: pc %p != chk6 %p", pc
, chk6
);
3057 *pc
++ = g6
; // chk6 points here
3059 // First cancel any previous ND group memberships we had, then create a fresh socket
3060 if (x
->BPF_mcfd
>= 0) close(x
->BPF_mcfd
);
3061 x
->BPF_mcfd
= socket(AF_INET6
, SOCK_DGRAM
, 0);
3063 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3064 if (rr
->resrec
.InterfaceID
== InterfaceID
&& rr
->AddressProxy
.type
== mDNSAddrType_IPv6
)
3066 const mDNSv6Addr
*const a
= &rr
->AddressProxy
.ip
.v6
;
3067 pc
->code
= BPF_JMP
+ BPF_JEQ
+ BPF_K
;
3068 BPF_SetOffset(pc
, jt
, ret6
);
3070 pc
->k
= (bpf_u_int32
)a
->b
[0x0C] << 24 | (bpf_u_int32
)a
->b
[0x0D] << 16 | (bpf_u_int32
)a
->b
[0x0E] << 8 | (bpf_u_int32
)a
->b
[0x0F];
3073 struct ipv6_mreq i6mr
;
3074 i6mr
.ipv6mr_interface
= x
->scope_id
;
3075 i6mr
.ipv6mr_multiaddr
= *(const struct in6_addr
*)&NDP_prefix
;
3076 i6mr
.ipv6mr_multiaddr
.s6_addr
[0xD] = a
->b
[0xD];
3077 i6mr
.ipv6mr_multiaddr
.s6_addr
[0xE] = a
->b
[0xE];
3078 i6mr
.ipv6mr_multiaddr
.s6_addr
[0xF] = a
->b
[0xF];
3080 // Do precautionary IPV6_LEAVE_GROUP first, necessary to clear stale kernel state
3081 mStatus err
= setsockopt(x
->BPF_mcfd
, IPPROTO_IPV6
, IPV6_LEAVE_GROUP
, &i6mr
, sizeof(i6mr
));
3082 if (err
< 0 && (errno
!= EADDRNOTAVAIL
))
3083 LogMsg("mDNSPlatformUpdateProxyList: IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err
, errno
, strerror(errno
), &i6mr
.ipv6mr_multiaddr
, i6mr
.ipv6mr_interface
);
3085 err
= setsockopt(x
->BPF_mcfd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &i6mr
, sizeof(i6mr
));
3086 if (err
< 0 && (errno
!= EADDRINUSE
)) // Joining same group twice can give "Address already in use" error -- no need to report that
3087 LogMsg("mDNSPlatformUpdateProxyList: IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err
, errno
, strerror(errno
), &i6mr
.ipv6mr_multiaddr
, i6mr
.ipv6mr_interface
);
3089 LogSPS("Joined IPv6 ND multicast group %.16a for %.16a", &i6mr
.ipv6mr_multiaddr
, a
);
3092 if (pc
!= fail
) LogMsg("mDNSPlatformUpdateProxyList: pc %p != fail %p", pc
, fail
);
3093 *pc
++ = rf
; // fail points here
3095 if (pc
!= ret4
) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret4 %p", pc
, ret4
);
3096 *pc
++ = r4a
; // ret4 points here
3101 if (pc
!= ret6
) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret6 %p", pc
, ret6
);
3102 *pc
++ = r6a
; // ret6 points here
3104 // For debugging BPF filter program
3106 for (q
=0; q
<prog
.bf_len
; q
++)
3107 LogSPS("mDNSPlatformUpdateProxyList: %2d { 0x%02x, %d, %d, 0x%08x },", q
, prog
.bf_insns
[q
].code
, prog
.bf_insns
[q
].jt
, prog
.bf_insns
[q
].jf
, prog
.bf_insns
[q
].k
);
3109 prog
.bf_len
= (u_int
)(pc
- filter
);
3110 prog
.bf_insns
= filter
;
3113 if (ioctl(x
->BPF_fd
, BIOCSETFNR
, &prog
) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) failed %d (%s)", prog
.bf_len
, errno
, strerror(errno
));
3114 else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) successful", prog
.bf_len
);
3117 mDNSexport
void mDNSPlatformReceiveBPF_fd(int fd
)
3119 mDNS
*const m
= &mDNSStorage
;
3122 NetworkInterfaceInfoOSX
*i
;
3123 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
) if (i
->BPF_fd
== -2) break;
3124 if (!i
) { LogSPS("mDNSPlatformReceiveBPF_fd: No Interfaces awaiting BPF fd %d; closing", fd
); close(fd
); }
3127 LogSPS("%s using BPF fd %d", i
->ifinfo
.ifname
, fd
);
3129 struct bpf_version v
;
3130 if (ioctl(fd
, BIOCVERSION
, &v
) < 0)
3131 LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION failed %d (%s)", fd
, i
->ifinfo
.ifname
, errno
, strerror(errno
));
3132 else if (BPF_MAJOR_VERSION
!= v
.bv_major
|| BPF_MINOR_VERSION
!= v
.bv_minor
)
3133 LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION header %d.%d kernel %d.%d",
3134 fd
, i
->ifinfo
.ifname
, BPF_MAJOR_VERSION
, BPF_MINOR_VERSION
, v
.bv_major
, v
.bv_minor
);
3136 if (ioctl(fd
, BIOCGBLEN
, &i
->BPF_len
) < 0)
3137 LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCGBLEN failed %d (%s)", fd
, i
->ifinfo
.ifname
, errno
, strerror(errno
));
3139 if (i
->BPF_len
> sizeof(m
->imsg
))
3141 i
->BPF_len
= sizeof(m
->imsg
);
3142 if (ioctl(fd
, BIOCSBLEN
, &i
->BPF_len
) < 0)
3143 LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN failed %d (%s)", fd
, i
->ifinfo
.ifname
, errno
, strerror(errno
));
3145 LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", fd
, i
->ifinfo
.ifname
, i
->BPF_len
);
3148 static const u_int opt_one
= 1;
3149 if (ioctl(fd
, BIOCIMMEDIATE
, &opt_one
) < 0)
3150 LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCIMMEDIATE failed %d (%s)", fd
, i
->ifinfo
.ifname
, errno
, strerror(errno
));
3152 //if (ioctl(fd, BIOCPROMISC, &opt_one) < 0)
3153 // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCPROMISC failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
3155 //if (ioctl(fd, BIOCSHDRCMPLT, &opt_one) < 0)
3156 // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSHDRCMPLT failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
3158 /* <rdar://problem/10287386>
3159 * make socket non blocking see comments in bpf_callback_common for more info
3161 if (fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
) < 0) // set non-blocking
3163 LogMsg("mDNSPlatformReceiveBPF_fd: %d %s O_NONBLOCK failed %d (%s)", fd
, i
->ifinfo
.ifname
, errno
, strerror(errno
));
3167 mDNSPlatformMemZero(&ifr
, sizeof(ifr
));
3168 strlcpy(ifr
.ifr_name
, i
->ifinfo
.ifname
, sizeof(ifr
.ifr_name
));
3169 if (ioctl(fd
, BIOCSETIF
, &ifr
) < 0)
3170 { LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSETIF failed %d (%s)", fd
, i
->ifinfo
.ifname
, errno
, strerror(errno
)); i
->BPF_fd
= -3; }
3173 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
3175 i
->BPF_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
, fd
, 0, dispatch_get_main_queue());
3176 if (!i
->BPF_source
) {LogMsg("mDNSPlatformReceiveBPF_fd: dispatch source create failed"); return;}
3177 dispatch_source_set_event_handler(i
->BPF_source
, ^{bpf_callback_dispatch(i
);});
3178 dispatch_source_set_cancel_handler(i
->BPF_source
, ^{close(fd
);});
3179 dispatch_resume(i
->BPF_source
);
3181 CFSocketContext myCFSocketContext
= { 0, i
, NULL
, NULL
, NULL
};
3183 i
->BPF_cfs
= CFSocketCreateWithNative(kCFAllocatorDefault
, fd
, kCFSocketReadCallBack
, bpf_callback
, &myCFSocketContext
);
3184 i
->BPF_rls
= CFSocketCreateRunLoopSource(kCFAllocatorDefault
, i
->BPF_cfs
, 0);
3185 CFRunLoopAddSource(CFRunLoopGetMain(), i
->BPF_rls
, kCFRunLoopDefaultMode
);
3187 mDNSPlatformUpdateProxyList(i
->ifinfo
.InterfaceID
);
3194 #endif // APPLE_OSX_mDNSResponder
3196 #if COMPILER_LIKES_PRAGMA_MARK
3198 #pragma mark - Key Management
3201 #ifndef NO_SECURITYFRAMEWORK
3202 mDNSlocal CFArrayRef
CopyCertChain(SecIdentityRef identity
)
3204 CFMutableArrayRef certChain
= NULL
;
3205 if (!identity
) { LogMsg("CopyCertChain: identity is NULL"); return(NULL
); }
3206 SecCertificateRef cert
;
3207 OSStatus err
= SecIdentityCopyCertificate(identity
, &cert
);
3208 if (err
|| !cert
) LogMsg("CopyCertChain: SecIdentityCopyCertificate() returned %d", (int) err
);
3211 #pragma clang diagnostic push
3212 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3213 SecPolicySearchRef searchRef
;
3214 err
= SecPolicySearchCreate(CSSM_CERT_X_509v3
, &CSSMOID_APPLE_X509_BASIC
, NULL
, &searchRef
);
3215 if (err
|| !searchRef
) LogMsg("CopyCertChain: SecPolicySearchCreate() returned %d", (int) err
);
3218 SecPolicyRef policy
;
3219 err
= SecPolicySearchCopyNext(searchRef
, &policy
);
3220 if (err
|| !policy
) LogMsg("CopyCertChain: SecPolicySearchCopyNext() returned %d", (int) err
);
3223 CFArrayRef wrappedCert
= CFArrayCreate(NULL
, (const void**) &cert
, 1, &kCFTypeArrayCallBacks
);
3224 if (!wrappedCert
) LogMsg("CopyCertChain: wrappedCert is NULL");
3228 err
= SecTrustCreateWithCertificates(wrappedCert
, policy
, &trust
);
3229 if (err
|| !trust
) LogMsg("CopyCertChain: SecTrustCreateWithCertificates() returned %d", (int) err
);
3232 err
= SecTrustEvaluate(trust
, NULL
);
3233 if (err
) LogMsg("CopyCertChain: SecTrustEvaluate() returned %d", (int) err
);
3236 CFArrayRef rawCertChain
;
3237 CSSM_TP_APPLE_EVIDENCE_INFO
*statusChain
= NULL
;
3238 err
= SecTrustGetResult(trust
, NULL
, &rawCertChain
, &statusChain
);
3239 if (err
|| !rawCertChain
|| !statusChain
) LogMsg("CopyCertChain: SecTrustGetResult() returned %d", (int) err
);
3242 certChain
= CFArrayCreateMutableCopy(NULL
, 0, rawCertChain
);
3243 if (!certChain
) LogMsg("CopyCertChain: certChain is NULL");
3246 // Replace the SecCertificateRef at certChain[0] with a SecIdentityRef per documentation for SSLSetCertificate:
3247 // <http://devworld.apple.com/documentation/Security/Reference/secureTransportRef/index.html>
3248 CFArraySetValueAtIndex(certChain
, 0, identity
);
3249 // Remove root from cert chain, but keep any and all intermediate certificates that have been signed by the root certificate
3250 if (CFArrayGetCount(certChain
) > 1) CFArrayRemoveValueAtIndex(certChain
, CFArrayGetCount(certChain
) - 1);
3252 CFRelease(rawCertChain
);
3253 // Do not free statusChain:
3254 // <http://developer.apple.com/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html> says:
3255 // certChain: Call the CFRelease function to release this object when you are finished with it.
3256 // statusChain: Do not attempt to free this pointer; it remains valid until the trust management object is released...
3261 CFRelease(wrappedCert
);
3265 CFRelease(searchRef
);
3267 #pragma clang diagnostic pop
3272 #endif /* NO_SECURITYFRAMEWORK */
3274 mDNSexport mStatus
mDNSPlatformTLSSetupCerts(void)
3276 #ifdef NO_SECURITYFRAMEWORK
3277 return mStatus_UnsupportedErr
;
3279 SecIdentityRef identity
= nil
;
3280 SecIdentitySearchRef srchRef
= nil
;
3283 #pragma clang diagnostic push
3284 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3285 // search for "any" identity matching specified key use
3286 // In this app, we expect there to be exactly one
3287 err
= SecIdentitySearchCreate(NULL
, CSSM_KEYUSE_DECRYPT
, &srchRef
);
3288 if (err
) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCreate returned %d", (int) err
); return err
; }
3290 err
= SecIdentitySearchCopyNext(srchRef
, &identity
);
3291 if (err
) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext returned %d", (int) err
); return err
; }
3292 #pragma clang diagnostic pop
3294 if (CFGetTypeID(identity
) != SecIdentityGetTypeID())
3295 { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext CFTypeID failure"); return mStatus_UnknownErr
; }
3297 // Found one. Call CopyCertChain to create the correct certificate chain.
3298 ServerCerts
= CopyCertChain(identity
);
3299 if (ServerCerts
== nil
) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: CopyCertChain error"); return mStatus_UnknownErr
; }
3301 return mStatus_NoError
;
3302 #endif /* NO_SECURITYFRAMEWORK */
3305 mDNSexport
void mDNSPlatformTLSTearDownCerts(void)
3307 #ifndef NO_SECURITYFRAMEWORK
3308 if (ServerCerts
) { CFRelease(ServerCerts
); ServerCerts
= NULL
; }
3309 #endif /* NO_SECURITYFRAMEWORK */
3313 mDNSlocal
void mDNSDomainLabelFromCFString(CFStringRef cfs
, domainlabel
*const namelabel
);
3315 // This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel
3316 mDNSlocal
void GetUserSpecifiedFriendlyComputerName(domainlabel
*const namelabel
)
3318 CFStringEncoding encoding
= kCFStringEncodingUTF8
;
3319 CFStringRef cfs
= SCDynamicStoreCopyComputerName(NULL
, &encoding
);
3321 if (cfs
== mDNSNULL
) {
3325 mDNSDomainLabelFromCFString(cfs
, namelabel
);
3330 mDNSlocal
void GetUserSpecifiedLocalHostName(domainlabel
*const namelabel
)
3332 CFStringRef cfs
= SCDynamicStoreCopyLocalHostName(NULL
);
3334 if (cfs
== mDNSNULL
) {
3338 mDNSDomainLabelFromCFString(cfs
, namelabel
);
3343 mDNSlocal
void mDNSDomainLabelFromCFString(CFStringRef cfs
, domainlabel
*const namelabel
)
3345 CFIndex num_of_bytes_write
= 0;
3346 CFStringGetBytes(cfs
, CFRangeMake(0, CFStringGetLength(cfs
)), kCFStringEncodingUTF8
, 0, FALSE
, namelabel
->c
+ 1, sizeof(*namelabel
) - 1, &num_of_bytes_write
);
3347 namelabel
->c
[0] = num_of_bytes_write
;
3350 mDNSexport mDNSBool
DictionaryIsEnabled(CFDictionaryRef dict
)
3353 CFNumberRef state
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("Enabled"));
3354 if (state
== NULL
) return mDNSfalse
;
3355 if (!CFNumberGetValue(state
, kCFNumberSInt32Type
, &val
))
3356 { LogMsg("ERROR: DictionaryIsEnabled - CFNumberGetValue"); return mDNSfalse
; }
3357 return val
? mDNStrue
: mDNSfalse
;
3360 mDNSlocal mStatus
SetupAddr(mDNSAddr
*ip
, const struct sockaddr
*const sa
)
3362 if (!sa
) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid
); }
3364 if (sa
->sa_family
== AF_INET
)
3366 struct sockaddr_in
*ifa_addr
= (struct sockaddr_in
*)sa
;
3367 ip
->type
= mDNSAddrType_IPv4
;
3368 ip
->ip
.v4
.NotAnInteger
= ifa_addr
->sin_addr
.s_addr
;
3369 return(mStatus_NoError
);
3372 if (sa
->sa_family
== AF_INET6
)
3374 struct sockaddr_in6
*ifa_addr
= (struct sockaddr_in6
*)sa
;
3375 // Inside the BSD kernel they use a hack where they stuff the sin6->sin6_scope_id
3376 // value into the second word of the IPv6 link-local address, so they can just
3377 // pass around IPv6 address structures instead of full sockaddr_in6 structures.
3378 // Those hacked IPv6 addresses aren't supposed to escape the kernel in that form, but they do.
3379 // To work around this we always whack the second word of any IPv6 link-local address back to zero.
3380 if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr
->sin6_addr
)) ifa_addr
->sin6_addr
.__u6_addr
.__u6_addr16
[1] = 0;
3381 ip
->type
= mDNSAddrType_IPv6
;
3382 ip
->ip
.v6
= *(mDNSv6Addr
*)&ifa_addr
->sin6_addr
;
3383 return(mStatus_NoError
);
3386 LogMsg("SetupAddr invalid sa_family %d", sa
->sa_family
);
3387 return(mStatus_Invalid
);
3390 mDNSlocal mDNSEthAddr
GetBSSID(char *ifa_name
)
3392 mDNSEthAddr eth
= zeroEthAddr
;
3394 CFStringRef entityname
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("State:/Network/Interface/%s/AirPort"), ifa_name
);
3397 CFDictionaryRef dict
= SCDynamicStoreCopyValue(NULL
, entityname
);
3400 CFRange range
= { 0, 6 }; // Offset, length
3401 CFDataRef data
= CFDictionaryGetValue(dict
, CFSTR("BSSID"));
3402 if (data
&& CFDataGetLength(data
) == 6)
3403 CFDataGetBytes(data
, range
, eth
.b
);
3406 CFRelease(entityname
);
3412 mDNSlocal
int GetMAC(mDNSEthAddr
*eth
, u_short ifindex
)
3414 struct ifaddrs
*ifa
;
3415 for (ifa
= myGetIfAddrs(0); ifa
; ifa
= ifa
->ifa_next
)
3416 if (ifa
->ifa_addr
&& ifa
->ifa_addr
->sa_family
== AF_LINK
)
3418 const struct sockaddr_dl
*const sdl
= (const struct sockaddr_dl
*)ifa
->ifa_addr
;
3419 if (sdl
->sdl_index
== ifindex
)
3420 { mDNSPlatformMemCopy(eth
->b
, sdl
->sdl_data
+ sdl
->sdl_nlen
, 6); return 0; }
3426 #ifndef SIOCGIFWAKEFLAGS
3427 #define SIOCGIFWAKEFLAGS _IOWR('i', 136, struct ifreq) /* get interface wake property flags */
3430 #ifndef IF_WAKE_ON_MAGIC_PACKET
3431 #define IF_WAKE_ON_MAGIC_PACKET 0x01
3434 #ifndef ifr_wake_flags
3435 #define ifr_wake_flags ifr_ifru.ifru_intval
3440 RegistryEntrySearchCFPropertyAndIOObject( io_registry_entry_t entry
,
3441 const io_name_t plane
,
3443 CFTypeRef
* outProperty
,
3444 io_registry_entry_t
* outEntry
)
3448 IOObjectRetain(entry
);
3451 CFTypeRef ref
= IORegistryEntryCreateCFProperty(entry
, keystr
, kCFAllocatorDefault
, mDNSNULL
);
3454 if (outProperty
) *outProperty
= ref
;
3455 else CFRelease(ref
);
3458 io_registry_entry_t parent
;
3459 kr
= IORegistryEntryGetParentEntry(entry
, plane
, &parent
);
3460 if (kr
!= KERN_SUCCESS
) parent
= mDNSNULL
;
3461 IOObjectRelease(entry
);
3464 if (!entry
) kr
= kIOReturnNoDevice
;
3467 if (outEntry
) *outEntry
= entry
;
3468 else IOObjectRelease(entry
);
3474 mDNSlocal mDNSBool
CheckInterfaceSupport(NetworkInterfaceInfo
*const intf
, const char *key
)
3476 io_service_t service
= IOServiceGetMatchingService(kIOMasterPortDefault
, IOBSDNameMatching(kIOMasterPortDefault
, 0, intf
->ifname
));
3479 LogSPS("CheckInterfaceSupport: No service for interface %s", intf
->ifname
);
3483 mDNSBool ret
= mDNSfalse
;
3485 CFStringRef keystr
= CFStringCreateWithCString(NULL
, key
, kCFStringEncodingUTF8
);
3486 kern_return_t kr
= RegistryEntrySearchCFPropertyAndIOObject(service
, kIOServicePlane
, keystr
, mDNSNULL
, mDNSNULL
);
3488 if (kr
== KERN_SUCCESS
) ret
= mDNStrue
;
3492 IOObjectGetClass(service
, n1
);
3493 LogRedact(MDNS_LOG_CATEGORY_SPS
, MDNS_LOG_INFO
,
3494 "CheckInterfaceSupport: No " PUB_S
" for interface " PUB_S
"/" PUB_S
" kr 0x%X", key
, intf
->ifname
, n1
, kr
);
3498 IOObjectRelease(service
);
3503 #if !TARGET_OS_WATCH
3504 mDNSlocal mDNSBool
InterfaceSupportsKeepAlive(NetworkInterfaceInfo
*const intf
)
3506 return CheckInterfaceSupport(intf
, mDNS_IOREG_KA_KEY
);
3510 mDNSlocal mDNSBool
NetWakeInterface(NetworkInterfaceInfoOSX
*i
)
3516 // We only use Sleep Proxy Service on multicast-capable interfaces, except loopback and D2D.
3517 if (!MulticastInterface(i
) || (i
->ifa_flags
& IFF_LOOPBACK
) || i
->D2DInterface
)
3519 LogRedact(MDNS_LOG_CATEGORY_SPS
, MDNS_LOG_DEBUG
,
3520 "NetWakeInterface: returning false for " PUB_S
, i
->ifinfo
.ifname
);
3524 // If the interface supports TCPKeepalive, it is capable of waking up for a magic packet
3525 // This check is needed since the SIOCGIFWAKEFLAGS ioctl returns wrong values for WOMP capability
3526 // when the power source is not AC Power.
3527 if (InterfaceSupportsKeepAlive(&i
->ifinfo
))
3529 LogSPS("NetWakeInterface: %s supports TCP Keepalive returning true", i
->ifinfo
.ifname
);
3533 int s
= socket(AF_INET
, SOCK_DGRAM
, 0);
3534 if (s
< 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i
->ifinfo
.ifname
, s
, errno
, strerror(errno
)); return(mDNSfalse
); }
3537 strlcpy(ifr
.ifr_name
, i
->ifinfo
.ifname
, sizeof(ifr
.ifr_name
));
3538 if (ioctl(s
, SIOCGIFWAKEFLAGS
, &ifr
) < 0)
3540 const int ioctl_errno
= errno
;
3541 // For some strange reason, in /usr/include/sys/errno.h, EOPNOTSUPP is defined to be
3542 // 102 when compiling kernel code, and 45 when compiling user-level code. Since this
3543 // error code is being returned from the kernel, we need to use the kernel version.
3544 #define KERNEL_EOPNOTSUPP 102
3545 if (ioctl_errno
!= KERNEL_EOPNOTSUPP
) // "Operation not supported on socket", the expected result on Leopard and earlier
3546 LogMsg("NetWakeInterface SIOCGIFWAKEFLAGS %s errno %d (%s)", i
->ifinfo
.ifname
, ioctl_errno
, strerror(ioctl_errno
));
3547 // If on Leopard or earlier, we get EOPNOTSUPP, so in that case
3548 // we enable WOL if this interface is not AirPort and "Wake for Network access" is turned on.
3549 ifr
.ifr_wake_flags
= (ioctl_errno
== KERNEL_EOPNOTSUPP
&& !(i
)->BSSID
.l
[0] && i
->m
->SystemWakeOnLANEnabled
) ? IF_WAKE_ON_MAGIC_PACKET
: 0;
3554 // ifr.ifr_wake_flags = IF_WAKE_ON_MAGIC_PACKET; // For testing with MacBook Air, using a USB dongle that doesn't actually support Wake-On-LAN
3556 LogRedact(MDNS_LOG_CATEGORY_SPS
, MDNS_LOG_INFO
,
3557 "NetWakeInterface: " PUB_S
" " PRI_IP_ADDR
" " PUB_S
" WOMP",
3558 i
->ifinfo
.ifname
, &i
->ifinfo
.ip
, (ifr
.ifr_wake_flags
& IF_WAKE_ON_MAGIC_PACKET
) ? "supports" : "no");
3560 return((ifr
.ifr_wake_flags
& IF_WAKE_ON_MAGIC_PACKET
) != 0);
3561 #endif // TARGET_OS_WATCH
3564 mDNSlocal u_int64_t
getExtendedFlags(const char *ifa_name
)
3569 sockFD
= socket(AF_INET
, SOCK_DGRAM
, 0);
3572 LogMsg("getExtendedFlags: socket() call failed, errno = %d (%s)", errno
, strerror(errno
));
3576 ifr
.ifr_addr
.sa_family
= AF_INET
;
3577 strlcpy(ifr
.ifr_name
, ifa_name
, sizeof(ifr
.ifr_name
));
3579 if (ioctl(sockFD
, SIOCGIFEFLAGS
, (caddr_t
)&ifr
) == -1)
3581 LogMsg("getExtendedFlags: SIOCGIFEFLAGS failed for %s, errno = %d (%s)", ifa_name
, errno
, strerror(errno
));
3586 return ifr
.ifr_eflags
;
3589 mDNSlocal mDNSBool
isExcludedInterface(int sockFD
, char * ifa_name
)
3593 // llw0 interface is excluded from Bonjour discover.
3594 // There currently is no interface attributed based way to identify this interface
3595 // until rdar://problem/47933782 is addressed.
3596 if (strncmp(ifa_name
, "llw", 3) == 0)
3598 LogMsg("isExcludedInterface: excluding %s", ifa_name
);
3602 // Coprocessor interfaces are also excluded.
3605 LogMsg("isExcludedInterface: invalid socket FD passed: %d", sockFD
);
3609 memset(&ifr
, 0, sizeof(struct ifreq
));
3610 strlcpy(ifr
.ifr_name
, ifa_name
, sizeof(ifr
.ifr_name
));
3612 if (ioctl(sockFD
, SIOCGIFFUNCTIONALTYPE
, (caddr_t
)&ifr
) == -1)
3614 LogMsg("isExcludedInterface: SIOCGIFFUNCTIONALTYPE failed, errno = %d (%s)", errno
, strerror(errno
));
3618 if (ifr
.ifr_functional_type
== IFRTYPE_FUNCTIONAL_INTCOPROC
)
3620 LogMsg("isExcludedInterface: excluding coprocessor interface %s", ifa_name
);
3627 #if TARGET_OS_IPHONE
3629 // Function pointers for the routines we use in the MobileWiFi framework.
3630 static WiFiManagerClientRef (*WiFiManagerClientCreate_p
)(CFAllocatorRef allocator
, WiFiClientType type
) = mDNSNULL
;
3631 static CFArrayRef (*WiFiManagerClientCopyDevices_p
)(WiFiManagerClientRef manager
) = mDNSNULL
;
3632 static WiFiNetworkRef (*WiFiDeviceClientCopyCurrentNetwork_p
)(WiFiDeviceClientRef device
) = mDNSNULL
;
3633 static bool (*WiFiNetworkIsCarPlay_p
)(WiFiNetworkRef network
) = mDNSNULL
;
3635 mDNSlocal mDNSBool
MobileWiFiLibLoad(void)
3637 static mDNSBool isInitialized
= mDNSfalse
;
3638 static void *MobileWiFiLib_p
= mDNSNULL
;
3639 static const char path
[] = "/System/Library/PrivateFrameworks/MobileWiFi.framework/MobileWiFi";
3643 if (!MobileWiFiLib_p
)
3645 MobileWiFiLib_p
= dlopen(path
, RTLD_LAZY
| RTLD_LOCAL
);
3646 if (!MobileWiFiLib_p
)
3648 LogInfo("MobileWiFiLibLoad: dlopen() failed.");
3653 if (!WiFiManagerClientCreate_p
)
3655 WiFiManagerClientCreate_p
= dlsym(MobileWiFiLib_p
, "WiFiManagerClientCreate");
3656 if (!WiFiManagerClientCreate_p
)
3658 LogInfo("MobileWiFiLibLoad: load of WiFiManagerClientCreate symbol failed.");
3663 if (!WiFiManagerClientCopyDevices_p
)
3665 WiFiManagerClientCopyDevices_p
= dlsym(MobileWiFiLib_p
, "WiFiManagerClientCopyDevices");
3666 if (!WiFiManagerClientCopyDevices_p
)
3668 LogInfo("MobileWiFiLibLoad: load of WiFiManagerClientCopyDevices symbol failed.");
3673 if (!WiFiDeviceClientCopyCurrentNetwork_p
)
3675 WiFiDeviceClientCopyCurrentNetwork_p
= dlsym(MobileWiFiLib_p
, "WiFiDeviceClientCopyCurrentNetwork");
3676 if (!WiFiDeviceClientCopyCurrentNetwork_p
)
3678 LogInfo("MobileWiFiLibLoad: load of WiFiDeviceClientCopyCurrentNetwork symbol failed.");
3683 if (!WiFiNetworkIsCarPlay_p
)
3685 WiFiNetworkIsCarPlay_p
= dlsym(MobileWiFiLib_p
, "WiFiNetworkIsCarPlay");
3686 if (!WiFiNetworkIsCarPlay_p
)
3688 LogInfo("MobileWiFiLibLoad: load of WiFiNetworkIsCarPlay symbol failed.");
3693 isInitialized
= mDNStrue
;
3697 return isInitialized
;
3700 #define CARPLAY_DEBUG 0
3702 // Return true if the interface is associate to a CarPlay hosted SSID.
3703 // If we have associated with a CarPlay hosted SSID, then use the same
3704 // optimizations that are used when an interface has the IFEF_DIRECTLINK flag set.
3705 mDNSlocal mDNSBool
IsCarPlaySSID(char *ifa_name
)
3707 static WiFiManagerClientRef manager
= NULL
;
3709 WiFiDeviceClientRef device
;
3710 WiFiNetworkRef network
;
3711 mDNSBool rvalue
= mDNSfalse
;
3713 if (!MobileWiFiLibLoad())
3715 LogInfo("IsCarPlaySSID: MobileWiFiLibLoad() failed!");
3719 // Cache the WiFiManagerClientRef.
3720 if (manager
== NULL
)
3721 manager
= WiFiManagerClientCreate_p(NULL
, kWiFiClientTypeNormal
);
3723 if (manager
== NULL
)
3725 LogInfo("IsCarPlaySSID: WiFiManagerClientCreate() failed!");
3729 devices
= WiFiManagerClientCopyDevices_p(manager
);
3731 // If the first call fails, update the cached WiFiManagerClientRef pointer and try again.
3732 if (devices
== NULL
)
3734 LogInfo("IsCarPlaySSID: First call to WiFiManagerClientCopyDevices() returned NULL for %s", ifa_name
);
3736 // Release the previously cached WiFiManagerClientRef which is apparently now stale.
3738 manager
= WiFiManagerClientCreate_p(NULL
, kWiFiClientTypeNormal
);
3739 if (manager
== NULL
)
3741 LogInfo("IsCarPlaySSID: WiFiManagerClientCreate() failed!");
3744 devices
= WiFiManagerClientCopyDevices_p(manager
);
3745 if (devices
== NULL
)
3747 LogInfo("IsCarPlaySSID: Second call to WiFiManagerClientCopyDevices() returned NULL for %s", ifa_name
);
3752 device
= (WiFiDeviceClientRef
)CFArrayGetValueAtIndex(devices
, 0);
3753 network
= WiFiDeviceClientCopyCurrentNetwork_p(device
);
3754 if (network
!= NULL
)
3756 if (WiFiNetworkIsCarPlay_p(network
))
3758 LogInfo("IsCarPlaySSID: %s is CarPlay hosted", ifa_name
);
3763 LogInfo("IsCarPlaySSID: %s is NOT CarPlay hosted", ifa_name
);
3764 #endif // CARPLAY_DEBUG
3769 LogInfo("IsCarPlaySSID: WiFiDeviceClientCopyCurrentNetwork() returned NULL for %s", ifa_name
);
3776 #else // TARGET_OS_IPHONE
3778 mDNSlocal mDNSBool
IsCarPlaySSID(char *ifa_name
)
3780 (void)ifa_name
; // unused
3782 // OSX WifiManager currently does not implement WiFiNetworkIsCarPlay()
3786 #endif // TARGET_OS_IPHONE
3788 // Returns pointer to newly created NetworkInterfaceInfoOSX object, or
3789 // pointer to already-existing NetworkInterfaceInfoOSX object found in list, or
3790 // may return NULL if out of memory (unlikely) or parameters are invalid for some reason
3791 // (e.g. sa_family not AF_INET or AF_INET6)
3793 mDNSlocal NetworkInterfaceInfoOSX
*AddInterfaceToList(struct ifaddrs
*ifa
, mDNSs32 utc
)
3795 mDNS
*const m
= &mDNSStorage
;
3796 mDNSu32 scope_id
= if_nametoindex(ifa
->ifa_name
);
3797 mDNSEthAddr bssid
= GetBSSID(ifa
->ifa_name
);
3798 u_int64_t eflags
= getExtendedFlags(ifa
->ifa_name
);
3801 if (SetupAddr(&ip
, ifa
->ifa_addr
) != mStatus_NoError
) return(NULL
);
3802 if (SetupAddr(&mask
, ifa
->ifa_netmask
) != mStatus_NoError
) return(NULL
);
3804 NetworkInterfaceInfoOSX
**p
;
3805 for (p
= &m
->p
->InterfaceList
; *p
; p
= &(*p
)->next
)
3806 if (scope_id
== (*p
)->scope_id
&&
3807 mDNSSameAddress(&ip
, &(*p
)->ifinfo
.ip
) &&
3808 mDNSSameEthAddress(&bssid
, &(*p
)->BSSID
))
3810 debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p, ifname before %s, after %s", scope_id
, &bssid
, &ip
, *p
, (*p
)->ifinfo
.ifname
, ifa
->ifa_name
);
3811 // The name should be updated to the new name so that we don't report a wrong name in our SIGINFO output.
3812 // When interfaces are created with same MAC address, kernel resurrects the old interface.
3813 // Even though the interface index is the same (which should be sufficient), when we receive a UDP packet
3814 // we get the corresponding name for the interface index on which the packet was received and check against
3815 // the InterfaceList for a matching name. So, keep the name in sync
3816 strlcpy((*p
)->ifinfo
.ifname
, ifa
->ifa_name
, sizeof((*p
)->ifinfo
.ifname
));
3818 // Determine if multicast state has changed.
3819 const mDNSBool txrx
= MulticastInterface(*p
);
3820 if ((*p
)->ifinfo
.McastTxRx
!= txrx
)
3822 (*p
)->ifinfo
.McastTxRx
= txrx
;
3823 (*p
)->Exists
= MulticastStateChanged
; // State change; need to deregister and reregister this interface
3826 (*p
)->Exists
= mDNStrue
;
3828 // If interface was not in getifaddrs list last time we looked, but it is now, update 'AppearanceTime' for this record
3829 if ((*p
)->LastSeen
!= utc
) (*p
)->AppearanceTime
= utc
;
3831 // If Wake-on-LAN capability of this interface has changed (e.g. because power cable on laptop has been disconnected)
3832 // we may need to start or stop or sleep proxy browse operation
3833 const mDNSBool NetWake
= NetWakeInterface(*p
);
3834 if ((*p
)->ifinfo
.NetWake
!= NetWake
)
3836 (*p
)->ifinfo
.NetWake
= NetWake
;
3837 // If this interface is already registered with mDNSCore, then we need to start or stop its NetWake browse on-the-fly.
3838 // If this interface is not already registered (i.e. it's a dormant interface we had in our list
3839 // from when we previously saw it) then we mustn't do that, because mDNSCore doesn't know about it yet.
3840 // In this case, the mDNS_RegisterInterface() call will take care of starting the NetWake browse if necessary.
3841 if ((*p
)->Registered
)
3844 if (NetWake
) mDNS_ActivateNetWake_internal (m
, &(*p
)->ifinfo
);
3845 else mDNS_DeactivateNetWake_internal(m
, &(*p
)->ifinfo
);
3849 // Reset the flag if it has changed this time.
3850 (*p
)->ifinfo
.IgnoreIPv4LL
= ((eflags
& IFEF_ARPLL
) != 0) ? mDNSfalse
: mDNStrue
;
3855 NetworkInterfaceInfoOSX
*i
= (NetworkInterfaceInfoOSX
*) callocL("NetworkInterfaceInfoOSX", sizeof(*i
));
3856 debugf("AddInterfaceToList: Making new interface %lu %.6a with address %#a at %p", scope_id
, &bssid
, &ip
, i
);
3857 if (!i
) return(mDNSNULL
);
3858 i
->ifinfo
.InterfaceID
= (mDNSInterfaceID
)(uintptr_t)scope_id
;
3860 i
->ifinfo
.mask
= mask
;
3861 strlcpy(i
->ifinfo
.ifname
, ifa
->ifa_name
, sizeof(i
->ifinfo
.ifname
));
3862 i
->ifinfo
.ifname
[sizeof(i
->ifinfo
.ifname
)-1] = 0;
3863 // We can be configured to disable multicast advertisement, but we want to to support
3864 // local-only services, which need a loopback address record.
3865 i
->ifinfo
.Advertise
= m
->DivertMulticastAdvertisements
? ((ifa
->ifa_flags
& IFF_LOOPBACK
) ? mDNStrue
: mDNSfalse
) : m
->AdvertiseLocalAddresses
;
3866 i
->ifinfo
.Loopback
= ((ifa
->ifa_flags
& IFF_LOOPBACK
) != 0) ? mDNStrue
: mDNSfalse
;
3867 i
->ifinfo
.IgnoreIPv4LL
= ((eflags
& IFEF_ARPLL
) != 0) ? mDNSfalse
: mDNStrue
;
3869 // Setting DirectLink indicates we can do the optimization of skipping the probe phase
3870 // for the interface address records since they should be unique.
3871 // Unfortunately, the legacy p2p* interfaces do not set the IFEF_LOCALNET_PRIVATE
3872 // or IFEF_DIRECTLINK flags, so we have to match against the name.
3873 if ((eflags
& (IFEF_DIRECTLINK
| IFEF_AWDL
)) || (strncmp(i
->ifinfo
.ifname
, "p2p", 3) == 0))
3874 i
->ifinfo
.DirectLink
= mDNStrue
;
3876 i
->ifinfo
.DirectLink
= IsCarPlaySSID(ifa
->ifa_name
);
3878 if (i
->ifinfo
.DirectLink
)
3879 LogInfo("AddInterfaceToList: DirectLink set for %s", ifa
->ifa_name
);
3883 i
->Exists
= mDNStrue
;
3884 i
->Flashing
= mDNSfalse
;
3885 i
->Occulting
= mDNSfalse
;
3887 i
->D2DInterface
= ((eflags
& IFEF_LOCALNET_PRIVATE
) || (strncmp(i
->ifinfo
.ifname
, "p2p", 3) == 0)) ? mDNStrue
: mDNSfalse
;
3888 if (i
->D2DInterface
)
3889 LogInfo("AddInterfaceToList: D2DInterface set for %s", ifa
->ifa_name
);
3890 i
->isAWDL
= (eflags
& IFEF_AWDL
) ? mDNStrue
: mDNSfalse
;
3892 if (eflags
& IFEF_AWDL
)
3894 // Set SupportsUnicastMDNSResponse false for the AWDL interface since unicast reserves
3895 // limited AWDL resources so we don't set the kDNSQClass_UnicastResponse bit in
3896 // Bonjour requests over the AWDL interface.
3897 i
->ifinfo
.SupportsUnicastMDNSResponse
= mDNSfalse
;
3898 AWDLInterfaceID
= i
->ifinfo
.InterfaceID
;
3899 LogInfo("AddInterfaceToList: AWDLInterfaceID = %d", (int) AWDLInterfaceID
);
3903 i
->ifinfo
.SupportsUnicastMDNSResponse
= mDNStrue
;
3905 i
->AppearanceTime
= utc
; // Brand new interface; AppearanceTime is now
3907 i
->ifa_flags
= ifa
->ifa_flags
;
3908 i
->scope_id
= scope_id
;
3910 i
->sa_family
= ifa
->ifa_addr
->sa_family
;
3914 i
->Registered
= mDNSNULL
;
3916 // MulticastInterface() depends on the "m" and "ifa_flags" values being initialized above.
3917 i
->ifinfo
.McastTxRx
= MulticastInterface(i
);
3918 // Do this AFTER i->BSSID has been set up
3919 i
->ifinfo
.NetWake
= (eflags
& IFEF_EXPENSIVE
)? mDNSfalse
: NetWakeInterface(i
);
3920 GetMAC(&i
->ifinfo
.MAC
, scope_id
);
3921 if (i
->ifinfo
.NetWake
&& !i
->ifinfo
.MAC
.l
[0])
3922 LogMsg("AddInterfaceToList: Bad MAC address %.6a for %d %s %#a", &i
->ifinfo
.MAC
, scope_id
, i
->ifinfo
.ifname
, &ip
);
3928 #if COMPILER_LIKES_PRAGMA_MARK
3930 #pragma mark - Power State & Configuration Change Management
3933 mDNSlocal mStatus
ReorderInterfaceList()
3935 // Disable Reorder lists till <rdar://problem/30071012> is fixed to prevent spurious name conflicts
3936 #ifdef PR_30071012_FIXED
3937 mDNS
*const m
= &mDNSStorage
;
3938 nwi_state_t state
= nwi_state_copy();
3940 if (state
== mDNSNULL
)
3942 LogMsg("NWI State is NULL!");
3943 return (mStatus_Invalid
);
3946 // Get the count of interfaces
3947 mDNSu32 count
= nwi_state_get_interface_names(state
, mDNSNULL
, 0);
3950 LogMsg("Unable to get the ordered list of interface names");
3951 nwi_state_release(state
);
3952 return (mStatus_Invalid
);
3955 // Get the ordered interface list
3957 const char *names
[count
];
3958 count
= nwi_state_get_interface_names(state
, names
, count
);
3960 NetworkInterfaceInfo
*newList
= mDNSNULL
;
3961 for (i
= count
-1; i
>= 0; i
--)
3962 { // Build a new ordered interface list
3963 NetworkInterfaceInfo
**ptr
= &m
->HostInterfaces
;
3964 while (*ptr
!= mDNSNULL
)
3966 if (strcmp((*ptr
)->ifname
, names
[i
]) == 0)
3968 NetworkInterfaceInfo
*node
= *ptr
;
3969 *ptr
= (*ptr
)->next
;
3970 node
->next
= newList
;
3974 ptr
= &((*ptr
)->next
);
3978 // Get to the end of the list
3979 NetworkInterfaceInfo
*newListEnd
= newList
;
3980 while (newListEnd
!= mDNSNULL
&& newListEnd
->next
!= mDNSNULL
)
3981 newListEnd
= newListEnd
->next
;
3983 // Add any remaing interfaces to the end of the sorted list
3984 if (newListEnd
!= mDNSNULL
)
3985 newListEnd
->next
= m
->HostInterfaces
;
3987 // If we have a valid new list, point to that now
3988 if (newList
!= mDNSNULL
)
3989 m
->HostInterfaces
= newList
;
3991 nwi_state_release(state
);
3992 #endif // PR_30071012_FIXED
3993 return (mStatus_NoError
);
3996 mDNSlocal mStatus
UpdateInterfaceList(mDNSs32 utc
)
3998 mDNS
*const m
= &mDNSStorage
;
3999 struct ifaddrs
*ifa
= myGetIfAddrs(0);
4000 char defaultname
[64];
4001 int InfoSocket
= socket(AF_INET6
, SOCK_DGRAM
, 0);
4002 if (InfoSocket
< 3 && errno
!= EAFNOSUPPORT
)
4003 LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket
, errno
, strerror(errno
));
4005 if (m
->SleepState
== SleepState_Sleeping
) ifa
= NULL
;
4009 #if LIST_ALL_INTERFACES
4012 if (ifa
->ifa_addr
->sa_family
== AF_APPLETALK
)
4013 LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_APPLETALK",
4014 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
, ifa
->ifa_addr
->sa_family
);
4015 else if (ifa
->ifa_addr
->sa_family
== AF_LINK
)
4016 LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_LINK",
4017 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
, ifa
->ifa_addr
->sa_family
);
4018 else if (ifa
->ifa_addr
->sa_family
!= AF_INET
&& ifa
->ifa_addr
->sa_family
!= AF_INET6
)
4019 LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)",
4020 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
, ifa
->ifa_addr
->sa_family
);
4023 LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X ifa_addr is NOT set",
4024 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
);
4026 if (!(ifa
->ifa_flags
& IFF_UP
))
4027 LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_UP",
4028 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
,
4029 ifa
->ifa_addr
? ifa
->ifa_addr
->sa_family
: 0);
4030 if (!(ifa
->ifa_flags
& IFF_MULTICAST
))
4031 LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_MULTICAST",
4032 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
,
4033 ifa
->ifa_addr
? ifa
->ifa_addr
->sa_family
: 0);
4034 if (ifa
->ifa_flags
& IFF_POINTOPOINT
)
4035 LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT",
4036 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
,
4037 ifa
->ifa_addr
? ifa
->ifa_addr
->sa_family
: 0);
4038 if (ifa
->ifa_flags
& IFF_LOOPBACK
)
4039 LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK",
4040 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
,
4041 ifa
->ifa_addr
? ifa
->ifa_addr
->sa_family
: 0);
4044 if (ifa
->ifa_addr
&& ifa
->ifa_addr
->sa_family
== AF_LINK
)
4046 struct sockaddr_dl
*sdl
= (struct sockaddr_dl
*)ifa
->ifa_addr
;
4047 if (sdl
->sdl_type
== IFT_ETHER
&& sdl
->sdl_alen
== sizeof(m
->PrimaryMAC
) && mDNSSameEthAddress(&m
->PrimaryMAC
, &zeroEthAddr
))
4048 mDNSPlatformMemCopy(m
->PrimaryMAC
.b
, sdl
->sdl_data
+ sdl
->sdl_nlen
, 6);
4051 if (ifa
->ifa_flags
& IFF_UP
&& ifa
->ifa_addr
&& !isExcludedInterface(InfoSocket
, ifa
->ifa_name
))
4052 if (ifa
->ifa_addr
->sa_family
== AF_INET
|| ifa
->ifa_addr
->sa_family
== AF_INET6
)
4054 if (!ifa
->ifa_netmask
)
4057 SetupAddr(&ip
, ifa
->ifa_addr
);
4058 LogMsg("UpdateInterfaceList: ifa_netmask is NULL for %5s(%d) Flags %04X Family %2d %#a",
4059 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
, ifa
->ifa_addr
->sa_family
, &ip
);
4061 // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be zero, so we don't complain about that
4062 // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
4063 else if (ifa
->ifa_netmask
->sa_family
!= ifa
->ifa_addr
->sa_family
&& ifa
->ifa_netmask
->sa_family
!= 0)
4066 SetupAddr(&ip
, ifa
->ifa_addr
);
4067 LogMsg("UpdateInterfaceList: ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d",
4068 ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
), ifa
->ifa_flags
, ifa
->ifa_addr
->sa_family
, &ip
, ifa
->ifa_netmask
->sa_family
);
4070 // Currently we use a few internal ones like mDNSInterfaceID_LocalOnly etc. that are negative values (0, -1, -2).
4071 else if ((int)if_nametoindex(ifa
->ifa_name
) <= 0)
4073 LogMsg("UpdateInterfaceList: if_nametoindex returned zero/negative value for %5s(%d)", ifa
->ifa_name
, if_nametoindex(ifa
->ifa_name
));
4077 // Make sure ifa_netmask->sa_family is set correctly
4078 // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
4079 ifa
->ifa_netmask
->sa_family
= ifa
->ifa_addr
->sa_family
;
4080 int ifru_flags6
= 0;
4082 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)ifa
->ifa_addr
;
4083 if (ifa
->ifa_addr
->sa_family
== AF_INET6
&& InfoSocket
>= 0)
4085 struct in6_ifreq ifr6
;
4086 mDNSPlatformMemZero((char *)&ifr6
, sizeof(ifr6
));
4087 strlcpy(ifr6
.ifr_name
, ifa
->ifa_name
, sizeof(ifr6
.ifr_name
));
4088 ifr6
.ifr_addr
= *sin6
;
4089 if (ioctl(InfoSocket
, SIOCGIFAFLAG_IN6
, &ifr6
) != -1)
4090 ifru_flags6
= ifr6
.ifr_ifru
.ifru_flags6
;
4091 verbosedebugf("%s %.16a %04X %04X", ifa
->ifa_name
, &sin6
->sin6_addr
, ifa
->ifa_flags
, ifru_flags6
);
4094 if (!(ifru_flags6
& (IN6_IFF_TENTATIVE
| IN6_IFF_DETACHED
| IN6_IFF_DEPRECATED
| IN6_IFF_TEMPORARY
)))
4096 AddInterfaceToList(ifa
, utc
);
4100 ifa
= ifa
->ifa_next
;
4103 if (InfoSocket
>= 0)
4106 mDNS_snprintf(defaultname
, sizeof(defaultname
), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen
, HINFO_HWstring
,
4107 m
->PrimaryMAC
.b
[0], m
->PrimaryMAC
.b
[1], m
->PrimaryMAC
.b
[2], m
->PrimaryMAC
.b
[3], m
->PrimaryMAC
.b
[4], m
->PrimaryMAC
.b
[5]);
4109 // Set up the nice label
4110 domainlabel nicelabel
;
4112 GetUserSpecifiedFriendlyComputerName(&nicelabel
);
4113 if (nicelabel
.c
[0] == 0)
4115 debugf("Couldn’t read user-specified Computer Name; using default “%s” instead", defaultname
);
4116 MakeDomainLabelFromLiteralString(&nicelabel
, defaultname
);
4119 // Set up the RFC 1034-compliant label
4120 domainlabel hostlabel
;
4122 GetUserSpecifiedLocalHostName(&hostlabel
);
4123 if (hostlabel
.c
[0] == 0)
4125 debugf("Couldn’t read user-specified Local Hostname; using default “%s.local” instead", defaultname
);
4126 MakeDomainLabelFromLiteralString(&hostlabel
, defaultname
);
4129 // We use a case-sensitive comparison here because even though changing the capitalization
4130 // of the name alone is not significant to DNS, it's still a change from the user's point of view
4131 if (SameDomainLabelCS(m
->p
->usernicelabel
.c
, nicelabel
.c
))
4132 debugf("Usernicelabel (%#s) unchanged since last time; not changing m->nicelabel (%#s)", m
->p
->usernicelabel
.c
, m
->nicelabel
.c
);
4135 if (m
->p
->usernicelabel
.c
[0]) // Don't show message first time through, when we first read name from prefs on boot
4136 LogMsg("User updated Computer Name from “%#s” to “%#s”", m
->p
->usernicelabel
.c
, nicelabel
.c
);
4137 m
->p
->usernicelabel
= m
->nicelabel
= nicelabel
;
4140 if (SameDomainLabelCS(m
->p
->userhostlabel
.c
, hostlabel
.c
))
4141 debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m
->p
->userhostlabel
.c
, m
->hostlabel
.c
);
4144 if (m
->p
->userhostlabel
.c
[0]) // Don't show message first time through, when we first read name from prefs on boot
4145 LogMsg("User updated Local Hostname from “%#s” to “%#s”", m
->p
->userhostlabel
.c
, hostlabel
.c
);
4146 m
->p
->userhostlabel
= m
->hostlabel
= hostlabel
;
4150 return(mStatus_NoError
);
4153 // Returns number of leading one-bits in mask: 0-32 for IPv4, 0-128 for IPv6
4154 // Returns -1 if all the one-bits are not contiguous
4155 mDNSlocal
int CountMaskBits(mDNSAddr
*mask
)
4157 int i
= 0, bits
= 0;
4158 int bytes
= mask
->type
== mDNSAddrType_IPv4
? 4 : mask
->type
== mDNSAddrType_IPv6
? 16 : 0;
4161 mDNSu8 b
= mask
->ip
.v6
.b
[i
++];
4162 while (b
& 0x80) { bits
++; b
<<= 1; }
4165 while (i
< bytes
) if (mask
->ip
.v6
.b
[i
++]) return(-1);
4169 // Returns count of non-link local V4 addresses registered (why? -- SC)
4170 mDNSlocal
int SetupActiveInterfaces(mDNSs32 utc
)
4172 mDNS
*const m
= &mDNSStorage
;
4173 NetworkInterfaceInfoOSX
*i
;
4176 // Recalculate SuppressProbes time based on the current set of active interfaces.
4177 m
->SuppressProbes
= 0;
4178 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
)
4181 NetworkInterfaceInfo
*const n
= &i
->ifinfo
;
4182 NetworkInterfaceInfoOSX
*primary
= SearchForInterfaceByName(i
->ifinfo
.ifname
, AF_UNSPEC
);
4184 if (i
->Registered
&& i
->Registered
!= primary
) // Sanity check
4186 LogMsg("SetupActiveInterfaces ERROR! n->Registered %p != primary %p", i
->Registered
, primary
);
4187 i
->Registered
= mDNSNULL
;
4192 InterfaceActivationSpeed activationSpeed
;
4194 // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface,
4195 // so we need to make sure we call mDNS_DeregisterInterface() before disposing it.
4196 // If i->Registered is NOT set, then we haven't registered it and we should not try to deregister it.
4197 i
->Registered
= primary
;
4199 // If i->LastSeen == utc, then this is a brand-new interface, just created, or an interface that never went away.
4200 // If i->LastSeen != utc, then this is an old interface, previously seen, that went away for (utc - i->LastSeen) seconds.
4201 // If the interface is an old one that went away and came back in less than a minute, then we're in a flapping scenario.
4202 i
->Occulting
= !(i
->ifa_flags
& IFF_LOOPBACK
) && (utc
- i
->LastSeen
> 0 && utc
- i
->LastSeen
< 60);
4204 // The "p2p*" interfaces used for legacy AirDrop reuse the scope-id, MAC address and the IP address
4205 // every time a new interface is created. We think it is a duplicate and hence consider it
4206 // as flashing and occulting, that is, flapping. If an interface is marked as flapping,
4207 // mDNS_RegisterInterface() changes the probe delay from 1/2 second to 5 seconds and
4208 // logs a warning message to system.log noting frequent interface transitions.
4209 // The same logic applies when the IFEF_DIRECTLINK flag is set on the interface.
4210 if ((strncmp(i
->ifinfo
.ifname
, "p2p", 3) == 0) || i
->ifinfo
.DirectLink
)
4212 activationSpeed
= FastActivation
;
4213 LogInfo("SetupActiveInterfaces: %s DirectLink interface registering", i
->ifinfo
.ifname
);
4215 #if MDNSRESPONDER_SUPPORTS(APPLE, SLOW_ACTIVATION)
4216 else if (i
->Flashing
&& i
->Occulting
)
4218 activationSpeed
= SlowActivation
;
4223 activationSpeed
= NormalActivation
;
4226 mDNS_RegisterInterface(m
, n
, activationSpeed
);
4228 if (!mDNSAddressIsLinkLocal(&n
->ip
)) count
++;
4229 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
4230 "SetupActiveInterfaces: Registered " PUB_S
" (%u) BSSID " PRI_MAC_ADDR
" Struct addr %p, primary %p,"
4231 " " PRI_IP_ADDR
"/%d" PUB_S PUB_S PUB_S
,
4232 i
->ifinfo
.ifname
, i
->scope_id
, &i
->BSSID
, i
, primary
, &n
->ip
, CountMaskBits(&n
->mask
),
4233 i
->Flashing
? " (Flashing)" : "",
4234 i
->Occulting
? " (Occulting)" : "",
4235 n
->InterfaceActive
? " (Primary)" : "");
4239 debugf("SetupActiveInterfaces: No Tx/Rx on %5s(%lu) %.6a InterfaceID %p %#a", i
->ifinfo
.ifname
, i
->scope_id
, &i
->BSSID
, i
->ifinfo
.InterfaceID
, &n
->ip
);
4240 #if TARGET_OS_IPHONE
4241 // We join the Bonjour multicast group on Apple embedded platforms ONLY when a client request is active,
4242 // so we leave the multicast group here to clear any residual group membership.
4243 if (i
->sa_family
== AF_INET
)
4246 primary
->ifa_v4addr
.s_addr
= n
->ip
.ip
.v4
.NotAnInteger
;
4247 imr
.imr_multiaddr
.s_addr
= AllDNSLinkGroup_v4
.ip
.v4
.NotAnInteger
;
4248 imr
.imr_interface
= primary
->ifa_v4addr
;
4250 if (SearchForInterfaceByName(i
->ifinfo
.ifname
, AF_INET
) == i
)
4252 LogInfo("SetupActiveInterfaces: %5s(%lu) Doing IP_DROP_MEMBERSHIP for %.4a on %.4a", i
->ifinfo
.ifname
, i
->scope_id
, &imr
.imr_multiaddr
, &imr
.imr_interface
);
4253 mStatus err
= setsockopt(m
->p
->permanentsockets
.sktv4
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, &imr
, sizeof(imr
));
4254 if (err
< 0 && (errno
!= EADDRNOTAVAIL
))
4255 LogMsg("setsockopt - IP_DROP_MEMBERSHIP error %d errno %d (%s)", err
, errno
, strerror(errno
));
4258 if (i
->sa_family
== AF_INET6
)
4260 struct ipv6_mreq i6mr
;
4261 i6mr
.ipv6mr_interface
= primary
->scope_id
;
4262 i6mr
.ipv6mr_multiaddr
= *(struct in6_addr
*)&AllDNSLinkGroup_v6
.ip
.v6
;
4264 if (SearchForInterfaceByName(i
->ifinfo
.ifname
, AF_INET6
) == i
)
4266 LogInfo("SetupActiveInterfaces: %5s(%lu) Doing IPV6_LEAVE_GROUP for %.16a on %u", i
->ifinfo
.ifname
, i
->scope_id
, &i6mr
.ipv6mr_multiaddr
, i6mr
.ipv6mr_interface
);
4267 mStatus err
= setsockopt(m
->p
->permanentsockets
.sktv6
, IPPROTO_IPV6
, IPV6_LEAVE_GROUP
, &i6mr
, sizeof(i6mr
));
4268 if (err
< 0 && (errno
!= EADDRNOTAVAIL
))
4269 LogMsg("setsockopt - IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err
, errno
, strerror(errno
), &i6mr
.ipv6mr_multiaddr
, i6mr
.ipv6mr_interface
);
4272 #endif // TARGET_OS_IPHONE
4276 if (i
->sa_family
== AF_INET
)
4279 primary
->ifa_v4addr
.s_addr
= n
->ip
.ip
.v4
.NotAnInteger
;
4280 imr
.imr_multiaddr
.s_addr
= AllDNSLinkGroup_v4
.ip
.v4
.NotAnInteger
;
4281 imr
.imr_interface
= primary
->ifa_v4addr
;
4283 // If this is our *first* IPv4 instance for this interface name, we need to do a IP_DROP_MEMBERSHIP first,
4284 // before trying to join the group, to clear out stale kernel state which may be lingering.
4285 // In particular, this happens with removable network interfaces like USB Ethernet adapters -- the kernel has stale state
4286 // from the last time the USB Ethernet adapter was connected, and part of the kernel thinks we've already joined the group
4287 // on that interface (so we get EADDRINUSE when we try to join again) but a different part of the kernel thinks we haven't
4288 // joined the group (so we receive no multicasts). Doing an IP_DROP_MEMBERSHIP before joining seems to flush the stale state.
4289 // Also, trying to make the code leave the group when the adapter is removed doesn't work either,
4290 // because by the time we get the configuration change notification, the interface is already gone,
4291 // so attempts to unsubscribe fail with EADDRNOTAVAIL (errno 49 "Can't assign requested address").
4292 // <rdar://problem/5585972> IP_ADD_MEMBERSHIP fails for previously-connected removable interfaces
4293 if (SearchForInterfaceByName(i
->ifinfo
.ifname
, AF_INET
) == i
)
4295 LogInfo("SetupActiveInterfaces: %5s(%lu) Doing precautionary IP_DROP_MEMBERSHIP for %.4a on %.4a", i
->ifinfo
.ifname
, i
->scope_id
, &imr
.imr_multiaddr
, &imr
.imr_interface
);
4296 mStatus err
= setsockopt(m
->p
->permanentsockets
.sktv4
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, &imr
, sizeof(imr
));
4297 if (err
< 0 && (errno
!= EADDRNOTAVAIL
))
4298 LogMsg("setsockopt - IP_DROP_MEMBERSHIP error %d errno %d (%s)", err
, errno
, strerror(errno
));
4301 LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv4 mcast group %.4a on %.4a", i
->ifinfo
.ifname
, i
->scope_id
, &imr
.imr_multiaddr
, &imr
.imr_interface
);
4302 mStatus err
= setsockopt(m
->p
->permanentsockets
.sktv4
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &imr
, sizeof(imr
));
4303 // Joining same group twice can give "Address already in use" error -- no need to report that
4304 if (err
< 0 && (errno
!= EADDRINUSE
))
4305 LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err
, errno
, strerror(errno
), &imr
.imr_multiaddr
, &imr
.imr_interface
);
4307 if (i
->sa_family
== AF_INET6
)
4309 struct ipv6_mreq i6mr
;
4310 i6mr
.ipv6mr_interface
= primary
->scope_id
;
4311 i6mr
.ipv6mr_multiaddr
= *(struct in6_addr
*)&AllDNSLinkGroup_v6
.ip
.v6
;
4313 if (SearchForInterfaceByName(i
->ifinfo
.ifname
, AF_INET6
) == i
)
4315 LogInfo("SetupActiveInterfaces: %5s(%lu) Doing precautionary IPV6_LEAVE_GROUP for %.16a on %u", i
->ifinfo
.ifname
, i
->scope_id
, &i6mr
.ipv6mr_multiaddr
, i6mr
.ipv6mr_interface
);
4316 mStatus err
= setsockopt(m
->p
->permanentsockets
.sktv6
, IPPROTO_IPV6
, IPV6_LEAVE_GROUP
, &i6mr
, sizeof(i6mr
));
4317 if (err
< 0 && (errno
!= EADDRNOTAVAIL
))
4318 LogMsg("setsockopt - IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err
, errno
, strerror(errno
), &i6mr
.ipv6mr_multiaddr
, i6mr
.ipv6mr_interface
);
4321 LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv6 mcast group %.16a on %u", i
->ifinfo
.ifname
, i
->scope_id
, &i6mr
.ipv6mr_multiaddr
, i6mr
.ipv6mr_interface
);
4322 mStatus err
= setsockopt(m
->p
->permanentsockets
.sktv6
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &i6mr
, sizeof(i6mr
));
4323 // Joining same group twice can give "Address already in use" error -- no need to report that
4324 if (err
< 0 && (errno
!= EADDRINUSE
))
4325 LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err
, errno
, strerror(errno
), &i6mr
.ipv6mr_multiaddr
, i6mr
.ipv6mr_interface
);
4334 mDNSlocal
void MarkAllInterfacesInactive(mDNSs32 utc
)
4336 NetworkInterfaceInfoOSX
*i
;
4337 for (i
= mDNSStorage
.p
->InterfaceList
; i
; i
= i
->next
)
4339 if (i
->Exists
) i
->LastSeen
= utc
;
4340 i
->Exists
= mDNSfalse
;
4344 // Returns count of non-link local V4 addresses deregistered (why? -- SC)
4345 mDNSlocal
int ClearInactiveInterfaces(mDNSs32 utc
)
4347 mDNS
*const m
= &mDNSStorage
;
4349 // If an interface is going away, then deregister this from the mDNSCore.
4350 // We also have to deregister it if the primary interface that it's using for its InterfaceID is going away.
4351 // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory
4352 // it refers to has gone away we'll crash.
4353 NetworkInterfaceInfoOSX
*i
;
4355 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
)
4357 // If this interface is no longer active, or its InterfaceID is changing, deregister it
4358 NetworkInterfaceInfoOSX
*primary
= SearchForInterfaceByName(i
->ifinfo
.ifname
, AF_UNSPEC
);
4360 if (i
->Exists
== 0 || i
->Exists
== MulticastStateChanged
|| i
->Registered
!= primary
)
4362 InterfaceActivationSpeed activationSpeed
;
4364 i
->Flashing
= !(i
->ifa_flags
& IFF_LOOPBACK
) && (utc
- i
->AppearanceTime
< 60);
4365 LogInfo("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s",
4366 i
->ifinfo
.ifname
, i
->scope_id
, &i
->BSSID
, i
->ifinfo
.InterfaceID
, i
, primary
,
4367 &i
->ifinfo
.ip
, CountMaskBits(&i
->ifinfo
.mask
),
4368 i
->Flashing
? " (Flashing)" : "",
4369 i
->Occulting
? " (Occulting)" : "",
4370 i
->ifinfo
.InterfaceActive
? " (Primary)" : "");
4372 // "p2p*" interfaces used for legacy AirDrop reuse the scope-id, MAC address and the IP address
4373 // every time it creates a new interface. We think it is a duplicate and hence consider it
4374 // as flashing and occulting. The "core" does not flush the cache for this case. This leads to
4375 // stale data returned to the application even after the interface is removed. The application
4376 // then starts to send data but the new interface is not yet created.
4377 // The same logic applies when the IFEF_DIRECTLINK flag is set on the interface.
4378 if ((strncmp(i
->ifinfo
.ifname
, "p2p", 3) == 0) || i
->ifinfo
.DirectLink
)
4380 activationSpeed
= FastActivation
;
4381 LogInfo("ClearInactiveInterfaces: %s DirectLink interface deregistering", i
->ifinfo
.ifname
);
4383 #if MDNSRESPONDER_SUPPORTS(APPLE, SLOW_ACTIVATION)
4384 else if (i
->Flashing
&& i
->Occulting
)
4386 activationSpeed
= SlowActivation
;
4391 activationSpeed
= NormalActivation
;
4393 mDNS_DeregisterInterface(m
, &i
->ifinfo
, activationSpeed
);
4395 if (!mDNSAddressIsLinkLocal(&i
->ifinfo
.ip
)) count
++;
4396 i
->Registered
= mDNSNULL
;
4397 // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface,
4398 // so we need to make sure we call mDNS_DeregisterInterface() before disposing it.
4399 // If i->Registered is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it.
4401 // Caution: If we ever decide to add code here to leave the multicast group, we need to make sure that this
4402 // is the LAST representative of this physical interface, or we'll unsubscribe from the group prematurely.
4407 // Now that everything that's going to deregister has done so, we can clean up and free the memory
4408 NetworkInterfaceInfoOSX
**p
= &m
->p
->InterfaceList
;
4412 // If no longer active, delete interface from list and free memory
4415 if (i
->LastSeen
== utc
) i
->LastSeen
= utc
- 1;
4416 const mDNSBool
delete = ((utc
- i
->LastSeen
) >= 60) ? mDNStrue
: mDNSfalse
;
4417 LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p(%p) %#a/%d Age %d%s", delete ? "Deleting" : "Holding",
4418 i
->ifinfo
.ifname
, i
->scope_id
, &i
->BSSID
, i
->ifinfo
.InterfaceID
, i
,
4419 &i
->ifinfo
.ip
, CountMaskBits(&i
->ifinfo
.mask
), utc
- i
->LastSeen
,
4420 i
->ifinfo
.InterfaceActive
? " (Primary)" : "");
4421 #if APPLE_OSX_mDNSResponder
4422 if (i
->BPF_fd
>= 0) CloseBPF(i
);
4423 #endif // APPLE_OSX_mDNSResponder
4427 freeL("NetworkInterfaceInfoOSX", i
);
4428 continue; // After deleting this object, don't want to do the "p = &i->next;" thing at the end of the loop
4436 mDNSlocal
void AppendDNameListElem(DNameListElem
***List
, mDNSu32 uid
, domainname
*name
)
4438 DNameListElem
*dnle
= (DNameListElem
*) callocL("DNameListElem/AppendDNameListElem", sizeof(*dnle
));
4439 if (!dnle
) LogMsg("ERROR: AppendDNameListElem: memory exhausted");
4442 dnle
->next
= mDNSNULL
;
4444 AssignDomainName(&dnle
->name
, name
);
4446 *List
= &dnle
->next
;
4450 mDNSlocal
int compare_dns_configs(const void *aa
, const void *bb
)
4452 dns_resolver_t
*a
= *(dns_resolver_t
**)aa
;
4453 dns_resolver_t
*b
= *(dns_resolver_t
**)bb
;
4455 return (a
->search_order
< b
->search_order
) ? -1 : (a
->search_order
== b
->search_order
) ? 0 : 1;
4458 mDNSlocal
void UpdateSearchDomainHash(MD5_CTX
*sdc
, char *domain
, mDNSInterfaceID InterfaceID
)
4460 mDNS
*const m
= &mDNSStorage
;
4462 mDNSu32 scopeid
= 0;
4468 // Hash the search domain name followed by the InterfaceID.
4469 // As we have scoped search domains, we also included InterfaceID. If either of them change,
4470 // we will detect it. Even if the order of them change, we will detect it.
4472 // Note: We have to handle a few of these tricky cases.
4474 // 1) Current: com, apple.com Changing to: comapple.com
4475 // 2) Current: a.com,b.com Changing to a.comb.com
4476 // 3) Current: a.com,b.com (ifid 8), Changing to a.com8b.com (ifid 8)
4477 // 4) Current: a.com (ifid 12), Changing to a.com1 (ifid: 2)
4479 // There are more variants of the above. The key thing is if we include the null in each case
4480 // at the end of name and the InterfaceID, it will prevent a new name (which can't include
4481 // NULL as part of the name) to be mistakenly thought of as a old name.
4483 scopeid
= mDNSPlatformInterfaceIndexfromInterfaceID(m
, InterfaceID
, mDNStrue
);
4484 // mDNS_snprintf always null terminates
4485 if (mDNS_snprintf(ifid_buf
, sizeof(ifid_buf
), "%u", scopeid
) >= sizeof(ifid_buf
))
4486 LogMsg("UpdateSearchDomainHash: mDNS_snprintf failed for scopeid %u", scopeid
);
4488 LogInfo("UpdateSearchDomainHash: buf %s, ifid_buf %s", buf
, ifid_buf
);
4489 MD5_Update(sdc
, buf
, strlen(buf
) + 1);
4490 MD5_Update(sdc
, ifid_buf
, strlen(ifid_buf
) + 1);
4493 mDNSlocal
void FinalizeSearchDomainHash(MD5_CTX
*sdc
)
4495 mDNS
*const m
= &mDNSStorage
;
4496 mDNSu8 md5_hash
[MD5_LEN
];
4498 MD5_Final(md5_hash
, sdc
);
4500 if (memcmp(md5_hash
, m
->SearchDomainsHash
, MD5_LEN
))
4502 // If the hash is different, either the search domains have changed or
4503 // the ordering between them has changed. Restart the questions that
4504 // would be affected by this.
4505 LogInfo("FinalizeSearchDomains: The hash is different");
4506 memcpy(m
->SearchDomainsHash
, md5_hash
, MD5_LEN
);
4507 RetrySearchDomainQuestions(m
);
4509 else { LogInfo("FinalizeSearchDomains: The hash is same"); }
4512 mDNSlocal
void ConfigSearchDomains(dns_resolver_t
*resolver
, mDNSInterfaceID interfaceId
, mDNSu32 scope
, MD5_CTX
*sdc
, uint64_t generation
)
4514 const char *scopeString
= DNSScopeToString(scope
);
4518 if (scope
== kScopeNone
)
4519 interfaceId
= mDNSInterface_Any
;
4521 if (scope
== kScopeNone
|| scope
== kScopeInterfaceID
)
4523 for (j
= 0; j
< resolver
->n_search
; j
++)
4525 if (MakeDomainNameFromDNSNameString(&d
, resolver
->search
[j
]) != NULL
)
4527 char interface_buf
[32];
4528 mDNS_snprintf(interface_buf
, sizeof(interface_buf
), "for interface %s", InterfaceNameForID(&mDNSStorage
, interfaceId
));
4529 LogInfo("ConfigSearchDomains: (%s) configuring search domain %s %s (generation= %llu)", scopeString
,
4530 resolver
->search
[j
], (interfaceId
== mDNSInterface_Any
) ? "" : interface_buf
, generation
);
4531 UpdateSearchDomainHash(sdc
, resolver
->search
[j
], interfaceId
);
4532 mDNS_AddSearchDomain_CString(resolver
->search
[j
], interfaceId
);
4536 LogInfo("ConfigSearchDomains: An invalid search domain was detected for %s domain %s n_nameserver %d, (generation= %llu)",
4537 DNSScopeToString(scope
), resolver
->domain
, resolver
->n_nameserver
, generation
);
4543 LogInfo("ConfigSearchDomains: (%s) Ignoring search domain for interface %s", scopeString
, InterfaceNameForID(&mDNSStorage
, interfaceId
));
4547 mDNSlocal mDNSInterfaceID
ConfigParseInterfaceID(mDNSu32 ifindex
)
4549 NetworkInterfaceInfoOSX
*ni
;
4550 mDNSInterfaceID interface
;
4552 for (ni
= mDNSStorage
.p
->InterfaceList
; ni
; ni
= ni
->next
)
4554 if (ni
->ifinfo
.InterfaceID
&& ni
->scope_id
== ifindex
)
4559 interface
= ni
->ifinfo
.InterfaceID
;
4563 // In rare circumstances, we could potentially hit this case where we cannot parse the InterfaceID
4564 // (see <rdar://problem/13214785>). At this point, we still accept the DNS Config from configd
4565 // Note: We currently ack the whole dns configuration and not individual resolvers or DNS servers.
4566 // As the caller is going to ack the configuration always, we have to add all the DNS servers
4567 // in the configuration. Otherwise, we won't have any DNS servers up until the network change.
4569 LogMsg("ConfigParseInterfaceID: interface specific index %d not found (interface may not be UP)",ifindex
);
4571 // Set the correct interface from configd before passing this to mDNS_AddDNSServer() below
4572 interface
= (mDNSInterfaceID
)(unsigned long)ifindex
;
4577 mDNSlocal
void ConfigNonUnicastResolver(dns_resolver_t
*r
)
4579 char *opt
= r
->options
;
4582 if (opt
&& !strncmp(opt
, "mdns", strlen(opt
)))
4584 if (!MakeDomainNameFromDNSNameString(&d
, r
->domain
))
4586 LogMsg("ConfigNonUnicastResolver: config->resolver bad domain %s", r
->domain
);
4589 mDNS_AddMcastResolver(&mDNSStorage
, &d
, mDNSInterface_Any
, r
->timeout
);
4593 mDNSlocal
void ConfigDNSServers(dns_resolver_t
*r
, mDNSInterfaceID interfaceID
, mDNSu32 scope
, mDNSu32 resGroupID
)
4596 if (!r
->domain
|| (*r
->domain
== '\0'))
4600 else if (!MakeDomainNameFromDNSNameString(&domain
, r
->domain
))
4602 LogMsg("ConfigDNSServers: bad domain %s", r
->domain
);
4605 // Parse the resolver specific attributes that affects all the DNS servers.
4606 const int32_t serviceID
= (scope
== kScopeServiceID
) ? r
->service_identifier
: 0;
4608 const mdns_interface_monitor_t monitor
= GetInterfaceMonitorForIndex((uint32_t)((uintptr_t)interfaceID
));
4609 const mDNSBool isExpensive
= (monitor
&& mdns_interface_monitor_is_expensive(monitor
)) ? mDNStrue
: mDNSfalse
;
4610 const mDNSBool isConstrained
= (monitor
&& mdns_interface_monitor_is_constrained(monitor
)) ? mDNStrue
: mDNSfalse
;
4611 const mDNSBool isCLAT46
= (monitor
&& mdns_interface_monitor_is_clat46(monitor
)) ? mDNStrue
: mDNSfalse
;
4612 const mDNSBool usableA
= (r
->flags
& DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS
) ? mDNStrue
: mDNSfalse
;
4613 const mDNSBool usableAAAA
= (r
->flags
& DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS
) ? mDNStrue
: mDNSfalse
;
4614 #if TARGET_OS_IPHONE
4615 const mDNSBool isCell
= (r
->reach_flags
& kSCNetworkReachabilityFlagsIsWWAN
) ? mDNStrue
: mDNSfalse
;
4617 const mDNSBool isCell
= mDNSfalse
;
4620 const mDNSIPPort port
= (r
->port
!= 0) ? mDNSOpaque16fromIntVal(r
->port
) : UnicastDNSPort
;
4621 for (int32_t i
= 0; i
< r
->n_nameserver
; i
++)
4623 const int family
= r
->nameserver
[i
]->sa_family
;
4624 if ((family
!= AF_INET
) && (family
!= AF_INET6
)) continue;
4627 if (SetupAddr(&saddr
, r
->nameserver
[i
]))
4629 LogMsg("ConfigDNSServers: Bad address");
4633 // The timeout value is for all the DNS servers in a given resolver, hence we pass
4634 // the timeout value only for the first DNSServer. If we don't have a value in the
4635 // resolver, then use the core's default value
4637 // Note: this assumes that when the core picks a list of DNSServers for a question,
4638 // it takes the sum of all the timeout values for all DNS servers. By doing this, it
4639 // tries all the DNS servers in a specified timeout
4640 DNSServer
*s
= mDNS_AddDNSServer(&mDNSStorage
, &domain
, interfaceID
, serviceID
, &saddr
, port
, scope
,
4641 (i
== 0) ? (r
->timeout
? r
->timeout
: DEFAULT_UDNS_TIMEOUT
) : 0, isCell
, isExpensive
, isConstrained
, isCLAT46
,
4642 resGroupID
, usableA
, usableAAAA
, mDNStrue
);
4645 LogInfo("ConfigDNSServers(%s): DNS server %#a:%d for domain %##s",
4646 DNSScopeToString(scope
), &s
->addr
, mDNSVal16(s
->port
), domain
.c
);
4651 // ConfigResolvers is called for different types of resolvers: Unscoped resolver, Interface scope resolver and
4652 // Service scope resolvers. This is indicated by the scope argument.
4654 // "resolver" has entries that should only be used for unscoped questions.
4656 // "scoped_resolver" has entries that should only be used for Interface scoped question i.e., questions that specify an
4657 // interface index (q->InterfaceID)
4659 // "service_specific_resolver" has entries that should be used for Service scoped question i.e., questions that specify
4660 // a service identifier (q->ServiceID)
4662 mDNSlocal
void ConfigResolvers(dns_config_t
*config
, mDNSu32 scope
, mDNSBool setsearch
, mDNSBool setservers
, MD5_CTX
*sdc
)
4665 dns_resolver_t
**resolver
;
4667 const char *scopeString
= DNSScopeToString(scope
);
4668 mDNSInterfaceID interface
;
4673 resolver
= config
->resolver
;
4674 nresolvers
= config
->n_resolver
;
4676 case kScopeInterfaceID
:
4677 resolver
= config
->scoped_resolver
;
4678 nresolvers
= config
->n_scoped_resolver
;
4680 case kScopeServiceID
:
4681 resolver
= config
->service_specific_resolver
;
4682 nresolvers
= config
->n_service_specific_resolver
;
4687 qsort(resolver
, nresolvers
, sizeof(dns_resolver_t
*), compare_dns_configs
);
4689 for (i
= 0; i
< nresolvers
; i
++)
4691 dns_resolver_t
*r
= resolver
[i
];
4693 LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scopeString
, i
, r
->domain
, r
->n_nameserver
);
4695 interface
= mDNSInterface_Any
;
4697 // Parse the interface index
4698 if (r
->if_index
!= 0)
4700 interface
= ConfigParseInterfaceID(r
->if_index
);
4705 ConfigSearchDomains(resolver
[i
], interface
, scope
, sdc
, config
->generation
);
4707 // Parse other scoped resolvers for search lists
4712 if (r
->port
== 5353 || r
->n_nameserver
== 0)
4714 ConfigNonUnicastResolver(r
);
4718 ConfigDNSServers(r
, interface
, scope
, mDNS_GetNextResolverGroupID());
4723 #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER)
4724 mDNSlocal mDNSBool
QuestionValidForDNSTrigger(const DNSQuestion
*q
)
4728 debugf("QuestionValidForDNSTrigger: Suppressed: %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4731 if (mDNSOpaque16IsZero(q
->TargetQID
))
4733 debugf("QuestionValidForDNSTrigger: Multicast: %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4736 // If we answered using LocalOnly records e.g., /etc/hosts, don't consider that a valid response
4738 if (q
->LOAddressAnswers
)
4740 debugf("QuestionValidForDNSTrigger: LocalOnly answers: %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4746 // This function is called if we are not delivering unicast answers to "A" or "AAAA" questions.
4747 // We set our state appropriately so that if we start receiving answers, trigger the
4748 // upper layer to retry DNS questions.
4749 mDNSexport
void mDNSPlatformUpdateDNSStatus(const DNSQuestion
*q
)
4751 mDNS
*const m
= &mDNSStorage
;
4752 if (!QuestionValidForDNSTrigger(q
))
4755 // Ignore applications that start and stop queries for no reason before we ever talk
4756 // to any DNS server.
4757 if (!q
->triedAllServersOnce
)
4759 LogInfo("QuestionValidForDNSTrigger: question %##s (%s) stopped too soon", q
->qname
.c
, DNSTypeName(q
->qtype
));
4762 if (q
->qtype
== kDNSType_A
)
4763 m
->p
->v4answers
= 0;
4764 if (q
->qtype
== kDNSType_AAAA
)
4765 m
->p
->v6answers
= 0;
4766 if (!m
->p
->v4answers
|| !m
->p
->v6answers
)
4768 LogInfo("mDNSPlatformUpdateDNSStatus: Trigger needed v4 %d, v6 %d, question %##s (%s)", m
->p
->v4answers
, m
->p
->v6answers
, q
->qname
.c
,
4769 DNSTypeName(q
->qtype
));
4772 #endif // MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER)
4774 mDNSlocal
void AckConfigd(dns_config_t
*config
)
4776 mDNS_CheckLock(&mDNSStorage
);
4778 // Acking the configuration triggers configd to reissue the reachability queries
4779 mDNSStorage
.p
->DNSTrigger
= NonZeroTime(mDNSStorage
.timenow
);
4780 _dns_configuration_ack(config
, "com.apple.mDNSResponder");
4783 #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER)
4784 // If v4q is non-NULL, it means we have received some answers for "A" type questions
4785 // If v6q is non-NULL, it means we have received some answers for "AAAA" type questions
4786 mDNSexport
void mDNSPlatformTriggerDNSRetry(const DNSQuestion
*v4q
, const DNSQuestion
*v6q
)
4788 mDNS
*const m
= &mDNSStorage
;
4789 mDNSBool trigger
= mDNSfalse
;
4792 // Don't send triggers too often.
4793 // If we have started delivering answers to questions, we should send a trigger
4794 // if the time permits. If we are delivering answers, we should set the state
4795 // of v4answers/v6answers to 1 and avoid sending a trigger. But, we don't know
4796 // whether the answers that are being delivered currently is for configd or some
4797 // other application. If we set the v4answers/v6answers to 1 and not deliver a trigger,
4798 // then we won't deliver the trigger later when it is okay to send one as the
4799 // "answers" are already set to 1. Hence, don't affect the state of v4answers and
4800 // v6answers if we are not delivering triggers.
4802 timenow
= m
->timenow
;
4803 if (m
->p
->DNSTrigger
&& (timenow
- m
->p
->DNSTrigger
) < DNS_TRIGGER_INTERVAL
)
4805 if (!m
->p
->v4answers
|| !m
->p
->v6answers
)
4807 debugf("mDNSPlatformTriggerDNSRetry: not triggering, time since last trigger %d ms, v4ans %d, v6ans %d",
4808 (timenow
- m
->p
->DNSTrigger
), m
->p
->v4answers
, m
->p
->v6answers
);
4814 if (v4q
!= NULL
&& QuestionValidForDNSTrigger(v4q
))
4816 int old
= m
->p
->v4answers
;
4818 m
->p
->v4answers
= 1;
4820 // If there are IPv4 answers now and previously we did not have
4821 // any answers, trigger a DNS change so that reachability
4822 // can retry the queries again.
4825 LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv4, last trigger %d ms, %##s (%s)", (timenow
- m
->p
->DNSTrigger
),
4826 v4q
->qname
.c
, DNSTypeName(v4q
->qtype
));
4830 if (v6q
!= NULL
&& QuestionValidForDNSTrigger(v6q
))
4832 int old
= m
->p
->v6answers
;
4834 m
->p
->v6answers
= 1;
4835 // If there are IPv6 answers now and previously we did not have
4836 // any answers, trigger a DNS change so that reachability
4837 // can retry the queries again.
4840 LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv6, last trigger %d ms, %##s (%s)", (timenow
- m
->p
->DNSTrigger
),
4841 v6q
->qname
.c
, DNSTypeName(v6q
->qtype
));
4847 dns_config_t
*config
= dns_configuration_copy();
4853 dns_configuration_free(config
);
4857 LogMsg("mDNSPlatformTriggerDNSRetry: ERROR!! configd did not return config");
4861 #endif // MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER)
4863 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
4864 mDNSlocal
void SetupActiveDirectoryDomain(dns_config_t
*config
)
4866 // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up
4867 // by someone using Microsoft Active Directory using "local" as a private internal top-level domain
4868 if (config
->n_resolver
&& config
->resolver
[0]->domain
&& config
->resolver
[0]->n_nameserver
&&
4869 config
->resolver
[0]->nameserver
[0])
4871 MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain
, config
->resolver
[0]->domain
);
4875 ActiveDirectoryPrimaryDomain
.c
[0] = 0;
4878 //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local");
4879 ActiveDirectoryPrimaryDomainLabelCount
= CountLabels(&ActiveDirectoryPrimaryDomain
);
4880 if (config
->n_resolver
&& config
->resolver
[0]->n_nameserver
&&
4881 SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain
, ActiveDirectoryPrimaryDomainLabelCount
- 1), &localdomain
))
4883 SetupAddr(&ActiveDirectoryPrimaryDomainServer
, config
->resolver
[0]->nameserver
[0]);
4887 AssignConstStringDomainName(&ActiveDirectoryPrimaryDomain
, "");
4888 ActiveDirectoryPrimaryDomainLabelCount
= 0;
4889 ActiveDirectoryPrimaryDomainServer
= zeroAddr
;
4894 mDNSlocal
void SetupDDNSDomains(domainname
*const fqdn
, DNameListElem
**RegDomains
, DNameListElem
**BrowseDomains
)
4897 char buf
[MAX_ESCAPED_DOMAIN_NAME
]; // Max legal C-string name, including terminating NULL
4900 CFDictionaryRef ddnsdict
= SCDynamicStoreCopyValue(NULL
, NetworkChangedKey_DynamicDNS
);
4905 CFArrayRef fqdnArray
= CFDictionaryGetValue(ddnsdict
, CFSTR("HostNames"));
4906 if (fqdnArray
&& CFArrayGetCount(fqdnArray
) > 0)
4908 // for now, we only look at the first array element. if we ever support multiple configurations, we will walk the list
4909 CFDictionaryRef fqdnDict
= CFArrayGetValueAtIndex(fqdnArray
, 0);
4910 if (fqdnDict
&& DictionaryIsEnabled(fqdnDict
))
4912 CFStringRef name
= CFDictionaryGetValue(fqdnDict
, CFSTR("Domain"));
4915 if (!CFStringGetCString(name
, buf
, sizeof(buf
), kCFStringEncodingUTF8
) ||
4916 !MakeDomainNameFromDNSNameString(fqdn
, buf
) || !fqdn
->c
[0])
4917 LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS host name: %s", buf
[0] ? buf
: "(unknown)");
4919 debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS host name: %s", buf
);
4926 CFArrayRef regArray
= CFDictionaryGetValue(ddnsdict
, CFSTR("RegistrationDomains"));
4927 if (regArray
&& CFArrayGetCount(regArray
) > 0)
4929 CFDictionaryRef regDict
= CFArrayGetValueAtIndex(regArray
, 0);
4930 if (regDict
&& DictionaryIsEnabled(regDict
))
4932 CFStringRef name
= CFDictionaryGetValue(regDict
, CFSTR("Domain"));
4935 if (!CFStringGetCString(name
, buf
, sizeof(buf
), kCFStringEncodingUTF8
) ||
4936 !MakeDomainNameFromDNSNameString(&d
, buf
) || !d
.c
[0])
4937 LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS registration domain: %s", buf
[0] ? buf
: "(unknown)");
4940 debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration domain: %s", buf
);
4941 AppendDNameListElem(&RegDomains
, 0, &d
);
4949 CFArrayRef browseArray
= CFDictionaryGetValue(ddnsdict
, CFSTR("BrowseDomains"));
4952 for (i
= 0; i
< CFArrayGetCount(browseArray
); i
++)
4954 CFDictionaryRef browseDict
= CFArrayGetValueAtIndex(browseArray
, i
);
4955 if (browseDict
&& DictionaryIsEnabled(browseDict
))
4957 CFStringRef name
= CFDictionaryGetValue(browseDict
, CFSTR("Domain"));
4960 if (!CFStringGetCString(name
, buf
, sizeof(buf
), kCFStringEncodingUTF8
) ||
4961 !MakeDomainNameFromDNSNameString(&d
, buf
) || !d
.c
[0])
4962 LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS browsing domain: %s", buf
[0] ? buf
: "(unknown)");
4965 debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browsing domain: %s", buf
);
4966 AppendDNameListElem(&BrowseDomains
, 0, &d
);
4973 CFRelease(ddnsdict
);
4977 // Returns mDNSfalse, if it does not set the configuration i.e., if the DNS configuration did not change
4978 mDNSexport mDNSBool
mDNSPlatformSetDNSConfig(mDNSBool setservers
, mDNSBool setsearch
, domainname
*const fqdn
,
4979 DNameListElem
**RegDomains
, DNameListElem
**BrowseDomains
, mDNSBool ackConfig
)
4981 mDNS
*const m
= &mDNSStorage
;
4982 MD5_CTX sdc
; // search domain context
4984 // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed
4985 if (fqdn
) fqdn
->c
[0] = 0;
4986 if (RegDomains
) *RegDomains
= NULL
;
4987 if (BrowseDomains
) *BrowseDomains
= NULL
;
4989 LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s",
4990 setservers
? " setservers" : "",
4991 setsearch
? " setsearch" : "",
4992 fqdn
? " fqdn" : "",
4993 RegDomains
? " RegDomains" : "",
4994 BrowseDomains
? " BrowseDomains" : "");
4996 if (setsearch
) MD5_Init(&sdc
);
4998 // Add the inferred address-based configuration discovery domains
4999 // (should really be in core code I think, not platform-specific)
5002 struct ifaddrs
*ifa
= mDNSNULL
;
5003 struct sockaddr_in saddr
;
5004 mDNSPlatformMemZero(&saddr
, sizeof(saddr
));
5005 saddr
.sin_len
= sizeof(saddr
);
5006 saddr
.sin_family
= AF_INET
;
5008 saddr
.sin_addr
.s_addr
= *(in_addr_t
*)&m
->Router
.ip
.v4
;
5010 // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation
5011 if (!AddrRequiresPPPConnection((struct sockaddr
*)&saddr
)) ifa
= myGetIfAddrs(1);
5018 if (ifa
->ifa_addr
->sa_family
== AF_INET
&&
5020 !(ifa
->ifa_flags
& IFF_LOOPBACK
) &&
5021 !SetupAddr(&a
, ifa
->ifa_addr
) &&
5022 !mDNSv4AddressIsLinkLocal(&a
.ip
.v4
) )
5024 // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be incorrect, so we explicitly fix it here before calling SetupAddr
5025 // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
5026 ifa
->ifa_netmask
->sa_family
= ifa
->ifa_addr
->sa_family
; // Make sure ifa_netmask->sa_family is set correctly
5027 SetupAddr(&n
, ifa
->ifa_netmask
);
5028 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
5029 mDNS_snprintf(buf
, sizeof(buf
), "%d.%d.%d.%d.in-addr.arpa.", a
.ip
.v4
.b
[3] & n
.ip
.v4
.b
[3],
5030 a
.ip
.v4
.b
[2] & n
.ip
.v4
.b
[2],
5031 a
.ip
.v4
.b
[1] & n
.ip
.v4
.b
[1],
5032 a
.ip
.v4
.b
[0] & n
.ip
.v4
.b
[0]);
5033 UpdateSearchDomainHash(&sdc
, buf
, NULL
);
5034 mDNS_AddSearchDomain_CString(buf
, mDNSNULL
);
5036 ifa
= ifa
->ifa_next
;
5040 #ifndef MDNS_NO_DNSINFO
5041 if (setservers
|| setsearch
)
5043 dns_config_t
*config
= dns_configuration_copy();
5046 // On 10.4, calls to dns_configuration_copy() early in the boot process often fail.
5047 // Apparently this is expected behaviour -- "not a bug".
5048 // Accordingly, we suppress syslog messages for the first three minutes after boot.
5049 // If we are still getting failures after three minutes, then we log them.
5050 if ((mDNSu32
)mDNSPlatformRawTime() > (mDNSu32
)(mDNSPlatformOneSecond
* 180))
5051 LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL");
5055 LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d, generation %llu, last %llu", config
->n_resolver
, config
->generation
, m
->p
->LastConfigGeneration
);
5057 // For every network change, mDNSPlatformSetDNSConfig is called twice. First,
5058 // to update the search domain list (in which case, the setsearch bool is set);
5059 // and second, to update the DNS server list (in which case, the setservers bool
5060 // is set). The code assumes only one of these flags, setsearch or setserver,
5061 // will be set when mDNSPlatformSetDNSConfig is called to handle a network change.
5062 // The mDNSPlatformSetDNSConfig function also assumes that ackCfg will be set
5063 // when setservers is set.
5065 // The search domains update occurs on every network change to avoid sync issues
5066 // that may occur if a network change happens during the processing
5067 // of a network change. The dns servers update occurs when the DNS config
5068 // changes. The dns servers stay in sync by saving the config's generation number
5069 // on every update; and only updating when the generation number changes.
5071 // If this is a DNS server update and the configuration hasn't changed, then skip update
5072 if (setservers
&& !m
->p
->if_interface_changed
&& m
->p
->LastConfigGeneration
== config
->generation
)
5074 LogInfo("mDNSPlatformSetDNSConfig(setservers): generation number %llu same, not processing", config
->generation
);
5075 dns_configuration_free(config
);
5076 SetupDDNSDomains(fqdn
, RegDomains
, BrowseDomains
);
5080 // Must check if setservers is true, because mDNSPlatformSetDNSConfig can be called for multiple times
5081 // with setservers equals to false. If setservers is false, we will end up with clearing if_interface_changed
5082 // without really updating the server.
5083 m
->p
->if_interface_changed
= mDNSfalse
;
5086 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
5087 SetupActiveDirectoryDomain(config
);
5089 ConfigResolvers(config
, kScopeNone
, setsearch
, setservers
, &sdc
);
5090 ConfigResolvers(config
, kScopeInterfaceID
, setsearch
, setservers
, &sdc
);
5091 ConfigResolvers(config
, kScopeServiceID
, setsearch
, setservers
, &sdc
);
5093 const CFIndex n
= m
->p
->InterfaceMonitors
? CFArrayGetCount(m
->p
->InterfaceMonitors
) : 0;
5094 for (CFIndex i
= n
- 1; i
>= 0; i
--)
5096 mdns_interface_monitor_t monitor
;
5097 monitor
= (mdns_interface_monitor_t
) CFArrayGetValueAtIndex(m
->p
->InterfaceMonitors
, i
);
5098 const uint32_t ifIndex
= mdns_interface_monitor_get_interface_index(monitor
);
5100 for (server
= m
->DNSServers
; server
; server
= server
->next
)
5102 if ((((uintptr_t)server
->interface
) == ifIndex
) && !(server
->flags
& DNSServerFlag_Delete
))
5109 mdns_retain(monitor
);
5110 CFArrayRemoveValueAtIndex(m
->p
->InterfaceMonitors
, i
);
5111 mdns_interface_monitor_invalidate(monitor
);
5112 mdns_release(monitor
);
5116 // Acking provides a hint to other processes that the current DNS configuration has completed
5117 // its update. When configd receives the ack, it publishes a notification.
5118 // Applications monitoring the notification then know when to re-issue their DNS queries
5119 // after a network change occurs.
5122 // Note: We have to set the generation number here when we are acking.
5123 // For every DNS configuration change, we do the following:
5125 // 1) Copy dns configuration, handle search domains change
5126 // 2) Copy dns configuration, handle dns server change
5128 // If we update the generation number at step (1), we won't process the
5129 // DNS servers the second time because generation number would be the same.
5130 // As we ack only when we process dns servers, we set the generation number
5132 m
->p
->LastConfigGeneration
= config
->generation
;
5133 LogInfo("mDNSPlatformSetDNSConfig: Acking configuration setservers %d, setsearch %d", setservers
, setsearch
);
5136 dns_configuration_free(config
);
5137 if (setsearch
) FinalizeSearchDomainHash(&sdc
);
5140 #endif // MDNS_NO_DNSINFO
5141 SetupDDNSDomains(fqdn
, RegDomains
, BrowseDomains
);
5146 mDNSexport mStatus
mDNSPlatformGetPrimaryInterface(mDNSAddr
*v4
, mDNSAddr
*v6
, mDNSAddr
*r
)
5150 CFDictionaryRef dict
= SCDynamicStoreCopyValue(NULL
, NetworkChangedKey_IPv4
);
5153 r
->type
= mDNSAddrType_IPv4
;
5154 r
->ip
.v4
= zerov4Addr
;
5155 CFStringRef string
= CFDictionaryGetValue(dict
, kSCPropNetIPv4Router
);
5158 if (!CFStringGetCString(string
, buf
, 256, kCFStringEncodingUTF8
))
5159 LogMsg("Could not convert router to CString");
5162 struct sockaddr_in saddr
;
5163 saddr
.sin_len
= sizeof(saddr
);
5164 saddr
.sin_family
= AF_INET
;
5166 inet_aton(buf
, &saddr
.sin_addr
);
5167 *(in_addr_t
*)&r
->ip
.v4
= saddr
.sin_addr
.s_addr
;
5170 string
= CFDictionaryGetValue(dict
, kSCDynamicStorePropNetPrimaryInterface
);
5173 mDNSBool HavePrimaryGlobalv6
= mDNSfalse
; // does the primary interface have a global v6 address?
5174 struct ifaddrs
*ifa
= myGetIfAddrs(1);
5175 *v4
= *v6
= zeroAddr
;
5177 if (!CFStringGetCString(string
, buf
, 256, kCFStringEncodingUTF8
))
5179 LogMsg("Could not convert router to CString");
5182 // find primary interface in list
5183 while (ifa
&& (mDNSIPv4AddressIsZero(v4
->ip
.v4
) || mDNSv4AddressIsLinkLocal(&v4
->ip
.v4
) || !HavePrimaryGlobalv6
))
5187 LogMsg("Skip interface, %s, since ifa_addr is not set.", (ifa
->ifa_name
) ? ifa
->ifa_name
: "name not found");
5188 ifa
= ifa
->ifa_next
;
5191 mDNSAddr tmp6
= zeroAddr
;
5192 if (!strcmp(buf
, ifa
->ifa_name
))
5194 if (ifa
->ifa_addr
->sa_family
== AF_INET
)
5196 if (mDNSIPv4AddressIsZero(v4
->ip
.v4
) || mDNSv4AddressIsLinkLocal(&v4
->ip
.v4
))
5197 SetupAddr(v4
, ifa
->ifa_addr
);
5199 else if (ifa
->ifa_addr
->sa_family
== AF_INET6
)
5201 SetupAddr(&tmp6
, ifa
->ifa_addr
);
5202 if (tmp6
.ip
.v6
.b
[0] >> 5 == 1) // global prefix: 001
5204 HavePrimaryGlobalv6
= mDNStrue
;
5211 // We'll take a V6 address from the non-primary interface if the primary interface doesn't have a global V6 address
5212 if (!HavePrimaryGlobalv6
&& ifa
->ifa_addr
->sa_family
== AF_INET6
&& !v6
->ip
.v6
.b
[0])
5214 SetupAddr(&tmp6
, ifa
->ifa_addr
);
5215 if (tmp6
.ip
.v6
.b
[0] >> 5 == 1)
5219 ifa
= ifa
->ifa_next
;
5221 // Note that while we advertise v6, we still require v4 (possibly NAT'd, but not link-local) because we must use
5222 // V4 to communicate w/ our DNS server
5228 return mStatus_NoError
;
5231 mDNSexport
void mDNSPlatformDynDNSHostNameStatusChanged(const domainname
*const dname
, const mStatus status
)
5233 LogInfo("mDNSPlatformDynDNSHostNameStatusChanged %d %##s", status
, dname
->c
);
5234 char uname
[MAX_ESCAPED_DOMAIN_NAME
]; // Max legal C-string name, including terminating NUL
5235 ConvertDomainNameToCString(dname
, uname
);
5241 if (!(*(p
+1)) && *p
== '.') *p
= 0; // if last character, strip trailing dot
5245 // We need to make a CFDictionary called "State:/Network/DynamicDNS" containing (at present) a single entity.
5246 // That single entity is a CFDictionary with name "HostNames".
5247 // The "HostNames" CFDictionary contains a set of name/value pairs, where the each name is the FQDN
5248 // in question, and the corresponding value is a CFDictionary giving the state for that FQDN.
5249 // (At present we only support a single FQDN, so this dictionary holds just a single name/value pair.)
5250 // The CFDictionary for each FQDN holds (at present) a single name/value pair,
5251 // where the name is "Status" and the value is a CFNumber giving an errror code (with zero meaning success).
5253 const CFStringRef StateKeys
[1] = { CFSTR("HostNames") };
5254 const CFStringRef HostKeys
[1] = { CFStringCreateWithCString(NULL
, uname
, kCFStringEncodingUTF8
) };
5255 const CFStringRef StatusKeys
[1] = { CFSTR("Status") };
5256 if (!HostKeys
[0]) LogMsg("SetDDNSNameStatus: CFStringCreateWithCString(%s) failed", uname
);
5259 const CFNumberRef StatusVals
[1] = { CFNumberCreate(NULL
, kCFNumberSInt32Type
, &status
) };
5260 if (StatusVals
[0] == NULL
) LogMsg("SetDDNSNameStatus: CFNumberCreate(%d) failed", status
);
5263 const CFDictionaryRef HostVals
[1] = { CFDictionaryCreate(NULL
, (void*)StatusKeys
, (void*)StatusVals
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
) };
5266 const CFDictionaryRef StateVals
[1] = { CFDictionaryCreate(NULL
, (void*)HostKeys
, (void*)HostVals
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
) };
5269 CFDictionaryRef StateDict
= CFDictionaryCreate(NULL
, (void*)StateKeys
, (void*)StateVals
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
5272 mDNSDynamicStoreSetConfig(kmDNSDynamicConfig
, mDNSNULL
, StateDict
);
5273 CFRelease(StateDict
);
5275 CFRelease(StateVals
[0]);
5277 CFRelease(HostVals
[0]);
5279 CFRelease(StatusVals
[0]);
5281 CFRelease(HostKeys
[0]);
5285 // MUST be called holding the lock
5286 mDNSlocal
void SetDomainSecrets_internal(mDNS
*m
)
5288 #ifdef NO_SECURITYFRAMEWORK
5290 LogMsg("Note: SetDomainSecrets: no keychain support");
5293 LogInfo("SetDomainSecrets");
5295 // Rather than immediately deleting all keys now, we mark them for deletion in ten seconds.
5296 // In the case where the user simultaneously removes their DDNS host name and the key
5297 // for it, this gives mDNSResponder ten seconds to gracefully delete the name from the
5298 // server before it loses access to the necessary key. Otherwise, we'd leave orphaned
5299 // address records behind that we no longer have permission to delete.
5300 DomainAuthInfo
*ptr
;
5301 for (ptr
= m
->AuthInfoList
; ptr
; ptr
= ptr
->next
)
5302 ptr
->deltime
= NonZeroTime(m
->timenow
+ mDNSPlatformOneSecond
*10);
5304 // String Array used to write list of private domains to Dynamic Store
5305 CFMutableArrayRef sa
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5306 if (!sa
) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; }
5308 CFDataRef data
= NULL
;
5309 const int itemsPerEntry
= 4; // domain name, key name, key value, Name value
5310 CFArrayRef secrets
= NULL
;
5311 int err
= mDNSKeychainGetSecrets(&secrets
);
5312 if (err
|| !secrets
)
5313 LogMsg("SetDomainSecrets: mDNSKeychainGetSecrets failed error %d CFArrayRef %p", err
, secrets
);
5316 CFIndex ArrayCount
= CFArrayGetCount(secrets
);
5317 // Iterate through the secrets
5318 for (i
= 0; i
< ArrayCount
; ++i
)
5321 CFArrayRef entry
= CFArrayGetValueAtIndex(secrets
, i
);
5322 if (CFArrayGetTypeID() != CFGetTypeID(entry
) || itemsPerEntry
!= CFArrayGetCount(entry
))
5323 { LogMsg("SetDomainSecrets: malformed entry %d, itemsPerEntry %d", i
, itemsPerEntry
); continue; }
5324 for (j
= 0; j
< CFArrayGetCount(entry
); ++j
)
5325 if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry
, j
)))
5326 { LogMsg("SetDomainSecrets: malformed entry item %d", j
); continue; }
5328 // The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness
5330 // Max legal domainname as C-string, including space for dnsprefix and terminating NUL
5331 // Get DNS domain this key is for (kmDNSKcWhere)
5332 char stringbuf
[MAX_ESCAPED_DOMAIN_NAME
+ sizeof(dnsprefix
)];
5333 data
= CFArrayGetValueAtIndex(entry
, kmDNSKcWhere
);
5334 if (CFDataGetLength(data
) >= (int)sizeof(stringbuf
))
5335 { LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data
)); continue; }
5336 CFDataGetBytes(data
, CFRangeMake(0, CFDataGetLength(data
)), (UInt8
*)stringbuf
);
5337 stringbuf
[CFDataGetLength(data
)] = '\0';
5340 if (!strncmp(stringbuf
, dnsprefix
, strlen(dnsprefix
)))
5341 offset
= strlen(dnsprefix
);
5344 if (!MakeDomainNameFromDNSNameString(&domain
, stringbuf
+ offset
)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf
); continue; }
5346 // Get key name (kmDNSKcAccount)
5347 data
= CFArrayGetValueAtIndex(entry
, kmDNSKcAccount
);
5348 if (CFDataGetLength(data
) >= (int)sizeof(stringbuf
))
5349 { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data
)); continue; }
5350 CFDataGetBytes(data
, CFRangeMake(0,CFDataGetLength(data
)), (UInt8
*)stringbuf
);
5351 stringbuf
[CFDataGetLength(data
)] = '\0';
5354 if (!MakeDomainNameFromDNSNameString(&keyname
, stringbuf
)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf
); continue; }
5356 // Get key data (kmDNSKcKey)
5357 data
= CFArrayGetValueAtIndex(entry
, kmDNSKcKey
);
5358 if (CFDataGetLength(data
) >= (int)sizeof(stringbuf
))
5360 LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data
));
5363 CFDataGetBytes(data
, CFRangeMake(0, CFDataGetLength(data
)), (UInt8
*)stringbuf
);
5364 stringbuf
[CFDataGetLength(data
)] = '\0'; // mDNS_SetSecretForDomain requires NULL-terminated C string for key
5366 // Get the Name of the keychain entry (kmDNSKcName) host or host:port
5367 // The hostname also has the port number and ":". It should take a maximum of 6 bytes.
5368 char hostbuf
[MAX_ESCAPED_DOMAIN_NAME
+ 6]; // Max legal domainname as C-string, including terminating NUL
5369 data
= CFArrayGetValueAtIndex(entry
, kmDNSKcName
);
5370 if (CFDataGetLength(data
) >= (int)sizeof(hostbuf
))
5372 LogMsg("SetDomainSecrets: host:port data too long: %d", CFDataGetLength(data
));
5375 CFDataGetBytes(data
, CFRangeMake(0,CFDataGetLength(data
)), (UInt8
*)hostbuf
);
5376 hostbuf
[CFDataGetLength(data
)] = '\0';
5378 domainname hostname
;
5381 hptr
= strchr(hostbuf
, ':');
5383 port
.NotAnInteger
= 0;
5390 while(hptr
&& *hptr
!= 0)
5392 if (*hptr
< '0' || *hptr
> '9')
5393 { LogMsg("SetDomainSecrets: Malformed Port number %d, val %d", *hptr
, val
); val
= 0; break;}
5394 val
= val
* 10 + *hptr
- '0';
5399 port
.NotAnInteger
= p
[0] << 8 | p
[1];
5401 // The hostbuf is of the format dsid@hostname:port. We don't care about the dsid.
5402 hptr
= strchr(hostbuf
, '@');
5407 if (!MakeDomainNameFromDNSNameString(&hostname
, hptr
)) { LogMsg("SetDomainSecrets: bad host name %s", hptr
); continue; }
5409 DomainAuthInfo
*FoundInList
;
5410 for (FoundInList
= m
->AuthInfoList
; FoundInList
; FoundInList
= FoundInList
->next
)
5411 if (SameDomainName(&FoundInList
->domain
, &domain
)) break;
5413 // Uncomment the line below to view the keys as they're read out of the system keychain
5414 // DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE!
5415 //LogInfo("SetDomainSecrets: domain %##s keyname %##s key %s hostname %##s port %d", &domain.c, &keyname.c, stringbuf, hostname.c, (port.b[0] << 8 | port.b[1]));
5416 LogInfo("SetDomainSecrets: domain %##s keyname %##s hostname %##s port %d", &domain
.c
, &keyname
.c
, hostname
.c
, (port
.b
[0] << 8 | port
.b
[1]));
5418 // If didn't find desired domain in the list, make a new entry
5422 ptr
= (DomainAuthInfo
*) callocL("DomainAuthInfo", sizeof(*ptr
));
5423 if (!ptr
) { LogMsg("SetDomainSecrets: No memory"); continue; }
5426 if (mDNS_SetSecretForDomain(m
, ptr
, &domain
, &keyname
, stringbuf
, &hostname
, &port
) == mStatus_BadParamErr
)
5428 if (!FoundInList
) mDNSPlatformMemFree(ptr
); // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately
5432 ConvertDomainNameToCString(&domain
, stringbuf
);
5433 CFStringRef cfs
= CFStringCreateWithCString(NULL
, stringbuf
, kCFStringEncodingUTF8
);
5434 if (cfs
) { CFArrayAppendValue(sa
, cfs
); CFRelease(cfs
); }
5439 if (!privateDnsArray
|| !CFEqual(privateDnsArray
, sa
))
5441 if (privateDnsArray
)
5442 CFRelease(privateDnsArray
);
5444 privateDnsArray
= sa
;
5445 CFRetain(privateDnsArray
);
5446 mDNSDynamicStoreSetConfig(kmDNSPrivateConfig
, mDNSNULL
, privateDnsArray
);
5450 CheckSuppressUnusableQuestions(m
);
5452 #endif /* NO_SECURITYFRAMEWORK */
5455 mDNSexport
void SetDomainSecrets(mDNS
*m
)
5458 // Don't get secrets for BTMM if running in debug mode
5459 if (!IsDebugSocketInUse())
5461 SetDomainSecrets_internal(m
);
5464 mDNSlocal
void SetLocalDomains(void)
5466 CFMutableArrayRef sa
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5467 if (!sa
) { LogMsg("SetLocalDomains: CFArrayCreateMutable failed"); return; }
5469 CFArrayAppendValue(sa
, CFSTR("local"));
5470 CFArrayAppendValue(sa
, CFSTR("254.169.in-addr.arpa"));
5471 CFArrayAppendValue(sa
, CFSTR("8.e.f.ip6.arpa"));
5472 CFArrayAppendValue(sa
, CFSTR("9.e.f.ip6.arpa"));
5473 CFArrayAppendValue(sa
, CFSTR("a.e.f.ip6.arpa"));
5474 CFArrayAppendValue(sa
, CFSTR("b.e.f.ip6.arpa"));
5476 mDNSDynamicStoreSetConfig(kmDNSMulticastConfig
, mDNSNULL
, sa
);
5480 #if !MDNSRESPONDER_SUPPORTS(APPLE, NO_WAKE_FOR_NET_ACCESS)
5481 mDNSlocal
void GetCurrentPMSetting(const CFStringRef name
, mDNSs32
*val
)
5483 CFDictionaryRef dict
= SCDynamicStoreCopyValue(NULL
, NetworkChangedKey_PowerSettings
);
5486 LogSPS("GetCurrentPMSetting: Could not get IOPM CurrentSettings dict");
5490 CFNumberRef number
= CFDictionaryGetValue(dict
, name
);
5491 if ((number
== NULL
) || CFGetTypeID(number
) != CFNumberGetTypeID() || !CFNumberGetValue(number
, kCFNumberSInt32Type
, val
))
5498 #if APPLE_OSX_mDNSResponder
5500 static CFMutableDictionaryRef spsStatusDict
= NULL
;
5501 static const CFStringRef kMetricRef
= CFSTR("Metric");
5503 mDNSlocal
void SPSStatusPutNumber(CFMutableDictionaryRef dict
, const mDNSu8
* const ptr
, CFStringRef key
)
5505 mDNSu8 tmp
= (ptr
[0] - '0') * 10 + ptr
[1] - '0';
5506 CFNumberRef num
= CFNumberCreate(NULL
, kCFNumberSInt8Type
, &tmp
);
5508 LogMsg("SPSStatusPutNumber: Could not create CFNumber");
5511 CFDictionarySetValue(dict
, key
, num
);
5516 mDNSlocal CFMutableDictionaryRef
SPSCreateDict(const mDNSu8
* const ptr
)
5518 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
5519 if (!dict
) { LogMsg("SPSCreateDict: Could not create CFDictionary dict"); return dict
; }
5522 buffer
[mDNS_snprintf(buffer
, sizeof(buffer
), "%##s", ptr
) - 1] = 0;
5523 CFStringRef spsname
= CFStringCreateWithCString(NULL
, buffer
, kCFStringEncodingUTF8
);
5524 if (!spsname
) { LogMsg("SPSCreateDict: Could not create CFString spsname full"); CFRelease(dict
); return NULL
; }
5525 CFDictionarySetValue(dict
, CFSTR("FullName"), spsname
);
5528 if (ptr
[0] >= 2) SPSStatusPutNumber(dict
, ptr
+ 1, CFSTR("Type"));
5529 if (ptr
[0] >= 5) SPSStatusPutNumber(dict
, ptr
+ 4, CFSTR("Portability"));
5530 if (ptr
[0] >= 8) SPSStatusPutNumber(dict
, ptr
+ 7, CFSTR("MarginalPower"));
5531 if (ptr
[0] >= 11) SPSStatusPutNumber(dict
, ptr
+10, CFSTR("TotalPower"));
5533 mDNSu32 tmp
= SPSMetric(ptr
);
5534 CFNumberRef num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &tmp
);
5536 LogMsg("SPSCreateDict: Could not create CFNumber");
5539 CFDictionarySetValue(dict
, kMetricRef
, num
);
5545 memcpy(buffer
, ptr
+ 13, ptr
[0] - 12);
5546 buffer
[ptr
[0] - 12] = 0;
5547 spsname
= CFStringCreateWithCString(NULL
, buffer
, kCFStringEncodingUTF8
);
5548 if (!spsname
) { LogMsg("SPSCreateDict: Could not create CFString spsname"); CFRelease(dict
); return NULL
; }
5551 CFDictionarySetValue(dict
, CFSTR("PrettyName"), spsname
);
5559 mDNSlocal CFComparisonResult
CompareSPSEntries(const void *val1
, const void *val2
, void *context
)
5562 return CFNumberCompare((CFNumberRef
)CFDictionaryGetValue((CFDictionaryRef
)val1
, kMetricRef
),
5563 (CFNumberRef
)CFDictionaryGetValue((CFDictionaryRef
)val2
, kMetricRef
),
5567 mDNSlocal
void UpdateSPSStatus(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
5569 NetworkInterfaceInfo
* info
= (NetworkInterfaceInfo
*)question
->QuestionContext
;
5570 debugf("UpdateSPSStatus: %s %##s %s %s", info
->ifname
, question
->qname
.c
, AddRecord
? "Add" : "Rmv", answer
? RRDisplayString(m
, answer
) : "<null>");
5573 mDNS_UpdateAllowSleep(m
);
5576 if (answer
&& SPSMetric(answer
->rdata
->u
.name
.c
) > 999999) return; // Ignore instances with invalid names
5580 spsStatusDict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
5581 if (!spsStatusDict
) { LogMsg("UpdateSPSStatus: Could not create CFDictionary spsStatusDict"); return; }
5584 CFStringRef ifname
= CFStringCreateWithCString(NULL
, info
->ifname
, kCFStringEncodingUTF8
);
5585 if (!ifname
) { LogMsg("UpdateSPSStatus: Could not create CFString ifname"); return; }
5587 CFMutableArrayRef array
= NULL
;
5589 if (!CFDictionaryGetValueIfPresent(spsStatusDict
, ifname
, (const void**) &array
))
5591 array
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5592 if (!array
) { LogMsg("UpdateSPSStatus: Could not create CFMutableArray"); CFRelease(ifname
); return; }
5593 CFDictionarySetValue(spsStatusDict
, ifname
, array
);
5594 CFRelease(array
); // let go of our reference, now that the dict has one
5597 if (!array
) { LogMsg("UpdateSPSStatus: Could not get CFMutableArray for %s", info
->ifname
); CFRelease(ifname
); return; }
5599 if (!answer
) // special call that means the question has been stopped (because the interface is going away)
5600 CFArrayRemoveAllValues(array
);
5603 CFMutableDictionaryRef dict
= SPSCreateDict(answer
->rdata
->u
.name
.c
);
5604 if (!dict
) { CFRelease(ifname
); return; }
5608 if (!CFArrayContainsValue(array
, CFRangeMake(0, CFArrayGetCount(array
)), dict
))
5611 for (i
=0; i
<CFArrayGetCount(array
); i
++)
5612 if (CompareSPSEntries(CFArrayGetValueAtIndex(array
, i
), dict
, NULL
) != kCFCompareLessThan
)
5614 CFArrayInsertValueAtIndex(array
, i
, dict
);
5616 else LogMsg("UpdateSPSStatus: %s array already contains %##s", info
->ifname
, answer
->rdata
->u
.name
.c
);
5620 CFIndex i
= CFArrayGetFirstIndexOfValue(array
, CFRangeMake(0, CFArrayGetCount(array
)), dict
);
5621 if (i
!= -1) CFArrayRemoveValueAtIndex(array
, i
);
5622 else LogMsg("UpdateSPSStatus: %s array does not contain %##s", info
->ifname
, answer
->rdata
->u
.name
.c
);
5628 if (!m
->ShutdownTime
) mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState
, info
->ifname
, array
);
5633 mDNSlocal mDNSs32
GetSystemSleepTimerSetting(void)
5636 SCDynamicStoreRef store
= SCDynamicStoreCreate(NULL
, CFSTR("mDNSResponder:GetSystemSleepTimerSetting"), NULL
, NULL
);
5638 LogMsg("GetSystemSleepTimerSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
5641 CFDictionaryRef dict
= SCDynamicStoreCopyValue(store
, NetworkChangedKey_PowerSettings
);
5644 CFNumberRef number
= CFDictionaryGetValue(dict
, CFSTR("System Sleep Timer"));
5645 if (number
!= NULL
) CFNumberGetValue(number
, kCFNumberSInt32Type
, &val
);
5653 mDNSlocal
void SetSPS(mDNS
*const m
)
5656 // If we ever want to know InternetSharing status in the future, use DNSXEnableProxy()
5657 mDNSu8 sps
= (OfferSleepProxyService
&& GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware
: 0;
5659 // For devices that are not running NAT, but are set to never sleep, we may choose to act
5660 // as a Sleep Proxy, but only for non-portable Macs (Portability > 35 means nominal weight < 3kg)
5661 //if (sps > mDNSSleepProxyMetric_PrimarySoftware && SPMetricPortability > 35) sps = 0;
5663 // If we decide to let laptops act as Sleep Proxy, we should do it only when running on AC power, not on battery
5665 // For devices that are unable to sleep at all to save power, or save 1W or less by sleeping,
5666 // it makes sense for them to offer low-priority Sleep Proxy service on the network.
5667 // We rate such a device as metric 70 ("Incidentally Available Hardware")
5668 if (SPMetricMarginalPower
<= 60 && !sps
) sps
= mDNSSleepProxyMetric_IncidentalHardware
;
5670 // If the launchd plist specifies an explicit value for the Intent Metric, then use that instead of the
5671 // computed value (currently 40 "Primary Network Infrastructure Software" or 80 "Incidentally Available Software")
5672 if (sps
&& OfferSleepProxyService
&& OfferSleepProxyService
< 100) sps
= OfferSleepProxyService
;
5674 #ifdef NO_APPLETV_SLEEP_PROXY_ON_WIFI
5675 // AppleTVs are not reliable sleep proxy servers on WiFi. Do not offer to be a BSP if the WiFi interface is active.
5678 NetworkInterfaceInfo
*intf
= mDNSNULL
;
5679 mDNSEthAddr bssid
= zeroEthAddr
;
5680 for (intf
= GetFirstActiveInterface(m
->HostInterfaces
); intf
; intf
= GetFirstActiveInterface(intf
->next
))
5682 if (intf
->InterfaceID
== AWDLInterfaceID
) continue;
5683 bssid
= GetBSSID(intf
->ifname
);
5684 if (!mDNSSameEthAddress(&bssid
, &zeroEthAddr
))
5686 LogMsg("SetSPS: AppleTV on WiFi - not advertising BSP services");
5692 #endif // NO_APPLETV_SLEEP_PROXY_ON_WIFI
5694 mDNSCoreBeSleepProxyServer(m
, sps
, SPMetricPortability
, SPMetricMarginalPower
, SPMetricTotalPower
, SPMetricFeatures
);
5697 // The definitions below should eventually come from some externally-supplied header file.
5698 // However, since these definitions can't really be changed without breaking binary compatibility,
5699 // they should never change, so in practice it should not be a big problem to have them defined here.
5702 { // commands from the daemon to the driver
5703 cmd_mDNSOffloadRR
= 21, // give the mdns update buffer to the driver
5706 typedef union { void *ptr
; mDNSOpaque64 sixtyfourbits
; } FatPtr
;
5709 { // cmd_mDNSOffloadRR structure
5710 uint32_t command
; // set to OffloadRR
5711 uint32_t rrBufferSize
; // number of bytes of RR records
5712 uint32_t numUDPPorts
; // number of SRV UDP ports
5713 uint32_t numTCPPorts
; // number of SRV TCP ports
5714 uint32_t numRRRecords
; // number of RR records
5715 uint32_t compression
; // rrRecords - compression is base for compressed strings
5716 FatPtr rrRecords
; // address of array of pointers to the rr records
5717 FatPtr udpPorts
; // address of udp port list (SRV)
5718 FatPtr tcpPorts
; // address of tcp port list (SRV)
5721 #include <IOKit/IOKitLib.h>
5722 #include <dns_util.h>
5724 mDNSlocal mDNSu32
GetPortArray(int trans
, mDNSIPPort
*portarray
)
5726 mDNS
*const m
= &mDNSStorage
;
5727 const domainlabel
*const tp
= (trans
== mDNSTransport_UDP
) ? (const domainlabel
*)"\x4_udp" : (const domainlabel
*)"\x4_tcp";
5731 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
5733 if (rr
->resrec
.rrtype
== kDNSType_SRV
&& SameDomainLabel(ThirdLabel(rr
->resrec
.name
)->c
, tp
->c
))
5740 for (i
= 0; i
< count
; i
++)
5741 if (mDNSSameIPPort(portarray
[i
], rr
->resrec
.rdata
->u
.srv
.port
))
5744 // Add it into the port list only if it not already present in the list
5746 portarray
[count
++] = rr
->resrec
.rdata
->u
.srv
.port
;
5753 #if APPLE_OSX_mDNSResponder && TARGET_OS_OSX
5754 mDNSlocal mDNSBool
SupportsTCPKeepAlive()
5756 IOReturn ret
= kIOReturnSuccess
;
5757 CFTypeRef obj
= NULL
;
5758 mDNSBool supports
= mDNSfalse
;
5760 ret
= IOPlatformCopyFeatureActive(CFSTR("TCPKeepAliveDuringSleep"), &obj
);
5761 if ((kIOReturnSuccess
== ret
) && (obj
!= NULL
))
5763 supports
= (obj
== kCFBooleanTrue
)? mDNStrue
: mDNSfalse
;
5766 LogSPS("%s: The hardware %s TCP Keep Alive", __func__
, (supports
? "supports" : "does not support"));
5770 mDNSlocal mDNSBool
OnBattery(void)
5772 CFTypeRef powerInfo
= IOPSCopyPowerSourcesInfo();
5773 CFTypeRef powerSrc
= IOPSGetProvidingPowerSourceType(powerInfo
);
5774 mDNSBool result
= mDNSfalse
;
5776 if (powerInfo
!= NULL
)
5778 result
= CFEqual(CFSTR(kIOPSBatteryPowerValue
), powerSrc
);
5779 CFRelease(powerInfo
);
5781 LogSPS("%s: The system is on %s", __func__
, (result
)? "Battery" : "AC Power");
5786 #define TfrRecordToNIC(RR) \
5787 ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name))))
5789 mDNSlocal mDNSu32
CountProxyRecords(uint32_t *const numbytes
, mDNSBool TCPKAOnly
, mDNSBool supportsTCPKA
)
5791 mDNS
*const m
= &mDNSStorage
;
5794 mDNSBool isKeepAliveRecord
= mDNSfalse
;
5798 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
5800 if (!(rr
->AuthFlags
& AuthFlagsWakeOnly
) && rr
->resrec
.RecordType
> kDNSRecordTypeDeregistering
)
5802 #if APPLE_OSX_mDNSResponder && TARGET_OS_OSX
5803 isKeepAliveRecord
= mDNS_KeepaliveRecord(&rr
->resrec
);
5804 // Skip over all other records if we are registering TCP KeepAlive records only
5805 // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive.
5806 if ((TCPKAOnly
&& !isKeepAliveRecord
) || (isKeepAliveRecord
&& !supportsTCPKA
))
5809 (void) TCPKAOnly
; // unused
5810 (void) supportsTCPKA
; // unused
5812 if (TfrRecordToNIC(rr
))
5814 // For KeepAlive records, use an estimated length of 256, which is the maximum size.
5815 const uint32_t rdataLen
= isKeepAliveRecord
? ((uint32_t)sizeof(UTF8str255
)) : rr
->resrec
.rdestimate
;
5816 const uint32_t recordSize
= DomainNameLength(rr
->resrec
.name
) + 10 + rdataLen
;
5817 *numbytes
+= recordSize
;
5818 LogSPS("CountProxyRecords: %3u size %5u total %5u %s", count
, recordSize
, *numbytes
, ARDisplayString(m
,rr
));
5826 mDNSlocal
void GetProxyRecords(DNSMessage
*const msg
, uint32_t *const numbytes
, FatPtr
*const records
,
5827 uint32_t *outRecordCount
, NetworkInterfaceInfo
*const intf
, mDNSBool TCPKAOnly
, mDNSBool supportsTCPKA
)
5829 mDNS
*const m
= &mDNSStorage
;
5830 mDNSu8
*p
= msg
->data
;
5831 const mDNSu8
*const limit
= p
+ *numbytes
;
5832 InitializeDNSMessage(&msg
->h
, zeroID
, zeroID
);
5837 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
5839 if (!(rr
->AuthFlags
& AuthFlagsWakeOnly
) && rr
->resrec
.RecordType
> kDNSRecordTypeDeregistering
)
5841 #if APPLE_OSX_mDNSResponder && TARGET_OS_OSX
5842 const mDNSBool isKeepAliveRecord
= mDNS_KeepaliveRecord(&rr
->resrec
);
5844 // Skip over all other records if we are registering TCP KeepAlive records only
5845 // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive
5846 // supportsTCPKA is set to true if both policy and interface allow TCP Keepalive
5847 if ((TCPKAOnly
&& !isKeepAliveRecord
) || (isKeepAliveRecord
&& !supportsTCPKA
))
5850 // Update the record before calculating the number of bytes required
5851 // We offload the TCP Keepalive record even if the update fails. When the driver gets the record, it will
5852 // attempt to update the record again.
5853 if (isKeepAliveRecord
)
5855 if (UpdateKeepaliveRData(m
, rr
, intf
, mDNSfalse
, mDNSNULL
) != mStatus_NoError
)
5857 LogSPS("GetProxyRecords: Failed to update keepalive record - %s", ARDisplayString(m
, rr
));
5860 // Offload only Valid Keepalive records
5861 if (!mDNSValidKeepAliveRecord(rr
))
5867 (void) intf
; // unused
5868 (void) TCPKAOnly
; // unused
5869 (void) supportsTCPKA
; // unused
5871 if (TfrRecordToNIC(rr
))
5873 records
[count
].sixtyfourbits
= zeroOpaque64
;
5874 records
[count
].ptr
= p
;
5875 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
5876 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the 'unique' bit so PutResourceRecord will set it
5877 p
= PutResourceRecordTTLWithLimit(msg
, p
, &msg
->h
.mDNS_numUpdates
, &rr
->resrec
, rr
->resrec
.rroriginalttl
, limit
);
5878 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear 'unique' bit back to normal state
5879 LogSPS("GetProxyRecords: %3d start %p end %p size %5d total %5d %s",
5880 count
, records
[count
].ptr
, p
, p
- (mDNSu8
*)records
[count
].ptr
, p
- msg
->data
, ARDisplayString(m
,rr
));
5885 *numbytes
= p
- msg
->data
;
5886 if (outRecordCount
) *outRecordCount
= count
;
5889 mDNSexport mDNSBool
SupportsInNICProxy(NetworkInterfaceInfo
*const intf
)
5891 if(!UseInternalSleepProxy
)
5893 LogMsg("SupportsInNICProxy: Internal Sleep Proxy is disabled");
5896 return CheckInterfaceSupport(intf
, mDNS_IOREG_KEY
);
5899 // Called with the lock held
5900 mDNSexport mStatus
ActivateLocalProxy(NetworkInterfaceInfo
*const intf
, mDNSBool offloadKeepAlivesOnly
, mDNSBool
*keepaliveOnly
)
5902 mStatus result
= mStatus_UnknownErr
;
5903 mDNSBool TCPKAOnly
= mDNSfalse
;
5904 mDNSBool supportsTCPKA
= mDNSfalse
;
5905 io_service_t service
= IOServiceGetMatchingService(kIOMasterPortDefault
, IOBSDNameMatching(kIOMasterPortDefault
, 0, intf
->ifname
));
5907 #if APPLE_OSX_mDNSResponder && TARGET_OS_OSX
5908 // Check if the interface supports TCP Keepalives and the system policy says it is ok to offload TCP Keepalive records
5909 supportsTCPKA
= (InterfaceSupportsKeepAlive(intf
) && SupportsTCPKeepAlive()) ? mDNStrue
: mDNSfalse
;
5910 if (!offloadKeepAlivesOnly
)
5912 // Only TCP Keepalive records are to be offloaded if
5913 // - The system is on battery
5914 // - OR wake for network access is not set but powernap is enabled
5915 TCPKAOnly
= supportsTCPKA
&& ((mDNSStorage
.SystemWakeOnLANEnabled
== mDNS_WakeOnBattery
) || OnBattery());
5919 TCPKAOnly
= mDNStrue
;
5922 (void)offloadKeepAlivesOnly
; // Unused.
5924 if (!service
) { LogMsg("ActivateLocalProxy: No service for interface %s", intf
->ifname
); return(mStatus_UnknownErr
); }
5927 IOObjectGetClass(service
, n1
);
5931 kern_return_t kr
= RegistryEntrySearchCFPropertyAndIOObject(service
, kIOServicePlane
, CFSTR(mDNS_IOREG_KEY
), &ref
, &parent
);
5932 IOObjectRelease(service
);
5933 if (kr
!= KERN_SUCCESS
) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s kr %d", intf
->ifname
, n1
, kr
);
5936 IOObjectGetClass(parent
, n2
);
5937 LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", intf
->ifname
, n1
, n2
);
5939 if (CFGetTypeID(ref
) != CFStringGetTypeID() || !CFEqual(ref
, CFSTR(mDNS_IOREG_VALUE
)))
5940 LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s",
5941 intf
->ifname
, n1
, n2
, CFStringGetCStringPtr(ref
, mDNSNULL
), mDNS_IOREG_VALUE
);
5942 else if (!UseInternalSleepProxy
)
5943 LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", intf
->ifname
);
5946 io_connect_t conObj
;
5947 kr
= IOServiceOpen(parent
, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE
, &conObj
);
5948 if (kr
!= KERN_SUCCESS
) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", intf
->ifname
, n1
, n2
, kr
);
5952 mDNSPlatformMemZero(&cmd
, sizeof(cmd
)); // When compiling 32-bit, make sure top 32 bits of 64-bit pointers get initialized to zero
5953 cmd
.command
= cmd_mDNSOffloadRR
;
5954 cmd
.numUDPPorts
= TCPKAOnly
? 0 : GetPortArray(mDNSTransport_UDP
, mDNSNULL
);
5955 cmd
.numTCPPorts
= TCPKAOnly
? 0 : GetPortArray(mDNSTransport_TCP
, mDNSNULL
);
5956 cmd
.numRRRecords
= CountProxyRecords(&cmd
.rrBufferSize
, TCPKAOnly
, supportsTCPKA
);
5957 cmd
.compression
= sizeof(DNSMessageHeader
);
5959 DNSMessage
*msg
= (DNSMessage
*) callocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader
) + cmd
.rrBufferSize
);
5960 cmd
.rrRecords
.ptr
= cmd
.numRRRecords
? callocL("mDNSOffloadCmd rrRecords", cmd
.numRRRecords
* sizeof(FatPtr
)) : NULL
;
5961 cmd
.udpPorts
.ptr
= cmd
.numUDPPorts
? callocL("mDNSOffloadCmd udpPorts" , cmd
.numUDPPorts
* sizeof(mDNSIPPort
)) : NULL
;
5962 cmd
.tcpPorts
.ptr
= cmd
.numTCPPorts
? callocL("mDNSOffloadCmd tcpPorts" , cmd
.numTCPPorts
* sizeof(mDNSIPPort
)) : NULL
;
5964 LogSPS("ActivateLocalProxy: msg %p %u RR %p %u, UDP %p %u, TCP %p %u",
5965 msg
, cmd
.rrBufferSize
,
5966 cmd
.rrRecords
.ptr
, cmd
.numRRRecords
,
5967 cmd
.udpPorts
.ptr
, cmd
.numUDPPorts
,
5968 cmd
.tcpPorts
.ptr
, cmd
.numTCPPorts
);
5970 if (msg
&& cmd
.rrRecords
.ptr
)
5972 GetProxyRecords(msg
, &cmd
.rrBufferSize
, cmd
.rrRecords
.ptr
, &cmd
.numRRRecords
, intf
, TCPKAOnly
, supportsTCPKA
);
5974 if (cmd
.udpPorts
.ptr
) cmd
.numUDPPorts
= TCPKAOnly
? 0 : GetPortArray(mDNSTransport_UDP
, cmd
.udpPorts
.ptr
);
5975 if (cmd
.tcpPorts
.ptr
) cmd
.numTCPPorts
= TCPKAOnly
? 0 : GetPortArray(mDNSTransport_TCP
, cmd
.tcpPorts
.ptr
);
5978 size_t outputDataSize
= sizeof(outputData
);
5979 kr
= IOConnectCallStructMethod(conObj
, 0, &cmd
, sizeof(cmd
), outputData
, &outputDataSize
);
5980 LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", intf
->ifname
, n1
, n2
, kr
);
5981 if (kr
== KERN_SUCCESS
) result
= mStatus_NoError
;
5983 if (cmd
.tcpPorts
.ptr
) freeL("mDNSOffloadCmd udpPorts", cmd
.tcpPorts
.ptr
);
5984 if (cmd
.udpPorts
.ptr
) freeL("mDNSOffloadCmd tcpPorts", cmd
.udpPorts
.ptr
);
5985 if (cmd
.rrRecords
.ptr
) freeL("mDNSOffloadCmd rrRecords", cmd
.rrRecords
.ptr
);
5986 if (msg
) freeL("mDNSOffloadCmd msg", msg
);
5987 IOServiceClose(conObj
);
5991 IOObjectRelease(parent
);
5993 *keepaliveOnly
= (TCPKAOnly
&& supportsTCPKA
) ? mDNStrue
: mDNSfalse
;
5997 #endif // APPLE_OSX_mDNSResponder
5999 mDNSlocal mDNSu8
SystemWakeForNetworkAccess(void)
6001 #if MDNSRESPONDER_SUPPORTS(APPLE, NO_WAKE_FOR_NET_ACCESS)
6002 LogRedact(MDNS_LOG_CATEGORY_SPS
, MDNS_LOG_DEBUG
, "SystemWakeForNetworkAccess: compile-time disabled");
6003 return ((mDNSu8
)mDNS_NoWake
);
6006 mDNSu8 ret
= (mDNSu8
)mDNS_NoWake
;
6008 if (DisableSleepProxyClient
)
6010 LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option");
6014 GetCurrentPMSetting(CFSTR("Wake On LAN"), &val
);
6016 ret
= (mDNSu8
)(val
!= 0) ? mDNS_WakeOnAC
: mDNS_NoWake
;
6018 #if APPLE_OSX_mDNSResponder && TARGET_OS_OSX
6019 // If we have TCP Keepalive support, system is capable of registering for TCP Keepalives.
6020 // Further policy decisions on whether to offload the records is handled during sleep processing.
6021 if ((ret
== mDNS_NoWake
) && SupportsTCPKeepAlive())
6022 ret
= (mDNSu8
)mDNS_WakeOnBattery
;
6023 #endif // APPLE_OSX_mDNSResponder
6025 LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", ret
);
6030 mDNSlocal mDNSBool
SystemSleepOnlyIfWakeOnLAN(void)
6033 // PrioritizeNetworkReachabilityOverSleep has been deprecated.
6034 // GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val);
6035 // Statically set the PrioritizeNetworkReachabilityOverSleep value to 1 for AppleTV
6038 return val
!= 0 ? mDNStrue
: mDNSfalse
;
6041 mDNSlocal mDNSBool
IsAppleNetwork(mDNS
*const m
)
6044 // Determine if we're on AppleNW based on DNSServer having 17.x.y.z IPv4 addr
6045 for (s
= m
->DNSServers
; s
; s
= s
->next
)
6047 if (s
->addr
.ip
.v4
.b
[0] == 17)
6049 LogInfo("IsAppleNetwork: Found 17.x.y.z DNSServer concluding that we are on AppleNW: %##s %#a", s
->domain
.c
, &s
->addr
);
6056 // Called with KQueueLock & mDNS lock
6057 // SetNetworkChanged is allowed to shorten (but not extend) the pause while we wait for configuration changes to settle
6058 mDNSlocal
void SetNetworkChanged(mDNSs32 delay
)
6060 mDNS
*const m
= &mDNSStorage
;
6062 if (!m
->NetworkChanged
|| m
->NetworkChanged
- NonZeroTime(m
->timenow
+ delay
) > 0)
6064 m
->NetworkChanged
= NonZeroTime(m
->timenow
+ delay
);
6065 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
, "SetNetworkChanged: Scheduling in %d ticks", delay
);
6069 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
6070 "SetNetworkChanged: *NOT* increasing delay from %d to %d", m
->NetworkChanged
- m
->timenow
, delay
);
6074 // Called with KQueueLock & mDNS lock
6075 mDNSlocal
void SetKeyChainTimer(mDNSs32 delay
)
6077 mDNS
*const m
= &mDNSStorage
;
6078 // If it's not set or it needs to happen sooner than when it's currently set
6079 if (!m
->p
->KeyChainTimer
|| m
->p
->KeyChainTimer
- NonZeroTime(m
->timenow
+ delay
) > 0)
6081 m
->p
->KeyChainTimer
= NonZeroTime(m
->timenow
+ delay
);
6082 LogInfo("SetKeyChainTimer: %d", delay
);
6086 mDNSexport
void mDNSMacOSXNetworkChanged(void)
6088 mDNS
*const m
= &mDNSStorage
;
6089 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
6090 "*** Network Configuration Change *** %d ticks late" PUB_S
,
6091 m
->NetworkChanged
? mDNS_TimeNow(m
) - m
->NetworkChanged
: 0,
6092 m
->NetworkChanged
? "" : " (no scheduled configuration change)");
6093 m
->NetworkChanged
= 0; // If we received a network change event and deferred processing, we're now dealing with it
6095 // If we have *any* TENTATIVE IPv6 addresses, wait until they've finished configuring
6096 int InfoSocket
= socket(AF_INET6
, SOCK_DGRAM
, 0);
6099 mDNSBool tentative
= mDNSfalse
;
6100 struct ifaddrs
*ifa
= myGetIfAddrs(1);
6103 if (ifa
->ifa_addr
&& ifa
->ifa_addr
->sa_family
== AF_INET6
)
6105 struct in6_ifreq ifr6
;
6106 mDNSPlatformMemZero((char *)&ifr6
, sizeof(ifr6
));
6107 strlcpy(ifr6
.ifr_name
, ifa
->ifa_name
, sizeof(ifr6
.ifr_name
));
6108 ifr6
.ifr_addr
= *(struct sockaddr_in6
*)ifa
->ifa_addr
;
6109 // We need to check for IN6_IFF_TENTATIVE here, not IN6_IFF_NOTREADY, because
6110 // IN6_IFF_NOTREADY includes both IN6_IFF_TENTATIVE and IN6_IFF_DUPLICATED addresses.
6111 // We can expect that an IN6_IFF_TENTATIVE address will shortly become ready,
6112 // but an IN6_IFF_DUPLICATED address may not.
6113 if (ioctl(InfoSocket
, SIOCGIFAFLAG_IN6
, &ifr6
) != -1)
6115 if (ifr6
.ifr_ifru
.ifru_flags6
& IN6_IFF_TENTATIVE
)
6117 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
6118 "*** Network Configuration Change *** IPv6 address " PRI_IPv6_ADDR
" TENTATIVE, will retry",
6119 &ifr6
.ifr_addr
.sin6_addr
);
6120 tentative
= mDNStrue
;
6121 // no need to check other interfaces if we already found out that one interface is TENTATIVE
6126 ifa
= ifa
->ifa_next
;
6132 SetNetworkChanged(mDNSPlatformOneSecond
/ 2);
6136 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
6137 "*** Network Configuration Change *** No IPv6 address TENTATIVE, will continue");
6140 mDNSs32 utc
= mDNSPlatformUTC();
6141 m
->SystemWakeOnLANEnabled
= SystemWakeForNetworkAccess();
6142 m
->SystemSleepOnlyIfWakeOnLAN
= SystemSleepOnlyIfWakeOnLAN();
6143 MarkAllInterfacesInactive(utc
);
6144 UpdateInterfaceList(utc
);
6145 ClearInactiveInterfaces(utc
);
6146 SetupActiveInterfaces(utc
);
6147 ReorderInterfaceList();
6149 #if APPLE_OSX_mDNSResponder
6152 NetworkInterfaceInfoOSX
*i
;
6153 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
)
6155 if (!m
->SPSSocket
) // Not being Sleep Proxy Server; close any open BPF fds
6157 if (i
->BPF_fd
>= 0 && CountProxyTargets(i
, mDNSNULL
, mDNSNULL
) == 0)
6160 else // else, we're Sleep Proxy Server; open BPF fds
6162 if (i
->Exists
&& (i
->Registered
== i
) && SPSInterface(i
) && i
->BPF_fd
== -1)
6164 LogMsg("%s mDNSMacOSXNetworkChanged: requesting BPF", i
->ifinfo
.ifname
);
6171 #endif // APPLE_OSX_mDNSResponder
6173 uDNS_SetupDNSConfig(m
);
6174 mDNS_ConfigChanged(m
);
6176 if (IsAppleNetwork(m
) != mDNS_McastTracingEnabled
)
6178 mDNS_McastTracingEnabled
= mDNS_McastTracingEnabled
? mDNSfalse
: mDNStrue
;
6179 LogInfo("mDNSMacOSXNetworkChanged: Multicast Tracing %s", mDNS_McastTracingEnabled
? "Enabled" : "Disabled");
6185 // Copy the fourth slash-delimited element from either:
6186 // State:/Network/Interface/<bsdname>/IPv4
6188 // Setup:/Network/Service/<servicename>/Interface
6189 mDNSlocal CFStringRef
CopyNameFromKey(CFStringRef key
)
6192 CFStringRef name
= NULL
;
6194 a
= CFStringCreateArrayBySeparatingStrings(NULL
, key
, CFSTR("/"));
6195 if (a
&& CFArrayGetCount(a
) == 5) name
= CFRetain(CFArrayGetValueAtIndex(a
, 3));
6196 if (a
!= NULL
) CFRelease(a
);
6201 // Whether a key from a network change notification corresponds to
6202 // an IP service that is explicitly configured for IPv4 Link Local
6203 mDNSlocal
int ChangedKeysHaveIPv4LL(CFArrayRef inkeys
)
6205 CFDictionaryRef dict
= NULL
;
6206 CFMutableArrayRef a
;
6207 const void **keys
= NULL
, **vals
= NULL
;
6208 CFStringRef pattern
= NULL
;
6212 jc
= CFArrayGetCount(inkeys
);
6215 a
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
6216 if (a
== NULL
) goto done
;
6218 // Setup:/Network/Service/[^/]+/Interface
6219 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCCompAnyRegex
, kSCEntNetInterface
);
6220 if (pattern
== NULL
) goto done
;
6221 CFArrayAppendValue(a
, pattern
);
6224 // Setup:/Network/Service/[^/]+/IPv4
6225 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCCompAnyRegex
, kSCEntNetIPv4
);
6226 if (pattern
== NULL
) goto done
;
6227 CFArrayAppendValue(a
, pattern
);
6230 dict
= SCDynamicStoreCopyMultiple(NULL
, NULL
, a
);
6235 LogMsg("ChangedKeysHaveIPv4LL: Empty dictionary");
6239 ic
= CFDictionaryGetCount(dict
);
6240 vals
= (const void **) mDNSPlatformMemAllocate(sizeof(void *) * ic
);
6241 keys
= (const void **) mDNSPlatformMemAllocate(sizeof(void *) * ic
);
6242 CFDictionaryGetKeysAndValues(dict
, keys
, vals
);
6244 // For each key we were given...
6245 for (j
= 0; j
< jc
; j
++)
6247 CFStringRef key
= CFArrayGetValueAtIndex(inkeys
, j
);
6248 CFStringRef ifname
= NULL
;
6252 // It would be nice to use a regex here
6253 if (!CFStringHasPrefix(key
, CFSTR("State:/Network/Interface/")) || !CFStringHasSuffix(key
, kSCEntNetIPv4
)) continue;
6255 if ((ifname
= CopyNameFromKey(key
)) == NULL
) continue;
6256 if (mDNS_LoggingEnabled
)
6258 if (!CFStringGetCString(ifname
, buf
, sizeof(buf
), kCFStringEncodingUTF8
)) buf
[0] = 0;
6259 LogInfo("ChangedKeysHaveIPv4LL: potential ifname %s", buf
);
6262 // Loop over the interfaces to find matching the ifname, and see if that one has kSCValNetIPv4ConfigMethodLinkLocal
6263 for (i
= 0; i
< ic
; i
++)
6265 CFDictionaryRef ipv4dict
;
6267 CFStringRef serviceid
;
6268 CFStringRef configmethod
;
6270 if (!CFStringHasSuffix(keys
[i
], kSCEntNetInterface
)) continue;
6272 if (CFDictionaryGetTypeID() != CFGetTypeID(vals
[i
])) continue;
6274 if ((name
= CFDictionaryGetValue(vals
[i
], kSCPropNetInterfaceDeviceName
)) == NULL
) continue;
6276 if (!CFEqual(ifname
, name
)) continue;
6278 if ((serviceid
= CopyNameFromKey(keys
[i
])) == NULL
) continue;
6279 if (mDNS_LoggingEnabled
)
6281 if (!CFStringGetCString(serviceid
, buf
, sizeof(buf
), kCFStringEncodingUTF8
)) buf
[0] = 0;
6282 LogInfo("ChangedKeysHaveIPv4LL: found serviceid %s", buf
);
6285 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, kSCDynamicStoreDomainSetup
, serviceid
, kSCEntNetIPv4
);
6286 CFRelease(serviceid
);
6287 if (pattern
== NULL
) continue;
6289 ipv4dict
= CFDictionaryGetValue(dict
, pattern
);
6291 if (!ipv4dict
|| CFDictionaryGetTypeID() != CFGetTypeID(ipv4dict
)) continue;
6293 configmethod
= CFDictionaryGetValue(ipv4dict
, kSCPropNetIPv4ConfigMethod
);
6294 if (!configmethod
) continue;
6296 if (mDNS_LoggingEnabled
)
6298 if (!CFStringGetCString(configmethod
, buf
, sizeof(buf
), kCFStringEncodingUTF8
)) buf
[0] = 0;
6299 LogInfo("ChangedKeysHaveIPv4LL: configmethod %s", buf
);
6302 if (CFEqual(configmethod
, kSCValNetIPv4ConfigMethodLinkLocal
)) { found
++; break; }
6309 if (vals
!= NULL
) mDNSPlatformMemFree(vals
);
6310 if (keys
!= NULL
) mDNSPlatformMemFree(keys
);
6311 if (dict
!= NULL
) CFRelease(dict
);
6316 mDNSlocal
void NetworkChanged(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *context
)
6318 (void)store
; // Parameter not used
6319 mDNS
*const m
= (mDNS
*const)context
;
6323 //mDNSs32 delay = mDNSPlatformOneSecond * 2; // Start off assuming a two-second delay
6324 const mDNSs32 delay
= (mDNSPlatformOneSecond
+ 39) / 40; // 25 ms delay
6326 const int c
= CFArrayGetCount(changedKeys
); // Count changes
6327 CFRange range
= { 0, c
};
6328 const int c_host
= (CFArrayContainsValue(changedKeys
, range
, NetworkChangedKey_Hostnames
) != 0);
6329 const int c_comp
= (CFArrayContainsValue(changedKeys
, range
, NetworkChangedKey_Computername
) != 0);
6330 const int c_udns
= (CFArrayContainsValue(changedKeys
, range
, NetworkChangedKey_DNS
) != 0);
6331 const int c_ddns
= (CFArrayContainsValue(changedKeys
, range
, NetworkChangedKey_DynamicDNS
) != 0);
6332 const int c_v4ll
= ChangedKeysHaveIPv4LL(changedKeys
);
6335 // Do immediate network changed processing for "p2p*" interfaces and
6336 // for interfaces with the IFEF_DIRECTLINK or IFEF_AWDL flag set or association with a CarPlay
6341 for (int i
= 0; i
< c
; i
++)
6343 CFStringRef key
= CFArrayGetValueAtIndex(changedKeys
, i
);
6345 // Only look at keys with prefix "State:/Network/Interface/"
6346 if (!CFStringHasPrefix(key
, NetworkChangedKey_StateInterfacePrefix
))
6349 // And suffix "IPv6" or "IPv4".
6350 if (!CFStringHasSuffix(key
, kSCEntNetIPv6
) && !CFStringHasSuffix(key
, kSCEntNetIPv4
))
6353 labels
= CFStringCreateArrayBySeparatingStrings(NULL
, key
, CFSTR("/"));
6356 n
= CFArrayGetCount(labels
);
6358 // Interface changes will have keys of the form:
6359 // State:/Network/Interface/<interfaceName>/IPv6
6360 // Thus five '/' seperated fields, the 4th one being the <interfaceName> string.
6365 // The 4th label (index = 3) should be the interface name.
6366 if (CFStringGetCString(CFArrayGetValueAtIndex(labels
, 3), buf
, sizeof(buf
), kCFStringEncodingUTF8
)
6367 && (strstr(buf
, "p2p") || (getExtendedFlags(buf
) & (IFEF_DIRECTLINK
| IFEF_AWDL
)) || IsCarPlaySSID(buf
)))
6369 LogInfo("NetworkChanged: interface %s qualifies for reduced change handling delay", buf
);
6379 //if (c && c - c_host - c_comp - c_udns - c_ddns - c_v4ll - c_fast == 0)
6380 // delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay
6382 if (mDNS_LoggingEnabled
)
6388 if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys
, i
), buf
, sizeof(buf
), kCFStringEncodingUTF8
)) buf
[0] = 0;
6389 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
, "*** Network Configuration Change *** SC key: " PUB_S
, buf
);
6391 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
6392 "*** Network Configuration Change *** %d change" PUB_S
" " PUB_S PUB_S PUB_S PUB_S PUB_S PUB_S
"delay %d" PUB_S
,
6394 c_host
? "(Local Hostname) " : "",
6395 c_comp
? "(Computer Name) " : "",
6396 c_udns
? "(DNS) " : "",
6397 c_ddns
? "(DynamicDNS) " : "",
6398 c_v4ll
? "(kSCValNetIPv4ConfigMethodLinkLocal) " : "",
6399 c_fast
? "(P2P/IFEF_DIRECTLINK/IFEF_AWDL/IsCarPlaySSID) " : "",
6401 c_ddns
? " + SetKeyChainTimer" : "");
6404 SetNetworkChanged(delay
);
6406 // Other software might pick up these changes to register or browse in WAB or BTMM domains,
6407 // so in order for secure updates to be made to the server, make sure to read the keychain and
6408 // setup the DomainAuthInfo before handing the network change.
6409 // If we don't, then we will first try to register services in the clear, then later setup the
6410 // DomainAuthInfo, which is incorrect.
6412 SetKeyChainTimer(delay
);
6414 // Don't try to call mDNSMacOSXNetworkChanged() here -- we're running on the wrong thread
6417 KQueueUnlock("NetworkChanged");
6420 #if APPLE_OSX_mDNSResponder
6421 mDNSlocal
void RefreshSPSStatus(const void *key
, const void *value
, void *context
)
6426 CFStringRef ifnameStr
= (CFStringRef
)key
;
6427 CFArrayRef array
= (CFArrayRef
)value
;
6428 if (!CFStringGetCString(ifnameStr
, buf
, sizeof(buf
), kCFStringEncodingUTF8
))
6431 LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf
, CFArrayGetCount(array
));
6432 mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState
, buf
, value
);
6436 mDNSlocal
void DynamicStoreReconnected(SCDynamicStoreRef store
, void *info
)
6438 mDNS
*const m
= (mDNS
*const)info
;
6441 KQueueLock(); // serialize with KQueueLoop()
6443 LogInfo("DynamicStoreReconnected: Reconnected");
6445 // State:/Network/MulticastDNS
6448 // State:/Network/DynamicDNS
6450 mDNSPlatformDynDNSHostNameStatusChanged(&m
->FQDN
, 1);
6452 // Note: PrivateDNS and BackToMyMac are automatically populated when configd is restarted
6453 // as we receive network change notifications and thus not necessary. But we leave it here
6454 // so that if things are done differently in the future, this code still works.
6456 // State:/Network/PrivateDNS
6457 if (privateDnsArray
)
6458 mDNSDynamicStoreSetConfig(kmDNSPrivateConfig
, mDNSNULL
, privateDnsArray
);
6460 #if APPLE_OSX_mDNSResponder
6461 // State:/Network/Interface/en0/SleepProxyServers
6463 CFDictionaryApplyFunction(spsStatusDict
, RefreshSPSStatus
, NULL
);
6465 KQueueUnlock("DynamicStoreReconnected");
6468 mDNSlocal mStatus
WatchForNetworkChanges(mDNS
*const m
)
6471 SCDynamicStoreContext context
= { 0, m
, NULL
, NULL
, NULL
};
6472 SCDynamicStoreRef store
= SCDynamicStoreCreate(NULL
, CFSTR("mDNSResponder:WatchForNetworkChanges"), NetworkChanged
, &context
);
6473 CFMutableArrayRef keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
6474 CFStringRef pattern1
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
, kSCDynamicStoreDomainState
, kSCCompAnyRegex
, kSCEntNetIPv4
);
6475 CFStringRef pattern2
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
, kSCDynamicStoreDomainState
, kSCCompAnyRegex
, kSCEntNetIPv6
);
6476 CFMutableArrayRef patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
6478 if (!store
) { LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); goto error
; }
6479 if (!keys
|| !pattern1
|| !pattern2
|| !patterns
) goto error
;
6481 CFArrayAppendValue(keys
, NetworkChangedKey_IPv4
);
6482 CFArrayAppendValue(keys
, NetworkChangedKey_IPv6
);
6483 CFArrayAppendValue(keys
, NetworkChangedKey_Hostnames
);
6484 CFArrayAppendValue(keys
, NetworkChangedKey_Computername
);
6485 CFArrayAppendValue(keys
, NetworkChangedKey_DNS
);
6486 CFArrayAppendValue(keys
, NetworkChangedKey_DynamicDNS
);
6487 CFArrayAppendValue(keys
, NetworkChangedKey_PowerSettings
);
6488 CFArrayAppendValue(patterns
, pattern1
);
6489 CFArrayAppendValue(patterns
, pattern2
);
6490 CFArrayAppendValue(patterns
, CFSTR("State:/Network/Interface/[^/]+/AirPort"));
6491 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
))
6492 { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error
; }
6494 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
6495 if (!SCDynamicStoreSetDispatchQueue(store
, dispatch_get_main_queue()))
6496 { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error
; }
6498 m
->p
->StoreRLS
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
6499 if (!m
->p
->StoreRLS
) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error
; }
6500 CFRunLoopAddSource(CFRunLoopGetMain(), m
->p
->StoreRLS
, kCFRunLoopDefaultMode
);
6502 SCDynamicStoreSetDisconnectCallBack(store
, DynamicStoreReconnected
);
6503 m
->p
->Store
= store
;
6508 if (store
) CFRelease(store
);
6511 if (patterns
) CFRelease(patterns
);
6512 if (pattern2
) CFRelease(pattern2
);
6513 if (pattern1
) CFRelease(pattern1
);
6514 if (keys
) CFRelease(keys
);
6520 mDNSlocal
void mDNSSetPacketFilterRules(char * ifname
, const ResourceRecord
*const excludeRecord
)
6522 mDNS
*const m
= &mDNSStorage
;
6524 pfArray_t portArray
;
6525 pfArray_t protocolArray
;
6528 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
6530 if ((rr
->resrec
.rrtype
== kDNSServiceType_SRV
)
6531 && ((rr
->ARType
== AuthRecordAnyIncludeP2P
) || (rr
->ARType
== AuthRecordAnyIncludeAWDLandP2P
)))
6535 if (count
>= PFPortArraySize
)
6537 LogMsg("mDNSSetPacketFilterRules: %d service limit, skipping %s", PFPortArraySize
, ARDisplayString(m
, rr
));
6541 if (excludeRecord
&& IdenticalResourceRecord(&rr
->resrec
, excludeRecord
))
6543 LogInfo("mDNSSetPacketFilterRules: record being removed, skipping %s", ARDisplayString(m
, rr
));
6547 LogMsg("mDNSSetPacketFilterRules: found %s", ARDisplayString(m
, rr
));
6549 portArray
[count
] = rr
->resrec
.rdata
->u
.srv
.port
.NotAnInteger
;
6551 // Assume <Service Instance>.<App Protocol>.<Transport Protocol>.<Name>
6552 p
= rr
->resrec
.name
->c
;
6554 // Skip to App Protocol
6558 // Skip to Transport Protocol
6562 if (SameDomainLabel(p
, (mDNSu8
*)"\x4" "_tcp"))
6564 protocolArray
[count
] = IPPROTO_TCP
;
6566 else if (SameDomainLabel(p
, (mDNSu8
*)"\x4" "_udp"))
6568 protocolArray
[count
] = IPPROTO_UDP
;
6572 LogMsg("mDNSSetPacketFilterRules: could not determine transport protocol of service");
6573 LogMsg("mDNSSetPacketFilterRules: %s", ARDisplayString(m
, rr
));
6579 mDNSPacketFilterControl(PF_SET_RULES
, ifname
, count
, portArray
, protocolArray
);
6582 // If the p2p interface already exists, update the Bonjour packet filter rules for it.
6583 mDNSexport
void mDNSUpdatePacketFilter(const ResourceRecord
*const excludeRecord
)
6585 mDNS
*const m
= &mDNSStorage
;
6587 NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
6590 if (strncmp(intf
->ifname
, "p2p", 3) == 0)
6592 LogInfo("mDNSInitPacketFilter: Setting rules for ifname %s", intf
->ifname
);
6593 mDNSSetPacketFilterRules(intf
->ifname
, excludeRecord
);
6596 intf
= GetFirstActiveInterface(intf
->next
);
6599 #else // !TARGET_OS_OSX
6600 // Currently no packet filter setup required on embedded platforms.
6601 mDNSexport
void mDNSUpdatePacketFilter(const ResourceRecord
*const excludeRecord
)
6603 (void) excludeRecord
; // unused
6607 // AWDL should no longer generate KEV_DL_MASTER_ELECTED events, so just log a message if we receive one.
6608 mDNSlocal
void newMasterElected(struct net_event_data
* ptr
)
6610 char ifname
[IFNAMSIZ
];
6611 mDNSu32 interfaceIndex
;
6613 snprintf(ifname
, IFNAMSIZ
, "%s%d", ptr
->if_name
, ptr
->if_unit
);
6614 interfaceIndex
= if_nametoindex(ifname
);
6616 if (!interfaceIndex
)
6618 LogMsg("newMasterElected: if_nametoindex(%s) failed", ifname
);
6622 LogInfo("newMasterElected: KEV_DL_MASTER_ELECTED received on ifname = %s, interfaceIndex = %d", ifname
, interfaceIndex
);
6625 // An ssth array of all zeroes indicates the peer has no services registered.
6626 mDNSlocal mDNSBool
allZeroSSTH(struct opaque_presence_indication
*op
)
6629 int *intp
= (int *) op
->ssth
;
6631 // MAX_SSTH_SIZE should always be a multiple of sizeof(int), if
6632 // it's not, print an error message and return false so that
6633 // corresponding peer records are not flushed when KEV_DL_NODE_PRESENCE event
6635 if (MAX_SSTH_SIZE
% sizeof(int))
6637 LogInfo("allZeroSSTH: MAX_SSTH_SIZE = %d not a multiple of sizeof(int)", MAX_SSTH_SIZE
);
6641 for (i
= 0; i
< (int)(MAX_SSTH_SIZE
/ sizeof(int)); i
++, intp
++)
6649 // Mark records from this peer for deletion from the cache.
6650 mDNSlocal
void removeCachedPeerRecords(mDNSu32 ifindex
, mDNSAddr
*ap
, bool purgeNow
)
6652 mDNS
*const m
= &mDNSStorage
;
6656 NetworkInterfaceInfoOSX
*infoOSX
;
6657 mDNSInterfaceID InterfaceID
;
6659 // Using mDNSPlatformInterfaceIDfromInterfaceIndex() would lead to recursive
6660 // locking issues, see: <rdar://problem/21332983>
6661 infoOSX
= IfindexToInterfaceInfoOSX((mDNSInterfaceID
)(uintptr_t)ifindex
);
6664 LogInfo("removeCachedPeerRecords: interface %d not yet active", ifindex
);
6667 InterfaceID
= infoOSX
->ifinfo
.InterfaceID
;
6669 FORALL_CACHERECORDS(slot
, cg
, cr
)
6671 if ((InterfaceID
== cr
->resrec
.InterfaceID
) && mDNSSameAddress(ap
, & cr
->sourceAddress
))
6673 LogInfo("removeCachedPeerRecords: %s %##s marking for deletion",
6674 DNSTypeName(cr
->resrec
.rrtype
), cr
->resrec
.name
->c
);
6677 mDNS_PurgeCacheResourceRecord(m
, cr
);
6679 mDNS_Reconfirm_internal(m
, cr
, 0); // use default minimum reconfirm time
6684 // Handle KEV_DL_NODE_PRESENCE event.
6685 mDNSlocal
void nodePresence(struct kev_dl_node_presence
* p
)
6687 struct opaque_presence_indication
*op
= (struct opaque_presence_indication
*) p
->node_service_info
;
6689 LogInfo("nodePresence: IPv6 address: %.16a, SUI %d", p
->sin6_node_address
.sin6_addr
.s6_addr
, op
->SUI
);
6691 // AWDL will generate a KEV_DL_NODE_PRESENCE event with SSTH field of
6692 // all zeroes when a node is present and has no services registered.
6693 if (allZeroSSTH(op
))
6697 peerAddr
.type
= mDNSAddrType_IPv6
;
6698 peerAddr
.ip
.v6
= *(mDNSv6Addr
*)&p
->sin6_node_address
.sin6_addr
;
6700 LogInfo("nodePresence: ssth is all zeroes, reconfirm cached records for this peer");
6701 removeCachedPeerRecords(p
->sdl_node_address
.sdl_index
, & peerAddr
, false);
6705 // Handle KEV_DL_NODE_ABSENCE event.
6706 mDNSlocal
void nodeAbsence(struct kev_dl_node_absence
* p
)
6710 peerAddr
.type
= mDNSAddrType_IPv6
;
6711 peerAddr
.ip
.v6
= *(mDNSv6Addr
*)&p
->sin6_node_address
.sin6_addr
;
6713 LogInfo("nodeAbsence: immediately purge cached records from %.16a", p
->sin6_node_address
.sin6_addr
.s6_addr
);
6714 removeCachedPeerRecords(p
->sdl_node_address
.sdl_index
, & peerAddr
, true);
6717 mDNSlocal
void SysEventCallBack(int s1
, short __unused filter
, void *context
, __unused mDNSBool encounteredEOF
)
6719 mDNS
*const m
= (mDNS
*const)context
;
6723 struct { struct kern_event_msg k
; char extra
[256]; } msg
;
6724 int bytes
= recv(s1
, &msg
, sizeof(msg
), 0);
6726 LogMsg("SysEventCallBack: recv error %d errno %d (%s)", bytes
, errno
, strerror(errno
));
6729 LogInfo("SysEventCallBack got %d bytes size %d %X %s %X %s %X %s id %d code %d %s",
6730 bytes
, msg
.k
.total_size
,
6731 msg
.k
.vendor_code
, msg
.k
.vendor_code
== KEV_VENDOR_APPLE
? "KEV_VENDOR_APPLE" : "?",
6732 msg
.k
.kev_class
, msg
.k
.kev_class
== KEV_NETWORK_CLASS
? "KEV_NETWORK_CLASS" : "?",
6733 msg
.k
.kev_subclass
, msg
.k
.kev_subclass
== KEV_DL_SUBCLASS
? "KEV_DL_SUBCLASS" : "?",
6734 msg
.k
.id
, msg
.k
.event_code
,
6735 msg
.k
.event_code
== KEV_DL_SIFFLAGS
? "KEV_DL_SIFFLAGS" :
6736 msg
.k
.event_code
== KEV_DL_SIFMETRICS
? "KEV_DL_SIFMETRICS" :
6737 msg
.k
.event_code
== KEV_DL_SIFMTU
? "KEV_DL_SIFMTU" :
6738 msg
.k
.event_code
== KEV_DL_SIFPHYS
? "KEV_DL_SIFPHYS" :
6739 msg
.k
.event_code
== KEV_DL_SIFMEDIA
? "KEV_DL_SIFMEDIA" :
6740 msg
.k
.event_code
== KEV_DL_SIFGENERIC
? "KEV_DL_SIFGENERIC" :
6741 msg
.k
.event_code
== KEV_DL_ADDMULTI
? "KEV_DL_ADDMULTI" :
6742 msg
.k
.event_code
== KEV_DL_DELMULTI
? "KEV_DL_DELMULTI" :
6743 msg
.k
.event_code
== KEV_DL_IF_ATTACHED
? "KEV_DL_IF_ATTACHED" :
6744 msg
.k
.event_code
== KEV_DL_IF_DETACHING
? "KEV_DL_IF_DETACHING" :
6745 msg
.k
.event_code
== KEV_DL_IF_DETACHED
? "KEV_DL_IF_DETACHED" :
6746 msg
.k
.event_code
== KEV_DL_LINK_OFF
? "KEV_DL_LINK_OFF" :
6747 msg
.k
.event_code
== KEV_DL_LINK_ON
? "KEV_DL_LINK_ON" :
6748 msg
.k
.event_code
== KEV_DL_PROTO_ATTACHED
? "KEV_DL_PROTO_ATTACHED" :
6749 msg
.k
.event_code
== KEV_DL_PROTO_DETACHED
? "KEV_DL_PROTO_DETACHED" :
6750 msg
.k
.event_code
== KEV_DL_LINK_ADDRESS_CHANGED
? "KEV_DL_LINK_ADDRESS_CHANGED" :
6751 msg
.k
.event_code
== KEV_DL_WAKEFLAGS_CHANGED
? "KEV_DL_WAKEFLAGS_CHANGED" :
6752 msg
.k
.event_code
== KEV_DL_IF_IDLE_ROUTE_REFCNT
? "KEV_DL_IF_IDLE_ROUTE_REFCNT" :
6753 msg
.k
.event_code
== KEV_DL_IFCAP_CHANGED
? "KEV_DL_IFCAP_CHANGED" :
6754 msg
.k
.event_code
== KEV_DL_LINK_QUALITY_METRIC_CHANGED
? "KEV_DL_LINK_QUALITY_METRIC_CHANGED" :
6755 msg
.k
.event_code
== KEV_DL_NODE_PRESENCE
? "KEV_DL_NODE_PRESENCE" :
6756 msg
.k
.event_code
== KEV_DL_NODE_ABSENCE
? "KEV_DL_NODE_ABSENCE" :
6757 msg
.k
.event_code
== KEV_DL_MASTER_ELECTED
? "KEV_DL_MASTER_ELECTED" :
6760 if (msg
.k
.event_code
== KEV_DL_NODE_PRESENCE
)
6761 nodePresence((struct kev_dl_node_presence
*) &msg
.k
.event_data
);
6763 if (msg
.k
.event_code
== KEV_DL_NODE_ABSENCE
)
6764 nodeAbsence((struct kev_dl_node_absence
*) &msg
.k
.event_data
);
6766 if (msg
.k
.event_code
== KEV_DL_MASTER_ELECTED
)
6767 newMasterElected((struct net_event_data
*) &msg
.k
.event_data
);
6769 // We receive network change notifications both through configd and through SYSPROTO_EVENT socket.
6770 // Configd may not generate network change events for manually configured interfaces (i.e., non-DHCP)
6771 // always during sleep/wakeup due to some race conditions (See radar:8666757). At the same time, if
6772 // "Wake on Network Access" is not turned on, the notification will not have KEV_DL_WAKEFLAGS_CHANGED.
6773 // Hence, during wake up, if we see a KEV_DL_LINK_ON (i.e., link is UP), we trigger a network change.
6775 if (msg
.k
.event_code
== KEV_DL_WAKEFLAGS_CHANGED
|| msg
.k
.event_code
== KEV_DL_LINK_ON
)
6776 SetNetworkChanged(mDNSPlatformOneSecond
* 2);
6779 // For p2p interfaces, need to open the advertised service port in the firewall.
6780 if (msg
.k
.event_code
== KEV_DL_IF_ATTACHED
)
6782 struct net_event_data
* p
;
6783 p
= (struct net_event_data
*) &msg
.k
.event_data
;
6785 if (strncmp(p
->if_name
, "p2p", 3) == 0)
6787 char ifname
[IFNAMSIZ
];
6788 snprintf(ifname
, IFNAMSIZ
, "%s%d", p
->if_name
, p
->if_unit
);
6790 LogInfo("SysEventCallBack: KEV_DL_IF_ATTACHED if_family = %d, if_unit = %d, if_name = %s", p
->if_family
, p
->if_unit
, p
->if_name
);
6792 mDNSSetPacketFilterRules(ifname
, NULL
);
6796 // For p2p interfaces, need to clear the firewall rules on interface detach
6797 if (msg
.k
.event_code
== KEV_DL_IF_DETACHED
)
6799 struct net_event_data
* p
;
6800 p
= (struct net_event_data
*) &msg
.k
.event_data
;
6802 if (strncmp(p
->if_name
, "p2p", 3) == 0)
6804 pfArray_t portArray
, protocolArray
; // not initialized since count is 0 for PF_CLEAR_RULES
6805 char ifname
[IFNAMSIZ
];
6806 snprintf(ifname
, IFNAMSIZ
, "%s%d", p
->if_name
, p
->if_unit
);
6808 LogInfo("SysEventCallBack: KEV_DL_IF_DETACHED if_family = %d, if_unit = %d, if_name = %s", p
->if_family
, p
->if_unit
, p
->if_name
);
6810 mDNSPacketFilterControl(PF_CLEAR_RULES
, ifname
, 0, portArray
, protocolArray
);
6819 mDNSlocal mStatus
WatchForSysEvents(mDNS
*const m
)
6821 m
->p
->SysEventNotifier
= socket(PF_SYSTEM
, SOCK_RAW
, SYSPROTO_EVENT
);
6822 if (m
->p
->SysEventNotifier
< 0)
6823 { LogMsg("WatchForSysEvents: socket failed error %d errno %d (%s)", m
->p
->SysEventNotifier
, errno
, strerror(errno
)); return(mStatus_NoMemoryErr
); }
6825 struct kev_request kev_req
= { KEV_VENDOR_APPLE
, KEV_NETWORK_CLASS
, KEV_DL_SUBCLASS
};
6826 int err
= ioctl(m
->p
->SysEventNotifier
, SIOCSKEVFILT
, &kev_req
);
6829 LogMsg("WatchForSysEvents: SIOCSKEVFILT failed error %d errno %d (%s)", err
, errno
, strerror(errno
));
6830 close(m
->p
->SysEventNotifier
);
6831 m
->p
->SysEventNotifier
= -1;
6832 return(mStatus_UnknownErr
);
6835 m
->p
->SysEventKQueue
.KQcallback
= SysEventCallBack
;
6836 m
->p
->SysEventKQueue
.KQcontext
= m
;
6837 m
->p
->SysEventKQueue
.KQtask
= "System Event Notifier";
6838 KQueueSet(m
->p
->SysEventNotifier
, EV_ADD
, EVFILT_READ
, &m
->p
->SysEventKQueue
);
6840 return(mStatus_NoError
);
6843 #ifndef NO_SECURITYFRAMEWORK
6844 mDNSlocal OSStatus
KeychainChanged(SecKeychainEvent keychainEvent
, SecKeychainCallbackInfo
*info
, void *context
)
6846 LogInfo("*** Keychain Changed ***");
6847 mDNS
*const m
= (mDNS
*const)context
;
6849 OSStatus err
= SecKeychainCopyDefault(&skc
);
6852 if (info
->keychain
== skc
)
6854 // For delete events, attempt to verify what item was deleted fail because the item is already gone, so we just assume they may be relevant
6855 mDNSBool relevant
= (keychainEvent
== kSecDeleteEvent
);
6858 UInt32 tags
[3] = { kSecTypeItemAttr
, kSecServiceItemAttr
, kSecAccountItemAttr
};
6859 SecKeychainAttributeInfo attrInfo
= { 3, tags
, NULL
}; // Count, array of tags, array of formats
6860 SecKeychainAttributeList
*a
= NULL
;
6861 err
= SecKeychainItemCopyAttributesAndData(info
->item
, &attrInfo
, NULL
, &a
, NULL
, NULL
);
6864 relevant
= ((a
->attr
[0].length
== 4 && (!strncasecmp(a
->attr
[0].data
, "ddns", 4) || !strncasecmp(a
->attr
[0].data
, "sndd", 4))) ||
6865 (a
->attr
[1].length
>= mDNSPlatformStrLen(dnsprefix
) && (!strncasecmp(a
->attr
[1].data
, dnsprefix
, mDNSPlatformStrLen(dnsprefix
)))));
6866 SecKeychainItemFreeAttributesAndData(a
, NULL
);
6871 LogInfo("*** Keychain Changed *** KeychainEvent=%d %s",
6873 keychainEvent
== kSecAddEvent
? "kSecAddEvent" :
6874 keychainEvent
== kSecDeleteEvent
? "kSecDeleteEvent" :
6875 keychainEvent
== kSecUpdateEvent
? "kSecUpdateEvent" : "<Unknown>");
6876 // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
6880 // To not read the keychain twice: when BTMM is enabled, changes happen to the keychain
6881 // then the BTMM DynStore dictionary, so delay reading the keychain for a second.
6882 // NetworkChanged() will reset the keychain timer to fire immediately when the DynStore changes.
6884 // In the "fixup" case where the BTMM DNS servers aren't accepting the key mDNSResponder has,
6885 // the DynStore dictionary won't change (because the BTMM zone won't change). In that case,
6886 // a one second delay is ok, as we'll still converge to correctness, and there's no race
6887 // condition between the RegistrationDomain and the DomainAuthInfo.
6889 // Lastly, non-BTMM WAB cases can use the keychain but not the DynStore, so we need to set
6890 // the timer here, as it will not get set by NetworkChanged().
6891 SetKeyChainTimer(mDNSPlatformOneSecond
);
6894 KQueueUnlock("KeychainChanged");
6904 mDNSlocal
void PowerOn(mDNS
*const m
)
6906 mDNSCoreMachineSleep(m
, false); // Will set m->SleepState = SleepState_Awake;
6908 if (m
->p
->WakeAtUTC
)
6910 long utc
= mDNSPlatformUTC();
6911 mDNSPowerRequest(-1,-1); // Need to explicitly clear any previous power requests -- they're not cleared automatically on wake
6912 if (m
->p
->WakeAtUTC
- utc
> 30)
6914 LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m
->p
->WakeAtUTC
- utc
);
6916 else if (utc
- m
->p
->WakeAtUTC
> 30)
6918 LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc
- m
->p
->WakeAtUTC
);
6920 else if (IsAppleTV())
6922 LogSPS("PowerChanged PowerOn %d seconds late, device is an AppleTV running iOS so not re-sleeping", utc
- m
->p
->WakeAtUTC
);
6926 LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m
->p
->WakeAtUTC
- utc
);
6927 m
->p
->RequestReSleep
= mDNS_TimeNow(m
) + 20 * mDNSPlatformOneSecond
;
6931 // Hold on to a sleep assertion to allow mDNSResponder to perform its maintenance activities.
6932 // This allows for the network link to come up, DHCP to get an address, mDNS to issue queries etc.
6933 // We will clear this assertion as soon as we think the mainenance activities are done.
6934 mDNSPlatformPreventSleep(DARK_WAKE_TIME
, "mDNSResponder:maintenance");
6938 mDNSlocal
void PowerChanged(void *refcon
, io_service_t service
, natural_t messageType
, void *messageArgument
)
6940 mDNS
*const m
= (mDNS
*const)refcon
;
6942 (void)service
; // Parameter not used
6943 debugf("PowerChanged %X %lX", messageType
, messageArgument
);
6945 // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting
6946 m
->SystemWakeOnLANEnabled
= SystemWakeForNetworkAccess();
6950 case kIOMessageCanSystemPowerOff
: LogSPS("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240
6951 case kIOMessageSystemWillPowerOff
: LogSPS("PowerChanged kIOMessageSystemWillPowerOff"); // E0000250
6952 mDNSCoreMachineSleep(m
, true);
6953 if (m
->SleepState
== SleepState_Sleeping
) mDNSMacOSXNetworkChanged();
6955 case kIOMessageSystemWillNotPowerOff
: LogSPS("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260
6956 case kIOMessageCanSystemSleep
: LogSPS("PowerChanged kIOMessageCanSystemSleep"); break; // E0000270
6957 case kIOMessageSystemWillSleep
: LogSPS("PowerChanged kIOMessageSystemWillSleep"); // E0000280
6958 mDNSCoreMachineSleep(m
, true);
6960 case kIOMessageSystemWillNotSleep
: LogSPS("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290
6961 case kIOMessageSystemHasPoweredOn
: LogSPS("PowerChanged kIOMessageSystemHasPoweredOn"); // E0000300
6962 // If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now
6965 LogMsg("PowerChanged kIOMessageSystemHasPoweredOn: ERROR m->SleepState %d", m
->SleepState
);
6968 // Just to be safe, schedule a mDNSMacOSXNetworkChanged(), in case we never received
6969 // the System Configuration Framework "network changed" event that we expect
6970 // to receive some time shortly after the kIOMessageSystemWillPowerOn message
6972 SetNetworkChanged(mDNSPlatformOneSecond
* 2);
6976 case kIOMessageSystemWillRestart
: LogSPS("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310
6977 case kIOMessageSystemWillPowerOn
: LogSPS("PowerChanged kIOMessageSystemWillPowerOn"); // E0000320
6979 // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake
6980 if (m
->SleepState
!= SleepState_Sleeping
)
6982 LogMsg("kIOMessageSystemWillPowerOn: ERROR m->SleepState %d", m
->SleepState
);
6983 m
->SleepState
= SleepState_Sleeping
;
6984 mDNSMacOSXNetworkChanged();
6988 default: LogSPS("PowerChanged unknown message %X", messageType
); break;
6991 if (messageType
== kIOMessageSystemWillSleep
)
6992 m
->p
->SleepCookie
= (long)messageArgument
;
6993 else if (messageType
== kIOMessageCanSystemSleep
)
6994 IOAllowPowerChange(m
->p
->PowerConnection
, (long)messageArgument
);
6996 KQueueUnlock("PowerChanged Sleep/Wake");
6999 // iPhone OS doesn't currently have SnowLeopard's IO Power Management
7000 // but it does define kIOPMAcknowledgmentOptionSystemCapabilityRequirements
7001 #if defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) && TARGET_OS_OSX
7002 mDNSlocal
void SnowLeopardPowerChanged(void *refcon
, IOPMConnection connection
, IOPMConnectionMessageToken token
, IOPMSystemPowerStateCapabilities eventDescriptor
)
7004 mDNS
*const m
= (mDNS
*const)refcon
;
7006 LogSPS("SnowLeopardPowerChanged %X %X %X%s%s%s%s%s",
7007 connection
, token
, eventDescriptor
,
7008 eventDescriptor
& kIOPMSystemPowerStateCapabilityCPU
? " CPU" : "",
7009 eventDescriptor
& kIOPMSystemPowerStateCapabilityVideo
? " Video" : "",
7010 eventDescriptor
& kIOPMSystemPowerStateCapabilityAudio
? " Audio" : "",
7011 eventDescriptor
& kIOPMSystemPowerStateCapabilityNetwork
? " Network" : "",
7012 eventDescriptor
& kIOPMSystemPowerStateCapabilityDisk
? " Disk" : "");
7014 // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting
7015 m
->SystemWakeOnLANEnabled
= SystemWakeForNetworkAccess();
7017 if (eventDescriptor
& kIOPMSystemPowerStateCapabilityCPU
)
7019 // We might be in Sleeping or Transferring state. When we go from "wakeup" to "sleep" state, we don't
7020 // go directly to sleep state, but transfer in to the sleep state during which SleepState is set to
7021 // SleepState_Transferring. During that time, we might get another wakeup before we transition to Sleeping
7022 // state. In that case, we need to acknowledge the previous "sleep" before we acknowledge the wakeup.
7025 LogSPS("SnowLeopardPowerChanged: Waking up, Acking old Sleep, SleepLimit %d SleepState %d", m
->SleepLimit
, m
->SleepState
);
7026 IOPMConnectionAcknowledgeEvent(connection
, m
->p
->SleepCookie
);
7029 LogSPS("SnowLeopardPowerChanged: Waking up, Acking Wakeup, SleepLimit %d SleepState %d", m
->SleepLimit
, m
->SleepState
);
7030 // CPU Waking. Note: Can get this message repeatedly, as other subsystems power up or down.
7031 if (m
->SleepState
!= SleepState_Awake
)
7034 // If the network notifications have already come before we got the wakeup, we ignored them and
7035 // in case we get no more, we need to trigger one.
7037 SetNetworkChanged(mDNSPlatformOneSecond
* 2);
7040 IOPMConnectionAcknowledgeEvent(connection
, token
);
7044 // CPU sleeping. Should not get this repeatedly -- once we're told that the CPU is halting
7045 // we should hear nothing more until we're told that the CPU has started executing again.
7046 if (m
->SleepState
) LogMsg("SnowLeopardPowerChanged: Sleep Error %X m->SleepState %d", eventDescriptor
, m
->SleepState
);
7048 //mDNSMacOSXNetworkChanged(m);
7049 mDNSCoreMachineSleep(m
, true);
7050 //if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m);
7051 m
->p
->SleepCookie
= token
;
7054 KQueueUnlock("SnowLeopardPowerChanged Sleep/Wake");
7058 #if COMPILER_LIKES_PRAGMA_MARK
7060 #pragma mark - /etc/hosts support
7063 // Implementation Notes
7065 // As /etc/hosts file can be huge (1000s of entries - when this comment was written, the test file had about
7066 // 23000 entries with about 4000 duplicates), we can't use a linked list to store these entries. So, we parse
7067 // them into a hash table. The implementation need to be able to do the following things efficiently
7069 // 1. Detect duplicates e.g., two entries with "1.2.3.4 foo"
7070 // 2. Detect whether /etc/hosts has changed and what has changed since the last read from the disk
7071 // 3. Ability to support multiple addresses per name e.g., "1.2.3.4 foo, 2.3.4.5 foo". To support this, we
7072 // need to be able set the RRSet of a resource record to the first one in the list and also update when
7073 // one of them go away. This is needed so that the core thinks that they are all part of the same RRSet and
7075 // 4. Don't maintain any local state about any records registered with the core to detect changes to /etc/hosts
7077 // CFDictionary is not a suitable candidate because it does not support duplicates and even if we use a custom
7078 // "hash" function to solve this, the others are hard to solve. Hence, we share the hash (AuthHash) implementation
7079 // of the core layer which does all of the above very efficiently
7081 #define ETCHOSTS_BUFSIZE 1024 // Buffer size to parse a single line in /etc/hosts
7083 mDNSexport
void FreeEtcHosts(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
7088 if (result
== mStatus_MemFree
)
7090 LogInfo("FreeEtcHosts: %s", ARDisplayString(m
, rr
));
7091 freeL("etchosts", rr
);
7095 // Returns true on success and false on failure
7096 mDNSlocal mDNSBool
mDNSMacOSXCreateEtcHostsEntry(const domainname
*domain
, const struct sockaddr
*sa
, const domainname
*cname
, char *ifname
, AuthHash
*auth
)
7101 mDNSInterfaceID InterfaceID
= mDNSInterface_LocalOnly
;
7106 LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! name NULL");
7111 LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa and cname both NULL");
7115 if (sa
&& sa
->sa_family
!= AF_INET
&& sa
->sa_family
!= AF_INET6
)
7117 LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa with bad family %d", sa
->sa_family
);
7124 mDNSu32 ifindex
= if_nametoindex(ifname
);
7127 LogMsg("mDNSMacOSXCreateEtcHostsEntry: hosts entry %##s with invalid ifname %s", domain
->c
, ifname
);
7130 InterfaceID
= (mDNSInterfaceID
)(uintptr_t)ifindex
;
7134 rrtype
= (sa
->sa_family
== AF_INET
? kDNSType_A
: kDNSType_AAAA
);
7136 rrtype
= kDNSType_CNAME
;
7138 // Check for duplicates. See whether we parsed an entry before like this ?
7139 namehash
= DomainNameHashValue(domain
);
7140 ag
= AuthGroupForName(auth
, namehash
, domain
);
7146 if (rr
->resrec
.rrtype
== rrtype
)
7148 if (rrtype
== kDNSType_A
)
7151 ip
.NotAnInteger
= ((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
;
7152 if (mDNSSameIPv4Address(rr
->resrec
.rdata
->u
.ipv4
, ip
) && InterfaceID
== rr
->resrec
.InterfaceID
)
7154 LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv4 address and InterfaceID for name %##s ID %d", domain
->c
, IIDPrintable(InterfaceID
));
7158 else if (rrtype
== kDNSType_AAAA
)
7161 ip6
.l
[0] = ((struct sockaddr_in6
*)sa
)->sin6_addr
.__u6_addr
.__u6_addr32
[0];
7162 ip6
.l
[1] = ((struct sockaddr_in6
*)sa
)->sin6_addr
.__u6_addr
.__u6_addr32
[1];
7163 ip6
.l
[2] = ((struct sockaddr_in6
*)sa
)->sin6_addr
.__u6_addr
.__u6_addr32
[2];
7164 ip6
.l
[3] = ((struct sockaddr_in6
*)sa
)->sin6_addr
.__u6_addr
.__u6_addr32
[3];
7165 if (mDNSSameIPv6Address(rr
->resrec
.rdata
->u
.ipv6
, ip6
) && InterfaceID
== rr
->resrec
.InterfaceID
)
7167 LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv6 address and InterfaceID for name %##s ID %d", domain
->c
, IIDPrintable(InterfaceID
));
7171 else if (rrtype
== kDNSType_CNAME
)
7173 if (SameDomainName(&rr
->resrec
.rdata
->u
.name
, cname
))
7175 LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same cname %##s for name %##s", cname
->c
, domain
->c
);
7183 rr
= (AuthRecord
*) callocL("etchosts", sizeof(*rr
));
7184 if (rr
== NULL
) return mDNSfalse
;
7185 mDNS_SetupResourceRecord(rr
, NULL
, InterfaceID
, rrtype
, 1, kDNSRecordTypeKnownUnique
, AuthRecordLocalOnly
, FreeEtcHosts
, NULL
);
7186 AssignDomainName(&rr
->namestorage
, domain
);
7190 rr
->resrec
.rdlength
= sa
->sa_family
== AF_INET
? sizeof(mDNSv4Addr
) : sizeof(mDNSv6Addr
);
7191 if (sa
->sa_family
== AF_INET
)
7192 rr
->resrec
.rdata
->u
.ipv4
.NotAnInteger
= ((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
;
7195 rr
->resrec
.rdata
->u
.ipv6
.l
[0] = ((struct sockaddr_in6
*)sa
)->sin6_addr
.__u6_addr
.__u6_addr32
[0];
7196 rr
->resrec
.rdata
->u
.ipv6
.l
[1] = ((struct sockaddr_in6
*)sa
)->sin6_addr
.__u6_addr
.__u6_addr32
[1];
7197 rr
->resrec
.rdata
->u
.ipv6
.l
[2] = ((struct sockaddr_in6
*)sa
)->sin6_addr
.__u6_addr
.__u6_addr32
[2];
7198 rr
->resrec
.rdata
->u
.ipv6
.l
[3] = ((struct sockaddr_in6
*)sa
)->sin6_addr
.__u6_addr
.__u6_addr32
[3];
7203 rr
->resrec
.rdlength
= DomainNameLength(cname
);
7204 rr
->resrec
.rdata
->u
.name
.c
[0] = 0;
7205 AssignDomainName(&rr
->resrec
.rdata
->u
.name
, cname
);
7207 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
7208 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Sets rr->rdatahash for us
7209 LogInfo("mDNSMacOSXCreateEtcHostsEntry: Adding resource record %s ID %d", ARDisplayString(&mDNSStorage
, rr
), IIDPrintable(rr
->resrec
.InterfaceID
));
7210 InsertAuthRecord(&mDNSStorage
, auth
, rr
);
7214 mDNSlocal
int EtcHostsParseOneName(int start
, int length
, char *buffer
, char **name
)
7219 for (i
= start
; i
< length
; i
++)
7221 if (buffer
[i
] == '#')
7223 if (buffer
[i
] != ' ' && buffer
[i
] != ',' && buffer
[i
] != '\t')
7227 // Found the start of a name, find the end and null terminate
7228 for (i
++; i
< length
; i
++)
7230 if (buffer
[i
] == ' ' || buffer
[i
] == ',' || buffer
[i
] == '\t')
7242 mDNSlocal
void mDNSMacOSXParseEtcHostsLine(char *buffer
, ssize_t length
, AuthHash
*auth
)
7246 char *ifname
= NULL
;
7253 //Ignore leading whitespaces and tabs
7254 while (*buffer
== ' ' || *buffer
== '\t')
7260 // Find the end of the address string
7261 for (i
= 0; i
< length
; i
++)
7263 if (buffer
[i
] == ' ' || buffer
[i
] == ',' || buffer
[i
] == '\t' || buffer
[i
] == '%')
7265 if (buffer
[i
] == '%')
7272 // Convert the address string to an address
7273 struct addrinfo hints
;
7274 bzero(&hints
, sizeof(hints
));
7275 hints
.ai_flags
= AI_NUMERICHOST
;
7276 struct addrinfo
*gairesults
= NULL
;
7277 if (getaddrinfo(buffer
, NULL
, &hints
, &gairesults
) != 0)
7279 LogInfo("mDNSMacOSXParseEtcHostsLine: getaddrinfo returning null");
7285 // Parse the interface
7286 ifname
= &buffer
[ifStart
];
7287 for (i
= ifStart
+ 1; i
< length
; i
++)
7289 if (buffer
[i
] == ' ' || buffer
[i
] == ',' || buffer
[i
] == '\t')
7297 i
= EtcHostsParseOneName(i
+ 1, length
, buffer
, &name1
);
7300 // Common case (no aliases) : The entry is of the form "1.2.3.4 somehost" with no trailing white spaces/tabs etc.
7301 if (!MakeDomainNameFromDNSNameString(&name1d
, name1
))
7303 LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1
);
7304 freeaddrinfo(gairesults
);
7307 mDNSMacOSXCreateEtcHostsEntry(&name1d
, gairesults
->ai_addr
, mDNSNULL
, ifname
, auth
);
7312 // We might have some extra white spaces at the end for the common case of "1.2.3.4 somehost".
7313 // When we parse again below, EtchHostsParseOneName would return -1 and we will end up
7314 // doing the right thing.
7316 if (!MakeDomainNameFromDNSNameString(&first
, name1
))
7318 LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1
);
7319 freeaddrinfo(gairesults
);
7322 mDNSMacOSXCreateEtcHostsEntry(&first
, gairesults
->ai_addr
, mDNSNULL
, ifname
, auth
);
7324 // /etc/hosts alias discussion:
7326 // If the /etc/hosts has an entry like this
7328 // ip_address cname [aliases...]
7329 // 1.2.3.4 sun star bright
7331 // star and bright are aliases (gethostbyname h_alias should point to these) and sun is the canonical
7332 // name (getaddrinfo ai_cannonname and gethostbyname h_name points to "sun")
7334 // To achieve this, we need to add the entry like this:
7340 // We store the first name we parsed in "first" and add the address (A/AAAA) record.
7341 // Then we parse additional names adding CNAME records till we reach the end.
7346 // Continue to parse additional aliases until we reach end of the line and
7347 // for each "alias" parsed, add a CNAME record where "alias" points to the first "name".
7348 // See also /etc/hosts alias discussion above
7350 i
= EtcHostsParseOneName(i
+ 1, length
, buffer
, &name2
);
7354 if ((aliasIndex
) && (*buffer
== *name2
))
7355 break; // break out of the loop if we wrap around
7357 if (!MakeDomainNameFromDNSNameString(&name2d
, name2
))
7359 LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name2
);
7360 freeaddrinfo(gairesults
);
7363 // Ignore if it points to itself
7364 if (!SameDomainName(&first
, &name2d
))
7366 if (!mDNSMacOSXCreateEtcHostsEntry(&name2d
, mDNSNULL
, &first
, ifname
, auth
))
7368 freeaddrinfo(gairesults
);
7374 LogInfo("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names first %##s, name2 %##s", first
.c
, name2d
.c
);
7378 else if (!aliasIndex
)
7380 // We have never parsed any aliases. This case happens if there
7381 // is just one name and some extra white spaces at the end.
7382 LogInfo("mDNSMacOSXParseEtcHostsLine: White space at the end of %##s", first
.c
);
7387 freeaddrinfo(gairesults
);
7390 mDNSlocal
void mDNSMacOSXParseEtcHosts(int fd
, AuthHash
*auth
)
7393 char buf
[ETCHOSTS_BUFSIZE
];
7397 if (fd
== -1) { LogInfo("mDNSMacOSXParseEtcHosts: fd is -1"); return; }
7399 fp
= fopen("/etc/hosts", "r");
7400 if (!fp
) { LogInfo("mDNSMacOSXParseEtcHosts: fp is NULL"); return; }
7404 good
= (fgets(buf
, ETCHOSTS_BUFSIZE
, fp
) != NULL
);
7407 // skip comment and empty lines
7408 if (buf
[0] == '#' || buf
[0] == '\r' || buf
[0] == '\n')
7412 if (!len
) break; // sanity check
7413 //Check for end of line code(mostly only \n but pre-OS X Macs could have only \r)
7414 if (buf
[len
- 1] == '\r' || buf
[len
- 1] == '\n')
7416 buf
[len
- 1] = '\0';
7419 // fgets always null terminates and hence even if we have no
7420 // newline at the end, it is null terminated. The callee
7421 // (mDNSMacOSXParseEtcHostsLine) expects the length to be such that
7422 // buf[length] is zero and hence we decrement len to reflect that.
7425 //Additional check when end of line code is 2 chars ie\r\n(DOS, other old OSes)
7426 //here we need to check for just \r but taking extra caution.
7427 if (buf
[len
- 1] == '\r' || buf
[len
- 1] == '\n')
7429 buf
[len
- 1] = '\0';
7433 if (!len
) //Sanity Check: len should never be zero
7435 LogMsg("mDNSMacOSXParseEtcHosts: Length is zero!");
7438 mDNSMacOSXParseEtcHostsLine(buf
, len
, auth
);
7443 mDNSlocal
void mDNSMacOSXUpdateEtcHosts(mDNS
*const m
);
7445 mDNSlocal
int mDNSMacOSXGetEtcHostsFD(void)
7447 mDNS
*const m
= &mDNSStorage
;
7448 #ifdef __DISPATCH_GROUP__
7449 // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch
7450 static dispatch_queue_t etcq
= 0;
7451 static dispatch_source_t etcsrc
= 0;
7452 static dispatch_source_t hostssrc
= 0;
7454 // First time through? just schedule ourselves on the main queue and we'll do the work later
7457 etcq
= dispatch_get_main_queue();
7460 // Do this work on the queue, not here - solves potential synchronization issues
7461 dispatch_async(etcq
, ^{mDNSMacOSXUpdateEtcHosts(m
);});
7466 if (hostssrc
) return dispatch_source_get_handle(hostssrc
);
7469 int fd
= open("/etc/hosts", O_RDONLY
);
7471 #ifdef __DISPATCH_GROUP__
7472 // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch
7475 // If the open failed and we're already watching /etc, we're done
7476 if (etcsrc
) { LogInfo("mDNSMacOSXGetEtcHostsFD: Returning etcfd because no etchosts"); return fd
; }
7478 // we aren't watching /etc, we should be
7479 fd
= open("/etc", O_RDONLY
);
7480 if (fd
== -1) { LogInfo("mDNSMacOSXGetEtcHostsFD: etc does not exist"); return -1; }
7481 etcsrc
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fd
, DISPATCH_VNODE_DELETE
| DISPATCH_VNODE_WRITE
| DISPATCH_VNODE_RENAME
, etcq
);
7487 dispatch_source_set_event_handler(etcsrc
,
7489 u_int32_t flags
= dispatch_source_get_data(etcsrc
);
7490 LogMsg("mDNSMacOSXGetEtcHostsFD: /etc changed 0x%x", flags
);
7491 if ((flags
& (DISPATCH_VNODE_DELETE
| DISPATCH_VNODE_RENAME
)) != 0)
7493 dispatch_source_cancel(etcsrc
);
7494 dispatch_release(etcsrc
);
7496 dispatch_async(etcq
, ^{mDNSMacOSXUpdateEtcHosts(m
);});
7499 if ((flags
& DISPATCH_VNODE_WRITE
) != 0 && hostssrc
== NULL
)
7501 mDNSMacOSXUpdateEtcHosts(m
);
7504 dispatch_source_set_cancel_handler(etcsrc
, ^{close(fd
);});
7505 dispatch_resume(etcsrc
);
7507 // Try and open /etc/hosts once more now that we're watching /etc, in case we missed the creation
7508 fd
= open("/etc/hosts", O_RDONLY
| O_EVTONLY
);
7509 if (fd
== -1) { LogMsg("mDNSMacOSXGetEtcHostsFD etc hosts does not exist, watching etc"); return -1; }
7512 // create a dispatch source to watch for changes to hosts file
7513 hostssrc
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fd
,
7514 (DISPATCH_VNODE_DELETE
| DISPATCH_VNODE_WRITE
| DISPATCH_VNODE_RENAME
|
7515 DISPATCH_VNODE_ATTRIB
| DISPATCH_VNODE_EXTEND
| DISPATCH_VNODE_LINK
| DISPATCH_VNODE_REVOKE
), etcq
);
7516 if (hostssrc
== NULL
)
7521 dispatch_source_set_event_handler(hostssrc
,
7523 u_int32_t flags
= dispatch_source_get_data(hostssrc
);
7524 LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts changed 0x%x", flags
);
7525 if ((flags
& (DISPATCH_VNODE_DELETE
| DISPATCH_VNODE_RENAME
)) != 0)
7527 dispatch_source_cancel(hostssrc
);
7528 dispatch_release(hostssrc
);
7530 // Bug in LibDispatch: wait a second before scheduling the block. If we schedule
7531 // the block immediately, we try to open the file and the file may not exist and may
7532 // fail to get a notification in the future. When the file does not exist and
7533 // we start to monitor the directory, on "dispatch_resume" of that source, there
7534 // is no guarantee that the file creation will be notified always because when
7535 // the dispatch_resume returns, the kevent manager may not have registered the
7536 // kevent yet but the file may have been created
7538 dispatch_async(etcq
, ^{mDNSMacOSXUpdateEtcHosts(m
);});
7541 if ((flags
& DISPATCH_VNODE_WRITE
) != 0)
7543 mDNSMacOSXUpdateEtcHosts(m
);
7546 dispatch_source_set_cancel_handler(hostssrc
, ^{LogInfo("mDNSMacOSXGetEtcHostsFD: Closing etchosts fd %d", fd
); close(fd
);});
7547 dispatch_resume(hostssrc
);
7549 // Cleanup /etc source, no need to watch it if we already have /etc/hosts
7552 dispatch_source_cancel(etcsrc
);
7553 dispatch_release(etcsrc
);
7557 LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts being monitored, and not etc");
7558 return hostssrc
? (int)dispatch_source_get_handle(hostssrc
) : -1;
7565 // When /etc/hosts is modified, flush all the cache records as there may be local
7566 // authoritative answers now
7567 mDNSlocal
void FlushAllCacheRecords(mDNS
*const m
)
7573 FORALL_CACHERECORDS(slot
, cg
, cr
)
7576 if (cr
->resrec
.InterfaceID
) continue;
7578 // If a resource record can answer A or AAAA, they need to be flushed so that we will
7579 // never used to deliver an ADD or RMV
7580 if (RRTypeAnswersQuestionType(&cr
->resrec
, kDNSType_A
) ||
7581 RRTypeAnswersQuestionType(&cr
->resrec
, kDNSType_AAAA
))
7583 LogInfo("FlushAllCacheRecords: Purging Resourcerecord %s", CRDisplayString(m
, cr
));
7584 mDNS_PurgeCacheResourceRecord(m
, cr
);
7589 // Add new entries to the core. If justCheck is set, this function does not add, just returns true
7590 mDNSlocal mDNSBool
EtcHostsAddNewEntries(AuthHash
*newhosts
, mDNSBool justCheck
)
7592 mDNS
*const m
= &mDNSStorage
;
7595 AuthRecord
*rr
, *primary
, *rrnext
;
7596 for (slot
= 0; slot
< AUTH_HASH_SLOTS
; slot
++)
7597 for (ag
= newhosts
->rrauth_hash
[slot
]; ag
; ag
= ag
->next
)
7600 for (rr
= ag
->members
; rr
; rr
= rrnext
)
7605 mDNSBool found
= mDNSfalse
;
7606 ag1
= AuthGroupForRecord(&m
->rrauth
, &rr
->resrec
);
7607 if (ag1
&& ag1
->members
)
7609 if (!primary
) primary
= ag1
->members
;
7613 // We are not using InterfaceID in checking for duplicates. This means,
7614 // if there are two addresses for a given name e.g., fe80::1%en0 and
7615 // fe80::1%en1, we only add the first one. It is not clear whether
7616 // this is a common case. To fix this, we also need to modify
7617 // mDNS_Register_internal in how it handles duplicates. If it becomes a
7618 // common case, we will fix it then.
7619 if (IdenticalResourceRecord(&rr1
->resrec
, &rr
->resrec
) && rr1
->resrec
.InterfaceID
== rr
->resrec
.InterfaceID
)
7621 LogInfo("EtcHostsAddNewEntries: Skipping, not adding %s", ARDisplayString(m
, rr1
));
7632 LogInfo("EtcHostsAddNewEntries: Entry %s not registered with core yet", ARDisplayString(m
, rr
));
7635 RemoveAuthRecord(m
, newhosts
, rr
);
7636 // if there is no primary, point to self
7637 rr
->RRSet
= (primary
? primary
: rr
);
7639 LogInfo("EtcHostsAddNewEntries: Adding %s ID %d", ARDisplayString(m
, rr
), IIDPrintable(rr
->resrec
.InterfaceID
));
7640 if (mDNS_Register_internal(m
, rr
) != mStatus_NoError
)
7641 LogMsg("EtcHostsAddNewEntries: mDNS_Register failed for %s", ARDisplayString(m
, rr
));
7648 // Delete entries from the core that are no longer needed. If justCheck is set, this function
7649 // does not delete, just returns true
7650 mDNSlocal mDNSBool
EtcHostsDeleteOldEntries(AuthHash
*newhosts
, mDNSBool justCheck
)
7652 mDNS
*const m
= &mDNSStorage
;
7655 AuthRecord
*rr
, *rrnext
;
7656 for (slot
= 0; slot
< AUTH_HASH_SLOTS
; slot
++)
7657 for (ag
= m
->rrauth
.rrauth_hash
[slot
]; ag
; ag
= ag
->next
)
7658 for (rr
= ag
->members
; rr
; rr
= rrnext
)
7660 mDNSBool found
= mDNSfalse
;
7664 if (rr
->RecordCallback
!= FreeEtcHosts
) continue;
7665 ag1
= AuthGroupForRecord(newhosts
, &rr
->resrec
);
7671 if (IdenticalResourceRecord(&rr1
->resrec
, &rr
->resrec
))
7673 LogInfo("EtcHostsDeleteOldEntries: Old record %s found in new, skipping", ARDisplayString(m
, rr
));
7680 // there is no corresponding record in newhosts for the same name. This means
7681 // we should delete this from the core.
7686 LogInfo("EtcHostsDeleteOldEntries: Record %s not found in new, deleting", ARDisplayString(m
, rr
));
7689 // if primary is going away, make sure that the rest of the records
7690 // point to the new primary
7691 if (rr
== ag
->members
)
7693 AuthRecord
*new_primary
= rr
->next
;
7694 AuthRecord
*r
= new_primary
;
7699 LogInfo("EtcHostsDeleteOldEntries: Updating Resource Record %s to primary", ARDisplayString(m
, r
));
7700 r
->RRSet
= new_primary
;
7702 else LogMsg("EtcHostsDeleteOldEntries: ERROR!! Resource Record %s not pointing to primary %##s", ARDisplayString(m
, r
), r
->resrec
.name
);
7706 LogInfo("EtcHostsDeleteOldEntries: Deleting %s", ARDisplayString(m
, rr
));
7707 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
7713 mDNSlocal
void UpdateEtcHosts(mDNS
*const m
, void *context
)
7715 AuthHash
*newhosts
= (AuthHash
*)context
;
7719 //Delete old entries from the core if they are not present in the newhosts
7720 EtcHostsDeleteOldEntries(newhosts
, mDNSfalse
);
7721 // Add the new entries to the core if not already present in the core
7722 EtcHostsAddNewEntries(newhosts
, mDNSfalse
);
7725 mDNSlocal
void FreeNewHosts(AuthHash
*newhosts
)
7728 AuthGroup
*ag
, *agnext
;
7729 AuthRecord
*rr
, *rrnext
;
7731 for (slot
= 0; slot
< AUTH_HASH_SLOTS
; slot
++)
7732 for (ag
= newhosts
->rrauth_hash
[slot
]; ag
; ag
= agnext
)
7735 for (rr
= ag
->members
; rr
; rr
= rrnext
)
7738 freeL("etchosts", rr
);
7740 freeL("AuthGroups", ag
);
7744 mDNSlocal
void mDNSMacOSXUpdateEtcHosts(mDNS
*const m
)
7748 // As we will be modifying the core, we can only have one thread running at
7749 // any point in time.
7752 mDNSPlatformMemZero(&newhosts
, sizeof(AuthHash
));
7754 // Get the file desecriptor (will trigger us to start watching for changes)
7755 int fd
= mDNSMacOSXGetEtcHostsFD();
7758 LogInfo("mDNSMacOSXUpdateEtcHosts: Parsing /etc/hosts fd %d", fd
);
7759 mDNSMacOSXParseEtcHosts(fd
, &newhosts
);
7761 else LogInfo("mDNSMacOSXUpdateEtcHosts: /etc/hosts is not present");
7763 // Optimization: Detect whether /etc/hosts changed or not.
7765 // 1. Check to see if there are any new entries. We do this by seeing whether any entries in
7766 // newhosts is already registered with core. If we find at least one entry that is not
7767 // registered with core, then it means we have work to do.
7769 // 2. Next, we check to see if any of the entries that are registered with core is not present
7770 // in newhosts. If we find at least one entry that is not present, it means we have work to
7773 // Note: We may not have to hold the lock right here as KQueueLock is held which prevents any
7774 // other thread from running. But mDNS_Lock is needed here as we will be traversing the core
7775 // data structure in EtcHostsDeleteOldEntries/NewEntries which might expect the lock to be held
7776 // in the future and this code does not have to change.
7778 // Add the new entries to the core if not already present in the core
7779 if (!EtcHostsAddNewEntries(&newhosts
, mDNStrue
))
7781 // No new entries to add, check to see if we need to delete any old entries from the
7782 // core if they are not present in the newhosts
7783 if (!EtcHostsDeleteOldEntries(&newhosts
, mDNStrue
))
7785 LogInfo("mDNSMacOSXUpdateEtcHosts: No work");
7786 FreeNewHosts(&newhosts
);
7788 KQueueUnlock("/etc/hosts changed");
7793 // This will flush the cache, stop and start the query so that the queries
7794 // can look at the /etc/hosts again
7798 // We can't delete and free the records here. We wait for the mDNSCoreRestartAddressQueries to
7799 // deliver RMV events. It has to be done in a deferred way because we can't deliver RMV
7800 // events for local records *before* the RMV events for cache records. mDNSCoreRestartAddressQueries
7801 // delivers these events in the right order and then calls us back to delete them.
7803 // Similarly, we do a deferred Registration of the record because mDNSCoreRestartAddressQueries
7804 // is a common function that looks at all local auth records and delivers a RMV including
7805 // the records that we might add here. If we deliver a ADD here, it will get a RMV and then when
7806 // the query is restarted, it will get another ADD. To avoid this (ADD-RMV-ADD), we defer registering
7807 // the record until the RMVs are delivered in mDNSCoreRestartAddressQueries after which UpdateEtcHosts
7808 // is called back where we do the Registration of the record. This results in RMV followed by ADD which
7810 mDNSCoreRestartAddressQueries(m
, mDNSfalse
, FlushAllCacheRecords
, UpdateEtcHosts
, &newhosts
);
7811 FreeNewHosts(&newhosts
);
7813 KQueueUnlock("/etc/hosts changed");
7816 #if COMPILER_LIKES_PRAGMA_MARK
7818 #pragma mark - Initialization & Teardown
7821 CF_EXPORT CFDictionaryRef
_CFCopySystemVersionDictionary(void);
7822 CF_EXPORT
const CFStringRef _kCFSystemVersionProductNameKey
;
7823 CF_EXPORT
const CFStringRef _kCFSystemVersionProductVersionKey
;
7824 CF_EXPORT
const CFStringRef _kCFSystemVersionBuildVersionKey
;
7826 // Major version 13 is 10.9.x
7827 mDNSexport
void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring
)
7829 int major
= 0, minor
= 0;
7830 char letter
= 0, prodname
[256]="<Unknown>", prodvers
[256]="<Unknown>", buildver
[256]="<Unknown>";
7831 CFDictionaryRef vers
= _CFCopySystemVersionDictionary();
7834 CFStringRef cfprodname
= CFDictionaryGetValue(vers
, _kCFSystemVersionProductNameKey
);
7835 CFStringRef cfprodvers
= CFDictionaryGetValue(vers
, _kCFSystemVersionProductVersionKey
);
7836 CFStringRef cfbuildver
= CFDictionaryGetValue(vers
, _kCFSystemVersionBuildVersionKey
);
7838 CFStringGetCString(cfprodname
, prodname
, sizeof(prodname
), kCFStringEncodingUTF8
);
7840 CFStringGetCString(cfprodvers
, prodvers
, sizeof(prodvers
), kCFStringEncodingUTF8
);
7841 if (cfbuildver
&& CFStringGetCString(cfbuildver
, buildver
, sizeof(buildver
), kCFStringEncodingUTF8
))
7842 sscanf(buildver
, "%d%c%d", &major
, &letter
, &minor
);
7848 LogMsg("Note: No Major Build Version number found; assuming 13");
7851 mDNS_snprintf(HINFO_SWstring
, 256, "%s %s (%s), %s", prodname
, prodvers
, buildver
, STRINGIFY(mDNSResponderVersion
));
7852 //LogMsg("%s %s (%s), %d %c %d", prodname, prodvers, buildver, major, letter, minor);
7854 // If product name is "Mac OS X" (or similar) we set OSXVers, else we set iOSVers;
7855 if ((prodname
[0] & 0xDF) == 'M')
7861 // Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT.
7862 // If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses --
7863 // we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses.
7864 mDNSlocal mDNSBool
mDNSPlatformInit_CanReceiveUnicast(void)
7867 int s
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
7869 LogMsg("mDNSPlatformInit_CanReceiveUnicast: socket error %d errno %d (%s)", s
, errno
, strerror(errno
));
7872 struct sockaddr_in s5353
;
7873 s5353
.sin_family
= AF_INET
;
7874 s5353
.sin_port
= MulticastDNSPort
.NotAnInteger
;
7875 s5353
.sin_addr
.s_addr
= 0;
7876 err
= bind(s
, (struct sockaddr
*)&s5353
, sizeof(s5353
));
7880 if (err
) LogMsg("No unicast UDP responses");
7881 else debugf("Unicast UDP responses okay");
7885 mDNSlocal
void CreatePTRRecord(const domainname
*domain
)
7888 const domainname
*pname
= (domainname
*)"\x9" "localhost";
7890 rr
= (AuthRecord
*) callocL("localhosts", sizeof(*rr
));
7891 if (rr
== NULL
) return;
7893 mDNS_SetupResourceRecord(rr
, mDNSNULL
, mDNSInterface_LocalOnly
, kDNSType_PTR
, kHostNameTTL
, kDNSRecordTypeKnownUnique
, AuthRecordLocalOnly
, mDNSNULL
, mDNSNULL
);
7894 AssignDomainName(&rr
->namestorage
, domain
);
7896 rr
->resrec
.rdlength
= DomainNameLength(pname
);
7897 rr
->resrec
.rdata
->u
.name
.c
[0] = 0;
7898 AssignDomainName(&rr
->resrec
.rdata
->u
.name
, pname
);
7900 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
7901 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Sets rr->rdatahash for us
7902 mDNS_Register(&mDNSStorage
, rr
);
7905 // Setup PTR records for 127.0.0.1 and ::1. This helps answering them locally rather than relying
7906 // on the external DNS server to answer this. Sometimes, the DNS servers don't respond in a timely
7907 // fashion and applications depending on this e.g., telnetd, times out after 30 seconds creating
7908 // a bad user experience. For now, we specifically create only localhosts to handle radar://9354225
7910 // Note: We could have set this up while parsing the entries in /etc/hosts. But this is kept separate
7911 // intentionally to avoid adding to the complexity of code handling /etc/hosts.
7912 mDNSlocal
void SetupLocalHostRecords(void)
7916 MakeDomainNameFromDNSNameString(&name
, "1.0.0.127.in-addr.arpa.");
7917 CreatePTRRecord(&name
);
7919 MakeDomainNameFromDNSNameString(&name
, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.");
7920 CreatePTRRecord(&name
);
7923 #if APPLE_OSX_mDNSResponder // Don't compile for dnsextd target
7924 mDNSlocal
void setSameDomainLabelPointer(void);
7927 // Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows:
7928 // 1) query for b._dns-sd._udp.local on LocalOnly interface
7929 // (.local manually generated via explicit callback)
7930 // 2) for each search domain (from prefs pane), query for b._dns-sd._udp.<searchdomain>.
7931 // 3) for each result from (2), register LocalOnly PTR record b._dns-sd._udp.local. -> <result>
7932 // 4) result above should generate a callback from question in (1). result added to global list
7933 // 5) global list delivered to client via GetSearchDomainList()
7934 // 6) client calls to enumerate domains now go over LocalOnly interface
7935 // (!!!KRS may add outgoing interface in addition)
7937 #if MDNSRESPONDER_SUPPORTS(APPLE, IGNORE_HOSTS_FILE)
7938 mDNSlocal mStatus
RegisterLocalOnlyAddressRecord(const domainname
*const name
, mDNSu16 type
, const void *rdata
, mDNSu16 rdlength
)
7943 if (rdlength
!= 4) return (mStatus_BadParamErr
);
7947 if (rdlength
!= 16) return (mStatus_BadParamErr
);
7951 return (mStatus_BadParamErr
);
7954 AuthRecord
*rr
= (AuthRecord
*) callocL("etchosts", sizeof(*rr
));
7955 if (!rr
) return (mStatus_NoMemoryErr
);
7957 mDNS_SetupResourceRecord(rr
, NULL
, mDNSInterface_LocalOnly
, type
, 1, kDNSRecordTypeKnownUnique
, AuthRecordLocalOnly
, FreeEtcHosts
, NULL
);
7958 AssignDomainName(&rr
->namestorage
, name
);
7959 mDNSPlatformMemCopy(rr
->resrec
.rdata
->u
.data
, rdata
, rdlength
);
7961 const mStatus err
= mDNS_Register_internal(&mDNSStorage
, rr
);
7964 LogMsg("RegisterLocalOnlyAddressRecord: mDNS_Register error %d registering %s", err
, ARDisplayString(&mDNSStorage
, rr
));
7965 freeL("etchosts", rr
);
7970 mDNSlocal
void RegisterLocalOnlyARecord(const domainname
*const name
, const mDNSv4Addr
*const addr
)
7972 RegisterLocalOnlyAddressRecord(name
, kDNSType_A
, addr
->b
, (mDNSu16
)sizeof(mDNSv4Addr
));
7975 mDNSlocal
void RegisterLocalOnlyAAAARecord(const domainname
*const name
, const mDNSv6Addr
*const addr
)
7977 RegisterLocalOnlyAddressRecord(name
, kDNSType_AAAA
, addr
->b
, (mDNSu16
)sizeof(mDNSv6Addr
));
7979 #endif // MDNSRESPONDER_SUPPORTS(APPLE, IGNORE_HOSTS_FILE)
7981 mDNSlocal mStatus
mDNSPlatformInit_setup(mDNS
*const m
)
7985 char HINFO_SWstring
[256] = "";
7986 mDNSMacOSXSystemBuildNumber(HINFO_SWstring
);
7988 #if APPLE_OSX_mDNSResponder
7989 setSameDomainLabelPointer();
7992 err
= mDNSHelperInit();
7996 // Store mDNSResponder Platform
7999 m
->mDNS_plat
= platform_OSX
;
8004 m
->mDNS_plat
= platform_Atv
;
8006 m
->mDNS_plat
= platform_iOS
;
8010 m
->mDNS_plat
= platform_NonApple
;
8013 // In 10.4, mDNSResponder is launched very early in the boot process, while other subsystems are still in the process of starting up.
8014 // If we can't read the user's preferences, then we sleep a bit and try again, for up to five seconds before we give up.
8016 for (i
=0; i
<100; i
++)
8018 domainlabel testlabel
;
8020 GetUserSpecifiedLocalHostName(&testlabel
);
8021 if (testlabel
.c
[0]) break;
8025 m
->hostlabel
.c
[0] = 0;
8027 #if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
8028 GetRandomUUIDLocalHostname(&m
->RandomizedHostname
);
8030 int get_model
[2] = { CTL_HW
, HW_MODEL
};
8031 size_t len_model
= sizeof(HINFO_HWstring_buffer
);
8033 // Normal Apple model names are of the form "iPhone2,1", and
8034 // internal code names are strings containing no commas, e.g. "N88AP".
8035 // We used to ignore internal code names, but Apple now uses these internal code names
8036 // even in released shipping products, so we no longer ignore strings containing no commas.
8037 // if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0 && strchr(HINFO_HWstring_buffer, ','))
8038 if (sysctl(get_model
, 2, HINFO_HWstring_buffer
, &len_model
, NULL
, 0) == 0)
8039 HINFO_HWstring
= HINFO_HWstring_buffer
;
8041 // For names of the form "iPhone2,1" we use "iPhone" as the prefix for automatic name generation.
8042 // For names of the form "N88AP" containg no comma, we use the entire string.
8043 HINFO_HWstring_prefixlen
= strchr(HINFO_HWstring_buffer
, ',') ? strcspn(HINFO_HWstring
, "0123456789") : strlen(HINFO_HWstring
);
8045 if (mDNSPlatformInit_CanReceiveUnicast())
8046 m
->CanReceiveUnicastOn5353
= mDNStrue
;
8048 mDNSu32 hlen
= mDNSPlatformStrLen(HINFO_HWstring
);
8049 mDNSu32 slen
= mDNSPlatformStrLen(HINFO_SWstring
);
8050 if (hlen
+ slen
< 254)
8052 m
->HIHardware
.c
[0] = hlen
;
8053 m
->HISoftware
.c
[0] = slen
;
8054 mDNSPlatformMemCopy(&m
->HIHardware
.c
[1], HINFO_HWstring
, hlen
);
8055 mDNSPlatformMemCopy(&m
->HISoftware
.c
[1], HINFO_SWstring
, slen
);
8058 m
->p
->permanentsockets
.port
= MulticastDNSPort
;
8059 m
->p
->permanentsockets
.m
= m
;
8060 m
->p
->permanentsockets
.sktv4
= -1;
8061 m
->p
->permanentsockets
.kqsv4
.KQcallback
= myKQSocketCallBack
;
8062 m
->p
->permanentsockets
.kqsv4
.KQcontext
= &m
->p
->permanentsockets
;
8063 m
->p
->permanentsockets
.kqsv4
.KQtask
= "IPv4 UDP packet reception";
8064 m
->p
->permanentsockets
.sktv6
= -1;
8065 m
->p
->permanentsockets
.kqsv6
.KQcallback
= myKQSocketCallBack
;
8066 m
->p
->permanentsockets
.kqsv6
.KQcontext
= &m
->p
->permanentsockets
;
8067 m
->p
->permanentsockets
.kqsv6
.KQtask
= "IPv6 UDP packet reception";
8069 err
= SetupSocket(&m
->p
->permanentsockets
, MulticastDNSPort
, AF_INET
, mDNSNULL
);
8070 if (err
) LogMsg("mDNSPlatformInit_setup: SetupSocket(AF_INET) failed error %d errno %d (%s)", err
, errno
, strerror(errno
));
8071 err
= SetupSocket(&m
->p
->permanentsockets
, MulticastDNSPort
, AF_INET6
, mDNSNULL
);
8072 if (err
) LogMsg("mDNSPlatformInit_setup: SetupSocket(AF_INET6) failed error %d errno %d (%s)", err
, errno
, strerror(errno
));
8074 struct sockaddr_in s4
;
8075 socklen_t n4
= sizeof(s4
);
8076 if (getsockname(m
->p
->permanentsockets
.sktv4
, (struct sockaddr
*)&s4
, &n4
) < 0)
8077 LogMsg("getsockname v4 error %d (%s)", errno
, strerror(errno
));
8079 m
->UnicastPort4
.NotAnInteger
= s4
.sin_port
;
8081 if (m
->p
->permanentsockets
.sktv6
>= 0)
8083 struct sockaddr_in6 s6
;
8084 socklen_t n6
= sizeof(s6
);
8085 if (getsockname(m
->p
->permanentsockets
.sktv6
, (struct sockaddr
*)&s6
, &n6
) < 0) LogMsg("getsockname v6 error %d (%s)", errno
, strerror(errno
));
8086 else m
->UnicastPort6
.NotAnInteger
= s6
.sin6_port
;
8089 m
->p
->InterfaceList
= mDNSNULL
;
8090 m
->p
->InterfaceMonitors
= NULL
;
8091 m
->p
->userhostlabel
.c
[0] = 0;
8092 m
->p
->usernicelabel
.c
[0] = 0;
8093 m
->p
->prevoldnicelabel
.c
[0] = 0;
8094 m
->p
->prevnewnicelabel
.c
[0] = 0;
8095 m
->p
->prevoldhostlabel
.c
[0] = 0;
8096 m
->p
->prevnewhostlabel
.c
[0] = 0;
8097 m
->p
->NotifyUser
= 0;
8098 m
->p
->KeyChainTimer
= 0;
8099 m
->p
->WakeAtUTC
= 0;
8100 m
->p
->RequestReSleep
= 0;
8101 // Assume that everything is good to begin with. If something is not working,
8102 // we will detect that when we start sending questions.
8103 m
->p
->v4answers
= 1;
8104 m
->p
->v6answers
= 1;
8105 m
->p
->DNSTrigger
= 0;
8106 m
->p
->LastConfigGeneration
= 0;
8107 m
->p
->if_interface_changed
= mDNSfalse
;
8109 NetworkChangedKey_IPv4
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetIPv4
);
8110 NetworkChangedKey_IPv6
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetIPv6
);
8111 NetworkChangedKey_Hostnames
= SCDynamicStoreKeyCreateHostNames(NULL
);
8112 NetworkChangedKey_Computername
= SCDynamicStoreKeyCreateComputerName(NULL
);
8113 NetworkChangedKey_DNS
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetDNS
);
8114 NetworkChangedKey_StateInterfacePrefix
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
, kSCDynamicStoreDomainState
, CFSTR(""), NULL
);
8115 if (!NetworkChangedKey_IPv4
|| !NetworkChangedKey_IPv6
|| !NetworkChangedKey_Hostnames
|| !NetworkChangedKey_Computername
|| !NetworkChangedKey_DNS
|| !NetworkChangedKey_StateInterfacePrefix
)
8116 { LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr
); }
8118 err
= WatchForNetworkChanges(m
);
8119 if (err
) { LogMsg("mDNSPlatformInit_setup: WatchForNetworkChanges failed %d", err
); return(err
); }
8121 err
= WatchForSysEvents(m
);
8122 if (err
) { LogMsg("mDNSPlatformInit_setup: WatchForSysEvents failed %d", err
); return(err
); }
8124 mDNSs32 utc
= mDNSPlatformUTC();
8125 m
->SystemWakeOnLANEnabled
= SystemWakeForNetworkAccess();
8127 UpdateInterfaceList(utc
);
8128 SetupActiveInterfaces(utc
);
8129 ReorderInterfaceList();
8131 // Explicitly ensure that our Keychain operations utilize the system domain.
8132 #ifndef NO_SECURITYFRAMEWORK
8133 SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem
);
8137 SetDomainSecrets(m
);
8141 #ifndef NO_SECURITYFRAMEWORK
8142 err
= SecKeychainAddCallback(KeychainChanged
, kSecAddEventMask
|kSecDeleteEventMask
|kSecUpdateEventMask
, m
);
8143 if (err
) { LogMsg("mDNSPlatformInit_setup: SecKeychainAddCallback failed %d", err
); return(err
); }
8146 #if !defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) || TARGET_OS_IPHONE
8147 LogMsg("Note: Compiled without SnowLeopard Fine-Grained Power Management support");
8150 IOReturn iopmerr
= IOPMConnectionCreate(CFSTR("mDNSResponder"), kIOPMSystemPowerStateCapabilityCPU
, &c
);
8151 if (iopmerr
) LogMsg("IOPMConnectionCreate failed %d", iopmerr
);
8154 iopmerr
= IOPMConnectionSetNotification(c
, m
, SnowLeopardPowerChanged
);
8155 if (iopmerr
) LogMsg("IOPMConnectionSetNotification failed %d", iopmerr
);
8158 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
8159 IOPMConnectionSetDispatchQueue(c
, dispatch_get_main_queue());
8160 LogInfo("IOPMConnectionSetDispatchQueue is now running");
8162 iopmerr
= IOPMConnectionScheduleWithRunLoop(c
, CFRunLoopGetMain(), kCFRunLoopDefaultMode
);
8163 if (iopmerr
) LogMsg("IOPMConnectionScheduleWithRunLoop failed %d", iopmerr
);
8164 LogInfo("IOPMConnectionScheduleWithRunLoop is now running");
8165 #endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */
8168 m
->p
->IOPMConnection
= iopmerr
? mDNSNULL
: c
;
8169 if (iopmerr
) // If IOPMConnectionCreate unavailable or failed, proceed with old-style power notification code below
8170 #endif // kIOPMAcknowledgmentOptionSystemCapabilityRequirements
8172 m
->p
->PowerConnection
= IORegisterForSystemPower(m
, &m
->p
->PowerPortRef
, PowerChanged
, &m
->p
->PowerNotifier
);
8173 if (!m
->p
->PowerConnection
) { LogMsg("mDNSPlatformInit_setup: IORegisterForSystemPower failed"); return(-1); }
8176 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
8177 IONotificationPortSetDispatchQueue(m
->p
->PowerPortRef
, dispatch_get_main_queue());
8179 CFRunLoopAddSource(CFRunLoopGetMain(), IONotificationPortGetRunLoopSource(m
->p
->PowerPortRef
), kCFRunLoopDefaultMode
);
8180 #endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */
8184 #if APPLE_OSX_mDNSResponder
8185 // Note: We use SPMetricPortability > 35 to indicate a laptop of some kind
8186 // SPMetricPortability <= 35 means nominally a non-portable machine (i.e. Mac mini or better)
8187 // Apple TVs, AirPort base stations, and Time Capsules do not actually weigh 3kg, but we assign them
8188 // higher 'nominal' masses to indicate they should be treated as being relatively less portable than a laptop
8189 if (!strncasecmp(HINFO_HWstring
, "Xserve", 6)) { SPMetricPortability
= 25 /* 30kg */; SPMetricMarginalPower
= 84 /* 250W */; SPMetricTotalPower
= 85 /* 300W */; }
8190 else if (!strncasecmp(HINFO_HWstring
, "RackMac", 7)) { SPMetricPortability
= 25 /* 30kg */; SPMetricMarginalPower
= 84 /* 250W */; SPMetricTotalPower
= 85 /* 300W */; }
8191 else if (!strncasecmp(HINFO_HWstring
, "MacPro", 6)) { SPMetricPortability
= 27 /* 20kg */; SPMetricMarginalPower
= 84 /* 250W */; SPMetricTotalPower
= 85 /* 300W */; }
8192 else if (!strncasecmp(HINFO_HWstring
, "PowerMac", 8)) { SPMetricPortability
= 27 /* 20kg */; SPMetricMarginalPower
= 82 /* 160W */; SPMetricTotalPower
= 83 /* 200W */; }
8193 else if (!strncasecmp(HINFO_HWstring
, "iMac", 4)) { SPMetricPortability
= 30 /* 10kg */; SPMetricMarginalPower
= 77 /* 50W */; SPMetricTotalPower
= 78 /* 60W */; }
8194 else if (!strncasecmp(HINFO_HWstring
, "Macmini", 7)) { SPMetricPortability
= 33 /* 5kg */; SPMetricMarginalPower
= 73 /* 20W */; SPMetricTotalPower
= 74 /* 25W */; }
8195 else if (!strncasecmp(HINFO_HWstring
, "TimeCapsule", 11)) { SPMetricPortability
= 34 /* 4kg */; SPMetricMarginalPower
= 10 /* ~0W */; SPMetricTotalPower
= 70 /* 13W */; }
8196 else if (!strncasecmp(HINFO_HWstring
, "AirPort", 7)) { SPMetricPortability
= 35 /* 3kg */; SPMetricMarginalPower
= 10 /* ~0W */; SPMetricTotalPower
= 70 /* 12W */; }
8197 else if ( IsAppleTV() ) { SPMetricPortability
= 35 /* 3kg */; SPMetricMarginalPower
= 60 /* 1W */; SPMetricTotalPower
= 63 /* 2W */; }
8198 else if (!strncasecmp(HINFO_HWstring
, "MacBook", 7)) { SPMetricPortability
= 37 /* 2kg */; SPMetricMarginalPower
= 71 /* 13W */; SPMetricTotalPower
= 72 /* 15W */; }
8199 else if (!strncasecmp(HINFO_HWstring
, "PowerBook", 9)) { SPMetricPortability
= 37 /* 2kg */; SPMetricMarginalPower
= 71 /* 13W */; SPMetricTotalPower
= 72 /* 15W */; }
8200 LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d Features %d",
8201 HINFO_HWstring_prefixlen
, HINFO_HWstring
, HINFO_HWstring
, SPMetricPortability
, SPMetricMarginalPower
, SPMetricTotalPower
, SPMetricFeatures
);
8202 #endif // APPLE_OSX_mDNSResponder
8204 // Currently this is not defined. SSL code will eventually fix this. If it becomes
8205 // critical, we will define this to workaround the bug in SSL.
8206 #ifdef __SSL_NEEDS_SERIALIZATION__
8207 SSLqueue
= dispatch_queue_create("com.apple.mDNSResponder.SSLQueue", NULL
);
8209 SSLqueue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
8211 if (SSLqueue
== mDNSNULL
) LogMsg("dispatch_queue_create: SSL queue NULL");
8213 #if MDNSRESPONDER_SUPPORTS(APPLE, IGNORE_HOSTS_FILE)
8214 // On device OSes (iOS, tvOS, watchOS, etc.), ignore /etc/hosts unless the OS is an internal build. When the /etc/hosts
8215 // file is ignored, LocalOnly auth records will be registered for localhost and broadcasthost addresses contained in the
8216 // standard /etc/hosts file:
8218 // 127.0.0.1 localhost
8219 // 255.255.255.255 broadcasthost
8222 if (!IsAppleInternalBuild())
8224 const domainname
*const localHostName
= (const domainname
*) "\x9" "localhost";
8225 const domainname
*const broadcastHostName
= (const domainname
*) "\xd" "broadcasthost";
8226 const mDNSv4Addr localHostV4
= { { 127, 0, 0, 1 } };
8227 mDNSv6Addr localHostV6
;
8229 // Register localhost 127.0.0.1 A record.
8231 RegisterLocalOnlyARecord(localHostName
, &localHostV4
);
8233 // Register broadcasthost 255.255.255.255 A record.
8235 RegisterLocalOnlyARecord(broadcastHostName
, &onesIPv4Addr
);
8237 // Register localhost ::1 AAAA record.
8239 mDNSPlatformMemZero(&localHostV6
, sizeof(localHostV6
));
8240 localHostV6
.b
[15] = 1;
8241 RegisterLocalOnlyAAAARecord(localHostName
, &localHostV6
);
8246 mDNSMacOSXUpdateEtcHosts(m
);
8248 SetupLocalHostRecords();
8250 #if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH)
8251 dso_transport_init();
8254 return(mStatus_NoError
);
8257 mDNSexport mStatus
mDNSPlatformInit(mDNS
*const m
)
8260 LogMsg("Note: Compiled without Apple-specific Split-DNS support");
8263 // Adding interfaces will use this flag, so set it now.
8264 m
->DivertMulticastAdvertisements
= !m
->AdvertiseLocalAddresses
;
8266 #if APPLE_OSX_mDNSResponder
8267 m
->SPSBrowseCallback
= UpdateSPSStatus
;
8268 #endif // APPLE_OSX_mDNSResponder
8270 mStatus result
= mDNSPlatformInit_setup(m
);
8272 // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already
8273 // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately
8274 if (result
== mStatus_NoError
)
8276 mDNSCoreInitComplete(m
, mStatus_NoError
);
8277 initializeD2DPlugins(m
);
8279 result
= DNSSECCryptoInit(m
);
8283 mDNSexport
void mDNSPlatformClose(mDNS
*const m
)
8285 if (m
->p
->PowerConnection
)
8287 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
8288 IONotificationPortSetDispatchQueue(m
->p
->PowerPortRef
, NULL
);
8290 CFRunLoopRemoveSource(CFRunLoopGetMain(), IONotificationPortGetRunLoopSource(m
->p
->PowerPortRef
), kCFRunLoopDefaultMode
);
8292 // According to <http://developer.apple.com/qa/qa2004/qa1340.html>, a single call
8293 // to IORegisterForSystemPower creates *three* objects that need to be disposed individually:
8294 IODeregisterForSystemPower(&m
->p
->PowerNotifier
);
8295 IOServiceClose ( m
->p
->PowerConnection
);
8296 IONotificationPortDestroy ( m
->p
->PowerPortRef
);
8297 m
->p
->PowerConnection
= 0;
8302 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
8303 if (!SCDynamicStoreSetDispatchQueue(m
->p
->Store
, NULL
))
8304 LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed");
8306 CFRunLoopRemoveSource(CFRunLoopGetMain(), m
->p
->StoreRLS
, kCFRunLoopDefaultMode
);
8307 CFRunLoopSourceInvalidate(m
->p
->StoreRLS
);
8308 CFRelease(m
->p
->StoreRLS
);
8309 m
->p
->StoreRLS
= NULL
;
8311 CFRelease(m
->p
->Store
);
8317 CFRunLoopRemoveSource(CFRunLoopGetMain(), m
->p
->PMRLS
, kCFRunLoopDefaultMode
);
8318 CFRunLoopSourceInvalidate(m
->p
->PMRLS
);
8319 CFRelease(m
->p
->PMRLS
);
8323 if (m
->p
->SysEventNotifier
>= 0) { close(m
->p
->SysEventNotifier
); m
->p
->SysEventNotifier
= -1; }
8324 terminateD2DPlugins();
8326 mDNSs32 utc
= mDNSPlatformUTC();
8327 MarkAllInterfacesInactive(utc
);
8328 ClearInactiveInterfaces(utc
);
8329 CloseSocketSet(&m
->p
->permanentsockets
);
8331 if (m
->p
->InterfaceMonitors
)
8333 CFArrayRef monitors
= m
->p
->InterfaceMonitors
;
8334 m
->p
->InterfaceMonitors
= NULL
;
8335 const CFIndex n
= CFArrayGetCount(monitors
);
8336 for (CFIndex i
= 0; i
< n
; i
++)
8338 mdns_interface_monitor_invalidate((mdns_interface_monitor_t
) CFArrayGetValueAtIndex(monitors
, i
));
8340 CFRelease(monitors
);
8344 #if COMPILER_LIKES_PRAGMA_MARK
8346 #pragma mark - General Platform Support Layer functions
8349 mDNSexport mDNSu32
mDNSPlatformRandomNumber(void)
8351 return(arc4random());
8354 mDNSexport mDNSs32 mDNSPlatformOneSecond
= 1000;
8355 mDNSexport mDNSu32 mDNSPlatformClockDivisor
= 0;
8357 mDNSexport mStatus
mDNSPlatformTimeInit(void)
8359 // Notes: Typical values for mach_timebase_info:
8360 // tbi.numer = 1000 million
8361 // tbi.denom = 33 million
8362 // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds;
8363 // numer / denom = nanoseconds per hardware clock tick (e.g. 30);
8364 // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033)
8365 // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333)
8366 // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds
8368 // Arithmetic notes:
8369 // tbi.denom is at least 1, and not more than 2^32-1.
8370 // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t.
8371 // tbi.denom is at least 1, and not more than 2^32-1.
8372 // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9.
8373 // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz,
8374 // which is unlikely on any current or future Macintosh.
8375 // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz.
8376 // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code.
8377 struct mach_timebase_info tbi
;
8378 kern_return_t result
= mach_timebase_info(&tbi
);
8379 if (result
== KERN_SUCCESS
) mDNSPlatformClockDivisor
= ((uint64_t)tbi
.denom
* 1000000) / tbi
.numer
;
8383 mDNSexport mDNSs32
mDNSPlatformRawTime(void)
8385 if (mDNSPlatformClockDivisor
== 0) { LogMsg("mDNSPlatformRawTime called before mDNSPlatformTimeInit"); return(0); }
8387 static uint64_t last_mach_absolute_time
= 0;
8388 //static uint64_t last_mach_absolute_time = 0x8000000000000000LL; // Use this value for testing the alert display
8389 uint64_t this_mach_absolute_time
= mach_absolute_time();
8390 if ((int64_t)this_mach_absolute_time
- (int64_t)last_mach_absolute_time
< 0)
8392 LogMsg("mDNSPlatformRawTime: last_mach_absolute_time %08X%08X", last_mach_absolute_time
);
8393 LogMsg("mDNSPlatformRawTime: this_mach_absolute_time %08X%08X", this_mach_absolute_time
);
8394 // Update last_mach_absolute_time *before* calling NotifyOfElusiveBug()
8395 last_mach_absolute_time
= this_mach_absolute_time
;
8396 // Note: This bug happens all the time on 10.3
8397 NotifyOfElusiveBug("mach_absolute_time went backwards!",
8398 "This error occurs from time to time, often on newly released hardware, "
8399 "and usually the exact cause is different in each instance.\r\r"
8400 "Please file a new Radar bug report with the title “mach_absolute_time went backwards” "
8401 "and assign it to Radar Component “Kernel” Version “X”.");
8403 last_mach_absolute_time
= this_mach_absolute_time
;
8405 return((mDNSs32
)(this_mach_absolute_time
/ mDNSPlatformClockDivisor
));
8408 mDNSexport mDNSs32
mDNSPlatformUTC(void)
8413 // Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves
8414 mDNSexport
void mDNSPlatformLock (const mDNS
*const m
) { (void)m
; }
8415 mDNSexport
void mDNSPlatformUnlock (const mDNS
*const m
) { (void)m
; }
8416 mDNSexport mDNSu32
mDNSPlatformStrLCopy( void *dst
, const void *src
, mDNSu32 dstlen
) { return (strlcpy((char *)dst
, (const char *)src
, dstlen
)); }
8417 mDNSexport mDNSu32
mDNSPlatformStrLen ( const void *src
) { return(strlen((const char*)src
)); }
8418 mDNSexport
void mDNSPlatformMemCopy( void *dst
, const void *src
, mDNSu32 len
) { memcpy(dst
, src
, len
); }
8419 mDNSexport mDNSBool
mDNSPlatformMemSame(const void *dst
, const void *src
, mDNSu32 len
) { return(memcmp(dst
, src
, len
) == 0); }
8420 mDNSexport
int mDNSPlatformMemCmp(const void *dst
, const void *src
, mDNSu32 len
) { return(memcmp(dst
, src
, len
)); }
8421 mDNSexport
void mDNSPlatformMemZero( void *dst
, mDNSu32 len
) { memset(dst
, 0, len
); }
8422 mDNSexport
void mDNSPlatformQsort ( void *base
, int nel
, int width
, int (*compar
)(const void *, const void *))
8424 return (qsort(base
, nel
, width
, compar
));
8426 #if !MDNS_MALLOC_DEBUGGING
8427 mDNSexport
void *mDNSPlatformMemAllocate(mDNSu32 len
) { return(mallocL("mDNSPlatformMemAllocate", len
)); }
8428 mDNSexport
void *mDNSPlatformMemAllocateClear(mDNSu32 len
) { return(callocL("mDNSPlatformMemAllocateClear", len
)); }
8429 mDNSexport
void mDNSPlatformMemFree (void *mem
) { freeL("mDNSPlatformMemFree", mem
); }
8432 mDNSexport
void mDNSPlatformSetAllowSleep(mDNSBool allowSleep
, const char *reason
)
8434 mDNS
*const m
= &mDNSStorage
;
8435 if (allowSleep
&& m
->p
->IOPMAssertion
)
8437 LogInfo("%s Destroying NoIdleSleep power assertion", __FUNCTION__
);
8438 IOPMAssertionRelease(m
->p
->IOPMAssertion
);
8439 m
->p
->IOPMAssertion
= 0;
8441 else if (!allowSleep
)
8443 #ifdef kIOPMAssertionTypeNoIdleSleep
8444 if (m
->p
->IOPMAssertion
)
8446 IOPMAssertionRelease(m
->p
->IOPMAssertion
);
8447 m
->p
->IOPMAssertion
= 0;
8450 CFStringRef assertionName
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s.%d %s"), getprogname(), getpid(), reason
? reason
: "");
8451 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep
, kIOPMAssertionLevelOn
, assertionName
? assertionName
: CFSTR("mDNSResponder"), &m
->p
->IOPMAssertion
);
8452 if (assertionName
) CFRelease(assertionName
);
8453 LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__
);
8458 mDNSexport
void mDNSPlatformPreventSleep(mDNSu32 timeout
, const char *reason
)
8460 mDNS
*const m
= &mDNSStorage
;
8461 if (m
->p
->IOPMAssertion
)
8463 LogSPS("Sleep Assertion is already being held. Will not attempt to get it again for %d seconds for %s", timeout
, reason
);
8466 #ifdef kIOPMAssertionTypeNoIdleSleep
8468 #if TARGET_OS_IPHONE
8470 return; // No need for maintenance wakes on non-AppleTV embedded devices.
8473 double timeoutVal
= (double)timeout
;
8474 CFStringRef str
= CFStringCreateWithCString(NULL
, reason
, kCFStringEncodingUTF8
);
8475 CFNumberRef Timeout_num
= CFNumberCreate(NULL
, kCFNumberDoubleType
, &timeoutVal
);
8476 CFMutableDictionaryRef assertionProperties
= CFDictionaryCreateMutable(NULL
, 0,
8477 &kCFTypeDictionaryKeyCallBacks
,
8478 &kCFTypeDictionaryValueCallBacks
);
8480 CFDictionarySetValue(assertionProperties
, kIOPMAssertionTypeKey
, kIOPMAssertPreventUserIdleSystemSleep
);
8482 CFDictionarySetValue(assertionProperties
, kIOPMAssertionTypeKey
, kIOPMAssertMaintenanceActivity
);
8484 CFDictionarySetValue(assertionProperties
, kIOPMAssertionTimeoutKey
, Timeout_num
);
8485 CFDictionarySetValue(assertionProperties
, kIOPMAssertionNameKey
, str
);
8487 IOPMAssertionCreateWithProperties(assertionProperties
, (IOPMAssertionID
*)&m
->p
->IOPMAssertion
);
8489 CFRelease(Timeout_num
);
8490 CFRelease(assertionProperties
);
8491 LogSPS("Got an idle sleep assertion for %d seconds for %s", timeout
, reason
);
8495 mDNSexport
void mDNSPlatformSendWakeupPacket(mDNSInterfaceID InterfaceID
, char *EthAddr
, char *IPAddr
, int iteration
)
8500 ifindex
= mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage
, InterfaceID
, mDNStrue
);
8503 LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex
);
8506 mDNSSendWakeupPacket(ifindex
, EthAddr
, IPAddr
, iteration
);
8509 mDNSexport mDNSBool
mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID
)
8511 NetworkInterfaceInfoOSX
*info
;
8513 if (InterfaceID
== mDNSInterface_P2P
)
8516 // mDNSInterface_BLE not considered a D2D interface for the purpose of this
8517 // routine, since it's not implemented via a D2D plugin.
8518 if (InterfaceID
== mDNSInterface_BLE
)
8521 if ( (InterfaceID
== mDNSInterface_Any
)
8522 || (InterfaceID
== mDNSInterfaceMark
)
8523 || (InterfaceID
== mDNSInterface_LocalOnly
))
8526 // Compare to cached AWDL interface ID.
8527 if (AWDLInterfaceID
&& (InterfaceID
== AWDLInterfaceID
))
8530 info
= IfindexToInterfaceInfoOSX(InterfaceID
);
8533 // this log message can print when operations are stopped on an interface that has gone away
8534 LogInfo("mDNSPlatformInterfaceIsD2D: Invalid interface index %d", InterfaceID
);
8538 return (mDNSBool
) info
->D2DInterface
;
8541 #if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
8542 mDNSexport mDNSBool
mDNSPlatformInterfaceIsAWDL(const mDNSInterfaceID interfaceID
)
8544 return ((AWDLInterfaceID
&& (interfaceID
== AWDLInterfaceID
)) ? mDNStrue
: mDNSfalse
);
8548 // Filter records send over P2P (D2D) type interfaces
8549 // Note that the terms P2P and D2D are used synonymously in the current code and comments.
8550 mDNSexport mDNSBool
mDNSPlatformValidRecordForInterface(const AuthRecord
*rr
, mDNSInterfaceID InterfaceID
)
8552 // For an explicit match to a valid interface ID, return true.
8553 if (rr
->resrec
.InterfaceID
== InterfaceID
)
8556 // Only filtering records for D2D type interfaces, return true for all other interface types.
8557 if (!mDNSPlatformInterfaceIsD2D(InterfaceID
))
8560 // If it's an AWDL interface the record must be explicitly marked to include AWDL.
8561 if (InterfaceID
== AWDLInterfaceID
)
8563 if (rr
->ARType
== AuthRecordAnyIncludeAWDL
|| rr
->ARType
== AuthRecordAnyIncludeAWDLandP2P
)
8569 // Send record if it is explicitly marked to include all other P2P type interfaces.
8570 if (rr
->ARType
== AuthRecordAnyIncludeP2P
|| rr
->ARType
== AuthRecordAnyIncludeAWDLandP2P
)
8573 // Don't send the record over this interface.
8577 // Filter questions send over P2P (D2D) type interfaces.
8578 mDNSexport mDNSBool
mDNSPlatformValidQuestionForInterface(DNSQuestion
*q
, const NetworkInterfaceInfo
*intf
)
8580 // For an explicit match to a valid interface ID, return true.
8581 if (q
->InterfaceID
== intf
->InterfaceID
)
8584 // Only filtering questions for D2D type interfaces
8585 if (!mDNSPlatformInterfaceIsD2D(intf
->InterfaceID
))
8588 // If it's an AWDL interface the question must be explicitly marked to include AWDL.
8589 if (intf
->InterfaceID
== AWDLInterfaceID
)
8591 if (q
->flags
& kDNSServiceFlagsIncludeAWDL
)
8597 // Sent question if it is explicitly marked to include all other P2P type interfaces.
8598 if (q
->flags
& kDNSServiceFlagsIncludeP2P
)
8601 // Don't send the question over this interface.
8605 // Returns true unless record was received over the AWDL interface and
8606 // the question was not specific to the AWDL interface or did not specify kDNSServiceInterfaceIndexAny
8607 // with the kDNSServiceFlagsIncludeAWDL flag set.
8608 mDNSexport mDNSBool
mDNSPlatformValidRecordForQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
8610 if (!rr
->InterfaceID
|| (rr
->InterfaceID
== q
->InterfaceID
))
8613 if ((rr
->InterfaceID
== AWDLInterfaceID
) && !(q
->flags
& kDNSServiceFlagsIncludeAWDL
))
8619 // formating time to RFC 4034 format
8620 mDNSexport
void mDNSPlatformFormatTime(unsigned long te
, mDNSu8
*buf
, int bufsize
)
8623 time_t t
= (time_t)te
;
8624 // Time since epoch : strftime takes "tm". Convert seconds to "tm" using
8625 // gmtime_r first and then use strftime
8626 gmtime_r(&t
, &tmTime
);
8627 strftime((char *)buf
, bufsize
, "%Y%m%d%H%M%S", &tmTime
);
8630 mDNSexport mDNSs32
mDNSPlatformGetPID()
8635 // Schedule a function asynchronously on the main queue
8636 mDNSexport
void mDNSPlatformDispatchAsync(mDNS
*const m
, void *context
, AsyncDispatchFunc func
)
8638 // KQueueLock/Unlock is used for two purposes
8640 // 1. We can't be running along with the KQueue thread and hence acquiring the lock
8641 // serializes the access to the "core"
8643 // 2. KQueueUnlock also sends a message wake up the KQueue thread which in turn wakes
8644 // up and calls udsserver_idle which schedules the messages across the uds socket.
8645 // If "func" delivers something to the uds socket from the dispatch thread, it will
8646 // not be delivered immediately if not for the Unlock.
8647 dispatch_async(dispatch_get_main_queue(), ^{
8650 KQueueUnlock("mDNSPlatformDispatchAsync");
8651 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
8652 // KQueueUnlock is a noop. Hence, we need to run kick off the idle loop
8653 // to handle any message that "func" might deliver.
8654 TriggerEventCompletion();
8659 // definitions for device-info record construction
8660 #define DEVINFO_MODEL "model="
8661 #define DEVINFO_MODEL_LEN sizeof_string(DEVINFO_MODEL)
8663 #define OSX_VER "osxvers="
8664 #define OSX_VER_LEN sizeof_string(OSX_VER)
8665 #define VER_NUM_LEN 2 // 2 digits of version number added to base string
8667 #define MODEL_COLOR "ecolor="
8668 #define MODEL_COLOR_LEN sizeof_string(MODEL_COLOR)
8669 #define MODEL_RGB_VALUE_LEN sizeof_string("255,255,255") // 'r,g,b'
8671 // Bytes available in TXT record for model name after subtracting space for other
8672 // fixed size strings and their length bytes.
8673 #define MAX_MODEL_NAME_LEN (256 - (DEVINFO_MODEL_LEN + 1) - (OSX_VER_LEN + VER_NUM_LEN + 1) - (MODEL_COLOR_LEN + MODEL_RGB_VALUE_LEN + 1))
8675 mDNSlocal mDNSu8
getModelIconColors(char *color
)
8677 mDNSPlatformMemZero(color
, MODEL_RGB_VALUE_LEN
+ 1);
8679 #if TARGET_OS_OSX && defined(kIOPlatformDeviceEnclosureColorKey)
8684 IOReturn rGetDeviceColor
= IOPlatformGetDeviceColor(kIOPlatformDeviceEnclosureColorKey
,
8685 &red
, &green
, &blue
);
8686 if (kIOReturnSuccess
== rGetDeviceColor
)
8688 // IOKit was able to get enclosure color for the current device.
8689 return snprintf(color
, MODEL_RGB_VALUE_LEN
+ 1, "%d,%d,%d", red
, green
, blue
);
8697 // Initialize device-info TXT record contents and return total length of record data.
8698 mDNSexport mDNSu32
initializeDeviceInfoTXT(mDNS
*m
, mDNSu8
*ptr
)
8700 mDNSu8
*bufferStart
= ptr
;
8701 mDNSu8 len
= m
->HIHardware
.c
[0] < MAX_MODEL_NAME_LEN
? m
->HIHardware
.c
[0] : MAX_MODEL_NAME_LEN
;
8703 *ptr
= DEVINFO_MODEL_LEN
+ len
; // total length of DEVINFO_MODEL string plus the hardware name string
8705 mDNSPlatformMemCopy(ptr
, DEVINFO_MODEL
, DEVINFO_MODEL_LEN
);
8706 ptr
+= DEVINFO_MODEL_LEN
;
8707 mDNSPlatformMemCopy(ptr
, m
->HIHardware
.c
+ 1, len
);
8710 // only include this string for OSX
8713 char ver_num
[VER_NUM_LEN
+ 1]; // version digits + null written by snprintf
8714 *ptr
= OSX_VER_LEN
+ VER_NUM_LEN
; // length byte
8716 mDNSPlatformMemCopy(ptr
, OSX_VER
, OSX_VER_LEN
);
8718 // convert version number to ASCII, add 1 for terminating null byte written by snprintf()
8719 // WARNING: This code assumes that OSXVers is always exactly two digits
8720 snprintf(ver_num
, VER_NUM_LEN
+ 1, "%d", OSXVers
);
8721 mDNSPlatformMemCopy(ptr
, ver_num
, VER_NUM_LEN
);
8724 char rgb
[MODEL_RGB_VALUE_LEN
+ 1]; // RGB value + null written by snprintf
8725 len
= getModelIconColors(rgb
);
8728 *ptr
= MODEL_COLOR_LEN
+ len
; // length byte
8731 mDNSPlatformMemCopy(ptr
, MODEL_COLOR
, MODEL_COLOR_LEN
);
8732 ptr
+= MODEL_COLOR_LEN
;
8734 mDNSPlatformMemCopy(ptr
, rgb
, len
);
8739 return (ptr
- bufferStart
);
8742 #if APPLE_OSX_mDNSResponder // Don't compile for dnsextd target
8744 // Use the scalar version of SameDomainLabel() by default
8745 mDNSlocal mDNSBool
scalarSameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
);
8746 mDNSlocal mDNSBool
vectorSameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
);
8747 mDNSlocal
mDNSBool (*SameDomainLabelPointer
)(const mDNSu8
*a
, const mDNSu8
*b
) = scalarSameDomainLabel
;
8749 #include <System/machine/cpu_capabilities.h>
8750 #define _cpu_capabilities ((uint32_t*) _COMM_PAGE_CPU_CAPABILITIES)[0]
8752 #if TARGET_OS_IPHONE
8753 #include <arm_neon.h>
8755 // Cache line aligned table that returns 32 for the upper case letters.
8756 // This will take up 4 cache lines.
8757 static const __attribute__ ((aligned(64))) uint8_t upper_to_lower_case_table
[256] = {
8758 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8759 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8760 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8761 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8762 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
8763 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
8764 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8765 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8766 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8767 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8768 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8771 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8772 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8773 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
8777 mDNSlocal mDNSBool
vectorSameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
8779 const int len
= *a
++;
8781 if (len
> MAX_DOMAIN_LABEL
)
8783 fprintf(stderr
, "v: Malformed label (too long)\n");
8792 uint32_t len_count
= len
;
8794 uint8x16_t vA
, vB
, vARotated
, vBRotated
, vMaskA
, vMaskB
;
8796 uint8x16_t v32
= vdupq_n_u8(32);
8797 uint8x16_t v37
= vdupq_n_u8(37);
8798 uint8x16_t v101
= vdupq_n_u8(101);
8799 #if !defined __arm64__
8801 uint32x2_t vtemp32d
;
8805 while(len_count
> 15)
8812 //Make vA to lowercase if there is any uppercase.
8813 vARotated
= vaddq_u8(vA
, v37
); //Map 'A' ~ 'Z' from '65' ~ '90' to '102' ~ '127'.
8814 vMaskA
= vcgtq_s8(vARotated
, v101
); //Check if anything is greater than '101' which means we have uppercase letters.
8815 vMaskA
= vandq_u8(vMaskA
, v32
); //Prepare 32 for the elements with uppercase letters.
8816 vA
= vaddq_u8(vA
, vMaskA
); //Add 32 only to the uppercase letters to make them lowercase letters.
8818 //Make vB to lowercase if there is any uppercase.
8819 vBRotated
= vaddq_u8(vB
, v37
); //Map 'A' ~ 'Z' from '65' ~ '90' to '102' ~ '127'.
8820 vMaskB
= vcgtq_s8(vBRotated
, v101
); //Check if anything is greater than '101' which means we have uppercase letters.
8821 vMaskB
= vandq_u8(vMaskB
, v32
); //Prepare 32 for the elements with uppercase letters.
8822 vB
= vaddq_u8(vB
, vMaskB
); //Add 32 only to the uppercase letters to make them lowercase letters.
8825 vA
= vceqq_u8(vA
, vB
);
8827 #if defined __arm64__
8828 //View 8-bit element as 32-bit => a3 a2 a1 a0
8829 //If min of 4 32-bit values in vA is 0xffffffff, then it means we have 0xff for all 16.
8830 if(vminvq_u32(vA
) != 0xffffffffU
)
8836 //See if any element was not same.
8837 //View 8-bit element as 16-bit => a7 a6 a5 a4 a3 a2 a1 a0
8838 //(a7+a6) (a5+a4) (a3+a2) (a1+a0) => Each will be 0xffff + 0xffff = 0x0001fffe when all same.
8839 vtemp32
= vpaddlq_u16(vA
);
8840 vtemp32d
= vpadd_u32(vget_low_u32(vtemp32
), vget_high_u32(vtemp32
));
8841 vtemp32d
= vpadd_u32(vtemp32d
, vtemp32d
);
8842 sum
= vget_lane_u32(vtemp32d
, 0);
8844 //0x0001fffe + 0x0001fffe + 0x0001fffe + 0x0001fffe = 0x0007fff8U when all same.
8845 if(sum
!= 0x0007fff8U
)
8854 uint8x8_t vAd
, vBd
, vARotatedd
, vBRotatedd
, vMaskAd
, vMaskBd
;
8856 uint8x8_t v32d
= vdup_n_u8(32);
8857 uint8x8_t v37d
= vdup_n_u8(37);
8858 uint8x8_t v101d
= vdup_n_u8(101);
8860 while(len_count
> 7)
8867 //Make vA to lowercase if there is any uppercase.
8868 vARotatedd
= vadd_u8(vAd
, v37d
); //Map 'A' ~ 'Z' from '65' ~ '90' to '102' ~ '127'.
8869 vMaskAd
= vcgt_s8(vARotatedd
, v101d
); //Check if anything is greater than '101' which means we have uppercase letters.
8870 vMaskAd
= vand_u8(vMaskAd
, v32d
); //Prepare 32 for the elements with uppercase letters.
8871 vAd
= vadd_u8(vAd
, vMaskAd
); //Add 32 only to the uppercase letters to make them lowercase letters.
8873 //Make vB to lowercase if there is any uppercase.
8874 vBRotatedd
= vadd_u8(vBd
, v37d
); //Map 'A' ~ 'Z' from '65' ~ '90' to '102' ~ '127'.
8875 vMaskBd
= vcgt_s8(vBRotatedd
, v101d
); //Check if anything is greater than '101' which means we have uppercase letters.
8876 vMaskBd
= vand_u8(vMaskBd
, v32d
); //Prepare 32 for the elements with uppercase letters.
8877 vBd
= vadd_u8(vBd
, vMaskBd
); //Add 32 only to the uppercase letters to make them lowercase letters.
8880 vAd
= vceq_u8(vAd
, vBd
);
8882 #if defined __arm64__
8883 //View 8-bit element as 32-bit => a1 a0
8884 //If min of 2 32-bit values in vAd is 0xffffffff, then it means we have 0xff for all 16.
8885 if(vminv_u32(vAd
) != 0xffffffffU
)
8891 //See if any element was not same.
8892 //View 8-bit element as 16-bit => a3 a2 a1 a0
8893 //(a3+a2) (a1+a0) => Each will be 0xffff + 0xffff = 0x0001fffe when all same.
8894 vtemp32d
= vpaddl_u16(vAd
);
8895 vtemp32d
= vpadd_u32(vtemp32d
, vtemp32d
);
8896 sum
= vget_lane_u32(vtemp32d
, 0);
8898 //0x0001fffe + 0x0001fffe = 0x0003fffc when all same.
8899 if(sum
!= 0x0003fffcU
)
8908 while(len_count
> 0)
8913 ac
+= upper_to_lower_case_table
[ac
];
8914 bc
+= upper_to_lower_case_table
[bc
];
8926 // Use vectorized implementation if it is supported on this platform.
8927 mDNSlocal
void setSameDomainLabelPointer(void)
8929 if(_cpu_capabilities
& kHasNeon
)
8932 SameDomainLabelPointer
= vectorSameDomainLabel
;
8933 LogMsg("setSameDomainLabelPointer: using vector code");
8936 LogMsg("setSameDomainLabelPointer: using scalar code");
8938 #endif // TARGET_OS_IPHONE
8941 #include <smmintrin.h>
8943 // Cache line aligned table that returns 32 for the upper case letters.
8944 // This will take up 4 cache lines.
8945 static const __attribute__ ((aligned(64))) uint8_t upper_to_lower_case_table
[256] = {
8946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8947 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8950 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
8951 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
8952 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8953 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8954 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8955 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8956 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8957 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8958 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8959 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8960 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8961 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
8965 mDNSlocal mDNSBool
vectorSameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
8967 const int len
= *a
++;
8969 if (len
> MAX_DOMAIN_LABEL
)
8971 fprintf(stderr
, "v: Malformed label (too long)\n");
8980 uint32_t len_count
= len
;
8982 static const __attribute__ ((aligned(16))) unsigned char c_32
[16] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 };
8983 static const __attribute__ ((aligned(16))) unsigned char c_37
[16] = { 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37 };
8984 static const __attribute__ ((aligned(16))) unsigned char c_101
[16] = { 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101 };
8985 __m128i v37
= _mm_load_si128((__m128i
*)c_37
);
8986 __m128i v101
= _mm_load_si128((__m128i
*)c_101
);
8987 __m128i v32
= _mm_load_si128((__m128i
*)c_32
);
8990 __m128i vA
, vB
, vARotated
, vBRotated
, vMaskA
, vMaskB
;
8992 //AVX code that uses higher bandwidth (more elements per vector) was removed
8993 //to speed up the processing on the small sizes.
8994 //When I had them, the performance of 1 ~ 8 characters were slower by about 10% ~ 30%.
8995 while(len_count
> 15)
8997 vA
= _mm_loadu_si128((__m128i
*)a
);
8998 vB
= _mm_loadu_si128((__m128i
*)b
);
9002 //Make vA to lowercase if there is any uppercase.
9003 vARotated
= _mm_add_epi8(vA
, v37
); //Map 'A' ~ 'Z' from '65' ~ '90' to '102' ~ '127'.
9004 vMaskA
= _mm_cmpgt_epi8(vARotated
, v101
); //Check if anything is greater than '101' which means we have uppercase letters.
9005 vMaskA
= _mm_and_si128(vMaskA
, v32
); //Prepare 32 for the elements with uppercase letters.
9006 vA
= _mm_add_epi8(vA
, vMaskA
); //Add 32 only to the uppercase letters to make them lowercase letters.
9008 //Make vB to lowercase if there is any uppercase.
9009 vBRotated
= _mm_add_epi8(vB
, v37
); //Map 'A' ~ 'Z' from '65' ~ '90' to '102' ~ '127'.
9010 vMaskB
= _mm_cmpgt_epi8(vBRotated
, v101
); //Check if anything is greater than '101' which means we have uppercase letters.
9011 vMaskB
= _mm_and_si128(vMaskB
, v32
); //Prepare 32 for the elements with uppercase letters.
9012 vB
= _mm_add_epi8(vB
, vMaskB
); //Add 32 only to the uppercase letters to make them lowercase letters.
9015 vA
= _mm_cmpeq_epi8(vA
, vB
);
9017 //Return if any different.
9018 is_equal
= _mm_movemask_epi8(vA
);
9019 is_equal
= is_equal
& 0xffff;
9020 if(is_equal
!= 0xffff)
9028 while(len_count
> 0)
9033 //Table will return 32 for upper case letters only.
9034 //0 will be returned for all others.
9035 ac
+= upper_to_lower_case_table
[ac
];
9036 bc
+= upper_to_lower_case_table
[bc
];
9038 //Return if a & b are different.
9049 // Use vectorized implementation if it is supported on this platform.
9050 mDNSlocal
void setSameDomainLabelPointer(void)
9052 if(_cpu_capabilities
& kHasSSE4_1
)
9055 SameDomainLabelPointer
= vectorSameDomainLabel
;
9056 LogMsg("setSameDomainLabelPointer: using vector code");
9059 LogMsg("setSameDomainLabelPointer: using scalar code");
9061 #endif // TARGET_OS_OSX
9063 // Original SameDomainLabel() implementation.
9064 mDNSlocal mDNSBool
scalarSameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
9067 const int len
= *a
++;
9069 if (len
> MAX_DOMAIN_LABEL
)
9070 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
9072 if (len
!= *b
++) return(mDNSfalse
);
9073 for (i
=0; i
<len
; i
++)
9077 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
9078 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
9079 if (ac
!= bc
) return(mDNSfalse
);
9084 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
9086 return (*SameDomainLabelPointer
)(a
, b
);
9089 #endif // APPLE_OSX_mDNSResponder
9091 #if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
9092 mDNSexport
void GetRandomUUIDLabel(domainlabel
*label
)
9095 uuid_string_t uuidStr
;
9096 uuid_generate_random(uuid
);
9097 uuid_unparse_lower(uuid
, uuidStr
);
9098 MakeDomainLabelFromLiteralString(label
, uuidStr
);
9101 mDNSexport
void GetRandomUUIDLocalHostname(domainname
*hostname
)
9103 domainlabel uuidLabel
;
9104 GetRandomUUIDLabel(&uuidLabel
);
9106 AppendDomainLabel(hostname
, &uuidLabel
);
9107 AppendLiteralLabelString(hostname
, "local");
9112 #include "../unittests/mdns_macosx_ut.c"