]>
Commit | Line | Data |
---|---|---|
7f0064bd A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
4a95efb2 | 3 | * Copyright (c) 2002-2011 Apple Computer, Inc. All rights reserved. |
c9b9ae52 | 4 | * |
67c8f8a1 A |
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 | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
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 | |
c9b9ae52 | 15 | * limitations under the License. |
263eeeab | 16 | */ |
c9b9ae52 | 17 | |
263eeeab A |
18 | // *************************************************************************** |
19 | // mDNSMacOSX.c: | |
20 | // Supporting routines to run mDNS on a CFRunLoop platform | |
21 | // *************************************************************************** | |
32bb7e43 | 22 | |
263eeeab A |
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 | |
32bb7e43 | 26 | |
263eeeab A |
27 | // For enabling AAAA records over IPv4. Setting this to 0 sends only |
28 | // A records over IPv4 and AAAA over IPv6. Setting this to 1 sends both | |
29 | // AAAA and A records over both IPv4 and IPv6. | |
30 | #define AAAA_OVER_V4 1 | |
32bb7e43 | 31 | |
263eeeab A |
32 | // In Mac OS X 10.4 and earlier, to reduce traffic, we would send and receive using IPv6 only on interfaces that had no routable |
33 | // IPv4 address. Having a routable IPv4 address assigned is a reasonable indicator of being on a large configured network, | |
34 | // which means there's a good chance that most or all the other devices on that network should also have IPv4. | |
35 | // By doing this we lost the ability to talk to true IPv6-only devices on that link, but we cut the packet rate in half. | |
36 | // At that time, reducing the packet rate was more important than v6-only devices on a large configured network, | |
37 | // so were willing to make that sacrifice. | |
38 | // In Mac OS X 10.5, in 2007, two things have changed: | |
39 | // 1. IPv6-only devices are starting to become more common, so we can't ignore them. | |
40 | // 2. Other efficiency improvements in the code mean that crude hacks like this should no longer be necessary. | |
32bb7e43 | 41 | |
263eeeab | 42 | #define USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 0 |
32bb7e43 | 43 | |
263eeeab A |
44 | #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above |
45 | #include "DNSCommon.h" | |
46 | #include "uDNS.h" | |
47 | #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform | |
48 | #include "dns_sd.h" // For mDNSInterface_LocalOnly etc. | |
49 | #include "PlatformCommon.h" | |
50 | #include "uds_daemon.h" | |
32bb7e43 | 51 | |
263eeeab A |
52 | #include <stdio.h> |
53 | #include <stdarg.h> // For va_list support | |
54 | #include <stdlib.h> // For arc4random | |
55 | #include <net/if.h> | |
56 | #include <net/if_types.h> // For IFT_ETHER | |
57 | #include <net/if_dl.h> | |
58 | #include <net/bpf.h> // For BIOCSETIF etc. | |
59 | #include <sys/uio.h> | |
60 | #include <sys/param.h> | |
61 | #include <sys/socket.h> | |
62 | #include <sys/sysctl.h> | |
63 | #include <sys/event.h> | |
64 | #include <fcntl.h> | |
65 | #include <sys/ioctl.h> | |
66 | #include <time.h> // platform support for UTC time | |
67 | #include <arpa/inet.h> // for inet_aton | |
68 | #include <pthread.h> | |
294beb6e | 69 | #include <netdb.h> // for getaddrinfo |
32bb7e43 | 70 | |
263eeeab A |
71 | #include <netinet/in.h> // For IP_RECVTTL |
72 | #ifndef IP_RECVTTL | |
73 | #define IP_RECVTTL 24 // bool; receive reception TTL w/dgram | |
74 | #endif | |
32bb7e43 | 75 | |
263eeeab A |
76 | #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below |
77 | #include <netinet/ip.h> // For IPTOS_LOWDELAY etc. | |
78 | #include <netinet6/in6_var.h> // For IN6_IFF_NOTREADY etc. | |
79 | #include <netinet6/nd6.h> // For ND6_INFINITE_LIFETIME etc. | |
32bb7e43 | 80 | |
263eeeab A |
81 | #if TARGET_OS_EMBEDDED |
82 | #define NO_SECURITYFRAMEWORK 1 | |
83 | #define NO_CFUSERNOTIFICATION 1 | |
84 | #endif | |
32bb7e43 | 85 | |
263eeeab A |
86 | #ifndef NO_SECURITYFRAMEWORK |
87 | #include <Security/SecureTransport.h> | |
88 | #include <Security/Security.h> | |
89 | #endif /* NO_SECURITYFRAMEWORK */ | |
32bb7e43 | 90 | |
263eeeab A |
91 | #include <DebugServices.h> |
92 | #include "dnsinfo.h" | |
32bb7e43 | 93 | |
263eeeab A |
94 | // Code contributed by Dave Heller: |
95 | // Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will | |
96 | // work on Mac OS X 10.1, which does not have the getifaddrs call. | |
97 | #define RUN_ON_PUMA_WITHOUT_IFADDRS 0 | |
98 | #if RUN_ON_PUMA_WITHOUT_IFADDRS | |
99 | #include "mDNSMacOSXPuma.c" | |
100 | #else | |
101 | #include <ifaddrs.h> | |
102 | #endif | |
32bb7e43 | 103 | |
263eeeab A |
104 | #include <IOKit/IOKitLib.h> |
105 | #include <IOKit/IOMessage.h> | |
32bb7e43 | 106 | |
294beb6e | 107 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
108 | // This is currently defined in IOKit/PrivateHeaders/IOKitLibPrivate.h. Till it becomes an Public |
109 | // API, we will have our own declaration | |
110 | void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_queue_t queue); | |
111 | #endif | |
32bb7e43 | 112 | |
263eeeab A |
113 | #if USE_IOPMCOPYACTIVEPMPREFERENCES |
114 | #include <IOKit/ps/IOPowerSources.h> | |
115 | #include <IOKit/ps/IOPowerSourcesPrivate.h> | |
116 | #endif | |
32bb7e43 | 117 | |
263eeeab A |
118 | #include <mach/mach_error.h> |
119 | #include <mach/mach_port.h> | |
120 | #include <mach/mach_time.h> | |
121 | #include "helper.h" | |
294beb6e | 122 | #include "P2PPacketFilter.h" |
32bb7e43 | 123 | |
263eeeab | 124 | #include <asl.h> |
32bb7e43 | 125 | |
294beb6e A |
126 | #include <SystemConfiguration/SCDynamicStorePrivate.h> |
127 | ||
263eeeab A |
128 | #if APPLE_OSX_mDNSResponder |
129 | #include <DeviceToDeviceManager/DeviceToDeviceManager.h> | |
130 | #include <AWACS.h> | |
32bb7e43 | 131 | |
263eeeab A |
132 | #if ! NO_D2D |
133 | D2DStatus D2DInitialize(CFRunLoopRef runLoop, D2DServiceCallback serviceCallback, void* userData) __attribute__((weak_import)); | |
134 | D2DStatus D2DTerminate() __attribute__((weak_import)); | |
135 | D2DStatus D2DStartAdvertisingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import)); | |
136 | D2DStatus D2DStopAdvertisingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import)); | |
137 | D2DStatus D2DStartBrowsingForKey(const Byte *key, const size_t keySize) __attribute__((weak_import)); | |
138 | D2DStatus D2DStopBrowsingForKey(const Byte *key, const size_t keySize) __attribute__((weak_import)); | |
139 | void D2DStartResolvingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import)); | |
140 | void D2DStopResolvingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import)); | |
141 | D2DStatus D2DRetain(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import)); | |
142 | D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import)); | |
32bb7e43 | 143 | |
263eeeab | 144 | #define CHECK_D2D_FUNCTION(X) if (X) |
32bb7e43 | 145 | |
263eeeab | 146 | #endif // ! NO_D2D |
32bb7e43 | 147 | |
263eeeab A |
148 | #else |
149 | #define NO_D2D 1 | |
150 | #define NO_AWACS 1 | |
151 | #endif // APPLE_OSX_mDNSResponder | |
32bb7e43 | 152 | |
263eeeab | 153 | #define kInterfaceSpecificOption "interface=" |
32bb7e43 | 154 | |
263eeeab A |
155 | // *************************************************************************** |
156 | // Globals | |
32bb7e43 | 157 | |
263eeeab A |
158 | #if COMPILER_LIKES_PRAGMA_MARK |
159 | #pragma mark - Globals | |
160 | #endif | |
32bb7e43 | 161 | |
263eeeab A |
162 | // By default we don't offer sleep proxy service |
163 | // If OfferSleepProxyService is set non-zero (typically via command-line switch), | |
164 | // then we'll offer sleep proxy service on desktop Macs that are set to never sleep. | |
165 | // We currently do not offer sleep proxy service on laptops, or on machines that are set to go to sleep. | |
166 | mDNSexport int OfferSleepProxyService = 0; | |
167 | mDNSexport int DisableSleepProxyClient = 0; | |
294beb6e | 168 | mDNSexport int UseInternalSleepProxy = 1; // Set to non-zero to use internal (in-NIC) Sleep Proxy |
32bb7e43 | 169 | |
263eeeab A |
170 | // We disable inbound relay connection if this value is set to true (typically via command-line switch). |
171 | mDNSBool DisableInboundRelayConnection = mDNSfalse; | |
294beb6e | 172 | mDNSexport int OSXVers, iOSVers; |
263eeeab | 173 | mDNSexport int KQueueFD; |
32bb7e43 | 174 | |
263eeeab A |
175 | #ifndef NO_SECURITYFRAMEWORK |
176 | static CFArrayRef ServerCerts; | |
177 | OSStatus SSLSetAllowAnonymousCiphers(SSLContextRef context, Boolean enable); | |
178 | #endif /* NO_SECURITYFRAMEWORK */ | |
32bb7e43 | 179 | |
263eeeab A |
180 | static CFStringRef NetworkChangedKey_IPv4; |
181 | static CFStringRef NetworkChangedKey_IPv6; | |
182 | static CFStringRef NetworkChangedKey_Hostnames; | |
183 | static CFStringRef NetworkChangedKey_Computername; | |
184 | static CFStringRef NetworkChangedKey_DNS; | |
185 | static CFStringRef NetworkChangedKey_DynamicDNS = CFSTR("Setup:/Network/DynamicDNS"); | |
186 | static CFStringRef NetworkChangedKey_BackToMyMac = CFSTR("Setup:/Network/BackToMyMac"); | |
187 | static CFStringRef NetworkChangedKey_BTMMConnectivity = CFSTR("State:/Network/Connectivity"); | |
188 | static CFStringRef NetworkChangedKey_PowerSettings = CFSTR("State:/IOKit/PowerManagement/CurrentSettings"); | |
32bb7e43 | 189 | |
263eeeab A |
190 | static char HINFO_HWstring_buffer[32]; |
191 | static char *HINFO_HWstring = "Device"; | |
192 | static int HINFO_HWstring_prefixlen = 6; | |
32bb7e43 | 193 | |
263eeeab | 194 | mDNSexport int WatchDogReportingThreshold = 250; |
32bb7e43 | 195 | |
294beb6e | 196 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
197 | dispatch_queue_t SSLqueue; |
198 | #endif | |
32bb7e43 | 199 | |
263eeeab A |
200 | #if APPLE_OSX_mDNSResponder |
201 | static mDNSu8 SPMetricPortability = 99; | |
202 | static mDNSu8 SPMetricMarginalPower = 99; | |
203 | static mDNSu8 SPMetricTotalPower = 99; | |
204 | mDNSexport domainname ActiveDirectoryPrimaryDomain; | |
205 | mDNSexport int ActiveDirectoryPrimaryDomainLabelCount; | |
206 | mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer; | |
207 | #endif // APPLE_OSX_mDNSResponder | |
32bb7e43 | 208 | |
294beb6e A |
209 | // Used by AutoTunnel |
210 | const char btmmprefix[] = "btmmdns:"; | |
211 | const char dnsprefix[] = "dns:"; | |
212 | ||
263eeeab A |
213 | // *************************************************************************** |
214 | #if COMPILER_LIKES_PRAGMA_MARK | |
215 | #pragma mark - | |
216 | #pragma mark - D2D Support | |
217 | #endif | |
32bb7e43 | 218 | |
263eeeab | 219 | #if ! NO_D2D |
32bb7e43 | 220 | |
263eeeab A |
221 | // Name compression items for fake packet version number 1 |
222 | static const mDNSu8 compression_packet_v1 = 0x01; | |
32bb7e43 | 223 | |
263eeeab A |
224 | static DNSMessage compression_base_msg = { { {{0}}, {{0}}, 2, 0, 0, 0 }, "\x04_tcp\x05local\x00\x00\x0C\x00\x01\x04_udp\xC0\x11\x00\x0C\x00\x01" }; |
225 | static mDNSu8 *const compression_limit = (mDNSu8 *) &compression_base_msg + sizeof(DNSMessage); | |
226 | static mDNSu8 *const compression_lhs = (mDNSu8 *const) compression_base_msg.data + 27; | |
32bb7e43 | 227 | |
263eeeab A |
228 | mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result); |
229 | mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len); | |
32bb7e43 | 230 | |
263eeeab | 231 | static ARListElem *D2DRecords = NULL; // List of locally-generated PTR records to records found via D2D |
32bb7e43 | 232 | |
263eeeab A |
233 | typedef struct D2DBrowseListElem |
234 | { | |
235 | struct D2DBrowseListElem *next; | |
236 | domainname name; | |
237 | mDNSu16 type; | |
238 | unsigned int refCount; | |
239 | } D2DBrowseListElem; | |
32bb7e43 | 240 | |
263eeeab | 241 | D2DBrowseListElem* D2DBrowseList = NULL; |
32bb7e43 | 242 | |
263eeeab A |
243 | mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val) |
244 | { | |
245 | ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF); | |
246 | ptr[1] = (mDNSu8)((val ) & 0xFF); | |
247 | return ptr + sizeof(mDNSu16); | |
248 | } | |
32bb7e43 | 249 | |
263eeeab A |
250 | mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val) |
251 | { | |
252 | ptr[0] = (mDNSu8)((val >> 24) & 0xFF); | |
253 | ptr[1] = (mDNSu8)((val >> 16) & 0xFF); | |
254 | ptr[2] = (mDNSu8)((val >> 8) & 0xFF); | |
255 | ptr[3] = (mDNSu8)((val ) & 0xFF); | |
256 | return ptr + sizeof(mDNSu32); | |
257 | } | |
32bb7e43 | 258 | |
263eeeab A |
259 | mDNSlocal void DomainnameToLower(const domainname * const in, domainname * const out) |
260 | { | |
261 | const mDNSu8 * const start = (const mDNSu8 * const)in; | |
262 | mDNSu8 *ptr = (mDNSu8*)start; | |
263 | while(*ptr) | |
264 | { | |
265 | mDNSu8 c = *ptr; | |
266 | out->c[ptr-start] = *ptr; | |
267 | ptr++; | |
268 | for (;c;c--,ptr++) out->c[ptr-start] = mDNSIsUpperCase(*ptr) ? (*ptr - 'A' + 'a') : *ptr; | |
269 | } | |
270 | out->c[ptr-start] = *ptr; | |
271 | } | |
32bb7e43 | 272 | |
263eeeab A |
273 | mDNSlocal mStatus DNSNameCompressionParseBytes(mDNS *const m, const mDNSu8 *const lhs, const mDNSu16 lhs_len, const mDNSu8 *const rhs, const mDNSu16 rhs_len, AuthRecord *rr) |
274 | { | |
275 | if (mDNS_LoggingEnabled) | |
276 | { | |
277 | LogInfo("%s", __func__); | |
278 | LogInfo(" Static Bytes: "); | |
279 | PrintHex((mDNSu8*)&compression_base_msg, compression_lhs - (mDNSu8*)&compression_base_msg); | |
280 | } | |
281 | ||
282 | mDNSu8 *ptr = compression_lhs; // pointer to the end of our fake packet | |
32bb7e43 | 283 | |
263eeeab A |
284 | // Check to make sure we're not going to go past the end of the DNSMessage data |
285 | // 7 = 2 for CLASS (-1 for our version) + 4 for TTL + 2 for RDLENGTH | |
286 | if (ptr + lhs_len - 7 + rhs_len >= compression_limit) return mStatus_NoMemoryErr; | |
287 | ||
288 | // Copy the LHS onto our fake wire packet | |
289 | mDNSPlatformMemCopy(ptr, lhs, lhs_len); | |
290 | ptr += lhs_len - 1; | |
291 | ||
292 | // Check the 'fake packet' version number, to ensure that we know how to decompress this data | |
293 | if (*ptr != compression_packet_v1) return mStatus_Incompatible; | |
294 | ||
295 | // two bytes of CLASS | |
296 | ptr = putVal16(ptr, kDNSClass_IN | kDNSClass_UniqueRRSet); | |
297 | ||
298 | // four bytes of TTL | |
299 | ptr = putVal32(ptr, 120); | |
300 | ||
301 | // Copy the RHS length into the RDLENGTH of our fake wire packet | |
302 | ptr = putVal16(ptr, rhs_len); | |
303 | ||
304 | // Copy the RHS onto our fake wire packet | |
305 | mDNSPlatformMemCopy(ptr, rhs, rhs_len); | |
306 | ptr += rhs_len; | |
307 | ||
308 | if (mDNS_LoggingEnabled) | |
309 | { | |
310 | LogInfo(" Our Bytes %d: ", __LINE__); | |
311 | PrintHex(compression_lhs, ptr - compression_lhs); | |
312 | } | |
32bb7e43 | 313 | |
263eeeab A |
314 | ptr = (mDNSu8 *) GetLargeResourceRecord(m, &compression_base_msg, compression_lhs, ptr, mDNSInterface_Any, kDNSRecordTypePacketAns, &m->rec); |
315 | if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) | |
316 | { LogMsg("DNSNameCompressionParseBytes: failed to get large RR"); m->rec.r.resrec.RecordType = 0; return mStatus_UnknownErr; } | |
317 | else LogInfo("DNSNameCompressionParseBytes: got rr: %s", CRDisplayString(m, &m->rec.r)); | |
32bb7e43 | 318 | |
294beb6e | 319 | mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, AuthRecordP2P, FreeD2DARElemCallback, NULL); |
263eeeab A |
320 | AssignDomainName(&rr->namestorage, &m->rec.namestorage); |
321 | rr->resrec.rdlength = m->rec.r.resrec.rdlength; | |
322 | rr->resrec.rdata->MaxRDLength = m->rec.r.resrec.rdlength; | |
323 | mDNSPlatformMemCopy(rr->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, m->rec.r.resrec.rdlength); | |
324 | rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); | |
325 | SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us | |
326 | ||
327 | m->rec.r.resrec.RecordType = 0; // Mark m->rec as no longer in use | |
32bb7e43 | 328 | |
263eeeab A |
329 | return mStatus_NoError; |
330 | } | |
32bb7e43 | 331 | |
263eeeab A |
332 | mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainname const *typeDomain, DNS_TypeValues qtype) |
333 | { | |
334 | mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain); | |
335 | if (!ptr) return ptr; | |
336 | *ptr = (qtype >> 8) & 0xff; | |
337 | ptr += 1; | |
338 | *ptr = qtype & 0xff; | |
339 | ptr += 1; | |
340 | *ptr = compression_packet_v1; | |
341 | return ptr + 1; | |
342 | } | |
32bb7e43 | 343 | |
263eeeab A |
344 | mDNSlocal mDNSu8 * DNSNameCompressionBuildRHS(mDNSu8 *start, const ResourceRecord *const resourceRecord) |
345 | { | |
346 | return putRData(&compression_base_msg, start, compression_limit, resourceRecord); | |
347 | } | |
32bb7e43 | 348 | |
263eeeab A |
349 | mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len) |
350 | { | |
351 | mDNSu8 *end = data + len; | |
352 | char buffer[49] = {0}; | |
353 | char *bufend = buffer + sizeof(buffer); | |
354 | while(data<end) | |
355 | { | |
356 | char *ptr = buffer; | |
357 | for(; data < end && ptr < bufend-1; ptr+=3,data++) | |
358 | mDNS_snprintf(ptr, bufend - ptr, "%02X ", *data); | |
359 | LogInfo(" %s", buffer); | |
360 | } | |
361 | } | |
32bb7e43 | 362 | |
263eeeab A |
363 | mDNSlocal void PrintHelper(const char *const tag, mDNSu8 *lhs, mDNSu16 lhs_len, mDNSu8 *rhs, mDNSu16 rhs_len) |
364 | { | |
365 | if (!mDNS_LoggingEnabled) return; | |
366 | ||
367 | LogInfo("%s:", tag); | |
368 | LogInfo(" LHS: "); | |
369 | PrintHex(lhs, lhs_len); | |
370 | ||
371 | if (!rhs) return; | |
372 | ||
373 | LogInfo(" RHS: "); | |
374 | PrintHex(rhs, rhs_len); | |
375 | } | |
32bb7e43 | 376 | |
263eeeab A |
377 | mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) |
378 | { | |
379 | (void)m; // unused | |
380 | if (result == mStatus_MemFree) | |
381 | { | |
382 | ARListElem **ptr = &D2DRecords; | |
383 | ARListElem *tmp; | |
384 | while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next; | |
385 | if (!*ptr) { LogMsg("FreeD2DARElemCallback: Could not find in D2DRecords: %s", ARDisplayString(m, rr)); return; } | |
386 | LogInfo("FreeD2DARElemCallback: Found in D2DRecords: %s", ARDisplayString(m, rr)); | |
387 | tmp = *ptr; | |
388 | *ptr = (*ptr)->next; | |
389 | // Just because we stoppped browsing, doesn't mean we should tear down the PAN connection. | |
390 | mDNSPlatformMemFree(tmp); | |
391 | } | |
392 | } | |
32bb7e43 | 393 | |
263eeeab A |
394 | mDNSlocal void xD2DClearCache(mDNS *const m, const domainname *regType) |
395 | { | |
396 | ARListElem *ptr = D2DRecords; | |
397 | for ( ; ptr ; ptr = ptr->next) | |
398 | { | |
399 | if (SameDomainName(&ptr->ar.namestorage, regType)) | |
400 | { | |
401 | char buffer[MAX_ESCAPED_DOMAIN_NAME]; | |
402 | mDNS_Deregister(m, &ptr->ar); | |
403 | ConvertDomainNameToCString(regType, buffer); | |
404 | LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", buffer); | |
405 | } | |
406 | } | |
407 | } | |
32bb7e43 | 408 | |
263eeeab A |
409 | mDNSlocal D2DBrowseListElem ** D2DFindInBrowseList(const domainname *const name, mDNSu16 type) |
410 | { | |
411 | D2DBrowseListElem **ptr = &D2DBrowseList; | |
32bb7e43 | 412 | |
263eeeab A |
413 | for ( ; *ptr; ptr = &(*ptr)->next) |
414 | if ((*ptr)->type == type && SameDomainName(&(*ptr)->name, name)) | |
415 | break; | |
416 | ||
417 | return ptr; | |
418 | } | |
32bb7e43 | 419 | |
263eeeab A |
420 | mDNSlocal unsigned int D2DBrowseListRefCount(const domainname *const name, mDNSu16 type) |
421 | { | |
422 | D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); | |
423 | return *ptr ? (*ptr)->refCount : 0; | |
424 | } | |
32bb7e43 | 425 | |
263eeeab A |
426 | mDNSlocal void D2DBrowseListRetain(const domainname *const name, mDNSu16 type) |
427 | { | |
428 | D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); | |
429 | ||
430 | if (!*ptr) | |
431 | { | |
432 | *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); | |
433 | mDNSPlatformMemZero(*ptr, sizeof(**ptr)); | |
434 | (*ptr)->type = type; | |
435 | AssignDomainName(&(*ptr)->name, name); | |
436 | } | |
437 | (*ptr)->refCount += 1; | |
438 | ||
439 | LogInfo("D2DBrowseListRetain: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); | |
440 | } | |
32bb7e43 | 441 | |
263eeeab A |
442 | mDNSlocal void D2DBrowseListRelease(const domainname *const name, mDNSu16 type) |
443 | { | |
444 | D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); | |
445 | ||
446 | if (!*ptr) { LogMsg("D2DBrowseListRelease: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return; } | |
447 | ||
448 | (*ptr)->refCount -= 1; | |
449 | ||
450 | LogInfo("D2DBrowseListRelease: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); | |
451 | ||
452 | if (!(*ptr)->refCount) | |
453 | { | |
454 | D2DBrowseListElem *tmp = *ptr; | |
455 | *ptr = (*ptr)->next; | |
456 | mDNSPlatformMemFree(tmp); | |
457 | } | |
458 | } | |
32bb7e43 | 459 | |
263eeeab A |
460 | mDNSlocal mStatus xD2DParse(mDNS *const m, const mDNSu8 * const lhs, const mDNSu16 lhs_len, const mDNSu8 * const rhs, const mDNSu16 rhs_len, AuthRecord *rr) |
461 | { | |
462 | if (*(lhs + (lhs_len - 1)) == compression_packet_v1) | |
463 | return DNSNameCompressionParseBytes(m, lhs, lhs_len, rhs, rhs_len, rr); | |
464 | else | |
465 | return mStatus_Incompatible; | |
466 | } | |
32bb7e43 | 467 | |
263eeeab A |
468 | mDNSlocal void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) |
469 | { | |
470 | (void)transportType; // We don't care about this, yet. | |
471 | (void)instanceHandle; // We don't care about this, yet. | |
472 | ||
473 | if (result == kD2DSuccess) | |
474 | { | |
475 | if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DAddToCache: NULL Byte * passed in or length == 0"); return; } | |
476 | ||
477 | mStatus err; | |
478 | ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(ARListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData))); | |
32bb7e43 | 479 | |
263eeeab | 480 | if (ptr == NULL) { LogMsg("xD2DAddToCache: memory allocation failure"); return; } |
32bb7e43 | 481 | |
263eeeab A |
482 | err = xD2DParse(m, (const mDNSu8 * const)key, (const mDNSu16)keySize, (const mDNSu8 * const)value, (const mDNSu16)valueSize, &ptr->ar); |
483 | if (err) | |
484 | { | |
485 | LogMsg("xD2DAddToCache: xD2DParse returned error: %d", err); | |
486 | PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); | |
487 | mDNSPlatformMemFree(ptr); | |
488 | return; | |
489 | } | |
263eeeab A |
490 | err = mDNS_Register(m, &ptr->ar); |
491 | if (err) | |
492 | { | |
493 | LogMsg("xD2DAddToCache: mDNS_Register returned error %d for %s", err, ARDisplayString(m, &ptr->ar)); | |
494 | mDNSPlatformMemFree(ptr); | |
495 | return; | |
496 | } | |
32bb7e43 | 497 | |
263eeeab A |
498 | LogInfo("xD2DAddToCache: mDNS_Register succeeded for %s", ARDisplayString(m, &ptr->ar)); |
499 | ptr->next = D2DRecords; | |
500 | D2DRecords = ptr; | |
501 | } | |
502 | else | |
503 | LogMsg("xD2DAddToCache: Unexpected result %d", result); | |
504 | } | |
32bb7e43 | 505 | |
263eeeab A |
506 | mDNSlocal ARListElem * xD2DFindInList(mDNS *const m, const Byte *const key, const size_t keySize, const Byte *const value, const size_t valueSize) |
507 | { | |
508 | ARListElem *ptr = D2DRecords; | |
509 | ARListElem *arptr; | |
510 | ||
511 | if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DFindInList: NULL Byte * passed in or length == 0"); return NULL; } | |
32bb7e43 | 512 | |
263eeeab A |
513 | arptr = mDNSPlatformMemAllocate(sizeof(ARListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData))); |
514 | if (arptr == NULL) { LogMsg("xD2DFindInList: memory allocation failure"); return NULL; } | |
32bb7e43 | 515 | |
263eeeab A |
516 | if (xD2DParse(m, (const mDNSu8 *const)key, (const mDNSu16)keySize, (const mDNSu8 *const)value, (const mDNSu16)valueSize, &arptr->ar) != mStatus_NoError) |
517 | { | |
518 | LogMsg("xD2DFindInList: xD2DParse failed for key: %p (%u) value: %p (%u)", key, keySize, value, valueSize); | |
519 | mDNSPlatformMemFree(arptr); | |
520 | return NULL; | |
521 | } | |
522 | ||
523 | while (ptr) | |
524 | { | |
525 | if (IdenticalResourceRecord(&arptr->ar.resrec, &ptr->ar.resrec)) break; | |
526 | ptr = ptr->next; | |
527 | } | |
528 | ||
529 | if (!ptr) LogMsg("xD2DFindInList: Could not find in D2DRecords: %s", ARDisplayString(m, &arptr->ar)); | |
530 | mDNSPlatformMemFree(arptr); | |
531 | return ptr; | |
532 | } | |
32bb7e43 | 533 | |
263eeeab A |
534 | mDNSlocal void xD2DRemoveFromCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) |
535 | { | |
536 | (void)transportType; // We don't care about this, yet. | |
537 | (void)instanceHandle; // We don't care about this, yet. | |
538 | ||
539 | if (result == kD2DSuccess) | |
540 | { | |
541 | ARListElem *ptr = xD2DFindInList(m, key, keySize, value, valueSize); | |
542 | if (ptr) | |
543 | { | |
544 | LogInfo("xD2DRemoveFromCache: Remove from cache: %s", ARDisplayString(m, &ptr->ar)); | |
545 | mDNS_Deregister(m, &ptr->ar); | |
546 | } | |
547 | } | |
548 | else | |
549 | LogMsg("xD2DRemoveFromCache: Unexpected result %d", result); | |
550 | } | |
32bb7e43 | 551 | |
263eeeab A |
552 | mDNSlocal void xD2DServiceResolved(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) |
553 | { | |
554 | (void)m; | |
555 | (void)key; | |
556 | (void)keySize; | |
557 | (void)value; | |
558 | (void)valueSize; | |
559 | ||
560 | if (result == kD2DSuccess) | |
561 | { | |
562 | LogInfo("xD2DServiceResolved: Starting up PAN connection for %p", instanceHandle); | |
563 | CHECK_D2D_FUNCTION(D2DRetain) D2DRetain(instanceHandle, transportType); | |
564 | } | |
565 | else LogMsg("xD2DServiceResolved: Unexpected result %d", result); | |
566 | } | |
32bb7e43 | 567 | |
263eeeab A |
568 | mDNSlocal void xD2DRetainHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) |
569 | { | |
570 | (void)m; | |
571 | (void)instanceHandle; | |
572 | (void)transportType; | |
573 | (void)key; | |
574 | (void)keySize; | |
575 | (void)value; | |
576 | (void)valueSize; | |
577 | ||
578 | if (result == kD2DSuccess) LogInfo("xD2DRetainHappened: Opening up PAN connection for %p", instanceHandle); | |
579 | else LogMsg("xD2DRetainHappened: Unexpected result %d", result); | |
580 | } | |
32bb7e43 | 581 | |
263eeeab A |
582 | mDNSlocal void xD2DReleaseHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) |
583 | { | |
584 | (void)m; | |
585 | (void)instanceHandle; | |
586 | (void)transportType; | |
587 | (void)key; | |
588 | (void)keySize; | |
589 | (void)value; | |
590 | (void)valueSize; | |
591 | ||
592 | if (result == kD2DSuccess) LogInfo("xD2DReleaseHappened: Closing PAN connection for %p", instanceHandle); | |
593 | else LogMsg("xD2DReleaseHappened: Unexpected result %d", result); | |
594 | } | |
32bb7e43 | 595 | |
263eeeab A |
596 | mDNSlocal void xD2DServiceCallback(D2DServiceEvent event, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize, void *userData) |
597 | { | |
598 | mDNS *m = (mDNS *) userData; | |
599 | const char *eventString = "unknown"; | |
600 | ||
601 | KQueueLock(m); | |
602 | ||
603 | if (keySize > 0xFFFF) LogMsg("xD2DServiceCallback: keySize too large: %u", keySize); | |
604 | if (valueSize > 0xFFFF) LogMsg("xD2DServiceCallback: valueSize too large: %u", valueSize); | |
605 | ||
606 | switch (event) | |
607 | { | |
608 | case D2DServiceFound: | |
609 | eventString = "D2DServiceFound"; | |
610 | break; | |
611 | case D2DServiceLost: | |
612 | eventString = "D2DServiceLost"; | |
613 | break; | |
614 | case D2DServiceResolved: | |
615 | eventString = "D2DServiceResolved"; | |
616 | break; | |
617 | case D2DServiceRetained: | |
618 | eventString = "D2DServiceRetained"; | |
619 | break; | |
620 | case D2DServiceReleased: | |
621 | eventString = "D2DServiceReleased"; | |
622 | break; | |
623 | default: | |
624 | break; | |
625 | } | |
626 | ||
627 | LogInfo("xD2DServiceCallback: event=%s result=%d instanceHandle=%p transportType=%d LHS=%p (%u) RHS=%p (%u) userData=%p", eventString, result, instanceHandle, transportType, key, keySize, value, valueSize, userData); | |
628 | PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); | |
629 | ||
630 | switch (event) | |
631 | { | |
632 | case D2DServiceFound: | |
633 | xD2DAddToCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize); | |
634 | break; | |
635 | case D2DServiceLost: | |
636 | xD2DRemoveFromCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize); | |
637 | break; | |
638 | case D2DServiceResolved: | |
639 | xD2DServiceResolved(m, result, instanceHandle, transportType, key, keySize, value, valueSize); | |
640 | break; | |
641 | case D2DServiceRetained: | |
642 | xD2DRetainHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize); | |
643 | break; | |
644 | case D2DServiceReleased: | |
645 | xD2DReleaseHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize); | |
646 | break; | |
647 | default: | |
648 | break; | |
649 | } | |
650 | ||
651 | // Need to tickle the main kqueue loop to potentially handle records we removed or added. | |
652 | KQueueUnlock(m, "xD2DServiceCallback"); | |
653 | } | |
32bb7e43 | 654 | |
263eeeab A |
655 | mDNSexport void external_start_browsing_for_service(mDNS *const m, const domainname *const typeDomain, DNS_TypeValues qtype) |
656 | { | |
657 | (void)m; | |
658 | domainname lower; | |
659 | ||
660 | if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA) | |
661 | { | |
662 | LogInfo("external_start_browsing_for_service: ignoring address record"); | |
663 | return; | |
664 | } | |
665 | ||
666 | DomainnameToLower(typeDomain, &lower); | |
667 | ||
668 | if (!D2DBrowseListRefCount(&lower, qtype)) | |
669 | { | |
670 | LogInfo("external_start_browsing_for_service: Starting browse for: %##s %s", lower.c, DNSTypeName(qtype)); | |
671 | mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); | |
672 | PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); | |
673 | CHECK_D2D_FUNCTION(D2DStartBrowsingForKey) D2DStartBrowsingForKey(compression_lhs, end - compression_lhs); | |
674 | } | |
675 | D2DBrowseListRetain(&lower, qtype); | |
676 | } | |
32bb7e43 | 677 | |
263eeeab A |
678 | mDNSexport void external_stop_browsing_for_service(mDNS *const m, const domainname *const typeDomain, DNS_TypeValues qtype) |
679 | { | |
680 | domainname lower; | |
681 | ||
682 | if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA) | |
683 | { | |
684 | LogInfo("external_stop_browsing_for_service: ignoring address record"); | |
685 | return; | |
686 | } | |
687 | ||
688 | DomainnameToLower(typeDomain, &lower); | |
689 | ||
690 | D2DBrowseListRelease(&lower, qtype); | |
691 | if (!D2DBrowseListRefCount(&lower, qtype)) | |
692 | { | |
693 | LogInfo("external_stop_browsing_for_service: Stopping browse for: %##s %s", lower.c, DNSTypeName(qtype)); | |
694 | mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); | |
695 | PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); | |
696 | CHECK_D2D_FUNCTION(D2DStopBrowsingForKey) D2DStopBrowsingForKey(compression_lhs, end - compression_lhs); | |
697 | xD2DClearCache(m, &lower); | |
698 | } | |
699 | } | |
32bb7e43 | 700 | |
263eeeab A |
701 | mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord) |
702 | { | |
703 | domainname lower; | |
704 | mDNSu8 *rhs = NULL; | |
705 | mDNSu8 *end = NULL; | |
706 | DomainnameToLower(resourceRecord->name, &lower); | |
707 | ||
708 | LogInfo("external_start_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord)); | |
294beb6e A |
709 | // For SRV records, update packet filter if p2p interface already exists, otherwise, |
710 | // if will be updated when we get the KEV_DL_IF_ATTACHED event for the interface. | |
711 | // Bonjour filter rules are removed when p2p interface KEV_DL_IF_DETACHED event is received. | |
712 | if (resourceRecord->rrtype == kDNSType_SRV) | |
713 | mDNSInitPacketFilter(); | |
714 | ||
263eeeab A |
715 | if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA) |
716 | { | |
717 | LogInfo("external_start_advertising_service: ignoring address record"); | |
718 | return; | |
719 | } | |
720 | rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype); | |
721 | end = DNSNameCompressionBuildRHS(rhs, resourceRecord); | |
722 | PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); | |
723 | CHECK_D2D_FUNCTION(D2DStartAdvertisingPair) D2DStartAdvertisingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs); | |
724 | } | |
32bb7e43 | 725 | |
263eeeab A |
726 | mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord) |
727 | { | |
728 | domainname lower; | |
729 | mDNSu8 *rhs = NULL; | |
730 | mDNSu8 *end = NULL; | |
731 | DomainnameToLower(resourceRecord->name, &lower); | |
732 | ||
733 | LogInfo("external_stop_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord)); | |
734 | if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA) | |
735 | { | |
736 | LogInfo("external_stop_advertising_service: ignoring address record"); | |
737 | return; | |
738 | } | |
739 | rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype); | |
740 | end = DNSNameCompressionBuildRHS(rhs, resourceRecord); | |
741 | PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); | |
742 | CHECK_D2D_FUNCTION(D2DStopAdvertisingPair) D2DStopAdvertisingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs); | |
743 | } | |
32bb7e43 | 744 | |
263eeeab A |
745 | mDNSexport void external_start_resolving_service(const domainname *const fqdn) |
746 | { | |
747 | domainname lower; | |
748 | mDNSu8 *rhs = NULL; | |
749 | mDNSu8 *end = NULL; | |
750 | DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); | |
751 | ||
752 | LogInfo("external_start_resolving_service: %##s", fqdn->c); | |
753 | rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR); | |
754 | end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn); | |
755 | PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); | |
756 | CHECK_D2D_FUNCTION(D2DStartResolvingPair) D2DStartResolvingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs); | |
757 | } | |
32bb7e43 | 758 | |
263eeeab A |
759 | mDNSexport void external_stop_resolving_service(const domainname *const fqdn) |
760 | { | |
761 | domainname lower; | |
762 | mDNSu8 *rhs = NULL; | |
763 | mDNSu8 *end = NULL; | |
764 | DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); | |
765 | ||
766 | LogInfo("external_stop_resolving_service: %##s", fqdn->c); | |
767 | rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR); | |
768 | end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn); | |
769 | PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); | |
770 | CHECK_D2D_FUNCTION(D2DStopResolvingPair) D2DStopResolvingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs); | |
771 | } | |
32bb7e43 | 772 | |
263eeeab | 773 | #elif APPLE_OSX_mDNSResponder |
32bb7e43 | 774 | |
263eeeab A |
775 | mDNSexport void external_start_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype) { (void)m; (void)type; (void)qtype; } |
776 | mDNSexport void external_stop_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype) { (void)m; (void)type; (void)qtype; } | |
777 | mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord) { (void)resourceRecord; } | |
778 | mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord) { (void)resourceRecord; } | |
779 | mDNSexport void external_start_resolving_service(const domainname *const fqdn) { (void)fqdn; } | |
780 | mDNSexport void external_stop_resolving_service(const domainname *const fqdn) { (void)fqdn; } | |
32bb7e43 | 781 | |
263eeeab | 782 | #endif // ! NO_D2D |
32bb7e43 | 783 | |
263eeeab A |
784 | // *************************************************************************** |
785 | // Functions | |
32bb7e43 | 786 | |
263eeeab A |
787 | #if COMPILER_LIKES_PRAGMA_MARK |
788 | #pragma mark - | |
789 | #pragma mark - Utility Functions | |
790 | #endif | |
32bb7e43 | 791 | |
263eeeab A |
792 | // We only attempt to send and receive multicast packets on interfaces that are |
793 | // (a) flagged as multicast-capable | |
794 | // (b) *not* flagged as point-to-point (e.g. modem) | |
795 | // Typically point-to-point interfaces are modems (including mobile-phone pseudo-modems), and we don't want | |
796 | // to run up the user's bill sending multicast traffic over a link where there's only a single device at the | |
797 | // other end, and that device (e.g. a modem bank) is probably not answering Multicast DNS queries anyway. | |
798 | #define MulticastInterface(i) (((i)->ifa_flags & IFF_MULTICAST) && !((i)->ifa_flags & IFF_POINTOPOINT)) | |
32bb7e43 | 799 | |
263eeeab A |
800 | mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg) // Both strings are UTF-8 text |
801 | { | |
802 | static int notifyCount = 0; | |
803 | if (notifyCount) return; | |
32bb7e43 | 804 | |
263eeeab A |
805 | // If we display our alert early in the boot process, then it vanishes once the desktop appears. |
806 | // To avoid this, we don't try to display alerts in the first three minutes after boot. | |
807 | if ((mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return; | |
32bb7e43 | 808 | |
263eeeab A |
809 | // Unless ForceAlerts is defined, we only show these bug report alerts on machines that have a 17.x.x.x address |
810 | #if !ForceAlerts | |
811 | { | |
812 | // Determine if we're at Apple (17.*.*.*) | |
263eeeab A |
813 | NetworkInterfaceInfoOSX *i; |
814 | for (i = mDNSStorage.p->InterfaceList; i; i = i->next) | |
815 | if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && i->ifinfo.ip.ip.v4.b[0] == 17) | |
816 | break; | |
817 | if (!i) return; // If not at Apple, don't show the alert | |
818 | } | |
819 | #endif | |
32bb7e43 | 820 | |
263eeeab A |
821 | LogMsg("%s", title); |
822 | LogMsg("%s", msg); | |
823 | // Display a notification to the user | |
824 | notifyCount++; | |
32bb7e43 | 825 | |
263eeeab A |
826 | #ifndef NO_CFUSERNOTIFICATION |
827 | mDNSNotify(title, msg); | |
828 | #endif /* NO_CFUSERNOTIFICATION */ | |
829 | } | |
32bb7e43 | 830 | |
263eeeab A |
831 | mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh) |
832 | { | |
833 | static struct ifaddrs *ifa = NULL; | |
32bb7e43 | 834 | |
263eeeab A |
835 | if (refresh && ifa) |
836 | { | |
837 | freeifaddrs(ifa); | |
838 | ifa = NULL; | |
839 | } | |
32bb7e43 | 840 | |
263eeeab | 841 | if (ifa == NULL) getifaddrs(&ifa); |
32bb7e43 | 842 | |
263eeeab A |
843 | return ifa; |
844 | } | |
32bb7e43 | 845 | |
263eeeab A |
846 | // To match *either* a v4 or v6 instance of this interface name, pass AF_UNSPEC for type |
847 | mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const char *ifname, int type) | |
848 | { | |
849 | NetworkInterfaceInfoOSX *i; | |
850 | for (i = m->p->InterfaceList; i; i = i->next) | |
851 | if (i->Exists && !strcmp(i->ifinfo.ifname, ifname) && | |
852 | ((type == AF_UNSPEC ) || | |
853 | (type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) || | |
854 | (type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6))) return(i); | |
855 | return(NULL); | |
856 | } | |
32bb7e43 | 857 | |
263eeeab A |
858 | mDNSlocal int myIfIndexToName(u_short ifindex, char *name) |
859 | { | |
860 | struct ifaddrs *ifa; | |
861 | for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) | |
862 | if (ifa->ifa_addr->sa_family == AF_LINK) | |
863 | if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == ifindex) | |
864 | { strlcpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; } | |
865 | return -1; | |
866 | } | |
32bb7e43 | 867 | |
263eeeab A |
868 | mDNSexport NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex) |
869 | { | |
870 | mDNSu32 scope_id = (mDNSu32)(uintptr_t)ifindex; | |
871 | NetworkInterfaceInfoOSX *i; | |
32bb7e43 | 872 | |
263eeeab A |
873 | // Don't get tricked by inactive interfaces |
874 | for (i = m->p->InterfaceList; i; i = i->next) | |
875 | if (i->Registered && i->scope_id == scope_id) return(i); | |
32bb7e43 | 876 | |
263eeeab A |
877 | return mDNSNULL; |
878 | } | |
32bb7e43 | 879 | |
263eeeab A |
880 | mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex) |
881 | { | |
882 | if (ifindex == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); | |
883 | if (ifindex == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); | |
884 | if (ifindex == kDNSServiceInterfaceIndexAny ) return(mDNSNULL); | |
32bb7e43 | 885 | |
263eeeab A |
886 | NetworkInterfaceInfoOSX* ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); |
887 | if (!ifi) | |
888 | { | |
889 | // Not found. Make sure our interface list is up to date, then try again. | |
890 | LogInfo("mDNSPlatformInterfaceIDfromInterfaceIndex: InterfaceID for interface index %d not found; Updating interface list", ifindex); | |
891 | mDNSMacOSXNetworkChanged(m); | |
892 | ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); | |
893 | } | |
32bb7e43 | 894 | |
263eeeab A |
895 | if (!ifi) return(mDNSNULL); |
896 | ||
897 | return(ifi->ifinfo.InterfaceID); | |
898 | } | |
32bb7e43 | 899 | |
32bb7e43 | 900 | |
294beb6e | 901 | mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange) |
263eeeab A |
902 | { |
903 | NetworkInterfaceInfoOSX *i; | |
904 | if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); | |
905 | if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); | |
906 | if (id == mDNSInterface_Any ) return(0); | |
32bb7e43 | 907 | |
263eeeab | 908 | mDNSu32 scope_id = (mDNSu32)(uintptr_t)id; |
32bb7e43 | 909 | |
263eeeab A |
910 | // Don't use i->Registered here, because we DO want to find inactive interfaces, which have no Registered set |
911 | for (i = m->p->InterfaceList; i; i = i->next) | |
912 | if (i->scope_id == scope_id) return(i->scope_id); | |
32bb7e43 | 913 | |
294beb6e A |
914 | // If we are supposed to suppress network change, return "id" back |
915 | if (suppressNetworkChange) return scope_id; | |
916 | ||
263eeeab A |
917 | // Not found. Make sure our interface list is up to date, then try again. |
918 | LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id); | |
919 | mDNSMacOSXNetworkChanged(m); | |
920 | for (i = m->p->InterfaceList; i; i = i->next) | |
921 | if (i->scope_id == scope_id) return(i->scope_id); | |
32bb7e43 | 922 | |
263eeeab A |
923 | return(0); |
924 | } | |
32bb7e43 | 925 | |
263eeeab A |
926 | #if APPLE_OSX_mDNSResponder |
927 | mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...) | |
928 | { | |
294beb6e | 929 | if (OSXVers < OSXVers_10_6_SnowLeopard) return; // Only do ASL on Mac OS X 10.6 and later (not on iOS) |
32bb7e43 | 930 | |
263eeeab A |
931 | static char buffer[512]; |
932 | aslmsg asl_msg = asl_new(ASL_TYPE_MSG); | |
32bb7e43 | 933 | |
263eeeab A |
934 | if (!asl_msg) { LogMsg("mDNSASLLog: asl_new failed"); return; } |
935 | if (uuid) | |
936 | { | |
937 | char uuidStr[37]; | |
938 | uuid_unparse(*uuid, uuidStr); | |
939 | asl_set (asl_msg, "com.apple.message.uuid", uuidStr); | |
940 | } | |
32bb7e43 | 941 | |
263eeeab A |
942 | static char domainBase[] = "com.apple.mDNSResponder.%s"; |
943 | mDNS_snprintf (buffer, sizeof(buffer), domainBase, subdomain); | |
944 | asl_set (asl_msg, "com.apple.message.domain", buffer); | |
32bb7e43 | 945 | |
263eeeab A |
946 | if (result) asl_set(asl_msg, "com.apple.message.result", result); |
947 | if (signature) asl_set(asl_msg, "com.apple.message.signature", signature); | |
32bb7e43 | 948 | |
263eeeab A |
949 | va_list ptr; |
950 | va_start(ptr,fmt); | |
951 | mDNS_vsnprintf(buffer, sizeof(buffer), fmt, ptr); | |
952 | va_end(ptr); | |
32bb7e43 | 953 | |
263eeeab A |
954 | int old_filter = asl_set_filter(NULL,ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); |
955 | asl_log(NULL, asl_msg, ASL_LEVEL_DEBUG, "%s", buffer); | |
956 | asl_set_filter(NULL, old_filter); | |
957 | asl_free(asl_msg); | |
958 | } | |
959 | #endif // APPLE_OSX_mDNSResponder | |
32bb7e43 | 960 | |
263eeeab A |
961 | #if COMPILER_LIKES_PRAGMA_MARK |
962 | #pragma mark - | |
963 | #pragma mark - UDP & TCP send & receive | |
964 | #endif | |
32bb7e43 | 965 | |
263eeeab A |
966 | mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr) |
967 | { | |
968 | mDNSBool result = mDNSfalse; | |
969 | SCNetworkConnectionFlags flags; | |
970 | SCNetworkReachabilityRef ReachRef = NULL; | |
32bb7e43 | 971 | |
263eeeab A |
972 | ReachRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, addr); |
973 | if (!ReachRef) { LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithAddress"); goto end; } | |
974 | if (!SCNetworkReachabilityGetFlags(ReachRef, &flags)) { LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags"); goto end; } | |
975 | result = flags & kSCNetworkFlagsConnectionRequired; | |
32bb7e43 | 976 | |
263eeeab A |
977 | end: |
978 | if (ReachRef) CFRelease(ReachRef); | |
979 | return result; | |
980 | } | |
32bb7e43 | 981 | |
263eeeab A |
982 | // Note: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket" |
983 | // Note: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface" | |
984 | // OR send via our primary v4 unicast socket | |
985 | // UPDATE: The UDPSocket *src parameter now allows the caller to specify the source socket | |
986 | mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, | |
987 | mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstPort) | |
988 | { | |
989 | NetworkInterfaceInfoOSX *info = mDNSNULL; | |
990 | struct sockaddr_storage to; | |
991 | int s = -1, err; | |
992 | mStatus result = mStatus_NoError; | |
32bb7e43 | 993 | |
263eeeab A |
994 | if (InterfaceID) |
995 | { | |
996 | info = IfindexToInterfaceInfoOSX(m, InterfaceID); | |
997 | if (info == NULL) | |
998 | { | |
999 | LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); | |
1000 | return mStatus_BadParamErr; | |
1001 | } | |
1002 | } | |
32bb7e43 | 1003 | |
263eeeab | 1004 | char *ifa_name = InterfaceID ? info->ifinfo.ifname : "unicast"; |
32bb7e43 | 1005 | |
263eeeab A |
1006 | if (dst->type == mDNSAddrType_IPv4) |
1007 | { | |
1008 | struct sockaddr_in *sin_to = (struct sockaddr_in*)&to; | |
1009 | sin_to->sin_len = sizeof(*sin_to); | |
1010 | sin_to->sin_family = AF_INET; | |
1011 | sin_to->sin_port = dstPort.NotAnInteger; | |
1012 | sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger; | |
1013 | s = (src ? src->ss : m->p->permanentsockets).sktv4; | |
32bb7e43 | 1014 | |
263eeeab A |
1015 | if (info) // Specify outgoing interface |
1016 | { | |
1017 | if (!mDNSAddrIsDNSMulticast(dst)) | |
1018 | { | |
1019 | #ifdef IP_BOUND_IF | |
1020 | if (info->scope_id == 0) | |
1021 | LogInfo("IP_BOUND_IF socket option not set -- info %p (%s) scope_id is zero", info, ifa_name); | |
1022 | else | |
1023 | setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); | |
1024 | #else | |
1025 | { | |
1026 | static int displayed = 0; | |
1027 | if (displayed < 1000) | |
1028 | { | |
1029 | displayed++; | |
1030 | LogInfo("IP_BOUND_IF socket option not defined -- cannot specify interface for unicast packets"); | |
1031 | } | |
1032 | } | |
1033 | #endif | |
1034 | } | |
1035 | else | |
1036 | #ifdef IP_MULTICAST_IFINDEX | |
1037 | { | |
1038 | err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IFINDEX, &info->scope_id, sizeof(info->scope_id)); | |
1039 | // We get an error when we compile on a machine that supports this option and run the binary on | |
1040 | // a different machine that does not support it | |
1041 | if (err < 0) | |
1042 | { | |
1043 | if (errno != ENOPROTOOPT) LogInfo("mDNSPlatformSendUDP: setsockopt: IP_MUTLTICAST_IFINDEX returned %d", errno); | |
1044 | err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr)); | |
1045 | if (err < 0 && !m->p->NetworkChanged) | |
1046 | LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno)); | |
1047 | } | |
1048 | } | |
1049 | #else | |
1050 | { | |
1051 | err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr)); | |
1052 | if (err < 0 && !m->p->NetworkChanged) | |
1053 | LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno)); | |
32bb7e43 | 1054 | |
263eeeab A |
1055 | } |
1056 | #endif | |
1057 | } | |
1058 | } | |
1059 | #ifndef NO_IPV6 | |
1060 | else if (dst->type == mDNSAddrType_IPv6) | |
1061 | { | |
1062 | struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to; | |
1063 | sin6_to->sin6_len = sizeof(*sin6_to); | |
1064 | sin6_to->sin6_family = AF_INET6; | |
1065 | sin6_to->sin6_port = dstPort.NotAnInteger; | |
1066 | sin6_to->sin6_flowinfo = 0; | |
1067 | sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6; | |
1068 | sin6_to->sin6_scope_id = info ? info->scope_id : 0; | |
1069 | s = (src ? src->ss : m->p->permanentsockets).sktv6; | |
1070 | if (info && mDNSAddrIsDNSMulticast(dst)) // Specify outgoing interface | |
1071 | { | |
1072 | err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &info->scope_id, sizeof(info->scope_id)); | |
294beb6e A |
1073 | if (err < 0) |
1074 | { | |
1075 | char name[IFNAMSIZ]; | |
1076 | if (if_indextoname(info->scope_id, name) != NULL) | |
1077 | LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno)); | |
1078 | else | |
1079 | LogInfo("setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface", info->scope_id); | |
1080 | } | |
263eeeab A |
1081 | } |
1082 | } | |
1083 | #endif | |
1084 | else | |
1085 | { | |
1086 | LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!"); | |
1087 | #if ForceAlerts | |
1088 | *(long*)0 = 0; | |
1089 | #endif | |
1090 | return mStatus_BadParamErr; | |
1091 | } | |
32bb7e43 | 1092 | |
263eeeab A |
1093 | if (s >= 0) |
1094 | verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %p %5s/%ld to %#a:%d skt %d", | |
1095 | InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s); | |
1096 | else | |
1097 | verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %p %5s/%ld (socket of this type not available)", | |
1098 | InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort)); | |
9f29194f | 1099 | |
263eeeab A |
1100 | // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet |
1101 | // If we don't have the corresponding type of socket available, then return mStatus_Invalid | |
1102 | if (s < 0) return(mStatus_Invalid); | |
5e65c77f | 1103 | |
263eeeab A |
1104 | err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len); |
1105 | if (err < 0) | |
1106 | { | |
1107 | static int MessageCount = 0; | |
1108 | // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations | |
1109 | if (!mDNSAddressIsAllDNSLinkGroup(dst)) | |
1110 | if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); | |
1111 | // Don't report EHOSTUNREACH in the first three minutes after boot | |
1112 | // This is because mDNSResponder intentionally starts up early in the boot process (See <rdar://problem/3409090>) | |
1113 | // but this means that sometimes it starts before configd has finished setting up the multicast routing entries. | |
1114 | if (errno == EHOSTUNREACH && (mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(mStatus_TransientErr); | |
1115 | // Don't report EADDRNOTAVAIL ("Can't assign requested address") if we're in the middle of a network configuration change | |
1116 | if (errno == EADDRNOTAVAIL && m->p->NetworkChanged) return(mStatus_TransientErr); | |
1117 | if (MessageCount < 1000) | |
1118 | { | |
1119 | MessageCount++; | |
1120 | if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN) | |
1121 | LogInfo("mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu", | |
1122 | s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); | |
1123 | else | |
1124 | LogMsg("mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu", | |
1125 | s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); | |
1126 | } | |
1127 | result = mStatus_UnknownErr; | |
1128 | } | |
5e65c77f | 1129 | |
263eeeab A |
1130 | #ifdef IP_BOUND_IF |
1131 | if (dst->type == mDNSAddrType_IPv4 && info && !mDNSAddrIsDNSMulticast(dst)) | |
1132 | { | |
1133 | static const mDNSu32 ifindex = 0; | |
1134 | setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex)); | |
1135 | } | |
1136 | #endif | |
5e65c77f | 1137 | |
263eeeab A |
1138 | return(result); |
1139 | } | |
5e65c77f | 1140 | |
263eeeab A |
1141 | mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, |
1142 | struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) | |
1143 | { | |
1144 | static unsigned int numLogMessages = 0; | |
1145 | struct iovec databuffers = { (char *)buffer, max }; | |
1146 | struct msghdr msg; | |
1147 | ssize_t n; | |
1148 | struct cmsghdr *cmPtr; | |
1149 | char ancillary[1024]; | |
5e65c77f | 1150 | |
263eeeab | 1151 | *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be |
5e65c77f | 1152 | |
263eeeab A |
1153 | // Set up the message |
1154 | msg.msg_name = (caddr_t)from; | |
1155 | msg.msg_namelen = *fromlen; | |
1156 | msg.msg_iov = &databuffers; | |
1157 | msg.msg_iovlen = 1; | |
1158 | msg.msg_control = (caddr_t)&ancillary; | |
1159 | msg.msg_controllen = sizeof(ancillary); | |
1160 | msg.msg_flags = 0; | |
5e65c77f | 1161 | |
263eeeab A |
1162 | // Receive the data |
1163 | n = recvmsg(s, &msg, 0); | |
1164 | if (n<0) | |
1165 | { | |
1166 | if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned error %d errno %d", s, n, errno); | |
1167 | return(-1); | |
1168 | } | |
1169 | if (msg.msg_controllen < (int)sizeof(struct cmsghdr)) | |
1170 | { | |
1171 | if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", | |
1172 | s, n, msg.msg_controllen, sizeof(struct cmsghdr)); | |
1173 | return(-1); | |
1174 | } | |
1175 | if (msg.msg_flags & MSG_CTRUNC) | |
1176 | { | |
1177 | if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); | |
1178 | return(-1); | |
1179 | } | |
5e65c77f | 1180 | |
263eeeab | 1181 | *fromlen = msg.msg_namelen; |
5e65c77f | 1182 | |
263eeeab A |
1183 | // Parse each option out of the ancillary data. |
1184 | for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) | |
1185 | { | |
1186 | // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); | |
1187 | if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) | |
1188 | { | |
1189 | dstaddr->type = mDNSAddrType_IPv4; | |
1190 | dstaddr->ip.v4 = *(mDNSv4Addr*)CMSG_DATA(cmPtr); | |
1191 | //LogMsg("mDNSMacOSX.c: recvmsg IP_RECVDSTADDR %.4a", &dstaddr->ip.v4); | |
1192 | } | |
1193 | if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) | |
1194 | { | |
1195 | struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); | |
1196 | if (sdl->sdl_nlen < IF_NAMESIZE) | |
1197 | { | |
1198 | mDNSPlatformMemCopy(ifname, sdl->sdl_data, sdl->sdl_nlen); | |
1199 | ifname[sdl->sdl_nlen] = 0; | |
1200 | // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); | |
1201 | } | |
1202 | } | |
1203 | if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL) | |
1204 | *ttl = *(u_char*)CMSG_DATA(cmPtr); | |
1205 | if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO) | |
1206 | { | |
1207 | struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr); | |
1208 | dstaddr->type = mDNSAddrType_IPv6; | |
1209 | dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr; | |
1210 | myIfIndexToName(ip6_info->ipi6_ifindex, ifname); | |
1211 | } | |
1212 | if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT) | |
1213 | *ttl = *(int*)CMSG_DATA(cmPtr); | |
1214 | } | |
5e65c77f | 1215 | |
263eeeab A |
1216 | return(n); |
1217 | } | |
5e65c77f | 1218 | |
263eeeab A |
1219 | mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context) |
1220 | { | |
1221 | KQSocketSet *const ss = (KQSocketSet *)context; | |
1222 | mDNS *const m = ss->m; | |
1223 | int err = 0, count = 0, closed = 0; | |
5e65c77f | 1224 | |
263eeeab A |
1225 | if (filter != EVFILT_READ) |
1226 | LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter, EVFILT_READ); | |
5e65c77f | 1227 | |
263eeeab A |
1228 | if (s1 != ss->sktv4 |
1229 | #ifndef NO_IPV6 | |
1230 | && s1 != ss->sktv6 | |
1231 | #endif | |
1232 | ) | |
1233 | { | |
1234 | LogMsg("myKQSocketCallBack: native socket %d", s1); | |
1235 | LogMsg("myKQSocketCallBack: sktv4 %d", ss->sktv4); | |
1236 | #ifndef NO_IPV6 | |
1237 | LogMsg("myKQSocketCallBack: sktv6 %d", ss->sktv6); | |
1238 | #endif | |
1239 | } | |
5e65c77f | 1240 | |
263eeeab A |
1241 | while (!closed) |
1242 | { | |
1243 | mDNSAddr senderAddr, destAddr; | |
1244 | mDNSIPPort senderPort; | |
1245 | struct sockaddr_storage from; | |
1246 | size_t fromlen = sizeof(from); | |
1247 | char packetifname[IF_NAMESIZE] = ""; | |
1248 | mDNSu8 ttl; | |
1249 | err = myrecvfrom(s1, &m->imsg, sizeof(m->imsg), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl); | |
1250 | if (err < 0) break; | |
5e65c77f | 1251 | |
263eeeab A |
1252 | count++; |
1253 | if (from.ss_family == AF_INET) | |
1254 | { | |
1255 | struct sockaddr_in *s = (struct sockaddr_in*)&from; | |
1256 | senderAddr.type = mDNSAddrType_IPv4; | |
1257 | senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; | |
1258 | senderPort.NotAnInteger = s->sin_port; | |
1259 | //LogInfo("myKQSocketCallBack received IPv4 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname); | |
1260 | } | |
1261 | else if (from.ss_family == AF_INET6) | |
1262 | { | |
1263 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; | |
1264 | senderAddr.type = mDNSAddrType_IPv6; | |
1265 | senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; | |
1266 | senderPort.NotAnInteger = sin6->sin6_port; | |
1267 | //LogInfo("myKQSocketCallBack received IPv6 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname); | |
1268 | } | |
1269 | else | |
1270 | { | |
1271 | LogMsg("myKQSocketCallBack from is unknown address family %d", from.ss_family); | |
1272 | return; | |
1273 | } | |
030b743d | 1274 | |
263eeeab A |
1275 | // Note: When handling multiple packets in a batch, MUST reset InterfaceID before handling each packet |
1276 | mDNSInterfaceID InterfaceID = mDNSNULL; | |
1277 | //NetworkInterfaceInfo *intf = m->HostInterfaces; | |
1278 | //while (intf && strcmp(intf->ifname, packetifname)) intf = intf->next; | |
030b743d | 1279 | |
263eeeab A |
1280 | NetworkInterfaceInfoOSX *intf = m->p->InterfaceList; |
1281 | while (intf && strcmp(intf->ifinfo.ifname, packetifname)) intf = intf->next; | |
030b743d | 1282 | |
263eeeab A |
1283 | // When going to sleep we deregister all our interfaces, but if the machine |
1284 | // takes a few seconds to sleep we may continue to receive multicasts | |
1285 | // during that time, which would confuse mDNSCoreReceive, because as far | |
1286 | // as it's concerned, we should have no active interfaces any more. | |
1287 | // Hence we ignore multicasts for which we can find no matching InterfaceID. | |
1288 | if (intf) InterfaceID = intf->ifinfo.InterfaceID; | |
1289 | else if (mDNSAddrIsDNSMulticast(&destAddr)) continue; | |
030b743d | 1290 | |
263eeeab A |
1291 | // LogMsg("myKQSocketCallBack got packet from %#a to %#a on interface %#a/%s", |
1292 | // &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifinfo.ifname); | |
030b743d | 1293 | |
263eeeab A |
1294 | // mDNSCoreReceive may close the socket we're reading from. We must break out of our |
1295 | // loop when that happens, or we may try to read from an invalid FD. We do this by | |
1296 | // setting the closeFlag pointer in the socketset, so CloseSocketSet can inform us | |
1297 | // if it closes the socketset. | |
1298 | ss->closeFlag = &closed; | |
030b743d | 1299 | |
263eeeab | 1300 | mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID); |
030b743d | 1301 | |
263eeeab A |
1302 | // if we didn't close, we can safely dereference the socketset, and should to |
1303 | // reset the closeFlag, since it points to something on the stack | |
1304 | if (!closed) ss->closeFlag = mDNSNULL; | |
1305 | } | |
030b743d | 1306 | |
263eeeab A |
1307 | if (err < 0 && (errno != EWOULDBLOCK || count == 0)) |
1308 | { | |
1309 | // Something is busted here. | |
1310 | // kqueue says there is a packet, but myrecvfrom says there is not. | |
1311 | // Try calling select() to get another opinion. | |
1312 | // Find out about other socket parameter that can help understand why select() says the socket is ready for read | |
1313 | // All of this is racy, as data may have arrived after the call to select() | |
1314 | static unsigned int numLogMessages = 0; | |
1315 | int save_errno = errno; | |
1316 | int so_error = -1; | |
1317 | int so_nread = -1; | |
1318 | int fionread = -1; | |
1319 | socklen_t solen = sizeof(int); | |
1320 | fd_set readfds; | |
1321 | struct timeval timeout; | |
1322 | int selectresult; | |
1323 | FD_ZERO(&readfds); | |
1324 | FD_SET(s1, &readfds); | |
1325 | timeout.tv_sec = 0; | |
1326 | timeout.tv_usec = 0; | |
1327 | selectresult = select(s1+1, &readfds, NULL, NULL, &timeout); | |
1328 | if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1) | |
1329 | LogMsg("myKQSocketCallBack getsockopt(SO_ERROR) error %d", errno); | |
1330 | if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1) | |
1331 | LogMsg("myKQSocketCallBack getsockopt(SO_NREAD) error %d", errno); | |
1332 | if (ioctl(s1, FIONREAD, &fionread) == -1) | |
1333 | LogMsg("myKQSocketCallBack ioctl(FIONREAD) error %d", errno); | |
1334 | if (numLogMessages++ < 100) | |
1335 | LogMsg("myKQSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d", | |
1336 | s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count); | |
1337 | if (numLogMessages > 5) | |
1338 | NotifyOfElusiveBug("Flaw in Kernel (select/recvfrom mismatch)", | |
1339 | "Congratulations, you've reproduced an elusive bug.\r" | |
1340 | "Please contact the current assignee of <rdar://problem/3375328>.\r" | |
1341 | "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r" | |
1342 | "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); | |
96f69b28 | 1343 | |
263eeeab A |
1344 | sleep(1); // After logging this error, rate limit so we don't flood syslog |
1345 | } | |
1346 | } | |
96f69b28 | 1347 | |
263eeeab | 1348 | // TCP socket support |
96f69b28 | 1349 | |
263eeeab A |
1350 | typedef enum |
1351 | { | |
1352 | handshake_required, | |
1353 | handshake_in_progress, | |
1354 | handshake_completed, | |
1355 | handshake_to_be_closed | |
1356 | } handshakeStatus; | |
1357 | ||
1358 | struct TCPSocket_struct | |
1359 | { | |
1360 | TCPSocketFlags flags; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags | |
1361 | TCPConnectionCallback callback; | |
1362 | int fd; | |
1363 | KQueueEntry *kqEntry; | |
1364 | KQSocketSet ss; | |
1365 | #ifndef NO_SECURITYFRAMEWORK | |
1366 | SSLContextRef tlsContext; | |
1367 | pthread_t handshake_thread; | |
1368 | #endif /* NO_SECURITYFRAMEWORK */ | |
1369 | domainname hostname; | |
1370 | void *context; | |
1371 | mDNSBool setup; | |
1372 | mDNSBool connected; | |
1373 | handshakeStatus handshake; | |
1374 | mDNS *m; // So we can call KQueueLock from the SSLHandshake thread | |
1375 | mStatus err; | |
1376 | }; | |
96f69b28 | 1377 | |
263eeeab A |
1378 | mDNSlocal void doTcpSocketCallback(TCPSocket *sock) |
1379 | { | |
1380 | mDNSBool c = !sock->connected; | |
1381 | sock->connected = mDNStrue; | |
1382 | sock->callback(sock, sock->context, c, sock->err); | |
1383 | // Note: the callback may call CloseConnection here, which frees the context structure! | |
1384 | } | |
96f69b28 | 1385 | |
263eeeab | 1386 | #ifndef NO_SECURITYFRAMEWORK |
96f69b28 | 1387 | |
263eeeab A |
1388 | mDNSlocal OSStatus tlsWriteSock(SSLConnectionRef connection, const void *data, size_t *dataLength) |
1389 | { | |
1390 | int ret = send(((TCPSocket *)connection)->fd, data, *dataLength, 0); | |
1391 | if (ret >= 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); } | |
1392 | if (ret >= 0) { *dataLength = ret; return(noErr); } | |
1393 | *dataLength = 0; | |
1394 | if (errno == EAGAIN ) return(errSSLWouldBlock); | |
1395 | if (errno == ENOENT ) return(errSSLClosedGraceful); | |
1396 | if (errno == EPIPE || errno == ECONNRESET) return(errSSLClosedAbort); | |
1397 | LogMsg("ERROR: tlsWriteSock: %d error %d (%s)\n", ((TCPSocket *)connection)->fd, errno, strerror(errno)); | |
1398 | return(errSSLClosedAbort); | |
1399 | } | |
283ee3ff | 1400 | |
263eeeab A |
1401 | mDNSlocal OSStatus tlsReadSock(SSLConnectionRef connection, void *data, size_t *dataLength) |
1402 | { | |
1403 | int ret = recv(((TCPSocket *)connection)->fd, data, *dataLength, 0); | |
1404 | if (ret > 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); } | |
1405 | if (ret > 0) { *dataLength = ret; return(noErr); } | |
1406 | *dataLength = 0; | |
1407 | if (ret == 0 || errno == ENOENT ) return(errSSLClosedGraceful); | |
1408 | if ( errno == EAGAIN ) return(errSSLWouldBlock); | |
1409 | if ( errno == ECONNRESET) return(errSSLClosedAbort); | |
1410 | LogMsg("ERROR: tlsSockRead: error %d (%s)\n", errno, strerror(errno)); | |
1411 | return(errSSLClosedAbort); | |
1412 | } | |
283ee3ff | 1413 | |
263eeeab A |
1414 | mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, mDNSBool server) |
1415 | { | |
1416 | char domname_cstr[MAX_ESCAPED_DOMAIN_NAME]; | |
7f0064bd | 1417 | |
263eeeab A |
1418 | mStatus err = SSLNewContext(server, &sock->tlsContext); |
1419 | if (err) { LogMsg("ERROR: tlsSetupSock: SSLNewContext failed with error code: %d", err); return(err); } | |
7f0064bd | 1420 | |
263eeeab A |
1421 | err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock); |
1422 | if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err); return(err); } | |
7f0064bd | 1423 | |
263eeeab A |
1424 | err = SSLSetConnection(sock->tlsContext, (SSLConnectionRef) sock); |
1425 | if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err); return(err); } | |
7f0064bd | 1426 | |
263eeeab A |
1427 | // Instead of listing all the acceptable ciphers, we just disable the bad ciphers. It does not disable |
1428 | // all the bad ciphers like RC4_MD5, but it assumes that the servers don't offer them. | |
1429 | err = SSLSetAllowAnonymousCiphers(sock->tlsContext, 0); | |
1430 | if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err); return(err); } | |
7f0064bd | 1431 | |
263eeeab A |
1432 | // We already checked for NULL in hostname and this should never happen. Hence, returning -1 |
1433 | // (error not in OSStatus space) is okay. | |
1434 | if (!sock->hostname.c[0]) {LogMsg("ERROR: tlsSetupSock: hostname NULL"); return -1; } | |
7f0064bd | 1435 | |
263eeeab A |
1436 | ConvertDomainNameToCString(&sock->hostname, domname_cstr); |
1437 | err = SSLSetPeerDomainName(sock->tlsContext, domname_cstr, strlen(domname_cstr)); | |
1438 | if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err); return(err); } | |
7f0064bd | 1439 | |
263eeeab A |
1440 | return(err); |
1441 | } | |
7f0064bd | 1442 | |
294beb6e | 1443 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
1444 | mDNSlocal void doSSLHandshake(void *ctx) |
1445 | { | |
1446 | TCPSocket *sock = (TCPSocket*)ctx; | |
1447 | mStatus err = SSLHandshake(sock->tlsContext); | |
1448 | ||
294beb6e | 1449 | //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is |
263eeeab A |
1450 | //defined, KQueueLock is a noop. Hence we need to serialize here |
1451 | // | |
1452 | //NOTE: We just can't serialize doTcpSocketCallback alone on the main queue. | |
1453 | //We need the rest of the logic also. Otherwise, we can enable the READ | |
1454 | //events below, dispatch a doTcpSocketCallback on the main queue. Assume it is | |
1455 | //ConnFailed which means we are going to free the tcpInfo. While it | |
1456 | //is waiting to be dispatched, another read event can come into tcpKQSocketCallback | |
1457 | //and potentially call doTCPCallback with error which can close the fd and free the | |
1458 | //tcpInfo. Later when the thread gets dispatched it will crash because the tcpInfo | |
1459 | //is already freed. | |
7f0064bd | 1460 | |
263eeeab | 1461 | dispatch_async(dispatch_get_main_queue(), ^{ |
7f0064bd | 1462 | |
263eeeab | 1463 | LogInfo("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock |
7f0064bd | 1464 | |
263eeeab A |
1465 | if (sock->handshake == handshake_to_be_closed) |
1466 | { | |
1467 | LogInfo("SSLHandshake completed after close"); | |
1468 | mDNSPlatformTCPCloseConnection(sock); | |
1469 | } | |
1470 | else | |
1471 | { | |
1472 | if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); | |
1473 | else LogMsg("doSSLHandshake: sock->fd is -1"); | |
1474 | ||
1475 | if (err == errSSLWouldBlock) | |
1476 | sock->handshake = handshake_required; | |
1477 | else | |
1478 | { | |
1479 | if (err) | |
1480 | { | |
1481 | LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); | |
1482 | SSLDisposeContext(sock->tlsContext); | |
1483 | sock->tlsContext = NULL; | |
1484 | } | |
1485 | ||
1486 | sock->err = err ? mStatus_ConnFailed : 0; | |
1487 | sock->handshake = handshake_completed; | |
1488 | ||
1489 | LogInfo("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd); | |
1490 | doTcpSocketCallback(sock); | |
1491 | } | |
1492 | } | |
1493 | ||
1494 | LogInfo("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd); | |
1495 | return; | |
1496 | }); | |
1497 | } | |
1498 | #else | |
1499 | mDNSlocal void *doSSLHandshake(void *ctx) | |
1500 | { | |
1501 | // Warning: Touching sock without the kqueue lock! | |
1502 | // We're protected because sock->handshake == handshake_in_progress | |
1503 | TCPSocket *sock = (TCPSocket*)ctx; | |
1504 | mDNS * const m = sock->m; // Get m now, as we may free sock if marked to be closed while we're waiting on SSLHandshake | |
1505 | mStatus err = SSLHandshake(sock->tlsContext); | |
1506 | ||
1507 | KQueueLock(m); | |
1508 | debugf("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock | |
7f0064bd | 1509 | |
263eeeab A |
1510 | if (sock->handshake == handshake_to_be_closed) |
1511 | { | |
1512 | LogInfo("SSLHandshake completed after close"); | |
1513 | mDNSPlatformTCPCloseConnection(sock); | |
1514 | } | |
1515 | else | |
1516 | { | |
1517 | if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); | |
1518 | else LogMsg("doSSLHandshake: sock->fd is -1"); | |
7f0064bd | 1519 | |
263eeeab A |
1520 | if (err == errSSLWouldBlock) |
1521 | sock->handshake = handshake_required; | |
1522 | else | |
1523 | { | |
1524 | if (err) | |
1525 | { | |
1526 | LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); | |
1527 | SSLDisposeContext(sock->tlsContext); | |
1528 | sock->tlsContext = NULL; | |
1529 | } | |
1530 | ||
1531 | sock->err = err ? mStatus_ConnFailed : 0; | |
1532 | sock->handshake = handshake_completed; | |
1533 | ||
1534 | debugf("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd); | |
1535 | doTcpSocketCallback(sock); | |
1536 | } | |
1537 | } | |
1538 | ||
1539 | debugf("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd); | |
1540 | KQueueUnlock(m, "doSSLHandshake"); | |
1541 | return NULL; | |
1542 | } | |
1543 | #endif | |
7f0064bd | 1544 | |
263eeeab A |
1545 | mDNSlocal mStatus spawnSSLHandshake(TCPSocket* sock) |
1546 | { | |
1547 | debugf("spawnSSLHandshake %p: entry", sock); | |
1548 | mStatus err; | |
7f0064bd | 1549 | |
263eeeab A |
1550 | if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake); |
1551 | sock->handshake = handshake_in_progress; | |
1552 | KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, sock->kqEntry); | |
294beb6e | 1553 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
7f0064bd | 1554 | |
263eeeab A |
1555 | // Dispatch it on a separate serial queue to avoid deadlocks with threads running on main queue |
1556 | dispatch_async(SSLqueue, ^{doSSLHandshake(sock);}); | |
1557 | err = 0; | |
1558 | #else | |
1559 | pthread_attr_t attr; | |
1560 | pthread_attr_init(&attr); | |
1561 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
1562 | err = pthread_create(&sock->handshake_thread, &attr, doSSLHandshake, sock); | |
1563 | pthread_attr_destroy(&attr); | |
1564 | if (err) | |
1565 | { | |
1566 | LogMsg("Could not start SSLHandshake thread: (%d) %s", err, strerror(err)); | |
1567 | sock->handshake = handshake_completed; | |
1568 | sock->err = err; | |
1569 | KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); | |
1570 | } | |
1571 | #endif | |
1572 | debugf("spawnSSLHandshake %p: done for %d", sock, sock->fd); | |
1573 | return err; | |
1574 | } | |
7f0064bd | 1575 | |
263eeeab A |
1576 | mDNSlocal mDNSBool IsTunnelModeDomain(const domainname *d) |
1577 | { | |
1578 | static const domainname *mmc = (const domainname*) "\x7" "members" "\x3" "mac" "\x3" "com"; | |
1579 | const domainname *d1 = mDNSNULL; // TLD | |
1580 | const domainname *d2 = mDNSNULL; // SLD | |
1581 | const domainname *d3 = mDNSNULL; | |
1582 | while (d->c[0]) { d3 = d2; d2 = d1; d1 = d; d = (const domainname*)(d->c + 1 + d->c[0]); } | |
1583 | return(d3 && SameDomainName(d3, mmc)); | |
1584 | } | |
7f0064bd | 1585 | |
263eeeab | 1586 | #endif /* NO_SECURITYFRAMEWORK */ |
7f0064bd | 1587 | |
263eeeab A |
1588 | mDNSlocal void tcpKQSocketCallback(__unused int fd, short filter, void *context) |
1589 | { | |
1590 | TCPSocket *sock = context; | |
1591 | sock->err = mStatus_NoError; | |
7f0064bd | 1592 | |
263eeeab A |
1593 | //if (filter == EVFILT_READ ) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_READ", filter); |
1594 | //if (filter == EVFILT_WRITE) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_WRITE", filter); | |
1595 | // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it here with EV_DELETE | |
1596 | if (filter == EVFILT_WRITE) KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry); | |
7f0064bd | 1597 | |
263eeeab A |
1598 | if (sock->flags & kTCPSocketFlags_UseTLS) |
1599 | { | |
1600 | #ifndef NO_SECURITYFRAMEWORK | |
1601 | if (!sock->setup) { sock->setup = mDNStrue; tlsSetupSock(sock, mDNSfalse); } | |
1602 | ||
1603 | if (sock->handshake == handshake_required) { if (spawnSSLHandshake(sock) == 0) return; } | |
1604 | else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed) return; | |
1605 | else if (sock->handshake != handshake_completed) | |
1606 | { | |
1607 | if (!sock->err) sock->err = mStatus_UnknownErr; | |
1608 | LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock->handshake); | |
1609 | } | |
1610 | #else | |
1611 | sock->err = mStatus_UnsupportedErr; | |
1612 | #endif /* NO_SECURITYFRAMEWORK */ | |
1613 | } | |
1614 | ||
1615 | doTcpSocketCallback(sock); | |
1616 | } | |
7f0064bd | 1617 | |
294beb6e | 1618 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
1619 | mDNSexport int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef) |
1620 | { | |
1621 | dispatch_queue_t queue = dispatch_get_main_queue(); | |
1622 | dispatch_source_t source; | |
1623 | if (flags == EV_DELETE) | |
1624 | { | |
1625 | if (filter == EVFILT_READ) | |
1626 | { | |
1627 | dispatch_source_cancel(entryRef->readSource); | |
1628 | dispatch_release(entryRef->readSource); | |
1629 | entryRef->readSource = mDNSNULL; | |
1630 | debugf("KQueueSet: source cancel for read %p, %p", entryRef->readSource, entryRef->writeSource); | |
1631 | } | |
1632 | else if (filter == EVFILT_WRITE) | |
1633 | { | |
1634 | dispatch_source_cancel(entryRef->writeSource); | |
1635 | dispatch_release(entryRef->writeSource); | |
1636 | entryRef->writeSource = mDNSNULL; | |
1637 | debugf("KQueueSet: source cancel for write %p, %p", entryRef->readSource, entryRef->writeSource); | |
1638 | } | |
1639 | else | |
1640 | LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_DELETE", filter); | |
1641 | return 0; | |
1642 | } | |
1643 | if (flags != EV_ADD) LogMsg("KQueueSet: Invalid flags %d", flags); | |
7f0064bd | 1644 | |
263eeeab A |
1645 | if (filter == EVFILT_READ) |
1646 | { | |
1647 | source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue); | |
1648 | } | |
1649 | else if (filter == EVFILT_WRITE) | |
1650 | { | |
1651 | source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, queue); | |
1652 | } | |
1653 | else | |
1654 | { | |
1655 | LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_ADD", filter); | |
1656 | return -1; | |
1657 | } | |
1658 | if (!source) return -1; | |
1659 | dispatch_source_set_event_handler(source, ^{ | |
7f0064bd | 1660 | |
263eeeab A |
1661 | mDNSs32 stime = mDNSPlatformRawTime(); |
1662 | entryRef->KQcallback(fd, filter, entryRef->KQcontext); | |
1663 | mDNSs32 etime = mDNSPlatformRawTime(); | |
1664 | if (etime - stime >= WatchDogReportingThreshold) | |
1665 | LogInfo("KQEntryCallback Block: WARNING: took %dms to complete", etime - stime); | |
7f0064bd | 1666 | |
263eeeab A |
1667 | // Trigger the event delivery to the application. Even though we trigger the |
1668 | // event completion after handling every event source, these all will hopefully | |
1669 | // get merged | |
1670 | TriggerEventCompletion(); | |
7f0064bd | 1671 | |
263eeeab A |
1672 | }); |
1673 | dispatch_source_set_cancel_handler(source, ^{ | |
1674 | if (entryRef->fdClosed) | |
1675 | { | |
1676 | //LogMsg("CancelHandler: closing fd %d", fd); | |
1677 | close(fd); | |
1678 | } | |
1679 | }); | |
1680 | dispatch_resume(source); | |
1681 | if (filter == EVFILT_READ) | |
1682 | entryRef->readSource = source; | |
1683 | else | |
1684 | entryRef->writeSource = source; | |
1685 | ||
1686 | return 0; | |
1687 | } | |
7f0064bd | 1688 | |
263eeeab A |
1689 | mDNSexport void KQueueLock(mDNS *const m) |
1690 | { | |
1691 | (void)m; //unused | |
1692 | } | |
1693 | mDNSexport void KQueueUnlock(mDNS *const m, const char const *task) | |
1694 | { | |
1695 | (void)m; //unused | |
1696 | (void)task; //unused | |
1697 | } | |
1698 | #else | |
1699 | mDNSexport int KQueueSet(int fd, u_short flags, short filter, const KQueueEntry *const entryRef) | |
1700 | { | |
1701 | struct kevent new_event; | |
1702 | EV_SET(&new_event, fd, filter, flags, 0, 0, (void*)entryRef); | |
1703 | return (kevent(KQueueFD, &new_event, 1, NULL, 0, NULL) < 0) ? errno : 0; | |
1704 | } | |
7f0064bd | 1705 | |
263eeeab A |
1706 | mDNSexport void KQueueLock(mDNS *const m) |
1707 | { | |
1708 | pthread_mutex_lock(&m->p->BigMutex); | |
1709 | m->p->BigMutexStartTime = mDNSPlatformRawTime(); | |
1710 | } | |
7f0064bd | 1711 | |
263eeeab A |
1712 | mDNSexport void KQueueUnlock(mDNS *const m, const char const *task) |
1713 | { | |
1714 | mDNSs32 end = mDNSPlatformRawTime(); | |
1715 | (void)task; | |
1716 | if (end - m->p->BigMutexStartTime >= WatchDogReportingThreshold) | |
1717 | LogInfo("WARNING: %s took %dms to complete", task, end - m->p->BigMutexStartTime); | |
7f0064bd | 1718 | |
263eeeab | 1719 | pthread_mutex_unlock(&m->p->BigMutex); |
7f0064bd | 1720 | |
263eeeab A |
1721 | char wake = 1; |
1722 | if (send(m->p->WakeKQueueLoopFD, &wake, sizeof(wake), 0) == -1) | |
1723 | LogMsg("ERROR: KQueueWake: send failed with error code: %d (%s)", errno, strerror(errno)); | |
1724 | } | |
1725 | #endif | |
7f0064bd | 1726 | |
263eeeab A |
1727 | mDNSexport void mDNSPlatformCloseFD(KQueueEntry *kq, int fd) |
1728 | { | |
294beb6e | 1729 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
1730 | (void)fd; //unused |
1731 | if (kq->readSource) | |
1732 | { | |
1733 | dispatch_source_cancel(kq->readSource); | |
1734 | kq->readSource = mDNSNULL; | |
1735 | } | |
1736 | if (kq->writeSource) | |
1737 | { | |
1738 | dispatch_source_cancel(kq->writeSource); | |
1739 | kq->writeSource = mDNSNULL; | |
1740 | } | |
1741 | // Close happens in the cancellation handler | |
1742 | debugf("mDNSPlatformCloseFD: resetting sources for %d", fd); | |
1743 | kq->fdClosed = mDNStrue; | |
1744 | #else | |
1745 | (void)kq; //unused | |
1746 | close(fd); | |
1747 | #endif | |
1748 | } | |
7f0064bd | 1749 | |
263eeeab A |
1750 | mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort *port) |
1751 | { | |
1752 | KQSocketSet *cp = &sock->ss; | |
1753 | #ifndef NO_IPV6 | |
1754 | int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; | |
1755 | KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; | |
1756 | #else | |
1757 | int *s = &cp->sktv4; | |
1758 | KQueueEntry *k = &cp->kqsv4; | |
1759 | #endif | |
1760 | const int on = 1; // "on" for setsockopt | |
1761 | mStatus err; | |
7f0064bd | 1762 | |
263eeeab A |
1763 | int skt = socket(sa_family, SOCK_STREAM, IPPROTO_TCP); |
1764 | if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupTCPSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } | |
1765 | if (sa_family == AF_INET) | |
1766 | { | |
1767 | // Bind it | |
1768 | struct sockaddr_in addr; | |
1769 | mDNSPlatformMemZero(&addr, sizeof(addr)); | |
1770 | addr.sin_family = AF_INET; | |
1771 | addr.sin_port = port->NotAnInteger; | |
1772 | err = bind(skt, (struct sockaddr*) &addr, sizeof(addr)); | |
1773 | if (err < 0) { LogMsg("ERROR: bind %s", strerror(errno)); return err; } | |
1774 | ||
1775 | // Receive interface identifiers | |
1776 | err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); | |
1777 | if (err < 0) { LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); return err; } | |
1778 | ||
1779 | mDNSPlatformMemZero(&addr, sizeof(addr)); | |
1780 | socklen_t len = sizeof(addr); | |
1781 | err = getsockname(skt, (struct sockaddr*) &addr, &len); | |
1782 | if (err < 0) { LogMsg("getsockname - %s", strerror(errno)); return err; } | |
1783 | ||
1784 | port->NotAnInteger = addr.sin_port; | |
1785 | } | |
1786 | else | |
1787 | { | |
1788 | // Bind it | |
1789 | struct sockaddr_in6 addr6; | |
1790 | mDNSPlatformMemZero(&addr6, sizeof(addr6)); | |
1791 | addr6.sin6_family = AF_INET6; | |
1792 | addr6.sin6_port = port->NotAnInteger; | |
1793 | err = bind(skt, (struct sockaddr*) &addr6, sizeof(addr6)); | |
1794 | if (err < 0) { LogMsg("ERROR: bind6 %s", strerror(errno)); return err; } | |
7f0064bd | 1795 | |
263eeeab | 1796 | // We want to receive destination addresses and receive interface identifiers |
294beb6e A |
1797 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); |
1798 | if (err < 0) { LogMsg("ERROR: setsockopt IPV6_RECVPKTINFO %s", strerror(errno)); return err; } | |
7f0064bd | 1799 | |
263eeeab A |
1800 | mDNSPlatformMemZero(&addr6, sizeof(addr6)); |
1801 | socklen_t len = sizeof(addr6); | |
1802 | err = getsockname(skt, (struct sockaddr *) &addr6, &len); | |
1803 | if (err < 0) { LogMsg("getsockname6 - %s", strerror(errno)); return err; } | |
1804 | ||
1805 | port->NotAnInteger = addr6.sin6_port; | |
7f0064bd | 1806 | |
263eeeab A |
1807 | } |
1808 | *s = skt; | |
1809 | k->KQcallback = tcpKQSocketCallback; | |
1810 | k->KQcontext = sock; | |
1811 | k->KQtask = "mDNSPlatformTCPSocket"; | |
294beb6e | 1812 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
1813 | k->readSource = mDNSNULL; |
1814 | k->writeSource = mDNSNULL; | |
1815 | k->fdClosed = mDNSfalse; | |
1816 | #endif | |
1817 | return mStatus_NoError; | |
1818 | } | |
7f0064bd | 1819 | |
263eeeab A |
1820 | mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port) |
1821 | { | |
1822 | mStatus err; | |
1823 | (void) m; | |
7f0064bd | 1824 | |
263eeeab A |
1825 | TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPSocket", sizeof(TCPSocket)); |
1826 | if (!sock) { LogMsg("mDNSPlatformTCPSocket: memory allocation failure"); return(mDNSNULL); } | |
7f0064bd | 1827 | |
263eeeab | 1828 | mDNSPlatformMemZero(sock, sizeof(TCPSocket)); |
7f0064bd | 1829 | |
263eeeab A |
1830 | sock->ss.m = m; |
1831 | sock->ss.sktv4 = -1; | |
1832 | #ifndef NO_IPV6 | |
1833 | sock->ss.sktv6 = -1; | |
1834 | #endif | |
1835 | err = SetupTCPSocket(sock, AF_INET, port); | |
1836 | #ifndef NO_IPV6 | |
1837 | if (!err) | |
1838 | { | |
1839 | err = SetupTCPSocket(sock, AF_INET6, port); | |
1840 | if (err) { mDNSPlatformCloseFD(&sock->ss.kqsv4, sock->ss.sktv4); sock->ss.sktv4 = -1; } | |
1841 | } | |
1842 | #endif | |
1843 | if (err) | |
1844 | { | |
1845 | LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock->fd, errno, strerror(errno)); | |
1846 | freeL("TCPSocket/mDNSPlatformTCPSocket", sock); | |
1847 | return(mDNSNULL); | |
1848 | } | |
7f0064bd | 1849 | |
263eeeab A |
1850 | sock->callback = mDNSNULL; |
1851 | sock->flags = flags; | |
1852 | sock->context = mDNSNULL; | |
1853 | sock->setup = mDNSfalse; | |
1854 | sock->connected = mDNSfalse; | |
1855 | sock->handshake = handshake_required; | |
1856 | sock->m = m; | |
1857 | sock->err = mStatus_NoError; | |
1858 | ||
1859 | return sock; | |
1860 | } | |
7f0064bd | 1861 | |
263eeeab A |
1862 | mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context) |
1863 | { | |
1864 | KQSocketSet *cp = &sock->ss; | |
1865 | #ifndef NO_IPV6 | |
1866 | int *s = (dst->type == mDNSAddrType_IPv4) ? &cp->sktv4 : &cp->sktv6; | |
1867 | KQueueEntry *k = (dst->type == mDNSAddrType_IPv4) ? &cp->kqsv4 : &cp->kqsv6; | |
1868 | #else | |
1869 | int *s = &cp->sktv4; | |
1870 | KQueueEntry *k = &cp->kqsv4; | |
1871 | #endif | |
1872 | mStatus err = mStatus_NoError; | |
1873 | struct sockaddr_storage ss; | |
7f0064bd | 1874 | |
263eeeab A |
1875 | sock->callback = callback; |
1876 | sock->context = context; | |
1877 | sock->setup = mDNSfalse; | |
1878 | sock->connected = mDNSfalse; | |
1879 | sock->handshake = handshake_required; | |
1880 | sock->err = mStatus_NoError; | |
7f0064bd | 1881 | |
263eeeab | 1882 | if (hostname) { debugf("mDNSPlatformTCPConnect: hostname %##s", hostname->c); AssignDomainName(&sock->hostname, hostname); } |
7f0064bd | 1883 | |
263eeeab A |
1884 | if (dst->type == mDNSAddrType_IPv4) |
1885 | { | |
1886 | struct sockaddr_in *saddr = (struct sockaddr_in *)&ss; | |
1887 | mDNSPlatformMemZero(saddr, sizeof(*saddr)); | |
1888 | saddr->sin_family = AF_INET; | |
1889 | saddr->sin_port = dstport.NotAnInteger; | |
1890 | saddr->sin_len = sizeof(*saddr); | |
1891 | saddr->sin_addr.s_addr = dst->ip.v4.NotAnInteger; | |
1892 | } | |
1893 | else | |
1894 | { | |
1895 | struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&ss; | |
1896 | mDNSPlatformMemZero(saddr6, sizeof(*saddr6)); | |
1897 | saddr6->sin6_family = AF_INET6; | |
1898 | saddr6->sin6_port = dstport.NotAnInteger; | |
1899 | saddr6->sin6_len = sizeof(*saddr6); | |
1900 | saddr6->sin6_addr = *(struct in6_addr *)&dst->ip.v6; | |
1901 | } | |
7f0064bd | 1902 | |
263eeeab A |
1903 | // Watch for connect complete (write is ready) |
1904 | // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it in tcpKQSocketCallback using EV_DELETE | |
1905 | if (KQueueSet(*s, EV_ADD /* | EV_ONESHOT */, EVFILT_WRITE, k)) | |
1906 | { | |
1907 | LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed"); | |
1908 | return errno; | |
1909 | } | |
7f0064bd | 1910 | |
263eeeab A |
1911 | // Watch for incoming data |
1912 | if (KQueueSet(*s, EV_ADD, EVFILT_READ, k)) | |
1913 | { | |
1914 | LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed"); | |
1915 | return errno; | |
1916 | } | |
7f0064bd | 1917 | |
263eeeab A |
1918 | if (fcntl(*s, F_SETFL, fcntl(*s, F_GETFL, 0) | O_NONBLOCK) < 0) // set non-blocking |
1919 | { | |
1920 | LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno)); | |
1921 | return mStatus_UnknownErr; | |
1922 | } | |
7f0064bd | 1923 | |
263eeeab A |
1924 | // We bind to the interface and all subsequent packets including the SYN will be sent out |
1925 | // on this interface | |
1926 | // | |
1927 | // Note: If we are in Active Directory domain, we may try TCP (if the response can't fit in | |
1928 | // UDP). mDNSInterface_Unicast indicates this case and not a valid interface. | |
1929 | if (InterfaceID && InterfaceID != mDNSInterface_Unicast) | |
1930 | { | |
263eeeab A |
1931 | NetworkInterfaceInfoOSX *info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); |
1932 | if (dst->type == mDNSAddrType_IPv4) | |
1933 | { | |
1934 | #ifdef IP_BOUND_IF | |
1935 | if (info) setsockopt(*s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); | |
1936 | else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; } | |
1937 | #else | |
1938 | (void)InterfaceID; // Unused | |
1939 | (void)info; // Unused | |
1940 | #endif | |
1941 | } | |
1942 | else | |
1943 | { | |
1944 | #ifdef IPV6_BOUND_IF | |
1945 | if (info) setsockopt(*s, IPPROTO_IPV6, IPV6_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); | |
1946 | else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; } | |
1947 | #else | |
1948 | (void)InterfaceID; // Unused | |
1949 | (void)info; // Unused | |
1950 | #endif | |
1951 | } | |
1952 | } | |
7f0064bd | 1953 | |
263eeeab A |
1954 | // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address |
1955 | // from which we can infer the destination address family. Hence we need to remember that here. | |
1956 | // Instead of remembering the address family, we remember the right fd. | |
1957 | sock->fd = *s; | |
1958 | sock->kqEntry = k; | |
1959 | // initiate connection wth peer | |
1960 | if (connect(*s, (struct sockaddr *)&ss, ss.ss_len) < 0) | |
1961 | { | |
1962 | if (errno == EINPROGRESS) return mStatus_ConnPending; | |
1963 | if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN) | |
1964 | LogInfo("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s)", sock->fd, errno, strerror(errno)); | |
1965 | else | |
1966 | LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s) length %d", sock->fd, errno, strerror(errno), ss.ss_len); | |
1967 | return mStatus_ConnFailed; | |
1968 | } | |
7f0064bd | 1969 | |
263eeeab A |
1970 | LogMsg("NOTE: mDNSPlatformTCPConnect completed synchronously"); |
1971 | // kQueue should notify us, but this LogMsg is to help track down if it doesn't | |
1972 | return err; | |
1973 | } | |
7f0064bd | 1974 | |
263eeeab A |
1975 | // Why doesn't mDNSPlatformTCPAccept actually call accept() ? |
1976 | mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int fd) | |
1977 | { | |
1978 | mStatus err = mStatus_NoError; | |
7f0064bd | 1979 | |
263eeeab A |
1980 | TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPAccept", sizeof(TCPSocket)); |
1981 | if (!sock) return(mDNSNULL); | |
7f0064bd | 1982 | |
263eeeab A |
1983 | mDNSPlatformMemZero(sock, sizeof(*sock)); |
1984 | sock->fd = fd; | |
1985 | sock->flags = flags; | |
7f0064bd | 1986 | |
263eeeab A |
1987 | if (flags & kTCPSocketFlags_UseTLS) |
1988 | { | |
1989 | #ifndef NO_SECURITYFRAMEWORK | |
1990 | if (!ServerCerts) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err = mStatus_UnknownErr; goto exit; } | |
7f0064bd | 1991 | |
263eeeab A |
1992 | err = tlsSetupSock(sock, mDNStrue); |
1993 | if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err); goto exit; } | |
7f0064bd | 1994 | |
263eeeab A |
1995 | err = SSLSetCertificate(sock->tlsContext, ServerCerts); |
1996 | if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: SSLSetCertificate failed with error code: %d", err); goto exit; } | |
1997 | #else | |
1998 | err = mStatus_UnsupportedErr; | |
1999 | #endif /* NO_SECURITYFRAMEWORK */ | |
2000 | } | |
2001 | #ifndef NO_SECURITYFRAMEWORK | |
2002 | exit: | |
2003 | #endif | |
7f0064bd | 2004 | |
263eeeab A |
2005 | if (err) { freeL("TCPSocket/mDNSPlatformTCPAccept", sock); return(mDNSNULL); } |
2006 | return(sock); | |
2007 | } | |
7f0064bd | 2008 | |
263eeeab A |
2009 | mDNSlocal void CloseSocketSet(KQSocketSet *ss) |
2010 | { | |
2011 | if (ss->sktv4 != -1) | |
2012 | { | |
2013 | mDNSPlatformCloseFD(&ss->kqsv4, ss->sktv4); | |
2014 | ss->sktv4 = -1; | |
2015 | } | |
2016 | #ifndef NO_IPV6 | |
2017 | if (ss->sktv6 != -1) | |
2018 | { | |
2019 | mDNSPlatformCloseFD(&ss->kqsv6, ss->sktv6); | |
2020 | ss->sktv6 = -1; | |
2021 | } | |
2022 | #endif | |
2023 | if (ss->closeFlag) *ss->closeFlag = 1; | |
2024 | } | |
7f0064bd | 2025 | |
263eeeab A |
2026 | mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) |
2027 | { | |
2028 | if (sock) | |
2029 | { | |
2030 | #ifndef NO_SECURITYFRAMEWORK | |
2031 | if (sock->tlsContext) | |
2032 | { | |
2033 | if (sock->handshake == handshake_in_progress) // SSLHandshake thread using this sock (esp. tlsContext) | |
2034 | { | |
2035 | LogInfo("mDNSPlatformTCPCloseConnection: called while handshake in progress"); | |
294beb6e A |
2036 | // When we come back from SSLHandshake, we will notice that a close was here and |
2037 | // call this function again which will do the cleanup then. | |
263eeeab | 2038 | sock->handshake = handshake_to_be_closed; |
263eeeab | 2039 | return; |
294beb6e | 2040 | } |
7f0064bd | 2041 | |
263eeeab A |
2042 | SSLClose(sock->tlsContext); |
2043 | SSLDisposeContext(sock->tlsContext); | |
2044 | sock->tlsContext = NULL; | |
2045 | } | |
2046 | #endif /* NO_SECURITYFRAMEWORK */ | |
2047 | if (sock->ss.sktv4 != -1) shutdown(sock->ss.sktv4, 2); | |
2048 | #ifndef NO_IPV6 | |
2049 | if (sock->ss.sktv6 != -1) shutdown(sock->ss.sktv6, 2); | |
2050 | #endif | |
2051 | CloseSocketSet(&sock->ss); | |
2052 | sock->fd = -1; | |
7f0064bd | 2053 | |
263eeeab A |
2054 | freeL("TCPSocket/mDNSPlatformTCPCloseConnection", sock); |
2055 | } | |
2056 | } | |
7f0064bd | 2057 | |
263eeeab A |
2058 | mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed) |
2059 | { | |
2060 | size_t nread = 0; | |
2061 | *closed = mDNSfalse; | |
7f0064bd | 2062 | |
263eeeab A |
2063 | if (sock->flags & kTCPSocketFlags_UseTLS) |
2064 | { | |
2065 | #ifndef NO_SECURITYFRAMEWORK | |
2066 | if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformReadTCP called while handshake required"); return 0; } | |
2067 | else if (sock->handshake == handshake_in_progress) return 0; | |
2068 | else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock->handshake); | |
7f0064bd | 2069 | |
263eeeab A |
2070 | //LogMsg("Starting SSLRead %d %X", sock->fd, fcntl(sock->fd, F_GETFL, 0)); |
2071 | mStatus err = SSLRead(sock->tlsContext, buf, buflen, &nread); | |
2072 | //LogMsg("SSLRead returned %d (%d) nread %d buflen %d", err, errSSLWouldBlock, nread, buflen); | |
2073 | if (err == errSSLClosedGraceful) { nread = 0; *closed = mDNStrue; } | |
2074 | else if (err && err != errSSLWouldBlock) | |
2075 | { LogMsg("ERROR: mDNSPlatformReadTCP - SSLRead: %d", err); nread = -1; *closed = mDNStrue; } | |
2076 | #else | |
2077 | nread = -1; | |
2078 | *closed = mDNStrue; | |
2079 | #endif /* NO_SECURITYFRAMEWORK */ | |
2080 | } | |
2081 | else | |
2082 | { | |
2083 | static int CLOSEDcount = 0; | |
2084 | static int EAGAINcount = 0; | |
2085 | nread = recv(sock->fd, buf, buflen, 0); | |
7f0064bd | 2086 | |
263eeeab A |
2087 | if (nread > 0) { CLOSEDcount = 0; EAGAINcount = 0; } // On success, clear our error counters |
2088 | else if (nread == 0) | |
2089 | { | |
2090 | *closed = mDNStrue; | |
2091 | if ((++CLOSEDcount % 1000) == 0) { LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got CLOSED %d times", sock->fd, CLOSEDcount); sleep(1); } | |
2092 | } | |
2093 | // else nread is negative -- see what kind of error we got | |
2094 | else if (errno == ECONNRESET) { nread = 0; *closed = mDNStrue; } | |
2095 | else if (errno != EAGAIN) { LogMsg("ERROR: mDNSPlatformReadTCP - recv: %d (%s)", errno, strerror(errno)); nread = -1; } | |
2096 | else // errno is EAGAIN (EWOULDBLOCK) -- no data available | |
2097 | { | |
2098 | nread = 0; | |
2099 | if ((++EAGAINcount % 1000) == 0) { LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got EAGAIN %d times", sock->fd, EAGAINcount); sleep(1); } | |
2100 | } | |
2101 | } | |
7f0064bd | 2102 | |
263eeeab A |
2103 | return nread; |
2104 | } | |
7f0064bd | 2105 | |
263eeeab A |
2106 | mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len) |
2107 | { | |
2108 | int nsent; | |
7f0064bd | 2109 | |
263eeeab A |
2110 | if (sock->flags & kTCPSocketFlags_UseTLS) |
2111 | { | |
2112 | #ifndef NO_SECURITYFRAMEWORK | |
2113 | size_t processed; | |
2114 | if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformWriteTCP called while handshake required"); return 0; } | |
2115 | if (sock->handshake == handshake_in_progress) return 0; | |
2116 | else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformWriteTCP called with unexpected SSLHandshake status: %d", sock->handshake); | |
2117 | ||
2118 | mStatus err = SSLWrite(sock->tlsContext, msg, len, &processed); | |
c9b9ae52 | 2119 | |
263eeeab A |
2120 | if (!err) nsent = (int) processed; |
2121 | else if (err == errSSLWouldBlock) nsent = 0; | |
2122 | else { LogMsg("ERROR: mDNSPlatformWriteTCP - SSLWrite returned %d", err); nsent = -1; } | |
2123 | #else | |
2124 | nsent = -1; | |
2125 | #endif /* NO_SECURITYFRAMEWORK */ | |
2126 | } | |
2127 | else | |
2128 | { | |
2129 | nsent = send(sock->fd, msg, len, 0); | |
2130 | if (nsent < 0) | |
2131 | { | |
2132 | if (errno == EAGAIN) nsent = 0; | |
2133 | else { LogMsg("ERROR: mDNSPlatformWriteTCP - send %s", strerror(errno)); nsent = -1; } | |
2134 | } | |
2135 | } | |
c9b9ae52 | 2136 | |
263eeeab A |
2137 | return nsent; |
2138 | } | |
c9b9ae52 | 2139 | |
263eeeab A |
2140 | mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) |
2141 | { | |
2142 | return sock->fd; | |
2143 | } | |
c9b9ae52 | 2144 | |
263eeeab A |
2145 | // If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface |
2146 | // If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries | |
2147 | mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa_family, mDNSIPPort *const outport) | |
2148 | { | |
2149 | #ifndef NO_IPV6 | |
2150 | int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; | |
2151 | KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; | |
2152 | #else | |
2153 | int *s = &cp->sktv4; | |
2154 | KQueueEntry *k = &cp->kqsv4; | |
2155 | #endif | |
2156 | const int on = 1; | |
2157 | const int twofivefive = 255; | |
2158 | mStatus err = mStatus_NoError; | |
2159 | char *errstr = mDNSNULL; | |
c9b9ae52 | 2160 | |
263eeeab A |
2161 | #ifdef NO_IPV6 |
2162 | if (sa_family != AF_INET) return -1; | |
2163 | #endif | |
2164 | ||
2165 | cp->closeFlag = mDNSNULL; | |
c9b9ae52 | 2166 | |
263eeeab A |
2167 | int skt = socket(sa_family, SOCK_DGRAM, IPPROTO_UDP); |
2168 | if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } | |
c9b9ae52 | 2169 | |
263eeeab A |
2170 | // ... with a shared UDP port, if it's for multicast receiving |
2171 | if (mDNSSameIPPort(port, MulticastDNSPort) || mDNSSameIPPort(port, NATPMPAnnouncementPort)) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); | |
2172 | if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; } | |
c9b9ae52 | 2173 | |
263eeeab A |
2174 | if (sa_family == AF_INET) |
2175 | { | |
2176 | // We want to receive destination addresses | |
2177 | err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); | |
2178 | if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; } | |
c9b9ae52 | 2179 | |
263eeeab A |
2180 | // We want to receive interface identifiers |
2181 | err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); | |
2182 | if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; } | |
c9b9ae52 | 2183 | |
263eeeab A |
2184 | // We want to receive packet TTL value so we can check it |
2185 | err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); | |
2186 | // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it | |
c9b9ae52 | 2187 | |
263eeeab A |
2188 | // Send unicast packets with TTL 255 |
2189 | err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); | |
2190 | if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; } | |
c9b9ae52 | 2191 | |
263eeeab A |
2192 | // And multicast packets with TTL 255 too |
2193 | err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); | |
2194 | if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; } | |
c9b9ae52 | 2195 | |
263eeeab A |
2196 | // And start listening for packets |
2197 | struct sockaddr_in listening_sockaddr; | |
2198 | listening_sockaddr.sin_family = AF_INET; | |
2199 | listening_sockaddr.sin_port = port.NotAnInteger; // Pass in opaque ID without any byte swapping | |
2200 | listening_sockaddr.sin_addr.s_addr = mDNSSameIPPort(port, NATPMPAnnouncementPort) ? AllHosts_v4.NotAnInteger : 0; | |
2201 | err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); | |
2202 | if (err) { errstr = "bind"; goto fail; } | |
2203 | if (outport) outport->NotAnInteger = listening_sockaddr.sin_port; | |
2204 | } | |
2205 | #ifndef NO_IPV6 | |
2206 | else if (sa_family == AF_INET6) | |
2207 | { | |
2208 | // NAT-PMP Announcements make no sense on IPv6, so bail early w/o error | |
2209 | if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort; return mStatus_NoError; } | |
2210 | ||
2211 | // We want to receive destination addresses and receive interface identifiers | |
294beb6e A |
2212 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); |
2213 | if (err < 0) { errstr = "setsockopt - IPV6_RECVPKTINFO"; goto fail; } | |
c9b9ae52 | 2214 | |
263eeeab | 2215 | // We want to receive packet hop count value so we can check it |
294beb6e A |
2216 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); |
2217 | if (err < 0) { errstr = "setsockopt - IPV6_RECVHOPLIMIT"; goto fail; } | |
c9b9ae52 | 2218 | |
263eeeab A |
2219 | // We want to receive only IPv6 packets. Without this option we get IPv4 packets too, |
2220 | // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address | |
2221 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); | |
2222 | if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; } | |
c9b9ae52 | 2223 | |
263eeeab A |
2224 | // Send unicast packets with TTL 255 |
2225 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive)); | |
2226 | if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; } | |
c9b9ae52 | 2227 | |
263eeeab A |
2228 | // And multicast packets with TTL 255 too |
2229 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive)); | |
2230 | if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; } | |
c9b9ae52 | 2231 | |
263eeeab A |
2232 | // Want to receive our own packets |
2233 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); | |
2234 | if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; } | |
c9b9ae52 | 2235 | |
263eeeab A |
2236 | // And start listening for packets |
2237 | struct sockaddr_in6 listening_sockaddr6; | |
2238 | mDNSPlatformMemZero(&listening_sockaddr6, sizeof(listening_sockaddr6)); | |
2239 | listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6); | |
2240 | listening_sockaddr6.sin6_family = AF_INET6; | |
2241 | listening_sockaddr6.sin6_port = port.NotAnInteger; // Pass in opaque ID without any byte swapping | |
2242 | listening_sockaddr6.sin6_flowinfo = 0; | |
2243 | listening_sockaddr6.sin6_addr = in6addr_any; // Want to receive multicasts AND unicasts on this socket | |
2244 | listening_sockaddr6.sin6_scope_id = 0; | |
2245 | err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6)); | |
2246 | if (err) { errstr = "bind"; goto fail; } | |
2247 | if (outport) outport->NotAnInteger = listening_sockaddr6.sin6_port; | |
2248 | } | |
2249 | #endif | |
67c8f8a1 | 2250 | |
263eeeab A |
2251 | fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking |
2252 | fcntl(skt, F_SETFD, 1); // set close-on-exec | |
2253 | *s = skt; | |
2254 | k->KQcallback = myKQSocketCallBack; | |
2255 | k->KQcontext = cp; | |
2256 | k->KQtask = "UDP packet reception"; | |
294beb6e | 2257 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
2258 | k->readSource = mDNSNULL; |
2259 | k->writeSource = mDNSNULL; | |
2260 | k->fdClosed = mDNSfalse; | |
2261 | #endif | |
2262 | KQueueSet(*s, EV_ADD, EVFILT_READ, k); | |
67c8f8a1 | 2263 | |
263eeeab | 2264 | return(err); |
c9b9ae52 | 2265 | |
263eeeab A |
2266 | fail: |
2267 | // For "bind" failures, only write log messages for our shared mDNS port, or for binding to zero | |
2268 | if (strcmp(errstr, "bind") || mDNSSameIPPort(port, MulticastDNSPort) || mDNSIPPortIsZero(port)) | |
2269 | LogMsg("%s skt %d port %d error %d errno %d (%s)", errstr, skt, mDNSVal16(port), err, errno, strerror(errno)); | |
283ee3ff | 2270 | |
263eeeab A |
2271 | // If we got a "bind" failure of EADDRINUSE, inform the caller as it might need to try another random port |
2272 | if (!strcmp(errstr, "bind") && errno == EADDRINUSE) | |
2273 | { | |
2274 | err = EADDRINUSE; | |
2275 | if (mDNSSameIPPort(port, MulticastDNSPort)) | |
2276 | NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed", | |
2277 | "Congratulations, you've reproduced an elusive bug.\r" | |
2278 | "Please contact the current assignee of <rdar://problem/3814904>.\r" | |
2279 | "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r" | |
2280 | "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); | |
2281 | } | |
283ee3ff | 2282 | |
263eeeab | 2283 | mDNSPlatformCloseFD(k, skt); |
67c8f8a1 A |
2284 | return(err); |
2285 | } | |
7f0064bd | 2286 | |
263eeeab | 2287 | mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport) |
32bb7e43 | 2288 | { |
263eeeab A |
2289 | mStatus err; |
2290 | mDNSIPPort port = requestedport; | |
2291 | mDNSBool randomizePort = mDNSIPPortIsZero(requestedport); | |
2292 | int i = 10000; // Try at most 10000 times to get a unique random port | |
2293 | UDPSocket *p = mallocL("UDPSocket", sizeof(UDPSocket)); | |
2294 | if (!p) { LogMsg("mDNSPlatformUDPSocket: memory exhausted"); return(mDNSNULL); } | |
2295 | mDNSPlatformMemZero(p, sizeof(UDPSocket)); | |
2296 | p->ss.port = zeroIPPort; | |
2297 | p->ss.m = m; | |
2298 | p->ss.sktv4 = -1; | |
2299 | #ifndef NO_IPV6 | |
2300 | p->ss.sktv6 = -1; | |
2301 | #endif | |
32bb7e43 | 2302 | |
263eeeab | 2303 | do |
32bb7e43 | 2304 | { |
263eeeab A |
2305 | // The kernel doesn't do cryptographically strong random port allocation, so we do it ourselves here |
2306 | if (randomizePort) port = mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF)); | |
2307 | err = SetupSocket(&p->ss, port, AF_INET, &p->ss.port); | |
2308 | #ifndef NO_IPV6 | |
2309 | if (!err) | |
32bb7e43 | 2310 | { |
263eeeab A |
2311 | err = SetupSocket(&p->ss, port, AF_INET6, &p->ss.port); |
2312 | if (err) { mDNSPlatformCloseFD(&p->ss.kqsv4, p->ss.sktv4); p->ss.sktv4 = -1; } | |
32bb7e43 | 2313 | } |
263eeeab A |
2314 | #endif |
2315 | i--; | |
2316 | } while (err == EADDRINUSE && randomizePort && i); | |
32bb7e43 | 2317 | |
32bb7e43 A |
2318 | if (err) |
2319 | { | |
263eeeab A |
2320 | // In customer builds we don't want to log failures with port 5351, because this is a known issue |
2321 | // of failing to bind to this port when Internet Sharing has already bound to it | |
2322 | // We also don't want to log about port 5350, due to a known bug when some other | |
2323 | // process is bound to it. | |
2324 | if (mDNSSameIPPort(requestedport, NATPMPPort) || mDNSSameIPPort(requestedport, NATPMPAnnouncementPort)) | |
2325 | LogInfo("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno)); | |
2326 | else LogMsg("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno)); | |
2327 | freeL("UDPSocket", p); | |
2328 | return(mDNSNULL); | |
32bb7e43 | 2329 | } |
263eeeab | 2330 | return(p); |
32bb7e43 A |
2331 | } |
2332 | ||
263eeeab | 2333 | mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) |
67c8f8a1 | 2334 | { |
263eeeab A |
2335 | CloseSocketSet(&sock->ss); |
2336 | freeL("UDPSocket", sock); | |
67c8f8a1 | 2337 | } |
8e92c31c | 2338 | |
263eeeab A |
2339 | #if COMPILER_LIKES_PRAGMA_MARK |
2340 | #pragma mark - | |
2341 | #pragma mark - BPF Raw packet sending/receiving | |
2342 | #endif | |
67c8f8a1 | 2343 | |
263eeeab | 2344 | #if APPLE_OSX_mDNSResponder |
67c8f8a1 | 2345 | |
263eeeab A |
2346 | mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) |
2347 | { | |
2348 | if (!InterfaceID) { LogMsg("mDNSPlatformSendRawPacket: No InterfaceID specified"); return; } | |
2349 | NetworkInterfaceInfoOSX *info; | |
67c8f8a1 | 2350 | |
263eeeab A |
2351 | info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); |
2352 | if (info == NULL) | |
8e92c31c | 2353 | { |
263eeeab A |
2354 | LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); |
2355 | return; | |
2356 | } | |
2357 | if (info->BPF_fd < 0) | |
2358 | LogMsg("mDNSPlatformSendRawPacket: %s BPF_fd %d not ready", info->ifinfo.ifname, info->BPF_fd); | |
2359 | else | |
2360 | { | |
2361 | //LogMsg("mDNSPlatformSendRawPacket %d bytes on %s", end - (mDNSu8 *)msg, info->ifinfo.ifname); | |
2362 | if (write(info->BPF_fd, msg, end - (mDNSu8 *)msg) < 0) | |
2363 | LogMsg("mDNSPlatformSendRawPacket: BPF write(%d) failed %d (%s)", info->BPF_fd, errno, strerror(errno)); | |
8e92c31c | 2364 | } |
8e92c31c A |
2365 | } |
2366 | ||
263eeeab | 2367 | mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) |
8e92c31c | 2368 | { |
263eeeab A |
2369 | if (!InterfaceID) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: No InterfaceID specified"); return; } |
2370 | NetworkInterfaceInfoOSX *info; | |
2371 | info = IfindexToInterfaceInfoOSX(m, InterfaceID); | |
2372 | if (info == NULL) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID); return; } | |
2373 | // Manually inject an entry into our local ARP cache. | |
2374 | // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.) | |
2375 | if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa)) | |
2376 | LogSPS("Don't need address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); | |
2377 | else | |
2378 | { | |
2379 | int result = mDNSSetLocalAddressCacheEntry(info->scope_id, tpa->type, tpa->ip.v6.b, tha->b); | |
2380 | if (result) LogMsg("Set local address cache entry for %s %#a %.6a failed: %d", info->ifinfo.ifname, tpa, tha, result); | |
2381 | else LogSPS("Set local address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); | |
2382 | } | |
67c8f8a1 | 2383 | } |
8e92c31c | 2384 | |
263eeeab | 2385 | mDNSlocal void CloseBPF(NetworkInterfaceInfoOSX *const i) |
67c8f8a1 | 2386 | { |
263eeeab | 2387 | LogSPS("%s closing BPF fd %d", i->ifinfo.ifname, i->BPF_fd); |
294beb6e | 2388 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
2389 | // close will happen in the cancel handler |
2390 | dispatch_source_cancel(i->BPF_source); | |
2391 | #else | |
2392 | ||
2393 | // Note: MUST NOT close() the underlying native BSD sockets. | |
2394 | // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately, because | |
2395 | // it first has to unhook the sockets from its select() call on its other thread, before it can safely close them. | |
2396 | CFRunLoopRemoveSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode); | |
2397 | CFRelease(i->BPF_rls); | |
2398 | CFSocketInvalidate(i->BPF_cfs); | |
2399 | CFRelease(i->BPF_cfs); | |
2400 | #endif | |
2401 | i->BPF_fd = -1; | |
2402 | if (i->BPF_mcfd >= 0) { close(i->BPF_mcfd); i->BPF_mcfd = -1; } | |
67c8f8a1 | 2403 | } |
7f0064bd | 2404 | |
263eeeab | 2405 | mDNSlocal void bpf_callback_common(NetworkInterfaceInfoOSX *info) |
67c8f8a1 | 2406 | { |
263eeeab | 2407 | KQueueLock(info->m); |
7f0064bd | 2408 | |
263eeeab A |
2409 | // 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 |
2410 | // kernel has a mechanism for dispatching all events to a single thread, but for now we have to guard against this race condition). | |
2411 | if (info->BPF_fd < 0) goto exit; | |
8e92c31c | 2412 | |
263eeeab A |
2413 | ssize_t n = read(info->BPF_fd, &info->m->imsg, info->BPF_len); |
2414 | const mDNSu8 *ptr = (const mDNSu8 *)&info->m->imsg; | |
2415 | const mDNSu8 *end = (const mDNSu8 *)&info->m->imsg + n; | |
2416 | debugf("%3d: bpf_callback got %d bytes on %s", info->BPF_fd, n, info->ifinfo.ifname); | |
67c8f8a1 | 2417 | |
263eeeab A |
2418 | if (n<0) |
2419 | { | |
2420 | LogMsg("Closing %s BPF fd %d due to error %d (%s)", info->ifinfo.ifname, info->BPF_fd, errno, strerror(errno)); | |
2421 | CloseBPF(info); | |
2422 | goto exit; | |
2423 | } | |
67c8f8a1 | 2424 | |
263eeeab | 2425 | while (ptr < end) |
8e92c31c | 2426 | { |
263eeeab A |
2427 | const struct bpf_hdr *const bh = (const struct bpf_hdr *)ptr; |
2428 | debugf("%3d: bpf_callback ptr %p bh_hdrlen %d data %p bh_caplen %4d bh_datalen %4d next %p remaining %4d", | |
2429 | info->BPF_fd, ptr, bh->bh_hdrlen, ptr + bh->bh_hdrlen, bh->bh_caplen, bh->bh_datalen, | |
2430 | ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen), end - (ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen))); | |
2431 | // Note that BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header. | |
2432 | // Given that An Ethernet header is 14 bytes, this means that if the network layer header (e.g. IP header, | |
2433 | // ARP message, etc.) is 4-byte aligned, then necessarily the Ethernet header will be NOT be 4-byte aligned. | |
2434 | mDNSCoreReceiveRawPacket(info->m, ptr + bh->bh_hdrlen, ptr + bh->bh_hdrlen + bh->bh_caplen, info->ifinfo.InterfaceID); | |
2435 | ptr += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen); | |
8e92c31c | 2436 | } |
263eeeab A |
2437 | exit: |
2438 | KQueueUnlock(info->m, "bpf_callback"); | |
2439 | } | |
294beb6e | 2440 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
2441 | mDNSlocal void bpf_callback_dispatch(NetworkInterfaceInfoOSX *const info) |
2442 | { | |
2443 | bpf_callback_common(info); | |
2444 | } | |
2445 | #else | |
2446 | mDNSlocal void bpf_callback(const CFSocketRef cfs, const CFSocketCallBackType CallBackType, const CFDataRef address, const void *const data, void *const context) | |
2447 | { | |
2448 | (void)cfs; | |
2449 | (void)CallBackType; | |
2450 | (void)address; | |
2451 | (void)data; | |
2452 | bpf_callback_common((NetworkInterfaceInfoOSX *)context); | |
2453 | } | |
2454 | #endif | |
67c8f8a1 | 2455 | |
263eeeab | 2456 | #define BPF_SetOffset(from, cond, to) (from)->cond = (to) - 1 - (from) |
67c8f8a1 | 2457 | |
263eeeab A |
2458 | mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int *p4, int *p6) |
2459 | { | |
2460 | int numv4 = 0, numv6 = 0; | |
2461 | AuthRecord *rr; | |
67c8f8a1 | 2462 | |
263eeeab A |
2463 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
2464 | if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) | |
2465 | { | |
2466 | if (p4) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.4a", x->BPF_fd, x->ifinfo.ifname, numv4, &rr->AddressProxy.ip.v4); | |
2467 | numv4++; | |
2468 | } | |
67c8f8a1 | 2469 | |
263eeeab A |
2470 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
2471 | if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6) | |
2472 | { | |
2473 | if (p6) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.16a", x->BPF_fd, x->ifinfo.ifname, numv6, &rr->AddressProxy.ip.v6); | |
2474 | numv6++; | |
2475 | } | |
67c8f8a1 | 2476 | |
263eeeab A |
2477 | if (p4) *p4 = numv4; |
2478 | if (p6) *p6 = numv6; | |
2479 | return(numv4 + numv6); | |
8e92c31c A |
2480 | } |
2481 | ||
263eeeab | 2482 | mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) |
8e92c31c | 2483 | { |
263eeeab | 2484 | NetworkInterfaceInfoOSX *x; |
67c8f8a1 | 2485 | |
263eeeab A |
2486 | // Note: We can't use IfIndexToInterfaceInfoOSX because that looks for Registered also. |
2487 | for (x = m->p->InterfaceList; x; x = x->next) if (x->ifinfo.InterfaceID == InterfaceID) break; | |
8e92c31c | 2488 | |
263eeeab A |
2489 | if (!x) { LogMsg("mDNSPlatformUpdateProxyList: ERROR InterfaceID %p not found", InterfaceID); return; } |
2490 | ||
2491 | #define MAX_BPF_ADDRS 250 | |
2492 | int numv4 = 0, numv6 = 0; | |
67c8f8a1 | 2493 | |
263eeeab | 2494 | if (CountProxyTargets(m, x, &numv4, &numv6) > MAX_BPF_ADDRS) |
8e92c31c | 2495 | { |
263eeeab A |
2496 | LogMsg("mDNSPlatformUpdateProxyList: ERROR Too many address proxy records v4 %d v6 %d", numv4, numv6); |
2497 | if (numv4 > MAX_BPF_ADDRS) numv4 = MAX_BPF_ADDRS; | |
2498 | numv6 = MAX_BPF_ADDRS - numv4; | |
8e92c31c | 2499 | } |
c9b9ae52 | 2500 | |
263eeeab | 2501 | LogSPS("mDNSPlatformUpdateProxyList: fd %d %-7s MAC %.6a %d v4 %d v6", x->BPF_fd, x->ifinfo.ifname, &x->ifinfo.MAC, numv4, numv6); |
67c8f8a1 | 2502 | |
263eeeab A |
2503 | // Caution: This is a static structure, so we need to be careful that any modifications we make to it |
2504 | // are done in such a way that they work correctly when mDNSPlatformUpdateProxyList is called multiple times | |
2505 | static struct bpf_insn filter[17 + MAX_BPF_ADDRS] = | |
67c8f8a1 | 2506 | { |
263eeeab | 2507 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), // 0 Read Ethertype (bytes 12,13) |
67c8f8a1 | 2508 | |
263eeeab A |
2509 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0806, 0, 1), // 1 If Ethertype == ARP goto next, else 3 |
2510 | BPF_STMT(BPF_RET + BPF_K, 42), // 2 Return 42-byte ARP | |
67c8f8a1 | 2511 | |
263eeeab | 2512 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0800, 4, 0), // 3 If Ethertype == IPv4 goto 8 (IPv4 address list check) else next |
67c8f8a1 | 2513 | |
263eeeab A |
2514 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x86DD, 0, 9), // 4 If Ethertype == IPv6 goto next, else exit |
2515 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), // 5 Read Protocol and Hop Limit (bytes 20,21) | |
2516 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x3AFF, 0, 9), // 6 If (Prot,TTL) == (3A,FF) goto next, else IPv6 address list check | |
2517 | BPF_STMT(BPF_RET + BPF_K, 86), // 7 Return 86-byte ND | |
67c8f8a1 | 2518 | |
263eeeab A |
2519 | // Is IPv4 packet; check if it's addressed to any IPv4 address we're proxying for |
2520 | BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 30), // 8 Read IPv4 Dst (bytes 30,31,32,33) | |
2521 | }; | |
c9b9ae52 | 2522 | |
263eeeab A |
2523 | struct bpf_insn *pc = &filter[9]; |
2524 | struct bpf_insn *chk6 = pc + numv4 + 1; // numv4 address checks, plus a "return 0" | |
2525 | struct bpf_insn *fail = chk6 + 1 + numv6; // Get v6 Dst LSW, plus numv6 address checks | |
2526 | struct bpf_insn *ret4 = fail + 1; | |
2527 | struct bpf_insn *ret6 = ret4 + 4; | |
67c8f8a1 | 2528 | |
263eeeab | 2529 | static const struct bpf_insn rf = BPF_STMT(BPF_RET + BPF_K, 0); // No match: Return nothing |
67c8f8a1 | 2530 | |
263eeeab | 2531 | static const struct bpf_insn g6 = BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 50); // Read IPv6 Dst LSW (bytes 50,51,52,53) |
67c8f8a1 | 2532 | |
263eeeab A |
2533 | static const struct bpf_insn r4a = BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14); // Get IP Header length (normally 20) |
2534 | 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) | |
2535 | static const struct bpf_insn r4c = BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0); // A += IP Header length | |
2536 | static const struct bpf_insn r4d = BPF_STMT(BPF_RET + BPF_A, 0); // Success: Return Ethernet + IP + TCP + 20 bytes spare (normally 74) | |
67c8f8a1 | 2537 | |
263eeeab | 2538 | static const struct bpf_insn r6a = BPF_STMT(BPF_RET + BPF_K, 94); // Success: Return Eth + IPv6 + TCP + 20 bytes spare |
67c8f8a1 | 2539 | |
263eeeab A |
2540 | BPF_SetOffset(&filter[4], jf, fail); // If Ethertype not ARP, IPv4, or IPv6, fail |
2541 | BPF_SetOffset(&filter[6], jf, chk6); // If IPv6 but not ICMPv6, go to IPv6 address list check | |
67c8f8a1 | 2542 | |
263eeeab A |
2543 | // BPF Byte-Order Note |
2544 | // The BPF API designers apparently thought that programmers would not be smart enough to use htons | |
2545 | // and htonl correctly to convert numeric values to network byte order on little-endian machines, | |
2546 | // so instead they chose to make the API implicitly byte-swap *ALL* values, even literal byte strings | |
2547 | // that shouldn't be byte-swapped, like ASCII text, Ethernet addresses, IP addresses, etc. | |
2548 | // As a result, if we put Ethernet addresses and IP addresses in the right byte order, the BPF API | |
2549 | // will byte-swap and make them backwards, and then our filter won't work. So, we have to arrange | |
2550 | // that on little-endian machines we deliberately put addresses in memory with the bytes backwards, | |
2551 | // so that when the BPF API goes through and swaps them all, they end up back as they should be. | |
2552 | // In summary, if we byte-swap all the non-numeric fields that shouldn't be swapped, and we *don't* | |
2553 | // swap any of the numeric values that *should* be byte-swapped, then the filter will work correctly. | |
c9b9ae52 | 2554 | |
263eeeab A |
2555 | // IPSEC capture size notes: |
2556 | // 8 bytes UDP header | |
2557 | // 4 bytes Non-ESP Marker | |
2558 | // 28 bytes IKE Header | |
2559 | // -- | |
2560 | // 40 Total. Capturing TCP Header + 20 gets us enough bytes to receive the IKE Header in a UDP-encapsulated IKE packet. | |
32bb7e43 | 2561 | |
263eeeab A |
2562 | AuthRecord *rr; |
2563 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
2564 | if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) | |
2565 | { | |
2566 | mDNSv4Addr a = rr->AddressProxy.ip.v4; | |
2567 | pc->code = BPF_JMP + BPF_JEQ + BPF_K; | |
2568 | BPF_SetOffset(pc, jt, ret4); | |
2569 | pc->jf = 0; | |
2570 | 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]; | |
2571 | pc++; | |
67c8f8a1 | 2572 | } |
263eeeab A |
2573 | *pc++ = rf; |
2574 | ||
2575 | if (pc != chk6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != chk6 %p", pc, chk6); | |
2576 | *pc++ = g6; // chk6 points here | |
2577 | ||
2578 | // First cancel any previous ND group memberships we had, then create a fresh socket | |
2579 | if (x->BPF_mcfd >= 0) close(x->BPF_mcfd); | |
2580 | x->BPF_mcfd = socket(AF_INET6, SOCK_DGRAM, 0); | |
2581 | ||
2582 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
2583 | if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6) | |
67c8f8a1 | 2584 | { |
263eeeab A |
2585 | const mDNSv6Addr *const a = &rr->AddressProxy.ip.v6; |
2586 | pc->code = BPF_JMP + BPF_JEQ + BPF_K; | |
2587 | BPF_SetOffset(pc, jt, ret6); | |
2588 | pc->jf = 0; | |
2589 | 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]; | |
2590 | pc++; | |
2591 | ||
2592 | struct ipv6_mreq i6mr; | |
2593 | i6mr.ipv6mr_interface = x->scope_id; | |
2594 | i6mr.ipv6mr_multiaddr = *(const struct in6_addr*)&NDP_prefix; | |
2595 | i6mr.ipv6mr_multiaddr.s6_addr[0xD] = a->b[0xD]; | |
2596 | i6mr.ipv6mr_multiaddr.s6_addr[0xE] = a->b[0xE]; | |
2597 | i6mr.ipv6mr_multiaddr.s6_addr[0xF] = a->b[0xF]; | |
2598 | ||
2599 | // Do precautionary IPV6_LEAVE_GROUP first, necessary to clear stale kernel state | |
2600 | mStatus err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr)); | |
2601 | if (err < 0 && (errno != EADDRNOTAVAIL)) | |
2602 | LogMsg("mDNSPlatformUpdateProxyList: IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); | |
2603 | ||
2604 | err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); | |
2605 | if (err < 0 && (errno != EADDRINUSE)) // Joining same group twice can give "Address already in use" error -- no need to report that | |
2606 | LogMsg("mDNSPlatformUpdateProxyList: IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); | |
2607 | ||
2608 | LogSPS("Joined IPv6 ND multicast group %.16a for %.16a", &i6mr.ipv6mr_multiaddr, a); | |
67c8f8a1 A |
2609 | } |
2610 | ||
263eeeab A |
2611 | if (pc != fail) LogMsg("mDNSPlatformUpdateProxyList: pc %p != fail %p", pc, fail); |
2612 | *pc++ = rf; // fail points here | |
283ee3ff | 2613 | |
263eeeab A |
2614 | if (pc != ret4) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret4 %p", pc, ret4); |
2615 | *pc++ = r4a; // ret4 points here | |
2616 | *pc++ = r4b; | |
2617 | *pc++ = r4c; | |
2618 | *pc++ = r4d; | |
7f0064bd | 2619 | |
263eeeab A |
2620 | if (pc != ret6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret6 %p", pc, ret6); |
2621 | *pc++ = r6a; // ret6 points here | |
283ee3ff | 2622 | |
263eeeab A |
2623 | struct bpf_program prog = { pc - filter, filter }; |
2624 | ||
2625 | #if 0 | |
2626 | // For debugging BPF filter program | |
2627 | unsigned int q; | |
2628 | for (q=0; q<prog.bf_len; q++) | |
2629 | 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); | |
2630 | #endif | |
2631 | ||
2632 | if (!numv4 && !numv6) | |
2633 | { | |
2634 | LogSPS("mDNSPlatformUpdateProxyList: No need for filter"); | |
2635 | if (m->timenow == 0) LogMsg("mDNSPlatformUpdateProxyList: m->timenow == 0"); | |
2636 | // Schedule check to see if we can close this BPF_fd now | |
2637 | if (!m->p->NetworkChanged) m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); | |
2638 | // prog.bf_len = 0; This seems to panic the kernel | |
2639 | if (x->BPF_fd < 0) return; // If we've already closed our BPF_fd, no need to generate an error message below | |
67c8f8a1 | 2640 | } |
263eeeab A |
2641 | |
2642 | if (ioctl(x->BPF_fd, BIOCSETF, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETF(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno)); | |
2643 | else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETF(%d) successful", prog.bf_len); | |
2644 | } | |
2645 | ||
2646 | mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) | |
2647 | { | |
2648 | mDNS_Lock(m); | |
2649 | ||
2650 | NetworkInterfaceInfoOSX *i; | |
2651 | for (i = m->p->InterfaceList; i; i = i->next) if (i->BPF_fd == -2) break; | |
2652 | if (!i) { LogSPS("mDNSPlatformReceiveBPF_fd: No Interfaces awaiting BPF fd %d; closing", fd); close(fd); } | |
67c8f8a1 A |
2653 | else |
2654 | { | |
263eeeab A |
2655 | LogSPS("%s using BPF fd %d", i->ifinfo.ifname, fd); |
2656 | ||
2657 | struct bpf_version v; | |
2658 | if (ioctl(fd, BIOCVERSION, &v) < 0) | |
2659 | LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); | |
2660 | else if (BPF_MAJOR_VERSION != v.bv_major || BPF_MINOR_VERSION != v.bv_minor) | |
2661 | LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION header %d.%d kernel %d.%d", | |
2662 | fd, i->ifinfo.ifname, BPF_MAJOR_VERSION, BPF_MINOR_VERSION, v.bv_major, v.bv_minor); | |
2663 | ||
2664 | if (ioctl(fd, BIOCGBLEN, &i->BPF_len) < 0) | |
2665 | LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCGBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); | |
2666 | ||
2667 | if (i->BPF_len > sizeof(m->imsg)) | |
67c8f8a1 | 2668 | { |
263eeeab A |
2669 | i->BPF_len = sizeof(m->imsg); |
2670 | if (ioctl(fd, BIOCSBLEN, &i->BPF_len) < 0) | |
2671 | LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); | |
294beb6e A |
2672 | else |
2673 | LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", fd, i->ifinfo.ifname, i->BPF_len); | |
67c8f8a1 | 2674 | } |
263eeeab A |
2675 | |
2676 | static const u_int opt_one = 1; | |
2677 | if (ioctl(fd, BIOCIMMEDIATE, &opt_one) < 0) | |
2678 | LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCIMMEDIATE failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); | |
2679 | ||
2680 | //if (ioctl(fd, BIOCPROMISC, &opt_one) < 0) | |
2681 | // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCPROMISC failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); | |
2682 | ||
2683 | //if (ioctl(fd, BIOCSHDRCMPLT, &opt_one) < 0) | |
2684 | // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSHDRCMPLT failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); | |
2685 | ||
2686 | struct ifreq ifr; | |
2687 | mDNSPlatformMemZero(&ifr, sizeof(ifr)); | |
2688 | strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name)); | |
2689 | if (ioctl(fd, BIOCSETIF, &ifr) < 0) | |
2690 | { LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSETIF failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); i->BPF_fd = -3; } | |
2691 | else | |
67c8f8a1 | 2692 | { |
294beb6e | 2693 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
2694 | i->BPF_fd = fd; |
2695 | i->BPF_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue()); | |
2696 | if (!i->BPF_source) {LogMsg("mDNSPlatformReceiveBPF_fd: dispatch source create failed");return;} | |
2697 | dispatch_source_set_event_handler(i->BPF_source, ^{bpf_callback_dispatch(i);}); | |
2698 | dispatch_source_set_cancel_handler(i->BPF_source, ^{close(fd);}); | |
2699 | dispatch_resume(i->BPF_source); | |
2700 | #else | |
2701 | CFSocketContext myCFSocketContext = { 0, i, NULL, NULL, NULL }; | |
2702 | i->BPF_fd = fd; | |
2703 | i->BPF_cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, bpf_callback, &myCFSocketContext); | |
2704 | i->BPF_rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, i->BPF_cfs, 0); | |
2705 | CFRunLoopAddSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode); | |
2706 | #endif | |
2707 | mDNSPlatformUpdateProxyList(m, i->ifinfo.InterfaceID); | |
7f0064bd | 2708 | } |
7f0064bd | 2709 | } |
67c8f8a1 | 2710 | |
263eeeab | 2711 | mDNS_Unlock(m); |
7f0064bd A |
2712 | } |
2713 | ||
263eeeab | 2714 | #endif // APPLE_OSX_mDNSResponder |
283ee3ff | 2715 | |
263eeeab A |
2716 | #if COMPILER_LIKES_PRAGMA_MARK |
2717 | #pragma mark - | |
2718 | #pragma mark - Key Management | |
2719 | #endif | |
05292456 | 2720 | |
263eeeab A |
2721 | #ifndef NO_SECURITYFRAMEWORK |
2722 | mDNSlocal CFArrayRef GetCertChain(SecIdentityRef identity) | |
2723 | { | |
2724 | CFMutableArrayRef certChain = NULL; | |
2725 | if (!identity) { LogMsg("getCertChain: identity is NULL"); return(NULL); } | |
2726 | SecCertificateRef cert; | |
2727 | OSStatus err = SecIdentityCopyCertificate(identity, &cert); | |
2728 | if (err || !cert) LogMsg("getCertChain: SecIdentityCopyCertificate() returned %d", (int) err); | |
67c8f8a1 A |
2729 | else |
2730 | { | |
263eeeab A |
2731 | SecPolicySearchRef searchRef; |
2732 | err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef); | |
2733 | if (err || !searchRef) LogMsg("getCertChain: SecPolicySearchCreate() returned %d", (int) err); | |
2734 | else | |
05292456 | 2735 | { |
263eeeab A |
2736 | SecPolicyRef policy; |
2737 | err = SecPolicySearchCopyNext(searchRef, &policy); | |
2738 | if (err || !policy) LogMsg("getCertChain: SecPolicySearchCopyNext() returned %d", (int) err); | |
2739 | else | |
2740 | { | |
2741 | CFArrayRef wrappedCert = CFArrayCreate(NULL, (const void**) &cert, 1, &kCFTypeArrayCallBacks); | |
2742 | if (!wrappedCert) LogMsg("getCertChain: wrappedCert is NULL"); | |
2743 | else | |
2744 | { | |
2745 | SecTrustRef trust; | |
2746 | err = SecTrustCreateWithCertificates(wrappedCert, policy, &trust); | |
2747 | if (err || !trust) LogMsg("getCertChain: SecTrustCreateWithCertificates() returned %d", (int) err); | |
2748 | else | |
2749 | { | |
2750 | err = SecTrustEvaluate(trust, NULL); | |
2751 | if (err) LogMsg("getCertChain: SecTrustEvaluate() returned %d", (int) err); | |
2752 | else | |
2753 | { | |
2754 | CFArrayRef rawCertChain; | |
2755 | CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL; | |
2756 | err = SecTrustGetResult(trust, NULL, &rawCertChain, &statusChain); | |
2757 | if (err || !rawCertChain || !statusChain) LogMsg("getCertChain: SecTrustGetResult() returned %d", (int) err); | |
2758 | else | |
2759 | { | |
2760 | certChain = CFArrayCreateMutableCopy(NULL, 0, rawCertChain); | |
2761 | if (!certChain) LogMsg("getCertChain: certChain is NULL"); | |
2762 | else | |
2763 | { | |
2764 | // Replace the SecCertificateRef at certChain[0] with a SecIdentityRef per documentation for SSLSetCertificate: | |
2765 | // <http://devworld.apple.com/documentation/Security/Reference/secureTransportRef/index.html> | |
2766 | CFArraySetValueAtIndex(certChain, 0, identity); | |
2767 | // Remove root from cert chain, but keep any and all intermediate certificates that have been signed by the root certificate | |
2768 | if (CFArrayGetCount(certChain) > 1) CFArrayRemoveValueAtIndex(certChain, CFArrayGetCount(certChain) - 1); | |
2769 | } | |
2770 | CFRelease(rawCertChain); | |
2771 | // Do not free statusChain: | |
2772 | // <http://developer.apple.com/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html> says: | |
2773 | // certChain: Call the CFRelease function to release this object when you are finished with it. | |
2774 | // statusChain: Do not attempt to free this pointer; it remains valid until the trust management object is released... | |
2775 | } | |
2776 | } | |
2777 | CFRelease(trust); | |
2778 | } | |
2779 | CFRelease(wrappedCert); | |
2780 | } | |
2781 | CFRelease(policy); | |
2782 | } | |
2783 | CFRelease(searchRef); | |
05292456 | 2784 | } |
263eeeab | 2785 | CFRelease(cert); |
7f0064bd | 2786 | } |
263eeeab | 2787 | return certChain; |
7f0064bd | 2788 | } |
263eeeab | 2789 | #endif /* NO_SECURITYFRAMEWORK */ |
7f0064bd | 2790 | |
263eeeab | 2791 | mDNSexport mStatus mDNSPlatformTLSSetupCerts(void) |
c9b9ae52 | 2792 | { |
263eeeab A |
2793 | #ifdef NO_SECURITYFRAMEWORK |
2794 | return mStatus_UnsupportedErr; | |
2795 | #else | |
2796 | SecIdentityRef identity = nil; | |
2797 | SecIdentitySearchRef srchRef = nil; | |
2798 | OSStatus err; | |
c9b9ae52 | 2799 | |
263eeeab A |
2800 | // search for "any" identity matching specified key use |
2801 | // In this app, we expect there to be exactly one | |
2802 | err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_DECRYPT, &srchRef); | |
2803 | if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCreate returned %d", (int) err); return err; } | |
c9b9ae52 | 2804 | |
263eeeab A |
2805 | err = SecIdentitySearchCopyNext(srchRef, &identity); |
2806 | if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext returned %d", (int) err); return err; } | |
67c8f8a1 | 2807 | |
263eeeab A |
2808 | if (CFGetTypeID(identity) != SecIdentityGetTypeID()) |
2809 | { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext CFTypeID failure"); return mStatus_UnknownErr; } | |
67c8f8a1 | 2810 | |
263eeeab A |
2811 | // Found one. Call getCertChain to create the correct certificate chain. |
2812 | ServerCerts = GetCertChain(identity); | |
2813 | if (ServerCerts == nil) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: getCertChain error"); return mStatus_UnknownErr; } | |
67c8f8a1 | 2814 | |
263eeeab A |
2815 | return mStatus_NoError; |
2816 | #endif /* NO_SECURITYFRAMEWORK */ | |
2817 | } | |
67c8f8a1 | 2818 | |
263eeeab A |
2819 | mDNSexport void mDNSPlatformTLSTearDownCerts(void) |
2820 | { | |
2821 | #ifndef NO_SECURITYFRAMEWORK | |
2822 | if (ServerCerts) { CFRelease(ServerCerts); ServerCerts = NULL; } | |
2823 | #endif /* NO_SECURITYFRAMEWORK */ | |
2824 | } | |
c9b9ae52 | 2825 | |
263eeeab A |
2826 | // This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel |
2827 | mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) | |
2828 | { | |
2829 | CFStringEncoding encoding = kCFStringEncodingUTF8; | |
2830 | CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); | |
2831 | if (cfs) | |
c9b9ae52 | 2832 | { |
263eeeab A |
2833 | CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); |
2834 | CFRelease(cfs); | |
c9b9ae52 | 2835 | } |
263eeeab | 2836 | } |
67c8f8a1 | 2837 | |
263eeeab A |
2838 | // This gets the text of the field currently labelled "Local Hostname" in the Sharing Prefs Control Panel |
2839 | mDNSlocal void GetUserSpecifiedLocalHostName(domainlabel *const namelabel) | |
2840 | { | |
2841 | CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); | |
2842 | if (cfs) | |
32bb7e43 | 2843 | { |
263eeeab A |
2844 | CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); |
2845 | CFRelease(cfs); | |
32bb7e43 | 2846 | } |
263eeeab | 2847 | } |
67c8f8a1 | 2848 | |
263eeeab A |
2849 | mDNSexport mDNSBool DictionaryIsEnabled(CFDictionaryRef dict) |
2850 | { | |
2851 | mDNSs32 val; | |
2852 | CFNumberRef state = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("Enabled")); | |
2853 | if (!state) return mDNSfalse; | |
2854 | if (!CFNumberGetValue(state, kCFNumberSInt32Type, &val)) | |
2855 | { LogMsg("ERROR: DictionaryIsEnabled - CFNumberGetValue"); return mDNSfalse; } | |
2856 | return val ? mDNStrue : mDNSfalse; | |
c9b9ae52 A |
2857 | } |
2858 | ||
263eeeab | 2859 | mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) |
67c8f8a1 | 2860 | { |
263eeeab | 2861 | if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); } |
9f29194f | 2862 | |
263eeeab | 2863 | if (sa->sa_family == AF_INET) |
9f29194f | 2864 | { |
263eeeab A |
2865 | struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa; |
2866 | ip->type = mDNSAddrType_IPv4; | |
2867 | ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr; | |
2868 | return(mStatus_NoError); | |
2869 | } | |
32bb7e43 | 2870 | |
263eeeab | 2871 | if (sa->sa_family == AF_INET6) |
67c8f8a1 | 2872 | { |
263eeeab A |
2873 | struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; |
2874 | // Inside the BSD kernel they use a hack where they stuff the sin6->sin6_scope_id | |
2875 | // value into the second word of the IPv6 link-local address, so they can just | |
2876 | // pass around IPv6 address structures instead of full sockaddr_in6 structures. | |
2877 | // Those hacked IPv6 addresses aren't supposed to escape the kernel in that form, but they do. | |
2878 | // To work around this we always whack the second word of any IPv6 link-local address back to zero. | |
2879 | if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0; | |
2880 | ip->type = mDNSAddrType_IPv6; | |
2881 | ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; | |
2882 | return(mStatus_NoError); | |
67c8f8a1 | 2883 | } |
263eeeab A |
2884 | |
2885 | LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); | |
2886 | return(mStatus_Invalid); | |
67c8f8a1 A |
2887 | } |
2888 | ||
263eeeab | 2889 | mDNSlocal mDNSEthAddr GetBSSID(char *ifa_name) |
67c8f8a1 | 2890 | { |
263eeeab A |
2891 | mDNSEthAddr eth = zeroEthAddr; |
2892 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetBSSID"), NULL, NULL); | |
2893 | if (!store) | |
2894 | LogMsg("GetBSSID: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); | |
2895 | else | |
67c8f8a1 | 2896 | { |
263eeeab A |
2897 | CFStringRef entityname = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Interface/%s/AirPort"), ifa_name); |
2898 | if (entityname) | |
2899 | { | |
2900 | CFDictionaryRef dict = SCDynamicStoreCopyValue(store, entityname); | |
2901 | if (dict) | |
2902 | { | |
2903 | CFRange range = { 0, 6 }; // Offset, length | |
2904 | CFDataRef data = CFDictionaryGetValue(dict, CFSTR("BSSID")); | |
2905 | if (data && CFDataGetLength(data) == 6) CFDataGetBytes(data, range, eth.b); | |
2906 | CFRelease(dict); | |
2907 | } | |
2908 | CFRelease(entityname); | |
2909 | } | |
2910 | CFRelease(store); | |
67c8f8a1 | 2911 | } |
263eeeab | 2912 | return(eth); |
67c8f8a1 A |
2913 | } |
2914 | ||
263eeeab | 2915 | mDNSlocal int GetMAC(mDNSEthAddr *eth, u_short ifindex) |
67c8f8a1 | 2916 | { |
263eeeab A |
2917 | struct ifaddrs *ifa; |
2918 | for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) | |
2919 | if (ifa->ifa_addr->sa_family == AF_LINK) | |
2920 | { | |
2921 | const struct sockaddr_dl *const sdl = (const struct sockaddr_dl *)ifa->ifa_addr; | |
2922 | if (sdl->sdl_index == ifindex) | |
2923 | { mDNSPlatformMemCopy(eth->b, sdl->sdl_data + sdl->sdl_nlen, 6); return 0; } | |
2924 | } | |
2925 | *eth = zeroEthAddr; | |
2926 | return -1; | |
67c8f8a1 A |
2927 | } |
2928 | ||
263eeeab A |
2929 | #ifndef SIOCGIFWAKEFLAGS |
2930 | #define SIOCGIFWAKEFLAGS _IOWR('i', 136, struct ifreq) /* get interface wake property flags */ | |
67c8f8a1 A |
2931 | #endif |
2932 | ||
263eeeab A |
2933 | #ifndef IF_WAKE_ON_MAGIC_PACKET |
2934 | #define IF_WAKE_ON_MAGIC_PACKET 0x01 | |
2935 | #endif | |
32bb7e43 | 2936 | |
263eeeab A |
2937 | #ifndef ifr_wake_flags |
2938 | #define ifr_wake_flags ifr_ifru.ifru_intval | |
2939 | #endif | |
2940 | ||
2941 | mDNSlocal mDNSBool NetWakeInterface(NetworkInterfaceInfoOSX *i) | |
67c8f8a1 | 2942 | { |
263eeeab A |
2943 | if (!MulticastInterface(i) ) return(mDNSfalse); // We only use Sleep Proxy Service on multicast-capable interfaces |
2944 | if (i->ifa_flags & IFF_LOOPBACK) return(mDNSfalse); // except loopback | |
ca3eca6b | 2945 | |
263eeeab A |
2946 | int s = socket(AF_INET, SOCK_DGRAM, 0); |
2947 | if (s < 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i->ifinfo.ifname, s, errno, strerror(errno)); return(mDNSfalse); } | |
2948 | ||
2949 | struct ifreq ifr; | |
2950 | strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name)); | |
2951 | if (ioctl(s, SIOCGIFWAKEFLAGS, &ifr) < 0) | |
67c8f8a1 | 2952 | { |
263eeeab A |
2953 | // For some strange reason, in /usr/include/sys/errno.h, EOPNOTSUPP is defined to be |
2954 | // 102 when compiling kernel code, and 45 when compiling user-level code. Since this | |
2955 | // error code is being returned from the kernel, we need to use the kernel version. | |
2956 | #define KERNEL_EOPNOTSUPP 102 | |
2957 | if (errno != KERNEL_EOPNOTSUPP) // "Operation not supported on socket", the expected result on Leopard and earlier | |
2958 | LogMsg("NetWakeInterface SIOCGIFWAKEFLAGS %s errno %d (%s)", i->ifinfo.ifname, errno, strerror(errno)); | |
2959 | // If on Leopard or earlier, we get EOPNOTSUPP, so in that case | |
2960 | // we enable WOL if this interface is not AirPort and "Wake for Network access" is turned on. | |
2961 | ifr.ifr_wake_flags = (errno == KERNEL_EOPNOTSUPP && !(i)->BSSID.l[0] && i->m->SystemWakeOnLANEnabled) ? IF_WAKE_ON_MAGIC_PACKET : 0; | |
32bb7e43 | 2962 | } |
263eeeab A |
2963 | |
2964 | close(s); | |
2965 | ||
2966 | // 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 | |
2967 | ||
2968 | LogSPS("%-6s %#-14a %s WOMP", i->ifinfo.ifname, &i->ifinfo.ip, (ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) ? "supports" : "no"); | |
2969 | ||
2970 | return((ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) != 0); | |
32bb7e43 A |
2971 | } |
2972 | ||
263eeeab A |
2973 | // Returns pointer to newly created NetworkInterfaceInfoOSX object, or |
2974 | // pointer to already-existing NetworkInterfaceInfoOSX object found in list, or | |
2975 | // may return NULL if out of memory (unlikely) or parameters are invalid for some reason | |
2976 | // (e.g. sa_family not AF_INET or AF_INET6) | |
2977 | mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa, mDNSs32 utc) | |
32bb7e43 | 2978 | { |
263eeeab A |
2979 | mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); |
2980 | mDNSEthAddr bssid = GetBSSID(ifa->ifa_name); | |
2981 | ||
2982 | mDNSAddr ip, mask; | |
2983 | if (SetupAddr(&ip, ifa->ifa_addr ) != mStatus_NoError) return(NULL); | |
2984 | if (SetupAddr(&mask, ifa->ifa_netmask) != mStatus_NoError) return(NULL); | |
2985 | ||
2986 | NetworkInterfaceInfoOSX **p; | |
2987 | for (p = &m->p->InterfaceList; *p; p = &(*p)->next) | |
2988 | if (scope_id == (*p)->scope_id && | |
2989 | mDNSSameAddress(&ip, &(*p)->ifinfo.ip) && | |
2990 | mDNSSameEthAddress(&bssid, &(*p)->BSSID)) | |
2991 | { | |
294beb6e A |
2992 | 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); |
2993 | // The name should be updated to the new name so that we don't report a wrong name in our SIGINFO output. | |
2994 | // When interfaces are created with same MAC address, kernel resurrects the old interface. | |
2995 | // Even though the interface index is the same (which should be sufficient), when we receive a UDP packet | |
2996 | // we get the corresponding name for the interface index on which the packet was received and check against | |
2997 | // the InterfaceList for a matching name. So, keep the name in sync | |
2998 | strlcpy((*p)->ifinfo.ifname, ifa->ifa_name, sizeof((*p)->ifinfo.ifname)); | |
263eeeab A |
2999 | (*p)->Exists = mDNStrue; |
3000 | // If interface was not in getifaddrs list last time we looked, but it is now, update 'AppearanceTime' for this record | |
3001 | if ((*p)->LastSeen != utc) (*p)->AppearanceTime = utc; | |
3002 | ||
3003 | // If Wake-on-LAN capability of this interface has changed (e.g. because power cable on laptop has been disconnected) | |
3004 | // we may need to start or stop or sleep proxy browse operation | |
3005 | const mDNSBool NetWake = NetWakeInterface(*p); | |
3006 | if ((*p)->ifinfo.NetWake != NetWake) | |
3007 | { | |
3008 | (*p)->ifinfo.NetWake = NetWake; | |
3009 | // If this interface is already registered with mDNSCore, then we need to start or stop its NetWake browse on-the-fly. | |
3010 | // If this interface is not already registered (i.e. it's a dormant interface we had in our list | |
3011 | // from when we previously saw it) then we mustn't do that, because mDNSCore doesn't know about it yet. | |
3012 | // In this case, the mDNS_RegisterInterface() call will take care of starting the NetWake browse if necessary. | |
3013 | if ((*p)->Registered) | |
3014 | { | |
3015 | mDNS_Lock(m); | |
3016 | if (NetWake) mDNS_ActivateNetWake_internal (m, &(*p)->ifinfo); | |
3017 | else mDNS_DeactivateNetWake_internal(m, &(*p)->ifinfo); | |
3018 | mDNS_Unlock(m); | |
3019 | } | |
3020 | } | |
3021 | ||
3022 | return(*p); | |
3023 | } | |
3024 | ||
3025 | NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i)); | |
3026 | debugf("AddInterfaceToList: Making new interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, i); | |
3027 | if (!i) return(mDNSNULL); | |
3028 | mDNSPlatformMemZero(i, sizeof(NetworkInterfaceInfoOSX)); | |
3029 | i->ifinfo.InterfaceID = (mDNSInterfaceID)(uintptr_t)scope_id; | |
3030 | i->ifinfo.ip = ip; | |
3031 | i->ifinfo.mask = mask; | |
3032 | strlcpy(i->ifinfo.ifname, ifa->ifa_name, sizeof(i->ifinfo.ifname)); | |
3033 | i->ifinfo.ifname[sizeof(i->ifinfo.ifname)-1] = 0; | |
3034 | // We can be configured to disable multicast advertisement, but we want to to support | |
3035 | // local-only services, which need a loopback address record. | |
3036 | i->ifinfo.Advertise = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses; | |
3037 | i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList | |
294beb6e | 3038 | i->ifinfo.Loopback = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse; |
263eeeab A |
3039 | |
3040 | i->next = mDNSNULL; | |
3041 | i->m = m; | |
3042 | i->Exists = mDNStrue; | |
3043 | i->Flashing = mDNSfalse; | |
3044 | i->Occulting = mDNSfalse; | |
3045 | i->AppearanceTime = utc; // Brand new interface; AppearanceTime is now | |
3046 | i->LastSeen = utc; | |
3047 | i->ifa_flags = ifa->ifa_flags; | |
3048 | i->scope_id = scope_id; | |
3049 | i->BSSID = bssid; | |
3050 | i->sa_family = ifa->ifa_addr->sa_family; | |
3051 | i->BPF_fd = -1; | |
3052 | i->BPF_mcfd = -1; | |
3053 | i->BPF_len = 0; | |
3054 | i->Registered = mDNSNULL; | |
32bb7e43 | 3055 | |
263eeeab A |
3056 | // Do this AFTER i->BSSID has been set up |
3057 | i->ifinfo.NetWake = NetWakeInterface(i); | |
3058 | GetMAC(&i->ifinfo.MAC, scope_id); | |
3059 | if (i->ifinfo.NetWake && !i->ifinfo.MAC.l[0]) | |
3060 | LogMsg("AddInterfaceToList: Bad MAC address %.6a for %d %s %#a", &i->ifinfo.MAC, scope_id, i->ifinfo.ifname, &ip); | |
32bb7e43 | 3061 | |
263eeeab A |
3062 | *p = i; |
3063 | return(i); | |
32bb7e43 A |
3064 | } |
3065 | ||
263eeeab A |
3066 | #if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 |
3067 | mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id) | |
32bb7e43 | 3068 | { |
263eeeab A |
3069 | NetworkInterfaceInfoOSX *i; |
3070 | for (i = m->p->InterfaceList; i; i = i->next) | |
3071 | if (i->Exists && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4) | |
3072 | if (!mDNSv4AddressIsLinkLocal(&i->ifinfo.ip.ip.v4)) | |
3073 | return(i); | |
3074 | return(mDNSNULL); | |
3075 | } | |
3076 | #endif | |
32bb7e43 | 3077 | |
263eeeab | 3078 | #if APPLE_OSX_mDNSResponder |
32bb7e43 | 3079 | |
263eeeab A |
3080 | #if COMPILER_LIKES_PRAGMA_MARK |
3081 | #pragma mark - | |
3082 | #pragma mark - AutoTunnel | |
3083 | #endif | |
32bb7e43 | 3084 | |
263eeeab | 3085 | #define kRacoonPort 4500 |
32bb7e43 | 3086 | |
263eeeab | 3087 | static DomainAuthInfo* AnonymousRacoonConfig = mDNSNULL; |
32bb7e43 | 3088 | |
5e65c77f A |
3089 | #ifndef NO_SECURITYFRAMEWORK |
3090 | ||
3091 | static CFMutableDictionaryRef domainStatusDict = NULL; | |
3092 | ||
3093 | // MUST be called with lock held | |
32bb7e43 | 3094 | mDNSlocal void RemoveAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info) |
5e65c77f A |
3095 | { |
3096 | char buffer[1024]; | |
294beb6e | 3097 | mDNSu32 buflen; |
5e65c77f A |
3098 | CFStringRef domain; |
3099 | ||
32bb7e43 | 3100 | LogInfo("RemoveAutoTunnelDomainStatus: %##s", info->domain.c); |
5e65c77f A |
3101 | |
3102 | if (!domainStatusDict) { LogMsg("RemoveAutoTunnelDomainStatus: No domainStatusDict"); return; } | |
3103 | ||
294beb6e A |
3104 | buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c); |
3105 | if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; // Strip the trailing dot for Classic | |
5e65c77f A |
3106 | domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); |
3107 | if (!domain) { LogMsg("RemoveAutoTunnelDomainStatus: Could not create CFString domain"); return; } | |
3108 | ||
3109 | if (CFDictionaryContainsKey(domainStatusDict, domain)) | |
3110 | { | |
3111 | CFDictionaryRemoveValue(domainStatusDict, domain); | |
32bb7e43 | 3112 | if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict); |
5e65c77f A |
3113 | } |
3114 | CFRelease(domain); | |
3115 | } | |
3116 | ||
32bb7e43 A |
3117 | mDNSlocal mStatus CheckQuestionForStatus(const DNSQuestion *const q) |
3118 | { | |
3119 | if (q->LongLived) | |
3120 | { | |
263eeeab | 3121 | if (q->servAddr.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes(q->servAddr.ip.v4)) |
32bb7e43 A |
3122 | return mStatus_NoSuchRecord; |
3123 | else if (q->state == LLQ_Poll) | |
3124 | return mStatus_PollingMode; | |
3125 | else if (q->state != LLQ_Established && !q->DuplicateOf) | |
3126 | return mStatus_TransientErr; | |
3127 | } | |
3128 | ||
3129 | return mStatus_NoError; | |
3130 | } | |
3131 | ||
263eeeab A |
3132 | mDNSlocal mStatus UpdateLLQStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info) |
3133 | { | |
3134 | mStatus status = mStatus_NoError; | |
3135 | DNSQuestion* q, *worst_q = mDNSNULL; | |
3136 | for (q = m->Questions; q; q=q->next) | |
3137 | if (q->AuthInfo == info) | |
3138 | { | |
3139 | mStatus newStatus = CheckQuestionForStatus(q); | |
3140 | if (newStatus == mStatus_NoSuchRecord) { status = newStatus; worst_q = q; break; } | |
3141 | else if (newStatus == mStatus_PollingMode) { status = newStatus; worst_q = q; } | |
3142 | else if (newStatus == mStatus_TransientErr && status == mStatus_NoError) { status = newStatus; worst_q = q; } | |
3143 | } | |
3144 | ||
3145 | if (status == mStatus_NoError) mDNS_snprintf(buffer, bufsz, "Success"); | |
3146 | else if (status == mStatus_NoSuchRecord) mDNS_snprintf(buffer, bufsz, "GetZoneData %s: %##s", worst_q->nta ? "not yet complete" : "failed", worst_q->qname.c); | |
3147 | else if (status == mStatus_PollingMode) mDNS_snprintf(buffer, bufsz, "Query polling %##s", worst_q->qname.c); | |
3148 | else if (status == mStatus_TransientErr) mDNS_snprintf(buffer, bufsz, "Query not yet established %##s", worst_q->qname.c); | |
3149 | return status; | |
3150 | } | |
3151 | ||
3152 | mDNSlocal mStatus UpdateRRStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info) | |
3153 | { | |
3154 | AuthRecord *r; | |
3155 | ||
3156 | if (info->deltime) return mStatus_NoError; | |
3157 | for (r = m->ResourceRecords; r; r = r->next) | |
3158 | { | |
3159 | // This function is called from UpdateAutoTunnelDomainStatus which in turn may be called from | |
3160 | // a callback e.g., CheckNATMappings. GetAuthInfoFor_internal does not like that (reentrancy being 1), | |
3161 | // hence we inline the code here. We just need the lock to walk the list of AuthInfos which the caller | |
3162 | // has already checked | |
3163 | const domainname *n = r->resrec.name; | |
3164 | while (n->c[0]) | |
3165 | { | |
3166 | DomainAuthInfo *ptr; | |
3167 | for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) | |
3168 | if (SameDomainName(&ptr->domain, n)) | |
3169 | { | |
4a95efb2 | 3170 | if (ptr == info && r->updateError == mStatus_BadSig) |
263eeeab A |
3171 | { |
3172 | mDNS_snprintf(buffer, bufsz, "Resource record update failed for %##s", r->resrec.name); | |
3173 | return r->updateError; | |
3174 | } | |
3175 | } | |
3176 | n = (const domainname *)(n->c + 1 + n->c[0]); | |
3177 | } | |
3178 | } | |
3179 | return mStatus_NoError; | |
3180 | } | |
3181 | ||
3182 | #endif // ndef NO_SECURITYFRAMEWORK | |
3183 | ||
5e65c77f A |
3184 | // MUST be called with lock held |
3185 | mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info) | |
3186 | { | |
3187 | #ifdef NO_SECURITYFRAMEWORK | |
3188 | (void)m; | |
3189 | (void)info; | |
3190 | #else | |
3191 | const NATTraversalInfo *const llq = m->LLQNAT.clientContext ? &m->LLQNAT : mDNSNULL; | |
3192 | const NATTraversalInfo *const tun = info->AutoTunnelNAT.clientContext ? &info->AutoTunnelNAT : mDNSNULL; | |
3193 | char buffer[1024]; | |
294beb6e | 3194 | mDNSu32 buflen = 0; |
5e65c77f A |
3195 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
3196 | CFStringRef domain = NULL; | |
3197 | CFStringRef tmp = NULL; | |
3198 | CFNumberRef num = NULL; | |
3199 | mStatus status = mStatus_NoError; | |
294beb6e A |
3200 | mStatus llqStatus = mStatus_NoError; |
3201 | char llqBuffer[1024]; | |
5e65c77f | 3202 | |
263eeeab | 3203 | if (!m->mDNS_busy) LogMsg("UpdateAutoTunnelDomainStatus: ERROR!! Lock not held"); |
5e65c77f A |
3204 | if (!domainStatusDict) |
3205 | { | |
3206 | domainStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
3207 | if (!domainStatusDict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary domainStatusDict"); return; } | |
3208 | } | |
3209 | ||
3210 | if (!dict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary dict"); return; } | |
3211 | ||
294beb6e A |
3212 | buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c); |
3213 | if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; // Strip the trailing dot for Classic | |
5e65c77f A |
3214 | domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); |
3215 | if (!domain) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString domain"); return; } | |
3216 | ||
3217 | mDNS_snprintf(buffer, sizeof(buffer), "%#a", &m->Router); | |
3218 | tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); | |
3219 | if (!tmp) | |
3220 | LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString RouterAddress"); | |
3221 | else | |
3222 | { | |
3223 | CFDictionarySetValue(dict, CFSTR("RouterAddress"), tmp); | |
3224 | CFRelease(tmp); | |
3225 | } | |
3226 | ||
3227 | mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &m->ExternalAddress); | |
3228 | tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); | |
3229 | if (!tmp) | |
3230 | LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress"); | |
3231 | else | |
3232 | { | |
3233 | CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp); | |
3234 | CFRelease(tmp); | |
3235 | } | |
3236 | ||
3237 | if (llq) | |
3238 | { | |
3239 | mDNSu32 port = mDNSVal16(llq->ExternalPort); | |
3240 | ||
3241 | num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port); | |
3242 | if (!num) | |
3243 | LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQExternalPort"); | |
3244 | else | |
3245 | { | |
3246 | CFDictionarySetValue(dict, CFSTR("LLQExternalPort"), num); | |
3247 | CFRelease(num); | |
3248 | } | |
3249 | ||
3250 | if (llq->Result) | |
3251 | { | |
3252 | num = CFNumberCreate(NULL, kCFNumberSInt32Type, &llq->Result); | |
3253 | if (!num) | |
3254 | LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQNPMStatus"); | |
3255 | else | |
3256 | { | |
3257 | CFDictionarySetValue(dict, CFSTR("LLQNPMStatus"), num); | |
3258 | CFRelease(num); | |
3259 | } | |
3260 | } | |
3261 | } | |
3262 | ||
3263 | if (tun) | |
3264 | { | |
3265 | mDNSu32 port = mDNSVal16(tun->ExternalPort); | |
3266 | ||
3267 | num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port); | |
3268 | if (!num) | |
3269 | LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelExternalPort"); | |
3270 | else | |
3271 | { | |
3272 | CFDictionarySetValue(dict, CFSTR("AutoTunnelExternalPort"), num); | |
3273 | CFRelease(num); | |
3274 | } | |
3275 | ||
3276 | if (tun->Result) | |
3277 | { | |
3278 | num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tun->Result); | |
3279 | if (!num) | |
3280 | LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelNPMStatus"); | |
3281 | else | |
3282 | { | |
3283 | CFDictionarySetValue(dict, CFSTR("AutoTunnelNPMStatus"), num); | |
3284 | CFRelease(num); | |
3285 | } | |
3286 | } | |
3287 | } | |
32bb7e43 A |
3288 | if (tun || llq) |
3289 | { | |
3290 | mDNSu32 code = m->LastNATMapResultCode; | |
3291 | ||
3292 | num = CFNumberCreate(NULL, kCFNumberSInt32Type, &code); | |
3293 | if (!num) | |
3294 | LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LastNATMapResultCode"); | |
3295 | else | |
3296 | { | |
3297 | CFDictionarySetValue(dict, CFSTR("LastNATMapResultCode"), num); | |
3298 | CFRelease(num); | |
3299 | } | |
3300 | } | |
5e65c77f | 3301 | |
294beb6e A |
3302 | mDNS_snprintf(buffer, sizeof(buffer), "Success"); |
3303 | llqStatus = UpdateLLQStatus(m, llqBuffer, sizeof(llqBuffer), info); | |
3304 | status = UpdateRRStatus(m, buffer, sizeof(buffer), info); | |
3305 | ||
3306 | // If we have a bad signature error updating a RR, it overrides any error as it needs to be | |
3307 | // reported so that it can be fixed automatically (or the user needs to be notified) | |
3308 | if (status != mStatus_NoError) | |
3309 | { | |
3310 | LogInfo("UpdateAutoTunnelDomainStatus: RR Status %d, %s", status, buffer); | |
3311 | } | |
3312 | else if (m->Router.type == mDNSAddrType_None) | |
3313 | { | |
3314 | status = mStatus_NoRouter; | |
3315 | mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none"); | |
3316 | } | |
3317 | else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4)) | |
3318 | { | |
3319 | status = mStatus_NoRouter; | |
3320 | mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero"); | |
3321 | } | |
3322 | else if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrOut)) | |
3323 | { | |
3324 | status = info->AutoTunnel == btmmprefix ? mStatus_ServiceNotRunning : mStatus_PollingMode; | |
3325 | mDNS_snprintf(buffer, sizeof(buffer), "No relay connection"); | |
263eeeab A |
3326 | } |
3327 | else if (!llq && !tun) | |
5e65c77f A |
3328 | { |
3329 | status = mStatus_NotInitializedErr; | |
3330 | mDNS_snprintf(buffer, sizeof(buffer), "Neither LLQ nor AutoTunnel NAT port mapping is currently active"); | |
32bb7e43 | 3331 | } |
294beb6e A |
3332 | else if (llqStatus == mStatus_NoSuchRecord) |
3333 | { | |
3334 | status = llqStatus; | |
3335 | mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); | |
3336 | } | |
3337 | else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT))) | |
5e65c77f A |
3338 | { |
3339 | status = mStatus_DoubleNAT; | |
3340 | mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting an external address"); | |
3341 | } | |
294beb6e A |
3342 | else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_NATPortMappingDisabled) || (tun && tun->Result == mStatus_NATPortMappingDisabled) || |
3343 | (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort)))))) | |
32bb7e43 A |
3344 | { |
3345 | status = mStatus_NATPortMappingDisabled; | |
3346 | mDNS_snprintf(buffer, sizeof(buffer), "NAT-PMP is disabled on the router"); | |
3347 | } | |
294beb6e | 3348 | else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result) || (tun && tun->Result))) |
5e65c77f A |
3349 | { |
3350 | status = mStatus_NATTraversal; | |
3351 | mDNS_snprintf(buffer, sizeof(buffer), "Error obtaining NAT port mapping from router"); | |
3352 | } | |
294beb6e | 3353 | else if (info->AutoTunnel == btmmprefix && ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort)))) |
5e65c77f A |
3354 | { |
3355 | status = mStatus_NATTraversal; | |
3356 | mDNS_snprintf(buffer, sizeof(buffer), "Unable to obtain NAT port mapping from router"); | |
3357 | } | |
294beb6e A |
3358 | else if (info->AutoTunnel == btmmprefix || llqStatus != mStatus_PollingMode) |
3359 | { | |
3360 | status = llqStatus; | |
3361 | mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); | |
3362 | LogInfo("UpdateAutoTunnelDomainStatus: LLQ Status %d, %s", status, buffer); | |
3363 | } | |
5e65c77f A |
3364 | else |
3365 | { | |
294beb6e | 3366 | mDNS_snprintf(buffer, sizeof(buffer), "Polling success"); |
5e65c77f A |
3367 | } |
3368 | ||
3369 | num = CFNumberCreate(NULL, kCFNumberSInt32Type, &status); | |
3370 | if (!num) | |
3371 | LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber StatusCode"); | |
3372 | else | |
3373 | { | |
3374 | CFDictionarySetValue(dict, CFSTR("StatusCode"), num); | |
3375 | CFRelease(num); | |
3376 | } | |
3377 | ||
3378 | tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); | |
3379 | if (!tmp) | |
3380 | LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString StatusMessage"); | |
3381 | else | |
3382 | { | |
3383 | CFDictionarySetValue(dict, CFSTR("StatusMessage"), tmp); | |
3384 | CFRelease(tmp); | |
3385 | } | |
3386 | ||
3387 | if (!CFDictionaryContainsKey(domainStatusDict, domain) || | |
3388 | !CFEqual(dict, (CFMutableDictionaryRef)CFDictionaryGetValue(domainStatusDict, domain))) | |
3389 | { | |
3390 | CFDictionarySetValue(domainStatusDict, domain, dict); | |
32bb7e43 A |
3391 | if (!m->ShutdownTime) |
3392 | { | |
3393 | static char statusBuf[16]; | |
3394 | mDNS_snprintf(statusBuf, sizeof(statusBuf), "%d", (int)status); | |
3395 | mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.domainstatus", status ? "failure" : "success", statusBuf, ""); | |
3396 | mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict); | |
3397 | } | |
5e65c77f A |
3398 | } |
3399 | ||
3400 | CFRelease(domain); | |
3401 | CFRelease(dict); | |
3402 | ||
32bb7e43 | 3403 | debugf("UpdateAutoTunnelDomainStatus: %s", buffer); |
5e65c77f A |
3404 | #endif // def NO_SECURITYFRAMEWORK |
3405 | } | |
3406 | ||
3407 | // MUST be called with lock held | |
3408 | mDNSexport void UpdateAutoTunnelDomainStatuses(const mDNS *const m) | |
3409 | { | |
3410 | #ifdef NO_SECURITYFRAMEWORK | |
3411 | (void)m; | |
3412 | #else | |
263eeeab | 3413 | if (!m->mDNS_busy) LogMsg("UpdateAutoTunnelDomainStatuses: ERROR!! Lock not held"); |
5e65c77f A |
3414 | DomainAuthInfo* info; |
3415 | for (info = m->AuthInfoList; info; info = info->next) | |
3416 | if (info->AutoTunnel && !info->deltime) | |
3417 | UpdateAutoTunnelDomainStatus(m, info); | |
3418 | #endif // def NO_SECURITYFRAMEWORK | |
3419 | } | |
3420 | ||
67c8f8a1 A |
3421 | // MUST be called with lock held |
3422 | mDNSlocal mDNSBool TunnelServers(mDNS *const m) | |
3423 | { | |
32bb7e43 A |
3424 | AuthRecord *r; |
3425 | for (r = m->ResourceRecords; r; r = r->next) | |
3426 | if (r->resrec.rrtype == kDNSType_SRV) | |
3427 | { | |
3428 | DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, r->resrec.name); | |
3429 | if (AuthInfo && AuthInfo->AutoTunnel && !AuthInfo->deltime) return(mDNStrue); | |
3430 | } | |
3431 | ||
67c8f8a1 A |
3432 | return(mDNSfalse); |
3433 | } | |
3434 | ||
3435 | // MUST be called with lock held | |
3436 | mDNSlocal mDNSBool TunnelClients(mDNS *const m) | |
3437 | { | |
3438 | ClientTunnel *p; | |
3439 | for (p = m->TunnelClients; p; p = p->next) | |
3440 | if (p->q.ThisQInterval < 0) | |
3441 | return(mDNStrue); | |
3442 | return(mDNSfalse); | |
3443 | } | |
3444 | ||
263eeeab | 3445 | mDNSlocal void UpdateAnonymousRacoonConfig(mDNS *m) // Determine whether we need racoon to accept incoming connections |
67c8f8a1 | 3446 | { |
263eeeab A |
3447 | DomainAuthInfo *info; |
3448 | ||
3449 | for (info = m->AuthInfoList; info; info = info->next) | |
3c427d54 | 3450 | if (info->AutoTunnel && !info->deltime && (!mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) || !mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn))) |
263eeeab A |
3451 | break; |
3452 | ||
3453 | if (info != AnonymousRacoonConfig) | |
67c8f8a1 | 3454 | { |
263eeeab A |
3455 | AnonymousRacoonConfig = info; |
3456 | // Create or revert configuration file, and start (or SIGHUP) Racoon | |
294beb6e | 3457 | (void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? AnonymousRacoonConfig->AutoTunnel : mDNSNULL, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL); |
263eeeab A |
3458 | } |
3459 | } | |
3460 | ||
3461 | // Caller should hold the lock. We don't call mDNS_Register (which acquires the lock) in this function because | |
3462 | // sometimes the caller may already be holding the lock e.g., RegisterAutoTunnel6Record and sometimes | |
3463 | // not e.g., RegisterAutoTunnelServiceRecord | |
3464 | mDNSlocal void RegisterAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info) | |
3465 | { | |
3466 | mStatus err; | |
3467 | mDNSBool NATProblem; | |
3468 | ||
3469 | if (!m->mDNS_busy) LogMsg("RegisterAutoTunnelHostRecord: ERROR!! Lock not held"); | |
3470 | ||
3471 | // We use AutoTunnelNAT.clientContext to infer that SetupLocalAutoTunnelInterface_internal has been | |
3472 | // called at least once with some Services/Records in the domain and hence it is safe to register | |
3473 | // records when this function is called. | |
3474 | if (!info->AutoTunnelNAT.clientContext) { LogInfo("RegisterAutoTunnelHostRecord: No services registered, not registering the record\n"); return; } | |
3475 | ||
3476 | // Are we behind a NAT with no NAT-PMP support or behind a Double NAT ? Double NATs may have | |
3477 | // NAT-PMP support but it still does not provide inbound connectivity. If there is no NAT-PMP | |
3478 | // support, ExternalPort is zero. If we are behind a Double NAT, then the NATResult is non-zero. | |
3479 | ||
3480 | NATProblem = mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) || info->AutoTunnelNAT.Result; | |
3481 | ||
3c427d54 | 3482 | if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn)) |
263eeeab A |
3483 | { |
3484 | // If we don't have a relay address, check to see if we are behind a Double NAT or NAT with no NAT-PMP | |
3485 | // support. | |
3486 | if (NATProblem) | |
3487 | { | |
3488 | LogInfo("RegisterAutoTunnelHostRecord %##s, not registering the Host Record, Neither AutoTunnel6 nor NAT is available, ClientContext %p, ExternalPort %d, NAT Result %d", info->domain.c, info->AutoTunnelNAT.clientContext, mDNSVal16(info->AutoTunnelNAT.ExternalPort), info->AutoTunnelNAT.Result); | |
3489 | return; | |
3490 | } | |
3491 | } | |
3492 | else | |
3493 | { | |
3494 | // Relay address may be non-zero but we might be going to sleep as the utun interface is not removed | |
3495 | // when going to sleep. If we are awake, we don't care about the NATProblem as the relay connnection | |
3496 | // is up. If we are going to sleep, we should not register the host record if we have a NAT problem. | |
3497 | if (m->SleepState != SleepState_Awake && NATProblem) | |
3498 | { | |
3499 | LogInfo("RegisterAutoTunnelHostRecord %##s, not registering the Host Record, Not in awake state(%d), and some NAT Problem, ClientContext %p, ExternalPort %d, NAT Result %d", info->domain.c, m->SleepState, info->AutoTunnelNAT.clientContext, mDNSVal16(info->AutoTunnelNAT.ExternalPort), info->AutoTunnelNAT.Result); | |
3500 | return; | |
3501 | } | |
3502 | } | |
67c8f8a1 | 3503 | |
263eeeab A |
3504 | // Note: |
3505 | // | |
3506 | // We use zero Requested port to infer that we should not be calling Register anymore as it might | |
3507 | // be shutdown or the DomainAuthInfo is going away. | |
3508 | // | |
3509 | // We can use a different set of state variables to track the above as the records registered in | |
3510 | // this function is not dependent on NAT traversal info. For the sake of simplicity, we just | |
3511 | // reuse the NAT variables. | |
3512 | ||
3513 | // Set up our address record for the internal tunnel address | |
3514 | // (User-visible user-friendly host name, used as target in AutoTunnel SRV records) | |
3515 | if (!mDNSIPPortIsZero(info->AutoTunnelNAT.RequestedPort) && info->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered) | |
3516 | { | |
96f69b28 A |
3517 | info->AutoTunnelHostRecord.namestorage.c[0] = 0; |
3518 | AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel); | |
3519 | AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain); | |
3520 | info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = m->AutoTunnelHostAddr; | |
67c8f8a1 | 3521 | info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeKnownUnique; |
67c8f8a1 | 3522 | |
263eeeab A |
3523 | err = mDNS_Register_internal(m, &info->AutoTunnelHostRecord); |
3524 | ||
3525 | if (err) LogMsg("RegisterAutoTunnelHostRecord error %d registering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c); | |
3526 | else | |
3527 | { | |
3528 | // Make sure we trigger the registration of all SRV records in regState_NoTarget again | |
3529 | m->NextSRVUpdate = NonZeroTime(m->timenow); | |
3530 | LogInfo("RegisterAutoTunnelHostRecord registering AutoTunnelHostRecord %##s", info->AutoTunnelHostRecord.namestorage.c); | |
3531 | } | |
3532 | } | |
3533 | else LogInfo("RegisterAutoTunnelHostRecord: Not registering Context %p Port %d Type %d", info->AutoTunnelNAT.clientContext, mDNSVal16(info->AutoTunnelNAT.RequestedPort), info->AutoTunnelHostRecord.resrec.RecordType); | |
3534 | } | |
3535 | ||
3536 | mDNSlocal void DeregisterAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info) | |
3537 | { | |
3538 | LogInfo("DeregisterAutoTunnelHostRecord %##s", info->domain.c); | |
3539 | ||
3540 | // Don't deregister if we have the AutoTunnel6 or AutoTunnelService records are registered. | |
3541 | // They indicate that BTMM is working | |
3542 | if (info->AutoTunnel6Record.resrec.RecordType > kDNSRecordTypeDeregistering || | |
3543 | info->AutoTunnelService.resrec.RecordType > kDNSRecordTypeDeregistering) | |
3544 | { | |
3545 | LogInfo("DeregisterAutoTunnelHostRecord %##s, not deregistering the Host Record AutoTunnel6 RecordType:%d AutoTunnel RecordType: %d", info->domain.c, | |
3546 | info->AutoTunnel6Record.resrec.RecordType, info->AutoTunnelService.resrec.RecordType); | |
3547 | return; | |
3548 | } | |
3549 | ||
3550 | if (info->AutoTunnelHostRecord.resrec.RecordType > kDNSRecordTypeDeregistering) | |
3551 | { | |
3552 | mStatus err = mDNS_Deregister(m, &info->AutoTunnelHostRecord); | |
3553 | if (err) | |
3554 | { | |
3555 | info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered; | |
3556 | LogMsg("DeregisterAutoTunnelHostRecord error %d deregistering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c); | |
3557 | } | |
3558 | else LogInfo("DeregisterAutoTunnelHostRecord: Deregistered AutoTunnel Host Record"); | |
3559 | } | |
3560 | else LogInfo("DeregisterAutoTunnelHostRecord: Not deregistering Host Record state:%d", info->AutoTunnelHostRecord.resrec.RecordType); | |
3561 | } | |
3562 | ||
3563 | mDNSlocal void RegisterAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info) | |
3564 | { | |
3565 | mStatus err; | |
3566 | ||
3567 | //if (m->mDNS_busy) LogMsg("RegisterAutoTunnelServiceRecords: ERROR!! Lock already held"); | |
3568 | ||
3569 | if (info->AutoTunnelNAT.clientContext && !info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && info->AutoTunnelTarget.resrec.RecordType == kDNSRecordTypeUnregistered) | |
3570 | { | |
3571 | LogInfo("RegisterAutoTunnelServiceRecords %##s (%#s)", info->domain.c, m->hostlabel.c); | |
67c8f8a1 | 3572 | |
263eeeab | 3573 | // 1. Set up our address record for the external tunnel address |
96f69b28 A |
3574 | // (Constructed name, not generally user-visible, used as target in IKE tunnel's SRV record) |
3575 | info->AutoTunnelTarget.namestorage.c[0] = 0; | |
3576 | AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->AutoTunnelLabel); | |
3577 | AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain); | |
263eeeab | 3578 | info->AutoTunnelTarget.resrec.rdata->u.ipv4 = info->AutoTunnelNAT.ExternalAddress; |
96f69b28 A |
3579 | info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique; |
3580 | ||
263eeeab A |
3581 | err = mDNS_Register(m, &info->AutoTunnelTarget); |
3582 | if (err) LogMsg("RegisterAutoTunnelServiceRecords error %d registering AutoTunnelTarget %##s", err, info->AutoTunnelTarget.namestorage.c); | |
3583 | else LogInfo("RegisterAutoTunnelServiceRecords registering AutoTunnelTarget %##s", info->AutoTunnelTarget.namestorage.c); | |
3584 | ||
3585 | } | |
96f69b28 | 3586 | |
263eeeab A |
3587 | if (info->AutoTunnelNAT.clientContext && !info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && info->AutoTunnelService.resrec.RecordType == kDNSRecordTypeUnregistered) |
3588 | { | |
3589 | // 2. Set up IKE tunnel's SRV record: "AutoTunnelHostRecord SRV 0 0 port AutoTunnelTarget" | |
96f69b28 A |
3590 | AssignDomainName (&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); |
3591 | AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel); | |
3592 | AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain); | |
3593 | info->AutoTunnelService.resrec.rdata->u.srv.priority = 0; | |
3594 | info->AutoTunnelService.resrec.rdata->u.srv.weight = 0; | |
3595 | info->AutoTunnelService.resrec.rdata->u.srv.port = info->AutoTunnelNAT.ExternalPort; | |
3596 | AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage); | |
3597 | info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique; | |
3598 | err = mDNS_Register(m, &info->AutoTunnelService); | |
263eeeab A |
3599 | if (err) LogMsg("RegisterAutoTunnelServiceRecords error %d registering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c); |
3600 | else LogInfo("RegisterAutoTunnelServiceRecords registering AutoTunnelService %##s", info->AutoTunnelService.namestorage.c); | |
96f69b28 | 3601 | |
32bb7e43 | 3602 | LogInfo("AutoTunnel server listening for connections on %##s[%.4a]:%d:%##s[%.16a]", |
67c8f8a1 A |
3603 | info->AutoTunnelTarget.namestorage.c, &m->AdvertisedV4.ip.v4, mDNSVal16(info->AutoTunnelNAT.IntPort), |
3604 | info->AutoTunnelHostRecord.namestorage.c, &m->AutoTunnelHostAddr); | |
3605 | } | |
263eeeab A |
3606 | mDNS_Lock(m); |
3607 | RegisterAutoTunnelHostRecord(m, info); | |
3608 | mDNS_Unlock(m); | |
67c8f8a1 A |
3609 | } |
3610 | ||
263eeeab | 3611 | mDNSlocal void DeregisterAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info) |
67c8f8a1 | 3612 | { |
263eeeab A |
3613 | LogInfo("DeregisterAutoTunnelServiceRecords %##s", info->domain.c); |
3614 | if (info->AutoTunnelTarget.resrec.RecordType > kDNSRecordTypeDeregistering) | |
67c8f8a1 | 3615 | { |
263eeeab | 3616 | mStatus err = mDNS_Deregister(m, &info->AutoTunnelTarget); |
67c8f8a1 A |
3617 | if (err) |
3618 | { | |
263eeeab A |
3619 | info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeUnregistered; |
3620 | LogMsg("DeregisterAutoTunnelServiceRecords error %d deregistering AutoTunnelTarget %##s", err, info->AutoTunnelTarget.namestorage.c); | |
67c8f8a1 | 3621 | } |
263eeeab | 3622 | else LogInfo("DeregisterAutoTunnelServiceRecords: Deregistered AutoTunnel Target Record"); |
67c8f8a1 | 3623 | |
67c8f8a1 | 3624 | } |
263eeeab | 3625 | else LogInfo("DeregisterAutoTunnelServiceRecords: Not deregistering Target record state:%d", info->AutoTunnelService.resrec.RecordType); |
67c8f8a1 | 3626 | |
263eeeab | 3627 | if (info->AutoTunnelService.resrec.RecordType > kDNSRecordTypeDeregistering) |
67c8f8a1 | 3628 | { |
263eeeab | 3629 | mStatus err = mDNS_Deregister(m, &info->AutoTunnelService); |
67c8f8a1 A |
3630 | if (err) |
3631 | { | |
263eeeab A |
3632 | info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeUnregistered; |
3633 | LogMsg("DeregisterAutoTunnelServiceRecords error %d deregistering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c); | |
67c8f8a1 | 3634 | } |
263eeeab A |
3635 | else LogInfo("DeregisterAutoTunnelServiceRecords: Deregistered AutoTunnel Service Record"); |
3636 | ||
3637 | } | |
3638 | else LogInfo("DeregisterAutoTunnelServiceRecords: Not deregistering service records state:%d", info->AutoTunnelService.resrec.RecordType); | |
3639 | ||
3640 | DeregisterAutoTunnelHostRecord(m, info); | |
3641 | } | |
3642 | ||
3643 | // Caller should hold the lock. We don't call mDNS_Register (which acquires the lock) in this function because | |
3644 | // sometimes the caller may already be holding the lock e.g., SetupLocalAutoTunnelInterface_internal and sometimes | |
3645 | // not e.g., AutoTunnelHostNameChanged | |
3646 | mDNSlocal void RegisterAutoTunnelDevInfoRecord(mDNS *m, DomainAuthInfo *info) | |
3647 | { | |
3648 | mStatus err; | |
3649 | ||
3650 | if (!m->mDNS_busy) LogMsg("RegisterAutoTunnelDevInfoRecord: Lock not held"); | |
3651 | // Note: | |
3652 | // a. We use AutoTunnelNAT.clientContext to infer that SetupLocalAutoTunnelInterface_internal has been | |
3653 | // called at least once with some Services/Records in the domain and hence it is safe to register | |
3654 | // records when this function is called. | |
3655 | // | |
3656 | // b. We use zero Requested port to infer that we should not be calling Register anymore as it might | |
3657 | // be shutdown or the DomainAuthInfo is going away. | |
3658 | // | |
3659 | // We can use a different set of state variables to track the above as the records registered in | |
3660 | // this function is not dependent on NAT traversal info. For the sake of simplicity, we just | |
3661 | // reuse the NAT variables. | |
3662 | ||
3663 | // Set up device info record | |
3664 | if (info->AutoTunnelNAT.clientContext && !mDNSIPPortIsZero(info->AutoTunnelNAT.RequestedPort) && info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) | |
3665 | { | |
3666 | ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain); | |
3667 | mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6; | |
3668 | mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6); | |
3669 | mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len); | |
3670 | info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string | |
3671 | info->AutoTunnelDeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string | |
3672 | info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique; | |
3673 | ||
3674 | err = mDNS_Register_internal(m, &info->AutoTunnelDeviceInfo); | |
3675 | if (err) LogMsg("RegisterAutoTunnelDevInfoRecord error %d registering AutoTunnelDeviceInfo %##s", err, info->AutoTunnelDeviceInfo.namestorage.c); | |
3676 | else LogInfo("RegisterAutoTunnelDevInfoRecord registering AutoTunnelDeviceInfo %##s", info->AutoTunnelDeviceInfo.namestorage.c); | |
67c8f8a1 | 3677 | } |
263eeeab A |
3678 | } |
3679 | ||
3680 | #ifndef NO_SECURITYFRAMEWORK | |
3681 | mDNSlocal void DeregisterAutoTunnelDevInfoRecord(mDNS *m, DomainAuthInfo *info) | |
3682 | { | |
3683 | LogInfo("DeregisterAutoTunnelDevInfoRecord %##s", info->domain.c); | |
67c8f8a1 A |
3684 | |
3685 | if (info->AutoTunnelDeviceInfo.resrec.RecordType > kDNSRecordTypeDeregistering) | |
3686 | { | |
3687 | mStatus err = mDNS_Deregister(m, &info->AutoTunnelDeviceInfo); | |
3688 | if (err) | |
3689 | { | |
3690 | info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered; | |
263eeeab A |
3691 | LogMsg("DeregisterAutoTunnelDevInfoRecord error %d deregistering AutoTunnelDeviceInfo %##s", err, info->AutoTunnelDeviceInfo.namestorage.c); |
3692 | } | |
3693 | else LogInfo("DeregisterAutoTunnelDevInfoRecord: Deregistered AutoTunnel Device Info"); | |
3694 | } | |
3695 | else LogInfo("DeregisterAutoTunnelDevInfoRecord: Not deregistering DeviceInfo Record state:%d", info->AutoTunnelDeviceInfo.resrec.RecordType); | |
3696 | } | |
3697 | #endif | |
3698 | ||
3699 | ||
3700 | // Caller should hold the lock. We don't call mDNS_Register (which acquires the lock) in this function because | |
3701 | // sometimes the caller may already be holding the lock e.g., SetupLocalAutoTunnelInterface_internal and sometimes | |
3702 | // not e.g., AutoTunnelHostNameChanged | |
3703 | mDNSlocal void RegisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info) | |
3704 | { | |
3705 | mStatus err; | |
3706 | ||
3707 | if (!m->mDNS_busy) LogMsg("RegisterAutoTunnel6Record: ERROR!! Lock not held"); | |
3708 | ||
3709 | // We deregister the AutoTunnel6Record during sleep and come back here (AutoTunnelRecordCallback) to | |
3710 | // register the address if needed. During that time, we might get a network change event which finds | |
3711 | // that the utun interface exists and tries to register the AutoTunnel6Record which should be stopped. | |
3712 | // Also the RelayAddress is reinitialized during that process which in turn causes the AutoTunnelRecordCallback | |
3713 | // to re-register again. To stop these, we check for the SleepState and register only if we are awake. | |
3714 | if (m->SleepState != SleepState_Awake) | |
3715 | { | |
3716 | LogInfo("RegisterAutoTunnel6Record: Not in awake state, SleepState %d", m->SleepState); | |
3717 | return; | |
3718 | } | |
3719 | ||
263eeeab A |
3720 | // if disabled administratively, don't register |
3721 | if (!m->RegisterAutoTunnel6 || DisableInboundRelayConnection) | |
3722 | { | |
3723 | LogInfo("RegisterAutoTunnel6Record: registration Disabled RegisterAutoTunnel6 %d, DisableInbound %d", | |
3724 | m->RegisterAutoTunnel6, DisableInboundRelayConnection); | |
3725 | return; | |
3726 | } | |
3727 | // | |
3728 | // If we have a valid Relay address, we need to register it now. When we got a valid address, we may not | |
3729 | // have registered it because it was waiting for at least one service to become active in the BTMM domain. | |
3730 | // During network change event, we might be called multiple times while the "Connectivity" key did not | |
3731 | // change, so check to see if the value has changed. This can also be zero when we are deregistering and | |
3732 | // getting called from the AutoTunnelRecordCallback | |
3733 | ||
3c427d54 | 3734 | if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn)) |
263eeeab A |
3735 | { |
3736 | LogInfo("RegisterAutoTunnel6Record: Relay address is zero, not registering"); | |
3737 | return; | |
3738 | } | |
3739 | ||
3740 | if ((info->AutoTunnel6Record.resrec.RecordType > kDNSRecordTypeDeregistering) && | |
3c427d54 | 3741 | (mDNSSameIPv6Address(info->AutoTunnel6Record.resrec.rdata->u.ipv6, m->AutoTunnelRelayAddrIn))) |
263eeeab | 3742 | { |
3c427d54 | 3743 | LogInfo("RegisterAutoTunnel6Record: Relay address %.16a same, not registering", &m->AutoTunnelRelayAddrIn); |
263eeeab A |
3744 | return; |
3745 | } | |
3746 | ||
3747 | // Note: | |
3748 | // a. We use AutoTunnelNAT.clientContext to infer that SetupLocalAutoTunnelInterface_internal has been | |
3749 | // called at least once with some Services/Records in the domain and hence it is safe to register | |
3750 | // records when this function is called. | |
3751 | // | |
3752 | // b. We use zero Requested port to infer that we should not be calling Register anymore as it might | |
3753 | // be shutdown or the DomainAuthInfo is going away. | |
3754 | // | |
3755 | // We can use a different set of state variables to track the above as the records registered in | |
3756 | // this function is not dependent on NAT traversal info. For the sake of simplicity, we just | |
3757 | // reuse the NAT variables. | |
3758 | ||
3759 | if (info->AutoTunnelNAT.clientContext && !mDNSIPPortIsZero(info->AutoTunnelNAT.RequestedPort) && | |
3760 | info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered) | |
3761 | { | |
3762 | AssignDomainName (&info->AutoTunnel6Record.namestorage, (const domainname*) "\x0C" "_autotunnel6"); | |
3763 | AppendDomainLabel(&info->AutoTunnel6Record.namestorage, &m->hostlabel); | |
3764 | AppendDomainName (&info->AutoTunnel6Record.namestorage, &info->domain); | |
3c427d54 | 3765 | info->AutoTunnel6Record.resrec.rdata->u.ipv6 = m->AutoTunnelRelayAddrIn; |
263eeeab A |
3766 | info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeKnownUnique; |
3767 | ||
3768 | err = mDNS_Register_internal(m, &info->AutoTunnel6Record); | |
3769 | if (err) LogMsg("RegisterAutoTunnel6Record error %d registering AutoTunnel6 Record %##s", err, info->AutoTunnel6Record.namestorage.c); | |
3770 | else LogInfo("RegisterAutoTunnel6Record registering AutoTunnel6 Record %##s", info->AutoTunnel6Record.namestorage.c); | |
3771 | ||
3772 | LogInfo("AutoTunnel6 server listening for connections on %##s[%.16a] :%##s[%.16a]", | |
3c427d54 | 3773 | info->AutoTunnel6Record.namestorage.c, &m->AutoTunnelRelayAddrIn, |
263eeeab A |
3774 | info->AutoTunnelHostRecord.namestorage.c, &m->AutoTunnelHostAddr); |
3775 | ||
3c427d54 | 3776 | } else {LogInfo("RegisterAutoTunnel6Record: client context %p, RequestedPort %d, Address %.16a, record type %d", info->AutoTunnelNAT.clientContext, info->AutoTunnelNAT.RequestedPort, &m->AutoTunnelRelayAddrIn, info->AutoTunnel6Record.resrec.RecordType);} |
263eeeab A |
3777 | |
3778 | RegisterAutoTunnelHostRecord(m, info); | |
3779 | // When the AutoTunnel6 record comes up, we need to kick racoon and update the status. | |
3780 | // If we had a port mapping, we would have done it in RegisterAutoTunnelServiceRecords. | |
3781 | // If we don't have a port mapping, we need to do it here. | |
3782 | UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections | |
3783 | UpdateAutoTunnelDomainStatus(m, info); | |
3784 | } | |
3785 | ||
3786 | mDNSlocal void DeregisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info) | |
3787 | { | |
3788 | LogInfo("DeregisterAutoTunnel6Record %##s", info->domain.c); | |
3789 | ||
3790 | if (info->AutoTunnel6Record.resrec.RecordType > kDNSRecordTypeDeregistering) | |
3791 | { | |
3792 | mStatus err = mDNS_Deregister(m, &info->AutoTunnel6Record); | |
3793 | if (err) | |
3794 | { | |
3795 | info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeUnregistered; | |
3796 | info->AutoTunnel6Record.resrec.rdata->u.ipv6 = zerov6Addr; | |
3797 | LogMsg("DeregisterAutoTunnel6Record error %d deregistering AutoTunnel6Record %##s", err, info->AutoTunnel6Record.namestorage.c); | |
67c8f8a1 | 3798 | } |
263eeeab | 3799 | else LogInfo("DeregisterAutoTunnel6Record: Deregistered AutoTunnel6 Record"); |
67c8f8a1 | 3800 | } |
263eeeab A |
3801 | else LogInfo("DeregisterAutoTunnel6Record: Not deregistering AuoTunnel6 record state:%d", info->AutoTunnel6Record.resrec.RecordType); |
3802 | ||
3803 | DeregisterAutoTunnelHostRecord(m, info); | |
3804 | // UpdateAutoTunnelDomainStatus is careful enough not to turn it on if we don't have | |
3805 | // a external port mapping. Otherwise, it will be turned off. | |
3806 | mDNS_Lock(m); | |
3807 | UpdateAutoTunnelDomainStatus(m, info); | |
3808 | mDNS_Unlock(m); | |
67c8f8a1 A |
3809 | } |
3810 | ||
3811 | mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result) | |
3812 | { | |
3813 | DomainAuthInfo *info = (DomainAuthInfo *)rr->RecordContext; | |
3814 | if (result == mStatus_MemFree) | |
3815 | { | |
32bb7e43 | 3816 | LogInfo("AutoTunnelRecordCallback MemFree %s", ARDisplayString(m, rr)); |
5e65c77f A |
3817 | // Reset the host record namestorage to force high-level PTR/SRV/TXT to deregister |
3818 | if (rr == &info->AutoTunnelHostRecord) | |
3819 | { | |
3820 | rr->namestorage.c[0] = 0; | |
3821 | m->NextSRVUpdate = NonZeroTime(m->timenow); | |
263eeeab A |
3822 | LogInfo("AutoTunnelRecordCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); |
3823 | } | |
3824 | if (m->ShutdownTime) {LogInfo("AutoTunnelRecordCallback: Shutdown, returning");return;} | |
3825 | if (rr == &info->AutoTunnelHostRecord) | |
3826 | { | |
3827 | LogInfo("AutoTunnelRecordCallback: calling RegisterAutoTunnelHostRecord"); | |
3828 | RegisterAutoTunnelHostRecord(m,info); | |
3829 | } | |
3830 | else if (rr == &info->AutoTunnelDeviceInfo) | |
3831 | { | |
3832 | LogInfo("AutoTunnelRecordCallback: Calling RegisterAutoTunnelDevInfoRecord"); | |
3833 | RegisterAutoTunnelDevInfoRecord(m,info); | |
3834 | } | |
3835 | else if (rr == &info->AutoTunnelService || rr == &info->AutoTunnelTarget) | |
3836 | { | |
3837 | LogInfo("AutoTunnelRecordCallback: Calling RegisterAutoTunnelServiceRecords"); | |
3838 | RegisterAutoTunnelServiceRecords(m,info); | |
3839 | } | |
3840 | else if (rr == &info->AutoTunnel6Record) | |
3841 | { | |
3842 | LogInfo("AutoTunnelRecordCallback: Calling RegisterAutoTunnel6Record"); | |
3843 | info->AutoTunnel6Record.resrec.rdata->u.ipv6 = zerov6Addr; | |
3844 | RegisterAutoTunnel6Record(m,info); | |
5e65c77f | 3845 | } |
67c8f8a1 A |
3846 | } |
3847 | } | |
3848 | ||
263eeeab A |
3849 | #ifndef NO_SECURITYFRAMEWORK |
3850 | mDNSlocal void AutoTunnelDeleteAuthInfoState(mDNS *m, DomainAuthInfo *info) | |
32bb7e43 | 3851 | { |
263eeeab A |
3852 | LogInfo("AutoTunnelDeleteAuthInfoState: Cleaning up state releated to Domain AuthInfo %##s", info->domain.c); |
3853 | ||
3854 | m->NextSRVUpdate = NonZeroTime(m->timenow); | |
3855 | DeregisterAutoTunnelDevInfoRecord(m, info); | |
3856 | DeregisterAutoTunnelServiceRecords(m, info); | |
3857 | DeregisterAutoTunnel6Record(m, info); | |
3858 | UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections | |
3859 | UpdateAutoTunnelDomainStatus(m, info); | |
32bb7e43 | 3860 | } |
263eeeab A |
3861 | #endif // ndef NO_SECURITYFRAMEWORK |
3862 | ||
67c8f8a1 A |
3863 | mDNSlocal void AutoTunnelNATCallback(mDNS *m, NATTraversalInfo *n) |
3864 | { | |
3865 | DomainAuthInfo *info = (DomainAuthInfo *)n->clientContext; | |
32bb7e43 | 3866 | LogInfo("AutoTunnelNATCallback Result %d %.4a Internal %d External %d %#s.%##s", |
96f69b28 | 3867 | n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), m->hostlabel.c, info->domain.c); |
67c8f8a1 | 3868 | |
030b743d | 3869 | m->NextSRVUpdate = NonZeroTime(m->timenow); |
263eeeab A |
3870 | LogInfo("AutoTunnelNATCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); |
3871 | ||
3872 | DeregisterAutoTunnelServiceRecords(m, info); | |
3873 | RegisterAutoTunnelServiceRecords(m, info); | |
32bb7e43 | 3874 | |
263eeeab | 3875 | UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections |
67c8f8a1 | 3876 | |
5e65c77f | 3877 | UpdateAutoTunnelDomainStatus(m, (DomainAuthInfo *)n->clientContext); |
67c8f8a1 A |
3878 | } |
3879 | ||
96f69b28 A |
3880 | mDNSlocal void AbortDeregistration(mDNS *const m, AuthRecord *rr) |
3881 | { | |
3882 | if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) | |
3883 | { | |
32bb7e43 | 3884 | LogInfo("Aborting deregistration of %s", ARDisplayString(m, rr)); |
96f69b28 A |
3885 | CompleteDeregistration(m, rr); |
3886 | } | |
3887 | else if (rr->resrec.RecordType != kDNSRecordTypeUnregistered) | |
3888 | LogMsg("AbortDeregistration ERROR RecordType %02X for %s", ARDisplayString(m, rr)); | |
3889 | } | |
3890 | ||
263eeeab A |
3891 | mDNSlocal void AutoTunnelHostNameChanged(mDNS *m, DomainAuthInfo *info) |
3892 | { | |
3893 | LogInfo("AutoTunnelHostNameChanged %#s.%##s", m->hostlabel.c, info->domain.c); | |
3894 | ||
294beb6e | 3895 | #ifndef NO_SECURITYFRAMEWORK |
263eeeab | 3896 | DeregisterAutoTunnelDevInfoRecord(m, info); |
294beb6e | 3897 | #endif |
263eeeab A |
3898 | DeregisterAutoTunnelServiceRecords(m, info); |
3899 | DeregisterAutoTunnel6Record(m, info); | |
3900 | RegisterAutoTunnelServiceRecords(m, info); | |
3901 | ||
3902 | mDNS_Lock(m); | |
3903 | RegisterAutoTunnelDevInfoRecord(m, info); | |
3904 | RegisterAutoTunnel6Record(m, info); | |
3905 | m->NextSRVUpdate = NonZeroTime(m->timenow); | |
3906 | mDNS_Unlock(m); | |
3907 | } | |
3908 | ||
3909 | mDNSlocal void SetupLocalAutoTunnel6Records(mDNS *const m, DomainAuthInfo *info) | |
3910 | { | |
3911 | AbortDeregistration(m, &info->AutoTunnelDeviceInfo); | |
3912 | AbortDeregistration(m, &info->AutoTunnel6Record); | |
3913 | ||
3914 | // When the BTMM is turned on/off too quickly, following things happen. | |
3915 | // | |
3916 | // 1. Turning off BTMM triggers deregistration of the DevInfo/AutoTunnel6 etc. records | |
3917 | // 2. While (1) is in progress, the BTMM is turned on | |
3918 | // | |
3919 | // At step (2), mDNS_SetSecretForDomain clears info->deltime indicating that the domain is valid | |
3920 | // while we have not processed the turning off BTMM completely. Hence, we end up calling this | |
3921 | // function to re-register the records. AbortDeregistration above aborts the Deregistration as the | |
3922 | // records are still in Deregistering state and in AutoTunnelRecordCallback we end up registering | |
3923 | // again. So, we have to be careful below to not call mDNS_SetupResourceRecord again which will | |
3924 | // reset the state to Unregistered and registering again will lead to error as it is registered | |
3925 | // and already in the list. Hence, register below only if needed. | |
3926 | ||
3927 | if (info->AutoTunnelDeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered || | |
3928 | info->AutoTunnel6Record.resrec.RecordType != kDNSRecordTypeUnregistered) | |
3929 | { | |
3930 | LogInfo("SetupLocalAutoTunnel6Records: AutoTunnel Records not in Unregistered state: Device: %d, AutoTunnel6:%d", | |
3931 | info->AutoTunnelDeviceInfo.resrec.RecordType, info->AutoTunnel6Record.resrec.RecordType); | |
3932 | } | |
3933 | ||
3934 | if (info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) | |
3935 | { | |
294beb6e | 3936 | mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); |
263eeeab A |
3937 | RegisterAutoTunnelDevInfoRecord(m, info); |
3938 | } | |
3939 | if (info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered) | |
3940 | { | |
294beb6e | 3941 | mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); |
263eeeab A |
3942 | RegisterAutoTunnel6Record(m, info); |
3943 | } | |
3944 | ||
3945 | UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections | |
3946 | ||
3947 | UpdateAutoTunnelDomainStatus(m, info); | |
3948 | m->NextSRVUpdate = NonZeroTime(m->timenow); | |
3949 | } | |
3950 | ||
67c8f8a1 A |
3951 | // Before SetupLocalAutoTunnelInterface_internal is called, |
3952 | // m->AutoTunnelHostAddr.b[0] must be non-zero, and there must be at least one TunnelClient or TunnelServer | |
3953 | // Must be called with the lock held | |
263eeeab | 3954 | mDNSexport void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool servicesStarting) |
67c8f8a1 | 3955 | { |
263eeeab | 3956 | // 1. Configure the local IPv6 ULA BTMM address |
67c8f8a1 A |
3957 | if (!m->AutoTunnelHostAddrActive) |
3958 | { | |
3959 | m->AutoTunnelHostAddrActive = mDNStrue; | |
263eeeab | 3960 | LogInfo("SetupLocalAutoTunnelInterface_internal: Setting up AutoTunnel address %.16a", &m->AutoTunnelHostAddr); |
67c8f8a1 A |
3961 | (void)mDNSAutoTunnelInterfaceUpDown(kmDNSUp, m->AutoTunnelHostAddr.b); |
3962 | } | |
3963 | ||
3964 | // 2. If we have at least one server (pending) listening, publish our records | |
263eeeab A |
3965 | // The services may not be in the list when it is first trying to resolve the target of the SRV record. |
3966 | // servicesStarting is an indication of that. Use that instead of looking up in the list of Services/Records. | |
3967 | if (servicesStarting || TunnelServers(m)) | |
67c8f8a1 A |
3968 | { |
3969 | DomainAuthInfo *info; | |
3970 | for (info = m->AuthInfoList; info; info = info->next) | |
3971 | { | |
3972 | if (info->AutoTunnel && !info->deltime && !info->AutoTunnelNAT.clientContext) | |
3973 | { | |
263eeeab A |
3974 | // If we just resurrected a DomainAuthInfo that is still deregistering, we need to abort the |
3975 | // deregistration process before re-using the AuthRecord memory | |
3976 | // | |
3977 | // Note: We don't need the same caution as in SetupLocalAutoTunnel6Records (see the comments there) | |
3978 | // as AutoTunnelRecordCallback (called as a result of AbortDeregistration) will not end up registering | |
3979 | // the records because clientContext is still NULL | |
3980 | ||
3981 | AbortDeregistration(m, &info->AutoTunnelTarget); | |
96f69b28 | 3982 | AbortDeregistration(m, &info->AutoTunnelService); |
263eeeab | 3983 | AbortDeregistration(m, &info->AutoTunnelHostRecord); |
96f69b28 | 3984 | |
263eeeab | 3985 | mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, |
294beb6e | 3986 | kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); |
263eeeab | 3987 | mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV, kHostNameTTL, |
294beb6e | 3988 | kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); |
263eeeab | 3989 | mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, |
294beb6e | 3990 | kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); |
67c8f8a1 A |
3991 | |
3992 | // Try to get a NAT port mapping for the AutoTunnelService | |
3993 | info->AutoTunnelNAT.clientCallback = AutoTunnelNATCallback; | |
3994 | info->AutoTunnelNAT.clientContext = info; | |
3995 | info->AutoTunnelNAT.Protocol = NATOp_MapUDP; | |
263eeeab A |
3996 | info->AutoTunnelNAT.IntPort = IPSECPort; |
3997 | info->AutoTunnelNAT.RequestedPort = IPSECPort; | |
67c8f8a1 A |
3998 | info->AutoTunnelNAT.NATLease = 0; |
3999 | mStatus err = mDNS_StartNATOperation_internal(m, &info->AutoTunnelNAT); | |
263eeeab A |
4000 | if (err) LogMsg("SetupLocalAutoTunnelInterface_internal: error %d starting NAT mapping", err); |
4001 | ||
4002 | // Register the records that can be done without communicating with the NAT | |
4003 | // | |
4004 | // Note: This should be done after we setup the AutoTunnelNAT information | |
4005 | // as some of the fields in that structure are used to infer other information | |
4006 | // e.g., is it okay to register now ? | |
4007 | ||
4008 | SetupLocalAutoTunnel6Records(m, info); | |
4009 | ||
4010 | } | |
4011 | } | |
4012 | } | |
4013 | } | |
4014 | ||
4015 | mDNSlocal mStatus AutoTunnelSetKeys(ClientTunnel *tun, mDNSBool AddNew) | |
4016 | { | |
4017 | mDNSv6Addr loc_outer6; | |
4018 | mDNSv6Addr rmt_outer6; | |
4019 | ||
4020 | // When we are tunneling over IPv6 Relay address, the port number is zero | |
4021 | if (mDNSIPPortIsZero(tun->rmt_outer_port)) | |
4022 | { | |
4023 | loc_outer6 = tun->loc_outer6; | |
4024 | rmt_outer6 = tun->rmt_outer6; | |
4025 | } | |
4026 | else | |
4027 | { | |
4028 | loc_outer6 = zerov6Addr; | |
4029 | loc_outer6.b[0] = tun->loc_outer.b[0]; | |
4030 | loc_outer6.b[1] = tun->loc_outer.b[1]; | |
4031 | loc_outer6.b[2] = tun->loc_outer.b[2]; | |
4032 | loc_outer6.b[3] = tun->loc_outer.b[3]; | |
4033 | ||
4034 | rmt_outer6 = zerov6Addr; | |
4035 | rmt_outer6.b[0] = tun->rmt_outer.b[0]; | |
4036 | rmt_outer6.b[1] = tun->rmt_outer.b[1]; | |
4037 | rmt_outer6.b[2] = tun->rmt_outer.b[2]; | |
4038 | rmt_outer6.b[3] = tun->rmt_outer.b[3]; | |
4039 | } | |
4040 | ||
294beb6e | 4041 | return(mDNSAutoTunnelSetKeys(AddNew ? kmDNSAutoTunnelSetKeysReplace : kmDNSAutoTunnelSetKeysDelete, tun->loc_inner.b, loc_outer6.b, kRacoonPort, tun->rmt_inner.b, rmt_outer6.b, mDNSVal16(tun->rmt_outer_port), tun->prefix, SkipLeadingLabels(&tun->dstname, 1))); |
263eeeab A |
4042 | } |
4043 | ||
4044 | // If the EUI-64 part of the IPv6 ULA matches, then that means the two addresses point to the same machine | |
4045 | #define mDNSSameClientTunnel(A,B) ((A)->l[2] == (B)->l[2] && (A)->l[3] == (B)->l[3]) | |
4046 | ||
4047 | mDNSlocal void ReissueBlockedQuestionWithType(mDNS *const m, domainname *d, mDNSBool success, mDNSu16 qtype) | |
4048 | { | |
4049 | DNSQuestion *q = m->Questions; | |
4050 | while (q) | |
4051 | { | |
4052 | if (q->NoAnswer == NoAnswer_Suspended && q->qtype == qtype && q->AuthInfo && q->AuthInfo->AutoTunnel && SameDomainName(&q->qname, d)) | |
4053 | { | |
4054 | LogInfo("Restart %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); | |
4055 | mDNSQuestionCallback *tmp = q->QuestionCallback; | |
4056 | q->QuestionCallback = AutoTunnelCallback; // Set QuestionCallback to suppress another call back to AddNewClientTunnel | |
4057 | mDNS_StopQuery(m, q); | |
4058 | mDNS_StartQuery(m, q); | |
4059 | q->QuestionCallback = tmp; // Restore QuestionCallback back to the real value | |
4060 | if (!success) q->NoAnswer = NoAnswer_Fail; | |
4061 | // When we call mDNS_StopQuery, it's possible for other subordinate questions like the GetZoneData query to be cancelled too. | |
4062 | // In general we have to assume that the question list might have changed in arbitrary ways. | |
4063 | // This code is itself called from a question callback, so the m->CurrentQuestion mechanism is | |
4064 | // already in use. The safest solution is just to go back to the start of the list and start again. | |
4065 | // In principle this sounds like an n^2 algorithm, but in practice we almost always activate | |
4066 | // just one suspended question, so it's really a 2n algorithm. | |
4067 | q = m->Questions; | |
4068 | } | |
4069 | else | |
4070 | q = q->next; | |
4071 | } | |
4072 | } | |
4073 | ||
4074 | mDNSlocal void ReissueBlockedQuestions(mDNS *const m, domainname *d, mDNSBool success) | |
4075 | { | |
4076 | // 1. We deliberately restart AAAA queries before A queries, because in the common case where a BTTM host has | |
4077 | // a v6 address but no v4 address, we prefer the caller to get the positive AAAA response before the A NXDOMAIN. | |
4078 | // 2. In the case of AAAA queries, if our tunnel setup failed, then we return a deliberate failure indication to the caller -- | |
4079 | // even if the name does have a valid AAAA record, we don't want clients trying to connect to it without a properly encrypted tunnel. | |
4080 | // 3. For A queries we never fabricate failures -- if a BTTM service is really using raw IPv4, then it doesn't need the IPv6 tunnel. | |
4081 | ReissueBlockedQuestionWithType(m, d, success, kDNSType_AAAA); | |
4082 | ReissueBlockedQuestionWithType(m, d, mDNStrue, kDNSType_A); | |
4083 | } | |
4084 | ||
4085 | mDNSlocal void UnlinkAndReissueBlockedQuestions(mDNS *const m, ClientTunnel *tun, mDNSBool success) | |
4086 | { | |
4087 | ClientTunnel **p = &m->TunnelClients; | |
4088 | while (*p != tun && *p) p = &(*p)->next; | |
4089 | if (*p) *p = tun->next; | |
4090 | ReissueBlockedQuestions(m, &tun->dstname, success); | |
4091 | LogInfo("UnlinkAndReissueBlockedQuestions: Disposing ClientTunnel %p", tun); | |
4092 | freeL("ClientTunnel", tun); | |
4093 | } | |
4094 | ||
4095 | mDNSlocal mDNSBool TunnelClientDeleteMatching(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel) | |
4096 | { | |
4097 | ClientTunnel **p; | |
4098 | mDNSBool needSetKeys = mDNStrue; | |
4099 | ||
4100 | p = &tun->next; | |
4101 | while (*p) | |
4102 | { | |
4103 | // Is this a tunnel to the same host that we are trying to setup now? | |
4104 | if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next; | |
4105 | else | |
4106 | { | |
4107 | ClientTunnel *old = *p; | |
4108 | if (v6Tunnel) | |
4109 | { | |
4110 | if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; } | |
4111 | LogInfo("TunnelClientDeleteMatching: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4112 | if (old->q.ThisQInterval >= 0) | |
4113 | { | |
4114 | LogInfo("TunnelClientDeleteMatching: Stopping query on IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4115 | mDNS_StopQuery(m, &old->q); | |
4116 | } | |
4117 | else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) || | |
4118 | !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) || | |
4119 | !mDNSSameIPv6Address(old->loc_outer6, tun->loc_outer6) || | |
4120 | !mDNSSameIPv6Address(old->rmt_outer6, tun->rmt_outer6)) | |
4121 | { | |
4122 | // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or | |
4123 | // the other parameters of the tunnel are different | |
4124 | LogInfo("TunnelClientDeleteMatching: Deleting existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4125 | AutoTunnelSetKeys(old, mDNSfalse); | |
4126 | } | |
4127 | else | |
4128 | { | |
4129 | // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old | |
4130 | // as "tun" and "old" are identical | |
4131 | LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, | |
4132 | &old->rmt_inner); | |
4133 | needSetKeys = mDNSfalse; | |
4134 | } | |
4135 | } | |
4136 | else | |
4137 | { | |
4138 | if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; } | |
4139 | LogInfo("TunnelClientDeleteMatching: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4140 | if (old->q.ThisQInterval >= 0) | |
4141 | { | |
4142 | LogInfo("TunnelClientDeleteMatching: Stopping query on IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4143 | mDNS_StopQuery(m, &old->q); | |
4144 | } | |
4145 | else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) || | |
4146 | !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) || | |
4147 | !mDNSSameIPv4Address(old->loc_outer, tun->loc_outer) || | |
4148 | !mDNSSameIPv4Address(old->rmt_outer, tun->rmt_outer) || | |
4149 | !mDNSSameIPPort(old->rmt_outer_port, tun->rmt_outer_port)) | |
4150 | { | |
4151 | // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or | |
4152 | // the other parameters of the tunnel are different | |
4153 | LogInfo("TunnelClientDeleteMatching: Deleting existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4154 | AutoTunnelSetKeys(old, mDNSfalse); | |
4155 | } | |
4156 | else | |
4157 | { | |
4158 | // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old | |
4159 | // as "tun" and "old" are identical | |
4160 | LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, | |
4161 | &old->rmt_inner); | |
4162 | needSetKeys = mDNSfalse; | |
4163 | } | |
4164 | } | |
4165 | ||
4166 | *p = old->next; | |
4167 | LogInfo("TunnelClientDeleteMatching: Disposing ClientTunnel %p", old); | |
4168 | freeL("ClientTunnel", old); | |
4169 | } | |
4170 | } | |
4171 | return needSetKeys; | |
4172 | } | |
4173 | ||
4174 | // v6Tunnel indicates whether to delete a tunnel whose outer header is IPv6. If false, outer IPv4 | |
4175 | // tunnel will be deleted | |
4176 | mDNSlocal void TunnelClientDeleteAny(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel) | |
4177 | { | |
4178 | ClientTunnel **p; | |
4179 | ||
4180 | p = &tun->next; | |
4181 | while (*p) | |
4182 | { | |
4183 | // If there is more than one client tunnel to the same host, delete all of them. | |
4184 | // We do this by just checking against the EUI64 rather than the full address | |
4185 | if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next; | |
4186 | else | |
4187 | { | |
4188 | ClientTunnel *old = *p; | |
4189 | if (v6Tunnel) | |
4190 | { | |
4191 | if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;} | |
4192 | LogInfo("TunnelClientDeleteAny: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4193 | } | |
4194 | else | |
4195 | { | |
4196 | if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;} | |
4197 | LogInfo("TunnelClientDeleteAny: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4198 | } | |
4199 | if (old->q.ThisQInterval >= 0) | |
4200 | { | |
4201 | LogInfo("TunnelClientDeleteAny: Stopping query on AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4202 | mDNS_StopQuery(m, &old->q); | |
4203 | } | |
4204 | else | |
4205 | { | |
4206 | LogInfo("TunnelClientDeleteAny: Deleting existing AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); | |
4207 | AutoTunnelSetKeys(old, mDNSfalse); | |
67c8f8a1 | 4208 | } |
263eeeab A |
4209 | *p = old->next; |
4210 | LogInfo("TunnelClientDeleteAny: Disposing ClientTunnel %p", old); | |
4211 | freeL("ClientTunnel", old); | |
67c8f8a1 A |
4212 | } |
4213 | } | |
4214 | } | |
4215 | ||
263eeeab | 4216 | mDNSlocal void TunnelClientFinish(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer) |
67c8f8a1 | 4217 | { |
263eeeab A |
4218 | mDNSBool needSetKeys = mDNStrue; |
4219 | ClientTunnel *tun = (ClientTunnel *)question->QuestionContext; | |
4220 | mDNSBool v6Tunnel = mDNSfalse; | |
67c8f8a1 | 4221 | |
263eeeab A |
4222 | // If the port is zero, then we have a relay address of the peer |
4223 | if (mDNSIPPortIsZero(tun->rmt_outer_port)) | |
4224 | v6Tunnel = mDNStrue; | |
67c8f8a1 | 4225 | |
263eeeab | 4226 | if (v6Tunnel) |
67c8f8a1 | 4227 | { |
263eeeab A |
4228 | LogInfo("TunnelClientFinish: Relay address %.16a", &answer->rdata->u.ipv6); |
4229 | tun->rmt_outer6 = answer->rdata->u.ipv6; | |
3c427d54 | 4230 | tun->loc_outer6 = m->AutoTunnelRelayAddrOut; |
263eeeab A |
4231 | } |
4232 | else | |
4233 | { | |
4234 | LogInfo("TunnelClientFinish: SRV target address %.4a", &answer->rdata->u.ipv4); | |
4235 | tun->rmt_outer = answer->rdata->u.ipv4; | |
4236 | mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} }; | |
4237 | tmpDst.ip.v4 = tun->rmt_outer; | |
4238 | mDNSAddr tmpSrc = zeroAddr; | |
4239 | mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst); | |
4240 | if (tmpSrc.type == mDNSAddrType_IPv4) tun->loc_outer = tmpSrc.ip.v4; | |
4241 | else tun->loc_outer = m->AdvertisedV4.ip.v4; | |
67c8f8a1 | 4242 | } |
67c8f8a1 | 4243 | |
263eeeab A |
4244 | question->ThisQInterval = -1; // So we know this tunnel setup has completed |
4245 | tun->loc_inner = m->AutoTunnelHostAddr; | |
030b743d | 4246 | |
263eeeab A |
4247 | // If we found a v6Relay address for our peer, delete all the v4Tunnels for our peer and |
4248 | // look for existing tunnels to see whether they have the same information for our peer. | |
4249 | // If not, delete them and need to create a new tunnel. If they are same, just use the | |
4250 | // same tunnel. Do the similar thing if we found a v4Tunnel end point for our peer. | |
4251 | TunnelClientDeleteAny(m, tun, !v6Tunnel); | |
4252 | needSetKeys = TunnelClientDeleteMatching(m, tun, v6Tunnel); | |
4253 | ||
4254 | if (needSetKeys) LogInfo("TunnelClientFinish: New %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner); | |
4255 | else LogInfo("TunnelClientFinish: Reusing exiting %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner); | |
4256 | ||
4257 | if (m->AutoTunnelHostAddr.b[0]) { mDNS_Lock(m); SetupLocalAutoTunnelInterface_internal(m, mDNSfalse); mDNS_Unlock(m); }; | |
4258 | ||
4259 | mStatus result = needSetKeys ? AutoTunnelSetKeys(tun, mDNStrue) : mStatus_NoError; | |
4260 | static char msgbuf[32]; | |
3c427d54 | 4261 | mDNS_snprintf(msgbuf, sizeof(msgbuf), "Tunnel setup - %d", result); |
263eeeab | 4262 | mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", result ? "failure" : "success", msgbuf, ""); |
263eeeab A |
4263 | // Kick off any questions that were held pending this tunnel setup |
4264 | ReissueBlockedQuestions(m, &tun->dstname, (result == mStatus_NoError) ? mDNStrue : mDNSfalse); | |
c9b9ae52 A |
4265 | } |
4266 | ||
67c8f8a1 A |
4267 | mDNSexport void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) |
4268 | { | |
4269 | ClientTunnel *tun = (ClientTunnel *)question->QuestionContext; | |
263eeeab | 4270 | |
32bb7e43 | 4271 | LogInfo("AutoTunnelCallback tun %p AddRecord %d rdlength %d qtype %d", tun, AddRecord, answer->rdlength, question->qtype); |
67c8f8a1 A |
4272 | |
4273 | if (!AddRecord) return; | |
4274 | mDNS_StopQuery(m, question); | |
4275 | ||
263eeeab A |
4276 | // If we are looking up the AAAA record for _autotunnel6, don't consider it as failure. |
4277 | // The code below will look for _autotunnel._udp SRV record followed by A record | |
4278 | if (tun->tc_state != TC_STATE_AAAA_PEER_RELAY && !answer->rdlength) | |
67c8f8a1 | 4279 | { |
32bb7e43 A |
4280 | LogInfo("AutoTunnelCallback NXDOMAIN %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); |
4281 | static char msgbuf[16]; | |
4282 | mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s lookup", DNSTypeName(question->qtype)); | |
4283 | mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", "failure", msgbuf, ""); | |
67c8f8a1 A |
4284 | UnlinkAndReissueBlockedQuestions(m, tun, mDNSfalse); |
4285 | return; | |
4286 | } | |
4287 | ||
263eeeab | 4288 | switch (tun->tc_state) |
67c8f8a1 | 4289 | { |
263eeeab A |
4290 | case TC_STATE_AAAA_PEER: |
4291 | if (question->qtype != kDNSType_AAAA) | |
4292 | { | |
4293 | LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER", question->qtype); | |
4294 | } | |
4295 | if (mDNSSameIPv6Address(answer->rdata->u.ipv6, m->AutoTunnelHostAddr)) | |
4296 | { | |
4297 | LogInfo("AutoTunnelCallback: suppressing tunnel to self %.16a", &answer->rdata->u.ipv6); | |
4298 | UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue); | |
4299 | return; | |
4300 | } | |
4301 | tun->rmt_inner = answer->rdata->u.ipv6; | |
4302 | LogInfo("AutoTunnelCallback:TC_STATE_AAAA_PEER: dst host %.16a", &tun->rmt_inner); | |
3c427d54 | 4303 | if (!mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrOut)) |
263eeeab A |
4304 | { |
4305 | LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA"); | |
4306 | tun->tc_state = TC_STATE_AAAA_PEER_RELAY; | |
4307 | question->qtype = kDNSType_AAAA; | |
4308 | AssignDomainName(&question->qname, (const domainname*) "\x0C" "_autotunnel6"); | |
4309 | } | |
67c8f8a1 A |
4310 | else |
4311 | { | |
263eeeab A |
4312 | LogInfo("AutoTunnelCallback: Looking up _autotunnel._udp SRV"); |
4313 | tun->tc_state = TC_STATE_SRV_PEER; | |
4314 | question->qtype = kDNSType_SRV; | |
4315 | AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); | |
67c8f8a1 | 4316 | } |
263eeeab A |
4317 | AppendDomainName(&question->qname, &tun->dstname); |
4318 | mDNS_StartQuery(m, &tun->q); | |
4319 | return; | |
4320 | case TC_STATE_AAAA_PEER_RELAY: | |
4321 | if (question->qtype != kDNSType_AAAA) | |
4322 | { | |
4323 | LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER_RELAY", question->qtype); | |
4324 | } | |
4325 | // If it failed, look for the SRV record. | |
4326 | if (!answer->rdlength) | |
4327 | { | |
4328 | LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA failed, trying SRV"); | |
4329 | tun->tc_state = TC_STATE_SRV_PEER; | |
4330 | AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); | |
4331 | AppendDomainName(&question->qname, &tun->dstname); | |
4332 | question->qtype = kDNSType_SRV; | |
4333 | mDNS_StartQuery(m, &tun->q); | |
4334 | return; | |
4335 | } | |
4336 | TunnelClientFinish(m, question, answer); | |
4337 | return; | |
4338 | case TC_STATE_SRV_PEER: | |
4339 | if (question->qtype != kDNSType_SRV) | |
4340 | { | |
4341 | LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_SRV_PEER", question->qtype); | |
4342 | } | |
4343 | LogInfo("AutoTunnelCallback: SRV target name %##s", answer->rdata->u.srv.target.c); | |
4344 | tun->tc_state = TC_STATE_ADDR_PEER; | |
4345 | AssignDomainName(&tun->q.qname, &answer->rdata->u.srv.target); | |
4346 | tun->rmt_outer_port = answer->rdata->u.srv.port; | |
4347 | question->qtype = kDNSType_A; | |
4348 | mDNS_StartQuery(m, &tun->q); | |
4349 | return; | |
4350 | case TC_STATE_ADDR_PEER: | |
4351 | if (question->qtype != kDNSType_A) | |
4352 | { | |
4353 | LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_ADDR_PEER", question->qtype); | |
4354 | } | |
4355 | TunnelClientFinish(m, question, answer); | |
4356 | return; | |
4357 | default: | |
4358 | LogMsg("AutoTunnelCallback: Unknown question %p", question); | |
67c8f8a1 | 4359 | } |
67c8f8a1 A |
4360 | } |
4361 | ||
4362 | // Must be called with the lock held | |
4363 | mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q) | |
4364 | { | |
4365 | ClientTunnel *p = mallocL("ClientTunnel", sizeof(ClientTunnel)); | |
4366 | if (!p) return; | |
294beb6e | 4367 | p->prefix = q->AuthInfo->AutoTunnel; |
67c8f8a1 | 4368 | AssignDomainName(&p->dstname, &q->qname); |
030b743d | 4369 | p->MarkedForDeletion = mDNSfalse; |
67c8f8a1 A |
4370 | p->loc_inner = zerov6Addr; |
4371 | p->loc_outer = zerov4Addr; | |
263eeeab | 4372 | p->loc_outer6 = zerov6Addr; |
67c8f8a1 A |
4373 | p->rmt_inner = zerov6Addr; |
4374 | p->rmt_outer = zerov4Addr; | |
263eeeab | 4375 | p->rmt_outer6 = zerov6Addr; |
67c8f8a1 | 4376 | p->rmt_outer_port = zeroIPPort; |
263eeeab | 4377 | p->tc_state = TC_STATE_AAAA_PEER; |
67c8f8a1 | 4378 | p->next = m->TunnelClients; |
030b743d | 4379 | m->TunnelClients = p; // We intentionally build list in reverse order |
67c8f8a1 A |
4380 | |
4381 | p->q.InterfaceID = mDNSInterface_Any; | |
4382 | p->q.Target = zeroAddr; | |
4383 | AssignDomainName(&p->q.qname, &q->qname); | |
4384 | p->q.qtype = kDNSType_AAAA; | |
4385 | p->q.qclass = kDNSClass_IN; | |
4386 | p->q.LongLived = mDNSfalse; | |
4387 | p->q.ExpectUnique = mDNStrue; | |
4388 | p->q.ForceMCast = mDNSfalse; | |
4389 | p->q.ReturnIntermed = mDNStrue; | |
263eeeab | 4390 | p->q.SuppressUnusable = mDNSfalse; |
294beb6e A |
4391 | p->q.SearchListIndex = 0; |
4392 | p->q.AppendSearchDomains = 0; | |
4393 | p->q.RetryWithSearchDomains = mDNSfalse; | |
4394 | p->q.TimeoutQuestion = 0; | |
4395 | p->q.WakeOnResolve = 0; | |
4396 | p->q.qnameOrig = mDNSNULL; | |
67c8f8a1 A |
4397 | p->q.QuestionCallback = AutoTunnelCallback; |
4398 | p->q.QuestionContext = p; | |
4399 | ||
32bb7e43 | 4400 | LogInfo("AddNewClientTunnel start tun %p %##s (%s)%s", p, &q->qname.c, DNSTypeName(q->qtype), q->LongLived ? " LongLived" : ""); |
67c8f8a1 A |
4401 | mDNS_StartQuery_internal(m, &p->q); |
4402 | } | |
4403 | ||
4404 | #endif // APPLE_OSX_mDNSResponder | |
4405 | ||
4406 | #if COMPILER_LIKES_PRAGMA_MARK | |
4407 | #pragma mark - | |
4408 | #pragma mark - Power State & Configuration Change Management | |
4409 | #endif | |
4410 | ||
283ee3ff | 4411 | mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) |
c9b9ae52 A |
4412 | { |
4413 | mDNSBool foundav4 = mDNSfalse; | |
7f0064bd | 4414 | mDNSBool foundav6 = mDNSfalse; |
c9b9ae52 | 4415 | struct ifaddrs *ifa = myGetIfAddrs(1); |
7f0064bd A |
4416 | struct ifaddrs *v4Loopback = NULL; |
4417 | struct ifaddrs *v6Loopback = NULL; | |
5e65c77f | 4418 | char defaultname[64]; |
67c8f8a1 | 4419 | #ifndef NO_IPV6 |
7f0064bd | 4420 | int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0); |
67c8f8a1 A |
4421 | if (InfoSocket < 3 && errno != EAFNOSUPPORT) LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno)); |
4422 | #endif | |
294beb6e | 4423 | |
4a95efb2 A |
4424 | // During wakeup, we may get a network change notification e.g., new addresses, before we get |
4425 | // a wake notification. This means that we have not set AnnounceOwner. Registering interfaces with | |
4426 | // core would cause us to probe again which will conflict with the sleep proxy server, if we had | |
4427 | // registered with it when going to sleep. Hence, need to delay until we get the wake notification | |
4428 | ||
4429 | if (m->SleepState == SleepState_Sleeping) ifa = NULL; | |
4430 | ||
c9b9ae52 A |
4431 | while (ifa) |
4432 | { | |
4433 | #if LIST_ALL_INTERFACES | |
4434 | if (ifa->ifa_addr->sa_family == AF_APPLETALK) | |
283ee3ff | 4435 | LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_APPLETALK", |
c9b9ae52 A |
4436 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); |
4437 | else if (ifa->ifa_addr->sa_family == AF_LINK) | |
283ee3ff | 4438 | LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_LINK", |
c9b9ae52 A |
4439 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); |
4440 | else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6) | |
283ee3ff | 4441 | LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)", |
c9b9ae52 A |
4442 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); |
4443 | if (!(ifa->ifa_flags & IFF_UP)) | |
283ee3ff | 4444 | LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_UP", |
c9b9ae52 | 4445 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); |
8e92c31c | 4446 | if (!(ifa->ifa_flags & IFF_MULTICAST)) |
283ee3ff | 4447 | LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_MULTICAST", |
8e92c31c | 4448 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); |
c9b9ae52 | 4449 | if (ifa->ifa_flags & IFF_POINTOPOINT) |
283ee3ff | 4450 | LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT", |
c9b9ae52 A |
4451 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); |
4452 | if (ifa->ifa_flags & IFF_LOOPBACK) | |
283ee3ff | 4453 | LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK", |
c9b9ae52 A |
4454 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); |
4455 | #endif | |
7f0064bd A |
4456 | |
4457 | if (ifa->ifa_addr->sa_family == AF_LINK) | |
4458 | { | |
4459 | struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; | |
32bb7e43 A |
4460 | if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == sizeof(m->PrimaryMAC) && mDNSSameEthAddress(&m->PrimaryMAC, &zeroEthAddr)) |
4461 | mDNSPlatformMemCopy(m->PrimaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 6); | |
7f0064bd A |
4462 | } |
4463 | ||
4464 | if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr) | |
8e92c31c | 4465 | if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) |
c9b9ae52 | 4466 | { |
7f0064bd A |
4467 | if (!ifa->ifa_netmask) |
4468 | { | |
4469 | mDNSAddr ip; | |
4470 | SetupAddr(&ip, ifa->ifa_addr); | |
4471 | LogMsg("getifaddrs: ifa_netmask is NULL for %5s(%d) Flags %04X Family %2d %#a", | |
4472 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip); | |
4473 | } | |
5e65c77f A |
4474 | // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be zero, so we don't complain about that |
4475 | // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet | |
4476 | else if (ifa->ifa_netmask->sa_family != ifa->ifa_addr->sa_family && ifa->ifa_netmask->sa_family != 0) | |
8e92c31c | 4477 | { |
7f0064bd A |
4478 | mDNSAddr ip; |
4479 | SetupAddr(&ip, ifa->ifa_addr); | |
4480 | LogMsg("getifaddrs ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d", | |
4481 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip, ifa->ifa_netmask->sa_family); | |
8e92c31c | 4482 | } |
ca3eca6b A |
4483 | // Currently we use a few internal ones like mDNSInterfaceID_LocalOnly etc. that are negative values (0, -1, -2). |
4484 | else if ((int)if_nametoindex(ifa->ifa_name) <= 0) | |
4485 | { | |
4486 | LogMsg("UpdateInterfaceList: if_nametoindex returned zero/negative value for %5s(%d)", ifa->ifa_name, if_nametoindex(ifa->ifa_name)); | |
4487 | } | |
7f0064bd | 4488 | else |
c9b9ae52 | 4489 | { |
5e65c77f A |
4490 | // Make sure ifa_netmask->sa_family is set correctly |
4491 | // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet | |
4492 | ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; | |
7f0064bd | 4493 | int ifru_flags6 = 0; |
67c8f8a1 | 4494 | #ifndef NO_IPV6 |
96f69b28 | 4495 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; |
7f0064bd | 4496 | if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) |
8e92c31c | 4497 | { |
7f0064bd | 4498 | struct in6_ifreq ifr6; |
67c8f8a1 A |
4499 | mDNSPlatformMemZero((char *)&ifr6, sizeof(ifr6)); |
4500 | strlcpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name)); | |
7f0064bd A |
4501 | ifr6.ifr_addr = *sin6; |
4502 | if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1) | |
4503 | ifru_flags6 = ifr6.ifr_ifru.ifru_flags6; | |
4504 | verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6); | |
4505 | } | |
67c8f8a1 | 4506 | #endif |
7f0064bd A |
4507 | if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) |
4508 | { | |
4509 | if (ifa->ifa_flags & IFF_LOOPBACK) | |
96f69b28 A |
4510 | { |
4511 | if (ifa->ifa_addr->sa_family == AF_INET) v4Loopback = ifa; | |
4512 | #ifndef NO_IPV6 | |
4513 | else if (sin6->sin6_addr.s6_addr[0] != 0xFD) v6Loopback = ifa; | |
4514 | #endif | |
4515 | } | |
7f0064bd A |
4516 | else |
4517 | { | |
f5e6e86c | 4518 | NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc); |
32bb7e43 | 4519 | if (i && MulticastInterface(i) && i->ifinfo.Advertise) |
f5e6e86c A |
4520 | { |
4521 | if (ifa->ifa_addr->sa_family == AF_INET) foundav4 = mDNStrue; | |
4522 | else foundav6 = mDNStrue; | |
4523 | } | |
7f0064bd | 4524 | } |
8e92c31c | 4525 | } |
c9b9ae52 A |
4526 | } |
4527 | } | |
c9b9ae52 A |
4528 | ifa = ifa->ifa_next; |
4529 | } | |
4530 | ||
32bb7e43 | 4531 | // For efficiency, we don't register a loopback interface when other interfaces of that family are available and advertising |
283ee3ff A |
4532 | if (!foundav4 && v4Loopback) AddInterfaceToList(m, v4Loopback, utc); |
4533 | if (!foundav6 && v6Loopback) AddInterfaceToList(m, v6Loopback, utc); | |
c9b9ae52 | 4534 | |
8e92c31c | 4535 | // Now the list is complete, set the McastTxRx setting for each interface. |
c9b9ae52 A |
4536 | NetworkInterfaceInfoOSX *i; |
4537 | for (i = m->p->InterfaceList; i; i = i->next) | |
8e92c31c | 4538 | if (i->Exists) |
c9b9ae52 | 4539 | { |
67c8f8a1 A |
4540 | mDNSBool txrx = MulticastInterface(i); |
4541 | #if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 | |
4542 | txrx = txrx && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); | |
4543 | #endif | |
8e92c31c | 4544 | if (i->ifinfo.McastTxRx != txrx) |
c9b9ae52 | 4545 | { |
8e92c31c A |
4546 | i->ifinfo.McastTxRx = txrx; |
4547 | i->Exists = 2; // State change; need to deregister and reregister this interface | |
c9b9ae52 A |
4548 | } |
4549 | } | |
67c8f8a1 A |
4550 | |
4551 | #ifndef NO_IPV6 | |
c9b9ae52 | 4552 | if (InfoSocket >= 0) close(InfoSocket); |
67c8f8a1 | 4553 | #endif |
c9b9ae52 | 4554 | |
67c8f8a1 | 4555 | // If we haven't set up AutoTunnelHostAddr yet, do it now |
32bb7e43 | 4556 | if (!mDNSSameEthAddress(&m->PrimaryMAC, &zeroEthAddr) && m->AutoTunnelHostAddr.b[0] == 0) |
67c8f8a1 A |
4557 | { |
4558 | m->AutoTunnelHostAddr.b[0x0] = 0xFD; // Required prefix for "locally assigned" ULA (See RFC 4193) | |
4559 | m->AutoTunnelHostAddr.b[0x1] = mDNSRandom(255); | |
4560 | m->AutoTunnelHostAddr.b[0x2] = mDNSRandom(255); | |
4561 | m->AutoTunnelHostAddr.b[0x3] = mDNSRandom(255); | |
4562 | m->AutoTunnelHostAddr.b[0x4] = mDNSRandom(255); | |
4563 | m->AutoTunnelHostAddr.b[0x5] = mDNSRandom(255); | |
4564 | m->AutoTunnelHostAddr.b[0x6] = mDNSRandom(255); | |
4565 | m->AutoTunnelHostAddr.b[0x7] = mDNSRandom(255); | |
32bb7e43 A |
4566 | m->AutoTunnelHostAddr.b[0x8] = m->PrimaryMAC.b[0] ^ 0x02; // See RFC 3513, Appendix A for explanation |
4567 | m->AutoTunnelHostAddr.b[0x9] = m->PrimaryMAC.b[1]; | |
4568 | m->AutoTunnelHostAddr.b[0xA] = m->PrimaryMAC.b[2]; | |
67c8f8a1 A |
4569 | m->AutoTunnelHostAddr.b[0xB] = 0xFF; |
4570 | m->AutoTunnelHostAddr.b[0xC] = 0xFE; | |
32bb7e43 A |
4571 | m->AutoTunnelHostAddr.b[0xD] = m->PrimaryMAC.b[3]; |
4572 | m->AutoTunnelHostAddr.b[0xE] = m->PrimaryMAC.b[4]; | |
4573 | m->AutoTunnelHostAddr.b[0xF] = m->PrimaryMAC.b[5]; | |
67c8f8a1 A |
4574 | m->AutoTunnelLabel.c[0] = mDNS_snprintf((char*)m->AutoTunnelLabel.c+1, 254, "AutoTunnel-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X", |
4575 | m->AutoTunnelHostAddr.b[0x8], m->AutoTunnelHostAddr.b[0x9], m->AutoTunnelHostAddr.b[0xA], m->AutoTunnelHostAddr.b[0xB], | |
4576 | m->AutoTunnelHostAddr.b[0xC], m->AutoTunnelHostAddr.b[0xD], m->AutoTunnelHostAddr.b[0xE], m->AutoTunnelHostAddr.b[0xF]); | |
32bb7e43 | 4577 | LogInfo("m->AutoTunnelLabel %#s", m->AutoTunnelLabel.c); |
67c8f8a1 | 4578 | } |
5e65c77f | 4579 | |
32bb7e43 A |
4580 | mDNS_snprintf(defaultname, sizeof(defaultname), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen, HINFO_HWstring, |
4581 | m->PrimaryMAC.b[0], m->PrimaryMAC.b[1], m->PrimaryMAC.b[2], m->PrimaryMAC.b[3], m->PrimaryMAC.b[4], m->PrimaryMAC.b[5]); | |
7f0064bd A |
4582 | |
4583 | // Set up the nice label | |
4584 | domainlabel nicelabel; | |
4585 | nicelabel.c[0] = 0; | |
4586 | GetUserSpecifiedFriendlyComputerName(&nicelabel); | |
283ee3ff A |
4587 | if (nicelabel.c[0] == 0) |
4588 | { | |
32bb7e43 | 4589 | debugf("Couldn’t read user-specified Computer Name; using default “%s” instead", defaultname); |
283ee3ff A |
4590 | MakeDomainLabelFromLiteralString(&nicelabel, defaultname); |
4591 | } | |
7f0064bd A |
4592 | |
4593 | // Set up the RFC 1034-compliant label | |
4594 | domainlabel hostlabel; | |
4595 | hostlabel.c[0] = 0; | |
4596 | GetUserSpecifiedLocalHostName(&hostlabel); | |
283ee3ff A |
4597 | if (hostlabel.c[0] == 0) |
4598 | { | |
32bb7e43 | 4599 | debugf("Couldn’t read user-specified Local Hostname; using default “%s.local” instead", defaultname); |
283ee3ff A |
4600 | MakeDomainLabelFromLiteralString(&hostlabel, defaultname); |
4601 | } | |
7f0064bd | 4602 | |
96f69b28 A |
4603 | mDNSBool namechange = mDNSfalse; |
4604 | ||
67c8f8a1 A |
4605 | // We use a case-sensitive comparison here because even though changing the capitalization |
4606 | // of the name alone is not significant to DNS, it's still a change from the user's point of view | |
4607 | if (SameDomainLabelCS(m->p->usernicelabel.c, nicelabel.c)) | |
7f0064bd A |
4608 | debugf("Usernicelabel (%#s) unchanged since last time; not changing m->nicelabel (%#s)", m->p->usernicelabel.c, m->nicelabel.c); |
4609 | else | |
4610 | { | |
67c8f8a1 | 4611 | if (m->p->usernicelabel.c[0]) // Don't show message first time through, when we first read name from prefs on boot |
32bb7e43 | 4612 | LogMsg("User updated Computer Name from “%#s” to “%#s”", m->p->usernicelabel.c, nicelabel.c); |
7f0064bd | 4613 | m->p->usernicelabel = m->nicelabel = nicelabel; |
96f69b28 | 4614 | namechange = mDNStrue; |
7f0064bd A |
4615 | } |
4616 | ||
67c8f8a1 | 4617 | if (SameDomainLabelCS(m->p->userhostlabel.c, hostlabel.c)) |
7f0064bd A |
4618 | debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c); |
4619 | else | |
4620 | { | |
67c8f8a1 | 4621 | if (m->p->userhostlabel.c[0]) // Don't show message first time through, when we first read name from prefs on boot |
32bb7e43 | 4622 | LogMsg("User updated Local Hostname from “%#s” to “%#s”", m->p->userhostlabel.c, hostlabel.c); |
7f0064bd A |
4623 | m->p->userhostlabel = m->hostlabel = hostlabel; |
4624 | mDNS_SetFQDN(m); | |
96f69b28 | 4625 | namechange = mDNStrue; |
7f0064bd A |
4626 | } |
4627 | ||
96f69b28 A |
4628 | #if APPLE_OSX_mDNSResponder |
4629 | if (namechange) // If either name has changed, we need to tickle our AutoTunnel state machine to update its registered records | |
4630 | { | |
4631 | DomainAuthInfo *info; | |
4632 | for (info = m->AuthInfoList; info; info = info->next) | |
263eeeab | 4633 | if (info->AutoTunnel) AutoTunnelHostNameChanged(m, info); |
96f69b28 | 4634 | } |
32bb7e43 | 4635 | #endif // APPLE_OSX_mDNSResponder |
96f69b28 | 4636 | |
7f0064bd | 4637 | return(mStatus_NoError); |
c9b9ae52 A |
4638 | } |
4639 | ||
67c8f8a1 A |
4640 | // Returns number of leading one-bits in mask: 0-32 for IPv4, 0-128 for IPv6 |
4641 | // Returns -1 if all the one-bits are not contiguous | |
283ee3ff A |
4642 | mDNSlocal int CountMaskBits(mDNSAddr *mask) |
4643 | { | |
4644 | int i = 0, bits = 0; | |
4645 | int bytes = mask->type == mDNSAddrType_IPv4 ? 4 : mask->type == mDNSAddrType_IPv6 ? 16 : 0; | |
4646 | while (i < bytes) | |
4647 | { | |
4648 | mDNSu8 b = mask->ip.v6.b[i++]; | |
4649 | while (b & 0x80) { bits++; b <<= 1; } | |
4650 | if (b) return(-1); | |
4651 | } | |
4652 | while (i < bytes) if (mask->ip.v6.b[i++]) return(-1); | |
4653 | return(bits); | |
4654 | } | |
4655 | ||
7f0064bd | 4656 | // returns count of non-link local V4 addresses registered |
283ee3ff | 4657 | mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) |
c9b9ae52 A |
4658 | { |
4659 | NetworkInterfaceInfoOSX *i; | |
7f0064bd | 4660 | int count = 0; |
c9b9ae52 | 4661 | for (i = m->p->InterfaceList; i; i = i->next) |
8e92c31c | 4662 | if (i->Exists) |
c9b9ae52 | 4663 | { |
030b743d | 4664 | NetworkInterfaceInfo *const n = &i->ifinfo; |
32bb7e43 A |
4665 | NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); |
4666 | if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname); | |
67c8f8a1 | 4667 | |
ca3eca6b | 4668 | if (i->Registered && i->Registered != primary) // Sanity check |
73792575 | 4669 | { |
ca3eca6b A |
4670 | LogMsg("SetupActiveInterfaces ERROR! n->Registered %p != primary %p", i->Registered, primary); |
4671 | i->Registered = mDNSNULL; | |
73792575 | 4672 | } |
67c8f8a1 | 4673 | |
ca3eca6b | 4674 | if (!i->Registered) |
c9b9ae52 | 4675 | { |
ca3eca6b | 4676 | // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, |
8e92c31c | 4677 | // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. |
ca3eca6b A |
4678 | // If i->Registered is NOT set, then we haven't registered it and we should not try to deregister it |
4679 | // | |
4680 | ||
4681 | i->Registered = primary; | |
030b743d | 4682 | |
283ee3ff A |
4683 | // If i->LastSeen == utc, then this is a brand-new interface, just created, or an interface that never went away. |
4684 | // If i->LastSeen != utc, then this is an old interface, previously seen, that went away for (utc - i->LastSeen) seconds. | |
4685 | // 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. | |
67c8f8a1 | 4686 | i->Occulting = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->LastSeen > 0 && utc - i->LastSeen < 60); |
030b743d | 4687 | |
294beb6e A |
4688 | // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address |
4689 | // everytime it creates a new interface. We think it is a duplicate and hence consider it | |
4690 | // as flashing and occulting, that is, flapping. If an interface is marked as flapping, | |
4691 | // mDNS_RegisterInterface() changes the probe delay from 1/2 second to 5 seconds and | |
4692 | // logs a warning message to system.log noting frequent interface transitions. | |
4693 | if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0) | |
4694 | { | |
4695 | LogInfo("SetupActiveInterfaces: P2P %s interface registering %s %s", i->ifinfo.ifname, | |
4696 | i->Flashing ? " (Flashing)" : "", | |
4697 | i->Occulting ? " (Occulting)" : ""); | |
4698 | mDNS_RegisterInterface(m, n, 0); | |
4699 | } | |
4700 | else | |
4701 | { | |
4702 | mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting); | |
4703 | } | |
ca3eca6b | 4704 | |
32bb7e43 | 4705 | if (!mDNSAddressIsLinkLocal(&n->ip)) count++; |
ca3eca6b A |
4706 | LogInfo("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", |
4707 | i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, &n->ip, CountMaskBits(&n->mask), | |
67c8f8a1 A |
4708 | i->Flashing ? " (Flashing)" : "", |
4709 | i->Occulting ? " (Occulting)" : "", | |
4710 | n->InterfaceActive ? " (Primary)" : ""); | |
67c8f8a1 | 4711 | |
030b743d | 4712 | if (!n->McastTxRx) |
ca3eca6b | 4713 | 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); |
030b743d | 4714 | else |
73792575 | 4715 | { |
030b743d A |
4716 | if (i->sa_family == AF_INET) |
4717 | { | |
4718 | struct ip_mreq imr; | |
32bb7e43 | 4719 | primary->ifa_v4addr.s_addr = n->ip.ip.v4.NotAnInteger; |
030b743d A |
4720 | imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; |
4721 | imr.imr_interface = primary->ifa_v4addr; | |
4722 | ||
4723 | // If this is our *first* IPv4 instance for this interface name, we need to do a IP_DROP_MEMBERSHIP first, | |
4724 | // before trying to join the group, to clear out stale kernel state which may be lingering. | |
4725 | // In particular, this happens with removable network interfaces like USB Ethernet adapters -- the kernel has stale state | |
4726 | // from the last time the USB Ethernet adapter was connected, and part of the kernel thinks we've already joined the group | |
4727 | // on that interface (so we get EADDRINUSE when we try to join again) but a different part of the kernel thinks we haven't | |
4728 | // joined the group (so we receive no multicasts). Doing an IP_DROP_MEMBERSHIP before joining seems to flush the stale state. | |
4729 | // Also, trying to make the code leave the group when the adapter is removed doesn't work either, | |
4730 | // because by the time we get the configuration change notification, the interface is already gone, | |
4731 | // so attempts to unsubscribe fail with EADDRNOTAVAIL (errno 49 "Can't assign requested address"). | |
4732 | // <rdar://problem/5585972> IP_ADD_MEMBERSHIP fails for previously-connected removable interfaces | |
32bb7e43 | 4733 | if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET) == i) |
030b743d | 4734 | { |
32bb7e43 | 4735 | 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); |
030b743d | 4736 | mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)); |
32bb7e43 A |
4737 | if (err < 0 && (errno != EADDRNOTAVAIL)) |
4738 | LogMsg("setsockopt - IP_DROP_MEMBERSHIP error %d errno %d (%s)", err, errno, strerror(errno)); | |
030b743d A |
4739 | } |
4740 | ||
32bb7e43 | 4741 | LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv4 mcast group %.4a on %.4a", i->ifinfo.ifname, i->scope_id, &imr.imr_multiaddr, &imr.imr_interface); |
030b743d A |
4742 | mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); |
4743 | // Joining same group twice can give "Address already in use" error -- no need to report that | |
32bb7e43 A |
4744 | if (err < 0 && (errno != EADDRINUSE)) |
4745 | LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err, errno, strerror(errno), &imr.imr_multiaddr, &imr.imr_interface); | |
030b743d | 4746 | } |
67c8f8a1 | 4747 | #ifndef NO_IPV6 |
030b743d A |
4748 | if (i->sa_family == AF_INET6) |
4749 | { | |
4750 | struct ipv6_mreq i6mr; | |
4751 | i6mr.ipv6mr_interface = primary->scope_id; | |
4752 | i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6; | |
4753 | ||
32bb7e43 | 4754 | if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET6) == i) |
030b743d | 4755 | { |
32bb7e43 | 4756 | 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); |
030b743d | 4757 | mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr)); |
32bb7e43 A |
4758 | if (err < 0 && (errno != EADDRNOTAVAIL)) |
4759 | LogMsg("setsockopt - IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); | |
030b743d A |
4760 | } |
4761 | ||
32bb7e43 | 4762 | LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv6 mcast group %.16a on %u", i->ifinfo.ifname, i->scope_id, &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); |
030b743d A |
4763 | mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); |
4764 | // Joining same group twice can give "Address already in use" error -- no need to report that | |
32bb7e43 A |
4765 | if (err < 0 && (errno != EADDRINUSE)) |
4766 | LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); | |
030b743d | 4767 | } |
67c8f8a1 | 4768 | #endif |
030b743d | 4769 | } |
c9b9ae52 A |
4770 | } |
4771 | } | |
32bb7e43 | 4772 | |
7f0064bd | 4773 | return count; |
c9b9ae52 A |
4774 | } |
4775 | ||
283ee3ff | 4776 | mDNSlocal void MarkAllInterfacesInactive(mDNS *const m, mDNSs32 utc) |
c9b9ae52 A |
4777 | { |
4778 | NetworkInterfaceInfoOSX *i; | |
4779 | for (i = m->p->InterfaceList; i; i = i->next) | |
283ee3ff A |
4780 | { |
4781 | if (i->Exists) i->LastSeen = utc; | |
8e92c31c | 4782 | i->Exists = mDNSfalse; |
283ee3ff | 4783 | } |
c9b9ae52 A |
4784 | } |
4785 | ||
7f0064bd | 4786 | // returns count of non-link local V4 addresses deregistered |
283ee3ff | 4787 | mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) |
c9b9ae52 A |
4788 | { |
4789 | // First pass: | |
4790 | // If an interface is going away, then deregister this from the mDNSCore. | |
8e92c31c | 4791 | // We also have to deregister it if the primary interface that it's using for its InterfaceID is going away. |
c9b9ae52 A |
4792 | // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory |
4793 | // it refers to has gone away we'll crash. | |
c9b9ae52 | 4794 | NetworkInterfaceInfoOSX *i; |
7f0064bd | 4795 | int count = 0; |
c9b9ae52 A |
4796 | for (i = m->p->InterfaceList; i; i = i->next) |
4797 | { | |
67c8f8a1 | 4798 | // If this interface is no longer active, or its InterfaceID is changing, deregister it |
32bb7e43 | 4799 | NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); |
ca3eca6b A |
4800 | if (i->Registered) |
4801 | if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary) | |
8e92c31c | 4802 | { |
67c8f8a1 | 4803 | i->Flashing = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->AppearanceTime < 60); |
ca3eca6b A |
4804 | LogInfo("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", |
4805 | i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, | |
67c8f8a1 A |
4806 | &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), |
4807 | i->Flashing ? " (Flashing)" : "", | |
4808 | i->Occulting ? " (Occulting)" : "", | |
4809 | i->ifinfo.InterfaceActive ? " (Primary)" : ""); | |
294beb6e A |
4810 | |
4811 | // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address | |
4812 | // everytime it creates a new interface. We think it is a duplicate and hence consider it | |
4813 | // as flashing and occulting. The "core" does not flush the cache for this case. This leads to | |
4814 | // stale data returned to the application even after the interface is removed. The application | |
4815 | // then starts to send data but the new interface is not yet created. | |
4816 | if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0) | |
4817 | { | |
4818 | LogInfo("ClearInactiveInterfaces: P2P %s interface deregistering %s %s", i->ifinfo.ifname, | |
4819 | i->Flashing ? " (Flashing)" : "", | |
4820 | i->Occulting ? " (Occulting)" : ""); | |
4821 | mDNS_DeregisterInterface(m, &i->ifinfo, 0); | |
4822 | } | |
4823 | else | |
4824 | { | |
4825 | mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting); | |
4826 | } | |
67c8f8a1 | 4827 | if (!mDNSAddressIsLinkLocal(&i->ifinfo.ip)) count++; |
ca3eca6b A |
4828 | i->Registered = mDNSNULL; |
4829 | // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, | |
8e92c31c | 4830 | // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. |
ca3eca6b | 4831 | // If i->Registered is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it. |
030b743d A |
4832 | |
4833 | // Caution: If we ever decide to add code here to leave the multicast group, we need to make sure that this | |
4834 | // is the LAST representative of this physical interface, or we'll unsubscribe from the group prematurely. | |
8e92c31c | 4835 | } |
c9b9ae52 A |
4836 | } |
4837 | ||
4838 | // Second pass: | |
030b743d | 4839 | // Now that everything that's going to deregister has done so, we can clean up and free the memory |
c9b9ae52 A |
4840 | NetworkInterfaceInfoOSX **p = &m->p->InterfaceList; |
4841 | while (*p) | |
4842 | { | |
4843 | i = *p; | |
67c8f8a1 | 4844 | // If no longer active, delete interface from list and free memory |
283ee3ff | 4845 | if (!i->Exists) |
c9b9ae52 | 4846 | { |
283ee3ff | 4847 | if (i->LastSeen == utc) i->LastSeen = utc - 1; |
ca3eca6b A |
4848 | mDNSBool delete = (NumCacheRecordsForInterfaceID(m, i->ifinfo.InterfaceID) == 0) && (utc - i->LastSeen >= 60); |
4849 | LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p(%p) %#a/%d Age %d%s", delete ? "Deleting" : "Holding", | |
4850 | i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, | |
283ee3ff A |
4851 | &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), utc - i->LastSeen, |
4852 | i->ifinfo.InterfaceActive ? " (Primary)" : ""); | |
32bb7e43 A |
4853 | #if APPLE_OSX_mDNSResponder |
4854 | if (i->BPF_fd >= 0) CloseBPF(i); | |
4855 | #endif // APPLE_OSX_mDNSResponder | |
283ee3ff A |
4856 | if (delete) |
4857 | { | |
4858 | *p = i->next; | |
283ee3ff A |
4859 | freeL("NetworkInterfaceInfoOSX", i); |
4860 | continue; // After deleting this object, don't want to do the "p = &i->next;" thing at the end of the loop | |
4861 | } | |
c9b9ae52 | 4862 | } |
283ee3ff | 4863 | p = &i->next; |
c9b9ae52 | 4864 | } |
7f0064bd | 4865 | return count; |
c9b9ae52 A |
4866 | } |
4867 | ||
67c8f8a1 | 4868 | mDNSlocal void AppendDNameListElem(DNameListElem ***List, mDNSu32 uid, domainname *name) |
7f0064bd | 4869 | { |
67c8f8a1 A |
4870 | DNameListElem *dnle = (DNameListElem*) mallocL("DNameListElem/AppendDNameListElem", sizeof(DNameListElem)); |
4871 | if (!dnle) LogMsg("ERROR: AppendDNameListElem: memory exhausted"); | |
4872 | else | |
7f0064bd | 4873 | { |
67c8f8a1 A |
4874 | dnle->next = mDNSNULL; |
4875 | dnle->uid = uid; | |
4876 | AssignDomainName(&dnle->name, name); | |
4877 | **List = dnle; | |
4878 | *List = &dnle->next; | |
7f0064bd | 4879 | } |
283ee3ff | 4880 | } |
7f0064bd | 4881 | |
32bb7e43 A |
4882 | mDNSlocal int compare_dns_configs(const void *aa, const void *bb) |
4883 | { | |
4884 | dns_resolver_t *a = *(dns_resolver_t**)aa; | |
4885 | dns_resolver_t *b = *(dns_resolver_t**)bb; | |
4886 | ||
4887 | return (a->search_order < b->search_order) ? -1 : (a->search_order == b->search_order) ? 0 : 1; | |
4888 | } | |
4889 | ||
294beb6e A |
4890 | // ConfigResolvers is called twice - once to parse the "scoped_resolver" list and second time to parse the "resolver" list. |
4891 | // "scoped_resolver" has entries that should only be used for "scoped_questions" (for questions that specify an interface index | |
4892 | // q->InterfaceID) and "resolver" entries should only be used for non-scoped questions. Entries in either of the list can specify | |
4893 | // an ifindex. This means that the dns query should be scoped to that interface when sent out on the wire. The flag value | |
4894 | // "DNS_RESOLVER_FLAGS_SCOPED" itself appears only in "scoped" list of resolvers. | |
4895 | // | |
4896 | // Before "scoped_resolver" was introduced, the entries in "resolver" list can contain options like "interface=en0" which | |
4897 | // was meant to scope the query (non-scoped queries) to a specific interface. We still support this option. On top of that, | |
4898 | // we also support a new way of specifying the interface index as described above. | |
263eeeab A |
4899 | mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool scope, mDNSBool setsearch, mDNSBool setservers) |
4900 | { | |
294beb6e | 4901 | int i, j; |
263eeeab A |
4902 | domainname d; |
4903 | #if DNSINFO_VERSION >= 20091104 | |
4904 | dns_resolver_t **resolver = scope ? config->scoped_resolver : config->resolver; | |
4905 | int nresolvers = scope ? config->n_scoped_resolver : config->n_resolver; | |
4906 | #else | |
4907 | (void) scope; // unused | |
4908 | dns_resolver_t **resolver = config->resolver; | |
4909 | int nresolvers = config->n_resolver; | |
4910 | #endif | |
4911 | ||
263eeeab A |
4912 | if (setsearch && !scope && nresolvers) |
4913 | { | |
4914 | // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains | |
4915 | // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if | |
4916 | // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and | |
4917 | // instead use the search domain list as the sole authority for what domains to search and in what order | |
4918 | // (and the domain from the "domain" field will also appear somewhere in that list). | |
4919 | // Also, all search domains get added to the search list for resolver[0], so the domains and/or | |
4920 | // search lists for other resolvers in the list need to be ignored. | |
4921 | // | |
4922 | // Note: Starting DNSINFO_VERSION 20091104, search list is present only in the first resolver (resolver 0). | |
4923 | // i.e., n_search for the first resolver is always non-zero. We don't guard it with #ifs for better readability | |
4924 | ||
4925 | if (resolver[0]->n_search == 0) | |
4926 | { | |
4927 | LogInfo("ConfigResolvers: (%s) configuring zeroth domain as search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->domain); | |
294beb6e | 4928 | mDNS_AddSearchDomain_CString(resolver[0]->domain, mDNSNULL); |
263eeeab A |
4929 | } |
4930 | else | |
4931 | { | |
4932 | for (i = 0; i < resolver[0]->n_search; i++) | |
4933 | { | |
4934 | LogInfo("ConfigResolvers: (%s) configuring search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->search[i]); | |
294beb6e | 4935 | mDNS_AddSearchDomain_CString(resolver[0]->search[i], mDNSNULL); |
263eeeab A |
4936 | } |
4937 | } | |
4938 | } | |
4939 | ||
294beb6e A |
4940 | // scoped search domains are set below. If neither scoped nor setting servers, we have nothing to do |
4941 | if (!scope && !setservers) return; | |
263eeeab A |
4942 | |
4943 | // For the "default" resolver ("resolver #1") the "domain" value is bogus and we need to ignore it. | |
4944 | // e.g. the default resolver's "domain" value might say "apple.com", which indicates that this resolver | |
4945 | // is only for names that fall under "apple.com", but that's not correct. Actually the default resolver is | |
4946 | // for all names not covered by a more specific resolver (i.e. its domain should be ".", the root domain). | |
4947 | // | |
4948 | // Note: Starting DNSINFO_VERSION 20091104, domain value of this first resolver (resolver 0) is always NULL. | |
4949 | // We don't guard it with #ifs for better readability | |
4950 | // | |
4951 | if ((nresolvers != 0) && resolver[0]->domain) | |
4952 | resolver[0]->domain[0] = 0; // don't stop pointing at the memory, just change the first byte | |
4953 | ||
4954 | qsort(resolver, nresolvers, sizeof(dns_resolver_t*), compare_dns_configs); | |
4955 | ||
4956 | for (i = 0; i < nresolvers; i++) | |
4957 | { | |
4958 | int n; | |
4959 | dns_resolver_t *r = resolver[i]; | |
4960 | mDNSInterfaceID interface = mDNSInterface_Any; | |
4961 | int disabled = 0; | |
4962 | ||
4963 | LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scope ? "Scoped" : "", i, r->domain, r->n_nameserver); | |
4964 | ||
4965 | // On Tiger, dnsinfo entries for mDNS domains have port 5353, the mDNS port. Ignore them. | |
4966 | // Note: Unlike the BSD Sockets APIs (where TCP and UDP port numbers are universally in network byte order) | |
4967 | // in Apple's "dnsinfo.h" API the port number is declared to be a "uint16_t in host byte order" | |
4968 | // We also don't need to do any more work if there are no nameserver addresses | |
294beb6e A |
4969 | if (r->port == 5353 || r->n_nameserver == 0) |
4970 | { | |
4971 | char *opt = r->options; | |
4972 | if (opt && !strncmp(opt, "mdns", strlen(opt))) | |
4973 | { | |
4974 | if (!MakeDomainNameFromDNSNameString(&d, r->domain)) | |
4975 | { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; } | |
4976 | mDNS_AddMcastResolver(m, &d, interface, r->timeout); | |
4977 | } | |
4978 | continue; | |
4979 | } | |
4980 | ||
263eeeab A |
4981 | |
4982 | if (!r->domain || !*r->domain) d.c[0] = 0; | |
4983 | else if (!MakeDomainNameFromDNSNameString(&d, r->domain)) | |
4984 | { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; } | |
4985 | ||
4986 | // DNS server option parsing | |
4987 | if (r->options != NULL) | |
4988 | { | |
4989 | char *nextOption = r->options; | |
4990 | char *currentOption = NULL; | |
4991 | while ((currentOption = strsep(&nextOption, " ")) != NULL && currentOption[0] != 0) | |
4992 | { | |
4993 | // The option may be in the form of interface=xxx where xxx is an interface name. | |
4994 | if (strncmp(currentOption, kInterfaceSpecificOption, sizeof(kInterfaceSpecificOption) - 1) == 0) | |
4995 | { | |
4996 | NetworkInterfaceInfoOSX *ni; | |
4997 | char ifname[IF_NAMESIZE+1]; | |
4998 | mDNSu32 ifindex = 0; | |
4999 | // If something goes wrong finding the interface, create the server entry anyhow but mark it as disabled. | |
5000 | // This allows us to block these special queries from going out on the wire. | |
5001 | strlcpy(ifname, currentOption + sizeof(kInterfaceSpecificOption)-1, sizeof(ifname)); | |
5002 | ifindex = if_nametoindex(ifname); | |
5003 | if (ifindex == 0) { disabled = 1; LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - interface %s not found", ifname); continue; } | |
5004 | LogInfo("ConfigResolvers: interface specific entry: %s on %s (%d)", r->domain, ifname, ifindex); | |
5005 | // Find the interface. Can't use mDNSPlatformInterfaceIDFromInterfaceIndex | |
5006 | // because that will call mDNSMacOSXNetworkChanged if the interface doesn't exist | |
5007 | for (ni = m->p->InterfaceList; ni; ni = ni->next) | |
5008 | if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex) break; | |
5009 | if (ni != NULL) interface = ni->ifinfo.InterfaceID; | |
5010 | if (interface == mDNSNULL) | |
5011 | { | |
5012 | disabled = 1; | |
5013 | LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - index %d (%s) not found", ifindex, ifname); | |
5014 | continue; | |
5015 | } | |
5016 | } | |
5017 | } | |
5018 | } | |
5019 | ||
294beb6e A |
5020 | // flags and if_index are defined only from this DNSINFO_VERSION onwards. |
5021 | // Parse the interface index if we have not already parsed one above. | |
263eeeab | 5022 | #if DNSINFO_VERSION >= 20091104 |
294beb6e | 5023 | if ((interface == mDNSInterface_Any) && (r->if_index != 0)) |
263eeeab A |
5024 | { |
5025 | NetworkInterfaceInfoOSX *ni; | |
5026 | interface = mDNSNULL; | |
5027 | for (ni = m->p->InterfaceList; ni; ni = ni->next) | |
5028 | if (ni->ifinfo.InterfaceID && ni->scope_id == r->if_index) break; | |
5029 | if (ni != NULL) interface = ni->ifinfo.InterfaceID; | |
5030 | if (interface == mDNSNULL) | |
5031 | { | |
5032 | disabled = 1; | |
5033 | LogMsg("ConfigResolvers: interface specific index %d not found", r->if_index); | |
5034 | continue; | |
5035 | } | |
5036 | } | |
5037 | #endif | |
5038 | ||
294beb6e A |
5039 | if (setsearch) |
5040 | { | |
5041 | // For non-scoped resolvers unlike scoped resolvers, only zeroth resolver has search lists if any. For scoped | |
5042 | // resolvers, we need to parse all the entries. | |
5043 | if (scope) | |
5044 | { | |
5045 | for (j = 0; j < resolver[i]->n_search; j++) | |
5046 | { | |
5047 | LogInfo("ConfigResolvers: (%s) configuring search list %s, Interface %p", scope ? "Scoped" : "Non-scoped", resolver[i]->search[j], interface); | |
5048 | mDNS_AddSearchDomain_CString(resolver[i]->search[j], interface); | |
5049 | } | |
5050 | // Parse other scoped resolvers for search lists | |
5051 | if (!setservers) continue; | |
5052 | } | |
5053 | } | |
5054 | ||
263eeeab A |
5055 | for (n = 0; n < r->n_nameserver; n++) |
5056 | if (r->nameserver[n]->sa_family == AF_INET || r->nameserver[n]->sa_family == AF_INET6) | |
5057 | { | |
5058 | mDNSAddr saddr; | |
5059 | // mDNSAddr saddr = { mDNSAddrType_IPv4, { { { 192, 168, 1, 1 } } } }; // for testing | |
5060 | if (SetupAddr(&saddr, r->nameserver[n])) LogMsg("RegisterSplitDNS: bad IP address"); | |
5061 | else | |
5062 | { | |
4a95efb2 | 5063 | mDNSBool cellIntf = mDNSfalse; |
263eeeab A |
5064 | mDNSBool scopedDNS = mDNSfalse; |
5065 | DNSServer *s; | |
5066 | #if DNSINFO_VERSION >= 20091104 | |
5067 | // By setting scoped, this DNSServer can only be picked if the right interfaceID | |
294beb6e | 5068 | // is given in the question. |
263eeeab | 5069 | if (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED) && (interface == mDNSNULL)) |
294beb6e A |
5070 | LogMsg("ConfigResolvers: ERROR: scoped is set but if_index %d is invalid for DNSServer %#a:%d", |
5071 | r->if_index, &saddr, mDNSVal16(r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort)); | |
263eeeab A |
5072 | else |
5073 | scopedDNS = (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED)) ? mDNStrue : mDNSfalse; | |
4a95efb2 A |
5074 | #endif |
5075 | #if DNSINFO_VERSION >= 20110420 | |
5076 | cellIntf = r->reach_flags & kSCNetworkReachabilityFlagsIsWWAN; | |
263eeeab | 5077 | #endif |
294beb6e A |
5078 | // The timeout value is for all the DNS servers in a given resolver, hence we pass |
5079 | // the timeout value only for the first DNSServer. If we don't have a value in the | |
5080 | // resolver, then use the core's default value | |
5081 | // | |
5082 | // Note: this assumes that when the core picks a list of DNSServers for a question, | |
5083 | // it takes the sum of all the timeout values for all DNS servers. By doing this, it | |
5084 | // tries all the DNS servers in a specified timeout | |
5085 | s = mDNS_AddDNSServer(m, &d, interface, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scopedDNS, | |
4a95efb2 | 5086 | (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0), cellIntf); |
263eeeab A |
5087 | if (s) |
5088 | { | |
5089 | if (disabled) s->teststate = DNSServer_Disabled; | |
294beb6e A |
5090 | LogInfo("ConfigResolvers: DNS server %#a:%d for domain %##s from slot %d, %d", |
5091 | &s->addr, mDNSVal16(s->port), d.c, i, n); | |
263eeeab A |
5092 | } |
5093 | } | |
5094 | } | |
5095 | } | |
5096 | } | |
5097 | ||
67c8f8a1 | 5098 | mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains) |
283ee3ff | 5099 | { |
67c8f8a1 A |
5100 | int i; |
5101 | char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL | |
5102 | domainname d; | |
7cb34e5c | 5103 | |
67c8f8a1 A |
5104 | // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed |
5105 | if (fqdn) fqdn->c[0] = 0; | |
5106 | if (RegDomains ) *RegDomains = NULL; | |
5107 | if (BrowseDomains) *BrowseDomains = NULL; | |
5108 | ||
32bb7e43 A |
5109 | LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s", |
5110 | setservers ? " setservers" : "", | |
5111 | setsearch ? " setsearch" : "", | |
5112 | fqdn ? " fqdn" : "", | |
5113 | RegDomains ? " RegDomains" : "", | |
5114 | BrowseDomains ? " BrowseDomains" : ""); | |
7cb34e5c | 5115 | |
67c8f8a1 A |
5116 | // Add the inferred address-based configuration discovery domains |
5117 | // (should really be in core code I think, not platform-specific) | |
5118 | if (setsearch) | |
5119 | { | |
32bb7e43 A |
5120 | struct ifaddrs *ifa = mDNSNULL; |
5121 | struct sockaddr_in saddr; | |
5122 | mDNSPlatformMemZero(&saddr, sizeof(saddr)); | |
5123 | saddr.sin_len = sizeof(saddr); | |
5124 | saddr.sin_family = AF_INET; | |
5125 | saddr.sin_port = 0; | |
5126 | saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4; | |
5127 | ||
5128 | // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation | |
5129 | if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa = myGetIfAddrs(1); | |
5130 | ||
67c8f8a1 | 5131 | while (ifa) |
7cb34e5c | 5132 | { |
67c8f8a1 A |
5133 | mDNSAddr a, n; |
5134 | if (ifa->ifa_addr->sa_family == AF_INET && | |
5135 | ifa->ifa_netmask && | |
5136 | !(ifa->ifa_flags & IFF_LOOPBACK) && | |
5137 | !SetupAddr(&a, ifa->ifa_addr) && | |
67c8f8a1 | 5138 | !mDNSv4AddressIsLinkLocal(&a.ip.v4) ) |
7cb34e5c | 5139 | { |
5e65c77f A |
5140 | // 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 |
5141 | // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet | |
5142 | ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; // Make sure ifa_netmask->sa_family is set correctly | |
5143 | SetupAddr(&n, ifa->ifa_netmask); | |
67c8f8a1 A |
5144 | // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code |
5145 | mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3], | |
5146 | a.ip.v4.b[2] & n.ip.v4.b[2], | |
5147 | a.ip.v4.b[1] & n.ip.v4.b[1], | |
5148 | a.ip.v4.b[0] & n.ip.v4.b[0]); | |
294beb6e | 5149 | mDNS_AddSearchDomain_CString(buf, mDNSNULL); |
7cb34e5c | 5150 | } |
67c8f8a1 | 5151 | ifa = ifa->ifa_next; |
7cb34e5c | 5152 | } |
7f0064bd | 5153 | } |
8e92c31c | 5154 | |
67c8f8a1 A |
5155 | #ifndef MDNS_NO_DNSINFO |
5156 | if (setservers || setsearch) | |
8e92c31c | 5157 | { |
67c8f8a1 A |
5158 | dns_config_t *config = dns_configuration_copy(); |
5159 | if (!config) | |
8e92c31c | 5160 | { |
67c8f8a1 A |
5161 | // When running on 10.3 (build 7xxx) and earlier, we don't expect dns_configuration_copy() to succeed |
5162 | // On 10.4, calls to dns_configuration_copy() early in the boot process often fail. | |
5163 | // Apparently this is expected behaviour -- "not a bug". | |
5164 | // Accordingly, we suppress syslog messages for the first three minutes after boot. | |
5165 | // If we are still getting failures after three minutes, then we log them. | |
294beb6e | 5166 | if ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180)) |
32bb7e43 | 5167 | LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL"); |
8e92c31c | 5168 | } |
67c8f8a1 | 5169 | else |
8e92c31c | 5170 | { |
32bb7e43 | 5171 | LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d", config->n_resolver); |
32bb7e43 A |
5172 | |
5173 | #if APPLE_OSX_mDNSResponder | |
5174 | // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up | |
5175 | // by someone using Microsoft Active Directory using "local" as a private internal top-level domain | |
5176 | if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver && config->resolver[0]->nameserver[0]) | |
5177 | MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain); | |
5178 | else ActiveDirectoryPrimaryDomain.c[0] = 0; | |
5179 | //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local"); | |
5180 | ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain); | |
5181 | if (config->n_resolver && config->resolver[0]->n_nameserver && SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain)) | |
5182 | SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]); | |
5183 | else | |
5184 | { | |
5185 | AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)""); | |
5186 | ActiveDirectoryPrimaryDomainLabelCount = 0; | |
5187 | ActiveDirectoryPrimaryDomainServer = zeroAddr; | |
5188 | } | |
5189 | #endif | |
5190 | ||
263eeeab A |
5191 | ConfigResolvers(m, config, mDNSfalse, setsearch, setservers); |
5192 | #if DNSINFO_VERSION >= 20091104 | |
5193 | ConfigResolvers(m, config, mDNStrue, setsearch, setservers); | |
5194 | #endif | |
67c8f8a1 | 5195 | dns_configuration_free(config); |
294beb6e | 5196 | if (setsearch) RetrySearchDomainQuestions(m); |
67c8f8a1 A |
5197 | setservers = mDNSfalse; // Done these now -- no need to fetch the same data from SCDynamicStore |
5198 | setsearch = mDNSfalse; | |
8e92c31c A |
5199 | } |
5200 | } | |
67c8f8a1 | 5201 | #endif // MDNS_NO_DNSINFO |
7f0064bd | 5202 | |
67c8f8a1 | 5203 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformSetDNSConfig"), NULL, NULL); |
32bb7e43 A |
5204 | if (!store) |
5205 | LogMsg("mDNSPlatformSetDNSConfig: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); | |
5206 | else | |
8e92c31c | 5207 | { |
030b743d A |
5208 | CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS); |
5209 | if (ddnsdict) | |
8e92c31c | 5210 | { |
67c8f8a1 | 5211 | if (fqdn) |
8e92c31c | 5212 | { |
030b743d | 5213 | CFArrayRef fqdnArray = CFDictionaryGetValue(ddnsdict, CFSTR("HostNames")); |
67c8f8a1 | 5214 | if (fqdnArray && CFArrayGetCount(fqdnArray) > 0) |
7f0064bd | 5215 | { |
67c8f8a1 A |
5216 | // for now, we only look at the first array element. if we ever support multiple configurations, we will walk the list |
5217 | CFDictionaryRef fqdnDict = CFArrayGetValueAtIndex(fqdnArray, 0); | |
32bb7e43 | 5218 | if (fqdnDict && DictionaryIsEnabled(fqdnDict)) |
67c8f8a1 A |
5219 | { |
5220 | CFStringRef name = CFDictionaryGetValue(fqdnDict, CFSTR("Domain")); | |
5221 | if (name) | |
5222 | { | |
5223 | if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || | |
5224 | !MakeDomainNameFromDNSNameString(fqdn, buf) || !fqdn->c[0]) | |
5225 | LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS host name: %s", buf[0] ? buf : "(unknown)"); | |
5226 | else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS host name: %s", buf); | |
5227 | } | |
5228 | } | |
7f0064bd | 5229 | } |
7f0064bd | 5230 | } |
283ee3ff | 5231 | |
67c8f8a1 | 5232 | if (RegDomains) |
8e92c31c | 5233 | { |
030b743d | 5234 | CFArrayRef regArray = CFDictionaryGetValue(ddnsdict, CFSTR("RegistrationDomains")); |
67c8f8a1 A |
5235 | if (regArray && CFArrayGetCount(regArray) > 0) |
5236 | { | |
5237 | CFDictionaryRef regDict = CFArrayGetValueAtIndex(regArray, 0); | |
32bb7e43 | 5238 | if (regDict && DictionaryIsEnabled(regDict)) |
67c8f8a1 A |
5239 | { |
5240 | CFStringRef name = CFDictionaryGetValue(regDict, CFSTR("Domain")); | |
5241 | if (name) | |
5242 | { | |
5243 | if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || | |
5244 | !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0]) | |
5245 | LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS registration domain: %s", buf[0] ? buf : "(unknown)"); | |
5246 | else | |
5247 | { | |
5248 | debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration domain: %s", buf); | |
5249 | AppendDNameListElem(&RegDomains, 0, &d); | |
5250 | } | |
5251 | } | |
5252 | } | |
5253 | } | |
8e92c31c | 5254 | } |
8e92c31c | 5255 | |
67c8f8a1 | 5256 | if (BrowseDomains) |
7cb34e5c | 5257 | { |
030b743d | 5258 | CFArrayRef browseArray = CFDictionaryGetValue(ddnsdict, CFSTR("BrowseDomains")); |
67c8f8a1 | 5259 | if (browseArray) |
7cb34e5c | 5260 | { |
67c8f8a1 A |
5261 | for (i = 0; i < CFArrayGetCount(browseArray); i++) |
5262 | { | |
5263 | CFDictionaryRef browseDict = CFArrayGetValueAtIndex(browseArray, i); | |
32bb7e43 | 5264 | if (browseDict && DictionaryIsEnabled(browseDict)) |
67c8f8a1 A |
5265 | { |
5266 | CFStringRef name = CFDictionaryGetValue(browseDict, CFSTR("Domain")); | |
5267 | if (name) | |
5268 | { | |
5269 | if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || | |
5270 | !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0]) | |
5271 | LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS browsing domain: %s", buf[0] ? buf : "(unknown)"); | |
5272 | else | |
5273 | { | |
5274 | debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browsing domain: %s", buf); | |
5275 | AppendDNameListElem(&BrowseDomains, 0, &d); | |
5276 | } | |
5277 | } | |
5278 | } | |
5279 | } | |
7cb34e5c A |
5280 | } |
5281 | } | |
030b743d | 5282 | CFRelease(ddnsdict); |
7f0064bd | 5283 | } |
8e92c31c | 5284 | |
67c8f8a1 | 5285 | if (RegDomains) |
283ee3ff | 5286 | { |
67c8f8a1 A |
5287 | CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac); |
5288 | if (btmm) | |
283ee3ff | 5289 | { |
67c8f8a1 A |
5290 | CFIndex size = CFDictionaryGetCount(btmm); |
5291 | const void *key[size]; | |
5292 | const void *val[size]; | |
5293 | CFDictionaryGetKeysAndValues(btmm, key, val); | |
5294 | for (i = 0; i < size; i++) | |
283ee3ff | 5295 | { |
32bb7e43 | 5296 | LogInfo("BackToMyMac %d", i); |
67c8f8a1 A |
5297 | if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8)) |
5298 | LogMsg("Can't read BackToMyMac %d key %s", i, buf); | |
5299 | else | |
5300 | { | |
5301 | mDNSu32 uid = atoi(buf); | |
5302 | if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8)) | |
5303 | LogMsg("Can't read BackToMyMac %d val %s", i, buf); | |
5304 | else if (MakeDomainNameFromDNSNameString(&d, buf) && d.c[0]) | |
5305 | { | |
32bb7e43 | 5306 | LogInfo("BackToMyMac %d %d %##s", i, uid, d.c); |
67c8f8a1 A |
5307 | AppendDNameListElem(&RegDomains, uid, &d); |
5308 | } | |
5309 | } | |
283ee3ff | 5310 | } |
67c8f8a1 | 5311 | CFRelease(btmm); |
283ee3ff A |
5312 | } |
5313 | } | |
8e92c31c | 5314 | |
67c8f8a1 | 5315 | if (setservers || setsearch) |
283ee3ff | 5316 | { |
030b743d A |
5317 | CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DNS); |
5318 | if (dict) | |
67c8f8a1 | 5319 | { |
030b743d | 5320 | if (setservers) |
67c8f8a1 | 5321 | { |
030b743d A |
5322 | CFArrayRef values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses); |
5323 | if (values) | |
67c8f8a1 | 5324 | { |
263eeeab | 5325 | LogInfo("DNS Server Address values: %d", (int)CFArrayGetCount(values)); |
030b743d | 5326 | for (i = 0; i < CFArrayGetCount(values); i++) |
67c8f8a1 | 5327 | { |
030b743d A |
5328 | CFStringRef s = CFArrayGetValueAtIndex(values, i); |
5329 | mDNSAddr addr = { mDNSAddrType_IPv4, { { { 0 } } } }; | |
5330 | if (s && CFStringGetCString(s, buf, 256, kCFStringEncodingUTF8) && | |
5331 | inet_aton(buf, (struct in_addr *) &addr.ip.v4)) | |
32bb7e43 A |
5332 | { |
5333 | LogInfo("Adding DNS server from dict: %s", buf); | |
4a95efb2 | 5334 | mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0, mDNSfalse); |
32bb7e43 | 5335 | } |
67c8f8a1 A |
5336 | } |
5337 | } | |
32bb7e43 | 5338 | else LogInfo("No DNS Server Address values"); |
030b743d A |
5339 | } |
5340 | if (setsearch) | |
5341 | { | |
5342 | // Add the manual and/or DHCP-dicovered search domains | |
5343 | CFArrayRef searchDomains = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains); | |
5344 | if (searchDomains) | |
67c8f8a1 | 5345 | { |
030b743d | 5346 | for (i = 0; i < CFArrayGetCount(searchDomains); i++) |
67c8f8a1 | 5347 | { |
030b743d A |
5348 | CFStringRef s = CFArrayGetValueAtIndex(searchDomains, i); |
5349 | if (s && CFStringGetCString(s, buf, sizeof(buf), kCFStringEncodingUTF8)) | |
294beb6e | 5350 | mDNS_AddSearchDomain_CString(buf, mDNSNULL); |
67c8f8a1 A |
5351 | } |
5352 | } | |
030b743d A |
5353 | else // No kSCPropNetDNSSearchDomains, so use kSCPropNetDNSDomainName |
5354 | { | |
5355 | // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains | |
5356 | // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if | |
5357 | // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and | |
5358 | // instead use the search domain list as the sole authority for what domains to search and in what order | |
5359 | // (and the domain from the "domain" field will also appear somewhere in that list). | |
5360 | CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetDNSDomainName); | |
5361 | if (string && CFStringGetCString(string, buf, sizeof(buf), kCFStringEncodingUTF8)) | |
294beb6e | 5362 | mDNS_AddSearchDomain_CString(buf, mDNSNULL); |
030b743d | 5363 | } |
294beb6e | 5364 | RetrySearchDomainQuestions(m); |
67c8f8a1 | 5365 | } |
030b743d | 5366 | CFRelease(dict); |
67c8f8a1 | 5367 | } |
283ee3ff | 5368 | } |
67c8f8a1 | 5369 | CFRelease(store); |
7f0064bd | 5370 | } |
67c8f8a1 | 5371 | } |
283ee3ff | 5372 | |
67c8f8a1 A |
5373 | mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *r) |
5374 | { | |
67c8f8a1 | 5375 | char buf[256]; |
030b743d | 5376 | (void)m; // Unused |
283ee3ff | 5377 | |
030b743d | 5378 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformGetPrimaryInterface"), NULL, NULL); |
32bb7e43 A |
5379 | if (!store) |
5380 | LogMsg("mDNSPlatformGetPrimaryInterface: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); | |
030b743d | 5381 | else |
7f0064bd | 5382 | { |
030b743d A |
5383 | CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_IPv4); |
5384 | if (dict) | |
7f0064bd | 5385 | { |
030b743d A |
5386 | r->type = mDNSAddrType_IPv4; |
5387 | r->ip.v4 = zerov4Addr; | |
5388 | CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetIPv4Router); | |
5389 | if (string) | |
67c8f8a1 | 5390 | { |
030b743d A |
5391 | if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) |
5392 | LogMsg("Could not convert router to CString"); | |
5393 | else | |
05292456 | 5394 | { |
030b743d A |
5395 | struct sockaddr_in saddr; |
5396 | saddr.sin_len = sizeof(saddr); | |
5397 | saddr.sin_family = AF_INET; | |
5398 | saddr.sin_port = 0; | |
5399 | inet_aton(buf, &saddr.sin_addr); | |
5400 | ||
32bb7e43 | 5401 | *(in_addr_t *)&r->ip.v4 = saddr.sin_addr.s_addr; |
05292456 A |
5402 | } |
5403 | } | |
030b743d A |
5404 | |
5405 | string = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface); | |
5406 | if (string) | |
7f0064bd | 5407 | { |
030b743d A |
5408 | mDNSBool HavePrimaryGlobalv6 = mDNSfalse; // does the primary interface have a global v6 address? |
5409 | struct ifaddrs *ifa = myGetIfAddrs(1); | |
5410 | ||
5411 | *v4 = *v6 = zeroAddr; | |
5412 | ||
5413 | if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) { LogMsg("Could not convert router to CString"); goto exit; } | |
5414 | ||
5415 | // find primary interface in list | |
5416 | while (ifa && (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4) || !HavePrimaryGlobalv6)) | |
7f0064bd | 5417 | { |
030b743d A |
5418 | mDNSAddr tmp6 = zeroAddr; |
5419 | if (!strcmp(buf, ifa->ifa_name)) | |
5420 | { | |
5421 | if (ifa->ifa_addr->sa_family == AF_INET) | |
5422 | { | |
5423 | if (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4)) SetupAddr(v4, ifa->ifa_addr); | |
5424 | } | |
5425 | else if (ifa->ifa_addr->sa_family == AF_INET6) | |
5426 | { | |
5427 | SetupAddr(&tmp6, ifa->ifa_addr); | |
5428 | if (tmp6.ip.v6.b[0] >> 5 == 1) // global prefix: 001 | |
5429 | { HavePrimaryGlobalv6 = mDNStrue; *v6 = tmp6; } | |
5430 | } | |
5431 | } | |
5432 | else | |
5433 | { | |
5434 | // We'll take a V6 address from the non-primary interface if the primary interface doesn't have a global V6 address | |
5435 | if (!HavePrimaryGlobalv6 && ifa->ifa_addr->sa_family == AF_INET6 && !v6->ip.v6.b[0]) | |
5436 | { | |
5437 | SetupAddr(&tmp6, ifa->ifa_addr); | |
5438 | if (tmp6.ip.v6.b[0] >> 5 == 1) *v6 = tmp6; | |
5439 | } | |
5440 | } | |
5441 | ifa = ifa->ifa_next; | |
7f0064bd | 5442 | } |
030b743d A |
5443 | |
5444 | // Note that while we advertise v6, we still require v4 (possibly NAT'd, but not link-local) because we must use | |
5445 | // V4 to communicate w/ our DNS server | |
7f0064bd | 5446 | } |
030b743d A |
5447 | |
5448 | exit: | |
5449 | CFRelease(dict); | |
7f0064bd | 5450 | } |
030b743d | 5451 | CFRelease(store); |
67c8f8a1 | 5452 | } |
263eeeab | 5453 | return mStatus_NoError; |
67c8f8a1 A |
5454 | } |
5455 | ||
5456 | mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status) | |
5457 | { | |
32bb7e43 | 5458 | LogInfo("mDNSPlatformDynDNSHostNameStatusChanged %d %##s", status, dname->c); |
67c8f8a1 A |
5459 | char uname[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL |
5460 | ConvertDomainNameToCString(dname, uname); | |
5461 | ||
5462 | char *p = uname; | |
5463 | while (*p) | |
5464 | { | |
5465 | *p = tolower(*p); | |
5466 | if (!(*(p+1)) && *p == '.') *p = 0; // if last character, strip trailing dot | |
5467 | p++; | |
5468 | } | |
5469 | ||
5470 | // We need to make a CFDictionary called "State:/Network/DynamicDNS" containing (at present) a single entity. | |
5471 | // That single entity is a CFDictionary with name "HostNames". | |
5472 | // The "HostNames" CFDictionary contains a set of name/value pairs, where the each name is the FQDN | |
5473 | // in question, and the corresponding value is a CFDictionary giving the state for that FQDN. | |
5474 | // (At present we only support a single FQDN, so this dictionary holds just a single name/value pair.) | |
5475 | // The CFDictionary for each FQDN holds (at present) a single name/value pair, | |
5476 | // where the name is "Status" and the value is a CFNumber giving an errror code (with zero meaning success). | |
5477 | ||
263eeeab A |
5478 | const CFStringRef StateKeys [1] = { CFSTR("HostNames") }; |
5479 | const CFStringRef HostKeys [1] = { CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8) }; | |
5480 | const CFStringRef StatusKeys[1] = { CFSTR("Status") }; | |
5481 | if (!HostKeys[0]) LogMsg("SetDDNSNameStatus: CFStringCreateWithCString(%s) failed", uname); | |
5482 | else | |
5483 | { | |
5484 | const CFNumberRef StatusVals[1] = { CFNumberCreate(NULL, kCFNumberSInt32Type, &status) }; | |
5485 | if (!StatusVals[0]) LogMsg("SetDDNSNameStatus: CFNumberCreate(%d) failed", status); | |
5486 | else | |
5487 | { | |
5488 | const CFDictionaryRef HostVals[1] = { CFDictionaryCreate(NULL, (void*)StatusKeys, (void*)StatusVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) }; | |
5489 | if (HostVals[0]) | |
5490 | { | |
5491 | const CFDictionaryRef StateVals[1] = { CFDictionaryCreate(NULL, (void*)HostKeys, (void*)HostVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) }; | |
5492 | if (StateVals[0]) | |
5493 | { | |
5494 | CFDictionaryRef StateDict = CFDictionaryCreate(NULL, (void*)StateKeys, (void*)StateVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
5495 | if (StateDict) | |
5496 | { | |
5497 | mDNSDynamicStoreSetConfig(kmDNSDynamicConfig, mDNSNULL, StateDict); | |
5498 | CFRelease(StateDict); | |
5499 | } | |
5500 | CFRelease(StateVals[0]); | |
5501 | } | |
5502 | CFRelease(HostVals[0]); | |
5503 | } | |
5504 | CFRelease(StatusVals[0]); | |
5505 | } | |
5506 | CFRelease(HostKeys[0]); | |
5507 | } | |
5508 | } | |
5509 | ||
5510 | #if APPLE_OSX_mDNSResponder | |
5511 | #if ! NO_AWACS | |
5512 | ||
5513 | // checks whether a domain is present in Setup:/Network/BackToMyMac. Just because there is a key in the | |
5514 | // keychain for a domain, it does not become a valid BTMM domain. If things get inconsistent, this will | |
5515 | // help catch it | |
5516 | mDNSlocal mDNSBool IsBTMMDomain(domainname *d) | |
5517 | { | |
5518 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:IsBTMMDomain"), NULL, NULL); | |
5519 | if (!store) | |
5520 | { | |
5521 | LogMsg("IsBTMMDomain: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); | |
5522 | return mDNSfalse; | |
5523 | } | |
5524 | CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac); | |
5525 | if (btmm) | |
5526 | { | |
5527 | CFIndex size = CFDictionaryGetCount(btmm); | |
5528 | char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL | |
5529 | const void *key[size]; | |
5530 | const void *val[size]; | |
5531 | domainname dom; | |
5532 | int i; | |
5533 | CFDictionaryGetKeysAndValues(btmm, key, val); | |
5534 | for (i = 0; i < size; i++) | |
5535 | { | |
5536 | LogInfo("BackToMyMac %d", i); | |
5537 | if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8)) | |
5538 | LogMsg("IsBTMMDomain: ERROR!! Can't read BackToMyMac %d key %s", i, buf); | |
5539 | else | |
5540 | { | |
5541 | mDNSu32 uid = atoi(buf); | |
5542 | if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8)) | |
5543 | LogMsg("IsBTMMDomain: Can't read BackToMyMac %d val %s", i, buf); | |
5544 | else if (MakeDomainNameFromDNSNameString(&dom, buf) && dom.c[0]) | |
5545 | { | |
5546 | if (SameDomainName(&dom, d)) | |
5547 | { | |
5548 | LogInfo("IsBTMMDomain: Domain %##s is a btmm domain, uid %u", d->c, uid); | |
5549 | CFRelease(btmm); | |
5550 | CFRelease(store); | |
5551 | return mDNStrue; | |
5552 | } | |
5553 | } | |
5554 | } | |
5555 | } | |
5556 | CFRelease(btmm); | |
5557 | } | |
5558 | CFRelease(store); | |
5559 | LogInfo("IsBTMMDomain: Domain %##s not a btmm domain", d->c); | |
5560 | return mDNSfalse; | |
5561 | } | |
5562 | ||
5563 | // Appends data to the buffer | |
5564 | mDNSlocal int AddOneItem(char *buf, int bufsz, char *data, int *currlen) | |
5565 | { | |
5566 | int len; | |
5567 | ||
5568 | len = strlcpy(buf + *currlen, data, bufsz - *currlen); | |
5569 | if (len >= (bufsz - *currlen)) | |
5570 | { | |
5571 | // if we have exceeded the space in buf, it has already been NULL terminated | |
5572 | // and we have nothing more to do. Set currlen to the last byte so that the caller | |
5573 | // knows to do the right thing | |
5574 | LogMsg("AddOneItem: Exceeded the max buffer size currlen %d, len %d", *currlen, len); | |
5575 | *currlen = bufsz - 1; | |
5576 | return -1; | |
5577 | } | |
5578 | else { (*currlen) += len; } | |
5579 | ||
5580 | buf[*currlen] = ','; | |
5581 | if (*currlen >= bufsz) | |
5582 | { | |
5583 | LogMsg("AddOneItem: ERROR!! How can currlen be %d", *currlen); | |
5584 | *currlen = bufsz - 1; | |
5585 | buf[*currlen] = 0; | |
5586 | return -1; | |
5587 | } | |
5588 | // if we have filled up the buffer exactly, then there is no more work to do | |
5589 | if (*currlen == bufsz - 1) { buf[*currlen] = 0; return -1; } | |
5590 | (*currlen)++; | |
5591 | return *currlen; | |
5592 | } | |
5593 | ||
5594 | // If we have at least one BTMM domain, then trigger the connection to the relay. If we have no | |
5595 | // BTMM domains, then bring down the connection to the relay. | |
5596 | mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) | |
5597 | { | |
5598 | DomainAuthInfo *BTMMDomain = mDNSNULL; | |
5599 | DomainAuthInfo *FoundInList; | |
4a95efb2 | 5600 | static mDNSBool AWACSDConnected = mDNSfalse; |
263eeeab A |
5601 | char AllUsers[1024]; // maximum size of mach message |
5602 | char AllPass[1024]; // maximum size of mach message | |
5603 | char username[MAX_DOMAIN_LABEL + 1]; | |
5604 | int currulen = 0; | |
5605 | int currplen = 0; | |
5606 | ||
5607 | // if a domain is being deleted, we want to send a disconnect. If we send a disconnect now, | |
5608 | // we may not be able to send the dns queries over the relay connection which may be needed | |
5609 | // for sending the deregistrations. Hence, we need to delay sending the disconnect. But we | |
5610 | // need to make sure that we send the disconnect before attempting the next connect as the | |
5611 | // awacs connections are redirected based on usernames. | |
5612 | // | |
5613 | // For now we send a disconnect immediately. When we start sending dns queries over the relay | |
5614 | // connection, we will need to fix this. | |
5615 | ||
5616 | for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) | |
5617 | if (!FoundInList->deltime && FoundInList->AutoTunnel && IsBTMMDomain(&FoundInList->domain)) | |
5618 | { | |
5619 | // We need the passwd from the first domain. | |
5620 | BTMMDomain = FoundInList; | |
5621 | ConvertDomainLabelToCString_unescaped((domainlabel *)BTMMDomain->domain.c, username); | |
5622 | LogInfo("UpdateBTMMRelayConnection: user %s for domain %##s", username, BTMMDomain->domain.c); | |
5623 | if (AddOneItem(AllUsers, sizeof(AllUsers), username, &currulen) == -1) break; | |
5624 | if (AddOneItem(AllPass, sizeof(AllPass), BTMMDomain->b64keydata, &currplen) == -1) break; | |
5625 | } | |
5626 | ||
5627 | if (BTMMDomain) | |
5628 | { | |
5629 | // In the normal case (where we neither exceed the buffer size nor write bytes that | |
5630 | // fit exactly into the buffer), currulen/currplen should be a different size than | |
5631 | // (AllUsers - 1) / (AllPass - 1). In that case, we need to override the "," with a NULL byte. | |
5632 | ||
5633 | if (currulen != (int)(sizeof(AllUsers) - 1)) AllUsers[currulen - 1] = 0; | |
5634 | if (currplen != (int)(sizeof(AllPass) - 1)) AllPass[currplen - 1] = 0; | |
5635 | ||
5636 | LogInfo("UpdateBTMMRelayConnection: AWS_Connect for user %s", AllUsers); | |
5637 | AWACS_Connect(AllUsers, AllPass, "hello.connectivity.me.com"); | |
5638 | AWACSDConnected = mDNStrue; | |
5639 | } | |
67c8f8a1 A |
5640 | else |
5641 | { | |
263eeeab A |
5642 | // Disconnect only if we connected previously |
5643 | if (AWACSDConnected) | |
05292456 | 5644 | { |
263eeeab A |
5645 | LogInfo("UpdateBTMMRelayConnection: AWS_Disconnect"); |
5646 | AWACS_Disconnect(); | |
5647 | AWACSDConnected = mDNSfalse; | |
05292456 | 5648 | } |
263eeeab | 5649 | else LogInfo("UpdateBTMMRelayConnection: Not calling AWS_Disconnect"); |
7f0064bd | 5650 | } |
67c8f8a1 | 5651 | } |
263eeeab A |
5652 | #else |
5653 | mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) | |
5654 | { | |
5655 | (void) m; // Unused | |
5656 | LogInfo("UpdateBTMMRelayConnection: AWACS connection not started, no AWACS library"); | |
5657 | } | |
5658 | #endif // ! NO_AWACS | |
5659 | #endif // APPLE_OSX_mDNSResponder | |
67c8f8a1 A |
5660 | |
5661 | // MUST be called holding the lock -- this routine calls SetupLocalAutoTunnelInterface_internal() | |
96f69b28 | 5662 | mDNSexport void SetDomainSecrets(mDNS *m) |
67c8f8a1 A |
5663 | { |
5664 | #ifdef NO_SECURITYFRAMEWORK | |
5665 | (void)m; | |
5666 | LogMsg("Note: SetDomainSecrets: no keychain support"); | |
5667 | #else | |
5668 | mDNSBool haveAutoTunnels = mDNSfalse; | |
5669 | ||
32bb7e43 | 5670 | LogInfo("SetDomainSecrets"); |
67c8f8a1 A |
5671 | |
5672 | // Rather than immediately deleting all keys now, we mark them for deletion in ten seconds. | |
5673 | // In the case where the user simultaneously removes their DDNS host name and the key | |
5674 | // for it, this gives mDNSResponder ten seconds to gracefully delete the name from the | |
5675 | // server before it loses access to the necessary key. Otherwise, we'd leave orphaned | |
5676 | // address records behind that we no longer have permission to delete. | |
5677 | DomainAuthInfo *ptr; | |
5678 | for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) | |
5679 | ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10); | |
5680 | ||
5681 | #if APPLE_OSX_mDNSResponder | |
5682 | { | |
5683 | // Mark all TunnelClients for deletion | |
5684 | ClientTunnel *client; | |
5685 | for (client = m->TunnelClients; client; client = client->next) | |
5686 | { | |
32bb7e43 | 5687 | LogInfo("SetDomainSecrets: tunnel to %##s marked for deletion", client->dstname.c); |
030b743d | 5688 | client->MarkedForDeletion = mDNStrue; |
67c8f8a1 A |
5689 | } |
5690 | } | |
32bb7e43 | 5691 | #endif // APPLE_OSX_mDNSResponder |
67c8f8a1 | 5692 | |
96f69b28 | 5693 | // String Array used to write list of private domains to Dynamic Store |
67c8f8a1 A |
5694 | CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
5695 | if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; } | |
5696 | CFIndex i; | |
5697 | CFDataRef data = NULL; | |
294beb6e | 5698 | const int itemsPerEntry = 4; // domain name, key name, key value, Name value |
67c8f8a1 A |
5699 | CFArrayRef secrets = NULL; |
5700 | int err = mDNSKeychainGetSecrets(&secrets); | |
5701 | if (err || !secrets) | |
5702 | LogMsg("SetDomainSecrets: mDNSKeychainGetSecrets failed error %d CFArrayRef %p", err, secrets); | |
5703 | else | |
5704 | { | |
5705 | CFIndex ArrayCount = CFArrayGetCount(secrets); | |
5706 | // Iterate through the secrets | |
5707 | for (i = 0; i < ArrayCount; ++i) | |
5708 | { | |
294beb6e A |
5709 | mDNSBool AutoTunnel; |
5710 | int j, offset; | |
67c8f8a1 A |
5711 | CFArrayRef entry = CFArrayGetValueAtIndex(secrets, i); |
5712 | if (CFArrayGetTypeID() != CFGetTypeID(entry) || itemsPerEntry != CFArrayGetCount(entry)) | |
294beb6e | 5713 | { LogMsg("SetDomainSecrets: malformed entry %d, itemsPerEntry %d", i, itemsPerEntry); continue; } |
67c8f8a1 A |
5714 | for (j = 0; j < CFArrayGetCount(entry); ++j) |
5715 | if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j))) | |
294beb6e | 5716 | { LogMsg("SetDomainSecrets: malformed entry item %d", j); continue; } |
67c8f8a1 | 5717 | |
67c8f8a1 | 5718 | // The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness |
96f69b28 | 5719 | |
294beb6e A |
5720 | // Max legal domainname as C-string, including space for btmmprefix and terminating NUL |
5721 | // Get DNS domain this key is for (kmDNSKcWhere) | |
5722 | char stringbuf[MAX_ESCAPED_DOMAIN_NAME + sizeof(btmmprefix)]; | |
5723 | data = CFArrayGetValueAtIndex(entry, kmDNSKcWhere); | |
96f69b28 | 5724 | if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) |
67c8f8a1 | 5725 | { LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; } |
96f69b28 A |
5726 | CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf); |
5727 | stringbuf[CFDataGetLength(data)] = '\0'; | |
67c8f8a1 | 5728 | |
294beb6e A |
5729 | AutoTunnel = mDNSfalse; |
5730 | offset = 0; | |
5731 | if (!strncmp(stringbuf, dnsprefix, strlen(dnsprefix))) | |
5732 | offset = strlen(dnsprefix); | |
5733 | else if (!strncmp(stringbuf, btmmprefix, strlen(btmmprefix))) | |
5734 | { | |
5735 | AutoTunnel = mDNStrue; | |
5736 | offset = strlen(btmmprefix); | |
5737 | } | |
67c8f8a1 | 5738 | domainname domain; |
294beb6e | 5739 | if (!MakeDomainNameFromDNSNameString(&domain, stringbuf + offset)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; } |
96f69b28 | 5740 | |
294beb6e A |
5741 | // Get key name (kmDNSKcAccount) |
5742 | data = CFArrayGetValueAtIndex(entry, kmDNSKcAccount); | |
96f69b28 A |
5743 | if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) |
5744 | { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; } | |
5745 | CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf); | |
5746 | stringbuf[CFDataGetLength(data)] = '\0'; | |
67c8f8a1 | 5747 | |
67c8f8a1 | 5748 | domainname keyname; |
96f69b28 | 5749 | if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; } |
67c8f8a1 | 5750 | |
294beb6e A |
5751 | // Get key data (kmDNSKcKey) |
5752 | data = CFArrayGetValueAtIndex(entry, kmDNSKcKey); | |
96f69b28 | 5753 | if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) |
67c8f8a1 | 5754 | { LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; } |
96f69b28 A |
5755 | CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf); |
5756 | stringbuf[CFDataGetLength(data)] = '\0'; // mDNS_SetSecretForDomain requires NULL-terminated C string for key | |
67c8f8a1 | 5757 | |
294beb6e A |
5758 | // Get the Name of the keychain entry (kmDNSKcName) host or host:port |
5759 | // The hostname also has the port number and ":". It should take a maximum of 6 bytes. | |
5760 | char hostbuf[MAX_ESCAPED_DOMAIN_NAME + 6]; // Max legal domainname as C-string, including terminating NUL | |
5761 | data = CFArrayGetValueAtIndex(entry, kmDNSKcName); | |
5762 | if (CFDataGetLength(data) >= (int)sizeof(hostbuf)) | |
5763 | { LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; } | |
5764 | CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)hostbuf); | |
5765 | hostbuf[CFDataGetLength(data)] = '\0'; | |
5766 | ||
5767 | domainname hostname; | |
5768 | mDNSIPPort port; | |
5769 | char *hptr; | |
5770 | hptr = strchr(hostbuf, ':'); | |
5771 | ||
5772 | port.NotAnInteger = 0; | |
5773 | if (hptr) | |
5774 | { | |
5775 | mDNSu8 *p; | |
5776 | mDNSu16 val = 0; | |
5777 | ||
5778 | *hptr++ = '\0'; | |
5779 | while(hptr && *hptr != 0) | |
5780 | { | |
5781 | if (*hptr < '0' || *hptr > '9') | |
5782 | { LogMsg("SetDomainSecrets: Malformed Port number %d, val %d", *hptr, val); val = 0; break;} | |
5783 | val = val * 10 + *hptr - '0'; | |
5784 | hptr++; | |
5785 | } | |
5786 | if (!val) continue; | |
5787 | p = (mDNSu8 *)&val; | |
5788 | port.NotAnInteger = p[0] << 8 | p[1]; | |
5789 | } | |
5790 | // The hostbuf is of the format dsid@hostname:port. We don't care about the dsid. | |
5791 | hptr = strchr(hostbuf, '@'); | |
5792 | if (hptr) | |
5793 | hptr++; | |
5794 | else | |
5795 | hptr = hostbuf; | |
5796 | if (!MakeDomainNameFromDNSNameString(&hostname, hptr)) { LogMsg("SetDomainSecrets: bad host name %s", hptr); continue; } | |
5797 | ||
67c8f8a1 A |
5798 | DomainAuthInfo *FoundInList; |
5799 | for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) | |
5800 | if (SameDomainName(&FoundInList->domain, &domain)) break; | |
5801 | ||
5802 | #if APPLE_OSX_mDNSResponder | |
5803 | if (FoundInList) | |
5804 | { | |
5805 | // If any client tunnel destination is in this domain, set deletion flag to false | |
5806 | ClientTunnel *client; | |
5807 | for (client = m->TunnelClients; client; client = client->next) | |
5808 | if (FoundInList == GetAuthInfoForName_internal(m, &client->dstname)) | |
5809 | { | |
32bb7e43 | 5810 | LogInfo("SetDomainSecrets: tunnel to %##s no longer marked for deletion", client->dstname.c); |
030b743d | 5811 | client->MarkedForDeletion = mDNSfalse; |
67c8f8a1 A |
5812 | } |
5813 | } | |
5814 | ||
32bb7e43 | 5815 | #endif // APPLE_OSX_mDNSResponder |
67c8f8a1 A |
5816 | |
5817 | // Uncomment the line below to view the keys as they're read out of the system keychain | |
5818 | // DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE! | |
294beb6e A |
5819 | //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])); |
5820 | LogInfo("SetDomainSecrets: domain %##s keyname %##s hostname %##s port %d", &domain.c, &keyname.c, hostname.c, (port.b[0] << 8 | port.b[1])); | |
67c8f8a1 A |
5821 | |
5822 | // If didn't find desired domain in the list, make a new entry | |
5823 | ptr = FoundInList; | |
5824 | if (FoundInList && FoundInList->AutoTunnel && haveAutoTunnels == mDNSfalse) haveAutoTunnels = mDNStrue; | |
5825 | if (!FoundInList) | |
5826 | { | |
5827 | ptr = (DomainAuthInfo*)mallocL("DomainAuthInfo", sizeof(*ptr)); | |
5828 | if (!ptr) { LogMsg("SetDomainSecrets: No memory"); continue; } | |
5829 | } | |
5830 | ||
294beb6e A |
5831 | //LogInfo("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain); |
5832 | ||
5833 | // It is an AutoTunnel if the keychains tells us so (with btmm prefix) or if it is a TunnelModeDomain | |
5834 | if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, &hostname, &port, (AutoTunnel ? btmmprefix : (IsTunnelModeDomain(&domain) ? dnsprefix : NULL))) == mStatus_BadParamErr) | |
67c8f8a1 A |
5835 | { |
5836 | if (!FoundInList) mDNSPlatformMemFree(ptr); // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately | |
5837 | continue; | |
5838 | } | |
5839 | ||
5840 | #if APPLE_OSX_mDNSResponder | |
5e65c77f | 5841 | if (ptr->AutoTunnel) UpdateAutoTunnelDomainStatus(m, ptr); |
32bb7e43 | 5842 | #endif // APPLE_OSX_mDNSResponder |
67c8f8a1 | 5843 | |
96f69b28 A |
5844 | ConvertDomainNameToCString(&domain, stringbuf); |
5845 | CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8); | |
67c8f8a1 A |
5846 | if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); } |
5847 | } | |
5848 | CFRelease(secrets); | |
5849 | } | |
32bb7e43 | 5850 | mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa); |
67c8f8a1 A |
5851 | CFRelease(sa); |
5852 | ||
32bb7e43 | 5853 | #if APPLE_OSX_mDNSResponder |
67c8f8a1 A |
5854 | { |
5855 | // clean up ClientTunnels | |
030b743d A |
5856 | ClientTunnel **pp = &m->TunnelClients; |
5857 | while (*pp) | |
67c8f8a1 | 5858 | { |
030b743d | 5859 | if ((*pp)->MarkedForDeletion) |
67c8f8a1 | 5860 | { |
030b743d | 5861 | ClientTunnel *cur = *pp; |
32bb7e43 | 5862 | LogInfo("SetDomainSecrets: removing client %p %##s from list", cur, cur->dstname.c); |
96f69b28 | 5863 | if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q); |
67c8f8a1 | 5864 | AutoTunnelSetKeys(cur, mDNSfalse); |
030b743d | 5865 | *pp = cur->next; |
67c8f8a1 A |
5866 | freeL("ClientTunnel", cur); |
5867 | } | |
32bb7e43 | 5868 | else |
030b743d | 5869 | pp = &(*pp)->next; |
67c8f8a1 A |
5870 | } |
5871 | ||
5872 | DomainAuthInfo *info = m->AuthInfoList; | |
5873 | while (info) | |
5874 | { | |
5e65c77f | 5875 | if (info->AutoTunnel && info->deltime) |
67c8f8a1 | 5876 | { |
5e65c77f | 5877 | if (info->AutoTunnelNAT.clientContext) |
67c8f8a1 | 5878 | { |
5e65c77f A |
5879 | // stop the NAT operation |
5880 | mDNS_StopNATOperation_internal(m, &info->AutoTunnelNAT); | |
5881 | if (info->AutoTunnelNAT.clientCallback) | |
5882 | { | |
263eeeab | 5883 | // Reset port and cleanup the state |
5e65c77f A |
5884 | info->AutoTunnelNAT.ExternalAddress = m->ExternalAddress; |
5885 | info->AutoTunnelNAT.ExternalPort = zeroIPPort; | |
263eeeab | 5886 | info->AutoTunnelNAT.RequestedPort = zeroIPPort; |
5e65c77f A |
5887 | info->AutoTunnelNAT.Lifetime = 0; |
5888 | info->AutoTunnelNAT.Result = mStatus_NoError; | |
5889 | mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback | |
263eeeab | 5890 | AutoTunnelDeleteAuthInfoState(m, info); |
5e65c77f A |
5891 | mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again |
5892 | } | |
5893 | info->AutoTunnelNAT.clientContext = mDNSNULL; | |
67c8f8a1 | 5894 | } |
32bb7e43 | 5895 | RemoveAutoTunnelDomainStatus(m, info); |
67c8f8a1 A |
5896 | } |
5897 | info = info->next; | |
5898 | } | |
5899 | ||
5900 | if (!haveAutoTunnels && !m->TunnelClients && m->AutoTunnelHostAddrActive) | |
5901 | { | |
5902 | // remove interface if no autotunnel servers and no more client tunnels | |
32bb7e43 | 5903 | LogInfo("SetDomainSecrets: Bringing tunnel interface DOWN"); |
67c8f8a1 A |
5904 | m->AutoTunnelHostAddrActive = mDNSfalse; |
5905 | (void)mDNSAutoTunnelInterfaceUpDown(kmDNSDown, m->AutoTunnelHostAddr.b); | |
32bb7e43 | 5906 | mDNSPlatformMemZero(m->AutoTunnelHostAddr.b, sizeof(m->AutoTunnelHostAddr.b)); |
67c8f8a1 | 5907 | } |
32bb7e43 | 5908 | |
67c8f8a1 A |
5909 | if (m->AutoTunnelHostAddr.b[0]) |
5910 | if (TunnelClients(m) || TunnelServers(m)) | |
263eeeab A |
5911 | SetupLocalAutoTunnelInterface_internal(m, mDNSfalse); |
5912 | ||
5913 | UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections | |
5914 | UpdateBTMMRelayConnection(m); | |
67c8f8a1 | 5915 | } |
32bb7e43 | 5916 | #endif // APPLE_OSX_mDNSResponder |
67c8f8a1 | 5917 | |
263eeeab A |
5918 | CheckSuppressUnusableQuestions(m); |
5919 | ||
67c8f8a1 A |
5920 | #endif /* NO_SECURITYFRAMEWORK */ |
5921 | } | |
5922 | ||
5923 | mDNSlocal void SetLocalDomains(void) | |
5924 | { | |
5925 | CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
5926 | if (!sa) { LogMsg("SetLocalDomains: CFArrayCreateMutable failed"); return; } | |
5927 | ||
5928 | CFArrayAppendValue(sa, CFSTR("local")); | |
5929 | CFArrayAppendValue(sa, CFSTR("254.169.in-addr.arpa")); | |
5930 | CFArrayAppendValue(sa, CFSTR("8.e.f.ip6.arpa")); | |
5931 | CFArrayAppendValue(sa, CFSTR("9.e.f.ip6.arpa")); | |
5932 | CFArrayAppendValue(sa, CFSTR("a.e.f.ip6.arpa")); | |
5933 | CFArrayAppendValue(sa, CFSTR("b.e.f.ip6.arpa")); | |
5934 | ||
32bb7e43 | 5935 | mDNSDynamicStoreSetConfig(kmDNSMulticastConfig, mDNSNULL, sa); |
67c8f8a1 | 5936 | CFRelease(sa); |
8e92c31c A |
5937 | } |
5938 | ||
32bb7e43 A |
5939 | mDNSlocal void GetCurrentPMSetting(const CFStringRef name, mDNSs32 *val) |
5940 | { | |
5941 | #if USE_IOPMCOPYACTIVEPMPREFERENCES | |
5942 | CFTypeRef blob = NULL; | |
5943 | CFStringRef str = NULL; | |
5944 | CFDictionaryRef odict = NULL; | |
5945 | CFDictionaryRef idict = NULL; | |
5946 | CFNumberRef number = NULL; | |
5947 | ||
5948 | blob = IOPSCopyPowerSourcesInfo(); | |
5949 | if (!blob) { LogMsg("GetCurrentPMSetting: IOPSCopyPowerSourcesInfo failed!"); goto end; } | |
5950 | ||
5951 | odict = IOPMCopyActivePMPreferences(); | |
5952 | if (!odict) { LogMsg("GetCurrentPMSetting: IOPMCopyActivePMPreferences failed!"); goto end; } | |
5953 | ||
5954 | str = IOPSGetProvidingPowerSourceType(blob); | |
5955 | if (!str) { LogMsg("GetCurrentPMSetting: IOPSGetProvidingPowerSourceType failed!"); goto end; } | |
5956 | ||
5957 | idict = CFDictionaryGetValue(odict, str); | |
5958 | if (!idict) | |
5959 | { | |
5960 | char buf[256]; | |
5961 | if (!CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; | |
5962 | LogMsg("GetCurrentPMSetting: CFDictionaryGetValue (%s) failed!", buf); | |
5963 | goto end; | |
5964 | } | |
5965 | ||
5966 | number = CFDictionaryGetValue(idict, name); | |
5967 | if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val)) | |
5968 | *val = 0; | |
5969 | end: | |
5970 | if (blob) CFRelease(blob); | |
5971 | if (odict) CFRelease(odict); | |
5972 | ||
5973 | #else | |
5974 | ||
5975 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetCurrentPMSetting"), NULL, NULL); | |
5976 | if (!store) LogMsg("GetCurrentPMSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); | |
5977 | else | |
5978 | { | |
263eeeab | 5979 | CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings); |
32bb7e43 A |
5980 | if (!dict) LogSPS("GetCurrentPMSetting: Could not get IOPM CurrentSettings dict"); |
5981 | else | |
5982 | { | |
5983 | CFNumberRef number = CFDictionaryGetValue(dict, name); | |
5984 | if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val)) | |
5985 | *val = 0; | |
5986 | CFRelease(dict); | |
5987 | } | |
5988 | CFRelease(store); | |
5989 | } | |
5990 | ||
5991 | #endif | |
5992 | } | |
5993 | ||
5994 | #if APPLE_OSX_mDNSResponder | |
5995 | ||
5996 | static CFMutableDictionaryRef spsStatusDict = NULL; | |
5997 | static const CFStringRef kMetricRef = CFSTR("Metric"); | |
5998 | ||
5999 | mDNSlocal void SPSStatusPutNumber(CFMutableDictionaryRef dict, const mDNSu8* const ptr, CFStringRef key) | |
6000 | { | |
6001 | mDNSu8 tmp = (ptr[0] - '0') * 10 + ptr[1] - '0'; | |
6002 | CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt8Type, &tmp); | |
6003 | if (!num) | |
6004 | LogMsg("SPSStatusPutNumber: Could not create CFNumber"); | |
6005 | else | |
6006 | { | |
6007 | CFDictionarySetValue(dict, key, num); | |
6008 | CFRelease(num); | |
6009 | } | |
6010 | } | |
6011 | ||
6012 | mDNSlocal CFMutableDictionaryRef SPSCreateDict(const mDNSu8* const ptr) | |
6013 | { | |
6014 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
6015 | if (!dict) { LogMsg("SPSCreateDict: Could not create CFDictionary dict"); return dict; } | |
6016 | ||
6017 | char buffer[1024]; | |
6018 | buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", ptr) - 1] = 0; | |
6019 | CFStringRef spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); | |
6020 | if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname full"); CFRelease(dict); return NULL; } | |
6021 | CFDictionarySetValue(dict, CFSTR("FullName"), spsname); | |
6022 | CFRelease(spsname); | |
6023 | ||
6024 | if (ptr[0] >= 2) SPSStatusPutNumber(dict, ptr + 1, CFSTR("Type")); | |
6025 | if (ptr[0] >= 5) SPSStatusPutNumber(dict, ptr + 4, CFSTR("Portability")); | |
6026 | if (ptr[0] >= 8) SPSStatusPutNumber(dict, ptr + 7, CFSTR("MarginalPower")); | |
6027 | if (ptr[0] >= 11) SPSStatusPutNumber(dict, ptr +10, CFSTR("TotalPower")); | |
6028 | ||
6029 | mDNSu32 tmp = SPSMetric(ptr); | |
6030 | CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tmp); | |
6031 | if (!num) | |
6032 | LogMsg("SPSCreateDict: Could not create CFNumber"); | |
6033 | else | |
6034 | { | |
6035 | CFDictionarySetValue(dict, kMetricRef, num); | |
6036 | CFRelease(num); | |
6037 | } | |
6038 | ||
6039 | if (ptr[0] >= 12) | |
6040 | { | |
6041 | memcpy(buffer, ptr + 13, ptr[0] - 12); | |
6042 | buffer[ptr[0] - 12] = 0; | |
6043 | spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); | |
6044 | if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname"); CFRelease(dict); return NULL; } | |
6045 | else | |
6046 | { | |
6047 | CFDictionarySetValue(dict, CFSTR("PrettyName"), spsname); | |
6048 | CFRelease(spsname); | |
6049 | } | |
6050 | } | |
6051 | ||
6052 | return dict; | |
6053 | } | |
6054 | ||
6055 | mDNSlocal CFComparisonResult CompareSPSEntries(const void *val1, const void *val2, void *context) | |
6056 | { | |
6057 | (void)context; | |
6058 | return CFNumberCompare((CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val1, kMetricRef), | |
6059 | (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val2, kMetricRef), | |
6060 | NULL); | |
6061 | } | |
6062 | ||
6063 | mDNSlocal void UpdateSPSStatus(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) | |
6064 | { | |
32bb7e43 A |
6065 | NetworkInterfaceInfo* info = (NetworkInterfaceInfo*)question->QuestionContext; |
6066 | debugf("UpdateSPSStatus: %s %##s %s %s", info->ifname, question->qname.c, AddRecord ? "Add" : "Rmv", answer ? RRDisplayString(m, answer) : "<null>"); | |
6067 | ||
263eeeab A |
6068 | mDNS_Lock(m); |
6069 | mDNS_UpdateAllowSleep(m); | |
6070 | mDNS_Unlock(m); | |
6071 | ||
32bb7e43 A |
6072 | if (answer && SPSMetric(answer->rdata->u.name.c) > 999999) return; // Ignore instances with invalid names |
6073 | ||
6074 | if (!spsStatusDict) | |
6075 | { | |
6076 | spsStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
6077 | if (!spsStatusDict) { LogMsg("UpdateSPSStatus: Could not create CFDictionary spsStatusDict"); return; } | |
6078 | } | |
6079 | ||
6080 | CFStringRef ifname = CFStringCreateWithCString(NULL, info->ifname, kCFStringEncodingUTF8); | |
6081 | if (!ifname) { LogMsg("UpdateSPSStatus: Could not create CFString ifname"); return; } | |
6082 | ||
6083 | CFMutableArrayRef array = NULL; | |
6084 | ||
6085 | if (!CFDictionaryGetValueIfPresent(spsStatusDict, ifname, (const void**) &array)) | |
6086 | { | |
6087 | array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
6088 | if (!array) { LogMsg("UpdateSPSStatus: Could not create CFMutableArray"); CFRelease(ifname); return; } | |
6089 | CFDictionarySetValue(spsStatusDict, ifname, array); | |
6090 | CFRelease(array); // let go of our reference, now that the dict has one | |
6091 | } | |
6092 | else | |
6093 | if (!array) { LogMsg("UpdateSPSStatus: Could not get CFMutableArray for %s", info->ifname); CFRelease(ifname); return; } | |
6094 | ||
6095 | if (!answer) // special call that means the question has been stopped (because the interface is going away) | |
6096 | CFArrayRemoveAllValues(array); | |
6097 | else | |
6098 | { | |
6099 | CFMutableDictionaryRef dict = SPSCreateDict(answer->rdata->u.name.c); | |
6100 | if (!dict) { CFRelease(ifname); return; } | |
6101 | ||
6102 | if (AddRecord) | |
6103 | { | |
6104 | if (!CFArrayContainsValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict)) | |
6105 | { | |
6106 | int i=0; | |
6107 | for (i=0; i<CFArrayGetCount(array); i++) | |
6108 | if (CompareSPSEntries(CFArrayGetValueAtIndex(array, i), dict, NULL) != kCFCompareLessThan) | |
6109 | break; | |
6110 | CFArrayInsertValueAtIndex(array, i, dict); | |
6111 | } | |
6112 | else LogMsg("UpdateSPSStatus: %s array already contains %##s", info->ifname, answer->rdata->u.name.c); | |
6113 | } | |
6114 | else | |
6115 | { | |
6116 | CFIndex i = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict); | |
6117 | if (i != -1) CFArrayRemoveValueAtIndex(array, i); | |
6118 | else LogMsg("UpdateSPSStatus: %s array does not contain %##s", info->ifname, answer->rdata->u.name.c); | |
6119 | } | |
6120 | ||
6121 | CFRelease(dict); | |
6122 | } | |
6123 | ||
6124 | if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, info->ifname, array); | |
6125 | ||
6126 | CFRelease(ifname); | |
6127 | } | |
6128 | ||
6129 | mDNSlocal mDNSs32 GetSystemSleepTimerSetting(void) | |
6130 | { | |
6131 | mDNSs32 val = -1; | |
6132 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetSystemSleepTimerSetting"), NULL, NULL); | |
6133 | if (!store) | |
6134 | LogMsg("GetSystemSleepTimerSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); | |
6135 | else | |
6136 | { | |
263eeeab | 6137 | CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings); |
32bb7e43 A |
6138 | if (dict) |
6139 | { | |
6140 | CFNumberRef number = CFDictionaryGetValue(dict, CFSTR("System Sleep Timer")); | |
6141 | if (number) CFNumberGetValue(number, kCFNumberSInt32Type, &val); | |
6142 | CFRelease(dict); | |
6143 | } | |
6144 | CFRelease(store); | |
6145 | } | |
6146 | return val; | |
6147 | } | |
6148 | ||
6149 | mDNSlocal void SetSPS(mDNS *const m) | |
6150 | { | |
6151 | SCPreferencesSynchronize(m->p->SCPrefs); | |
6152 | CFDictionaryRef dict = SCPreferencesGetValue(m->p->SCPrefs, CFSTR("NAT")); | |
6153 | mDNSBool natenabled = (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID()) && DictionaryIsEnabled(dict)); | |
294beb6e A |
6154 | mDNSu8 sps = natenabled ? mDNSSleepProxyMetric_PrimarySoftware : |
6155 | (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0; | |
32bb7e43 A |
6156 | |
6157 | // For devices that are not running NAT, but are set to never sleep, we may choose to act | |
6158 | // as a Sleep Proxy, but only for non-portable Macs (Portability > 35 means nominal weight < 3kg) | |
294beb6e | 6159 | //if (sps > mDNSSleepProxyMetric_PrimarySoftware && SPMetricPortability > 35) sps = 0; |
32bb7e43 A |
6160 | |
6161 | // If we decide to let laptops act as Sleep Proxy, we should do it only when running on AC power, not on battery | |
6162 | ||
294beb6e | 6163 | // For devices that are unable to sleep at all to save power, or save 1W or less by sleeping, |
32bb7e43 A |
6164 | // it makes sense for them to offer low-priority Sleep Proxy service on the network. |
6165 | // We rate such a device as metric 70 ("Incidentally Available Hardware") | |
294beb6e A |
6166 | if (SPMetricMarginalPower <= 60 && !sps) sps = mDNSSleepProxyMetric_IncidentalHardware; |
6167 | ||
6168 | // If the launchd plist specifies an explicit value for the Intent Metric, then use that instead of the | |
6169 | // computed value (currently 40 "Primary Network Infrastructure Software" or 80 "Incidentally Available Software") | |
6170 | if (sps && OfferSleepProxyService && OfferSleepProxyService < 100) sps = OfferSleepProxyService; | |
32bb7e43 A |
6171 | |
6172 | mDNSCoreBeSleepProxyServer(m, sps, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower); | |
6173 | } | |
6174 | ||
6175 | mDNSlocal void InternetSharingChanged(SCPreferencesRef prefs, SCPreferencesNotification notificationType, void *context) | |
6176 | { | |
6177 | (void)prefs; // Parameter not used | |
6178 | (void)notificationType; // Parameter not used | |
6179 | mDNS *const m = (mDNS *const)context; | |
6180 | KQueueLock(m); | |
6181 | mDNS_Lock(m); | |
6182 | ||
6183 | // Tell platform layer to open or close its BPF fds | |
6184 | if (!m->p->NetworkChanged || | |
6185 | m->p->NetworkChanged - NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2) < 0) | |
6186 | { | |
6187 | m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); | |
6188 | LogInfo("InternetSharingChanged: Set NetworkChanged to %d (%d)", m->p->NetworkChanged - m->timenow, m->p->NetworkChanged); | |
6189 | } | |
6190 | ||
6191 | mDNS_Unlock(m); | |
6192 | KQueueUnlock(m, "InternetSharingChanged"); | |
6193 | } | |
6194 | ||
6195 | mDNSlocal mStatus WatchForInternetSharingChanges(mDNS *const m) | |
6196 | { | |
6197 | SCPreferencesRef SCPrefs = SCPreferencesCreate(NULL, CFSTR("mDNSResponder:WatchForInternetSharingChanges"), CFSTR("com.apple.nat.plist")); | |
6198 | if (!SCPrefs) { LogMsg("SCPreferencesCreate failed: %s", SCErrorString(SCError())); return(mStatus_NoMemoryErr); } | |
6199 | ||
6200 | SCPreferencesContext context = { 0, m, NULL, NULL, NULL }; | |
6201 | if (!SCPreferencesSetCallback(SCPrefs, InternetSharingChanged, &context)) | |
6202 | { LogMsg("SCPreferencesSetCallback failed: %s", SCErrorString(SCError())); CFRelease(SCPrefs); return(mStatus_NoMemoryErr); } | |
6203 | ||
294beb6e | 6204 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
6205 | if (!SCPreferencesSetDispatchQueue( SCPrefs, dispatch_get_main_queue())) |
6206 | { LogMsg("SCPreferencesSetDispatchQueue failed: %s", SCErrorString(SCError())); return(mStatus_NoMemoryErr); } | |
6207 | #else | |
32bb7e43 A |
6208 | if (!SCPreferencesScheduleWithRunLoop(SCPrefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) |
6209 | { LogMsg("SCPreferencesScheduleWithRunLoop failed: %s", SCErrorString(SCError())); CFRelease(SCPrefs); return(mStatus_NoMemoryErr); } | |
263eeeab | 6210 | #endif |
32bb7e43 A |
6211 | |
6212 | m->p->SCPrefs = SCPrefs; | |
6213 | return(mStatus_NoError); | |
6214 | } | |
6215 | ||
1a175162 A |
6216 | // The definitions below should eventually come from some externally-supplied header file. |
6217 | // However, since these definitions can't really be changed without breaking binary compatibility, | |
6218 | // they should never change, so in practice it should not be a big problem to have them defined here. | |
6219 | ||
6220 | #define mDNS_IOREG_KEY "mDNS_KEY" | |
6221 | #define mDNS_IOREG_VALUE "2009-07-30" | |
6222 | #define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS' | |
6223 | ||
6224 | enum | |
6225 | { // commands from the daemon to the driver | |
6226 | cmd_mDNSOffloadRR = 21, // give the mdns update buffer to the driver | |
6227 | }; | |
6228 | ||
6229 | typedef union { void *ptr; mDNSOpaque64 sixtyfourbits; } FatPtr; | |
6230 | ||
6231 | typedef struct | |
6232 | { // cmd_mDNSOffloadRR structure | |
6233 | uint32_t command; // set to OffloadRR | |
6234 | uint32_t rrBufferSize; // number of bytes of RR records | |
6235 | uint32_t numUDPPorts; // number of SRV UDP ports | |
6236 | uint32_t numTCPPorts; // number of SRV TCP ports | |
6237 | uint32_t numRRRecords; // number of RR records | |
6238 | uint32_t compression; // rrRecords - compression is base for compressed strings | |
6239 | FatPtr rrRecords; // address of array of pointers to the rr records | |
6240 | FatPtr udpPorts; // address of udp port list (SRV) | |
6241 | FatPtr tcpPorts; // address of tcp port list (SRV) | |
6242 | } mDNSOffloadCmd; | |
6243 | ||
6244 | #include <IOKit/IOKitLib.h> | |
6245 | #include <dns_util.h> | |
6246 | ||
6247 | mDNSlocal mDNSu16 GetPortArray(mDNS *const m, int trans, mDNSIPPort *portarray) | |
6248 | { | |
6249 | const domainlabel *const tp = (trans == mDNSTransport_UDP) ? (const domainlabel *)"\x4_udp" : (const domainlabel *)"\x4_tcp"; | |
6250 | int count = 0; | |
6251 | AuthRecord *rr; | |
6252 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
6253 | if (rr->resrec.rrtype == kDNSType_SRV && SameDomainLabel(ThirdLabel(rr->resrec.name)->c, tp->c)) | |
6254 | { | |
6255 | if (portarray) portarray[count] = rr->resrec.rdata->u.srv.port; | |
6256 | count++; | |
6257 | } | |
6258 | ||
6259 | // If Back to My Mac is on, also wake for packets to the IPSEC UDP port (4500) | |
263eeeab | 6260 | if (trans == mDNSTransport_UDP && TunnelServers(m)) |
1a175162 A |
6261 | { |
6262 | LogSPS("GetPortArray Back to My Mac at %d", count); | |
6263 | if (portarray) portarray[count] = IPSECPort; | |
6264 | count++; | |
6265 | } | |
6266 | return(count); | |
6267 | } | |
6268 | ||
6269 | #define TfrRecordToNIC(RR) \ | |
294beb6e | 6270 | ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name)))) |
1a175162 | 6271 | |
7dc70602 | 6272 | mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes) |
1a175162 A |
6273 | { |
6274 | *numbytes = 0; | |
6275 | int count = 0; | |
6276 | AuthRecord *rr; | |
6277 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
6278 | if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) | |
6279 | if (TfrRecordToNIC(rr)) | |
6280 | { | |
6281 | *numbytes += DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate; | |
7dc70602 A |
6282 | LogSPS("CountProxyRecords: %3d size %5d total %5d %s", |
6283 | count, DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate, *numbytes, ARDisplayString(m,rr)); | |
1a175162 A |
6284 | count++; |
6285 | } | |
6286 | return(count); | |
6287 | } | |
6288 | ||
7dc70602 | 6289 | mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *const numbytes, FatPtr *const records) |
1a175162 A |
6290 | { |
6291 | mDNSu8 *p = msg->data; | |
7dc70602 | 6292 | const mDNSu8 *const limit = p + *numbytes; |
1a175162 A |
6293 | InitializeDNSMessage(&msg->h, zeroID, zeroID); |
6294 | ||
6295 | int count = 0; | |
6296 | AuthRecord *rr; | |
6297 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
6298 | if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) | |
6299 | if (TfrRecordToNIC(rr)) | |
6300 | { | |
6301 | records[count].sixtyfourbits = zeroOpaque64; | |
6302 | records[count].ptr = p; | |
6303 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) | |
6304 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the 'unique' bit so PutResourceRecord will set it | |
6305 | p = PutResourceRecordTTLWithLimit(msg, p, &msg->h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); | |
6306 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear 'unique' bit back to normal state | |
7dc70602 A |
6307 | LogSPS("GetProxyRecords: %3d start %p end %p size %5d total %5d %s", |
6308 | count, records[count].ptr, p, p - (mDNSu8 *)records[count].ptr, p - msg->data, ARDisplayString(m,rr)); | |
1a175162 A |
6309 | count++; |
6310 | } | |
7dc70602 | 6311 | *numbytes = p - msg->data; |
1a175162 A |
6312 | } |
6313 | ||
6314 | // If compiling with old headers and libraries (pre 10.5) that don't include IOConnectCallStructMethod | |
6315 | // then we declare a dummy version here so that the code at least compiles | |
6316 | #ifndef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER | |
6317 | static kern_return_t | |
6318 | IOConnectCallStructMethod( | |
6319 | mach_port_t connection, // In | |
7dc70602 | 6320 | uint32_t selector, // In |
1a175162 A |
6321 | const void *inputStruct, // In |
6322 | size_t inputStructCnt, // In | |
6323 | void *outputStruct, // Out | |
6324 | size_t *outputStructCnt) // In/Out | |
6325 | { | |
6326 | (void)connection; | |
6327 | (void)selector; | |
6328 | (void)inputStruct; | |
6329 | (void)inputStructCnt; | |
6330 | (void)outputStruct; | |
6331 | (void)outputStructCnt; | |
6332 | LogMsg("Compiled without IOConnectCallStructMethod"); | |
6333 | return(KERN_FAILURE); | |
6334 | } | |
6335 | #endif | |
6336 | ||
263eeeab | 6337 | mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname) // Called with the lock held |
1a175162 A |
6338 | { |
6339 | mStatus result = mStatus_UnknownErr; | |
6340 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, ifname)); | |
6341 | if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", ifname); return(mStatus_UnknownErr); } | |
6342 | ||
6343 | io_name_t n1, n2; | |
6344 | IOObjectGetClass(service, n1); | |
6345 | io_object_t parent; | |
6346 | kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); | |
6347 | if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", ifname, n1, kr); | |
6348 | else | |
6349 | { | |
6350 | IOObjectGetClass(parent, n2); | |
6351 | LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", ifname, n1, n2); | |
6352 | const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, CFSTR(mDNS_IOREG_KEY), kCFAllocatorDefault, mDNSNULL); | |
6353 | if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", ifname, n1, n2); | |
6354 | else | |
6355 | { | |
6356 | if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE))) | |
6357 | LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s", | |
6358 | ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE); | |
294beb6e A |
6359 | else if (!UseInternalSleepProxy) |
6360 | LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", ifname); | |
1a175162 A |
6361 | else |
6362 | { | |
6363 | io_connect_t conObj; | |
6364 | kr = IOServiceOpen(parent, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE, &conObj); | |
6365 | if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", ifname, n1, n2, kr); | |
6366 | else | |
6367 | { | |
6368 | mDNSOffloadCmd cmd; | |
6369 | mDNSPlatformMemZero(&cmd, sizeof(cmd)); // When compiling 32-bit, make sure top 32 bits of 64-bit pointers get initialized to zero | |
6370 | cmd.command = cmd_mDNSOffloadRR; | |
6371 | cmd.numUDPPorts = GetPortArray(m, mDNSTransport_UDP, mDNSNULL); | |
6372 | cmd.numTCPPorts = GetPortArray(m, mDNSTransport_TCP, mDNSNULL); | |
6373 | cmd.numRRRecords = CountProxyRecords(m, &cmd.rrBufferSize); | |
6374 | cmd.compression = sizeof(DNSMessageHeader); | |
6375 | ||
6376 | DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize); | |
6377 | cmd.rrRecords.ptr = mallocL("mDNSOffloadCmd rrRecords", cmd.numRRRecords * sizeof(FatPtr)); | |
6378 | cmd.udpPorts .ptr = mallocL("mDNSOffloadCmd udpPorts", cmd.numUDPPorts * sizeof(mDNSIPPort)); | |
6379 | cmd.tcpPorts .ptr = mallocL("mDNSOffloadCmd tcpPorts", cmd.numTCPPorts * sizeof(mDNSIPPort)); | |
6380 | ||
6381 | LogSPS("ActivateLocalProxy: msg %p %d RR %p %d, UDP %p %d, TCP %p %d", | |
6382 | msg, cmd.rrBufferSize, | |
6383 | cmd.rrRecords.ptr, cmd.numRRRecords, | |
6384 | cmd.udpPorts .ptr, cmd.numUDPPorts, | |
6385 | cmd.tcpPorts .ptr, cmd.numTCPPorts); | |
6386 | ||
6387 | if (!msg || !cmd.rrRecords.ptr || !cmd.udpPorts.ptr || !cmd.tcpPorts.ptr) | |
6388 | LogMsg("ActivateLocalProxy: Failed to allocate memory: msg %p %d RR %p %d, UDP %p %d, TCP %p %d", | |
6389 | msg, cmd.rrBufferSize, | |
6390 | cmd.rrRecords.ptr, cmd.numRRRecords, | |
6391 | cmd.udpPorts .ptr, cmd.numUDPPorts, | |
6392 | cmd.tcpPorts .ptr, cmd.numTCPPorts); | |
6393 | else | |
6394 | { | |
7dc70602 | 6395 | GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr); |
1a175162 A |
6396 | GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr); |
6397 | GetPortArray(m, mDNSTransport_TCP, cmd.tcpPorts.ptr); | |
6398 | char outputData[2]; | |
6399 | size_t outputDataSize = sizeof(outputData); | |
6400 | kr = IOConnectCallStructMethod(conObj, 0, &cmd, sizeof(cmd), outputData, &outputDataSize); | |
6401 | LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", ifname, n1, n2, kr); | |
6402 | if (kr == KERN_SUCCESS) result = mStatus_NoError; | |
6403 | } | |
6404 | ||
6405 | if (cmd.tcpPorts. ptr) freeL("mDNSOffloadCmd udpPorts", cmd.tcpPorts .ptr); | |
6406 | if (cmd.udpPorts. ptr) freeL("mDNSOffloadCmd tcpPorts", cmd.udpPorts .ptr); | |
6407 | if (cmd.rrRecords.ptr) freeL("mDNSOffloadCmd rrRecords", cmd.rrRecords.ptr); | |
6408 | if (msg) freeL("mDNSOffloadCmd msg", msg); | |
6409 | IOServiceClose(conObj); | |
6410 | } | |
6411 | } | |
6412 | CFRelease(ref); | |
6413 | } | |
6414 | IOObjectRelease(parent); | |
6415 | } | |
6416 | IOObjectRelease(service); | |
6417 | return result; | |
6418 | } | |
6419 | ||
32bb7e43 A |
6420 | #endif // APPLE_OSX_mDNSResponder |
6421 | ||
6422 | static io_service_t g_rootdomain = MACH_PORT_NULL; | |
6423 | ||
6424 | mDNSlocal mDNSBool SystemWakeForNetworkAccess(void) | |
6425 | { | |
6426 | mDNSs32 val = 0; | |
6427 | CFBooleanRef clamshellStop = NULL; | |
6428 | mDNSBool retnow = mDNSfalse; | |
6429 | ||
263eeeab A |
6430 | if (DisableSleepProxyClient) { LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); return mDNSfalse; } |
6431 | ||
32bb7e43 A |
6432 | GetCurrentPMSetting(CFSTR("Wake On LAN"), &val); |
6433 | LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", val); | |
6434 | if (!val) return mDNSfalse; | |
6435 | ||
6436 | if (!g_rootdomain) g_rootdomain = IORegistryEntryFromPath(MACH_PORT_NULL, kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain"); | |
6437 | if (!g_rootdomain) { LogMsg("SystemWakeForNetworkAccess: IORegistryEntryFromPath failed; assuming no clamshell so can WOMP"); return mDNStrue; } | |
6438 | ||
6439 | clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellStateKey), kCFAllocatorDefault, 0); | |
6440 | if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; } | |
6441 | retnow = clamshellStop == kCFBooleanFalse; | |
6442 | CFRelease(clamshellStop); | |
6443 | if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey is false; clamshell is open so can WOMP"); return mDNStrue; } | |
6444 | ||
6445 | clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellCausesSleepKey), kCFAllocatorDefault, 0); | |
6446 | if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; } | |
263eeeab A |
6447 | retnow = (clamshellStop == kCFBooleanFalse); |
6448 | CFRelease(clamshellStop); | |
32bb7e43 A |
6449 | if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey is false; clamshell is closed but can WOMP"); return mDNStrue; } |
6450 | ||
6451 | LogSPS("SystemWakeForNetworkAccess: clamshell is closed and can't WOMP"); | |
6452 | return mDNSfalse; | |
6453 | } | |
6454 | ||
263eeeab A |
6455 | mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void) |
6456 | { | |
6457 | mDNSs32 val = 0; | |
294beb6e | 6458 | GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val); |
263eeeab A |
6459 | return val != 0 ? mDNStrue : mDNSfalse; |
6460 | } | |
6461 | ||
6462 | #if APPLE_OSX_mDNSResponder | |
6463 | // If the _autotunnel6 record is still there in the list, we are waiting for the ack from | |
6464 | // the server. | |
6465 | // | |
6466 | // If we are behind a double-NAT or NAT with no NAT-PMP support, we should make sure that all our | |
6467 | // BTMM records are deregistered so that it does not appear on the Finder sidebar of our peers | |
6468 | // when we go to sleep. First _autotunnel6 and the host record gets deregistered, then SRV | |
6469 | // (UpdateAllSrvRecords) and then PTR and TXT | |
6470 | // | |
6471 | // Note: We wait up to a maximum of 10 seconds before we ack the sleep. So, returning "false" | |
6472 | // here does not necessarily mean that it will be honored. | |
6473 | mDNSexport mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr) | |
6474 | { | |
6475 | if (!AuthRecord_uDNS(rr)) return mDNStrue; | |
6476 | ||
4a95efb2 | 6477 | if (SameDomainLabel(rr->namestorage.c, (const mDNSu8 *)"\x0c_autotunnel6")) |
263eeeab A |
6478 | { |
6479 | LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr)); | |
6480 | return mDNSfalse; | |
6481 | } | |
6482 | // Just check for the SRV record alone as the PTR and TXT records are dependent on SRV | |
6483 | // and will get deregistered together in a single update. We also don't check for TXT | |
6484 | // records as _kerberos TXT record is always there even when there are no services | |
6485 | // and we don't want to delay the sleep in that case. | |
6486 | if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result) | |
6487 | { | |
6488 | if ((rr->resrec.rrtype == kDNSType_SRV) && rr->state != regState_NoTarget && rr->zone) | |
6489 | { | |
6490 | DomainAuthInfo *info = GetAuthInfoForName_internal(m, rr->zone); | |
6491 | if (info && info->AutoTunnel) | |
6492 | { | |
6493 | LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr)); | |
6494 | return mDNSfalse; | |
6495 | } | |
6496 | } | |
6497 | } | |
6498 | return mDNStrue; | |
6499 | } | |
6500 | ||
6501 | // Note: BTMMDict needs to be retained by the caller if needed | |
6502 | mDNSlocal CFDictionaryRef ParseBackToMyMacKey(CFDictionaryRef connd) | |
6503 | { | |
6504 | CFDictionaryRef BTMMDict = CFDictionaryGetValue(connd, CFSTR("BackToMyMac")); | |
6505 | if (!BTMMDict) | |
6506 | { | |
6507 | LogInfo("ParseBackToMyMacKey: CFDictionaryGetValue No value for BackToMyMac"); | |
6508 | return NULL; | |
6509 | } | |
6510 | ||
6511 | // Non-dictionary is treated as non-existent dictionary | |
6512 | if (CFGetTypeID(BTMMDict) != CFDictionaryGetTypeID()) | |
6513 | {LogMsg("ERROR: ParseBackToMyMacKey: CFDictionaryGetValue BackToMyMac not a dictionary"); CFRelease(BTMMDict); return NULL;} | |
6514 | ||
6515 | return BTMMDict; | |
6516 | } | |
6517 | ||
6518 | mDNSlocal void ParseBTMMInterfaceKey(CFDictionaryRef BTMMDict, char *buf, int buflen) | |
6519 | { | |
6520 | CFTypeRef string; | |
6521 | mDNSBool ifExists; | |
6522 | ||
6523 | ifExists = CFDictionaryGetValueIfPresent(BTMMDict, CFSTR("Interface"), &string); | |
6524 | if (ifExists) | |
6525 | { | |
6526 | if (!CFStringGetCString(string, buf, buflen, kCFStringEncodingUTF8)) | |
6527 | { | |
6528 | LogMsg("ERROR: ParseBTMMInterfaceKey: Could not convert Interface to CString"); | |
6529 | if (buflen) buf[0] = 0; | |
6530 | return; | |
6531 | } | |
6532 | else | |
6533 | debugf("ParseBTMMInterfaceKey: Interface Key exists %s", buf); | |
6534 | } | |
6535 | else | |
6536 | { | |
6537 | if (buflen) buf[0] = 0; | |
6538 | debugf("ParseBTMMInterfaceKey: Interface Key does not exist"); | |
6539 | } | |
6540 | } | |
6541 | ||
6542 | mDNSexport void RemoveAutoTunnel6Record(mDNS *const m) | |
6543 | { | |
6544 | DomainAuthInfo *info; | |
6545 | char buf[IFNAMSIZ]; | |
6546 | ||
6547 | // Did we parse a non-empty dictionary before ? | |
6548 | if (!m->p->ConndBTMMDict || (CFDictionaryGetCount(m->p->ConndBTMMDict) == 0)) | |
6549 | { | |
6550 | LogInfo("RemoveAutoTunnel6Record: Never registered any records before, not deregistering %p", m->p->ConndBTMMDict); | |
6551 | return; | |
6552 | } | |
6553 | ||
6554 | // Did we have a non-NULL Interface name before ? | |
6555 | ParseBTMMInterfaceKey(m->p->ConndBTMMDict, buf, sizeof(buf)); | |
6556 | if (!strlen(buf)) | |
6557 | { | |
6558 | LogInfo("RemoveAutoTunnel6Record: Interface name already NULL, not deregistering"); | |
6559 | return; | |
6560 | } | |
6561 | ||
6562 | // Set the address to zero before calling DeregisterAutoTunnel6Record. If we call | |
6563 | // Deregister too quickly before the previous Register completed (just scheduled | |
6564 | // to be sent out) and when DeregisterAutoTunnel6Record calls mDNS_Register_internal, | |
3c427d54 | 6565 | // it invokes the AutoTunnelRecordCallback immediately and AutoTunnelRelayAddrIn should |
263eeeab | 6566 | // be zero so that we don't register again. |
3c427d54 | 6567 | m->AutoTunnelRelayAddrIn = zerov6Addr; |
263eeeab A |
6568 | if (!m->AuthInfoList) LogInfo("RemoveAutoTunnel6Record: No Domain AuthInfo"); |
6569 | for (info = m->AuthInfoList; info; info = info->next) | |
6570 | { | |
6571 | if (!info->AutoTunnel) { LogInfo("RemoveAutoTunnel6Record: Domain %##s not an AutoTunnel", info->domain.c); continue;} | |
6572 | ||
6573 | if (info->deltime) {LogInfo("RemoveAutoTunnel6Record: Domain %##s about to be deleted", info->domain.c); continue;} | |
6574 | ||
6575 | LogInfo("RemoveAutoTunnel6Record: Deregistering records for domain %##s", info->domain.c); | |
6576 | DeregisterAutoTunnel6Record(m, info); | |
6577 | } | |
6578 | CFRelease(m->p->ConndBTMMDict); | |
6579 | m->p->ConndBTMMDict = NULL; | |
6580 | } | |
6581 | ||
6582 | // Returns zero on success | |
6583 | mDNSlocal int GetIPv6AddressForIfname(char *ifname, mDNSv6Addr *ipv6Addr) | |
6584 | { | |
6585 | struct ifaddrs *ifa; | |
6586 | struct ifaddrs *ifaddrs; | |
6587 | mDNSAddr addr; | |
6588 | ||
6589 | if (if_nametoindex(ifname) == 0) {LogInfo("GetIPv6AddressForIfname: Invalid name %s", ifname); return (-1);} | |
6590 | ||
6591 | if (getifaddrs(&ifaddrs) < 0) {LogInfo("GetIPv6AddressForIfname: getifaddrs failed"); return (-1);} | |
6592 | ||
6593 | /* | |
6594 | * Find the ifaddr entry corresponding to the interface name, | |
6595 | * and return the first matching non-linklocal IPv6 address. | |
6596 | */ | |
6597 | for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) | |
6598 | { | |
6599 | if (strncmp(ifa->ifa_name, ifname, IFNAMSIZ) != 0) | |
6600 | continue; | |
6601 | if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) | |
6602 | { | |
6603 | struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)ifa->ifa_addr; | |
6604 | if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) | |
6605 | continue; | |
6606 | if (SetupAddr(&addr, ifa->ifa_addr) != mStatus_NoError) | |
6607 | { | |
6608 | LogInfo("GetIPv6AddressForIfname: SetupAddr error, continuing to the next address"); | |
6609 | continue; | |
6610 | } | |
6611 | else | |
6612 | { | |
6613 | *ipv6Addr = *(mDNSv6Addr *)&addr.ip.v6; | |
6614 | LogInfo("GetIPv6AddressForIfname: Returning IPv6 address %.16a", ipv6Addr); | |
6615 | freeifaddrs(ifaddrs); | |
6616 | return 0; | |
6617 | } | |
6618 | } | |
6619 | } | |
6620 | LogInfo("GetIPv6AddressForIfname: No Valid IPv6 address"); | |
6621 | freeifaddrs(ifaddrs); | |
6622 | return (-1); | |
6623 | } | |
6624 | ||
6625 | mDNSlocal void AddAutoTunnel6Record(mDNS *const m, char *ifname, CFDictionaryRef BTMMDict) | |
6626 | { | |
6627 | mDNSv6Addr v6addr; | |
6628 | DomainAuthInfo *info; | |
6629 | ||
6630 | if (GetIPv6AddressForIfname(ifname, &v6addr) != 0) | |
6631 | { | |
6632 | LogInfo("AddAutoTunnel6Record: No Valid IPv6 addresses found for %s", ifname); | |
6633 | // If the interface does not exist but the dictionary has the value, we treat | |
6634 | // this case as though the dictionary does not have the value | |
6635 | RemoveAutoTunnel6Record(m); | |
6636 | // If awacsd crashes or exits for some reason, restart the relay connection | |
6637 | UpdateBTMMRelayConnection(m); | |
6638 | return; | |
6639 | } | |
6640 | ||
3c427d54 A |
6641 | m->AutoTunnelRelayAddrOut = v6addr; |
6642 | ||
6643 | // if disabled administratively, don't bother to register. RegisterAutoTunnel6Record makes these same | |
6644 | // checks, but we do it here not just as an optimization but mainly to keep AutoTunnelRelayAddrIn zero | |
6645 | // as a non-zero AutoTunnelRelayAddrIn indicates that we have registered _autotunnel6 record and hence | |
6646 | // other hosts can connect to this host through the relay | |
6647 | if (!m->RegisterAutoTunnel6 || DisableInboundRelayConnection) | |
6648 | { | |
6649 | LogInfo("RegisterAutoTunnel6Record: registration Disabled RegisterAutoTunnel6 %d, DisableInbound %d", | |
6650 | m->RegisterAutoTunnel6, DisableInboundRelayConnection); | |
6651 | return; | |
6652 | } | |
6653 | m->AutoTunnelRelayAddrIn = v6addr; | |
263eeeab A |
6654 | |
6655 | if (!m->AuthInfoList) LogInfo("AddAutoTunnel6Record: No Domain AuthInfo"); | |
6656 | for (info = m->AuthInfoList; info; info = info->next) | |
6657 | { | |
6658 | // clientContext for a domain tells us that we are listening for at least one Service/Record | |
6659 | // in a domain and SetLocalAutoTunnelInterface_internal was called | |
6660 | if (!info->AutoTunnel) { LogInfo("AddAutoTunnel6Record: Domain %##s not an AutoTunnel", info->domain.c); continue;} | |
6661 | ||
6662 | if (!info->AutoTunnelNAT.clientContext) {LogInfo("AddAutoTunnel6Record: Domain %##s has no services", info->domain.c); continue;} | |
6663 | ||
6664 | if (info->deltime) {LogInfo("AddAutoTunnel6Record: Domain %##s about to be deleted", info->domain.c); continue;} | |
6665 | ||
6666 | LogInfo("AddAutoTunnel6Record: Registering records for domain %##s", info->domain.c); | |
6667 | mDNS_Lock(m); | |
6668 | RegisterAutoTunnel6Record(m, info); | |
6669 | mDNS_Unlock(m); | |
6670 | } | |
6671 | if (m->p->ConndBTMMDict) CFRelease(m->p->ConndBTMMDict); | |
6672 | m->p->ConndBTMMDict = CFRetain(BTMMDict); | |
6673 | } | |
6674 | ||
6675 | mDNSlocal void ParseBackToMyMac(mDNS *const m, CFDictionaryRef connd) | |
6676 | { | |
6677 | CFDictionaryRef BTMMDict; | |
6678 | char buf[IFNAMSIZ]; | |
6679 | ||
6680 | BTMMDict = ParseBackToMyMacKey(connd); | |
6681 | if (!BTMMDict) | |
6682 | { | |
6683 | LogInfo("ParseBackToMyMac: CFDictionaryGetValue No value for BackToMyMac, Removing autotunnel6"); | |
6684 | RemoveAutoTunnel6Record(m); | |
3c427d54 A |
6685 | // Note: AutoTunnelRelayAddrIn is zeroed out in RemoveAutoTunnel6Record as it is called |
6686 | // from other places. | |
6687 | m->AutoTunnelRelayAddrOut = zerov6Addr; | |
294beb6e A |
6688 | mDNS_Lock(m); |
6689 | UpdateAutoTunnelDomainStatuses(m); | |
6690 | mDNS_Unlock(m); | |
263eeeab A |
6691 | return; |
6692 | } | |
6693 | ||
6694 | ParseBTMMInterfaceKey(BTMMDict, buf, sizeof(buf)); | |
6695 | if (!strlen(buf)) | |
6696 | { | |
6697 | LogInfo("ParseBackToMyMac: NULL value for Interface, Removing autotunnel6"); | |
6698 | RemoveAutoTunnel6Record(m); | |
3c427d54 | 6699 | m->AutoTunnelRelayAddrOut = zerov6Addr; |
263eeeab A |
6700 | // We don't have a utun interface, start the relay connection if possible |
6701 | UpdateBTMMRelayConnection(m); | |
6702 | } | |
6703 | else | |
6704 | { | |
6705 | LogInfo("ParseBackToMyMac: non-NULL value for Interface, Adding autotunnel6"); | |
6706 | AddAutoTunnel6Record(m, buf, BTMMDict); | |
6707 | } | |
294beb6e A |
6708 | |
6709 | mDNS_Lock(m); | |
6710 | UpdateAutoTunnelDomainStatuses(m); | |
6711 | mDNS_Unlock(m); | |
263eeeab A |
6712 | } |
6713 | ||
294beb6e | 6714 | mDNSlocal void SetupConndConfigChanges(mDNS *const m) |
263eeeab A |
6715 | { |
6716 | CFDictionaryRef connd; | |
6717 | SCDynamicStoreRef store; | |
6718 | ||
6719 | store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:SetupConndConfigChanges"), NULL, NULL); | |
6720 | if (!store) {LogMsg("SetupConndConfigChanges: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); return;} | |
6721 | ||
6722 | connd = SCDynamicStoreCopyValue(store, NetworkChangedKey_BTMMConnectivity); | |
6723 | if (!connd) | |
6724 | {LogInfo("SetupConndConfigChanges: SCDynamicStoreCopyValue failed: %s", SCErrorString(SCError())); CFRelease(store); return;} | |
6725 | else | |
6726 | { | |
6727 | ParseBackToMyMac(m, connd); | |
6728 | } | |
6729 | CFRelease(connd); | |
6730 | CFRelease(store); | |
6731 | } | |
6732 | #endif /* APPLE_OSX_mDNSResponder */ | |
6733 | ||
6734 | ||
7f0064bd | 6735 | mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) |
67c8f8a1 | 6736 | { |
32bb7e43 | 6737 | LogInfo("*** Network Configuration Change *** (%d)%s", |
67c8f8a1 | 6738 | m->p->NetworkChanged ? mDNS_TimeNow(m) - m->p->NetworkChanged : 0, |
96f69b28 | 6739 | m->p->NetworkChanged ? "" : " (no scheduled configuration change)"); |
7cb34e5c | 6740 | m->p->NetworkChanged = 0; // If we received a network change event and deferred processing, we're now dealing with it |
283ee3ff | 6741 | mDNSs32 utc = mDNSPlatformUTC(); |
32bb7e43 | 6742 | m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); |
263eeeab | 6743 | m->SystemSleepOnlyIfWakeOnLAN = SystemSleepOnlyIfWakeOnLAN(); |
283ee3ff A |
6744 | MarkAllInterfacesInactive(m, utc); |
6745 | UpdateInterfaceList(m, utc); | |
96f69b28 A |
6746 | ClearInactiveInterfaces(m, utc); |
6747 | SetupActiveInterfaces(m, utc); | |
67c8f8a1 | 6748 | |
32bb7e43 A |
6749 | #if APPLE_OSX_mDNSResponder |
6750 | ||
263eeeab A |
6751 | SetupConndConfigChanges(m); |
6752 | ||
32bb7e43 | 6753 | if (m->AutoTunnelHostAddr.b[0]) |
67c8f8a1 | 6754 | { |
32bb7e43 A |
6755 | mDNS_Lock(m); |
6756 | if (TunnelClients(m) || TunnelServers(m)) | |
263eeeab | 6757 | SetupLocalAutoTunnelInterface_internal(m, mDNSfalse); |
32bb7e43 A |
6758 | mDNS_Unlock(m); |
6759 | } | |
6760 | ||
6761 | // Scan to find client tunnels whose questions have completed, | |
6762 | // but whose local inner/outer addresses have changed since the tunnel was set up | |
6763 | ClientTunnel *p; | |
6764 | for (p = m->TunnelClients; p; p = p->next) | |
6765 | if (p->q.ThisQInterval < 0) | |
67c8f8a1 | 6766 | { |
263eeeab | 6767 | if (!mDNSIPPortIsZero(p->rmt_outer_port)) |
67c8f8a1 | 6768 | { |
263eeeab A |
6769 | mDNSAddr tmpSrc = zeroAddr; |
6770 | mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} }; | |
6771 | tmpDst.ip.v4 = p->rmt_outer; | |
6772 | mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst); | |
6773 | if (!mDNSSameIPv6Address(p->loc_inner, m->AutoTunnelHostAddr) || | |
6774 | !mDNSSameIPv4Address(p->loc_outer, tmpSrc.ip.v4)) | |
6775 | { | |
6776 | AutoTunnelSetKeys(p, mDNSfalse); | |
6777 | p->loc_inner = m->AutoTunnelHostAddr; | |
6778 | p->loc_outer = tmpSrc.ip.v4; | |
6779 | AutoTunnelSetKeys(p, mDNStrue); | |
6780 | } | |
67c8f8a1 | 6781 | } |
263eeeab A |
6782 | else |
6783 | { | |
6784 | if (!mDNSSameIPv6Address(p->loc_inner, m->AutoTunnelHostAddr) || | |
3c427d54 | 6785 | !mDNSSameIPv6Address(p->loc_outer6, m->AutoTunnelRelayAddrOut)) |
263eeeab A |
6786 | { |
6787 | AutoTunnelSetKeys(p, mDNSfalse); | |
6788 | p->loc_inner = m->AutoTunnelHostAddr; | |
3c427d54 | 6789 | p->loc_outer6 = m->AutoTunnelRelayAddrOut; |
263eeeab A |
6790 | AutoTunnelSetKeys(p, mDNStrue); |
6791 | } | |
6792 | } | |
32bb7e43 A |
6793 | } |
6794 | ||
263eeeab | 6795 | |
32bb7e43 A |
6796 | SetSPS(m); |
6797 | ||
6798 | NetworkInterfaceInfoOSX *i; | |
6799 | for (i = m->p->InterfaceList; i; i = i->next) | |
6800 | { | |
6801 | if (!m->SPSSocket) // Not being Sleep Proxy Server; close any open BPF fds | |
6802 | { | |
6803 | if (i->BPF_fd >= 0 && CountProxyTargets(m, i, mDNSNULL, mDNSNULL) == 0) CloseBPF(i); | |
6804 | } | |
6805 | else // else, we're Sleep Proxy Server; open BPF fds | |
6806 | { | |
263eeeab | 6807 | if (i->Exists && i->Registered == i && i->ifinfo.McastTxRx && !(i->ifa_flags & IFF_LOOPBACK) && i->BPF_fd == -1) |
32bb7e43 A |
6808 | { LogSPS("%s requesting BPF", i->ifinfo.ifname); i->BPF_fd = -2; mDNSRequestBPF(); } |
6809 | } | |
67c8f8a1 | 6810 | } |
32bb7e43 A |
6811 | |
6812 | #endif // APPLE_OSX_mDNSResponder | |
67c8f8a1 | 6813 | |
96f69b28 | 6814 | uDNS_SetupDNSConfig(m); |
32bb7e43 A |
6815 | mDNS_ConfigChanged(m); |
6816 | } | |
67c8f8a1 | 6817 | |
32bb7e43 A |
6818 | // Called with KQueueLock & mDNS lock |
6819 | mDNSlocal void SetNetworkChanged(mDNS *const m, mDNSs32 delay) | |
6820 | { | |
6821 | if (!m->p->NetworkChanged || m->p->NetworkChanged - NonZeroTime(m->timenow + delay) < 0) | |
6822 | { | |
6823 | m->p->NetworkChanged = NonZeroTime(m->timenow + delay); | |
6824 | LogInfo("SetNetworkChanged: setting network changed to %d (%d)", delay, m->p->NetworkChanged); | |
6825 | } | |
7f0064bd | 6826 | } |
8e92c31c | 6827 | |
294beb6e A |
6828 | // Called with KQueueLock & mDNS lock |
6829 | mDNSlocal void SetKeyChainTimer(mDNS *const m, mDNSs32 delay) | |
6830 | { | |
6831 | // If it's not set or it needs to happen sooner than when it's currently set | |
6832 | if (!m->p->KeyChainTimer || m->p->KeyChainTimer - NonZeroTime(m->timenow + delay) > 0) | |
6833 | { | |
6834 | m->p->KeyChainTimer = NonZeroTime(m->timenow + delay); | |
6835 | LogInfo("SetKeyChainTimer: %d", delay); | |
6836 | } | |
6837 | } | |
32bb7e43 | 6838 | |
263eeeab A |
6839 | // Copy the fourth slash-delimited element from either: |
6840 | // State:/Network/Interface/<bsdname>/IPv4 | |
6841 | // or | |
6842 | // Setup:/Network/Service/<servicename>/Interface | |
6843 | mDNSlocal CFStringRef CopyNameFromKey(CFStringRef key) | |
6844 | { | |
6845 | CFArrayRef a; | |
6846 | CFStringRef name = NULL; | |
6847 | ||
6848 | a = CFStringCreateArrayBySeparatingStrings(NULL, key, CFSTR("/")); | |
6849 | if (a && CFArrayGetCount(a) == 5) name = CFRetain(CFArrayGetValueAtIndex(a, 3)); | |
6850 | if (a != NULL) CFRelease(a); | |
6851 | ||
6852 | return name; | |
6853 | } | |
6854 | ||
6855 | // Whether a key from a network change notification corresponds to | |
6856 | // an IP service that is explicitly configured for IPv4 Link Local | |
6857 | mDNSlocal mDNSBool ChangedKeysHaveIPv4LL(CFArrayRef inkeys) | |
6858 | { | |
6859 | SCDynamicStoreRef store = NULL; | |
6860 | CFDictionaryRef dict = NULL; | |
6861 | CFMutableArrayRef a; | |
6862 | const void **keys = NULL, **vals = NULL; | |
6863 | CFStringRef pattern = NULL; | |
6864 | int i, ic, j, jc; | |
6865 | mDNSBool found = mDNSfalse; | |
6866 | ||
6867 | jc = CFArrayGetCount(inkeys); | |
6868 | if (!jc) goto done; | |
6869 | ||
6870 | store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:ChangedKeysHaveIPv4LL"), NULL, NULL); | |
6871 | if (store == NULL) goto done; | |
6872 | ||
6873 | a = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
6874 | if (a == NULL) goto done; | |
6875 | ||
6876 | // Setup:/Network/Service/[^/]+/Interface | |
6877 | pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetInterface); | |
6878 | if (pattern == NULL) goto done; | |
6879 | CFArrayAppendValue(a, pattern); | |
6880 | CFRelease(pattern); | |
6881 | ||
6882 | // Setup:/Network/Service/[^/]+/IPv4 | |
6883 | pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetIPv4); | |
6884 | if (pattern == NULL) goto done; | |
6885 | CFArrayAppendValue(a, pattern); | |
6886 | CFRelease(pattern); | |
6887 | ||
6888 | dict = SCDynamicStoreCopyMultiple(store, NULL, a); | |
6889 | CFRelease(a); | |
6890 | ||
6891 | if (!dict) | |
6892 | { | |
6893 | LogMsg("ChangedKeysHaveIPv4LL: Empty dictionary"); | |
6894 | goto done; | |
6895 | } | |
6896 | ||
6897 | ic = CFDictionaryGetCount(dict); | |
6898 | vals = mDNSPlatformMemAllocate(sizeof (void *) * ic); | |
6899 | keys = mDNSPlatformMemAllocate(sizeof (void *) * ic); | |
6900 | CFDictionaryGetKeysAndValues(dict, keys, vals); | |
6901 | ||
6902 | for (j = 0; j < jc && !found; j++) | |
6903 | { | |
6904 | CFStringRef key = CFArrayGetValueAtIndex(inkeys, j); | |
6905 | CFStringRef ifname = NULL; | |
6906 | ||
6907 | char buf[256]; | |
6908 | ||
6909 | // It would be nice to use a regex here | |
6910 | if (!CFStringHasPrefix(key, CFSTR("State:/Network/Interface/")) || !CFStringHasSuffix(key, kSCEntNetIPv4)) continue; | |
6911 | ||
6912 | if ((ifname = CopyNameFromKey(key)) == NULL) continue; | |
6913 | if (mDNS_LoggingEnabled) | |
6914 | { | |
6915 | if (!CFStringGetCString(ifname, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; | |
6916 | LogInfo("ChangedKeysHaveIPv4LL: potential ifname %s", buf); | |
6917 | } | |
6918 | ||
6919 | for (i = 0; i < ic; i++) | |
6920 | { | |
6921 | CFDictionaryRef ipv4dict; | |
6922 | CFStringRef name; | |
6923 | CFStringRef serviceid; | |
6924 | CFStringRef configmethod; | |
6925 | ||
6926 | if (!CFStringHasSuffix(keys[i], kSCEntNetInterface)) continue; | |
6927 | ||
6928 | if (CFDictionaryGetTypeID() != CFGetTypeID(vals[i])) continue; | |
6929 | ||
6930 | if ((name = CFDictionaryGetValue(vals[i], kSCPropNetInterfaceDeviceName)) == NULL) continue; | |
6931 | ||
6932 | if (!CFEqual(ifname, name)) continue; | |
6933 | ||
6934 | if ((serviceid = CopyNameFromKey(keys[i])) == NULL) continue; | |
6935 | if (mDNS_LoggingEnabled) | |
6936 | { | |
6937 | if (!CFStringGetCString(serviceid, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; | |
6938 | LogInfo("ChangedKeysHaveIPv4LL: found serviceid %s", buf); | |
6939 | } | |
6940 | ||
6941 | pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceid, kSCEntNetIPv4); | |
6942 | CFRelease(serviceid); | |
6943 | if (pattern == NULL) continue; | |
6944 | ||
6945 | ipv4dict = CFDictionaryGetValue(dict, pattern); | |
6946 | CFRelease(pattern); | |
6947 | if (!ipv4dict || CFDictionaryGetTypeID() != CFGetTypeID(ipv4dict)) continue; | |
6948 | ||
6949 | configmethod = CFDictionaryGetValue(ipv4dict, kSCPropNetIPv4ConfigMethod); | |
6950 | if (!configmethod) continue; | |
6951 | ||
6952 | if (mDNS_LoggingEnabled) | |
6953 | { | |
6954 | if (!CFStringGetCString(configmethod, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; | |
6955 | LogInfo("ChangedKeysHaveIPv4LL: configmethod %s", buf); | |
6956 | } | |
6957 | ||
6958 | if (CFEqual(configmethod, kSCValNetIPv4ConfigMethodLinkLocal)) { found = mDNStrue; break; } | |
6959 | } | |
6960 | ||
6961 | CFRelease(ifname); | |
6962 | } | |
6963 | ||
6964 | done: | |
6965 | if (vals != NULL) mDNSPlatformMemFree(vals); | |
6966 | if (keys != NULL) mDNSPlatformMemFree(keys); | |
6967 | if (dict != NULL) CFRelease(dict); | |
6968 | if (store != NULL) CFRelease(store); | |
6969 | ||
6970 | return found; | |
6971 | } | |
6972 | ||
c9b9ae52 A |
6973 | mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) |
6974 | { | |
8e92c31c | 6975 | (void)store; // Parameter not used |
294beb6e | 6976 | mDNSBool changeNow = mDNSfalse; |
c9b9ae52 | 6977 | mDNS *const m = (mDNS *const)context; |
67c8f8a1 | 6978 | KQueueLock(m); |
7f0064bd | 6979 | mDNS_Lock(m); |
283ee3ff | 6980 | |
32bb7e43 | 6981 | mDNSs32 delay = mDNSPlatformOneSecond * 2; // Start off assuming a two-second delay |
283ee3ff | 6982 | |
67c8f8a1 | 6983 | int c = CFArrayGetCount(changedKeys); // Count changes |
283ee3ff | 6984 | CFRange range = { 0, c }; |
67c8f8a1 A |
6985 | int c1 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Hostnames ) != 0); |
6986 | int c2 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0); | |
6987 | int c3 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS ) != 0); | |
32bb7e43 A |
6988 | int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS ) != 0); |
6989 | if (c && c - c1 - c2 - c3 - c4 == 0) delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay | |
030b743d | 6990 | |
294beb6e A |
6991 | { |
6992 | int i; | |
6993 | for (i=0; i<c; i++) | |
6994 | { | |
6995 | char buf[256]; | |
6996 | if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; | |
6997 | if (buf[0]) | |
6998 | { | |
6999 | if (strstr(buf, "p2p")) | |
7000 | { | |
7001 | LogInfo("NetworkChanged SC key: %s, not delaying network change", buf); | |
7002 | changeNow = mDNStrue; | |
7003 | break; | |
7004 | } | |
7005 | } | |
7006 | } | |
7007 | } | |
7008 | ||
32bb7e43 | 7009 | if (mDNS_LoggingEnabled) |
67c8f8a1 | 7010 | { |
32bb7e43 A |
7011 | int i; |
7012 | for (i=0; i<c; i++) | |
7013 | { | |
7014 | char buf[256]; | |
7015 | if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; | |
7016 | LogInfo("*** NetworkChanged SC key: %s", buf); | |
7017 | } | |
7018 | LogInfo("*** NetworkChanged *** %d change%s %s%s%s%sdelay %d", | |
7019 | c, c>1?"s":"", | |
7020 | c1 ? "(Local Hostname) " : "", | |
7021 | c2 ? "(Computer Name) " : "", | |
7022 | c3 ? "(DynamicDNS) " : "", | |
7023 | c4 ? "(DNS) " : "", | |
7024 | delay); | |
7025 | } | |
030b743d | 7026 | |
294beb6e A |
7027 | mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac); |
7028 | if (btmmChanged) delay = 0; | |
7029 | ||
32bb7e43 A |
7030 | SetNetworkChanged(m, delay); |
7031 | ||
294beb6e A |
7032 | // Other software might pick up these changes to register or browse in WAB or BTMM domains, |
7033 | // so in order for secure updates to be made to the server, make sure to read the keychain and | |
7034 | // setup the DomainAuthInfo before handing the network change. | |
7035 | // If we don't, then we will first try to register services in the clear, then later setup the | |
7036 | // DomainAuthInfo, which is incorrect. | |
7037 | if (c3 || btmmChanged) | |
7038 | SetKeyChainTimer(m, delay); | |
96f69b28 | 7039 | |
7f0064bd | 7040 | mDNS_Unlock(m); |
32bb7e43 A |
7041 | |
7042 | // If DNS settings changed, immediately force a reconfig (esp. cache flush) | |
263eeeab | 7043 | // Similarly, if an interface changed that is explicitly IPv4 link local, immediately force a reconfig |
294beb6e | 7044 | if (c4 || ChangedKeysHaveIPv4LL(changedKeys) || changeNow) mDNSMacOSXNetworkChanged(m); |
32bb7e43 | 7045 | |
67c8f8a1 | 7046 | KQueueUnlock(m, "NetworkChanged"); |
c9b9ae52 A |
7047 | } |
7048 | ||
294beb6e A |
7049 | #if APPLE_OSX_mDNSResponder |
7050 | mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *context) | |
7051 | { | |
7052 | (void)context; | |
7053 | char buf[IFNAMSIZ]; | |
7054 | ||
7055 | CFStringRef ifnameStr = (CFStringRef)key; | |
7056 | CFArrayRef array = (CFArrayRef)value; | |
7057 | if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; | |
7058 | ||
7059 | LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array)); | |
7060 | mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value); | |
7061 | } | |
7062 | #endif | |
7063 | ||
7064 | mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info) | |
7065 | { | |
7066 | mDNS *const m = (mDNS *const)info; | |
7067 | (void)store; | |
7068 | ||
7069 | LogInfo("DynamicStoreReconnected: Reconnected"); | |
7070 | ||
7071 | // State:/Network/MulticastDNS | |
7072 | SetLocalDomains(); | |
7073 | ||
7074 | // State:/Network/DynamicDNS | |
7075 | if (m->FQDN.c[0]) | |
7076 | mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); | |
7077 | ||
7078 | // Note: PrivateDNS and BackToMyMac are automatically populated when configd is restarted | |
7079 | // as we receive network change notifications and thus not necessary. But we leave it here | |
7080 | // so that if things are done differently in the future, this code still works. | |
7081 | ||
7082 | // State:/Network/PrivateDNS | |
7083 | CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
7084 | if (!sa) | |
7085 | LogMsg("DynamicStoreReconnected:PrivateDNS: CFArrayCreateMutable failed"); | |
7086 | else | |
7087 | { | |
7088 | DomainAuthInfo *FoundInList; | |
7089 | char stringbuf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal domainname as C-string, including terminating NUL | |
7090 | for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) | |
7091 | { | |
7092 | ConvertDomainNameToCString(&FoundInList->domain, stringbuf); | |
7093 | CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8); | |
7094 | if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); } | |
7095 | } | |
7096 | mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa); | |
7097 | CFRelease(sa); | |
7098 | } | |
7099 | ||
7100 | // State:/Network/BackToMyMac | |
7101 | #if APPLE_OSX_mDNSResponder | |
7102 | mDNS_Lock(m); | |
7103 | UpdateAutoTunnelDomainStatuses(m); | |
7104 | mDNS_Unlock(m); | |
7105 | ||
7106 | // State:/Network/Interface/en0/SleepProxyServers | |
7107 | if (spsStatusDict) CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL); | |
7108 | #endif | |
7109 | } | |
7110 | ||
c9b9ae52 A |
7111 | mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) |
7112 | { | |
7113 | mStatus err = -1; | |
7114 | SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; | |
7f0064bd | 7115 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:WatchForNetworkChanges"), NetworkChanged, &context); |
67c8f8a1 | 7116 | CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
c9b9ae52 A |
7117 | CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); |
7118 | CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); | |
c9b9ae52 A |
7119 | CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
7120 | ||
7f0064bd | 7121 | if (!store) { LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); goto error; } |
67c8f8a1 A |
7122 | if (!keys || !pattern1 || !pattern2 || !patterns) goto error; |
7123 | ||
7124 | CFArrayAppendValue(keys, NetworkChangedKey_IPv4); | |
7125 | CFArrayAppendValue(keys, NetworkChangedKey_IPv6); | |
7126 | CFArrayAppendValue(keys, NetworkChangedKey_Hostnames); | |
7127 | CFArrayAppendValue(keys, NetworkChangedKey_Computername); | |
7128 | CFArrayAppendValue(keys, NetworkChangedKey_DNS); | |
7129 | CFArrayAppendValue(keys, NetworkChangedKey_DynamicDNS); | |
7130 | CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac); | |
263eeeab A |
7131 | CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); // should remove as part of <rdar://problem/6751656> |
7132 | CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity); | |
c9b9ae52 A |
7133 | CFArrayAppendValue(patterns, pattern1); |
7134 | CFArrayAppendValue(patterns, pattern2); | |
7f0064bd | 7135 | CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort")); |
c9b9ae52 | 7136 | if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) |
7f0064bd | 7137 | { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; } |
c9b9ae52 | 7138 | |
294beb6e | 7139 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
7140 | if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue())) |
7141 | { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } | |
7142 | #else | |
c9b9ae52 | 7143 | m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); |
7f0064bd | 7144 | if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } |
c9b9ae52 | 7145 | CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); |
263eeeab | 7146 | #endif |
294beb6e | 7147 | SCDynamicStoreSetDisconnectCallBack(store, DynamicStoreReconnected); |
c9b9ae52 A |
7148 | m->p->Store = store; |
7149 | err = 0; | |
7150 | goto exit; | |
7151 | ||
67c8f8a1 | 7152 | error: |
c9b9ae52 A |
7153 | if (store) CFRelease(store); |
7154 | ||
67c8f8a1 A |
7155 | exit: |
7156 | if (patterns) CFRelease(patterns); | |
c9b9ae52 | 7157 | if (pattern2) CFRelease(pattern2); |
67c8f8a1 | 7158 | if (pattern1) CFRelease(pattern1); |
c9b9ae52 | 7159 | if (keys) CFRelease(keys); |
67c8f8a1 | 7160 | |
c9b9ae52 A |
7161 | return(err); |
7162 | } | |
7163 | ||
32bb7e43 A |
7164 | #if 0 // <rdar://problem/6751656> |
7165 | mDNSlocal void PMChanged(void *context) | |
7166 | { | |
7167 | mDNS *const m = (mDNS *const)context; | |
7168 | ||
7169 | KQueueLock(m); | |
7170 | mDNS_Lock(m); | |
7171 | ||
7172 | LogSPS("PMChanged"); | |
7173 | ||
7174 | SetNetworkChanged(m, mDNSPlatformOneSecond * 2); | |
7175 | ||
7176 | mDNS_Unlock(m); | |
7177 | KQueueUnlock(m, "PMChanged"); | |
7178 | } | |
7179 | ||
7180 | mDNSlocal mStatus WatchForPMChanges(mDNS *const m) | |
7181 | { | |
7182 | m->p->PMRLS = IOPMPrefsNotificationCreateRunLoopSource(PMChanged, m); | |
7183 | if (!m->p->PMRLS) { LogMsg("IOPMPrefsNotificationCreateRunLoopSource failed!"); return mStatus_UnknownErr; } | |
7184 | ||
7185 | CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode); | |
7186 | ||
7187 | return mStatus_NoError; | |
7188 | } | |
7189 | #endif | |
7190 | ||
7191 | #ifndef KEV_DL_WAKEFLAGS_CHANGED | |
7192 | #define KEV_DL_WAKEFLAGS_CHANGED 17 | |
7193 | #endif | |
7194 | ||
294beb6e A |
7195 | #if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded |
7196 | ||
7197 | mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname) | |
7198 | { | |
7199 | AuthRecord *rr; | |
7200 | int found = 0; | |
7201 | ||
7202 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
7203 | { | |
7204 | if ((rr->resrec.rrtype == kDNSServiceType_SRV) && (rr->ARType == AuthRecordAnyIncludeP2P)) | |
7205 | { | |
7206 | uint16_t port = rr->resrec.rdata->u.srv.port.NotAnInteger; | |
7207 | uint16_t protocol; | |
7208 | const mDNSu8 *p; | |
7209 | ||
7210 | LogInfo("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr)); | |
7211 | ||
7212 | // Note presence of more than one p2p service in list | |
7213 | // since code is currently opening only one port in the packet filter. | |
7214 | found++; | |
7215 | if (found > 1) | |
7216 | { | |
7217 | LogMsg("mDNSSetPacketFilterRules: found service #%d %s", found, ARDisplayString(m, rr)); | |
7218 | } | |
7219 | ||
7220 | // Assume <Service Instance>.<App Protocol>.<Transport Protocol>.<Name> | |
7221 | p = rr->resrec.name->c; | |
7222 | ||
7223 | // Skip to App Protocol | |
7224 | if (p[0]) p += 1 + p[0]; | |
7225 | // Skip to Transport Protocol | |
7226 | if (p[0]) p += 1 + p[0]; | |
7227 | ||
7228 | if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocol = IPPROTO_TCP; | |
7229 | else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = IPPROTO_UDP; | |
7230 | else | |
7231 | { | |
7232 | LogMsg("mDNSSetPacketFilterRules: could not determine transport protocol of service"); | |
7233 | LogMsg("mDNSSetPacketFilterRules: %s", ARDisplayString(m, rr)); | |
7234 | return; | |
7235 | } | |
7236 | ||
7237 | LogInfo("mDNSSetPacketFilterRules: ifname %s, port(in NBO)0x%X, protocol %d", | |
7238 | ifname, port, protocol); | |
7239 | mDNSPacketFilterControl(PF_SET_RULES, ifname, port, protocol); | |
7240 | } | |
7241 | } | |
7242 | } | |
7243 | #endif // !TARGET_OS_EMBEDDED | |
7244 | ||
7245 | ||
7246 | #if APPLE_OSX_mDNSResponder | |
7247 | ||
7248 | #if !TARGET_OS_EMBEDDED | |
7249 | // If the p2p interface already exists, set the Bonjour packet filter rules for it. | |
7250 | mDNSexport void mDNSInitPacketFilter(void) | |
7251 | { | |
7252 | mDNS *const m = & mDNSStorage; | |
7253 | ||
7254 | NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); | |
7255 | while (intf) | |
7256 | { | |
7257 | if (strncmp(intf->ifname, "p2p", 3) == 0) | |
7258 | { | |
7259 | LogInfo("mDNSInitPacketFilter: Setting rules for ifname %s", intf->ifname); | |
7260 | mDNSSetPacketFilterRules(m, intf->ifname); | |
7261 | break; | |
7262 | } | |
7263 | intf = GetFirstActiveInterface(intf->next); | |
7264 | } | |
7265 | } | |
7266 | ||
7267 | #else // !TARGET_OS_EMBEDDED | |
7268 | ||
7269 | // Currently no packet filter setup required embedded on platforms. | |
7270 | mDNSexport void mDNSInitPacketFilter() | |
7271 | { | |
7272 | } | |
7273 | ||
7274 | #endif // !TARGET_OS_EMBEDDED | |
7275 | #endif // APPLE_OSX_mDNSResponder | |
7276 | ||
32bb7e43 A |
7277 | mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context) |
7278 | { | |
7279 | mDNS *const m = (mDNS *const)context; | |
7280 | ||
7281 | mDNS_Lock(m); | |
7282 | ||
7283 | struct { struct kern_event_msg k; char extra[256]; } msg; | |
7284 | int bytes = recv(s1, &msg, sizeof(msg), 0); | |
7285 | if (bytes < 0) | |
7286 | LogMsg("SysEventCallBack: recv error %d errno %d (%s)", bytes, errno, strerror(errno)); | |
7287 | else | |
7288 | { | |
7289 | LogInfo("SysEventCallBack got %d bytes size %d %X %s %X %s %X %s id %d code %d %s", | |
7290 | bytes, msg.k.total_size, | |
7291 | msg.k.vendor_code , msg.k.vendor_code == KEV_VENDOR_APPLE ? "KEV_VENDOR_APPLE" : "?", | |
7292 | msg.k.kev_class , msg.k.kev_class == KEV_NETWORK_CLASS ? "KEV_NETWORK_CLASS" : "?", | |
7293 | msg.k.kev_subclass, msg.k.kev_subclass == KEV_DL_SUBCLASS ? "KEV_DL_SUBCLASS" : "?", | |
7294 | msg.k.id, msg.k.event_code, | |
7295 | msg.k.event_code == KEV_DL_SIFFLAGS ? "KEV_DL_SIFFLAGS" : | |
7296 | msg.k.event_code == KEV_DL_SIFMETRICS ? "KEV_DL_SIFMETRICS" : | |
7297 | msg.k.event_code == KEV_DL_SIFMTU ? "KEV_DL_SIFMTU" : | |
7298 | msg.k.event_code == KEV_DL_SIFPHYS ? "KEV_DL_SIFPHYS" : | |
7299 | msg.k.event_code == KEV_DL_SIFMEDIA ? "KEV_DL_SIFMEDIA" : | |
7300 | msg.k.event_code == KEV_DL_SIFGENERIC ? "KEV_DL_SIFGENERIC" : | |
7301 | msg.k.event_code == KEV_DL_ADDMULTI ? "KEV_DL_ADDMULTI" : | |
7302 | msg.k.event_code == KEV_DL_DELMULTI ? "KEV_DL_DELMULTI" : | |
7303 | msg.k.event_code == KEV_DL_IF_ATTACHED ? "KEV_DL_IF_ATTACHED" : | |
7304 | msg.k.event_code == KEV_DL_IF_DETACHING ? "KEV_DL_IF_DETACHING" : | |
7305 | msg.k.event_code == KEV_DL_IF_DETACHED ? "KEV_DL_IF_DETACHED" : | |
7306 | msg.k.event_code == KEV_DL_LINK_OFF ? "KEV_DL_LINK_OFF" : | |
7307 | msg.k.event_code == KEV_DL_LINK_ON ? "KEV_DL_LINK_ON" : | |
7308 | msg.k.event_code == KEV_DL_PROTO_ATTACHED ? "KEV_DL_PROTO_ATTACHED" : | |
7309 | msg.k.event_code == KEV_DL_PROTO_DETACHED ? "KEV_DL_PROTO_DETACHED" : | |
7310 | msg.k.event_code == KEV_DL_LINK_ADDRESS_CHANGED ? "KEV_DL_LINK_ADDRESS_CHANGED" : | |
7311 | msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED ? "KEV_DL_WAKEFLAGS_CHANGED" : "?"); | |
7312 | ||
3c427d54 A |
7313 | // We receive network change notifications both through configd and through SYSPROTO_EVENT socket. |
7314 | // Configd may not generate network change events for manually configured interfaces (i.e., non-DHCP) | |
7315 | // always during sleep/wakeup due to some race conditions (See radar:8666757). At the same time, if | |
7316 | // "Wake on Network Access" is not turned on, the notification will not have KEV_DL_WAKEFLAGS_CHANGED. | |
7317 | // Hence, during wake up, if we see a KEV_DL_LINK_ON (i.e., link is UP), we trigger a network change. | |
7318 | ||
7319 | if (msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED || msg.k.event_code == KEV_DL_LINK_ON) | |
7320 | SetNetworkChanged(m, mDNSPlatformOneSecond * 2); | |
294beb6e A |
7321 | |
7322 | #if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded | |
7323 | ||
7324 | // For p2p interfaces, need to open the advertised service port in the firewall. | |
7325 | if (msg.k.event_code == KEV_DL_IF_ATTACHED) | |
7326 | { | |
7327 | struct net_event_data * p; | |
7328 | p = (struct net_event_data *) & msg.k.event_data; | |
7329 | ||
7330 | if (strncmp(p->if_name, "p2p", 3) == 0) | |
7331 | { | |
7332 | char ifname[IFNAMSIZ]; | |
7333 | snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit); | |
7334 | ||
7335 | LogInfo("SysEventCallBack: KEV_DL_IF_ATTACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name); | |
7336 | ||
7337 | mDNSSetPacketFilterRules(m, ifname); | |
7338 | } | |
7339 | } | |
7340 | ||
7341 | // For p2p interfaces, need to clear the firewall rules on interface detach | |
7342 | if (msg.k.event_code == KEV_DL_IF_DETACHED) | |
7343 | { | |
7344 | struct net_event_data * p; | |
7345 | p = (struct net_event_data *) & msg.k.event_data; | |
7346 | ||
7347 | if (strncmp(p->if_name, "p2p", 3) == 0) | |
7348 | { | |
7349 | char ifname[IFNAMSIZ]; | |
7350 | snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit); | |
7351 | ||
7352 | LogInfo("SysEventCallBack: KEV_DL_IF_DETACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name); | |
7353 | ||
7354 | mDNSPacketFilterControl(PF_CLEAR_RULES, ifname, 0, 0); | |
7355 | } | |
7356 | } | |
7357 | #endif // !TARGET_OS_EMBEDDED | |
7358 | ||
32bb7e43 A |
7359 | } |
7360 | ||
7361 | mDNS_Unlock(m); | |
7362 | } | |
7363 | ||
7364 | mDNSlocal mStatus WatchForSysEvents(mDNS *const m) | |
7365 | { | |
7366 | m->p->SysEventNotifier = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT); | |
7367 | if (m->p->SysEventNotifier < 0) | |
263eeeab | 7368 | { LogMsg("WatchForSysEvents: socket failed error %d errno %d (%s)", m->p->SysEventNotifier, errno, strerror(errno)); return(mStatus_NoMemoryErr); } |
32bb7e43 A |
7369 | |
7370 | struct kev_request kev_req = { KEV_VENDOR_APPLE, KEV_NETWORK_CLASS, KEV_DL_SUBCLASS }; | |
7371 | int err = ioctl(m->p->SysEventNotifier, SIOCSKEVFILT, &kev_req); | |
7372 | if (err < 0) | |
7373 | { | |
263eeeab | 7374 | LogMsg("WatchForSysEvents: SIOCSKEVFILT failed error %d errno %d (%s)", err, errno, strerror(errno)); |
32bb7e43 A |
7375 | close(m->p->SysEventNotifier); |
7376 | m->p->SysEventNotifier = -1; | |
7377 | return(mStatus_UnknownErr); | |
7378 | } | |
7379 | ||
7380 | m->p->SysEventKQueue.KQcallback = SysEventCallBack; | |
7381 | m->p->SysEventKQueue.KQcontext = m; | |
7382 | m->p->SysEventKQueue.KQtask = "System Event Notifier"; | |
7383 | KQueueSet(m->p->SysEventNotifier, EV_ADD, EVFILT_READ, &m->p->SysEventKQueue); | |
7384 | ||
7385 | return(mStatus_NoError); | |
7386 | } | |
7387 | ||
67c8f8a1 A |
7388 | #ifndef NO_SECURITYFRAMEWORK |
7389 | mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCallbackInfo *info, void *context) | |
7390 | { | |
32bb7e43 | 7391 | LogInfo("*** Keychain Changed ***"); |
67c8f8a1 A |
7392 | mDNS *const m = (mDNS *const)context; |
7393 | SecKeychainRef skc; | |
7394 | OSStatus err = SecKeychainCopyDefault(&skc); | |
7395 | if (!err) | |
7396 | { | |
7397 | if (info->keychain == skc) | |
7398 | { | |
7399 | // 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 | |
7400 | mDNSBool relevant = (keychainEvent == kSecDeleteEvent); | |
7401 | if (!relevant) | |
7402 | { | |
7403 | UInt32 tags[3] = { kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr }; | |
7404 | SecKeychainAttributeInfo attrInfo = { 3, tags, NULL }; // Count, array of tags, array of formats | |
7405 | SecKeychainAttributeList *a = NULL; | |
7406 | err = SecKeychainItemCopyAttributesAndData(info->item, &attrInfo, NULL, &a, NULL, NULL); | |
7407 | if (!err) | |
7408 | { | |
7409 | relevant = ((a->attr[0].length == 4 && (!strncasecmp(a->attr[0].data, "ddns", 4) || !strncasecmp(a->attr[0].data, "sndd", 4))) || | |
294beb6e A |
7410 | (a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix)))) || |
7411 | (a->attr[1].length >= mDNSPlatformStrLen(btmmprefix) && (!strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix))))); | |
67c8f8a1 A |
7412 | SecKeychainItemFreeAttributesAndData(a, NULL); |
7413 | } | |
7414 | } | |
7415 | if (relevant) | |
7416 | { | |
32bb7e43 | 7417 | LogInfo("*** Keychain Changed *** KeychainEvent=%d %s", |
67c8f8a1 | 7418 | keychainEvent, |
32bb7e43 A |
7419 | keychainEvent == kSecAddEvent ? "kSecAddEvent" : |
7420 | keychainEvent == kSecDeleteEvent ? "kSecDeleteEvent" : | |
7421 | keychainEvent == kSecUpdateEvent ? "kSecUpdateEvent" : "<Unknown>"); | |
96f69b28 | 7422 | // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding |
67c8f8a1 A |
7423 | KQueueLock(m); |
7424 | mDNS_Lock(m); | |
294beb6e A |
7425 | |
7426 | // To not read the keychain twice: when BTMM is enabled, changes happen to the keychain | |
7427 | // then the BTMM DynStore dictionary, so delay reading the keychain for a second. | |
7428 | // NetworkChanged() will reset the keychain timer to fire immediately when the DynStore changes. | |
7429 | // | |
7430 | // In the "fixup" case where the BTMM DNS servers aren't accepting the key mDNSResponder has, | |
7431 | // the DynStore dictionary won't change (because the BTMM zone won't change). In that case, | |
7432 | // a one second delay is ok, as we'll still converge to correctness, and there's no race | |
7433 | // condition between the RegistrationDomain and the DomainAuthInfo. | |
7434 | // | |
7435 | // Lastly, non-BTMM WAB cases can use the keychain but not the DynStore, so we need to set | |
7436 | // the timer here, as it will not get set by NetworkChanged(). | |
7437 | SetKeyChainTimer(m, mDNSPlatformOneSecond); | |
7438 | ||
67c8f8a1 A |
7439 | mDNS_Unlock(m); |
7440 | KQueueUnlock(m, "KeychainChanged"); | |
7441 | } | |
7442 | } | |
7443 | CFRelease(skc); | |
7444 | } | |
7445 | ||
7446 | return 0; | |
7447 | } | |
7448 | #endif | |
7449 | ||
32bb7e43 A |
7450 | mDNSlocal void PowerOn(mDNS *const m) |
7451 | { | |
7452 | mDNSCoreMachineSleep(m, false); // Will set m->SleepState = SleepState_Awake; | |
7453 | if (m->p->WakeAtUTC) | |
7454 | { | |
7455 | long utc = mDNSPlatformUTC(); | |
7456 | mDNSPowerRequest(-1,-1); // Need to explicitly clear any previous power requests -- they're not cleared automatically on wake | |
294beb6e A |
7457 | if (m->p->WakeAtUTC - utc > 30) LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc); |
7458 | else if (utc - m->p->WakeAtUTC > 30) LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC); | |
7459 | else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is K66AP so not re-sleeping", utc - m->p->WakeAtUTC); | |
32bb7e43 A |
7460 | else |
7461 | { | |
294beb6e A |
7462 | LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc); |
7463 | m->p->RequestReSleep = mDNS_TimeNow(m) + 20 * mDNSPlatformOneSecond; | |
32bb7e43 A |
7464 | } |
7465 | } | |
7466 | } | |
7467 | ||
c9b9ae52 A |
7468 | mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) |
7469 | { | |
7470 | mDNS *const m = (mDNS *const)refcon; | |
67c8f8a1 | 7471 | KQueueLock(m); |
8e92c31c | 7472 | (void)service; // Parameter not used |
32bb7e43 A |
7473 | debugf("PowerChanged %X %lX", messageType, messageArgument); |
7474 | ||
7475 | // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting | |
7476 | m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); | |
7477 | ||
c9b9ae52 A |
7478 | switch(messageType) |
7479 | { | |
294beb6e A |
7480 | case kIOMessageCanSystemPowerOff: LogSPS("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 |
7481 | case kIOMessageSystemWillPowerOff: LogSPS("PowerChanged kIOMessageSystemWillPowerOff"); // E0000250 | |
32bb7e43 A |
7482 | mDNSCoreMachineSleep(m, true); |
7483 | if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m); | |
7484 | break; | |
294beb6e A |
7485 | case kIOMessageSystemWillNotPowerOff: LogSPS("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 |
7486 | case kIOMessageCanSystemSleep: LogSPS("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 | |
7487 | case kIOMessageSystemWillSleep: LogSPS("PowerChanged kIOMessageSystemWillSleep"); // E0000280 | |
32bb7e43 A |
7488 | mDNSCoreMachineSleep(m, true); |
7489 | break; | |
294beb6e A |
7490 | case kIOMessageSystemWillNotSleep: LogSPS("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 |
7491 | case kIOMessageSystemHasPoweredOn: LogSPS("PowerChanged kIOMessageSystemHasPoweredOn"); // E0000300 | |
7f0064bd | 7492 | // If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now |
32bb7e43 A |
7493 | if (m->SleepState) |
7494 | { | |
7495 | LogMsg("PowerChanged kIOMessageSystemHasPoweredOn: ERROR m->SleepState %d", m->SleepState); | |
7496 | PowerOn(m); | |
7497 | } | |
7498 | // Just to be safe, schedule a mDNSMacOSXNetworkChanged(), in case we never received | |
7499 | // the System Configuration Framework "network changed" event that we expect | |
7500 | // to receive some time shortly after the kIOMessageSystemWillPowerOn message | |
7501 | mDNS_Lock(m); | |
7502 | if (!m->p->NetworkChanged || | |
7503 | m->p->NetworkChanged - NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2) < 0) | |
7504 | m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); | |
7505 | mDNS_Unlock(m); | |
7506 | ||
7507 | break; | |
294beb6e A |
7508 | case kIOMessageSystemWillRestart: LogSPS("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310 |
7509 | case kIOMessageSystemWillPowerOn: LogSPS("PowerChanged kIOMessageSystemWillPowerOn"); // E0000320 | |
32bb7e43 A |
7510 | |
7511 | // On Leopard and earlier, on wake from sleep, instead of reporting link state down, Apple | |
7512 | // Ethernet drivers report "hardware incapable of detecting link state", which the kernel | |
7513 | // interprets as "should assume we have networking", which results in the first 4-5 seconds | |
7514 | // of packets vanishing into a black hole. To work around this, on wake from sleep, | |
7515 | // we block for five seconds to let Ethernet come up, and then resume normal operation. | |
294beb6e | 7516 | if (OSXVers && OSXVers < OSXVers_10_6_SnowLeopard) |
32bb7e43 A |
7517 | { |
7518 | sleep(5); | |
7519 | LogMsg("Running on Mac OS X version 10.%d earlier than 10.6; " | |
7520 | "PowerChanged did sleep(5) to wait for Ethernet hardware", OSXVers - OSXVers_Base); | |
7521 | } | |
7522 | ||
7f0064bd | 7523 | // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake |
32bb7e43 A |
7524 | if (m->SleepState != SleepState_Sleeping) |
7525 | { | |
7526 | LogMsg("kIOMessageSystemWillPowerOn: ERROR m->SleepState %d", m->SleepState); | |
7527 | m->SleepState = SleepState_Sleeping; | |
7528 | mDNSMacOSXNetworkChanged(m); | |
7529 | } | |
7530 | PowerOn(m); | |
7531 | break; | |
294beb6e | 7532 | default: LogSPS("PowerChanged unknown message %X", messageType); break; |
c9b9ae52 | 7533 | } |
030b743d | 7534 | |
32bb7e43 A |
7535 | if (messageType == kIOMessageSystemWillSleep) m->p->SleepCookie = (long)messageArgument; |
7536 | else IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); | |
7537 | ||
7538 | KQueueUnlock(m, "PowerChanged Sleep/Wake"); | |
7539 | } | |
7540 | ||
263eeeab A |
7541 | // iPhone OS doesn't currently have SnowLeopard's IO Power Management |
7542 | // but it does define kIOPMAcknowledgmentOptionSystemCapabilityRequirements | |
7543 | #if defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) && !TARGET_OS_EMBEDDED | |
32bb7e43 A |
7544 | mDNSlocal void SnowLeopardPowerChanged(void *refcon, IOPMConnection connection, IOPMConnectionMessageToken token, IOPMSystemPowerStateCapabilities eventDescriptor) |
7545 | { | |
7546 | mDNS *const m = (mDNS *const)refcon; | |
7547 | KQueueLock(m); | |
7548 | LogSPS("SnowLeopardPowerChanged %X %X %X%s%s%s%s%s", | |
7549 | connection, token, eventDescriptor, | |
7550 | eventDescriptor & kIOPMSystemPowerStateCapabilityCPU ? " CPU" : "", | |
7551 | eventDescriptor & kIOPMSystemPowerStateCapabilityVideo ? " Video" : "", | |
7552 | eventDescriptor & kIOPMSystemPowerStateCapabilityAudio ? " Audio" : "", | |
7553 | eventDescriptor & kIOPMSystemPowerStateCapabilityNetwork ? " Network" : "", | |
7554 | eventDescriptor & kIOPMSystemPowerStateCapabilityDisk ? " Disk" : ""); | |
7555 | ||
7556 | // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting | |
7557 | m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); | |
7558 | ||
7559 | if (eventDescriptor & kIOPMSystemPowerStateCapabilityCPU) | |
030b743d | 7560 | { |
263eeeab A |
7561 | // We might be in Sleeping or Transferring state. When we go from "wakeup" to "sleep" state, we don't |
7562 | // go directly to sleep state, but transfer in to the sleep state during which SleepState is set to | |
7563 | // SleepState_Transferring. During that time, we might get another wakeup before we transition to Sleeping | |
7564 | // state. In that case, we need to acknowledge the previous "sleep" before we acknowledge the wakeup. | |
7565 | if (m->SleepLimit) | |
7566 | { | |
7567 | LogSPS("SnowLeopardPowerChanged: Waking up, Acking old Sleep, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState); | |
7568 | IOPMConnectionAcknowledgeEvent(connection, m->p->SleepCookie); | |
7569 | m->SleepLimit = 0; | |
7570 | } | |
7571 | LogSPS("SnowLeopardPowerChanged: Waking up, Acking Wakeup, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState); | |
32bb7e43 | 7572 | // CPU Waking. Note: Can get this message repeatedly, as other subsystems power up or down. |
263eeeab | 7573 | if (m->SleepState != SleepState_Awake) PowerOn(m); |
32bb7e43 | 7574 | IOPMConnectionAcknowledgeEvent(connection, token); |
030b743d A |
7575 | } |
7576 | else | |
32bb7e43 A |
7577 | { |
7578 | // CPU sleeping. Should not get this repeatedly -- once we're told that the CPU is halting | |
7579 | // we should hear nothing more until we're told that the CPU has started executing again. | |
7580 | if (m->SleepState) LogMsg("SnowLeopardPowerChanged: Sleep Error %X m->SleepState %d", eventDescriptor, m->SleepState); | |
7581 | //sleep(5); | |
7582 | //mDNSMacOSXNetworkChanged(m); | |
7583 | mDNSCoreMachineSleep(m, true); | |
7584 | //if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m); | |
7585 | m->p->SleepCookie = token; | |
7586 | } | |
030b743d | 7587 | |
32bb7e43 | 7588 | KQueueUnlock(m, "SnowLeopardPowerChanged Sleep/Wake"); |
c9b9ae52 | 7589 | } |
32bb7e43 A |
7590 | #endif |
7591 | ||
294beb6e A |
7592 | #if COMPILER_LIKES_PRAGMA_MARK |
7593 | #pragma mark - | |
7594 | #pragma mark - /etc/hosts support | |
7595 | #endif | |
7596 | ||
7597 | // Implementation Notes | |
7598 | // | |
7599 | // As /etc/hosts file can be huge (1000s of entries - when this comment was written, the test file had about | |
7600 | // 23000 entries with about 4000 duplicates), we can't use a linked list to store these entries. So, we parse | |
7601 | // them into a hash table. The implementation need to be able to do the following things efficiently | |
7602 | // | |
7603 | // 1. Detect duplicates e.g., two entries with "1.2.3.4 foo" | |
7604 | // 2. Detect whether /etc/hosts has changed and what has changed since the last read from the disk | |
7605 | // 3. Ability to support multiple addresses per name e.g., "1.2.3.4 foo, 2.3.4.5 foo". To support this, we | |
7606 | // need to be able set the RRSet of a resource record to the first one in the list and also update when | |
7607 | // one of them go away. This is needed so that the core thinks that they are all part of the same RRSet and | |
7608 | // not a duplicate | |
7609 | // 4. Don't maintain any local state about any records registered with the core to detect changes to /etc/hosts | |
7610 | // | |
7611 | // CFDictionary is not a suitable candidate because it does not support duplicates and even if we use a custom | |
7612 | // "hash" function to solve this, the others are hard to solve. Hence, we share the hash (AuthHash) implementation | |
7613 | // of the core layer which does all of the above very efficiently | |
7614 | ||
7615 | mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result) | |
7616 | { | |
7617 | (void)m; // unused | |
7618 | (void)rr; | |
7619 | (void)result; | |
7620 | if (result == mStatus_MemFree) | |
7621 | { | |
7622 | LogInfo("FreeEtcHosts: %s", ARDisplayString(m, rr)); | |
7623 | freeL("etchosts", rr); | |
7624 | } | |
7625 | } | |
7626 | ||
7627 | // Returns true on success and false on failure | |
7628 | mDNSlocal mDNSBool mDNSMacOSXCreateEtcHostsEntry(mDNS *const m, const domainname *domain, const struct sockaddr *sa, const domainname *cname, char *ifname, AuthHash *auth) | |
7629 | { | |
7630 | AuthRecord *rr; | |
7631 | mDNSu32 slot; | |
7632 | mDNSu32 namehash; | |
7633 | AuthGroup *ag; | |
7634 | mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly; | |
7635 | mDNSu16 rrtype; | |
7636 | ||
7637 | if (!domain) | |
7638 | { | |
7639 | LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! name NULL"); | |
7640 | return mDNSfalse; | |
7641 | } | |
7642 | if (!sa && !cname) | |
7643 | { | |
7644 | LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa and cname both NULL"); | |
7645 | return mDNSfalse; | |
7646 | } | |
7647 | ||
7648 | if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6) | |
7649 | { | |
7650 | LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa with bad family %d", sa->sa_family); | |
7651 | return mDNSfalse; | |
7652 | } | |
7653 | ||
7654 | ||
7655 | if (ifname) | |
7656 | { | |
7657 | mDNSu32 ifindex = if_nametoindex(ifname); | |
7658 | if (!ifindex) | |
7659 | { | |
7660 | LogMsg("mDNSMacOSXCreateEtcHostsEntry: hosts entry %##s with invalid ifname %s", domain->c, ifname); | |
7661 | return mDNSfalse; | |
7662 | } | |
7663 | InterfaceID = (mDNSInterfaceID)(uintptr_t)ifindex; | |
7664 | } | |
7665 | ||
7666 | if (sa) | |
7667 | rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA); | |
7668 | else | |
7669 | rrtype = kDNSType_CNAME; | |
7670 | ||
7671 | // Check for duplicates. See whether we parsed an entry before like this ? | |
7672 | slot = AuthHashSlot(domain); | |
7673 | namehash = DomainNameHashValue(domain); | |
7674 | ag = AuthGroupForName(auth, slot, namehash, domain); | |
7675 | if (ag) | |
7676 | { | |
7677 | rr = ag->members; | |
7678 | while (rr) | |
7679 | { | |
7680 | if (rr->resrec.rrtype == rrtype) | |
7681 | { | |
7682 | if (rrtype == kDNSType_A) | |
7683 | { | |
7684 | mDNSv4Addr ip; | |
7685 | ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr; | |
7686 | if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip)) | |
7687 | { | |
7688 | LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv4 address for name %##s", domain->c); | |
7689 | return mDNSfalse; | |
7690 | } | |
7691 | } | |
7692 | else if (rrtype == kDNSType_AAAA) | |
7693 | { | |
7694 | mDNSv6Addr ip6; | |
7695 | ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0]; | |
7696 | ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1]; | |
7697 | ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2]; | |
7698 | ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3]; | |
7699 | if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6)) | |
7700 | { | |
7701 | LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv6 address for name %##s", domain->c); | |
7702 | return mDNSfalse; | |
7703 | } | |
7704 | } | |
7705 | else if (rrtype == kDNSType_CNAME) | |
7706 | { | |
7707 | if (SameDomainName(&rr->resrec.rdata->u.name, cname)) | |
7708 | { | |
7709 | LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same cname %##s for name %##s", cname->c, domain->c); | |
7710 | return mDNSfalse; | |
7711 | } | |
7712 | } | |
7713 | } | |
7714 | rr = rr->next; | |
7715 | } | |
7716 | } | |
7717 | rr= mallocL("etchosts", sizeof(*rr)); | |
7718 | if (rr == NULL) return mDNSfalse; | |
7719 | mDNSPlatformMemZero(rr, sizeof(*rr)); | |
7720 | mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL); | |
7721 | AssignDomainName(&rr->namestorage, domain); | |
7722 | ||
7723 | if (sa) | |
7724 | { | |
7725 | rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr); | |
7726 | if (sa->sa_family == AF_INET) | |
7727 | rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr; | |
7728 | else | |
7729 | { | |
7730 | rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0]; | |
7731 | rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1]; | |
7732 | rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2]; | |
7733 | rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3]; | |
7734 | } | |
7735 | } | |
7736 | else | |
7737 | { | |
7738 | rr->resrec.rdlength = DomainNameLength(cname); | |
7739 | rr->resrec.rdata->u.name.c[0] = 0; | |
7740 | AssignDomainName(&rr->resrec.rdata->u.name, cname); | |
7741 | } | |
7742 | rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); | |
7743 | SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us | |
7744 | LogInfo("mDNSMacOSXCreateEtcHostsEntry: Adding resource record %s", ARDisplayString(m, rr)); | |
7745 | InsertAuthRecord(m, auth, rr); | |
7746 | return mDNStrue; | |
7747 | } | |
7748 | ||
7749 | mDNSlocal int EtcHostsParseOneName(int start, int length, char *buffer, char **name) | |
7750 | { | |
7751 | int i; | |
7752 | ||
7753 | *name = NULL; | |
7754 | for (i = start; i < length; i++) | |
7755 | { | |
7756 | if (buffer[i] != ' ' && buffer[i] != ',' && buffer[i] != '\t') | |
7757 | { | |
7758 | *name = &buffer[i]; | |
7759 | ||
7760 | // Found the start of a name, find the end and null terminate | |
7761 | for (i++; i < length; i++) | |
7762 | { | |
7763 | if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t') | |
7764 | { | |
7765 | buffer[i] = 0; | |
7766 | break; | |
7767 | } | |
7768 | } | |
7769 | return i; | |
7770 | } | |
7771 | } | |
7772 | return -1; | |
7773 | } | |
7774 | ||
7775 | mDNSlocal void mDNSMacOSXParseEtcHostsLine(mDNS *const m, char *buffer, ssize_t length, AuthHash *auth) | |
7776 | { | |
7777 | int i; | |
7778 | int ifStart = 0; | |
7779 | char *ifname = NULL; | |
7780 | domainname name1d; | |
7781 | domainname name2d; | |
7782 | char *name1; | |
7783 | char *name2; | |
7784 | int aliasIndex; | |
7785 | ||
7786 | // Find the end of the address string | |
7787 | for (i = 0; i < length; i++) | |
7788 | { | |
7789 | if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t' || buffer[i] == '%') | |
7790 | { | |
7791 | if (buffer[i] == '%') | |
7792 | ifStart = i + 1; | |
7793 | buffer[i] = 0; | |
7794 | break; | |
7795 | } | |
7796 | } | |
7797 | ||
7798 | // Convert the address string to an address | |
7799 | struct addrinfo hints; | |
7800 | bzero(&hints, sizeof(hints)); | |
7801 | hints.ai_flags = AI_NUMERICHOST; | |
7802 | struct addrinfo *gairesults = NULL; | |
7803 | if (getaddrinfo(buffer, NULL, &hints, &gairesults) != 0) | |
7804 | { | |
7805 | LogInfo("mDNSMacOSXParseEtcHostsLine: getaddrinfo returning null"); | |
7806 | return; | |
7807 | } | |
7808 | ||
7809 | if (ifStart) | |
7810 | { | |
7811 | // Parse the interface | |
7812 | ifname = &buffer[ifStart]; | |
7813 | for (i = ifStart + 1; i < length; i++) | |
7814 | { | |
7815 | if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t') | |
7816 | { | |
7817 | buffer[i] = 0; | |
7818 | break; | |
7819 | } | |
7820 | } | |
7821 | } | |
7822 | ||
7823 | i = EtcHostsParseOneName(i + 1, length, buffer, &name1); | |
7824 | if (i == length) | |
7825 | { | |
7826 | // Common case (no aliases) : The entry is of the form "1.2.3.4 somehost" with no trailing white spaces/tabs etc. | |
7827 | if (!MakeDomainNameFromDNSNameString(&name1d, name1)) | |
7828 | { | |
7829 | LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1); | |
7830 | freeaddrinfo(gairesults); | |
7831 | return; | |
7832 | } | |
7833 | mDNSMacOSXCreateEtcHostsEntry(m, &name1d, gairesults->ai_addr, mDNSNULL, ifname, auth); | |
7834 | } | |
7835 | else if (i != -1) | |
7836 | { | |
7837 | domainname first; | |
7838 | // We might have some extra white spaces at the end for the common case of "1.2.3.4 somehost". | |
7839 | // When we parse again below, EtchHostsParseOneName would return -1 and we will end up | |
7840 | // doing the right thing. | |
7841 | if (!MakeDomainNameFromDNSNameString(&first, name1)) | |
7842 | { | |
7843 | LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1); | |
7844 | freeaddrinfo(gairesults); | |
7845 | return; | |
7846 | } | |
7847 | // If the /etc/hosts has an entry like this | |
7848 | // | |
7849 | // 1.2.3.4 sun star bright | |
7850 | // | |
7851 | // star and bright are aliases (gethostbyname h_alias should point to these) and sun is the canonical | |
7852 | // name (getaddrinfo ai_cannonname and gethostbyname h_name points to "sun") | |
7853 | // | |
7854 | // To achieve this, we need to add the entry like this: | |
7855 | // | |
7856 | // star CNAME bright | |
7857 | // bright CNAME sun | |
7858 | // sun A 1.2.3.4 | |
7859 | // | |
7860 | // We store the first name we parsed in "first". Then we parse additional names adding CNAME records | |
7861 | // till we reach the end. When we reach the end, we wrap around and add one final CNAME with the last | |
7862 | // entry and the first entry. Finally, we add the Address (A/AAAA) record. | |
7863 | aliasIndex = 0; | |
7864 | while (i <= length) | |
7865 | { | |
7866 | // Parse a name. If there are no names, we need to know whether we | |
7867 | // parsed CNAMEs before or not. If we parsed CNAMEs before, then we | |
7868 | // add a CNAME with the last name and the first name. Otherwise, this | |
7869 | // is same as the common case above where the line has just one name | |
7870 | // but with trailing white spaces. | |
7871 | i = EtcHostsParseOneName(i + 1, length, buffer, &name2); | |
7872 | if (name2) | |
7873 | { | |
7874 | if (!MakeDomainNameFromDNSNameString(&name2d, name2)) | |
7875 | { | |
7876 | LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name2); | |
7877 | freeaddrinfo(gairesults); | |
7878 | return; | |
7879 | } | |
7880 | aliasIndex++; | |
7881 | } | |
7882 | else if (!aliasIndex) | |
7883 | { | |
7884 | // We have never parsed any aliases. This case happens if there | |
7885 | // is just one name and some extra white spaces at the end. | |
7886 | LogInfo("mDNSMacOSXParseEtcHostsLine: White space at the end of %##s", first.c); | |
7887 | break; | |
7888 | } | |
7889 | else | |
7890 | { | |
7891 | // We have parsed at least one alias before and we reached the end of the line. | |
7892 | // Setup a CNAME for the last name with "first" name as its RDATA | |
7893 | name2d.c[0] = 0; | |
7894 | AssignDomainName(&name2d, &first); | |
7895 | } | |
7896 | ||
7897 | // Don't add a CNAME for the first alias we parse (see the example above). | |
7898 | // As we parse more, we might discover that there are no more aliases, in | |
7899 | // which case we would have set "name2d" to "first" above. We need to add | |
7900 | // the CNAME in that case. | |
7901 | ||
7902 | if (aliasIndex > 1 || SameDomainName(&name2d, &first)) | |
7903 | { | |
7904 | // Ignore if it points to itself | |
7905 | if (!SameDomainName(&name1d, &name2d)) | |
7906 | { | |
7907 | if (!mDNSMacOSXCreateEtcHostsEntry(m, &name1d, mDNSNULL, &name2d, ifname, auth)) | |
7908 | { | |
7909 | freeaddrinfo(gairesults); | |
7910 | return; | |
7911 | } | |
7912 | } | |
7913 | else | |
7914 | LogMsg("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names name1 %##s, name2 %##s", name1d.c, name2d.c); | |
7915 | } | |
7916 | ||
7917 | // If we have already wrapped around, we just need to add the A/AAAA record alone | |
7918 | // which is done below | |
7919 | if (SameDomainName(&name2d, &first)) break; | |
7920 | ||
7921 | // Remember the current name so that we can set the CNAME record if we parse one | |
7922 | // more name | |
7923 | name1d.c[0] = 0; | |
7924 | AssignDomainName(&name1d, &name2d); | |
7925 | } | |
7926 | // Added all the CNAMEs if any, add the "A/AAAA" record | |
7927 | mDNSMacOSXCreateEtcHostsEntry(m, &first, gairesults->ai_addr, mDNSNULL, ifname, auth); | |
7928 | } | |
7929 | freeaddrinfo(gairesults); | |
7930 | } | |
7931 | ||
7932 | mDNSlocal void mDNSMacOSXParseEtcHosts(mDNS *const m, int fd, AuthHash *auth) | |
7933 | { | |
7934 | if (fd == -1) { LogInfo("mDNSMacOSXParseEtcHosts: fd is -1"); return; } | |
4a95efb2 A |
7935 | |
7936 | LogInfo("mDNSMacOSXParseEtcHosts: Parsing etc hosts"); | |
3c3ba2cc | 7937 | |
4a95efb2 A |
7938 | // Read through the file |
7939 | char readBuf[1024]; // support a maximum line of 1024 bytes including newline | |
7940 | off_t offset = 0; | |
7941 | int comment = 0; | |
7942 | ssize_t i = 0; | |
7943 | ssize_t length; | |
7944 | ssize_t linestart; | |
7945 | while ((length = pread(fd, &readBuf[i], sizeof(readBuf) - i, offset) + i) >= i) | |
3c3ba2cc | 7946 | { |
4a95efb2 | 7947 | if (length == i) |
8b5f5b69 | 7948 | { |
4a95efb2 A |
7949 | // end of file |
7950 | if (length != 0) mDNSMacOSXParseEtcHostsLine(m, readBuf, length, auth); | |
7951 | break; | |
8b5f5b69 | 7952 | } |
4a95efb2 A |
7953 | |
7954 | // increment our offset by the number of bytes we read | |
7955 | offset += length - i; | |
7956 | ||
7957 | // loop through the buffer looking for end of lines | |
7958 | for (linestart = 0; i < length; i++) | |
8b5f5b69 | 7959 | { |
4a95efb2 | 7960 | if (readBuf[i] == '#' || readBuf[i] == '\r' || readBuf[i] == '\n') |
8b5f5b69 | 7961 | { |
4a95efb2 A |
7962 | int parseline = i - linestart > 0 && comment == 0; |
7963 | comment = readBuf[i] == '#'; | |
7964 | if (parseline) | |
7965 | { | |
7966 | readBuf[i] = 0; | |
7967 | mDNSMacOSXParseEtcHostsLine(m, &readBuf[linestart], i - linestart, auth); | |
7968 | } | |
7969 | linestart = i + 1; | |
8b5f5b69 A |
7970 | } |
7971 | } | |
4a95efb2 A |
7972 | |
7973 | // prepare to read the next chunk of the file | |
7974 | if (linestart == 0) | |
8b5f5b69 | 7975 | { |
4a95efb2 A |
7976 | // single line was larger than our buffer, we're going to ignore it |
7977 | comment = 1; // treat remainder of line as comment | |
7978 | i = 0; | |
7979 | } | |
7980 | else | |
7981 | { | |
7982 | i = length - linestart; | |
7983 | if (i) memmove(readBuf, &readBuf[linestart], i); | |
8b5f5b69 | 7984 | } |
294beb6e A |
7985 | } |
7986 | } | |
7987 | ||
7988 | mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m); | |
7989 | ||
7990 | mDNSlocal int mDNSMacOSXGetEtcHostsFD(mDNS *const m) | |
7991 | { | |
7992 | #ifdef __DISPATCH_GROUP__ | |
7993 | // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch | |
7994 | static dispatch_queue_t etcq = 0; | |
7995 | static dispatch_source_t etcsrc = 0; | |
7996 | static dispatch_source_t hostssrc = 0; | |
7997 | ||
7998 | // First time through? just schedule ourselves on the main queue and we'll do the work later | |
7999 | if (!etcq) | |
8000 | { | |
8001 | etcq = dispatch_get_main_queue(); | |
8002 | if (etcq) | |
8003 | { | |
8004 | // Do this work on the queue, not here - solves potential synchronization issues | |
8005 | dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); | |
8006 | } | |
8007 | return -1; | |
8008 | } | |
8009 | ||
8010 | if (hostssrc) return dispatch_source_get_handle(hostssrc); | |
8011 | #endif | |
8012 | ||
8013 | int fd = open("/etc/hosts", O_RDONLY); | |
8014 | ||
8015 | #ifdef __DISPATCH_GROUP__ | |
8016 | // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch | |
8017 | if (fd == -1) | |
8018 | { | |
8019 | // If the open failed and we're already watching /etc, we're done | |
8020 | if (etcsrc) { LogInfo("mDNSMacOSXGetEtcHostsFD: Returning etcfd because no etchosts"); return fd; } | |
8021 | ||
8022 | // we aren't watching /etc, we should be | |
8023 | fd = open("/etc", O_RDONLY); | |
8024 | if (fd == -1) { LogInfo("mDNSMacOSXGetEtcHostsFD: etc does not exist"); return -1; } | |
8025 | etcsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME, etcq); | |
8026 | if (etcsrc == NULL) | |
8027 | { | |
8028 | close(fd); | |
8029 | return -1; | |
8030 | } | |
8031 | dispatch_source_set_event_handler(etcsrc, | |
8032 | ^{ | |
8033 | u_int32_t flags = dispatch_source_get_data(etcsrc); | |
8034 | LogMsg("mDNSMacOSXGetEtcHostsFD: /etc changed 0x%x", flags); | |
8035 | if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0) | |
8036 | { | |
8037 | dispatch_source_cancel(etcsrc); | |
8038 | dispatch_release(etcsrc); | |
8039 | etcsrc = NULL; | |
8040 | dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); | |
8041 | return; | |
8042 | } | |
8043 | if ((flags & DISPATCH_VNODE_WRITE) != 0 && hostssrc == NULL) | |
8044 | { | |
8045 | mDNSMacOSXUpdateEtcHosts(m); | |
8046 | } | |
8047 | }); | |
8048 | dispatch_source_set_cancel_handler(etcsrc, ^{close(fd);}); | |
8049 | dispatch_resume(etcsrc); | |
8050 | ||
8051 | // Try and open /etc/hosts once more now that we're watching /etc, in case we missed the creation | |
8052 | fd = open("/etc/hosts", O_RDONLY | O_EVTONLY); | |
8053 | if (fd == -1) { LogMsg("mDNSMacOSXGetEtcHostsFD etc hosts does not exist, watching etc"); return -1; } | |
8054 | } | |
8055 | ||
8056 | // create a dispatch source to watch for changes to hosts file | |
8057 | hostssrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, | |
8058 | (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME | | |
8059 | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_LINK | DISPATCH_VNODE_REVOKE), etcq); | |
8060 | if (hostssrc == NULL) | |
8061 | { | |
8062 | close(fd); | |
8063 | return -1; | |
8064 | } | |
8065 | dispatch_source_set_event_handler(hostssrc, | |
8066 | ^{ | |
8067 | u_int32_t flags = dispatch_source_get_data(hostssrc); | |
8068 | LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts changed 0x%x", flags); | |
8069 | if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0) | |
8070 | { | |
8071 | dispatch_source_cancel(hostssrc); | |
8072 | dispatch_release(hostssrc); | |
8073 | hostssrc = NULL; | |
8074 | // Bug in LibDispatch: wait a second before scheduling the block. If we schedule | |
8075 | // the block immediately, we try to open the file and the file may not exist and may | |
8076 | // fail to get a notification in the future. When the file does not exist and | |
8077 | // we start to monitor the directory, on "dispatch_resume" of that source, there | |
8078 | // is no guarantee that the file creation will be notified always because when | |
8079 | // the dispatch_resume returns, the kevent manager may not have registered the | |
8080 | // kevent yet but the file may have been created | |
8081 | usleep(1000000); | |
8082 | dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); | |
8083 | return; | |
8084 | } | |
8085 | if ((flags & DISPATCH_VNODE_WRITE) != 0) | |
8086 | { | |
8087 | mDNSMacOSXUpdateEtcHosts(m); | |
8088 | } | |
8089 | }); | |
8090 | dispatch_source_set_cancel_handler(hostssrc, ^{LogInfo("mDNSMacOSXGetEtcHostsFD: Closing etchosts fd %d", fd); close(fd);}); | |
8091 | dispatch_resume(hostssrc); | |
8092 | ||
8093 | // Cleanup /etc source, no need to watch it if we already have /etc/hosts | |
8094 | if (etcsrc) | |
8095 | { | |
8096 | dispatch_source_cancel(etcsrc); | |
8097 | dispatch_release(etcsrc); | |
8098 | etcsrc = NULL; | |
8099 | } | |
8100 | ||
8101 | LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts being monitored, and not etc"); | |
8102 | return hostssrc ? (int)dispatch_source_get_handle(hostssrc) : -1; | |
8103 | #else | |
8104 | (void)m; | |
8105 | return fd; | |
4a95efb2 | 8106 | #endif |
294beb6e A |
8107 | } |
8108 | ||
8109 | // When /etc/hosts is modified, flush all the cache records as there may be local | |
8110 | // authoritative answers now | |
8111 | mDNSlocal void FlushAllCacheRecords(mDNS *const m) | |
8112 | { | |
8113 | CacheRecord *cr; | |
8114 | mDNSu32 slot; | |
8115 | CacheGroup *cg; | |
8116 | ||
8117 | FORALL_CACHERECORDS(slot, cg, cr) | |
8118 | { | |
8119 | // Skip multicast. | |
8120 | if (cr->resrec.InterfaceID) continue; | |
8121 | ||
8122 | // If a resource record can answer A or AAAA, they need to be flushed so that we will | |
8123 | // never used to deliver an ADD or RMV | |
8124 | if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) || | |
8125 | RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA)) | |
8126 | { | |
8127 | LogInfo("FlushAllCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr)); | |
8128 | mDNS_PurgeCacheResourceRecord(m, cr); | |
8129 | } | |
8130 | } | |
8131 | } | |
8132 | ||
8133 | // Add new entries to the core. If justCheck is set, this function does not add, just returns true | |
8134 | mDNSlocal mDNSBool EtcHostsAddNewEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck) | |
8135 | { | |
8136 | AuthGroup *ag; | |
8137 | mDNSu32 slot; | |
8138 | AuthRecord *rr, *primary, *rrnext; | |
8139 | for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) | |
8140 | for (ag = newhosts->rrauth_hash[slot]; ag; ag = ag->next) | |
8141 | { | |
8142 | primary = NULL; | |
8143 | for (rr = ag->members; rr; rr = rrnext) | |
8144 | { | |
8145 | rrnext = rr->next; | |
8146 | AuthGroup *ag1; | |
8147 | AuthRecord *rr1; | |
8148 | mDNSBool found = mDNSfalse; | |
8149 | ag1 = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec); | |
8150 | if (ag1 && ag1->members) | |
8151 | { | |
8152 | if (!primary) primary = ag1->members; | |
8153 | rr1 = ag1->members; | |
8154 | while (rr1) | |
8155 | { | |
8156 | // We are not using InterfaceID in checking for duplicates. This means, | |
8157 | // if there are two addresses for a given name e.g., fe80::1%en0 and | |
8158 | // fe80::1%en1, we only add the first one. It is not clear whether | |
8159 | // this is a common case. To fix this, we also need to modify | |
8160 | // mDNS_Register_internal in how it handles duplicates. If it becomes a | |
8161 | // common case, we will fix it then. | |
8162 | if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec)) | |
8163 | { | |
8164 | LogInfo("EtcHostsAddNewEntries: Skipping, not adding %s", ARDisplayString(m, rr1)); | |
8165 | found = mDNStrue; | |
8166 | break; | |
8167 | } | |
8168 | rr1 = rr1->next; | |
8169 | } | |
8170 | } | |
8171 | if (!found) | |
8172 | { | |
8173 | if (justCheck) | |
8174 | { | |
8175 | LogInfo("EtcHostsAddNewEntries: Entry %s not registered with core yet", ARDisplayString(m, rr)); | |
8176 | return mDNStrue; | |
8177 | } | |
8178 | RemoveAuthRecord(m, newhosts, rr); | |
8179 | // if there is no primary, point to self | |
8180 | rr->RRSet = (primary ? primary : rr); | |
8181 | rr->next = NULL; | |
8182 | LogInfo("EtcHostsAddNewEntries: Adding %s", ARDisplayString(m, rr)); | |
8183 | if (mDNS_Register_internal(m, rr) != mStatus_NoError) | |
8184 | LogMsg("EtcHostsAddNewEntries: mDNS_Register failed for %s", ARDisplayString(m, rr)); | |
8185 | } | |
8186 | } | |
8187 | } | |
8188 | return mDNSfalse; | |
8189 | } | |
8190 | ||
8191 | // Delete entries from the core that are no longer needed. If justCheck is set, this function | |
8192 | // does not delete, just returns true | |
8193 | mDNSlocal mDNSBool EtcHostsDeleteOldEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck) | |
8194 | { | |
8195 | AuthGroup *ag; | |
8196 | mDNSu32 slot; | |
8197 | AuthRecord *rr, *primary, *rrnext; | |
8198 | for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) | |
8199 | for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) | |
8200 | for (rr = ag->members; rr; rr = rrnext) | |
8201 | { | |
8202 | mDNSBool found = mDNSfalse; | |
8203 | AuthGroup *ag1; | |
8204 | AuthRecord *rr1; | |
8205 | rrnext = rr->next; | |
8206 | if (rr->RecordCallback != FreeEtcHosts) continue; | |
8207 | ag1 = AuthGroupForRecord(newhosts, slot, &rr->resrec); | |
8208 | if (ag1) | |
8209 | { | |
8210 | primary = rr1 = ag1->members; | |
8211 | while (rr1) | |
8212 | { | |
8213 | if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec)) | |
8214 | { | |
8215 | LogInfo("EtcHostsDeleteOldEntries: Old record %s found in new, skipping", ARDisplayString(m, rr)); | |
8216 | found = mDNStrue; | |
8217 | break; | |
8218 | } | |
8219 | rr1 = rr1->next; | |
8220 | } | |
8221 | } | |
8222 | // there is no corresponding record in newhosts for the same name. This means | |
8223 | // we should delete this from the core. | |
8224 | if (!found) | |
8225 | { | |
8226 | if (justCheck) | |
8227 | { | |
8228 | LogInfo("EtcHostsDeleteOldEntries: Record %s not found in new, deleting", ARDisplayString(m, rr)); | |
8229 | return mDNStrue; | |
8230 | } | |
8231 | // if primary is going away, make sure that the rest of the records | |
8232 | // point to the new primary | |
8233 | if (rr == ag->members) | |
8234 | { | |
8235 | AuthRecord *new_primary = rr->next; | |
8236 | AuthRecord *r = new_primary; | |
8237 | while (r) | |
8238 | { | |
8239 | if (r->RRSet == rr) | |
8240 | { | |
8241 | LogInfo("EtcHostsDeleteOldEntries: Updating Resource Record %s to primary", ARDisplayString(m, r)); | |
8242 | r->RRSet = new_primary; | |
8243 | } | |
8244 | else LogMsg("EtcHostsDeleteOldEntries: ERROR!! Resource Record %s not pointing to primary %##s", ARDisplayString(m, r), r->resrec.name); | |
8245 | r = r->next; | |
8246 | } | |
8247 | } | |
8248 | LogInfo("EtcHostsDeleteOldEntries: Deleting %s", ARDisplayString(m, rr)); | |
8249 | mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); | |
8250 | } | |
8251 | } | |
8252 | return mDNSfalse; | |
8253 | } | |
8254 | ||
8255 | mDNSlocal void UpdateEtcHosts(mDNS *const m, void *context) | |
8256 | { | |
8257 | AuthHash *newhosts = (AuthHash *)context; | |
8258 | ||
8259 | if (!m->mDNS_busy) LogMsg("UpdateEtcHosts: ERROR!! Lock not held"); | |
8260 | ||
8261 | //Delete old entries from the core if they are not present in the newhosts | |
8262 | EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse); | |
8263 | // Add the new entries to the core if not already present in the core | |
8264 | EtcHostsAddNewEntries(m, newhosts, mDNSfalse); | |
8265 | } | |
8266 | ||
8267 | mDNSlocal void FreeNewHosts(AuthHash *newhosts) | |
8268 | { | |
8269 | mDNSu32 slot; | |
8270 | AuthGroup *ag, *agnext; | |
8271 | AuthRecord *rr, *rrnext; | |
8272 | ||
8273 | for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) | |
8274 | for (ag = newhosts->rrauth_hash[slot]; ag; ag = agnext) | |
8275 | { | |
8276 | agnext = ag->next; | |
8277 | for (rr = ag->members; rr; rr = rrnext) | |
8278 | { | |
8279 | rrnext = rr->next; | |
8280 | freeL("etchosts", rr); | |
8281 | } | |
8282 | freeL("AuthGroups", ag); | |
8283 | } | |
8284 | } | |
8285 | ||
8286 | mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m) | |
8287 | { | |
8288 | AuthHash newhosts; | |
8289 | ||
8290 | // As we will be modifying the core, we can only have one thread running at | |
8291 | // any point in time. | |
8292 | KQueueLock(m); | |
8293 | ||
8294 | mDNSPlatformMemZero(&newhosts, sizeof(AuthHash)); | |
8295 | ||
8296 | // Get the file desecriptor (will trigger us to start watching for changes) | |
8297 | int fd = mDNSMacOSXGetEtcHostsFD(m); | |
8298 | if (fd != -1) | |
8299 | { | |
8300 | LogInfo("mDNSMacOSXUpdateEtcHosts: Parsing /etc/hosts fd %d", fd); | |
8301 | mDNSMacOSXParseEtcHosts(m, fd, &newhosts); | |
8302 | } | |
8303 | else LogInfo("mDNSMacOSXUpdateEtcHosts: /etc/hosts is not present"); | |
8304 | ||
8305 | // Optimization: Detect whether /etc/hosts changed or not. | |
8306 | // | |
8307 | // 1. Check to see if there are any new entries. We do this by seeing whether any entries in | |
8308 | // newhosts is already registered with core. If we find at least one entry that is not | |
8309 | // registered with core, then it means we have work to do. | |
8310 | // | |
8311 | // 2. Next, we check to see if any of the entries that are registered with core is not present | |
8312 | // in newhosts. If we find at least one entry that is not present, it means we have work to | |
8313 | // do. | |
8314 | // | |
8315 | // Note: We may not have to hold the lock right here as KQueueLock is held which prevents any | |
8316 | // other thread from running. But mDNS_Lock is needed here as we will be traversing the core | |
8317 | // data structure in EtcHostsDeleteOldEntries/NewEntries which might expect the lock to be held | |
8318 | // in the future and this code does not have to change. | |
8319 | mDNS_Lock(m); | |
8320 | // Add the new entries to the core if not already present in the core | |
8321 | if (!EtcHostsAddNewEntries(m, &newhosts, mDNStrue)) | |
8322 | { | |
8323 | // No new entries to add, check to see if we need to delete any old entries from the | |
8324 | // core if they are not present in the newhosts | |
8325 | if (!EtcHostsDeleteOldEntries(m, &newhosts, mDNStrue)) | |
8326 | { | |
8327 | LogInfo("mDNSMacOSXUpdateEtcHosts: No work"); | |
8328 | mDNS_Unlock(m); | |
8329 | KQueueUnlock(m, "/etc/hosts changed"); | |
8330 | FreeNewHosts(&newhosts); | |
8331 | return; | |
8332 | } | |
8333 | } | |
8334 | ||
8335 | // This will flush the cache, stop and start the query so that the queries | |
8336 | // can look at the /etc/hosts again | |
8337 | // | |
8338 | // Notes: | |
8339 | // | |
8340 | // We can't delete and free the records here. We wait for the mDNSCoreRestartAddressQueries to | |
8341 | // deliver RMV events. It has to be done in a deferred way because we can't deliver RMV | |
8342 | // events for local records *before* the RMV events for cache records. mDNSCoreRestartAddressQueries | |
8343 | // delivers these events in the right order and then calls us back to delete them. | |
8344 | // | |
8345 | // Similarly, we do a deferred Registration of the record because mDNSCoreRestartAddressQueries | |
8346 | // is a common function that looks at all local auth records and delivers a RMV including | |
8347 | // the records that we might add here. If we deliver a ADD here, it will get a RMV and then when | |
8348 | // the query is restarted, it will get another ADD. To avoid this (ADD-RMV-ADD), we defer registering | |
8349 | // the record until the RMVs are delivered in mDNSCoreRestartAddressQueries after which UpdateEtcHosts | |
8350 | // is called back where we do the Registration of the record. This results in RMV followed by ADD which | |
8351 | // looks normal. | |
8352 | mDNSCoreRestartAddressQueries(m, mDNSfalse, FlushAllCacheRecords, UpdateEtcHosts, &newhosts); | |
8353 | mDNS_Unlock(m); | |
8354 | ||
8355 | KQueueUnlock(m, "/etc/hosts changed"); | |
8356 | FreeNewHosts(&newhosts); | |
8357 | } | |
8358 | ||
67c8f8a1 A |
8359 | #if COMPILER_LIKES_PRAGMA_MARK |
8360 | #pragma mark - | |
8361 | #pragma mark - Initialization & Teardown | |
8362 | #endif | |
c9b9ae52 A |
8363 | |
8364 | CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); | |
8365 | CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; | |
8366 | CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; | |
8e92c31c | 8367 | CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; |
c9b9ae52 | 8368 | |
32bb7e43 A |
8369 | // Major version 6 is 10.2.x (Jaguar) |
8370 | // Major version 7 is 10.3.x (Panther) | |
8371 | // Major version 8 is 10.4.x (Tiger) | |
8372 | // Major version 9 is 10.5.x (Leopard) | |
8373 | // Major version 10 is 10.6.x (SnowLeopard) | |
294beb6e | 8374 | mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) |
c9b9ae52 A |
8375 | { |
8376 | int major = 0, minor = 0; | |
67c8f8a1 | 8377 | char letter = 0, prodname[256]="<Unknown>", prodvers[256]="<Unknown>", buildver[256]="<Unknown>"; |
c9b9ae52 A |
8378 | CFDictionaryRef vers = _CFCopySystemVersionDictionary(); |
8379 | if (vers) | |
8380 | { | |
8381 | CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey); | |
8382 | CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey); | |
8383 | CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); | |
8384 | if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); | |
8385 | if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); | |
32bb7e43 A |
8386 | if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8)) |
8387 | sscanf(buildver, "%d%c%d", &major, &letter, &minor); | |
c9b9ae52 A |
8388 | CFRelease(vers); |
8389 | } | |
67c8f8a1 | 8390 | if (!major) { major=8; LogMsg("Note: No Major Build Version number found; assuming 8"); } |
5e65c77f | 8391 | if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion)); |
294beb6e A |
8392 | //LogMsg("%s %s (%s), %d %c %d", prodname, prodvers, buildver, major, letter, minor); |
8393 | ||
8394 | // If product name is "Mac OS X" (or similar) we set OSXVers, else we set iOSVers; | |
8395 | if ((prodname[0] & 0xDF) == 'M') OSXVers = major; else iOSVers = major; | |
c9b9ae52 A |
8396 | } |
8397 | ||
8e92c31c A |
8398 | // Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. |
8399 | // If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- | |
8400 | // we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. | |
7f0064bd | 8401 | mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void) |
c9b9ae52 | 8402 | { |
7f0064bd | 8403 | int err = -1; |
8e92c31c | 8404 | int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
7f0064bd A |
8405 | if (s < 3) |
8406 | LogMsg("mDNSPlatformInit_CanReceiveUnicast: socket error %d errno %d (%s)", s, errno, strerror(errno)); | |
8407 | else | |
8e92c31c | 8408 | { |
7f0064bd A |
8409 | struct sockaddr_in s5353; |
8410 | s5353.sin_family = AF_INET; | |
8411 | s5353.sin_port = MulticastDNSPort.NotAnInteger; | |
8412 | s5353.sin_addr.s_addr = 0; | |
8413 | err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); | |
8414 | close(s); | |
8e92c31c | 8415 | } |
8e92c31c | 8416 | |
7f0064bd A |
8417 | if (err) LogMsg("No unicast UDP responses"); |
8418 | else debugf("Unicast UDP responses okay"); | |
8419 | return(err == 0); | |
8e92c31c A |
8420 | } |
8421 | ||
4a95efb2 A |
8422 | mDNSlocal void CreatePTRRecord(mDNS *const m, const domainname *domain) |
8423 | { | |
8424 | AuthRecord *rr; | |
8425 | const domainname *pname = (domainname *)"\x9""localhost"; | |
8426 | ||
8427 | rr= mallocL("localhosts", sizeof(*rr)); | |
8428 | if (rr == NULL) return; | |
8429 | mDNSPlatformMemZero(rr, sizeof(*rr)); | |
8430 | ||
8431 | mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, mDNSNULL, mDNSNULL); | |
8432 | AssignDomainName(&rr->namestorage, domain); | |
8433 | ||
8434 | rr->resrec.rdlength = DomainNameLength(pname); | |
8435 | rr->resrec.rdata->u.name.c[0] = 0; | |
8436 | AssignDomainName(&rr->resrec.rdata->u.name, pname); | |
8437 | ||
8438 | rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); | |
8439 | SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us | |
8440 | mDNS_Register(m, rr); | |
8441 | } | |
8442 | ||
8443 | // Setup PTR records for 127.0.0.1 and ::1. This helps answering them locally rather than relying | |
8444 | // on the external DNS server to answer this. Sometimes, the DNS servers don't respond in a timely | |
8445 | // fashion and applications depending on this e.g., telnetd, times out after 30 seconds creating | |
8446 | // a bad user experience. For now, we specifically create only localhosts to handle radar://9354225 | |
8447 | // | |
8448 | // Note: We could have set this up while parsing the entries in /etc/hosts. But this is kept separate | |
8449 | // intentionally to avoid adding to the complexity of code handling /etc/hosts. | |
8450 | mDNSlocal void SetupLocalHostRecords(mDNS *const m) | |
8451 | { | |
8452 | char buffer[MAX_REVERSE_MAPPING_NAME]; | |
8453 | domainname name; | |
8454 | int i; | |
8455 | struct in6_addr addr; | |
8456 | mDNSu8 *ptr = addr.__u6_addr.__u6_addr8; | |
8457 | ||
8458 | if (inet_pton(AF_INET, "127.0.0.1", &addr) == 1) | |
8459 | { | |
8460 | mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", | |
8461 | ptr[3], ptr[2], ptr[1], ptr[0]); | |
8462 | MakeDomainNameFromDNSNameString(&name, buffer); | |
8463 | CreatePTRRecord(m, &name); | |
8464 | } | |
8465 | else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET failed"); | |
8466 | ||
8467 | if (inet_pton(AF_INET6, "::1", &addr) == 1) | |
8468 | { | |
8469 | for (i = 0; i < 16; i++) | |
8470 | { | |
8471 | static const char hexValues[] = "0123456789ABCDEF"; | |
8472 | buffer[i * 4 ] = hexValues[ptr[15 - i] & 0x0F]; | |
8473 | buffer[i * 4 + 1] = '.'; | |
8474 | buffer[i * 4 + 2] = hexValues[ptr[15 - i] >> 4]; | |
8475 | buffer[i * 4 + 3] = '.'; | |
8476 | } | |
8477 | mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); | |
8478 | MakeDomainNameFromDNSNameString(&name, buffer); | |
8479 | CreatePTRRecord(m, &name); | |
8480 | } | |
8481 | else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET6 failed"); | |
8482 | } | |
8483 | ||
8e92c31c | 8484 | // Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows: |
283ee3ff | 8485 | // 1) query for b._dns-sd._udp.local on LocalOnly interface |
8e92c31c | 8486 | // (.local manually generated via explicit callback) |
283ee3ff A |
8487 | // 2) for each search domain (from prefs pane), query for b._dns-sd._udp.<searchdomain>. |
8488 | // 3) for each result from (2), register LocalOnly PTR record b._dns-sd._udp.local. -> <result> | |
8e92c31c A |
8489 | // 4) result above should generate a callback from question in (1). result added to global list |
8490 | // 5) global list delivered to client via GetSearchDomainList() | |
8491 | // 6) client calls to enumerate domains now go over LocalOnly interface | |
8492 | // (!!!KRS may add outgoing interface in addition) | |
8493 | ||
67c8f8a1 | 8494 | mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) |
8e92c31c A |
8495 | { |
8496 | mStatus err; | |
32bb7e43 A |
8497 | m->p->CFRunLoop = CFRunLoopGetCurrent(); |
8498 | ||
8499 | char HINFO_SWstring[256] = ""; | |
294beb6e | 8500 | mDNSMacOSXSystemBuildNumber(HINFO_SWstring); |
7f0064bd | 8501 | |
283ee3ff A |
8502 | // In 10.4, mDNSResponder is launched very early in the boot process, while other subsystems are still in the process of starting up. |
8503 | // 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. | |
8504 | int i; | |
8505 | for (i=0; i<100; i++) | |
8506 | { | |
8507 | domainlabel testlabel; | |
8508 | testlabel.c[0] = 0; | |
8509 | GetUserSpecifiedLocalHostName(&testlabel); | |
8510 | if (testlabel.c[0]) break; | |
8511 | usleep(50000); | |
8512 | } | |
8513 | ||
67c8f8a1 | 8514 | m->hostlabel.c[0] = 0; |
8e92c31c | 8515 | |
c9b9ae52 A |
8516 | int get_model[2] = { CTL_HW, HW_MODEL }; |
8517 | size_t len_model = sizeof(HINFO_HWstring_buffer); | |
263eeeab A |
8518 | |
8519 | // Normal Apple model names are of the form "iPhone2,1", and | |
8520 | // internal code names are strings containing no commas, e.g. "N88AP". | |
8521 | // We used to ignore internal code names, but Apple now uses these internal code names | |
8522 | // even in released shipping products, so we no longer ignore strings containing no commas. | |
8523 | // if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0 && strchr(HINFO_HWstring_buffer, ',')) | |
8524 | if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0) | |
c9b9ae52 A |
8525 | HINFO_HWstring = HINFO_HWstring_buffer; |
8526 | ||
294beb6e A |
8527 | // For names of the form "iPhone2,1" we use "iPhone" as the prefix for automatic name generation. |
8528 | // For names of the form "N88AP" containg no comma, we use the entire string. | |
8529 | HINFO_HWstring_prefixlen = strchr(HINFO_HWstring_buffer, ',') ? strcspn(HINFO_HWstring, "0123456789") : strlen(HINFO_HWstring); | |
8530 | ||
8531 | if (OSXVers && OSXVers <= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LimitedIPv6; | |
8532 | if (OSXVers && OSXVers >= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LossySyslog; | |
32bb7e43 | 8533 | if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; |
c9b9ae52 A |
8534 | |
8535 | mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); | |
8536 | mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); | |
8537 | if (hlen + slen < 254) | |
8538 | { | |
8539 | m->HIHardware.c[0] = hlen; | |
8540 | m->HISoftware.c[0] = slen; | |
67c8f8a1 A |
8541 | mDNSPlatformMemCopy(&m->HIHardware.c[1], HINFO_HWstring, hlen); |
8542 | mDNSPlatformMemCopy(&m->HISoftware.c[1], HINFO_SWstring, slen); | |
c9b9ae52 A |
8543 | } |
8544 | ||
9f29194f | 8545 | m->p->permanentsockets.port = MulticastDNSPort; |
67c8f8a1 | 8546 | m->p->permanentsockets.m = m; |
263eeeab A |
8547 | m->p->permanentsockets.sktv4 = -1; |
8548 | m->p->permanentsockets.kqsv4.KQcallback = myKQSocketCallBack; | |
8549 | m->p->permanentsockets.kqsv4.KQcontext = &m->p->permanentsockets; | |
8550 | m->p->permanentsockets.kqsv4.KQtask = "UDP packet reception"; | |
8551 | #ifndef NO_IPV6 | |
8552 | m->p->permanentsockets.sktv6 = -1; | |
8553 | m->p->permanentsockets.kqsv6.KQcallback = myKQSocketCallBack; | |
8554 | m->p->permanentsockets.kqsv6.KQcontext = &m->p->permanentsockets; | |
8555 | m->p->permanentsockets.kqsv6.KQtask = "UDP packet reception"; | |
8556 | #endif | |
67c8f8a1 | 8557 | |
030b743d | 8558 | err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL); |
67c8f8a1 | 8559 | #ifndef NO_IPV6 |
030b743d | 8560 | err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL); |
67c8f8a1 | 8561 | #endif |
7f0064bd A |
8562 | |
8563 | struct sockaddr_in s4; | |
05292456 | 8564 | socklen_t n4 = sizeof(s4); |
67c8f8a1 | 8565 | if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno)); |
7f0064bd | 8566 | else m->UnicastPort4.NotAnInteger = s4.sin_port; |
67c8f8a1 A |
8567 | #ifndef NO_IPV6 |
8568 | if (m->p->permanentsockets.sktv6 >= 0) | |
8569 | { | |
8570 | struct sockaddr_in6 s6; | |
8571 | socklen_t n6 = sizeof(s6); | |
8572 | if (getsockname(m->p->permanentsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno)); | |
8573 | else m->UnicastPort6.NotAnInteger = s6.sin6_port; | |
8574 | } | |
8575 | #endif | |
8e92c31c | 8576 | |
c9b9ae52 A |
8577 | m->p->InterfaceList = mDNSNULL; |
8578 | m->p->userhostlabel.c[0] = 0; | |
7f0064bd | 8579 | m->p->usernicelabel.c[0] = 0; |
263eeeab A |
8580 | m->p->prevoldnicelabel.c[0] = 0; |
8581 | m->p->prevnewnicelabel.c[0] = 0; | |
8582 | m->p->prevoldhostlabel.c[0] = 0; | |
8583 | m->p->prevnewhostlabel.c[0] = 0; | |
7f0064bd | 8584 | m->p->NotifyUser = 0; |
294beb6e | 8585 | m->p->KeyChainTimer = 0; |
32bb7e43 A |
8586 | m->p->WakeAtUTC = 0; |
8587 | m->p->RequestReSleep = 0; | |
8588 | ||
8589 | #if APPLE_OSX_mDNSResponder | |
8590 | uuid_generate(m->asl_uuid); | |
8591 | #endif | |
67c8f8a1 A |
8592 | |
8593 | m->AutoTunnelHostAddr.b[0] = 0; // Zero out AutoTunnelHostAddr so UpdateInterfaceList() know it has to set it up | |
3c427d54 A |
8594 | m->AutoTunnelRelayAddrIn = zerov6Addr; |
8595 | m->AutoTunnelRelayAddrOut = zerov6Addr; | |
67c8f8a1 | 8596 | |
67c8f8a1 A |
8597 | NetworkChangedKey_IPv4 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); |
8598 | NetworkChangedKey_IPv6 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); | |
8599 | NetworkChangedKey_Hostnames = SCDynamicStoreKeyCreateHostNames(NULL); | |
8600 | NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL); | |
8601 | NetworkChangedKey_DNS = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); | |
8602 | if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS) | |
8603 | { LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr); } | |
8604 | ||
c9b9ae52 | 8605 | err = WatchForNetworkChanges(m); |
67c8f8a1 A |
8606 | if (err) { LogMsg("mDNSPlatformInit_setup: WatchForNetworkChanges failed %d", err); return(err); } |
8607 | ||
32bb7e43 A |
8608 | #if 0 // <rdar://problem/6751656> |
8609 | err = WatchForPMChanges(m); | |
8610 | if (err) { LogMsg("mDNSPlatformInit_setup: WatchForPMChanges failed %d", err); return(err); } | |
8611 | #endif | |
8612 | ||
8613 | err = WatchForSysEvents(m); | |
8614 | if (err) { LogMsg("mDNSPlatformInit_setup: WatchForSysEvents failed %d", err); return(err); } | |
8615 | ||
8616 | mDNSs32 utc = mDNSPlatformUTC(); | |
8617 | m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); | |
8618 | UpdateInterfaceList(m, utc); | |
8619 | SetupActiveInterfaces(m, utc); | |
8620 | ||
67c8f8a1 A |
8621 | // Explicitly ensure that our Keychain operations utilize the system domain. |
8622 | #ifndef NO_SECURITYFRAMEWORK | |
8623 | SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); | |
8624 | #endif | |
8625 | ||
8626 | mDNS_Lock(m); | |
8627 | SetDomainSecrets(m); | |
8628 | SetLocalDomains(); | |
8629 | mDNS_Unlock(m); | |
8630 | ||
8631 | #ifndef NO_SECURITYFRAMEWORK | |
8632 | err = SecKeychainAddCallback(KeychainChanged, kSecAddEventMask|kSecDeleteEventMask|kSecUpdateEventMask, m); | |
8633 | if (err) { LogMsg("mDNSPlatformInit_setup: SecKeychainAddCallback failed %d", err); return(err); } | |
8634 | #endif | |
8e92c31c | 8635 | |
263eeeab | 8636 | #if !defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) || TARGET_OS_EMBEDDED |
32bb7e43 A |
8637 | LogMsg("Note: Compiled without SnowLeopard Fine-Grained Power Management support"); |
8638 | #else | |
8639 | IOPMConnection c; | |
8640 | IOReturn iopmerr = IOPMConnectionCreate(CFSTR("mDNSResponder"), kIOPMSystemPowerStateCapabilityCPU, &c); | |
8641 | if (iopmerr) LogMsg("IOPMConnectionCreate failed %d", iopmerr); | |
8642 | else | |
8643 | { | |
8644 | iopmerr = IOPMConnectionSetNotification(c, m, SnowLeopardPowerChanged); | |
8645 | if (iopmerr) LogMsg("IOPMConnectionSetNotification failed %d", iopmerr); | |
8646 | else | |
8647 | { | |
8648 | iopmerr = IOPMConnectionScheduleWithRunLoop(c, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); | |
8649 | if (iopmerr) LogMsg("IOPMConnectionScheduleWithRunLoop failed %d", iopmerr); | |
8650 | } | |
8651 | } | |
8652 | m->p->IOPMConnection = iopmerr ? mDNSNULL : c; | |
8653 | if (iopmerr) // If IOPMConnectionCreate unavailable or failed, proceed with old-style power notification code below | |
8654 | #endif // kIOPMAcknowledgmentOptionSystemCapabilityRequirements | |
8655 | { | |
8656 | m->p->PowerConnection = IORegisterForSystemPower(m, &m->p->PowerPortRef, PowerChanged, &m->p->PowerNotifier); | |
8657 | if (!m->p->PowerConnection) { LogMsg("mDNSPlatformInit_setup: IORegisterForSystemPower failed"); return(-1); } | |
263eeeab A |
8658 | else |
8659 | { | |
294beb6e | 8660 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
8661 | IONotificationPortSetDispatchQueue(m->p->PowerPortRef, dispatch_get_main_queue()); |
8662 | #else | |
8663 | CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); | |
294beb6e | 8664 | #endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */ |
263eeeab | 8665 | } |
32bb7e43 | 8666 | } |
8e92c31c | 8667 | |
32bb7e43 A |
8668 | #if APPLE_OSX_mDNSResponder |
8669 | // Note: We use SPMetricPortability > 35 to indicate a laptop of some kind | |
8670 | // SPMetricPortability <= 35 means nominally a non-portable machine (i.e. Mac mini or better) | |
263eeeab A |
8671 | // Apple TVs, AirPort base stations, and Time Capsules do not actually weigh 3kg, but we assign them |
8672 | // higher 'nominal' masses to indicate they should be treated as being relatively less portable than a laptop | |
8673 | if (!strncasecmp(HINFO_HWstring, "Xserve", 6)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } | |
8674 | else if (!strncasecmp(HINFO_HWstring, "RackMac", 7)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } | |
8675 | else if (!strncasecmp(HINFO_HWstring, "MacPro", 6)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } | |
8676 | else if (!strncasecmp(HINFO_HWstring, "PowerMac", 8)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 82 /* 160W */; SPMetricTotalPower = 83 /* 200W */; } | |
8677 | else if (!strncasecmp(HINFO_HWstring, "iMac", 4)) { SPMetricPortability = 30 /* 10kg */; SPMetricMarginalPower = 77 /* 50W */; SPMetricTotalPower = 78 /* 60W */; } | |
8678 | else if (!strncasecmp(HINFO_HWstring, "Macmini", 7)) { SPMetricPortability = 33 /* 5kg */; SPMetricMarginalPower = 73 /* 20W */; SPMetricTotalPower = 74 /* 25W */; } | |
8679 | else if (!strncasecmp(HINFO_HWstring, "TimeCapsule", 11)) { SPMetricPortability = 34 /* 4kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 13W */; } | |
8680 | else if (!strncasecmp(HINFO_HWstring, "AirPort", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 12W */; } | |
8681 | else if (!strncasecmp(HINFO_HWstring, "AppleTV", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 73 /* 20W */; } | |
294beb6e | 8682 | else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 60 /* 1W */; SPMetricTotalPower = 63 /* 2W */; } |
263eeeab A |
8683 | else if (!strncasecmp(HINFO_HWstring, "MacBook", 7)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } |
8684 | else if (!strncasecmp(HINFO_HWstring, "PowerBook", 9)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } | |
32bb7e43 A |
8685 | LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d", |
8686 | HINFO_HWstring_prefixlen, HINFO_HWstring, HINFO_HWstring, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower); | |
8687 | ||
8688 | err = WatchForInternetSharingChanges(m); | |
8689 | if (err) { LogMsg("WatchForInternetSharingChanges failed %d", err); return(err); } | |
263eeeab A |
8690 | |
8691 | m->p->ConndBTMMDict = mDNSNULL; | |
32bb7e43 A |
8692 | #endif // APPLE_OSX_mDNSResponder |
8693 | ||
294beb6e | 8694 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
8695 | // Currently this is not defined. SSL code will eventually fix this. If it becomes |
8696 | // critical, we will define this to workaround the bug in SSL. | |
8697 | #ifdef __SSL_NEEDS_SERIALIZATION__ | |
8698 | SSLqueue = dispatch_queue_create("com.apple.mDNSResponder.SSLQueue", NULL); | |
8699 | #else | |
8700 | SSLqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); | |
8701 | #endif | |
8702 | if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL"); | |
8703 | #endif | |
294beb6e | 8704 | mDNSMacOSXUpdateEtcHosts(m); |
4a95efb2 | 8705 | SetupLocalHostRecords(m); |
67c8f8a1 | 8706 | return(mStatus_NoError); |
c9b9ae52 A |
8707 | } |
8708 | ||
8709 | mDNSexport mStatus mDNSPlatformInit(mDNS *const m) | |
8710 | { | |
67c8f8a1 A |
8711 | #if MDNS_NO_DNSINFO |
8712 | LogMsg("Note: Compiled without Apple-specific Split-DNS support"); | |
8713 | #endif | |
8714 | ||
32bb7e43 A |
8715 | // Adding interfaces will use this flag, so set it now. |
8716 | m->DivertMulticastAdvertisements = !m->AdvertiseLocalAddresses; | |
8717 | ||
8718 | #if APPLE_OSX_mDNSResponder | |
8719 | m->SPSBrowseCallback = UpdateSPSStatus; | |
263eeeab | 8720 | #endif // APPLE_OSX_mDNSResponder |
32bb7e43 | 8721 | |
c9b9ae52 | 8722 | mStatus result = mDNSPlatformInit_setup(m); |
67c8f8a1 | 8723 | |
c9b9ae52 A |
8724 | // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already |
8725 | // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately | |
263eeeab A |
8726 | if (result == mStatus_NoError) |
8727 | { | |
8728 | mDNSCoreInitComplete(m, mStatus_NoError); | |
8729 | ||
8730 | #if ! NO_D2D | |
8731 | // We only initialize if mDNSCore successfully initialized. | |
8732 | CHECK_D2D_FUNCTION(D2DInitialize) | |
8733 | { | |
8734 | D2DStatus ds = D2DInitialize(m->p->CFRunLoop, xD2DServiceCallback, m) ; | |
8735 | if (ds != kD2DSuccess) | |
8736 | LogMsg("D2DInitialiize failed: %d", ds); | |
8737 | else | |
8738 | LogMsg("D2DInitialize succeeded"); | |
8739 | } | |
8740 | #endif // ! NO_D2D | |
8741 | ||
8742 | } | |
c9b9ae52 A |
8743 | return(result); |
8744 | } | |
8745 | ||
8746 | mDNSexport void mDNSPlatformClose(mDNS *const m) | |
8747 | { | |
8748 | if (m->p->PowerConnection) | |
8749 | { | |
294beb6e | 8750 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
8751 | IONotificationPortSetDispatchQueue(m->p->PowerPortRef, NULL); |
8752 | #else | |
67c8f8a1 | 8753 | CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); |
263eeeab | 8754 | #endif |
67c8f8a1 A |
8755 | // According to <http://developer.apple.com/qa/qa2004/qa1340.html>, a single call |
8756 | // to IORegisterForSystemPower creates *three* objects that need to be disposed individually: | |
c9b9ae52 | 8757 | IODeregisterForSystemPower(&m->p->PowerNotifier); |
67c8f8a1 A |
8758 | IOServiceClose ( m->p->PowerConnection); |
8759 | IONotificationPortDestroy ( m->p->PowerPortRef); | |
7f0064bd | 8760 | m->p->PowerConnection = 0; |
c9b9ae52 | 8761 | } |
67c8f8a1 | 8762 | |
c9b9ae52 A |
8763 | if (m->p->Store) |
8764 | { | |
294beb6e | 8765 | #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM |
263eeeab A |
8766 | if (!SCDynamicStoreSetDispatchQueue(m->p->Store, NULL)) |
8767 | LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed"); | |
8768 | #else | |
c9b9ae52 A |
8769 | CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); |
8770 | CFRunLoopSourceInvalidate(m->p->StoreRLS); | |
8771 | CFRelease(m->p->StoreRLS); | |
263eeeab A |
8772 | m->p->StoreRLS = NULL; |
8773 | #endif | |
c9b9ae52 A |
8774 | CFRelease(m->p->Store); |
8775 | m->p->Store = NULL; | |
c9b9ae52 | 8776 | } |
32bb7e43 A |
8777 | |
8778 | if (m->p->PMRLS) | |
8779 | { | |
8780 | CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode); | |
8781 | CFRunLoopSourceInvalidate(m->p->PMRLS); | |
8782 | CFRelease(m->p->PMRLS); | |
8783 | m->p->PMRLS = NULL; | |
8784 | } | |
8785 | ||
8786 | if (m->p->SysEventNotifier >= 0) { close(m->p->SysEventNotifier); m->p->SysEventNotifier = -1; } | |
67c8f8a1 | 8787 | |
263eeeab A |
8788 | #if ! NO_D2D |
8789 | CHECK_D2D_FUNCTION(D2DTerminate) | |
8790 | { | |
8791 | D2DStatus ds = D2DTerminate(); | |
8792 | if (ds != kD2DSuccess) | |
8793 | LogMsg("D2DTerminate failed: %d", ds); | |
8794 | else | |
8795 | LogMsg("D2DTerminate succeeded"); | |
8796 | } | |
8797 | #endif // ! NO_D2D | |
8798 | ||
283ee3ff A |
8799 | mDNSs32 utc = mDNSPlatformUTC(); |
8800 | MarkAllInterfacesInactive(m, utc); | |
8801 | ClearInactiveInterfaces(m, utc); | |
67c8f8a1 A |
8802 | CloseSocketSet(&m->p->permanentsockets); |
8803 | ||
32bb7e43 | 8804 | #if APPLE_OSX_mDNSResponder |
5e65c77f A |
8805 | // clean up tunnels |
8806 | while (m->TunnelClients) | |
8807 | { | |
8808 | ClientTunnel *cur = m->TunnelClients; | |
32bb7e43 | 8809 | LogInfo("mDNSPlatformClose: removing client tunnel %p %##s from list", cur, cur->dstname.c); |
5e65c77f A |
8810 | if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q); |
8811 | AutoTunnelSetKeys(cur, mDNSfalse); | |
8812 | m->TunnelClients = cur->next; | |
8813 | freeL("ClientTunnel", cur); | |
8814 | } | |
8815 | ||
8816 | if (AnonymousRacoonConfig) | |
8817 | { | |
32bb7e43 A |
8818 | AnonymousRacoonConfig = mDNSNULL; |
8819 | LogInfo("mDNSPlatformClose: Deconfiguring autotunnel"); | |
294beb6e | 8820 | (void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL); |
5e65c77f A |
8821 | } |
8822 | ||
67c8f8a1 A |
8823 | if (m->AutoTunnelHostAddrActive && m->AutoTunnelHostAddr.b[0]) |
8824 | { | |
8825 | m->AutoTunnelHostAddrActive = mDNSfalse; | |
32bb7e43 | 8826 | LogInfo("mDNSPlatformClose: Removing AutoTunnel address %.16a", &m->AutoTunnelHostAddr); |
67c8f8a1 A |
8827 | (void)mDNSAutoTunnelInterfaceUpDown(kmDNSDown, m->AutoTunnelHostAddr.b); |
8828 | } | |
263eeeab | 8829 | if (m->p->ConndBTMMDict) CFRelease(m->p->ConndBTMMDict); |
32bb7e43 | 8830 | #endif // APPLE_OSX_mDNSResponder |
c9b9ae52 A |
8831 | } |
8832 | ||
67c8f8a1 A |
8833 | #if COMPILER_LIKES_PRAGMA_MARK |
8834 | #pragma mark - | |
8835 | #pragma mark - General Platform Support Layer functions | |
8836 | #endif | |
8837 | ||
32bb7e43 | 8838 | mDNSexport mDNSu32 mDNSPlatformRandomNumber(void) |
7f0064bd | 8839 | { |
32bb7e43 | 8840 | return(arc4random()); |
7f0064bd A |
8841 | } |
8842 | ||
8843 | mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; | |
5e65c77f | 8844 | mDNSexport mDNSu32 mDNSPlatformClockDivisor = 0; |
c9b9ae52 | 8845 | |
7f0064bd | 8846 | mDNSexport mStatus mDNSPlatformTimeInit(void) |
c9b9ae52 A |
8847 | { |
8848 | // Notes: Typical values for mach_timebase_info: | |
8849 | // tbi.numer = 1000 million | |
8850 | // tbi.denom = 33 million | |
8851 | // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds; | |
8852 | // numer / denom = nanoseconds per hardware clock tick (e.g. 30); | |
8853 | // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033) | |
8854 | // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333) | |
8855 | // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds | |
8856 | // | |
8857 | // Arithmetic notes: | |
8858 | // tbi.denom is at least 1, and not more than 2^32-1. | |
8859 | // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t. | |
8860 | // tbi.denom is at least 1, and not more than 2^32-1. | |
8861 | // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9. | |
8862 | // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz, | |
8863 | // which is unlikely on any current or future Macintosh. | |
8864 | // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz. | |
8865 | // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code. | |
8866 | struct mach_timebase_info tbi; | |
8867 | kern_return_t result = mach_timebase_info(&tbi); | |
5e65c77f | 8868 | if (result == KERN_SUCCESS) mDNSPlatformClockDivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer; |
7f0064bd | 8869 | return(result); |
c9b9ae52 A |
8870 | } |
8871 | ||
7f0064bd | 8872 | mDNSexport mDNSs32 mDNSPlatformRawTime(void) |
c9b9ae52 | 8873 | { |
5e65c77f | 8874 | if (mDNSPlatformClockDivisor == 0) { LogMsg("mDNSPlatformRawTime called before mDNSPlatformTimeInit"); return(0); } |
7f0064bd A |
8875 | |
8876 | static uint64_t last_mach_absolute_time = 0; | |
67c8f8a1 | 8877 | //static uint64_t last_mach_absolute_time = 0x8000000000000000LL; // Use this value for testing the alert display |
7f0064bd A |
8878 | uint64_t this_mach_absolute_time = mach_absolute_time(); |
8879 | if ((int64_t)this_mach_absolute_time - (int64_t)last_mach_absolute_time < 0) | |
8880 | { | |
8881 | LogMsg("mDNSPlatformRawTime: last_mach_absolute_time %08X%08X", last_mach_absolute_time); | |
8882 | LogMsg("mDNSPlatformRawTime: this_mach_absolute_time %08X%08X", this_mach_absolute_time); | |
8883 | // Update last_mach_absolute_time *before* calling NotifyOfElusiveBug() | |
8884 | last_mach_absolute_time = this_mach_absolute_time; | |
294beb6e A |
8885 | // Note: This bug happens all the time on 10.3 |
8886 | NotifyOfElusiveBug("mach_absolute_time went backwards!", | |
8887 | "This error occurs from time to time, often on newly released hardware, " | |
8888 | "and usually the exact cause is different in each instance.\r\r" | |
8889 | "Please file a new Radar bug report with the title “mach_absolute_time went backwards” " | |
8890 | "and assign it to Radar Component “Kernel” Version “X”."); | |
7f0064bd A |
8891 | } |
8892 | last_mach_absolute_time = this_mach_absolute_time; | |
8893 | ||
5e65c77f | 8894 | return((mDNSs32)(this_mach_absolute_time / mDNSPlatformClockDivisor)); |
c9b9ae52 A |
8895 | } |
8896 | ||
8e92c31c A |
8897 | mDNSexport mDNSs32 mDNSPlatformUTC(void) |
8898 | { | |
8899 | return time(NULL); | |
8900 | } | |
8901 | ||
c9b9ae52 A |
8902 | // Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves |
8903 | mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; } | |
8904 | mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; } | |
67c8f8a1 A |
8905 | mDNSexport void mDNSPlatformStrCopy( void *dst, const void *src) { strcpy((char *)dst, (char *)src); } |
8906 | mDNSexport mDNSu32 mDNSPlatformStrLen ( const void *src) { return(strlen((char*)src)); } | |
8907 | mDNSexport void mDNSPlatformMemCopy( void *dst, const void *src, mDNSu32 len) { memcpy(dst, src, len); } | |
8908 | mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len) { return(memcmp(dst, src, len) == 0); } | |
32bb7e43 | 8909 | mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) { memset(dst, 0, len); } |
67c8f8a1 | 8910 | #if !(APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING) |
c9b9ae52 | 8911 | mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); } |
67c8f8a1 | 8912 | #endif |
c9b9ae52 | 8913 | mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); } |
263eeeab | 8914 | |
294beb6e | 8915 | mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason) |
263eeeab A |
8916 | { |
8917 | if (allowSleep && m->p->IOPMAssertion) | |
8918 | { | |
8919 | LogInfo("%s Destroying NoIdleSleep power assertion", __FUNCTION__); | |
8920 | IOPMAssertionRelease(m->p->IOPMAssertion); | |
8921 | m->p->IOPMAssertion = 0; | |
8922 | } | |
8923 | else if (!allowSleep && m->p->IOPMAssertion == 0) | |
8924 | { | |
294beb6e A |
8925 | #ifdef kIOPMAssertionTypeNoIdleSleep |
8926 | CFStringRef assertionName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%d %s"), getprogname(), getpid(), reason ? reason : ""); | |
8927 | IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, assertionName ? assertionName : CFSTR("mDNSResponder"), &m->p->IOPMAssertion); | |
8928 | if (assertionName) CFRelease(assertionName); | |
263eeeab | 8929 | LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__); |
294beb6e | 8930 | #endif |
263eeeab A |
8931 | } |
8932 | } | |
1f519c61 A |
8933 | |
8934 | mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration) | |
8935 | { | |
8936 | mDNSu32 ifindex; | |
8937 | ||
8938 | // Sanity check | |
294beb6e | 8939 | ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue); |
1f519c61 A |
8940 | if (ifindex <= 0) |
8941 | { | |
8942 | LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex); | |
8943 | return; | |
8944 | } | |
8945 | mDNSSendWakeupPacket(ifindex, EthAddr, IPAddr, iteration); | |
8946 | } | |
294beb6e A |
8947 | |
8948 | // Called for rr->InterfaceID == mDNSInterface_Any. | |
8949 | // If current interface is P2P, verify that record is marked to IncludeP2P. | |
8950 | mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf) | |
8951 | { | |
8952 | mDNSBool p2pInterface = (strncmp(intf->ifname, "p2p", 3) == 0); | |
8953 | ||
8954 | if (!p2pInterface || (rr->ARType == AuthRecordAnyIncludeP2P)) | |
8955 | return mDNStrue; | |
8956 | else | |
8957 | return mDNSfalse; | |
8958 | } |