1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved.
4 *
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
15 * limitations under the License.
16 */
17
18// ***************************************************************************
19// mDNSMacOSX.c:
20// Supporting routines to run mDNS on a CFRunLoop platform
21// ***************************************************************************
22
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
26
27#include "mDNSEmbeddedAPI.h"        // Defines the interface provided to the client layer above
28#include "DNSCommon.h"
29#include "uDNS.h"
30#include "mDNSMacOSX.h"             // Defines the specific types needed to run mDNS on this platform
31#include "dns_sd.h"                 // For mDNSInterface_LocalOnly etc.
32#include "PlatformCommon.h"
33#include "uds_daemon.h"
34#include "CryptoSupport.h"
35
36#include <stdio.h>
37#include <stdarg.h>                 // For va_list support
38#include <stdlib.h>                 // For arc4random
39#include <net/if.h>
40#include <net/if_types.h>           // For IFT_ETHER
41#include <net/if_dl.h>
42#include <net/bpf.h>                // For BIOCSETIF etc.
43#include <sys/uio.h>
44#include <sys/param.h>
45#include <sys/socket.h>
46#include <sys/sysctl.h>
47#include <sys/event.h>
48#include <fcntl.h>
49#include <sys/ioctl.h>
50#include <time.h>                   // platform support for UTC time
51#include <arpa/inet.h>              // for inet_aton
52#include <pthread.h>
53#include <netdb.h>                  // for getaddrinfo
54#include <sys/sockio.h>             // for SIOCGIFEFLAGS
55#include <notify.h>
56#include <netinet/in.h>             // For IP_RECVTTL
57#ifndef IP_RECVTTL
58#define IP_RECVTTL 24               // bool; receive reception TTL w/dgram
59#endif
60
61#include <netinet/in_systm.h>       // For n_long, required by <netinet/ip.h> below
62#include <netinet/ip.h>             // For IPTOS_LOWDELAY etc.
63#include <netinet6/in6_var.h>       // For IN6_IFF_NOTREADY etc.
64#include <netinet6/nd6.h>           // For ND6_INFINITE_LIFETIME etc.
65
66#include <netinet/tcp.h>
67
68#include <DebugServices.h>
69#include "dnsinfo.h"
70
71#include <ifaddrs.h>
72
73#include <IOKit/IOKitLib.h>
74#include <IOKit/IOMessage.h>
75
76#include <IOKit/ps/IOPowerSources.h>
77#include <IOKit/ps/IOPowerSourcesPrivate.h>
78#include <IOKit/ps/IOPSKeys.h>
79
80#include <mach/mach_error.h>
81#include <mach/mach_port.h>
82#include <mach/mach_time.h>
83#include "helper.h"
84#include "P2PPacketFilter.h"
85
86#include <asl.h>
87#include <SystemConfiguration/SCPrivate.h>
88
89// Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic.
90#include <Kernel/IOKit/apple80211/apple80211_var.h>
91
92#if APPLE_OSX_mDNSResponder
93#include <DeviceToDeviceManager/DeviceToDeviceManager.h>
94#include <AWACS.h>
95#if !NO_D2D
96D2DStatus D2DInitialize(CFRunLoopRef runLoop, D2DServiceCallback serviceCallback, void* userData) __attribute__((weak_import));
97D2DStatus D2DRetain(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
98D2DStatus D2DStopAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
99D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
100D2DStatus D2DStartAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
101D2DStatus D2DStartBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import));
102D2DStatus D2DStopBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import));
103void D2DStartResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
104void D2DStopResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
105D2DStatus D2DTerminate() __attribute__((weak_import));
106
107#endif // ! NO_D2D
108
109#else
110#define NO_D2D 1
111#define NO_AWACS 1
112#endif // APPLE_OSX_mDNSResponder
113
114#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
115#include <IOKit/platform/IOPlatformSupportPrivate.h>
116#endif // APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
117
118
119#define kInterfaceSpecificOption "interface="
120
121#define mDNS_IOREG_KEY               "mDNS_KEY"
122#define mDNS_IOREG_VALUE             "2009-07-30"
123#define mDNS_IOREG_KA_KEY            "mDNS_Keepalive"
124#define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS'
125
126// cache the InterfaceID of the AWDL interface
127static mDNSInterfaceID AWDLInterfaceID;
128
129// ***************************************************************************
130// Globals
131
132#if COMPILER_LIKES_PRAGMA_MARK
133#pragma mark - Globals
134#endif
135
136// By default we don't offer sleep proxy service
137// If OfferSleepProxyService is set non-zero (typically via command-line switch),
138// then we'll offer sleep proxy service on desktop Macs that are set to never sleep.
139// We currently do not offer sleep proxy service on laptops, or on machines that are set to go to sleep.
140mDNSexport int OfferSleepProxyService = 0;
141mDNSexport int DisableSleepProxyClient = 0;
142mDNSexport int UseInternalSleepProxy = 1;       // Set to non-zero to use internal (in-NIC) Sleep Proxy
143
144mDNSexport int OSXVers, iOSVers;
145mDNSexport int KQueueFD;
146
147#ifndef NO_SECURITYFRAMEWORK
148static CFArrayRef ServerCerts;
149OSStatus SSLSetAllowAnonymousCiphers(SSLContextRef context, Boolean enable);
150#endif /* NO_SECURITYFRAMEWORK */
151
152static CFStringRef NetworkChangedKey_IPv4;
153static CFStringRef NetworkChangedKey_IPv6;
154static CFStringRef NetworkChangedKey_Hostnames;
155static CFStringRef NetworkChangedKey_Computername;
156static CFStringRef NetworkChangedKey_DNS;
157static CFStringRef NetworkChangedKey_StateInterfacePrefix;
158static CFStringRef NetworkChangedKey_DynamicDNS       = CFSTR("Setup:/Network/DynamicDNS");
159static CFStringRef NetworkChangedKey_BackToMyMac      = CFSTR("Setup:/Network/BackToMyMac");
160static CFStringRef NetworkChangedKey_BTMMConnectivity = CFSTR("State:/Network/Connectivity");
161static CFStringRef NetworkChangedKey_PowerSettings    = CFSTR("State:/IOKit/PowerManagement/CurrentSettings");
162
163static char HINFO_HWstring_buffer[32];
164static char *HINFO_HWstring = "Device";
165static int HINFO_HWstring_prefixlen = 6;
166
167mDNSexport int WatchDogReportingThreshold = 250;
168
169dispatch_queue_t SSLqueue;
170
171//To prevent blocking the main queue, all writes to DynamicStore happen on the DynamicStoreQueue
172static dispatch_queue_t DynamicStoreQueue;
173
174#if TARGET_OS_EMBEDDED
175#define kmDNSResponderManagedPrefsID CFSTR("/Library/Managed Preferences/mobile/com.apple.mDNSResponder.plist")
176#endif
177
178#if APPLE_OSX_mDNSResponder
179static mDNSu8 SPMetricPortability   = 99;
180static mDNSu8 SPMetricMarginalPower = 99;
181static mDNSu8 SPMetricTotalPower    = 99;
182static mDNSu8 SPMetricFeatures      = 1; /* The current version supports TCP Keep Alive Feature */
183mDNSexport domainname ActiveDirectoryPrimaryDomain;
184mDNSexport int ActiveDirectoryPrimaryDomainLabelCount;
185mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer;
186#endif // APPLE_OSX_mDNSResponder
187
188// Don't send triggers too often. We arbitrarily limit it to three minutes.
189#define DNS_TRIGGER_INTERVAL (180 * mDNSPlatformOneSecond)
190
191// Used by AutoTunnel
192const char btmmprefix[] = "btmmdns:";
193const char dnsprefix[] = "dns:";
194
195// String Array used to write list of private domains to Dynamic Store
196static CFArrayRef privateDnsArray = NULL;
197
198// ***************************************************************************
199#if COMPILER_LIKES_PRAGMA_MARK
200#pragma mark -
201#pragma mark - D2D Support
202#endif
203
204#if !NO_D2D
205
206mDNSexport void D2D_start_advertising_interface(NetworkInterfaceInfo *interface)
207{
208    // AWDL wants the address and reverse address PTR record communicated
209    // via the D2D interface layer.
210    if (interface->InterfaceID == AWDLInterfaceID)
211    {
212        // only log if we have a valid record to start advertising
213        if (interface->RR_A.resrec.RecordType || interface->RR_PTR.resrec.RecordType)
214            LogInfo("D2D_start_advertising_interface: %s", interface->ifname);
215
216        if (interface->RR_A.resrec.RecordType)
217            external_start_advertising_service(&interface->RR_A.resrec, NULL);
218        if (interface->RR_PTR.resrec.RecordType)
219            external_start_advertising_service(&interface->RR_PTR.resrec, NULL);
220    }
221}
222
223mDNSexport void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface)
224{
225    if (interface->InterfaceID == AWDLInterfaceID)
226    {
227        // only log if we have a valid record to stop advertising
228        if (interface->RR_A.resrec.RecordType || interface->RR_PTR.resrec.RecordType)
229            LogInfo("D2D_stop_advertising_interface: %s", interface->ifname);
230
231        if (interface->RR_A.resrec.RecordType)
232            external_stop_advertising_service(&interface->RR_A.resrec, NULL);
233        if (interface->RR_PTR.resrec.RecordType)
234            external_stop_advertising_service(&interface->RR_PTR.resrec, NULL);
235    }
236}
237
238// Name compression items for fake packet version number 1
239static const mDNSu8 compression_packet_v1 = 0x01;
240
241static 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" };
242static mDNSu8 *const compression_limit = (mDNSu8 *) &compression_base_msg + sizeof(DNSMessage);
243static mDNSu8 *const compression_lhs = (mDNSu8 *const) compression_base_msg.data + 27;
244
245mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
246mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len);
247
248typedef struct D2DRecordListElem
249{
250    struct D2DRecordListElem *next;
251    D2DServiceInstance       instanceHandle;
252    D2DTransportType         transportType;
253    AuthRecord               ar;    // must be last in the structure to accomodate extra space
254                                    // allocated for large records.
255} D2DRecordListElem;
256
257static D2DRecordListElem *D2DRecords = NULL; // List of records returned with D2DServiceFound events
258
259typedef struct D2DBrowseListElem
260{
261    struct D2DBrowseListElem *next;
262    domainname name;
263    mDNSu16 type;
264    unsigned int refCount;
265} D2DBrowseListElem;
266
267D2DBrowseListElem* D2DBrowseList = NULL;
268
269mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
270{
271    ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
272    ptr[1] = (mDNSu8)((val      ) & 0xFF);
273    return ptr + sizeof(mDNSu16);
274}
275
276mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
277{
278    ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
279    ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
280    ptr[2] = (mDNSu8)((val >>  8) & 0xFF);
281    ptr[3] = (mDNSu8)((val      ) & 0xFF);
282    return ptr + sizeof(mDNSu32);
283}
284
285mDNSlocal void DomainnameToLower(const domainname * const in, domainname * const out)
286{
287    const mDNSu8 * const start = (const mDNSu8 * const)in;
288    mDNSu8 *ptr = (mDNSu8*)start;
289    while(*ptr)
290    {
291        mDNSu8 c = *ptr;
292        out->c[ptr-start] = *ptr;
293        ptr++;
294        for (; c; c--,ptr++) out->c[ptr-start] = mDNSIsUpperCase(*ptr) ? (*ptr - 'A' + 'a') : *ptr;
295    }
296    out->c[ptr-start] = *ptr;
297}
298
299mDNSlocal mStatus DNSNameCompressionParseBytes(mDNS *const m, const mDNSu8 *const lhs, const mDNSu16 lhs_len, const mDNSu8 *const rhs, const mDNSu16 rhs_len, AuthRecord *rr)
300{
301    if (mDNS_LoggingEnabled)
302    {
303        LogInfo("%s", __func__);
304        LogInfo("  Static Bytes: (%d bytes)", compression_lhs - (mDNSu8*)&compression_base_msg);
305        PrintHex((mDNSu8*)&compression_base_msg, compression_lhs - (mDNSu8*)&compression_base_msg);
306    }
307
308    mDNSu8 *ptr = compression_lhs; // pointer to the end of our fake packet
309
310    // Check to make sure we're not going to go past the end of the DNSMessage data
311    // 7 = 2 for CLASS (-1 for our version) + 4 for TTL + 2 for RDLENGTH
312    if (ptr + lhs_len - 7 + rhs_len >= compression_limit) return mStatus_NoMemoryErr;
313
314    // Copy the LHS onto our fake wire packet
315    mDNSPlatformMemCopy(ptr, lhs, lhs_len);
316    ptr += lhs_len - 1;
317
318    // Check the 'fake packet' version number, to ensure that we know how to decompress this data
319    if (*ptr != compression_packet_v1) return mStatus_Incompatible;
320
321    // two bytes of CLASS
322    ptr = putVal16(ptr, kDNSClass_IN | kDNSClass_UniqueRRSet);
323
324    // four bytes of TTL
325    ptr = putVal32(ptr, 120);
326
327    // Copy the RHS length into the RDLENGTH of our fake wire packet
328    ptr = putVal16(ptr, rhs_len);
329
330    // Copy the RHS onto our fake wire packet
331    mDNSPlatformMemCopy(ptr, rhs, rhs_len);
332    ptr += rhs_len;
333
334    if (mDNS_LoggingEnabled)
335    {
336        LogInfo("  Our Bytes (%d bytes): ", ptr - compression_lhs);
337        PrintHex(compression_lhs, ptr - compression_lhs);
338    }
339
340    ptr = (mDNSu8 *) GetLargeResourceRecord(m, &compression_base_msg, compression_lhs, ptr, mDNSInterface_Any, kDNSRecordTypePacketAns, &m->rec);
341    if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative)
342    { LogMsg("DNSNameCompressionParseBytes: failed to get large RR"); m->rec.r.resrec.RecordType = 0; return mStatus_UnknownErr; }
343    else LogInfo("DNSNameCompressionParseBytes: got rr: %s", CRDisplayString(m, &m->rec.r));
344
345    mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, AuthRecordP2P, FreeD2DARElemCallback, NULL);
346    AssignDomainName(&rr->namestorage, &m->rec.namestorage);
347    rr->resrec.rdlength = m->rec.r.resrec.rdlength;
348    rr->resrec.rdata->MaxRDLength = m->rec.r.resrec.rdlength;
349    mDNSPlatformMemCopy(rr->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, m->rec.r.resrec.rdlength);
350    rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
351    SetNewRData(&rr->resrec, mDNSNULL, 0);  // Sets rr->rdatahash for us
352
353    m->rec.r.resrec.RecordType = 0; // Mark m->rec as no longer in use
354
355    return mStatus_NoError;
356}
357
358mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainname* typeDomain, DNS_TypeValues qtype)
359{
360    mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain);
361    if (!ptr) return ptr;
362    *ptr = (qtype >> 8) & 0xff;
363    ptr += 1;
364    *ptr = qtype & 0xff;
365    ptr += 1;
366    *ptr = compression_packet_v1;
367    return ptr + 1;
368}
369
370mDNSlocal mDNSu8 * DNSNameCompressionBuildRHS(mDNSu8 *start, const ResourceRecord *const resourceRecord)
371{
372    return putRData(&compression_base_msg, start, compression_limit, resourceRecord);
373}
374
375#define PRINT_DEBUG_BYTES_LIMIT 64  // set limit on number of record bytes printed for debugging
376
377mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len)
378{
379    mDNSu8 *end;
380    char buffer[49] = {0};
381    char *bufend = buffer + sizeof(buffer);
382
383    if (len > PRINT_DEBUG_BYTES_LIMIT)
384    {
385        LogInfo(" (limiting debug output to %d bytes)", PRINT_DEBUG_BYTES_LIMIT);
386        len = PRINT_DEBUG_BYTES_LIMIT;
387    }
388    end = data + len;
389
390    while(data < end)
391    {
392        char *ptr = buffer;
393        for(; data < end && ptr < bufend-1; ptr+=3,data++)
394            mDNS_snprintf(ptr, bufend - ptr, "%02X ", *data);
395        LogInfo("    %s", buffer);
396    }
397}
398
399mDNSlocal void PrintHelper(const char *const tag, mDNSu8 *lhs, mDNSu16 lhs_len, mDNSu8 *rhs, mDNSu16 rhs_len)
400{
401    if (!mDNS_LoggingEnabled) return;
402
403    LogInfo("%s:", tag);
404    LogInfo("  LHS: (%d bytes)", lhs_len);
405    PrintHex(lhs, lhs_len);
406
407    if (!rhs) return;
408
409    LogInfo("  RHS: (%d bytes)", rhs_len);
410    PrintHex(rhs, rhs_len);
411}
412
413mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
414{
415    (void)m;  // unused
416    if (result == mStatus_MemFree)
417    {
418        D2DRecordListElem **ptr = &D2DRecords;
419        D2DRecordListElem *tmp;
420        while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next;
421        if (!*ptr) { LogMsg("FreeD2DARElemCallback: Could not find in D2DRecords: %s", ARDisplayString(m, rr)); return; }
422        LogInfo("FreeD2DARElemCallback: Found in D2DRecords: %s", ARDisplayString(m, rr));
423        tmp = *ptr;
424        *ptr = (*ptr)->next;
425        // Just because we stoppped browsing, doesn't mean we should tear down the PAN connection.
426        mDNSPlatformMemFree(tmp);
427    }
428}
429
430mDNSexport void external_connection_release(const domainname *instance)
431{
432    (void) instance;
433    D2DRecordListElem *ptr = D2DRecords;
434
435    for ( ; ptr ; ptr = ptr->next)
436    {
437        if ((ptr->ar.resrec.rrtype == kDNSServiceType_PTR) &&
438             SameDomainName(&ptr->ar.rdatastorage.u.name, instance))
439        {
440            LogInfo("external_connection_release: Calling D2DRelease(instanceHandle = %p, transportType = %d",
441                ptr->instanceHandle,  ptr->transportType);
442            if (D2DRelease) D2DRelease(ptr->instanceHandle, ptr->transportType);
443        }
444    }
445}
446
447mDNSlocal void xD2DClearCache(const domainname *regType, DNS_TypeValues qtype)
448{
449    D2DRecordListElem *ptr = D2DRecords;
450    for ( ; ptr ; ptr = ptr->next)
451    {
452        if ((ptr->ar.resrec.rrtype == qtype) && SameDomainName(&ptr->ar.namestorage, regType))
453        {
454            mDNS_Deregister(&mDNSStorage, &ptr->ar);
455            LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", ARDisplayString(&mDNSStorage, &ptr->ar));
456        }
457    }
458}
459
460mDNSlocal D2DBrowseListElem ** D2DFindInBrowseList(const domainname *const name, mDNSu16 type)
461{
462    D2DBrowseListElem **ptr = &D2DBrowseList;
463
464    for ( ; *ptr; ptr = &(*ptr)->next)
465        if ((*ptr)->type == type && SameDomainName(&(*ptr)->name, name))
466            break;
467
468    return ptr;
469}
470
471mDNSlocal unsigned int D2DBrowseListRefCount(const domainname *const name, mDNSu16 type)
472{
473    D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type);
474    return *ptr ? (*ptr)->refCount : 0;
475}
476
477mDNSlocal void D2DBrowseListRetain(const domainname *const name, mDNSu16 type)
478{
479    D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type);
480
481    if (!*ptr)
482    {
483        *ptr = mDNSPlatformMemAllocate(sizeof(**ptr));
484        mDNSPlatformMemZero(*ptr, sizeof(**ptr));
485        (*ptr)->type = type;
486        AssignDomainName(&(*ptr)->name, name);
487    }
488    (*ptr)->refCount += 1;
489
490    LogInfo("D2DBrowseListRetain: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount);
491}
492
493mDNSlocal void D2DBrowseListRelease(const domainname *const name, mDNSu16 type)
494{
495    D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type);
496
497    if (!*ptr) { LogMsg("D2DBrowseListRelease: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return; }
498
499    (*ptr)->refCount -= 1;
500
501    LogInfo("D2DBrowseListRelease: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount);
502
503    if (!(*ptr)->refCount)
504    {
505        D2DBrowseListElem *tmp = *ptr;
506        *ptr = (*ptr)->next;
507        mDNSPlatformMemFree(tmp);
508    }
509}
510
511mDNSlocal mStatus xD2DParse(mDNS *const m, const mDNSu8 * const lhs, const mDNSu16 lhs_len, const mDNSu8 * const rhs, const mDNSu16 rhs_len, AuthRecord *rr)
512{
513    if (*(lhs + (lhs_len - 1)) == compression_packet_v1)
514        return DNSNameCompressionParseBytes(m, lhs, lhs_len, rhs, rhs_len, rr);
515    else
516        return mStatus_Incompatible;
517}
518
519mDNSlocal void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
520{
521    if (result == kD2DSuccess)
522    {
523        if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DAddToCache: NULL Byte * passed in or length == 0"); return; }
524
525        mStatus err;
526        D2DRecordListElem *ptr = mDNSPlatformMemAllocate(sizeof(D2DRecordListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData)));
527
528        if (ptr == NULL) { LogMsg("xD2DAddToCache: memory allocation failure"); return; }
529
530        err = xD2DParse(m, (const mDNSu8 * const)key, (const mDNSu16)keySize, (const mDNSu8 * const)value, (const mDNSu16)valueSize, &ptr->ar);
531        if (err)
532        {
533            LogMsg("xD2DAddToCache: xD2DParse returned error: %d", err);
534            PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize);
535            mDNSPlatformMemFree(ptr);
536            return;
537        }
538        err = mDNS_Register(m, &ptr->ar);
539        if (err)
540        {
541            LogMsg("xD2DAddToCache: mDNS_Register returned error %d for %s", err, ARDisplayString(m, &ptr->ar));
542            mDNSPlatformMemFree(ptr);
543            return;
544        }
545
546        LogInfo("xD2DAddToCache: mDNS_Register succeeded for %s", ARDisplayString(m, &ptr->ar));
547        ptr->instanceHandle = instanceHandle;
548        ptr->transportType = transportType;
549        ptr->next = D2DRecords;
550        D2DRecords = ptr;
551    }
552    else
553        LogMsg("xD2DAddToCache: Unexpected result %d", result);
554}
555
556mDNSlocal D2DRecordListElem * xD2DFindInList(mDNS *const m, const Byte *const key, const size_t keySize, const Byte *const value, const size_t valueSize)
557{
558    D2DRecordListElem *ptr = D2DRecords;
559    D2DRecordListElem *arptr;
560
561    if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DFindInList: NULL Byte * passed in or length == 0"); return NULL; }
562
563    arptr = mDNSPlatformMemAllocate(sizeof(D2DRecordListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData)));
564    if (arptr == NULL) { LogMsg("xD2DFindInList: memory allocation failure"); return NULL; }
565
566    if (xD2DParse(m, (const mDNSu8 *const)key, (const mDNSu16)keySize, (const mDNSu8 *const)value, (const mDNSu16)valueSize, &arptr->ar) != mStatus_NoError)
567    {
568        LogMsg("xD2DFindInList: xD2DParse failed for key: %p (%u) value: %p (%u)", key, keySize, value, valueSize);
569        mDNSPlatformMemFree(arptr);
570        return NULL;
571    }
572
573    while (ptr)
574    {
575        if (IdenticalResourceRecord(&arptr->ar.resrec, &ptr->ar.resrec)) break;
576        ptr = ptr->next;
577    }
578
579    if (!ptr) LogMsg("xD2DFindInList: Could not find in D2DRecords: %s", ARDisplayString(m, &arptr->ar));
580    mDNSPlatformMemFree(arptr);
581    return ptr;
582}
583
584mDNSlocal void xD2DRemoveFromCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
585{
586    (void)transportType; // We don't care about this, yet.
587    (void)instanceHandle; // We don't care about this, yet.
588
589    if (result == kD2DSuccess)
590    {
591        D2DRecordListElem *ptr = xD2DFindInList(m, key, keySize, value, valueSize);
592        if (ptr)
593        {
594            LogInfo("xD2DRemoveFromCache: Remove from cache: %s", ARDisplayString(m, &ptr->ar));
595            mDNS_Deregister(m, &ptr->ar);
596        }
597    }
598    else
599        LogMsg("xD2DRemoveFromCache: Unexpected result %d", result);
600}
601
602mDNSlocal void xD2DServiceResolved(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
603{
604    (void)m;
605    (void)key;
606    (void)keySize;
607    (void)value;
608    (void)valueSize;
609
610    if (result == kD2DSuccess)
611    {
612        LogInfo("xD2DServiceResolved: Starting up PAN connection for %p", instanceHandle);
613        if (D2DRetain) D2DRetain(instanceHandle, transportType);
614    }
615    else LogMsg("xD2DServiceResolved: Unexpected result %d", result);
616}
617
618mDNSlocal void xD2DRetainHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
619{
620    (void)m;
621    (void)instanceHandle;
622    (void)transportType;
623    (void)key;
624    (void)keySize;
625    (void)value;
626    (void)valueSize;
627
628    if (result == kD2DSuccess) LogInfo("xD2DRetainHappened: Opening up PAN connection for %p", instanceHandle);
629    else LogMsg("xD2DRetainHappened: Unexpected result %d", result);
630}
631
632mDNSlocal void xD2DReleaseHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
633{
634    (void)m;
635    (void)instanceHandle;
636    (void)transportType;
637    (void)key;
638    (void)keySize;
639    (void)value;
640    (void)valueSize;
641
642    if (result == kD2DSuccess) LogInfo("xD2DReleaseHappened: Closing PAN connection for %p", instanceHandle);
643    else LogMsg("xD2DReleaseHappened: Unexpected result %d", result);
644}
645
646mDNSlocal void xD2DServiceCallback(D2DServiceEvent event, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize, void *userData)
647{
648    mDNS *m = (mDNS *) userData;
649    const char *eventString = "unknown";
650
651    KQueueLock(m);
652
653    if (keySize   > 0xFFFF) LogMsg("xD2DServiceCallback: keySize too large: %u", keySize);
654    if (valueSize > 0xFFFF) LogMsg("xD2DServiceCallback: valueSize too large: %u", valueSize);
655
656    switch (event)
657    {
658    case D2DServiceFound:
659        eventString = "D2DServiceFound";
660        break;
661    case D2DServiceLost:
662        eventString = "D2DServiceLost";
663        break;
664    case D2DServiceResolved:
665        eventString = "D2DServiceResolved";
666        break;
667    case D2DServiceRetained:
668        eventString = "D2DServiceRetained";
669        break;
670    case D2DServiceReleased:
671        eventString = "D2DServiceReleased";
672        break;
673    default:
674        break;
675    }
676
677    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);
678    PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize);
679
680    switch (event)
681    {
682    case D2DServiceFound:
683        xD2DAddToCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
684        break;
685    case D2DServiceLost:
686        xD2DRemoveFromCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
687        break;
688    case D2DServiceResolved:
689        xD2DServiceResolved(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
690        break;
691    case D2DServiceRetained:
692        xD2DRetainHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
693        break;
694    case D2DServiceReleased:
695        xD2DReleaseHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
696        break;
697    default:
698        break;
699    }
700
701    // Need to tickle the main kqueue loop to potentially handle records we removed or added.
702    KQueueUnlock(m, "xD2DServiceCallback");
703}
704
705// Map interface index and flags to a specific D2D transport type or D2DTransportMax if all plugins
706// should be called.
707// When D2DTransportMax is returned, if a specific transport should not be called, *excludedTransportType
708// will be set to the excluded transport value, otherwise, it will be set to D2DTransportMax.
709// If the return value is not D2DTransportMax, excludedTransportType is undefined.
710
711mDNSlocal D2DTransportType xD2DInterfaceToTransportType(mDNSInterfaceID InterfaceID, DNSServiceFlags flags, D2DTransportType * excludedTransportType)
712{
713    NetworkInterfaceInfoOSX *info;
714
715    // Default exludes the D2DAWDLTransport when D2DTransportMax is returned.
716    *excludedTransportType = D2DAWDLTransport;
717
718    // Call all D2D plugins when both kDNSServiceFlagsIncludeP2P and kDNSServiceFlagsIncludeAWDL are set.
719    if ((flags & kDNSServiceFlagsIncludeP2P) && (flags & kDNSServiceFlagsIncludeAWDL))
720    {
721        LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (including AWDL) since both kDNSServiceFlagsIncludeP2P and kDNSServiceFlagsIncludeAWDL are set");
722        *excludedTransportType = D2DTransportMax;
723        return D2DTransportMax;
724    }
725    // Call all D2D plugins (exlcluding AWDL) when only kDNSServiceFlagsIncludeP2P is set.
726    else if (flags & kDNSServiceFlagsIncludeP2P)
727    {
728        LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (excluding AWDL) since only kDNSServiceFlagsIncludeP2P is set");
729        return D2DTransportMax;
730    }
731    // Call AWDL D2D plugin when only kDNSServiceFlagsIncludeAWDL is set.
732    else if (flags & kDNSServiceFlagsIncludeAWDL)
733    {
734        LogInfo("xD2DInterfaceToTransportType: returning D2DAWDLTransport since only kDNSServiceFlagsIncludeAWDL is set");
735        return D2DAWDLTransport;
736    }
737
738    if (InterfaceID == mDNSInterface_P2P)
739    {
740        LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (excluding AWDL) for interface index mDNSInterface_P2P");
741        return D2DTransportMax;
742    }
743
744    // Compare to cached AWDL interface ID.
745    if (AWDLInterfaceID && (InterfaceID == AWDLInterfaceID))
746    {
747        LogInfo("xD2DInterfaceToTransportType: returning D2DAWDLTransport for interface index %d", InterfaceID);
748        return D2DAWDLTransport;
749    }
750
751    info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID);
752    if (info == NULL)
753    {
754        LogInfo("xD2DInterfaceToTransportType: Invalid interface index %d", InterfaceID);
755        return D2DTransportMax;
756    }
757
758    // Recognize AirDrop specific p2p* interface based on interface name.
759    if (strncmp(info->ifinfo.ifname, "p2p", 3) == 0)
760    {
761        LogInfo("xD2DInterfaceToTransportType: returning D2DWifiPeerToPeerTransport for interface index %d", InterfaceID);
762        return D2DWifiPeerToPeerTransport;
763    }
764
765    // Currently there is no way to identify Bluetooth interface by name,
766    // since they use "en*" based name strings.
767
768    LogInfo("xD2DInterfaceToTransportType: returning default D2DTransportMax for interface index %d", InterfaceID);
769    return D2DTransportMax;
770}
771
772mDNSexport void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags)
773{
774    domainname lower;
775
776    if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA)
777    {
778        LogInfo("external_start_browsing_for_service: ignoring address record");
779        return;
780    }
781
782    DomainnameToLower(typeDomain, &lower);
783
784    if (!D2DBrowseListRefCount(&lower, qtype))
785    {
786        D2DTransportType transportType, excludedTransport;
787
788        LogInfo("external_start_browsing_for_service: Starting browse for: %##s %s", lower.c, DNSTypeName(qtype));
789        mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype);
790        PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0);
791
792        transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
793        if (transportType == D2DTransportMax)
794        {
795            D2DTransportType i;
796            for (i = 0; i < D2DTransportMax; i++)
797            {
798                if (i == excludedTransport) continue;
799                if (D2DStartBrowsingForKeyOnTransport) D2DStartBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, i);
800            }
801        }
802        else
803        {
804            if (D2DStartBrowsingForKeyOnTransport) D2DStartBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType);
805        }
806    }
807    D2DBrowseListRetain(&lower, qtype);
808}
809
810mDNSexport void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags)
811{
812    domainname lower;
813
814    if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA)
815    {
816        LogInfo("external_stop_browsing_for_service: ignoring address record");
817        return;
818    }
819
820    DomainnameToLower(typeDomain, &lower);
821
822    D2DBrowseListRelease(&lower, qtype);
823    if (!D2DBrowseListRefCount(&lower, qtype))
824    {
825        D2DTransportType transportType, excludedTransport;
826
827        LogInfo("external_stop_browsing_for_service: Stopping browse for: %##s %s", lower.c, DNSTypeName(qtype));
828        mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype);
829        PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0);
830
831        transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
832        if (transportType == D2DTransportMax)
833        {
834            D2DTransportType i;
835            for (i = 0; i < D2DTransportMax; i++)
836            {
837                if (i == excludedTransport) continue;
838                if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, i);
839            }
840        }
841        else
842        {
843            if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType);
844        }
845
846        // The D2D driver may not generate the D2DServiceLost event for this key after
847        // the D2DStopBrowsingForKey*() call above.  So, we flush the key from the D2D
848        // record cache now.
849        xD2DClearCache(&lower, qtype);
850    }
851}
852
853mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags)
854{
855    domainname lower;
856    mDNSu8 *rhs = NULL;
857    mDNSu8 *end = NULL;
858    D2DTransportType transportType, excludedTransport;
859    DomainnameToLower(resourceRecord->name, &lower);
860
861    LogInfo("external_start_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord));
862    // For SRV records, update packet filter if p2p interface already exists, otherwise,
863    // if will be updated when we get the KEV_DL_IF_ATTACHED event for the interface.
864    if (resourceRecord->rrtype == kDNSType_SRV)
865        mDNSUpdatePacketFilter(NULL);
866
867    rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype);
868    end = DNSNameCompressionBuildRHS(rhs, resourceRecord);
869    PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
870
871    transportType = xD2DInterfaceToTransportType(resourceRecord->InterfaceID, flags, & excludedTransport);
872    if (transportType == D2DTransportMax)
873    {
874        D2DTransportType i;
875        for (i = 0; i < D2DTransportMax; i++)
876        {
877            if (i == excludedTransport) continue;
878            if (D2DStartAdvertisingPairOnTransport) D2DStartAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
879        }
880    }
881    else
882    {
883        if (D2DStartAdvertisingPairOnTransport) D2DStartAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
884    }
885}
886
887mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags)
888{
889    domainname lower;
890    mDNSu8 *rhs = NULL;
891    mDNSu8 *end = NULL;
892    D2DTransportType transportType, excludedTransport;
893    DomainnameToLower(resourceRecord->name, &lower);
894
895    LogInfo("external_stop_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord));
896
897    // For SRV records, update packet filter to to remove this port from list
898    if (resourceRecord->rrtype == kDNSType_SRV)
899        mDNSUpdatePacketFilter(resourceRecord);
900
901    rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype);
902    end = DNSNameCompressionBuildRHS(rhs, resourceRecord);
903    PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
904
905    transportType = xD2DInterfaceToTransportType(resourceRecord->InterfaceID, flags, & excludedTransport);
906    if (transportType == D2DTransportMax)
907    {
908        D2DTransportType i;
909        for (i = 0; i < D2DTransportMax; i++)
910        {
911            if (i == excludedTransport) continue;
912            if (D2DStopAdvertisingPairOnTransport) D2DStopAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
913        }
914    }
915    else
916    {
917        if (D2DStopAdvertisingPairOnTransport) D2DStopAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
918    }
919}
920
921mDNSexport void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags)
922{
923    domainname lower;
924    mDNSu8 *rhs = NULL;
925    mDNSu8 *end = NULL;
926    mDNSBool AWDL_used = false;   // whether AWDL was used for this resolve
927    D2DTransportType transportType, excludedTransport;
928    DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
929
930    LogInfo("external_start_resolving_service: %##s", fqdn->c);
931    rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR);
932    end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn);
933    PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
934
935    transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
936    if (transportType == D2DTransportMax)
937    {
938        // Resolving over all the transports, except for excludedTransport if set.
939        D2DTransportType i;
940        for (i = 0; i < D2DTransportMax; i++)
941        {
942            if (i == excludedTransport) continue;
943            if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
944
945            if (i == D2DAWDLTransport)
946                AWDL_used = true;
947        }
948    }
949    else
950    {
951        // Resolving over one specific transport.
952        if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
953
954        if (transportType == D2DAWDLTransport)
955            AWDL_used = true;
956    }
957
958    // AWDL wants the SRV and TXT record queries communicated over the D2D interface.
959    // We only want these records going to AWDL, so use AWDLInterfaceID as the
960    // interface and don't set any other flags.
961    if (AWDL_used && AWDLInterfaceID)
962    {
963        LogInfo("external_start_resolving_service: browse for TXT and SRV over AWDL");
964        external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, NULL);
965        external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, NULL);
966    }
967}
968
969mDNSexport void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags)
970{
971    domainname lower;
972    mDNSu8 *rhs = NULL;
973    mDNSu8 *end = NULL;
974    mDNSBool AWDL_used = false;   // whether AWDL was used for this resolve
975    D2DTransportType transportType, excludedTransport;
976    DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
977
978    LogInfo("external_stop_resolving_service: %##s", fqdn->c);
979    rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR);
980    end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn);
981    PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
982
983    transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
984    if (transportType == D2DTransportMax)
985    {
986        D2DTransportType i;
987        for (i = 0; i < D2DTransportMax; i++)
988        {
989            if (i == excludedTransport) continue;
990            if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
991
992            if (i == D2DAWDLTransport)
993                AWDL_used = true;
994        }
995    }
996    else
997    {
998        if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
999
1000        if (transportType == D2DAWDLTransport)
1001            AWDL_used = true;
1002    }
1003
1004    // AWDL wants the SRV and TXT record queries communicated over the D2D interface.
1005    // We only want these records going to AWDL, so use AWDLInterfaceID as the
1006    // interface and don't set any other flags.
1007    if (AWDL_used && AWDLInterfaceID)
1008    {
1009        LogInfo("external_stop_resolving_service: stop browse for TXT and SRV on AWDL");
1010        external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, NULL);
1011        external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, NULL);
1012    }
1013}
1014
1015#elif APPLE_OSX_mDNSResponder
1016
1017mDNSexport void external_start_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags) { (void)m; (void)type; (void)qtype; (void)flags;}
1018mDNSexport void external_stop_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags) { (void)m; (void)type; (void)qtype; (void)flags;}
1019mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { (void)resourceRecord; (void)flags;}
1020mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { (void)resourceRecord; (void)flags;}
1021mDNSexport void external_start_resolving_service(const domainname *const fqdn, DNSServiceFlags flags)  { (void)fqdn; (void)flags;}
1022mDNSexport void external_stop_resolving_service(const domainname *const fqdn, DNSServiceFlags flags)  { (void)fqdn; (void)flags;}
1023
1024#endif // ! NO_D2D
1025
1026// ***************************************************************************
1027// Functions
1028
1029#if COMPILER_LIKES_PRAGMA_MARK
1030#pragma mark -
1031#pragma mark - Utility Functions
1032#endif
1033
1034// We only attempt to send and receive multicast packets on interfaces that are
1035// (a) flagged as multicast-capable
1036// (b) *not* flagged as point-to-point (e.g. modem)
1037// Typically point-to-point interfaces are modems (including mobile-phone pseudo-modems), and we don't want
1038// to run up the user's bill sending multicast traffic over a link where there's only a single device at the
1039// other end, and that device (e.g. a modem bank) is probably not answering Multicast DNS queries anyway.
1040#define MulticastInterface(i) (((i)->ifa_flags & IFF_MULTICAST) && !((i)->ifa_flags & IFF_POINTOPOINT))
1041
1042mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg)  // Both strings are UTF-8 text
1043{
1044    static int notifyCount = 0;
1045    if (notifyCount) return;
1046
1047    // If we display our alert early in the boot process, then it vanishes once the desktop appears.
1048    // To avoid this, we don't try to display alerts in the first three minutes after boot.
1049    if ((mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return;
1050
1051    // Unless ForceAlerts is defined, we only show these bug report alerts on machines that have a 17.x.x.x address
1052    #if !ForceAlerts
1053    {
1054        // Determine if we're at Apple (17.*.*.*)
1055        NetworkInterfaceInfoOSX *i;
1056        for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
1057            if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && i->ifinfo.ip.ip.v4.b[0] == 17)
1058                break;
1059        if (!i) return; // If not at Apple, don't show the alert
1060    }
1061    #endif
1062
1063    LogMsg("%s", title);
1064    LogMsg("%s", msg);
1065    // Display a notification to the user
1066    notifyCount++;
1067
1068#ifndef NO_CFUSERNOTIFICATION
1069    mDNSNotify(title, msg);
1070#endif /* NO_CFUSERNOTIFICATION */
1071}
1072
1073// Returns true if it is an AppleTV based hardware running iOS, false otherwise
1074mDNSlocal mDNSBool IsAppleTV(void)
1075{
1076#if TARGET_OS_EMBEDDED
1077    static mDNSBool sInitialized = mDNSfalse;
1078    static mDNSBool sIsAppleTV   = mDNSfalse;
1079    CFStringRef deviceClass = NULL;
1080
1081    if(!sInitialized)
1082    {
1083        deviceClass = (CFStringRef) MGCopyAnswer(kMGQDeviceClass, NULL);
1084        if(deviceClass)
1085        {
1086            if(CFEqual(deviceClass, kMGDeviceClassAppleTV))
1087                sIsAppleTV = mDNStrue;
1088            CFRelease(deviceClass);
1089        }
1090        sInitialized = mDNStrue;
1091    }
1092    return(sIsAppleTV);
1093#else
1094    return mDNSfalse;
1095#endif // TARGET_OS_EMBEDDED
1096}
1097
1098mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh)
1099{
1100    static struct ifaddrs *ifa = NULL;
1101
1102    if (refresh && ifa)
1103    {
1104        freeifaddrs(ifa);
1105        ifa = NULL;
1106    }
1107
1108    if (ifa == NULL)
1109        getifaddrs(&ifa);
1110    return ifa;
1111}
1112
1113mDNSlocal void DynamicStoreWrite(int key, const char* subkey, uintptr_t value, signed long valueCnt)
1114{
1115    CFStringRef sckey       = NULL;
1116    Boolean release_sckey   = FALSE;
1117    CFDataRef bytes         = NULL;
1118    CFPropertyListRef plist = NULL;
1119    SCDynamicStoreRef store = NULL;
1120
1121    switch ((enum mDNSDynamicStoreSetConfigKey)key)
1122    {
1123        case kmDNSMulticastConfig:
1124            sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS);
1125            break;
1126        case kmDNSDynamicConfig:
1127            sckey = CFSTR("State:/Network/DynamicDNS");
1128            break;
1129        case kmDNSPrivateConfig:
1130            sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS);
1131            break;
1132        case kmDNSBackToMyMacConfig:
1133            sckey = CFSTR("State:/Network/BackToMyMac");
1134            break;
1135        case kmDNSSleepProxyServersState:
1136        {
1137            CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0);
1138            CFStringAppend(tmp, CFSTR("State:/Network/Interface/"));
1139            CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8);
1140            CFStringAppend(tmp, CFSTR("/SleepProxyServers"));
1141            sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp);
1142            release_sckey = TRUE;
1143            CFRelease(tmp);
1144            break;
1145        }
1146        case kmDNSDebugState:
1147            sckey = CFSTR("State:/Network/mDNSResponder/DebugState");
1148            break;
1149        default:
1150            LogMsg("unrecognized key %d", key);
1151            goto fin;
1152    }
1153    if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value,
1154                                                     valueCnt, kCFAllocatorNull)))
1155    {
1156        LogMsg("CFDataCreateWithBytesNoCopy of value failed");
1157        goto fin;
1158    }
1159    if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes,
1160                                                         kCFPropertyListImmutable, NULL)))
1161    {
1162        LogMsg("CFPropertyListCreateFromXMLData of bytes failed");
1163        goto fin;
1164    }
1165    CFRelease(bytes);
1166    bytes = NULL;
1167    if (NULL == (store = SCDynamicStoreCreate(NULL,
1168                                              CFSTR(kmDNSResponderServName), NULL, NULL)))
1169    {
1170        LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
1171        goto fin;
1172    }
1173    SCDynamicStoreSetValue(store, sckey, plist);
1174
1175fin:
1176    if (NULL != bytes)
1177        CFRelease(bytes);
1178    if (NULL != plist)
1179        CFRelease(plist);
1180    if (NULL != store)
1181        CFRelease(store);
1182    if (release_sckey && sckey)
1183        CFRelease(sckey);
1184}
1185
1186mDNSexport void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value)
1187{
1188    CFPropertyListRef valueCopy;
1189    char *subkeyCopy  = NULL;
1190    if (!value)
1191        return;
1192
1193    // We need to copy the key and value before we dispatch off the block below as the
1194    // caller will free the memory once we return from this function.
1195    valueCopy = CFPropertyListCreateDeepCopy(NULL, value, kCFPropertyListImmutable);
1196    if (!valueCopy)
1197    {
1198        LogMsg("mDNSDynamicStoreSetConfig: ERROR valueCopy NULL");
1199        return;
1200    }
1201    if (subkey)
1202    {
1203        int len    = strlen(subkey);
1204        subkeyCopy = mDNSPlatformMemAllocate(len + 1);
1205        if (!subkeyCopy)
1206        {
1207            LogMsg("mDNSDynamicStoreSetConfig: ERROR subkeyCopy NULL");
1208            return;
1209        }
1210        mDNSPlatformMemCopy(subkeyCopy, subkey, len);
1211        subkeyCopy[len] = 0;
1212    }
1213
1214    dispatch_async(DynamicStoreQueue, ^{
1215        CFWriteStreamRef stream = NULL;
1216        CFDataRef bytes = NULL;
1217        CFStringRef error;
1218        CFIndex ret;
1219
1220        if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL)))
1221        {
1222            LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCreateWithAllocatedBuffers failed (Object creation failed)");
1223            goto END;
1224        }
1225        CFWriteStreamOpen(stream);
1226        ret = CFPropertyListWriteToStream(valueCopy, stream, kCFPropertyListBinaryFormat_v1_0, &error);
1227        if (ret == 0)
1228        {
1229            LogMsg("mDNSDynamicStoreSetConfig : CFPropertyListWriteToStream failed (Could not write property list to stream)");
1230            goto END;
1231        }
1232        if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten)))
1233        {
1234            LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCopyProperty failed (Object creation failed) ");
1235            goto END;
1236        }
1237        CFWriteStreamClose(stream);
1238        CFRelease(stream);
1239        stream = NULL;
1240        LogInfo("mDNSDynamicStoreSetConfig: key %d subkey %s", key, subkeyCopy);
1241        DynamicStoreWrite(key, subkeyCopy ? subkeyCopy : "", (uintptr_t)CFDataGetBytePtr(bytes), CFDataGetLength(bytes));
1242
1243    END:
1244        CFRelease(valueCopy);
1245        if (NULL != stream)
1246        {
1247            CFWriteStreamClose(stream);
1248            CFRelease(stream);
1249        }
1250        if (NULL != bytes)
1251            CFRelease(bytes);
1252        if (subkeyCopy)
1253            mDNSPlatformMemFree(subkeyCopy);
1254    });
1255}
1256
1257// To match *either* a v4 or v6 instance of this interface name, pass AF_UNSPEC for type
1258mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const char *ifname, int type)
1259{
1260    NetworkInterfaceInfoOSX *i;
1261    for (i = m->p->InterfaceList; i; i = i->next)
1262        if (i->Exists && !strcmp(i->ifinfo.ifname, ifname) &&
1263            ((type == AF_UNSPEC                                         ) ||
1264             (type == AF_INET  && i->ifinfo.ip.type == mDNSAddrType_IPv4) ||
1265             (type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6))) return(i);
1266    return(NULL);
1267}
1268
1269#if TARGET_OS_EMBEDDED
1270mDNSlocal SCPreferencesRef mDNSManagedPrefsGet(void)
1271{
1272    SCPreferencesRef smDNSManagedPrefs = NULL;
1273    smDNSManagedPrefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("mDNSManagedPrefs"), kmDNSResponderManagedPrefsID);
1274
1275    return (smDNSManagedPrefs);
1276}
1277
1278mDNSlocal mDNSBool GetmDNSManagedPrefKeyVal(SCPreferencesRef prefs, CFStringRef key)
1279{
1280    mDNSBool val = mDNSfalse;
1281    CFBooleanRef val_cf = NULL;
1282
1283    if (prefs != NULL)
1284    {
1285        val_cf = SCPreferencesGetValue(prefs, key);
1286        if (isA_CFBoolean(val_cf) != NULL)
1287            val = CFBooleanGetValue(val_cf); //When mDNSResponder-Debug-profile is Installed
1288        else
1289            val = mDNSfalse; //When mDNSResponder-Debug-profile is Uninstalled
1290    }
1291    else
1292    {
1293        LogMsg("GetmDNSManagedPrefKeyVal: mDNSManagedPrefs are NULL!");
1294        val = mDNSfalse;
1295    }
1296    if (val_cf)
1297        CFRelease(val_cf);
1298    return (val);
1299}
1300
1301mDNSexport mDNSBool GetmDNSManagedPref(CFStringRef key)
1302{
1303    SCPreferencesRef  managed = NULL;
1304    mDNSBool ret_value;
1305
1306    managed = mDNSManagedPrefsGet();
1307    ret_value = GetmDNSManagedPrefKeyVal(managed, key);
1308
1309    if (managed)
1310        CFRelease(managed);
1311    return (ret_value);
1312}
1313#endif //TARGET_OS_EMBEDDED
1314
1315mDNSlocal int myIfIndexToName(u_short ifindex, char *name)
1316{
1317    struct ifaddrs *ifa;
1318    for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next)
1319        if (ifa->ifa_addr->sa_family == AF_LINK)
1320            if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == ifindex)
1321            { strlcpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; }
1322    return -1;
1323}
1324
1325mDNSexport NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex)
1326{
1327    mDNSu32 scope_id = (mDNSu32)(uintptr_t)ifindex;
1328    NetworkInterfaceInfoOSX *i;
1329
1330    // Don't get tricked by inactive interfaces
1331    for (i = m->p->InterfaceList; i; i = i->next)
1332        if (i->Registered && i->scope_id == scope_id) return(i);
1333
1334    return mDNSNULL;
1335}
1336
1337mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex)
1338{
1339    if (ifindex == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly);
1340    if (ifindex == kDNSServiceInterfaceIndexP2P      ) return(mDNSInterface_P2P);
1341    if (ifindex == kDNSServiceInterfaceIndexAny      ) return(mDNSNULL);
1342
1343    NetworkInterfaceInfoOSX* ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex);
1344    if (!ifi)
1345    {
1346        // Not found. Make sure our interface list is up to date, then try again.
1347        LogInfo("mDNSPlatformInterfaceIDfromInterfaceIndex: InterfaceID for interface index %d not found; Updating interface list", ifindex);
1348        mDNSMacOSXNetworkChanged(m);
1349        ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex);
1350    }
1351
1352    if (!ifi) return(mDNSNULL);
1353
1354    return(ifi->ifinfo.InterfaceID);
1355}
1356
1357
1358mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange)
1359{
1360    NetworkInterfaceInfoOSX *i;
1361    if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly);
1362    if (id == mDNSInterface_P2P      ) return(kDNSServiceInterfaceIndexP2P);
1363    if (id == mDNSInterface_Any      ) return(0);
1364
1365    mDNSu32 scope_id = (mDNSu32)(uintptr_t)id;
1366
1367    // Don't use i->Registered here, because we DO want to find inactive interfaces, which have no Registered set
1368    for (i = m->p->InterfaceList; i; i = i->next)
1369        if (i->scope_id == scope_id) return(i->scope_id);
1370
1371    // If we are supposed to suppress network change, return "id" back
1372    if (suppressNetworkChange) return scope_id;
1373
1374    // Not found. Make sure our interface list is up to date, then try again.
1375    LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id);
1376    mDNSMacOSXNetworkChanged(m);
1377    for (i = m->p->InterfaceList; i; i = i->next)
1378        if (i->scope_id == scope_id) return(i->scope_id);
1379
1380    return(0);
1381}
1382
1383#if APPLE_OSX_mDNSResponder
1384mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...)
1385{
1386    if (iOSVers)
1387        return; // No ASL on iOS
1388
1389    static char buffer[512];
1390    aslmsg asl_msg = asl_new(ASL_TYPE_MSG);
1391
1392    if (!asl_msg)   { LogMsg("mDNSASLLog: asl_new failed"); return; }
1393    if (uuid)
1394    {
1395        char uuidStr[37];
1396        uuid_unparse(*uuid, uuidStr);
1397        asl_set     (asl_msg, "com.apple.message.uuid", uuidStr);
1398    }
1399
1400    static char domainBase[] = "com.apple.mDNSResponder.%s";
1401    mDNS_snprintf   (buffer, sizeof(buffer), domainBase, subdomain);
1402    asl_set         (asl_msg, "com.apple.message.domain", buffer);
1403
1404    if (result) asl_set(asl_msg, "com.apple.message.result", result);
1405    if (signature) asl_set(asl_msg, "com.apple.message.signature", signature);
1406
1407    va_list ptr;
1408    va_start(ptr,fmt);
1409    mDNS_vsnprintf(buffer, sizeof(buffer), fmt, ptr);
1410    va_end(ptr);
1411
1412    int old_filter = asl_set_filter(NULL,ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
1413    asl_log(NULL, asl_msg, ASL_LEVEL_DEBUG, "%s", buffer);
1414    asl_set_filter(NULL, old_filter);
1415    asl_free(asl_msg);
1416}
1417
1418
1419mDNSlocal void mDNSLogDNSSECStatistics(mDNS *const m)
1420{
1421    char    buffer[16];
1422
1423    aslmsg  aslmsg = asl_new(ASL_TYPE_MSG);
1424
1425    // If we failed to allocate an aslmsg structure, keep accumulating
1426    // the statistics and try again at the next log interval.
1427    if (!aslmsg)
1428    {
1429        LogMsg("mDNSLogDNSSECStatistics: asl_new() failed!");
1430        return;
1431    }
1432
1433    asl_set(aslmsg,"com.apple.message.domain", "com.apple.mDNSResponder.DNSSECstatistics");
1434
1435    if (m->rrcache_totalused_unicast)
1436    {
1437        mDNS_snprintf(buffer, sizeof(buffer), "%u", (mDNSu32) ((unsigned long)(m->DNSSECStats.TotalMemUsed * 100))/m->rrcache_totalused_unicast);
1438    }
1439    else
1440    {
1441        LogMsg("mDNSLogDNSSECStatistics: unicast is zero");
1442        buffer[0] = 0;
1443    }
1444    asl_set(aslmsg,"com.apple.message.MemUsage", buffer);
1445
1446    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency0);
1447    asl_set(aslmsg,"com.apple.message.Latency0", buffer);
1448    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency10);
1449    asl_set(aslmsg,"com.apple.message.Latency10", buffer);
1450    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency20);
1451    asl_set(aslmsg,"com.apple.message.Latency20", buffer);
1452    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency50);
1453    asl_set(aslmsg,"com.apple.message.Latency50", buffer);
1454    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency100);
1455    asl_set(aslmsg,"com.apple.message.Latency100", buffer);
1456
1457    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets0);
1458    asl_set(aslmsg,"com.apple.message.ExtraPackets0", buffer);
1459    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets3);
1460    asl_set(aslmsg,"com.apple.message.ExtraPackets3", buffer);
1461    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets7);
1462    asl_set(aslmsg,"com.apple.message.ExtraPackets7", buffer);
1463    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets10);
1464    asl_set(aslmsg,"com.apple.message.ExtraPackets10", buffer);
1465
1466    // Ignore IndeterminateStatus as we don't log them
1467    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.SecureStatus);
1468    asl_set(aslmsg,"com.apple.message.SecureStatus", buffer);
1469    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.InsecureStatus);
1470    asl_set(aslmsg,"com.apple.message.InsecureStatus", buffer);
1471    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.BogusStatus);
1472    asl_set(aslmsg,"com.apple.message.BogusStatus", buffer);
1473    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.NoResponseStatus);
1474    asl_set(aslmsg,"com.apple.message.NoResponseStatus", buffer);
1475
1476    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.NumProbesSent);
1477    asl_set(aslmsg,"com.apple.message.NumProbesSent", buffer);
1478    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize0);
1479    asl_set(aslmsg,"com.apple.message.MsgSize0", buffer);
1480    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize1);
1481    asl_set(aslmsg,"com.apple.message.MsgSize1", buffer);
1482    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize2);
1483    asl_set(aslmsg,"com.apple.message.MsgSize2", buffer);
1484
1485    asl_log(NULL, aslmsg, ASL_LEVEL_NOTICE, "");
1486    asl_free(aslmsg);
1487}
1488
1489// Calculate packets per hour given total packet count and interval in seconds.
1490// Cast one term of multiplication to (long) to use 64-bit arithmetic
1491// and avoid a potential 32-bit overflow prior to the division.
1492#define ONE_HOUR    3600
1493#define PACKET_RATE(PACKETS, INTERVAL) (int)(((long) (PACKETS) * ONE_HOUR)/(INTERVAL))
1494
1495// Put packet rate data in discrete buckets.
1496mDNSlocal int mDNSBucketData(int inputData, int interval)
1497{
1498    if (!interval)
1499    {
1500        LogMsg("mDNSBucketData: interval is zero!");
1501        return 0;
1502    }
1503
1504    int ratePerHour = PACKET_RATE(inputData, interval);
1505    int bucket;
1506
1507    if (ratePerHour == 0)
1508        bucket = 0;
1509    else if (ratePerHour <= 10)
1510        bucket = 10;
1511    else if (ratePerHour <= 100)
1512        bucket = 100;
1513    else if (ratePerHour <= 1000)
1514        bucket = 1000;
1515    else if (ratePerHour <= 5000)
1516        bucket = 5000;
1517    else if (ratePerHour <= 10000)
1518        bucket = 10000;
1519    else if (ratePerHour <= 50000)
1520        bucket = 50000;
1521    else if (ratePerHour <= 100000)
1522        bucket = 100000;
1523    else if (ratePerHour <= 250000)
1524        bucket = 250000;
1525    else if (ratePerHour <= 500000)
1526        bucket = 500000;
1527    else
1528        bucket = 1000000;
1529
1530    return bucket;
1531}
1532
1533mDNSlocal void mDNSLogBonjourStatistics(mDNS *const m)
1534{
1535    static mDNSs32 last_PktNum, last_MPktNum;
1536    static mDNSs32 last_UnicastPacketsSent, last_MulticastPacketsSent;
1537    static mDNSs32 last_RemoteSubnet;
1538
1539    mDNSs32 interval;
1540    char    buffer[16];
1541    mDNSs32 inMulticast = m->MPktNum - last_MPktNum;
1542    mDNSs32 inUnicast   = m->PktNum - last_PktNum - inMulticast;
1543    mDNSs32 outUnicast  = m->UnicastPacketsSent - last_UnicastPacketsSent;
1544    mDNSs32 outMulticast = m->MulticastPacketsSent - last_MulticastPacketsSent;
1545    mDNSs32 remoteSubnet = m->RemoteSubnet - last_RemoteSubnet;
1546
1547
1548    // save starting values for new interval
1549    last_PktNum = m->PktNum;
1550    last_MPktNum = m->MPktNum;
1551    last_UnicastPacketsSent = m->UnicastPacketsSent;
1552    last_MulticastPacketsSent = m->MulticastPacketsSent;
1553    last_RemoteSubnet = m->RemoteSubnet;
1554
1555    // Need a non-zero active time interval.
1556    if (!m->ActiveStatTime)
1557        return;
1558
1559    // Round interval time to nearest hour boundary. Less then 30 minutes rounds to zero.
1560    interval = (m->ActiveStatTime + ONE_HOUR/2)/ONE_HOUR;
1561
1562    // Use a minimum of 30 minutes of awake time to calculate average packet rates.
1563    // The rounded awake interval should not be greater than the rounded reporting
1564    // interval.
1565    if ((interval == 0) || (interval > (kDefaultNextStatsticsLogTime + ONE_HOUR/2)/ONE_HOUR))
1566        return;
1567
1568    aslmsg  aslmsg = asl_new(ASL_TYPE_MSG);
1569
1570    if (!aslmsg)
1571    {
1572        LogMsg("mDNSLogBonjourStatistics: asl_new() failed!");
1573        return;
1574    }
1575    // log in MessageTracer format
1576    asl_set(aslmsg,"com.apple.message.domain", "com.apple.mDNSResponder.statistics");
1577
1578    snprintf(buffer, sizeof(buffer), "%d", interval);
1579    asl_set(aslmsg,"com.apple.message.interval", buffer);
1580
1581    // log the packet rates as packets per hour
1582    snprintf(buffer, sizeof(buffer), "%d",
1583            mDNSBucketData(inUnicast, m->ActiveStatTime));
1584    asl_set(aslmsg,"com.apple.message.UnicastIn", buffer);
1585
1586    snprintf(buffer, sizeof(buffer), "%d",
1587            mDNSBucketData(inMulticast, m->ActiveStatTime));
1588    asl_set(aslmsg,"com.apple.message.MulticastIn", buffer);
1589
1590    snprintf(buffer, sizeof(buffer), "%d",
1591            mDNSBucketData(outUnicast, m->ActiveStatTime));
1592    asl_set(aslmsg,"com.apple.message.UnicastOut", buffer);
1593
1594    snprintf(buffer, sizeof(buffer), "%d",
1595            mDNSBucketData(outMulticast, m->ActiveStatTime));
1596    asl_set(aslmsg,"com.apple.message.MulticastOut", buffer);
1597
1598    snprintf(buffer, sizeof(buffer), "%d",
1599            mDNSBucketData(remoteSubnet, m->ActiveStatTime));
1600    asl_set(aslmsg,"com.apple.message.RemoteSubnet", buffer);
1601
1602    asl_log(NULL, aslmsg, ASL_LEVEL_NOTICE, "");
1603
1604    asl_free(aslmsg);
1605}
1606
1607// Log multicast and unicast traffic statistics to MessageTracer on OSX
1608mDNSexport void mDNSLogStatistics(mDNS *const m)
1609{
1610    // MessageTracer only available on OSX
1611    if (iOSVers)
1612        return;
1613
1614    mDNSs32 currentUTC = mDNSPlatformUTC();
1615
1616    // log runtime statistics
1617    if ((currentUTC - m->NextStatLogTime) >= 0)
1618    {
1619        m->NextStatLogTime = currentUTC + kDefaultNextStatsticsLogTime;
1620        // If StatStartTime is zero, it hasn't been reinitialized yet
1621        // in the wakeup code path.
1622        if (m->StatStartTime)
1623        {
1624            m->ActiveStatTime += currentUTC - m->StatStartTime;
1625        }
1626
1627        // Only log statistics if we have recorded some active time during
1628        // this statistics interval.
1629        if (m->ActiveStatTime)
1630        {
1631            mDNSLogBonjourStatistics(m);
1632            mDNSLogDNSSECStatistics(m);
1633        }
1634
1635        // Start a new statistics gathering interval.
1636        m->StatStartTime = currentUTC;
1637        m->ActiveStatTime = 0;
1638    }
1639}
1640
1641#endif // APPLE_OSX_mDNSResponder
1642
1643#if COMPILER_LIKES_PRAGMA_MARK
1644#pragma mark -
1645#pragma mark - UDP & TCP send & receive
1646#endif
1647
1648mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr)
1649{
1650    mDNSBool result = mDNSfalse;
1651    SCNetworkConnectionFlags flags;
1652    CFDataRef remote_addr;
1653    CFMutableDictionaryRef options;
1654    SCNetworkReachabilityRef ReachRef = NULL;
1655
1656    options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1657    remote_addr = CFDataCreate(NULL, (const UInt8 *)addr, addr->sa_len);
1658    CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, remote_addr);
1659    CFDictionarySetValue(options, kSCNetworkReachabilityOptionServerBypass, kCFBooleanTrue);
1660    ReachRef = SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault, options);
1661    CFRelease(options);
1662    CFRelease(remote_addr);
1663
1664    if (!ReachRef)
1665    {
1666        LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithOptions");
1667        goto end;
1668    }
1669    if (!SCNetworkReachabilityGetFlags(ReachRef, &flags))
1670    {
1671        LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags");
1672        goto end;
1673    }
1674    result = flags & kSCNetworkFlagsConnectionRequired;
1675
1676end:
1677    if (ReachRef)
1678        CFRelease(ReachRef);
1679    return result;
1680}
1681
1682// Set traffic class for socket
1683mDNSlocal void setTrafficClass(int socketfd, mDNSBool useBackgroundTrafficClass)
1684{
1685    int traffic_class;
1686
1687    if (useBackgroundTrafficClass)
1688        traffic_class = SO_TC_BK_SYS;
1689    else
1690        traffic_class = SO_TC_CTL;
1691
1692    (void) setsockopt(socketfd, SOL_SOCKET, SO_TRAFFIC_CLASS, (void *)&traffic_class, sizeof(traffic_class));
1693}
1694
1695mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q)
1696{
1697    if (src)
1698    {
1699        int s;
1700
1701        if (dst->type == mDNSAddrType_IPv4)
1702        {
1703            s = src->ss.sktv4;
1704        }
1705        else
1706        {
1707            s = src->ss.sktv6;
1708        }
1709
1710        if (q->pid)
1711        {
1712            if (setsockopt(s, SOL_SOCKET, SO_DELEGATED, &q->pid, sizeof(q->pid)) == -1)
1713            {
1714                LogInfo("mDNSPlatformSetDelegatePID: Delegate PID failed %s for PID %d", strerror(errno), q->pid);
1715            }
1716        }
1717        else
1718        {
1719            if (setsockopt(s, SOL_SOCKET, SO_DELEGATED_UUID, &q->uuid, sizeof(q->uuid)) == -1)
1720            {
1721                LogInfo("mDNSPlatformSetDelegatePID: Delegate UUID failed %s", strerror(errno));
1722            }
1723        }
1724    }
1725}
1726
1727// Note: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket"
1728// Note: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface"
1729// OR send via our primary v4 unicast socket
1730// UPDATE: The UDPSocket *src parameter now allows the caller to specify the source socket
1731mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end,
1732                                       mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst,
1733                                       mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass)
1734{
1735    NetworkInterfaceInfoOSX *info = mDNSNULL;
1736    struct sockaddr_storage to;
1737    int s = -1, err;
1738    mStatus result = mStatus_NoError;
1739
1740    if (InterfaceID)
1741    {
1742        info = IfindexToInterfaceInfoOSX(m, InterfaceID);
1743        if (info == NULL)
1744        {
1745            // We may not have registered interfaces with the "core" as we may not have
1746            // seen any interface notifications yet. This typically happens during wakeup
1747            // where we might try to send DNS requests (non-SuppressUnusable questions internal
1748            // to mDNSResponder) before we receive network notifications.
1749            LogInfo("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID);
1750            return mStatus_BadParamErr;
1751        }
1752    }
1753
1754    char *ifa_name = InterfaceID ? info->ifinfo.ifname : "unicast";
1755
1756    if (dst->type == mDNSAddrType_IPv4)
1757    {
1758        struct sockaddr_in *sin_to = (struct sockaddr_in*)&to;
1759        sin_to->sin_len            = sizeof(*sin_to);
1760        sin_to->sin_family         = AF_INET;
1761        sin_to->sin_port           = dstPort.NotAnInteger;
1762        sin_to->sin_addr.s_addr    = dst->ip.v4.NotAnInteger;
1763        s = (src ? src->ss : m->p->permanentsockets).sktv4;
1764
1765        if (info)   // Specify outgoing interface
1766        {
1767            if (!mDNSAddrIsDNSMulticast(dst))
1768            {
1769                #ifdef IP_BOUND_IF
1770                if (info->scope_id == 0)
1771                    LogInfo("IP_BOUND_IF socket option not set -- info %p (%s) scope_id is zero", info, ifa_name);
1772                else
1773                    setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id));
1774                #else
1775                {
1776                    static int displayed = 0;
1777                    if (displayed < 1000)
1778                    {
1779                        displayed++;
1780                        LogInfo("IP_BOUND_IF socket option not defined -- cannot specify interface for unicast packets");
1781                    }
1782                }
1783                #endif
1784            }
1785            else
1786                #ifdef IP_MULTICAST_IFINDEX
1787            {
1788                err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IFINDEX, &info->scope_id, sizeof(info->scope_id));
1789                // We get an error when we compile on a machine that supports this option and run the binary on
1790                // a different machine that does not support it
1791                if (err < 0)
1792                {
1793                    if (errno != ENOPROTOOPT) LogInfo("mDNSPlatformSendUDP: setsockopt: IP_MUTLTICAST_IFINDEX returned %d", errno);
1794                    err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr));
1795                    if (err < 0 && !m->p->NetworkChanged)
1796                        LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno));
1797                }
1798            }
1799                #else
1800            {
1801                err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr));
1802                if (err < 0 && !m->p->NetworkChanged)
1803                    LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno));
1804
1805            }
1806                #endif
1807        }
1808    }
1809
1810    else if (dst->type == mDNSAddrType_IPv6)
1811    {
1812        struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to;
1813        sin6_to->sin6_len            = sizeof(*sin6_to);
1814        sin6_to->sin6_family         = AF_INET6;
1815        sin6_to->sin6_port           = dstPort.NotAnInteger;
1816        sin6_to->sin6_flowinfo       = 0;
1817        sin6_to->sin6_addr           = *(struct in6_addr*)&dst->ip.v6;
1818        sin6_to->sin6_scope_id       = info ? info->scope_id : 0;
1819        s = (src ? src->ss : m->p->permanentsockets).sktv6;
1820        if (info && mDNSAddrIsDNSMulticast(dst))    // Specify outgoing interface
1821        {
1822            err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &info->scope_id, sizeof(info->scope_id));
1823            if (err < 0)
1824            {
1825                char name[IFNAMSIZ];
1826                if (if_indextoname(info->scope_id, name) != NULL)
1827                    LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno));
1828                else
1829                    LogInfo("setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface", info->scope_id);
1830            }
1831        }
1832    }
1833
1834    else
1835    {
1836        LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!");
1837#if ForceAlerts
1838        *(long*)0 = 0;
1839#endif
1840        return mStatus_BadParamErr;
1841    }
1842
1843    if (s >= 0)
1844        verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %p %5s/%ld to %#a:%d skt %d",
1845                      InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s);
1846    else
1847        verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %p %5s/%ld (socket of this type not available)",
1848                      InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort));
1849
1850    // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet
1851    // If we don't have the corresponding type of socket available, then return mStatus_Invalid
1852    if (s < 0) return(mStatus_Invalid);
1853
1854    // switch to background traffic class for this message if requested
1855    if (useBackgroundTrafficClass)
1856        setTrafficClass(s, useBackgroundTrafficClass);
1857
1858    err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len);
1859
1860    // set traffic class back to default value
1861    if (useBackgroundTrafficClass)
1862        setTrafficClass(s, mDNSfalse);
1863
1864    if (err < 0)
1865    {
1866        static int MessageCount = 0;
1867        LogInfo("mDNSPlatformSendUDP -> sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu",
1868                s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow));
1869        if (!mDNSAddressIsAllDNSLinkGroup(dst))
1870            if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr);
1871        // Don't report EHOSTUNREACH in the first three minutes after boot
1872        // This is because mDNSResponder intentionally starts up early in the boot process (See <rdar://problem/3409090>)
1873        // but this means that sometimes it starts before configd has finished setting up the multicast routing entries.
1874        if (errno == EHOSTUNREACH && (mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(mStatus_TransientErr);
1875        // Don't report EADDRNOTAVAIL ("Can't assign requested address") if we're in the middle of a network configuration change
1876        if (errno == EADDRNOTAVAIL && m->p->NetworkChanged) return(mStatus_TransientErr);
1877        if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN)
1878            LogInfo("mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu",
1879                    s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow));
1880        else
1881        {
1882            MessageCount++;
1883            if (MessageCount < 50)  // Cap and ensure NO spamming of LogMsgs
1884                LogMsg("mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu MessageCount is %d",
1885                       s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow), MessageCount);
1886            else  // If logging is enabled, remove the cap and log aggressively
1887                LogInfo("mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu MessageCount is %d",
1888                        s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow), MessageCount);
1889        }
1890
1891        result = mStatus_UnknownErr;
1892    }
1893
1894#ifdef IP_BOUND_IF
1895    if (dst->type == mDNSAddrType_IPv4 && info && !mDNSAddrIsDNSMulticast(dst))
1896    {
1897        static const mDNSu32 ifindex = 0;
1898        setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex));
1899    }
1900#endif
1901
1902    return(result);
1903}
1904
1905mDNSexport ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
1906                             struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl)
1907{
1908    static unsigned int numLogMessages = 0;
1909    struct iovec databuffers = { (char *)buffer, max };
1910    struct msghdr msg;
1911    ssize_t n;
1912    struct cmsghdr *cmPtr;
1913    char ancillary[1024];
1914
1915    *ttl = 255;  // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be
1916
1917    // Set up the message
1918    msg.msg_name       = (caddr_t)from;
1919    msg.msg_namelen    = *fromlen;
1920    msg.msg_iov        = &databuffers;
1921    msg.msg_iovlen     = 1;
1922    msg.msg_control    = (caddr_t)&ancillary;
1923    msg.msg_controllen = sizeof(ancillary);
1924    msg.msg_flags      = 0;
1925
1926    // Receive the data
1927    n = recvmsg(s, &msg, 0);
1928    if (n<0)
1929    {
1930        if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned error %d errno %d", s, n, errno);
1931        return(-1);
1932    }
1933    if (msg.msg_controllen < (int)sizeof(struct cmsghdr))
1934    {
1935        if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu, errno %d",
1936                                           s, n, msg.msg_controllen, sizeof(struct cmsghdr), errno);
1937        return(-1);
1938    }
1939    if (msg.msg_flags & MSG_CTRUNC)
1940    {
1941        if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s);
1942        return(-1);
1943    }
1944
1945    *fromlen = msg.msg_namelen;
1946
1947    // Parse each option out of the ancillary data.
1948    for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr))
1949    {
1950        // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type);
1951        if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR)
1952        {
1953            dstaddr->type = mDNSAddrType_IPv4;
1954            dstaddr->ip.v4 = *(mDNSv4Addr*)CMSG_DATA(cmPtr);
1955            //LogMsg("mDNSMacOSX.c: recvmsg IP_RECVDSTADDR %.4a", &dstaddr->ip.v4);
1956        }
1957        if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF)
1958        {
1959            struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr);
1960            if (sdl->sdl_nlen < IF_NAMESIZE)
1961            {
1962                mDNSPlatformMemCopy(ifname, sdl->sdl_data, sdl->sdl_nlen);
1963                ifname[sdl->sdl_nlen] = 0;
1964                // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen);
1965            }
1966        }
1967        if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL)
1968            *ttl = *(u_char*)CMSG_DATA(cmPtr);
1969        if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO)
1970        {
1971            struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr);
1972            dstaddr->type = mDNSAddrType_IPv6;
1973            dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr;
1974            myIfIndexToName(ip6_info->ipi6_ifindex, ifname);
1975        }
1976        if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT)
1977            *ttl = *(int*)CMSG_DATA(cmPtr);
1978    }
1979
1980    return(n);
1981}
1982
1983mDNSlocal mDNSInterfaceID FindMyInterface(mDNS *const m, const mDNSAddr *addr)
1984{
1985    NetworkInterfaceInfo *intf;
1986
1987    if (addr->type == mDNSAddrType_IPv4)
1988    {
1989        for (intf = m->HostInterfaces; intf; intf = intf->next)
1990        {
1991            if (intf->ip.type == addr->type && intf->McastTxRx)
1992            {
1993                if ((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) == 0)
1994                {
1995                    return(intf->InterfaceID);
1996                }
1997            }
1998        }
1999    }
2000
2001    if (addr->type == mDNSAddrType_IPv6)
2002    {
2003        for (intf = m->HostInterfaces; intf; intf = intf->next)
2004        {
2005            if (intf->ip.type == addr->type && intf->McastTxRx)
2006            {
2007                if (((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) == 0) &&
2008                    ((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) == 0) &&
2009                    ((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) == 0) &&
2010                    (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) == 0)))
2011                    {
2012                        return(intf->InterfaceID);
2013                    }
2014            }
2015        }
2016    }
2017    return(mDNSInterface_Any);
2018}
2019
2020mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src)
2021{
2022    // We should have a DNSMessage header followed by the question and an answer
2023    // which also includes a CNAME (that's when this function is called).  To keep it
2024    // simple, we expect at least the size of DNSMessage header(12) and size of "A"
2025    // record (14 bytes).
2026    char buffer[26];
2027    int ret;
2028
2029    (void) m;
2030
2031    if (!src)
2032        return mDNSfalse;
2033
2034    ret = recv(src->ss.sktv4, buffer, sizeof(buffer), MSG_PEEK);
2035    if (ret > 0)
2036        return mDNStrue;
2037    else
2038        return mDNSfalse;
2039}
2040
2041mDNSexport void myKQSocketCallBack(int s1, short filter, void *context)
2042{
2043    KQSocketSet *const ss = (KQSocketSet *)context;
2044    mDNS *const m = ss->m;
2045    int err = 0, count = 0, closed = 0;
2046
2047    if (filter != EVFILT_READ)
2048        LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter, EVFILT_READ);
2049
2050    if (s1 != ss->sktv4 && s1 != ss->sktv6)
2051    {
2052        LogMsg("myKQSocketCallBack: native socket %d", s1);
2053        LogMsg("myKQSocketCallBack: sktv4 %d sktv6 %d", ss->sktv4, ss->sktv6);
2054    }
2055
2056    while (!closed)
2057    {
2058        mDNSAddr senderAddr, destAddr;
2059        mDNSIPPort senderPort;
2060        struct sockaddr_storage from;
2061        size_t fromlen = sizeof(from);
2062        char packetifname[IF_NAMESIZE] = "";
2063        mDNSu8 ttl;
2064        err = myrecvfrom(s1, &m->imsg, sizeof(m->imsg), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl);
2065        if (err < 0) break;
2066
2067        count++;
2068        if (from.ss_family == AF_INET)
2069        {
2070            struct sockaddr_in *s = (struct sockaddr_in*)&from;
2071            senderAddr.type = mDNSAddrType_IPv4;
2072            senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr;
2073            senderPort.NotAnInteger = s->sin_port;
2074            //LogInfo("myKQSocketCallBack received IPv4 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname);
2075        }
2076        else if (from.ss_family == AF_INET6)
2077        {
2078            struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from;
2079            senderAddr.type = mDNSAddrType_IPv6;
2080            senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr;
2081            senderPort.NotAnInteger = sin6->sin6_port;
2082            //LogInfo("myKQSocketCallBack received IPv6 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname);
2083        }
2084        else
2085        {
2086            LogMsg("myKQSocketCallBack from is unknown address family %d", from.ss_family);
2087            return;
2088        }
2089
2090        // Note: When handling multiple packets in a batch, MUST reset InterfaceID before handling each packet
2091        mDNSInterfaceID InterfaceID = mDNSNULL;
2092        NetworkInterfaceInfoOSX *intf = m->p->InterfaceList;
2093        while (intf)
2094        {
2095            if (intf->Exists && !strcmp(intf->ifinfo.ifname, packetifname))
2096                break;
2097            intf = intf->next;
2098        }
2099
2100        // When going to sleep we deregister all our interfaces, but if the machine
2101        // takes a few seconds to sleep we may continue to receive multicasts
2102        // during that time, which would confuse mDNSCoreReceive, because as far
2103        // as it's concerned, we should have no active interfaces any more.
2104        // Hence we ignore multicasts for which we can find no matching InterfaceID.
2105        if (intf)
2106            InterfaceID = intf->ifinfo.InterfaceID;
2107        else if (mDNSAddrIsDNSMulticast(&destAddr))
2108            continue;
2109
2110        if (!InterfaceID)
2111        {
2112            InterfaceID = FindMyInterface(m, &destAddr);
2113        }
2114
2115//		LogMsg("myKQSocketCallBack got packet from %#a to %#a on interface %#a/%s",
2116//			&senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifinfo.ifname);
2117
2118        // mDNSCoreReceive may close the socket we're reading from.  We must break out of our
2119        // loop when that happens, or we may try to read from an invalid FD.  We do this by
2120        // setting the closeFlag pointer in the socketset, so CloseSocketSet can inform us
2121        // if it closes the socketset.
2122        ss->closeFlag = &closed;
2123
2124        if (ss->proxy)
2125        {
2126            m->p->UDPProxyCallback(m, &m->p->UDPProxy, (unsigned char *)&m->imsg, (unsigned char*)&m->imsg + err, &senderAddr,
2127                senderPort, &destAddr, ss->port, InterfaceID, NULL);
2128        }
2129        else
2130        {
2131            mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID);
2132        }
2133
2134        // if we didn't close, we can safely dereference the socketset, and should to
2135        // reset the closeFlag, since it points to something on the stack
2136        if (!closed) ss->closeFlag = mDNSNULL;
2137    }
2138
2139    if (err < 0 && (errno != EWOULDBLOCK || count == 0))
2140    {
2141        // Something is busted here.
2142        // kqueue says there is a packet, but myrecvfrom says there is not.
2143        // Try calling select() to get another opinion.
2144        // Find out about other socket parameter that can help understand why select() says the socket is ready for read
2145        // All of this is racy, as data may have arrived after the call to select()
2146        static unsigned int numLogMessages = 0;
2147        int save_errno = errno;
2148        int so_error = -1;
2149        int so_nread = -1;
2150        int fionread = -1;
2151        socklen_t solen = sizeof(int);
2152        fd_set readfds;
2153        struct timeval timeout;
2154        int selectresult;
2155        FD_ZERO(&readfds);
2156        FD_SET(s1, &readfds);
2157        timeout.tv_sec  = 0;
2158        timeout.tv_usec = 0;
2159        selectresult = select(s1+1, &readfds, NULL, NULL, &timeout);
2160        if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1)
2161            LogMsg("myKQSocketCallBack getsockopt(SO_ERROR) error %d", errno);
2162        if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1)
2163            LogMsg("myKQSocketCallBack getsockopt(SO_NREAD) error %d", errno);
2164        if (ioctl(s1, FIONREAD, &fionread) == -1)
2165            LogMsg("myKQSocketCallBack ioctl(FIONREAD) error %d", errno);
2166        if (numLogMessages++ < 100)
2167            LogMsg("myKQSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d",
2168                   s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count);
2169        if (numLogMessages > 5)
2170            NotifyOfElusiveBug("Flaw in Kernel (select/recvfrom mismatch)",
2171                               "Congratulations, you've reproduced an elusive bug.\r"
2172                               "Please contact the current assignee of <rdar://problem/3375328>.\r"
2173                               "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r"
2174                               "If possible, please leave your machine undisturbed so that someone can come to investigate the problem.");
2175
2176        sleep(1);       // After logging this error, rate limit so we don't flood syslog
2177    }
2178}
2179
2180mDNSlocal void doTcpSocketCallback(TCPSocket *sock)
2181{
2182    mDNSBool c = !sock->connected;
2183    sock->connected = mDNStrue;
2184    sock->callback(sock, sock->context, c, sock->err);
2185    // Note: the callback may call CloseConnection here, which frees the context structure!
2186}
2187
2188#ifndef NO_SECURITYFRAMEWORK
2189
2190mDNSlocal OSStatus tlsWriteSock(SSLConnectionRef connection, const void *data, size_t *dataLength)
2191{
2192    int ret = send(((TCPSocket *)connection)->fd, data, *dataLength, 0);
2193    if (ret >= 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); }
2194    if (ret >= 0)                              { *dataLength = ret; return(noErr); }
2195    *dataLength = 0;
2196    if (errno == EAGAIN                      ) return(errSSLWouldBlock);
2197    if (errno == ENOENT                      ) return(errSSLClosedGraceful);
2198    if (errno == EPIPE || errno == ECONNRESET) return(errSSLClosedAbort);
2199    LogMsg("ERROR: tlsWriteSock: %d error %d (%s)\n", ((TCPSocket *)connection)->fd, errno, strerror(errno));
2200    return(errSSLClosedAbort);
2201}
2202
2203mDNSlocal OSStatus tlsReadSock(SSLConnectionRef connection, void *data, size_t *dataLength)
2204{
2205    int ret = recv(((TCPSocket *)connection)->fd, data, *dataLength, 0);
2206    if (ret > 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); }
2207    if (ret > 0)                              { *dataLength = ret; return(noErr); }
2208    *dataLength = 0;
2209    if (ret == 0 || errno == ENOENT    ) return(errSSLClosedGraceful);
2210    if (            errno == EAGAIN    ) return(errSSLWouldBlock);
2211    if (            errno == ECONNRESET) return(errSSLClosedAbort);
2212    LogMsg("ERROR: tlsSockRead: error %d (%s)\n", errno, strerror(errno));
2213    return(errSSLClosedAbort);
2214}
2215
2216mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, SSLProtocolSide pside, SSLConnectionType ctype)
2217{
2218    char domname_cstr[MAX_ESCAPED_DOMAIN_NAME];
2219
2220    sock->tlsContext = SSLCreateContext(kCFAllocatorDefault, pside, ctype);
2221    if (!sock->tlsContext)
2222    {
2223        LogMsg("ERROR: tlsSetupSock: SSLCreateContext failed");
2224        return(mStatus_UnknownErr);
2225    }
2226
2227    mStatus err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock);
2228    if (err)
2229    {
2230        LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err);
2231        goto fail;
2232    }
2233
2234    err = SSLSetConnection(sock->tlsContext, (SSLConnectionRef) sock);
2235    if (err)
2236    {
2237        LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err);
2238        goto fail;
2239    }
2240
2241    // Instead of listing all the acceptable ciphers, we just disable the bad ciphers. It does not disable
2242    // all the bad ciphers like RC4_MD5, but it assumes that the servers don't offer them.
2243    err = SSLSetAllowAnonymousCiphers(sock->tlsContext, 0);
2244    if (err)
2245    {
2246        LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err);
2247        goto fail;
2248    }
2249
2250    // We already checked for NULL in hostname and this should never happen. Hence, returning -1
2251    // (error not in OSStatus space) is okay.
2252    if (!sock->hostname.c[0])
2253    {
2254        LogMsg("ERROR: tlsSetupSock: hostname NULL");
2255        err = -1;
2256        goto fail;
2257    }
2258
2259    ConvertDomainNameToCString(&sock->hostname, domname_cstr);
2260    err = SSLSetPeerDomainName(sock->tlsContext, domname_cstr, strlen(domname_cstr));
2261    if (err)
2262    {
2263        LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err);
2264        goto fail;
2265    }
2266
2267    return(err);
2268
2269fail:
2270    if (sock->tlsContext)
2271        CFRelease(sock->tlsContext);
2272    return(err);
2273}
2274
2275#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2276mDNSlocal void doSSLHandshake(TCPSocket *sock)
2277{
2278    mStatus err = SSLHandshake(sock->tlsContext);
2279
2280    //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is
2281    //defined, KQueueLock is a noop. Hence we need to serialize here
2282    //
2283    //NOTE: We just can't serialize doTcpSocketCallback alone on the main queue.
2284    //We need the rest of the logic also. Otherwise, we can enable the READ
2285    //events below, dispatch a doTcpSocketCallback on the main queue. Assume it is
2286    //ConnFailed which means we are going to free the tcpInfo. While it
2287    //is waiting to be dispatched, another read event can come into tcpKQSocketCallback
2288    //and potentially call doTCPCallback with error which can close the fd and free the
2289    //tcpInfo. Later when the thread gets dispatched it will crash because the tcpInfo
2290    //is already freed.
2291
2292    dispatch_async(dispatch_get_main_queue(), ^{
2293
2294                       LogInfo("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock
2295
2296                       if (sock->handshake == handshake_to_be_closed)
2297                       {
2298                           LogInfo("SSLHandshake completed after close");
2299                           mDNSPlatformTCPCloseConnection(sock);
2300                       }
2301                       else
2302                       {
2303                           if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry);
2304                           else LogMsg("doSSLHandshake: sock->fd is -1");
2305
2306                           if (err == errSSLWouldBlock)
2307                               sock->handshake = handshake_required;
2308                           else
2309                           {
2310                               if (err)
2311                               {
2312                                   LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : "");
2313                                   CFRelease(sock->tlsContext);
2314                                   sock->tlsContext = NULL;
2315                               }
2316
2317                               sock->err = err ? mStatus_ConnFailed : 0;
2318                               sock->handshake = handshake_completed;
2319
2320                               LogInfo("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd);
2321                               doTcpSocketCallback(sock);
2322                           }
2323                       }
2324
2325                       LogInfo("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd);
2326                       return;
2327                   });
2328}
2329#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2330mDNSlocal void *doSSLHandshake(TCPSocket *sock)
2331{
2332    // Warning: Touching sock without the kqueue lock!
2333    // We're protected because sock->handshake == handshake_in_progress
2334    mDNS * const m = sock->m; // Get m now, as we may free sock if marked to be closed while we're waiting on SSLHandshake
2335    mStatus err = SSLHandshake(sock->tlsContext);
2336
2337    KQueueLock(m);
2338    debugf("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock
2339
2340    if (sock->handshake == handshake_to_be_closed)
2341    {
2342        LogInfo("SSLHandshake completed after close");
2343        mDNSPlatformTCPCloseConnection(sock);
2344    }
2345    else
2346    {
2347        if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry);
2348        else LogMsg("doSSLHandshake: sock->fd is -1");
2349
2350        if (err == errSSLWouldBlock)
2351            sock->handshake = handshake_required;
2352        else
2353        {
2354            if (err)
2355            {
2356                LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : "");
2357                CFRelease(sock->tlsContext);
2358                sock->tlsContext = NULL;
2359            }
2360
2361            sock->err = err ? mStatus_ConnFailed : 0;
2362            sock->handshake = handshake_completed;
2363
2364            debugf("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd);
2365            doTcpSocketCallback(sock);
2366        }
2367    }
2368
2369    debugf("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd);
2370    KQueueUnlock(m, "doSSLHandshake");
2371    return NULL;
2372}
2373#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2374
2375mDNSlocal void spawnSSLHandshake(TCPSocket* sock)
2376{
2377    debugf("spawnSSLHandshake %p: entry", sock);
2378
2379    if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake);
2380    sock->handshake = handshake_in_progress;
2381    KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, sock->kqEntry);
2382
2383    // Dispatch it on a separate queue to help avoid blocking other threads/queues, and
2384    // to limit the number of threads used for SSLHandshake
2385    dispatch_async(SSLqueue, ^{doSSLHandshake(sock);});
2386
2387    debugf("spawnSSLHandshake %p: done for %d", sock, sock->fd);
2388}
2389
2390#endif /* NO_SECURITYFRAMEWORK */
2391
2392mDNSlocal void tcpKQSocketCallback(__unused int fd, short filter, void *context)
2393{
2394    TCPSocket *sock = context;
2395    sock->err = mStatus_NoError;
2396
2397    //if (filter == EVFILT_READ ) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_READ", filter);
2398    //if (filter == EVFILT_WRITE) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_WRITE", filter);
2399    // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it here with EV_DELETE
2400    if (filter == EVFILT_WRITE)
2401        KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry);
2402
2403    if (sock->flags & kTCPSocketFlags_UseTLS)
2404    {
2405#ifndef NO_SECURITYFRAMEWORK
2406        if (!sock->setup)
2407        {
2408            sock->setup = mDNStrue;
2409            sock->err = tlsSetupSock(sock, kSSLClientSide, kSSLStreamType);
2410            if (sock->err)
2411            {
2412                LogMsg("ERROR: tcpKQSocketCallback: tlsSetupSock failed with error code: %d", sock->err);
2413                return;
2414            }
2415        }
2416        if (sock->handshake == handshake_required)
2417        {
2418            spawnSSLHandshake(sock);
2419            return;
2420        }
2421        else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed)
2422        {
2423            return;
2424        }
2425        else if (sock->handshake != handshake_completed)
2426        {
2427            if (!sock->err)
2428                sock->err = mStatus_UnknownErr;
2429            LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock->handshake);
2430        }
2431#else  /* NO_SECURITYFRAMEWORK */
2432        sock->err = mStatus_UnsupportedErr;
2433#endif /* NO_SECURITYFRAMEWORK */
2434    }
2435
2436    doTcpSocketCallback(sock);
2437}
2438
2439#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2440mDNSexport int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef)
2441{
2442    dispatch_queue_t queue = dispatch_get_main_queue();
2443    dispatch_source_t source;
2444    if (flags == EV_DELETE)
2445    {
2446        if (filter == EVFILT_READ)
2447        {
2448            dispatch_source_cancel(entryRef->readSource);
2449            dispatch_release(entryRef->readSource);
2450            entryRef->readSource = mDNSNULL;
2451            debugf("KQueueSet: source cancel for read %p, %p", entryRef->readSource, entryRef->writeSource);
2452        }
2453        else if (filter == EVFILT_WRITE)
2454        {
2455            dispatch_source_cancel(entryRef->writeSource);
2456            dispatch_release(entryRef->writeSource);
2457            entryRef->writeSource = mDNSNULL;
2458            debugf("KQueueSet: source cancel for write %p, %p", entryRef->readSource, entryRef->writeSource);
2459        }
2460        else
2461            LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_DELETE", filter);
2462        return 0;
2463    }
2464    if (flags != EV_ADD) LogMsg("KQueueSet: Invalid flags %d", flags);
2465
2466    if (filter == EVFILT_READ)
2467    {
2468        source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue);
2469    }
2470    else if (filter == EVFILT_WRITE)
2471    {
2472        source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, queue);
2473    }
2474    else
2475    {
2476        LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_ADD", filter);
2477        return -1;
2478    }
2479    if (!source) return -1;
2480    dispatch_source_set_event_handler(source, ^{
2481
2482                                          mDNSs32 stime = mDNSPlatformRawTime();
2483                                          entryRef->KQcallback(fd, filter, entryRef->KQcontext);
2484                                          mDNSs32 etime = mDNSPlatformRawTime();
2485                                          if (etime - stime >= WatchDogReportingThreshold)
2486                                              LogInfo("KQEntryCallback Block: WARNING: took %dms to complete", etime - stime);
2487
2488                                          // Trigger the event delivery to the application. Even though we trigger the
2489                                          // event completion after handling every event source, these all will hopefully
2490                                          // get merged
2491                                          TriggerEventCompletion();
2492
2493                                      });
2494    dispatch_source_set_cancel_handler(source, ^{
2495                                           if (entryRef->fdClosed)
2496                                           {
2497                                               //LogMsg("CancelHandler: closing fd %d", fd);
2498                                               close(fd);
2499                                           }
2500                                       });
2501    dispatch_resume(source);
2502    if (filter == EVFILT_READ)
2503        entryRef->readSource = source;
2504    else
2505        entryRef->writeSource = source;
2506
2507    return 0;
2508}
2509
2510mDNSexport void KQueueLock(mDNS *const m)
2511{
2512    (void)m; //unused
2513}
2514mDNSexport void KQueueUnlock(mDNS *const m, const char const *task)
2515{
2516    (void)m; //unused
2517    (void)task; //unused
2518}
2519#else
2520mDNSexport int KQueueSet(int fd, u_short flags, short filter, const KQueueEntry *const entryRef)
2521{
2522    struct kevent new_event;
2523    EV_SET(&new_event, fd, filter, flags, 0, 0, (void*)entryRef);
2524    return (kevent(KQueueFD, &new_event, 1, NULL, 0, NULL) < 0) ? errno : 0;
2525}
2526
2527mDNSexport void KQueueLock(mDNS *const m)
2528{
2529    pthread_mutex_lock(&m->p->BigMutex);
2530    m->p->BigMutexStartTime = mDNSPlatformRawTime();
2531}
2532
2533mDNSexport void KQueueUnlock(mDNS *const m, const char* task)
2534{
2535    mDNSs32 end = mDNSPlatformRawTime();
2536    (void)task;
2537    if (end - m->p->BigMutexStartTime >= WatchDogReportingThreshold)
2538        LogInfo("WARNING: %s took %dms to complete", task, end - m->p->BigMutexStartTime);
2539
2540    pthread_mutex_unlock(&m->p->BigMutex);
2541
2542    char wake = 1;
2543    if (send(m->p->WakeKQueueLoopFD, &wake, sizeof(wake), 0) == -1)
2544        LogMsg("ERROR: KQueueWake: send failed with error code: %d (%s)", errno, strerror(errno));
2545}
2546#endif
2547
2548mDNSexport void mDNSPlatformCloseFD(KQueueEntry *kq, int fd)
2549{
2550#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2551        (void) fd; //unused
2552    if (kq->readSource)
2553    {
2554        dispatch_source_cancel(kq->readSource);
2555        kq->readSource = mDNSNULL;
2556    }
2557    if (kq->writeSource)
2558    {
2559        dispatch_source_cancel(kq->writeSource);
2560        kq->writeSource = mDNSNULL;
2561    }
2562    // Close happens in the cancellation handler
2563    debugf("mDNSPlatformCloseFD: resetting sources for %d", fd);
2564    kq->fdClosed = mDNStrue;
2565#else
2566    (void)kq; //unused
2567    close(fd);
2568#endif
2569}
2570
2571mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass)
2572{
2573    KQSocketSet *cp = &sock->ss;
2574    int         *s        = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
2575    KQueueEntry *k        = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
2576    const int on = 1;  // "on" for setsockopt
2577    mStatus err;
2578
2579    int skt = socket(sa_family, SOCK_STREAM, IPPROTO_TCP);
2580    if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupTCPSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno));return(skt); }
2581
2582    // for TCP sockets, the traffic class is set once and not changed
2583    setTrafficClass(skt, useBackgroundTrafficClass);
2584
2585    if (sa_family == AF_INET)
2586    {
2587        // Bind it
2588        struct sockaddr_in addr;
2589        mDNSPlatformMemZero(&addr, sizeof(addr));
2590        addr.sin_family = AF_INET;
2591        addr.sin_port = port->NotAnInteger;
2592        err = bind(skt, (struct sockaddr*) &addr, sizeof(addr));
2593        if (err < 0) { LogMsg("ERROR: bind %s", strerror(errno)); return err; }
2594
2595        // Receive interface identifiers
2596        err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
2597        if (err < 0) { LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); return err; }
2598
2599        mDNSPlatformMemZero(&addr, sizeof(addr));
2600        socklen_t len = sizeof(addr);
2601        err = getsockname(skt, (struct sockaddr*) &addr, &len);
2602        if (err < 0) { LogMsg("getsockname - %s", strerror(errno)); return err; }
2603
2604        port->NotAnInteger = addr.sin_port;
2605    }
2606    else
2607    {
2608        // Bind it
2609        struct sockaddr_in6 addr6;
2610        mDNSPlatformMemZero(&addr6, sizeof(addr6));
2611        addr6.sin6_family = AF_INET6;
2612        addr6.sin6_port = port->NotAnInteger;
2613        err = bind(skt, (struct sockaddr*) &addr6, sizeof(addr6));
2614        if (err < 0) { LogMsg("ERROR: bind6 %s", strerror(errno)); return err; }
2615
2616        // We want to receive destination addresses and receive interface identifiers
2617        err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
2618        if (err < 0) { LogMsg("ERROR: setsockopt IPV6_RECVPKTINFO %s", strerror(errno)); return err; }
2619
2620        mDNSPlatformMemZero(&addr6, sizeof(addr6));
2621        socklen_t len = sizeof(addr6);
2622        err = getsockname(skt, (struct sockaddr *) &addr6, &len);
2623        if (err < 0) { LogMsg("getsockname6 - %s", strerror(errno)); return err; }
2624
2625        port->NotAnInteger = addr6.sin6_port;
2626
2627    }
2628    *s = skt;
2629    k->KQcallback = tcpKQSocketCallback;
2630    k->KQcontext  = sock;
2631    k->KQtask     = "mDNSPlatformTCPSocket";
2632#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2633    k->readSource = mDNSNULL;
2634    k->writeSource = mDNSNULL;
2635    k->fdClosed = mDNSfalse;
2636#endif
2637    return mStatus_NoError;
2638}
2639
2640mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass)
2641{
2642    mStatus err;
2643    (void) m;
2644
2645    TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPSocket", sizeof(TCPSocket));
2646    if (!sock) { LogMsg("mDNSPlatformTCPSocket: memory allocation failure"); return(mDNSNULL); }
2647
2648    mDNSPlatformMemZero(sock, sizeof(TCPSocket));
2649
2650    sock->ss.m     = m;
2651    sock->ss.sktv4 = -1;
2652    sock->ss.sktv6 = -1;
2653    err = SetupTCPSocket(sock, AF_INET, port, useBackgroundTrafficClass);
2654
2655    if (!err)
2656    {
2657        err = SetupTCPSocket(sock, AF_INET6, port, useBackgroundTrafficClass);
2658        if (err) { mDNSPlatformCloseFD(&sock->ss.kqsv4, sock->ss.sktv4); sock->ss.sktv4 = -1; }
2659    }
2660    if (err)
2661    {
2662        LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock->fd, errno, strerror(errno));
2663        freeL("TCPSocket/mDNSPlatformTCPSocket", sock);
2664        return(mDNSNULL);
2665    }
2666    // sock->fd is used as the default fd  if the caller does not call mDNSPlatformTCPConnect
2667    sock->fd                = sock->ss.sktv4;
2668    sock->callback          = mDNSNULL;
2669    sock->flags             = flags;
2670    sock->context           = mDNSNULL;
2671    sock->setup             = mDNSfalse;
2672    sock->connected         = mDNSfalse;
2673    sock->handshake         = handshake_required;
2674    sock->m                 = m;
2675    sock->err               = mStatus_NoError;
2676
2677    return sock;
2678}
2679
2680mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context)
2681{
2682    KQSocketSet *cp = &sock->ss;
2683    int         *s        = (dst->type == mDNSAddrType_IPv4) ? &cp->sktv4 : &cp->sktv6;
2684    KQueueEntry *k        = (dst->type == mDNSAddrType_IPv4) ? &cp->kqsv4 : &cp->kqsv6;
2685    mStatus err = mStatus_NoError;
2686    struct sockaddr_storage ss;
2687
2688    sock->callback          = callback;
2689    sock->context           = context;
2690    sock->setup             = mDNSfalse;
2691    sock->connected         = mDNSfalse;
2692    sock->handshake         = handshake_required;
2693    sock->err               = mStatus_NoError;
2694
2695    if (hostname) { debugf("mDNSPlatformTCPConnect: hostname %##s", hostname->c); AssignDomainName(&sock->hostname, hostname); }
2696
2697    if (dst->type == mDNSAddrType_IPv4)
2698    {
2699        struct sockaddr_in *saddr = (struct sockaddr_in *)&ss;
2700        mDNSPlatformMemZero(saddr, sizeof(*saddr));
2701        saddr->sin_family      = AF_INET;
2702        saddr->sin_port        = dstport.NotAnInteger;
2703        saddr->sin_len         = sizeof(*saddr);
2704        saddr->sin_addr.s_addr = dst->ip.v4.NotAnInteger;
2705    }
2706    else
2707    {
2708        struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&ss;
2709        mDNSPlatformMemZero(saddr6, sizeof(*saddr6));
2710        saddr6->sin6_family      = AF_INET6;
2711        saddr6->sin6_port        = dstport.NotAnInteger;
2712        saddr6->sin6_len         = sizeof(*saddr6);
2713        saddr6->sin6_addr        = *(struct in6_addr *)&dst->ip.v6;
2714    }
2715
2716    // Watch for connect complete (write is ready)
2717    // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it in tcpKQSocketCallback using EV_DELETE
2718    if (KQueueSet(*s, EV_ADD /* | EV_ONESHOT */, EVFILT_WRITE, k))
2719    {
2720        LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed");
2721        return errno;
2722    }
2723
2724    // Watch for incoming data
2725    if (KQueueSet(*s, EV_ADD, EVFILT_READ, k))
2726    {
2727        LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed");
2728        return errno;
2729    }
2730
2731    if (fcntl(*s, F_SETFL, fcntl(*s, F_GETFL, 0) | O_NONBLOCK) < 0) // set non-blocking
2732    {
2733        LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno));
2734        return mStatus_UnknownErr;
2735    }
2736
2737    // We bind to the interface and all subsequent packets including the SYN will be sent out
2738    // on this interface
2739    //
2740    // Note: If we are in Active Directory domain, we may try TCP (if the response can't fit in
2741    // UDP). mDNSInterface_Unicast indicates this case and not a valid interface.
2742    if (InterfaceID && InterfaceID != mDNSInterface_Unicast)
2743    {
2744        NetworkInterfaceInfoOSX *info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID);
2745        if (dst->type == mDNSAddrType_IPv4)
2746        {
2747        #ifdef IP_BOUND_IF
2748            if (info) setsockopt(*s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id));
2749            else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; }
2750        #else
2751            (void)InterfaceID; // Unused
2752            (void)info; // Unused
2753        #endif
2754        }
2755        else
2756        {
2757        #ifdef IPV6_BOUND_IF
2758            if (info) setsockopt(*s, IPPROTO_IPV6, IPV6_BOUND_IF, &info->scope_id, sizeof(info->scope_id));
2759            else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; }
2760        #else
2761            (void)InterfaceID; // Unused
2762            (void)info; // Unused
2763        #endif
2764        }
2765    }
2766
2767    // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address
2768    // from which we can infer the destination address family. Hence we need to remember that here.
2769    // Instead of remembering the address family, we remember the right fd.
2770    sock->fd = *s;
2771    sock->kqEntry = k;
2772    // initiate connection wth peer
2773    if (connect(*s, (struct sockaddr *)&ss, ss.ss_len) < 0)
2774    {
2775        if (errno == EINPROGRESS) return mStatus_ConnPending;
2776        if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN)
2777            LogInfo("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s)", sock->fd, errno, strerror(errno));
2778        else
2779            LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s) length %d", sock->fd, errno, strerror(errno), ss.ss_len);
2780        return mStatus_ConnFailed;
2781    }
2782
2783    LogMsg("NOTE: mDNSPlatformTCPConnect completed synchronously");
2784    // kQueue should notify us, but this LogMsg is to help track down if it doesn't
2785    return err;
2786}
2787
2788// Why doesn't mDNSPlatformTCPAccept actually call accept() ?
2789mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int fd)
2790{
2791    mStatus err = mStatus_NoError;
2792
2793    TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPAccept", sizeof(TCPSocket));
2794    if (!sock) return(mDNSNULL);
2795
2796    mDNSPlatformMemZero(sock, sizeof(*sock));
2797    sock->fd = fd;
2798    sock->flags = flags;
2799
2800    if (flags & kTCPSocketFlags_UseTLS)
2801    {
2802#ifndef NO_SECURITYFRAMEWORK
2803        if (!ServerCerts) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err = mStatus_UnknownErr; goto exit; }
2804
2805        err = tlsSetupSock(sock, kSSLServerSide, kSSLStreamType);
2806        if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err); goto exit; }
2807
2808        err = SSLSetCertificate(sock->tlsContext, ServerCerts);
2809        if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: SSLSetCertificate failed with error code: %d", err); goto exit; }
2810#else
2811        err = mStatus_UnsupportedErr;
2812#endif /* NO_SECURITYFRAMEWORK */
2813    }
2814#ifndef NO_SECURITYFRAMEWORK
2815exit:
2816#endif
2817
2818    if (err) { freeL("TCPSocket/mDNSPlatformTCPAccept", sock); return(mDNSNULL); }
2819    return(sock);
2820}
2821
2822mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock)
2823{
2824    mDNSu16 port;
2825
2826    port = -1;
2827    if (sock)
2828    {
2829        port = sock->ss.port.NotAnInteger;
2830    }
2831    return port;
2832}
2833
2834mDNSlocal void CloseSocketSet(KQSocketSet *ss)
2835{
2836    if (ss->sktv4 != -1)
2837    {
2838        mDNSPlatformCloseFD(&ss->kqsv4,  ss->sktv4);
2839        ss->sktv4 = -1;
2840    }
2841    if (ss->sktv6 != -1)
2842    {
2843        mDNSPlatformCloseFD(&ss->kqsv6,  ss->sktv6);
2844        ss->sktv6 = -1;
2845    }
2846    if (ss->closeFlag) *ss->closeFlag = 1;
2847}
2848
2849mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock)
2850{
2851    if (sock)
2852    {
2853#ifndef NO_SECURITYFRAMEWORK
2854        if (sock->tlsContext)
2855        {
2856            if (sock->handshake == handshake_in_progress) // SSLHandshake thread using this sock (esp. tlsContext)
2857            {
2858                LogInfo("mDNSPlatformTCPCloseConnection: called while handshake in progress");
2859                // When we come back from SSLHandshake, we will notice that a close was here and
2860                // call this function again which will do the cleanup then.
2861                sock->handshake = handshake_to_be_closed;
2862                return;
2863            }
2864
2865            SSLClose(sock->tlsContext);
2866            CFRelease(sock->tlsContext);
2867            sock->tlsContext = NULL;
2868        }
2869#endif /* NO_SECURITYFRAMEWORK */
2870        if (sock->ss.sktv4 != -1)
2871            shutdown(sock->ss.sktv4, 2);
2872        if (sock->ss.sktv6 != -1)
2873            shutdown(sock->ss.sktv6, 2);
2874        CloseSocketSet(&sock->ss);
2875        sock->fd = -1;
2876
2877        freeL("TCPSocket/mDNSPlatformTCPCloseConnection", sock);
2878    }
2879}
2880
2881mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed)
2882{
2883    ssize_t nread = 0;
2884    *closed = mDNSfalse;
2885
2886    if (sock->flags & kTCPSocketFlags_UseTLS)
2887    {
2888#ifndef NO_SECURITYFRAMEWORK
2889        if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformReadTCP called while handshake required"); return 0; }
2890        else if (sock->handshake == handshake_in_progress) return 0;
2891        else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock->handshake);
2892
2893        //LogMsg("Starting SSLRead %d %X", sock->fd, fcntl(sock->fd, F_GETFL, 0));
2894        mStatus err = SSLRead(sock->tlsContext, buf, buflen, (size_t *)&nread);
2895        //LogMsg("SSLRead returned %d (%d) nread %d buflen %d", err, errSSLWouldBlock, nread, buflen);
2896        if (err == errSSLClosedGraceful) { nread = 0; *closed = mDNStrue; }
2897        else if (err && err != errSSLWouldBlock)
2898        { LogMsg("ERROR: mDNSPlatformReadTCP - SSLRead: %d", err); nread = -1; *closed = mDNStrue; }
2899#else
2900        nread = -1;
2901        *closed = mDNStrue;
2902#endif /* NO_SECURITYFRAMEWORK */
2903    }
2904    else
2905    {
2906        static int CLOSEDcount = 0;
2907        static int EAGAINcount = 0;
2908        nread = recv(sock->fd, buf, buflen, 0);
2909
2910        if (nread > 0)
2911        {
2912            CLOSEDcount = 0;
2913            EAGAINcount = 0;
2914        } // On success, clear our error counters
2915        else if (nread == 0)
2916        {
2917            *closed = mDNStrue;
2918            if ((++CLOSEDcount % 1000) == 0)
2919            {
2920                LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got CLOSED %d times", sock->fd, CLOSEDcount);
2921                assert(CLOSEDcount < 1000);
2922                // Recovery Mechanism to bail mDNSResponder out of trouble: Instead of logging the same error msg multiple times,
2923                // crash mDNSResponder using assert() and restart fresh. See advantages below:
2924                // 1.Better User Experience
2925                // 2.CrashLogs frequency can be monitored
2926                // 3.StackTrace can be used for more info
2927            }
2928        }
2929        // else nread is negative -- see what kind of error we got
2930        else if (errno == ECONNRESET) { nread = 0; *closed = mDNStrue; }
2931        else if (errno != EAGAIN) { LogMsg("ERROR: mDNSPlatformReadTCP - recv: %d (%s)", errno, strerror(errno)); nread = -1; }
2932        else // errno is EAGAIN (EWOULDBLOCK) -- no data available
2933        {
2934            nread = 0;
2935            if ((++EAGAINcount % 1000) == 0) { LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got EAGAIN %d times", sock->fd, EAGAINcount); sleep(1); }
2936        }
2937    }
2938
2939    return nread;
2940}
2941
2942mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len)
2943{
2944    int nsent;
2945
2946    if (sock->flags & kTCPSocketFlags_UseTLS)
2947    {
2948#ifndef NO_SECURITYFRAMEWORK
2949        size_t processed;
2950        if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformWriteTCP called while handshake required"); return 0; }
2951        if (sock->handshake == handshake_in_progress) return 0;
2952        else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformWriteTCP called with unexpected SSLHandshake status: %d", sock->handshake);
2953
2954        mStatus err = SSLWrite(sock->tlsContext, msg, len, &processed);
2955
2956        if (!err) nsent = (int) processed;
2957        else if (err == errSSLWouldBlock) nsent = 0;
2958        else { LogMsg("ERROR: mDNSPlatformWriteTCP - SSLWrite returned %d", err); nsent = -1; }
2959#else
2960        nsent = -1;
2961#endif /* NO_SECURITYFRAMEWORK */
2962    }
2963    else
2964    {
2965        nsent = send(sock->fd, msg, len, 0);
2966        if (nsent < 0)
2967        {
2968            if (errno == EAGAIN) nsent = 0;
2969            else { LogMsg("ERROR: mDNSPlatformWriteTCP - send %s", strerror(errno)); nsent = -1; }
2970        }
2971    }
2972
2973    return nsent;
2974}
2975
2976mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock)
2977{
2978    return sock->fd;
2979}
2980
2981// If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface
2982// If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries
2983mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa_family, mDNSIPPort *const outport)
2984{
2985    int         *s        = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
2986    KQueueEntry *k        = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
2987    const int on = 1;
2988    const int twofivefive = 255;
2989    mStatus err = mStatus_NoError;
2990    char *errstr = mDNSNULL;
2991    const int mtu = 0;
2992
2993    cp->closeFlag = mDNSNULL;
2994
2995    int skt = socket(sa_family, SOCK_DGRAM, IPPROTO_UDP);
2996    if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno));return(skt); }
2997
2998    // set default traffic class
2999    setTrafficClass(skt, mDNSfalse);
3000
3001#ifdef SO_RECV_ANYIF
3002    // Enable inbound packets on IFEF_AWDL interface.
3003    // Only done for multicast sockets, since we don't expect unicast socket operations
3004    // on the IFEF_AWDL interface. Operation is a no-op for other interface types.
3005    if (mDNSSameIPPort(port, MulticastDNSPort))
3006    {
3007        err = setsockopt(skt, SOL_SOCKET, SO_RECV_ANYIF, &on, sizeof(on));
3008        if (err < 0) { errstr = "setsockopt - SO_RECV_ANYIF"; goto fail; }
3009    }
3010#endif // SO_RECV_ANYIF
3011
3012    // ... with a shared UDP port, if it's for multicast receiving
3013    if (mDNSSameIPPort(port, MulticastDNSPort) || mDNSSameIPPort(port, NATPMPAnnouncementPort)) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
3014    if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; }
3015
3016    if (sa_family == AF_INET)
3017    {
3018        // We want to receive destination addresses
3019        err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
3020        if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; }
3021
3022        // We want to receive interface identifiers
3023        err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
3024        if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; }
3025
3026        // We want to receive packet TTL value so we can check it
3027        err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on));
3028        // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it
3029
3030        // Send unicast packets with TTL 255
3031        err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive));
3032        if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; }
3033
3034        // And multicast packets with TTL 255 too
3035        err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive));
3036        if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; }
3037
3038        // And start listening for packets
3039        struct sockaddr_in listening_sockaddr;
3040        listening_sockaddr.sin_family      = AF_INET;
3041        listening_sockaddr.sin_port        = port.NotAnInteger;     // Pass in opaque ID without any byte swapping
3042        listening_sockaddr.sin_addr.s_addr = mDNSSameIPPort(port, NATPMPAnnouncementPort) ? AllHosts_v4.NotAnInteger : 0;
3043        err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr));
3044        if (err) { errstr = "bind"; goto fail; }
3045        if (outport) outport->NotAnInteger = listening_sockaddr.sin_port;
3046    }
3047    else if (sa_family == AF_INET6)
3048    {
3049        // NAT-PMP Announcements make no sense on IPv6, and we don't support IPv6 for PCP, so bail early w/o error
3050        if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort;return mStatus_NoError; }
3051
3052        // We want to receive destination addresses and receive interface identifiers
3053        err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
3054        if (err < 0) { errstr = "setsockopt - IPV6_RECVPKTINFO"; goto fail; }
3055
3056        // We want to receive packet hop count value so we can check it
3057        err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
3058        if (err < 0) { errstr = "setsockopt - IPV6_RECVHOPLIMIT"; goto fail; }
3059
3060        // We want to receive only IPv6 packets. Without this option we get IPv4 packets too,
3061        // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address
3062        err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
3063        if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; }
3064
3065        // Send unicast packets with TTL 255
3066        err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive));
3067        if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; }
3068
3069        // And multicast packets with TTL 255 too
3070        err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive));
3071        if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; }
3072
3073        // Want to receive our own packets
3074        err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on));
3075        if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; }
3076
3077        // Disable default option to send mDNSv6 packets at min IPv6 MTU: RFC 3542, Sec 11
3078        err = setsockopt(skt, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &mtu, sizeof(mtu));
3079        if (err < 0) // Since it is an optimization if we fail just log the err, no need to close the skt
3080            LogMsg("SetupSocket: setsockopt - IPV6_USE_MIN_MTU: IP6PO_MINMTU_DISABLE socket %d err %d errno %d (%s)",
3081                    skt, err, errno, strerror(errno));
3082
3083        // And start listening for packets
3084        struct sockaddr_in6 listening_sockaddr6;
3085        mDNSPlatformMemZero(&listening_sockaddr6, sizeof(listening_sockaddr6));
3086        listening_sockaddr6.sin6_len         = sizeof(listening_sockaddr6);
3087        listening_sockaddr6.sin6_family      = AF_INET6;
3088        listening_sockaddr6.sin6_port        = port.NotAnInteger;       // Pass in opaque ID without any byte swapping
3089        listening_sockaddr6.sin6_flowinfo    = 0;
3090        listening_sockaddr6.sin6_addr        = in6addr_any; // Want to receive multicasts AND unicasts on this socket
3091        listening_sockaddr6.sin6_scope_id    = 0;
3092        err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6));
3093        if (err) { errstr = "bind"; goto fail; }
3094        if (outport) outport->NotAnInteger = listening_sockaddr6.sin6_port;
3095    }
3096
3097    fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking
3098    fcntl(skt, F_SETFD, 1); // set close-on-exec
3099    *s = skt;
3100    k->KQcallback = myKQSocketCallBack;
3101    k->KQcontext  = cp;
3102    k->KQtask     = "UDP packet reception";
3103#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
3104    k->readSource = mDNSNULL;
3105    k->writeSource = mDNSNULL;
3106    k->fdClosed = mDNSfalse;
3107#endif
3108    KQueueSet(*s, EV_ADD, EVFILT_READ, k);
3109
3110    return(err);
3111
3112fail:
3113    // For "bind" failures, only write log messages for our shared mDNS port, or for binding to zero
3114    if (strcmp(errstr, "bind") || mDNSSameIPPort(port, MulticastDNSPort) || mDNSIPPortIsZero(port))
3115        LogMsg("%s skt %d port %d error %d errno %d (%s)", errstr, skt, mDNSVal16(port), err, errno, strerror(errno));
3116
3117    // If we got a "bind" failure of EADDRINUSE, inform the caller as it might need to try another random port
3118    if (!strcmp(errstr, "bind") && errno == EADDRINUSE)
3119    {
3120        err = EADDRINUSE;
3121        if (mDNSSameIPPort(port, MulticastDNSPort))
3122            NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed",
3123                               "Congratulations, you've reproduced an elusive bug.\r"
3124                               "Please contact the current assignee of <rdar://problem/3814904>.\r"
3125                               "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r"
3126                               "If possible, please leave your machine undisturbed so that someone can come to investigate the problem.");
3127    }
3128
3129    mDNSPlatformCloseFD(k, skt);
3130    return(err);
3131}
3132
3133mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport)
3134{
3135    mStatus err;
3136    mDNSIPPort port = requestedport;
3137    mDNSBool randomizePort = mDNSIPPortIsZero(requestedport);
3138    int i = 10000; // Try at most 10000 times to get a unique random port
3139    UDPSocket *p = mallocL("UDPSocket", sizeof(UDPSocket));
3140    if (!p) { LogMsg("mDNSPlatformUDPSocket: memory exhausted"); return(mDNSNULL); }
3141    mDNSPlatformMemZero(p, sizeof(UDPSocket));
3142    p->ss.port  = zeroIPPort;
3143    p->ss.m     = m;
3144    p->ss.sktv4 = -1;
3145    p->ss.sktv6 = -1;
3146    p->ss.proxy = mDNSfalse;
3147
3148    do
3149    {
3150        // The kernel doesn't do cryptographically strong random port allocation, so we do it ourselves here
3151        if (randomizePort) port = mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF));
3152        err = SetupSocket(&p->ss, port, AF_INET, &p->ss.port);
3153        if (!err)
3154        {
3155            err = SetupSocket(&p->ss, port, AF_INET6, &p->ss.port);
3156            if (err) { mDNSPlatformCloseFD(&p->ss.kqsv4, p->ss.sktv4); p->ss.sktv4 = -1; }
3157        }
3158        i--;
3159    } while (err == EADDRINUSE && randomizePort && i);
3160
3161    if (err)
3162    {
3163        // In customer builds we don't want to log failures with port 5351, because this is a known issue
3164        // of failing to bind to this port when Internet Sharing has already bound to it
3165        // We also don't want to log about port 5350, due to a known bug when some other
3166        // process is bound to it.
3167        if (mDNSSameIPPort(requestedport, NATPMPPort) || mDNSSameIPPort(requestedport, NATPMPAnnouncementPort))
3168            LogInfo("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno));
3169        else LogMsg("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno));
3170        freeL("UDPSocket", p);
3171        return(mDNSNULL);
3172    }
3173    return(p);
3174}
3175
3176mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock)
3177{
3178    CloseSocketSet(&sock->ss);
3179    freeL("UDPSocket", sock);
3180}
3181
3182#if COMPILER_LIKES_PRAGMA_MARK
3183#pragma mark -
3184#pragma mark - BPF Raw packet sending/receiving
3185#endif
3186
3187#if APPLE_OSX_mDNSResponder
3188
3189mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
3190{
3191    if (!InterfaceID) { LogMsg("mDNSPlatformSendRawPacket: No InterfaceID specified"); return; }
3192    NetworkInterfaceInfoOSX *info;
3193
3194    info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID);
3195    if (info == NULL)
3196    {
3197        LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID);
3198        return;
3199    }
3200    if (info->BPF_fd < 0)
3201        LogMsg("mDNSPlatformSendRawPacket: %s BPF_fd %d not ready", info->ifinfo.ifname, info->BPF_fd);
3202    else
3203    {
3204        //LogMsg("mDNSPlatformSendRawPacket %d bytes on %s", end - (mDNSu8 *)msg, info->ifinfo.ifname);
3205        if (write(info->BPF_fd, msg, end - (mDNSu8 *)msg) < 0)
3206            LogMsg("mDNSPlatformSendRawPacket: BPF write(%d) failed %d (%s)", info->BPF_fd, errno, strerror(errno));
3207    }
3208}
3209
3210mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID)
3211{
3212    if (!InterfaceID) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: No InterfaceID specified"); return; }
3213    NetworkInterfaceInfoOSX *info;
3214    info = IfindexToInterfaceInfoOSX(m, InterfaceID);
3215    if (info == NULL) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID); return; }
3216    // Manually inject an entry into our local ARP cache.
3217    // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.)
3218    if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa, mDNSNULL))
3219        LogSPS("Don't need address cache entry for %s %#a %.6a",            info->ifinfo.ifname, tpa, tha);
3220    else
3221    {
3222        int result = mDNSSetLocalAddressCacheEntry(info->scope_id, tpa->type, tpa->ip.v6.b, tha->b);
3223        if (result) LogMsg("Set local address cache entry for %s %#a %.6a failed: %d", info->ifinfo.ifname, tpa, tha, result);
3224        else LogSPS("Set local address cache entry for %s %#a %.6a",            info->ifinfo.ifname, tpa, tha);
3225    }
3226}
3227
3228mDNSlocal void CloseBPF(NetworkInterfaceInfoOSX *const i)
3229{
3230    LogSPS("%s closing BPF fd %d", i->ifinfo.ifname, i->BPF_fd);
3231#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
3232    // close will happen in the cancel handler
3233    dispatch_source_cancel(i->BPF_source);
3234#else
3235
3236    // Note: MUST NOT close() the underlying native BSD sockets.
3237    // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately, because
3238    // it first has to unhook the sockets from its select() call on its other thread, before it can safely close them.
3239    CFRunLoopRemoveSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode);
3240    CFRelease(i->BPF_rls);
3241    CFSocketInvalidate(i->BPF_cfs);
3242    CFRelease(i->BPF_cfs);
3243#endif
3244    i->BPF_fd = -1;
3245    if (i->BPF_mcfd >= 0) { close(i->BPF_mcfd); i->BPF_mcfd = -1; }
3246}
3247
3248mDNSlocal void bpf_callback_common(NetworkInterfaceInfoOSX *info)
3249{
3250    KQueueLock(info->m);
3251
3252    // 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
3253    // kernel has a mechanism for dispatching all events to a single thread, but for now we have to guard against this race condition).
3254    if (info->BPF_fd < 0) goto exit;
3255
3256    ssize_t n = read(info->BPF_fd, &info->m->imsg, info->BPF_len);
3257    const mDNSu8 *ptr = (const mDNSu8 *)&info->m->imsg;
3258    const mDNSu8 *end = (const mDNSu8 *)&info->m->imsg + n;
3259    debugf("%3d: bpf_callback got %d bytes on %s", info->BPF_fd, n, info->ifinfo.ifname);
3260
3261    if (n<0)
3262    {
3263        /* <rdar://problem/10287386>
3264         * sometimes there can be a race condition btw when the bpf socket
3265         * gets data and the callback get scheduled and when we call BIOCSETF (which
3266         * clears the socket).  this can cause the read to hang for a really long time
3267         * and effectively prevent us from responding to requests for long periods of time.
3268         * to prevent this make the socket non blocking and just bail if we dont get anything
3269         */
3270        if (errno == EAGAIN)
3271        {
3272            LogMsg("bpf_callback got EAGAIN bailing");
3273            goto exit;
3274        }
3275        LogMsg("Closing %s BPF fd %d due to error %d (%s)", info->ifinfo.ifname, info->BPF_fd, errno, strerror(errno));
3276        CloseBPF(info);
3277        goto exit;
3278    }
3279
3280    while (ptr < end)
3281    {
3282        const struct bpf_hdr *const bh = (const struct bpf_hdr *)ptr;
3283        debugf("%3d: bpf_callback ptr %p bh_hdrlen %d data %p bh_caplen %4d bh_datalen %4d next %p remaining %4d",
3284               info->BPF_fd, ptr, bh->bh_hdrlen, ptr + bh->bh_hdrlen, bh->bh_caplen, bh->bh_datalen,
3285               ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen), end - (ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen)));
3286        // Note that BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header.
3287        // Given that An Ethernet header is 14 bytes, this means that if the network layer header (e.g. IP header,
3288        // ARP message, etc.) is 4-byte aligned, then necessarily the Ethernet header will be NOT be 4-byte aligned.
3289        mDNSCoreReceiveRawPacket(info->m, ptr + bh->bh_hdrlen, ptr + bh->bh_hdrlen + bh->bh_caplen, info->ifinfo.InterfaceID);
3290        ptr += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen);
3291    }
3292exit:
3293    KQueueUnlock(info->m, "bpf_callback");
3294}
3295#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
3296mDNSlocal void bpf_callback_dispatch(NetworkInterfaceInfoOSX *const info)
3297{
3298    bpf_callback_common(info);
3299}
3300#else
3301mDNSlocal void bpf_callback(const CFSocketRef cfs, const CFSocketCallBackType CallBackType, const CFDataRef address, const void *const data, void *const context)
3302{
3303    (void)cfs;
3304    (void)CallBackType;
3305    (void)address;
3306    (void)data;
3307    bpf_callback_common((NetworkInterfaceInfoOSX *)context);
3308}
3309#endif
3310
3311mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win)
3312{
3313    LogMsg("mDNSPlatformSendKeepalive called\n");
3314    mDNSSendKeepalive(sadd->ip.v6.b, dadd->ip.v6.b, lport->NotAnInteger, rport->NotAnInteger, seq, ack, win);
3315}
3316
3317mDNSexport mStatus  mDNSPlatformClearSPSMACAddr(void)
3318{
3319    SCDynamicStoreRef store = NULL;
3320    CFStringRef  entityname = NULL;
3321
3322    if ((store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:ClearSPSMACAddress"), NULL, NULL)))
3323    {
3324        if ((entityname = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s%s"), "State:/Network/Interface/", "[^/]", "/BonjourSleepProxyAddress")))
3325        {
3326            if (SCDynamicStoreRemoveValue(store, entityname) == false)
3327                LogMsg("mDNSPlatformClearSPSMACAddr: Unable to remove key");
3328        }
3329    }
3330
3331    if (entityname) CFRelease(entityname);
3332    if (store)      CFRelease(store);
3333    return KERN_SUCCESS;
3334}
3335
3336mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname)
3337{
3338    int family = (spsaddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6;
3339    LogSPS("mDNSPlatformStoreSPSMACAddr : Storing %#a on interface %s", spsaddr, ifname);
3340    mDNSStoreSPSMACAddress(family, spsaddr->ip.v6.b, ifname);
3341    return KERN_SUCCESS;
3342}
3343
3344mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr)
3345{
3346    int family = (raddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6;
3347
3348    mDNSGetRemoteMAC(m, family, raddr->ip.v6.b);
3349    return KERN_SUCCESS;
3350}
3351
3352mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti)
3353{
3354    mDNSs32 intfid;
3355    mDNSs32 error  = 0;
3356    int     family = (laddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6;
3357
3358    error = mDNSRetrieveTCPInfo(family, laddr->ip.v6.b, lport->NotAnInteger, raddr->ip.v6.b, rport->NotAnInteger, (uint32_t *)&(mti->seq), (uint32_t *)&(mti->ack), (uint16_t *)&(mti->window), (int32_t*)&intfid);
3359    if (error != KERN_SUCCESS)
3360    {
3361        LogMsg("%s: mDNSRetrieveTCPInfo returned : %d", __func__, error);
3362        return error;
3363    }
3364    mti->IntfId = mDNSPlatformInterfaceIDfromInterfaceIndex(m, intfid);
3365    return error;
3366}
3367
3368#define BPF_SetOffset(from, cond, to) (from)->cond = (to) - 1 - (from)
3369
3370mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int *p4, int *p6)
3371{
3372    int numv4 = 0, numv6 = 0;
3373    AuthRecord *rr;
3374
3375    for (rr = m->ResourceRecords; rr; rr=rr->next)
3376        if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4)
3377        {
3378            if (p4) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.4a", x->BPF_fd, x->ifinfo.ifname, numv4, &rr->AddressProxy.ip.v4);
3379            numv4++;
3380        }
3381
3382    for (rr = m->ResourceRecords; rr; rr=rr->next)
3383        if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6)
3384        {
3385            if (p6) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.16a", x->BPF_fd, x->ifinfo.ifname, numv6, &rr->AddressProxy.ip.v6);
3386            numv6++;
3387        }
3388
3389    if (p4) *p4 = numv4;
3390    if (p6) *p6 = numv6;
3391    return(numv4 + numv6);
3392}
3393
3394mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID)
3395{
3396    NetworkInterfaceInfoOSX *x;
3397
3398    // Note: We can't use IfIndexToInterfaceInfoOSX because that looks for Registered also.
3399    for (x = m->p->InterfaceList; x; x = x->next) if (x->ifinfo.InterfaceID == InterfaceID) break;
3400
3401    if (!x) { LogMsg("mDNSPlatformUpdateProxyList: ERROR InterfaceID %p not found", InterfaceID); return; }
3402
3403    #define MAX_BPF_ADDRS 250
3404    int numv4 = 0, numv6 = 0;
3405
3406    if (CountProxyTargets(m, x, &numv4, &numv6) > MAX_BPF_ADDRS)
3407    {
3408        LogMsg("mDNSPlatformUpdateProxyList: ERROR Too many address proxy records v4 %d v6 %d", numv4, numv6);
3409        if (numv4 > MAX_BPF_ADDRS) numv4 = MAX_BPF_ADDRS;
3410        numv6 = MAX_BPF_ADDRS - numv4;
3411    }
3412
3413    LogSPS("mDNSPlatformUpdateProxyList: fd %d %-7s MAC  %.6a %d v4 %d v6", x->BPF_fd, x->ifinfo.ifname, &x->ifinfo.MAC, numv4, numv6);
3414
3415    // Caution: This is a static structure, so we need to be careful that any modifications we make to it
3416    // are done in such a way that they work correctly when mDNSPlatformUpdateProxyList is called multiple times
3417    static struct bpf_insn filter[17 + MAX_BPF_ADDRS] =
3418    {
3419        BPF_STMT(BPF_LD  + BPF_H   + BPF_ABS, 12),              // 0 Read Ethertype (bytes 12,13)
3420
3421        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0806, 0, 1),      // 1 If Ethertype == ARP goto next, else 3
3422        BPF_STMT(BPF_RET + BPF_K,             42),              // 2 Return 42-byte ARP
3423
3424        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0800, 4, 0),      // 3 If Ethertype == IPv4 goto 8 (IPv4 address list check) else next
3425
3426        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x86DD, 0, 9),      // 4 If Ethertype == IPv6 goto next, else exit
3427        BPF_STMT(BPF_LD  + BPF_H   + BPF_ABS, 20),              // 5 Read Protocol and Hop Limit (bytes 20,21)
3428        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x3AFF, 0, 9),      // 6 If (Prot,TTL) == (3A,FF) goto next, else IPv6 address list check
3429        BPF_STMT(BPF_RET + BPF_K,             86),              // 7 Return 86-byte ND
3430
3431        // Is IPv4 packet; check if it's addressed to any IPv4 address we're proxying for
3432        BPF_STMT(BPF_LD  + BPF_W   + BPF_ABS, 30),              // 8 Read IPv4 Dst (bytes 30,31,32,33)
3433    };
3434
3435    struct bpf_insn *pc   = &filter[9];
3436    struct bpf_insn *chk6 = pc   + numv4 + 1;   // numv4 address checks, plus a "return 0"
3437    struct bpf_insn *fail = chk6 + 1 + numv6;   // Get v6 Dst LSW, plus numv6 address checks
3438    struct bpf_insn *ret4 = fail + 1;
3439    struct bpf_insn *ret6 = ret4 + 4;
3440
3441    static const struct bpf_insn rf  = BPF_STMT(BPF_RET + BPF_K, 0);                // No match: Return nothing
3442
3443    static const struct bpf_insn g6  = BPF_STMT(BPF_LD  + BPF_W   + BPF_ABS, 50);   // Read IPv6 Dst LSW (bytes 50,51,52,53)
3444
3445    static const struct bpf_insn r4a = BPF_STMT(BPF_LDX + BPF_B   + BPF_MSH, 14);   // Get IP Header length (normally 20)
3446    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)
3447    static const struct bpf_insn r4c = BPF_STMT(BPF_ALU + BPF_ADD + BPF_X,    0);   // A += IP Header length
3448    static const struct bpf_insn r4d = BPF_STMT(BPF_RET + BPF_A, 0);                // Success: Return Ethernet + IP + TCP + 20 bytes spare (normally 74)
3449
3450    static const struct bpf_insn r6a = BPF_STMT(BPF_RET + BPF_K, 94);               // Success: Return Eth + IPv6 + TCP + 20 bytes spare
3451
3452    BPF_SetOffset(&filter[4], jf, fail);    // If Ethertype not ARP, IPv4, or IPv6, fail
3453    BPF_SetOffset(&filter[6], jf, chk6);    // If IPv6 but not ICMPv6, go to IPv6 address list check
3454
3455    // BPF Byte-Order Note
3456    // The BPF API designers apparently thought that programmers would not be smart enough to use htons
3457    // and htonl correctly to convert numeric values to network byte order on little-endian machines,
3458    // so instead they chose to make the API implicitly byte-swap *ALL* values, even literal byte strings
3459    // that shouldn't be byte-swapped, like ASCII text, Ethernet addresses, IP addresses, etc.
3460    // As a result, if we put Ethernet addresses and IP addresses in the right byte order, the BPF API
3461    // will byte-swap and make them backwards, and then our filter won't work. So, we have to arrange
3462    // that on little-endian machines we deliberately put addresses in memory with the bytes backwards,
3463    // so that when the BPF API goes through and swaps them all, they end up back as they should be.
3464    // In summary, if we byte-swap all the non-numeric fields that shouldn't be swapped, and we *don't*
3465    // swap any of the numeric values that *should* be byte-swapped, then the filter will work correctly.
3466
3467    // IPSEC capture size notes:
3468    //  8 bytes UDP header
3469    //  4 bytes Non-ESP Marker
3470    // 28 bytes IKE Header
3471    // --
3472    // 40 Total. Capturing TCP Header + 20 gets us enough bytes to receive the IKE Header in a UDP-encapsulated IKE packet.
3473
3474    AuthRecord *rr;
3475    for (rr = m->ResourceRecords; rr; rr=rr->next)
3476        if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4)
3477        {
3478            mDNSv4Addr a = rr->AddressProxy.ip.v4;
3479            pc->code = BPF_JMP + BPF_JEQ + BPF_K;
3480            BPF_SetOffset(pc, jt, ret4);
3481            pc->jf   = 0;
3482            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];
3483            pc++;
3484        }
3485    *pc++ = rf;
3486
3487    if (pc != chk6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != chk6 %p", pc, chk6);
3488    *pc++ = g6; // chk6 points here
3489
3490    // First cancel any previous ND group memberships we had, then create a fresh socket
3491    if (x->BPF_mcfd >= 0) close(x->BPF_mcfd);
3492    x->BPF_mcfd = socket(AF_INET6, SOCK_DGRAM, 0);
3493
3494    for (rr = m->ResourceRecords; rr; rr=rr->next)
3495        if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6)
3496        {
3497            const mDNSv6Addr *const a = &rr->AddressProxy.ip.v6;
3498            pc->code = BPF_JMP + BPF_JEQ + BPF_K;
3499            BPF_SetOffset(pc, jt, ret6);
3500            pc->jf   = 0;
3501            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];
3502            pc++;
3503
3504            struct ipv6_mreq i6mr;
3505            i6mr.ipv6mr_interface = x->scope_id;
3506            i6mr.ipv6mr_multiaddr = *(const struct in6_addr*)&NDP_prefix;
3507            i6mr.ipv6mr_multiaddr.s6_addr[0xD] = a->b[0xD];
3508            i6mr.ipv6mr_multiaddr.s6_addr[0xE] = a->b[0xE];
3509            i6mr.ipv6mr_multiaddr.s6_addr[0xF] = a->b[0xF];
3510
3511            // Do precautionary IPV6_LEAVE_GROUP first, necessary to clear stale kernel state
3512            mStatus err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr));
3513            if (err < 0 && (errno != EADDRNOTAVAIL))
3514                LogMsg("mDNSPlatformUpdateProxyList: IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
3515
3516            err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr));
3517            if (err < 0 && (errno != EADDRINUSE))   // Joining same group twice can give "Address already in use" error -- no need to report that
3518                LogMsg("mDNSPlatformUpdateProxyList: IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
3519
3520            LogSPS("Joined IPv6 ND multicast group %.16a for %.16a", &i6mr.ipv6mr_multiaddr, a);
3521        }
3522
3523    if (pc != fail) LogMsg("mDNSPlatformUpdateProxyList: pc %p != fail %p", pc, fail);
3524    *pc++ = rf;     // fail points here
3525
3526    if (pc != ret4) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret4 %p", pc, ret4);
3527    *pc++ = r4a;    // ret4 points here
3528    *pc++ = r4b;
3529    *pc++ = r4c;
3530    *pc++ = r4d;
3531
3532    if (pc != ret6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret6 %p", pc, ret6);
3533    *pc++ = r6a;    // ret6 points here
3534
3535    struct bpf_program prog = { pc - filter, filter };
3536
3537#if 0
3538    // For debugging BPF filter program
3539    unsigned int q;
3540    for (q=0; q<prog.bf_len; q++)
3541        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);
3542#endif
3543
3544    if (!numv4 && !numv6)
3545    {
3546        LogSPS("mDNSPlatformUpdateProxyList: No need for filter");
3547        if (m->timenow == 0) LogMsg("mDNSPlatformUpdateProxyList: m->timenow == 0");
3548        // Schedule check to see if we can close this BPF_fd now
3549        if (!m->p->NetworkChanged) m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2);
3550        // prog.bf_len = 0; This seems to panic the kernel
3551        if (x->BPF_fd < 0) return;      // If we've already closed our BPF_fd, no need to generate an error message below
3552    }
3553
3554    if (ioctl(x->BPF_fd, BIOCSETFNR, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno));
3555    else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) successful", prog.bf_len);
3556}
3557
3558mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd)
3559{
3560    mDNS_Lock(m);
3561
3562    NetworkInterfaceInfoOSX *i;
3563    for (i = m->p->InterfaceList; i; i = i->next) if (i->BPF_fd == -2) break;
3564    if (!i) { LogSPS("mDNSPlatformReceiveBPF_fd: No Interfaces awaiting BPF fd %d; closing", fd); close(fd); }
3565    else
3566    {
3567        LogSPS("%s using   BPF fd %d", i->ifinfo.ifname, fd);
3568
3569        struct bpf_version v;
3570        if (ioctl(fd, BIOCVERSION, &v) < 0)
3571            LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
3572        else if (BPF_MAJOR_VERSION != v.bv_major || BPF_MINOR_VERSION != v.bv_minor)
3573            LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION header %d.%d kernel %d.%d",
3574                   fd, i->ifinfo.ifname, BPF_MAJOR_VERSION, BPF_MINOR_VERSION, v.bv_major, v.bv_minor);
3575
3576        if (ioctl(fd, BIOCGBLEN, &i->BPF_len) < 0)
3577            LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCGBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
3578
3579        if (i->BPF_len > sizeof(m->imsg))
3580        {
3581            i->BPF_len = sizeof(m->imsg);
3582            if (ioctl(fd, BIOCSBLEN, &i->BPF_len) < 0)
3583                LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
3584            else
3585                LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", fd, i->ifinfo.ifname, i->BPF_len);
3586        }
3587
3588        static const u_int opt_one = 1;
3589        if (ioctl(fd, BIOCIMMEDIATE, &opt_one) < 0)
3590            LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCIMMEDIATE failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
3591
3592        //if (ioctl(fd, BIOCPROMISC, &opt_one) < 0)
3593        //	LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCPROMISC failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
3594
3595        //if (ioctl(fd, BIOCSHDRCMPLT, &opt_one) < 0)
3596        //	LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSHDRCMPLT failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
3597
3598        /*  <rdar://problem/10287386>
3599         *  make socket non blocking see comments in bpf_callback_common for more info
3600         */
3601        if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) // set non-blocking
3602        {
3603            LogMsg("mDNSPlatformReceiveBPF_fd: %d %s O_NONBLOCK failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
3604        }
3605
3606        struct ifreq ifr;
3607        mDNSPlatformMemZero(&ifr, sizeof(ifr));
3608        strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name));
3609        if (ioctl(fd, BIOCSETIF, &ifr) < 0)
3610        { LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSETIF failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); i->BPF_fd = -3; }
3611        else
3612        {
3613#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
3614            i->BPF_fd  = fd;
3615            i->BPF_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue());
3616            if (!i->BPF_source) {LogMsg("mDNSPlatformReceiveBPF_fd: dispatch source create failed"); return;}
3617            dispatch_source_set_event_handler(i->BPF_source, ^{bpf_callback_dispatch(i);});
3618            dispatch_source_set_cancel_handler(i->BPF_source, ^{close(fd);});
3619            dispatch_resume(i->BPF_source);
3620#else
3621            CFSocketContext myCFSocketContext = { 0, i, NULL, NULL, NULL };
3622            i->BPF_fd  = fd;
3623            i->BPF_cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, bpf_callback, &myCFSocketContext);
3624            i->BPF_rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, i->BPF_cfs, 0);
3625            CFRunLoopAddSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode);
3626#endif
3627            mDNSPlatformUpdateProxyList(m, i->ifinfo.InterfaceID);
3628        }
3629    }
3630
3631    mDNS_Unlock(m);
3632}
3633
3634#endif // APPLE_OSX_mDNSResponder
3635
3636#if COMPILER_LIKES_PRAGMA_MARK
3637#pragma mark -
3638#pragma mark - Key Management
3639#endif
3640
3641#ifndef NO_SECURITYFRAMEWORK
3642mDNSlocal CFArrayRef GetCertChain(SecIdentityRef identity)
3643{
3644    CFMutableArrayRef certChain = NULL;
3645    if (!identity) { LogMsg("getCertChain: identity is NULL"); return(NULL); }
3646    SecCertificateRef cert;
3647    OSStatus err = SecIdentityCopyCertificate(identity, &cert);
3648    if (err || !cert) LogMsg("getCertChain: SecIdentityCopyCertificate() returned %d", (int) err);
3649    else
3650    {
3651        SecPolicySearchRef searchRef;
3652        err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef);
3653        if (err || !searchRef) LogMsg("getCertChain: SecPolicySearchCreate() returned %d", (int) err);
3654        else
3655        {
3656            SecPolicyRef policy;
3657            err = SecPolicySearchCopyNext(searchRef, &policy);
3658            if (err || !policy) LogMsg("getCertChain: SecPolicySearchCopyNext() returned %d", (int) err);
3659            else
3660            {
3661                CFArrayRef wrappedCert = CFArrayCreate(NULL, (const void**) &cert, 1, &kCFTypeArrayCallBacks);
3662                if (!wrappedCert) LogMsg("getCertChain: wrappedCert is NULL");
3663                else
3664                {
3665                    SecTrustRef trust;
3666                    err = SecTrustCreateWithCertificates(wrappedCert, policy, &trust);
3667                    if (err || !trust) LogMsg("getCertChain: SecTrustCreateWithCertificates() returned %d", (int) err);
3668                    else
3669                    {
3670                        err = SecTrustEvaluate(trust, NULL);
3671                        if (err) LogMsg("getCertChain: SecTrustEvaluate() returned %d", (int) err);
3672                        else
3673                        {
3674                            CFArrayRef rawCertChain;
3675                            CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL;
3676                            err = SecTrustGetResult(trust, NULL, &rawCertChain, &statusChain);
3677                            if (err || !rawCertChain || !statusChain) LogMsg("getCertChain: SecTrustGetResult() returned %d", (int) err);
3678                            else
3679                            {
3680                                certChain = CFArrayCreateMutableCopy(NULL, 0, rawCertChain);
3681                                if (!certChain) LogMsg("getCertChain: certChain is NULL");
3682                                else
3683                                {
3684                                    // Replace the SecCertificateRef at certChain[0] with a SecIdentityRef per documentation for SSLSetCertificate:
3685                                    // <http://devworld.apple.com/documentation/Security/Reference/secureTransportRef/index.html>
3686                                    CFArraySetValueAtIndex(certChain, 0, identity);
3687                                    // Remove root from cert chain, but keep any and all intermediate certificates that have been signed by the root certificate
3688                                    if (CFArrayGetCount(certChain) > 1) CFArrayRemoveValueAtIndex(certChain, CFArrayGetCount(certChain) - 1);
3689                                }
3690                                CFRelease(rawCertChain);
3691                                // Do not free statusChain:
3692                                // <http://developer.apple.com/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html> says:
3693                                // certChain: Call the CFRelease function to release this object when you are finished with it.
3694                                // statusChain: Do not attempt to free this pointer; it remains valid until the trust management object is released...
3695                            }
3696                        }
3697                        CFRelease(trust);
3698                    }
3699                    CFRelease(wrappedCert);
3700                }
3701                CFRelease(policy);
3702            }
3703            CFRelease(searchRef);
3704        }
3705        CFRelease(cert);
3706    }
3707    return certChain;
3708}
3709#endif /* NO_SECURITYFRAMEWORK */
3710
3711mDNSexport mStatus mDNSPlatformTLSSetupCerts(void)
3712{
3713#ifdef NO_SECURITYFRAMEWORK
3714    return mStatus_UnsupportedErr;
3715#else
3716    SecIdentityRef identity = nil;
3717    SecIdentitySearchRef srchRef = nil;
3718    OSStatus err;
3719
3720    // search for "any" identity matching specified key use
3721    // In this app, we expect there to be exactly one
3722    err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_DECRYPT, &srchRef);
3723    if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCreate returned %d", (int) err); return err; }
3724
3725    err = SecIdentitySearchCopyNext(srchRef, &identity);
3726    if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext returned %d", (int) err); return err; }
3727
3728    if (CFGetTypeID(identity) != SecIdentityGetTypeID())
3729    { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext CFTypeID failure"); return mStatus_UnknownErr; }
3730
3731    // Found one. Call getCertChain to create the correct certificate chain.
3732    ServerCerts = GetCertChain(identity);
3733    if (ServerCerts == nil) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: getCertChain error"); return mStatus_UnknownErr; }
3734
3735    return mStatus_NoError;
3736#endif /* NO_SECURITYFRAMEWORK */
3737}
3738
3739mDNSexport void  mDNSPlatformTLSTearDownCerts(void)
3740{
3741#ifndef NO_SECURITYFRAMEWORK
3742    if (ServerCerts) { CFRelease(ServerCerts); ServerCerts = NULL; }
3743#endif /* NO_SECURITYFRAMEWORK */
3744}
3745
3746// This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel
3747mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel)
3748{
3749    CFStringEncoding encoding = kCFStringEncodingUTF8;
3750    CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding);
3751    if (cfs)
3752    {
3753        CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8);
3754        CFRelease(cfs);
3755    }
3756}
3757
3758// This gets the text of the field currently labelled "Local Hostname" in the Sharing Prefs Control Panel
3759mDNSlocal void GetUserSpecifiedLocalHostName(domainlabel *const namelabel)
3760{
3761    CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL);
3762    if (cfs)
3763    {
3764        CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8);
3765        CFRelease(cfs);
3766    }
3767}
3768
3769mDNSexport mDNSBool DictionaryIsEnabled(CFDictionaryRef dict)
3770{
3771    mDNSs32 val;
3772    CFNumberRef state = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("Enabled"));
3773    if (!state) return mDNSfalse;
3774    if (!CFNumberGetValue(state, kCFNumberSInt32Type, &val))
3775    { LogMsg("ERROR: DictionaryIsEnabled - CFNumberGetValue"); return mDNSfalse; }
3776    return val ? mDNStrue : mDNSfalse;
3777}
3778
3779mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa)
3780{
3781    if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); }
3782
3783    if (sa->sa_family == AF_INET)
3784    {
3785        struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa;
3786        ip->type = mDNSAddrType_IPv4;
3787        ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr;
3788        return(mStatus_NoError);
3789    }
3790
3791    if (sa->sa_family == AF_INET6)
3792    {
3793        struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa;
3794        // Inside the BSD kernel they use a hack where they stuff the sin6->sin6_scope_id
3795        // value into the second word of the IPv6 link-local address, so they can just
3796        // pass around IPv6 address structures instead of full sockaddr_in6 structures.
3797        // Those hacked IPv6 addresses aren't supposed to escape the kernel in that form, but they do.
3798        // To work around this we always whack the second word of any IPv6 link-local address back to zero.
3799        if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
3800        ip->type = mDNSAddrType_IPv6;
3801        ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr;
3802        return(mStatus_NoError);
3803    }
3804
3805    LogMsg("SetupAddr invalid sa_family %d", sa->sa_family);
3806    return(mStatus_Invalid);
3807}
3808
3809mDNSlocal mDNSEthAddr GetBSSID(char *ifa_name)
3810{
3811    mDNSEthAddr eth = zeroEthAddr;
3812    SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetBSSID"), NULL, NULL);
3813    if (!store)
3814        LogMsg("GetBSSID: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
3815    else
3816    {
3817        CFStringRef entityname = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Interface/%s/AirPort"), ifa_name);
3818        if (entityname)
3819        {
3820            CFDictionaryRef dict = SCDynamicStoreCopyValue(store, entityname);
3821            if (dict)
3822            {
3823                CFRange range = { 0, 6 };       // Offset, length
3824                CFDataRef data = CFDictionaryGetValue(dict, CFSTR("BSSID"));
3825                if (data && CFDataGetLength(data) == 6) CFDataGetBytes(data, range, eth.b);
3826                CFRelease(dict);
3827            }
3828            CFRelease(entityname);
3829        }
3830        CFRelease(store);
3831    }
3832    return(eth);
3833}
3834
3835mDNSlocal int GetMAC(mDNSEthAddr *eth, u_short ifindex)
3836{
3837    struct ifaddrs *ifa;
3838    for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next)
3839        if (ifa->ifa_addr->sa_family == AF_LINK)
3840        {
3841            const struct sockaddr_dl *const sdl = (const struct sockaddr_dl *)ifa->ifa_addr;
3842            if (sdl->sdl_index == ifindex)
3843            { mDNSPlatformMemCopy(eth->b, sdl->sdl_data + sdl->sdl_nlen, 6); return 0; }
3844        }
3845    *eth = zeroEthAddr;
3846    return -1;
3847}
3848
3849#ifndef SIOCGIFWAKEFLAGS
3850#define SIOCGIFWAKEFLAGS _IOWR('i', 136, struct ifreq) /* get interface wake property flags */
3851#endif
3852
3853#ifndef IF_WAKE_ON_MAGIC_PACKET
3854#define IF_WAKE_ON_MAGIC_PACKET 0x01
3855#endif
3856
3857#ifndef ifr_wake_flags
3858#define ifr_wake_flags ifr_ifru.ifru_intval
3859#endif
3860
3861mDNSlocal mDNSBool  CheckInterfaceSupport(NetworkInterfaceInfo *const intf, const char *key)
3862{
3863    io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, intf->ifname));
3864    if (!service)
3865    {
3866        LogSPS("CheckInterfaceSupport: No service for interface %s", intf->ifname);
3867        return mDNSfalse;
3868    }
3869
3870    io_name_t n1, n2;
3871    IOObjectGetClass(service, n1);
3872    io_object_t parent;
3873    mDNSBool    ret = mDNSfalse;
3874    kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
3875    if (kr == KERN_SUCCESS)
3876    {
3877        CFStringRef keystr =  CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
3878        IOObjectGetClass(parent, n2);
3879        LogSPS("CheckInterfaceSupport: Interface %s service %s parent %s", intf->ifname, n1, n2);
3880        const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, keystr, kCFAllocatorDefault, mDNSNULL);
3881        if (!ref)
3882        {
3883            LogSPS("CheckInterfaceSupport: No mDNS_IOREG_KEY for interface %s/%s/%s", intf->ifname, n1, n2);
3884            ret = mDNSfalse;
3885        }
3886        else
3887        {
3888            ret = mDNStrue;
3889            CFRelease(ref);
3890        }
3891        IOObjectRelease(parent);
3892        CFRelease(keystr);
3893    }
3894    else
3895    {
3896        LogSPS("CheckInterfaceSupport: IORegistryEntryGetParentEntry for %s/%s failed %d", intf->ifname, n1, kr);
3897        ret = mDNSfalse;
3898    }
3899    IOObjectRelease(service);
3900    return ret;
3901}
3902
3903
3904mDNSlocal  mDNSBool InterfaceSupportsKeepAlive(NetworkInterfaceInfo *const intf)
3905{
3906    return CheckInterfaceSupport(intf, mDNS_IOREG_KA_KEY);
3907}
3908
3909mDNSlocal mDNSBool NetWakeInterface(NetworkInterfaceInfoOSX *i)
3910{
3911    if (!MulticastInterface(i)     ) return(mDNSfalse); // We only use Sleep Proxy Service on multicast-capable interfaces
3912    if (i->ifa_flags & IFF_LOOPBACK) return(mDNSfalse); // except loopback
3913
3914    // If the interface supports TCPKeepalive, it is capable of waking up for a magic packet
3915    // This check is needed since the SIOCGIFWAKEFLAGS ioctl returns wrong values for WOMP capability
3916    // when the power source is not AC Power.
3917    if (InterfaceSupportsKeepAlive(&i->ifinfo))
3918    {
3919        LogSPS("NetWakeInterface: %s supports TCP Keepalive returning true", i->ifinfo.ifname);
3920        return mDNStrue;
3921    }
3922
3923    int s = socket(AF_INET, SOCK_DGRAM, 0);
3924    if (s < 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i->ifinfo.ifname, s, errno, strerror(errno)); return(mDNSfalse); }
3925
3926    struct ifreq ifr;
3927    strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name));
3928    if (ioctl(s, SIOCGIFWAKEFLAGS, &ifr) < 0)
3929    {
3930        // For some strange reason, in /usr/include/sys/errno.h, EOPNOTSUPP is defined to be
3931        // 102 when compiling kernel code, and 45 when compiling user-level code. Since this
3932        // error code is being returned from the kernel, we need to use the kernel version.
3933        #define KERNEL_EOPNOTSUPP 102
3934        if (errno != KERNEL_EOPNOTSUPP) // "Operation not supported on socket", the expected result on Leopard and earlier
3935            LogMsg("NetWakeInterface SIOCGIFWAKEFLAGS %s errno %d (%s)", i->ifinfo.ifname, errno, strerror(errno));
3936        // If on Leopard or earlier, we get EOPNOTSUPP, so in that case
3937        // we enable WOL if this interface is not AirPort and "Wake for Network access" is turned on.
3938        ifr.ifr_wake_flags = (errno == KERNEL_EOPNOTSUPP && !(i)->BSSID.l[0] && i->m->SystemWakeOnLANEnabled) ? IF_WAKE_ON_MAGIC_PACKET : 0;
3939    }
3940
3941    close(s);
3942
3943    // 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
3944
3945    LogSPS("%-6s %#-14a %s WOMP", i->ifinfo.ifname, &i->ifinfo.ip, (ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) ? "supports" : "no");
3946
3947    return((ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) != 0);
3948}
3949
3950mDNSlocal u_int64_t getExtendedFlags(char * ifa_name)
3951{
3952    int sockFD;
3953    struct ifreq ifr;
3954
3955    sockFD = socket(AF_INET, SOCK_DGRAM, 0);
3956    if (sockFD < 0)
3957    {
3958        LogMsg("getExtendedFlags: socket() call failed, errno = %d (%s)", errno, strerror(errno));
3959        return 0;
3960    }
3961
3962    ifr.ifr_addr.sa_family = AF_INET;
3963    strlcpy(ifr.ifr_name, ifa_name, sizeof(ifr.ifr_name));
3964
3965    if (ioctl(sockFD, SIOCGIFEFLAGS, (caddr_t)&ifr) == -1)
3966    {
3967        LogMsg("getExtendedFlags: SIOCGIFEFLAGS failed, errno = %d (%s)", errno, strerror(errno));
3968        ifr.ifr_eflags = 0;
3969    }
3970    LogInfo("getExtendedFlags: %s ifr_eflags = 0x%x", ifa_name, ifr.ifr_eflags);
3971
3972    close(sockFD);
3973    return ifr.ifr_eflags;
3974}
3975
3976// Returns pointer to newly created NetworkInterfaceInfoOSX object, or
3977// pointer to already-existing NetworkInterfaceInfoOSX object found in list, or
3978// may return NULL if out of memory (unlikely) or parameters are invalid for some reason
3979// (e.g. sa_family not AF_INET or AF_INET6)
3980mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa, mDNSs32 utc)
3981{
3982    mDNSu32 scope_id  = if_nametoindex(ifa->ifa_name);
3983    mDNSEthAddr bssid = GetBSSID(ifa->ifa_name);
3984    u_int64_t   eflags = getExtendedFlags(ifa->ifa_name);
3985
3986    mDNSAddr ip, mask;
3987    if (SetupAddr(&ip,   ifa->ifa_addr   ) != mStatus_NoError) return(NULL);
3988    if (SetupAddr(&mask, ifa->ifa_netmask) != mStatus_NoError) return(NULL);
3989
3990    NetworkInterfaceInfoOSX **p;
3991    for (p = &m->p->InterfaceList; *p; p = &(*p)->next)
3992        if (scope_id == (*p)->scope_id &&
3993            mDNSSameAddress(&ip, &(*p)->ifinfo.ip) &&
3994            mDNSSameEthAddress(&bssid, &(*p)->BSSID))
3995        {
3996            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);
3997            // The name should be updated to the new name so that we don't report a wrong name in our SIGINFO output.
3998            // When interfaces are created with same MAC address, kernel resurrects the old interface.
3999            // Even though the interface index is the same (which should be sufficient), when we receive a UDP packet
4000            // we get the corresponding name for the interface index on which the packet was received and check against
4001            // the InterfaceList for a matching name. So, keep the name in sync
4002            strlcpy((*p)->ifinfo.ifname, ifa->ifa_name, sizeof((*p)->ifinfo.ifname));
4003            (*p)->Exists = mDNStrue;
4004            // If interface was not in getifaddrs list last time we looked, but it is now, update 'AppearanceTime' for this record
4005            if ((*p)->LastSeen != utc) (*p)->AppearanceTime = utc;
4006
4007            // If Wake-on-LAN capability of this interface has changed (e.g. because power cable on laptop has been disconnected)
4008            // we may need to start or stop or sleep proxy browse operation
4009            const mDNSBool NetWake = NetWakeInterface(*p);
4010            if ((*p)->ifinfo.NetWake != NetWake)
4011            {
4012                (*p)->ifinfo.NetWake = NetWake;
4013                // If this interface is already registered with mDNSCore, then we need to start or stop its NetWake browse on-the-fly.
4014                // If this interface is not already registered (i.e. it's a dormant interface we had in our list
4015                // from when we previously saw it) then we mustn't do that, because mDNSCore doesn't know about it yet.
4016                // In this case, the mDNS_RegisterInterface() call will take care of starting the NetWake browse if necessary.
4017                if ((*p)->Registered)
4018                {
4019                    mDNS_Lock(m);
4020                    if (NetWake) mDNS_ActivateNetWake_internal  (m, &(*p)->ifinfo);
4021                    else mDNS_DeactivateNetWake_internal(m, &(*p)->ifinfo);
4022                    mDNS_Unlock(m);
4023                }
4024            }
4025            // Reset the flag if it has changed this time.
4026            (*p)->ifinfo.IgnoreIPv4LL = ((eflags & IFEF_ARPLL) != 0) ? mDNSfalse : mDNStrue;
4027
4028            return(*p);
4029        }
4030
4031    NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i));
4032    debugf("AddInterfaceToList: Making   new   interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, i);
4033    if (!i) return(mDNSNULL);
4034    mDNSPlatformMemZero(i, sizeof(NetworkInterfaceInfoOSX));
4035    i->ifinfo.InterfaceID = (mDNSInterfaceID)(uintptr_t)scope_id;
4036    i->ifinfo.ip          = ip;
4037    i->ifinfo.mask        = mask;
4038    strlcpy(i->ifinfo.ifname, ifa->ifa_name, sizeof(i->ifinfo.ifname));
4039    i->ifinfo.ifname[sizeof(i->ifinfo.ifname)-1] = 0;
4040    // We can be configured to disable multicast advertisement, but we want to to support
4041    // local-only services, which need a loopback address record.
4042    i->ifinfo.Advertise   = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses;
4043    i->ifinfo.McastTxRx   = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList
4044    i->ifinfo.Loopback    = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse;
4045    i->ifinfo.IgnoreIPv4LL = ((eflags & IFEF_ARPLL) != 0) ? mDNSfalse : mDNStrue;
4046    i->ifinfo.DirectLink  = (eflags & IFEF_DIRECTLINK) ? mDNStrue: mDNSfalse;
4047
4048    i->next            = mDNSNULL;
4049    i->m               = m;
4050    i->Exists          = mDNStrue;
4051    i->Flashing        = mDNSfalse;
4052    i->Occulting       = mDNSfalse;
4053    i->D2DInterface    = (eflags & IFEF_LOCALNET_PRIVATE) ? mDNStrue: mDNSfalse;
4054    if (eflags & IFEF_AWDL)
4055    {
4056        AWDLInterfaceID = i->ifinfo.InterfaceID;
4057        LogInfo("AddInterfaceToList: AWDLInterfaceID = %d", (int) AWDLInterfaceID);
4058    }
4059    i->AppearanceTime  = utc;       // Brand new interface; AppearanceTime is now
4060    i->LastSeen        = utc;
4061    i->ifa_flags       = ifa->ifa_flags;
4062    i->scope_id        = scope_id;
4063    i->BSSID           = bssid;
4064    i->sa_family       = ifa->ifa_addr->sa_family;
4065    i->BPF_fd          = -1;
4066    i->BPF_mcfd        = -1;
4067    i->BPF_len         = 0;
4068    i->Registered      = mDNSNULL;
4069
4070    // Do this AFTER i->BSSID has been set up
4071    i->ifinfo.NetWake  = NetWakeInterface(i);
4072    GetMAC(&i->ifinfo.MAC, scope_id);
4073    if (i->ifinfo.NetWake && !i->ifinfo.MAC.l[0])
4074        LogMsg("AddInterfaceToList: Bad MAC address %.6a for %d %s %#a", &i->ifinfo.MAC, scope_id, i->ifinfo.ifname, &ip);
4075
4076    *p = i;
4077    return(i);
4078}
4079
4080#if APPLE_OSX_mDNSResponder
4081
4082#if COMPILER_LIKES_PRAGMA_MARK
4083#pragma mark -
4084#pragma mark - AutoTunnel
4085#endif
4086
4087#define kRacoonPort 4500
4088
4089static DomainAuthInfo* AnonymousRacoonConfig = mDNSNULL;
4090
4091#ifndef NO_SECURITYFRAMEWORK
4092
4093static CFMutableDictionaryRef domainStatusDict = NULL;
4094
4095mDNSlocal mStatus CheckQuestionForStatus(const DNSQuestion *const q)
4096{
4097    if (q->LongLived)
4098    {
4099        if (q->servAddr.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes(q->servAddr.ip.v4))
4100            return mStatus_NoSuchRecord;
4101        else if (q->state == LLQ_Poll)
4102            return mStatus_PollingMode;
4103        else if (q->state != LLQ_Established && !q->DuplicateOf)
4104            return mStatus_TransientErr;
4105    }
4106
4107    return mStatus_NoError;
4108}
4109
4110mDNSlocal mStatus UpdateLLQStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info)
4111{
4112    mStatus status = mStatus_NoError;
4113    DNSQuestion* q, *worst_q = mDNSNULL;
4114    for (q = m->Questions; q; q=q->next)
4115        if (q->AuthInfo == info)
4116        {
4117            mStatus newStatus = CheckQuestionForStatus(q);
4118            if      (newStatus == mStatus_NoSuchRecord) { status = newStatus; worst_q = q; break; }
4119            else if (newStatus == mStatus_PollingMode)  { status = newStatus; worst_q = q; }
4120            else if (newStatus == mStatus_TransientErr && status == mStatus_NoError) { status = newStatus; worst_q = q; }
4121        }
4122
4123    if      (status == mStatus_NoError) mDNS_snprintf(buffer, bufsz, "Success");
4124    else if (status == mStatus_NoSuchRecord) mDNS_snprintf(buffer, bufsz, "GetZoneData %s: %##s", worst_q->nta ? "not yet complete" : "failed", worst_q->qname.c);
4125    else if (status == mStatus_PollingMode) mDNS_snprintf(buffer, bufsz, "Query polling %##s", worst_q->qname.c);
4126    else if (status == mStatus_TransientErr) mDNS_snprintf(buffer, bufsz, "Query not yet established %##s", worst_q->qname.c);
4127    return status;
4128}
4129
4130mDNSlocal mStatus UpdateRRStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info)
4131{
4132    AuthRecord *r;
4133
4134    if (info->deltime) return mStatus_NoError;
4135    for (r = m->ResourceRecords; r; r = r->next)
4136    {
4137        // This function is called from UpdateAutoTunnelDomainStatus which in turn may be called from
4138        // a callback e.g., CheckNATMappings. GetAuthInfoFor_internal does not like that (reentrancy being 1),
4139        // hence we inline the code here. We just need the lock to walk the list of AuthInfos which the caller
4140        // has already checked
4141        const domainname *n = r->resrec.name;
4142        while (n->c[0])
4143        {
4144            DomainAuthInfo *ptr;
4145            for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
4146                if (SameDomainName(&ptr->domain, n))
4147                {
4148                    if (ptr == info && (r->updateError == mStatus_BadSig || r->updateError == mStatus_BadKey || r->updateError == mStatus_BadTime))
4149                    {
4150                        mDNS_snprintf(buffer, bufsz, "Resource record update failed for %##s", r->resrec.name);
4151                        return r->updateError;
4152                    }
4153                }
4154            n = (const domainname *)(n->c + 1 + n->c[0]);
4155        }
4156    }
4157    return mStatus_NoError;
4158}
4159
4160#endif // ndef NO_SECURITYFRAMEWORK
4161
4162// MUST be called with lock held
4163mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info)
4164{
4165#ifdef NO_SECURITYFRAMEWORK
4166        (void) m;
4167    (void)info;
4168#else
4169    // Note that in the LLQNAT, the clientCallback being non-zero means it's in use,
4170    // whereas in the AutoTunnelNAT, the clientContext being non-zero means it's in use
4171    const NATTraversalInfo *const llq = m->LLQNAT.clientCallback ? &m->LLQNAT : mDNSNULL;
4172    const NATTraversalInfo *const tun = m->AutoTunnelNAT.clientContext ? &m->AutoTunnelNAT : mDNSNULL;
4173    char buffer[1024];
4174    mDNSu32 buflen = 0;
4175    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4176    CFStringRef domain = NULL;
4177    CFStringRef tmp = NULL;
4178    CFNumberRef num = NULL;
4179    mStatus status = mStatus_NoError;
4180    mStatus llqStatus = mStatus_NoError;
4181    char llqBuffer[1024];
4182
4183    mDNS_CheckLock(m);
4184
4185    if (!domainStatusDict)
4186    {
4187        domainStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4188        if (!domainStatusDict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary domainStatusDict"); return; }
4189    }
4190
4191    if (!dict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary dict"); return; }
4192
4193    buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c);
4194    domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
4195    if (!domain) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString domain"); return; }
4196
4197    if (info->deltime)
4198    {
4199        if (CFDictionaryContainsKey(domainStatusDict, domain))
4200        {
4201            CFDictionaryRemoveValue(domainStatusDict, domain);
4202            if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict);
4203        }
4204        CFRelease(domain);
4205        CFRelease(dict);
4206
4207        return;
4208    }
4209
4210    mDNS_snprintf(buffer, sizeof(buffer), "%#a", &m->Router);
4211    tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
4212    if (!tmp)
4213        LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString RouterAddress");
4214    else
4215    {
4216        CFDictionarySetValue(dict, CFSTR("RouterAddress"), tmp);
4217        CFRelease(tmp);
4218    }
4219
4220    if (llq)
4221    {
4222        mDNSu32 port = mDNSVal16(llq->ExternalPort);
4223
4224        num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port);
4225        if (!num)
4226            LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQExternalPort");
4227        else
4228        {
4229            CFDictionarySetValue(dict, CFSTR("LLQExternalPort"), num);
4230            CFRelease(num);
4231        }
4232
4233        if (llq->Result)
4234        {
4235            num = CFNumberCreate(NULL, kCFNumberSInt32Type, &llq->Result);
4236            if (!num)
4237                LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQNPMStatus");
4238            else
4239            {
4240                CFDictionarySetValue(dict, CFSTR("LLQNPMStatus"), num);
4241                CFRelease(num);
4242            }
4243        }
4244    }
4245
4246    if (tun)
4247    {
4248        mDNSu32 port = mDNSVal16(tun->ExternalPort);
4249
4250        num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port);
4251        if (!num)
4252            LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelExternalPort");
4253        else
4254        {
4255            CFDictionarySetValue(dict, CFSTR("AutoTunnelExternalPort"), num);
4256            CFRelease(num);
4257        }
4258
4259        mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &tun->ExternalAddress);
4260        tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
4261        if (!tmp)
4262            LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress");
4263        else
4264        {
4265            CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp);
4266            CFRelease(tmp);
4267        }
4268
4269        if (tun->Result)
4270        {
4271            num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tun->Result);
4272            if (!num)
4273                LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelNPMStatus");
4274            else
4275            {
4276                CFDictionarySetValue(dict, CFSTR("AutoTunnelNPMStatus"), num);
4277                CFRelease(num);
4278            }
4279        }
4280    }
4281    if (tun || llq)
4282    {
4283        mDNSu32 code = m->LastNATMapResultCode;
4284
4285        num = CFNumberCreate(NULL, kCFNumberSInt32Type, &code);
4286        if (!num)
4287            LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LastNATMapResultCode");
4288        else
4289        {
4290            CFDictionarySetValue(dict, CFSTR("LastNATMapResultCode"), num);
4291            CFRelease(num);
4292        }
4293    }
4294
4295    mDNS_snprintf(buffer, sizeof(buffer), "Success");
4296    llqStatus = UpdateLLQStatus(m, llqBuffer, sizeof(llqBuffer), info);
4297    status = UpdateRRStatus(m, buffer, sizeof(buffer), info);
4298
4299    // If we have a bad signature error updating a RR, it overrides any error as it needs to be
4300    // reported so that it can be fixed automatically (or the user needs to be notified)
4301    if (status != mStatus_NoError)
4302    {
4303        LogInfo("UpdateAutoTunnelDomainStatus: RR Status %d, %s", status, buffer);
4304    }
4305    else if (m->Router.type == mDNSAddrType_None)
4306    {
4307        status = mStatus_NoRouter;
4308        mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none");
4309    }
4310    else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4))
4311    {
4312        status = mStatus_NoRouter;
4313        mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero");
4314    }
4315    else if (mDNSIPv6AddressIsZero(info->AutoTunnelInnerAddress))
4316    {
4317        status = mStatus_ServiceNotRunning;
4318        mDNS_snprintf(buffer, sizeof(buffer), "No inner address");
4319    }
4320    else if (!llq && !tun)
4321    {
4322        status = mStatus_NotInitializedErr;
4323        mDNS_snprintf(buffer, sizeof(buffer), "Neither LLQ nor AutoTunnel NAT port mapping is currently active");
4324    }
4325    else if (llqStatus == mStatus_NoSuchRecord)
4326    {
4327        status = llqStatus;
4328        mDNS_snprintf(buffer, sizeof(buffer), llqBuffer);
4329    }
4330    else if ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT))
4331    {
4332        status = mStatus_DoubleNAT;
4333        mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting a private address");
4334    }
4335    else if ((llq && llq->Result == mStatus_NATPortMappingDisabled) ||
4336             (tun && tun->Result == mStatus_NATPortMappingDisabled) ||
4337             (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort)))))
4338    {
4339        status = mStatus_NATPortMappingDisabled;
4340        mDNS_snprintf(buffer, sizeof(buffer), "PCP/NAT-PMP is disabled on the router");
4341    }
4342    else if ((llq && llq->Result) || (tun && tun->Result))
4343    {
4344        status = mStatus_NATTraversal;
4345        mDNS_snprintf(buffer, sizeof(buffer), "Error obtaining NAT port mapping from router");
4346    }
4347    else if ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort)))
4348    {
4349        status = mStatus_NATTraversal;
4350        mDNS_snprintf(buffer, sizeof(buffer), "Unable to obtain NAT port mapping from router");
4351    }
4352    else
4353    {
4354        status = llqStatus;
4355        mDNS_snprintf(buffer, sizeof(buffer), llqBuffer);
4356        LogInfo("UpdateAutoTunnelDomainStatus: LLQ Status %d, %s", status, buffer);
4357    }
4358
4359    num = CFNumberCreate(NULL, kCFNumberSInt32Type, &status);
4360    if (!num)
4361        LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber StatusCode");
4362    else
4363    {
4364        CFDictionarySetValue(dict, CFSTR("StatusCode"), num);
4365        CFRelease(num);
4366    }
4367
4368    tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
4369    if (!tmp)
4370        LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString StatusMessage");
4371    else
4372    {
4373        CFDictionarySetValue(dict, CFSTR("StatusMessage"), tmp);
4374        CFRelease(tmp);
4375    }
4376
4377    if (!CFDictionaryContainsKey(domainStatusDict, domain) ||
4378        !CFEqual(dict, (CFMutableDictionaryRef)CFDictionaryGetValue(domainStatusDict, domain)))
4379    {
4380        CFDictionarySetValue(domainStatusDict, domain, dict);
4381        if (!m->ShutdownTime)
4382        {
4383            static char statusBuf[16];
4384            mDNS_snprintf(statusBuf, sizeof(statusBuf), "%d", (int)status);
4385            mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.domainstatus", status ? "failure" : "success", statusBuf, "");
4386            mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict);
4387        }
4388    }
4389
4390    CFRelease(domain);
4391    CFRelease(dict);
4392
4393    debugf("UpdateAutoTunnelDomainStatus: %s", buffer);
4394#endif // def NO_SECURITYFRAMEWORK
4395}
4396
4397// MUST be called with lock held
4398mDNSexport void UpdateAutoTunnelDomainStatuses(const mDNS *const m)
4399{
4400#ifdef NO_SECURITYFRAMEWORK
4401        (void) m;
4402#else
4403    mDNS_CheckLock(m);
4404    DomainAuthInfo* info;
4405    for (info = m->AuthInfoList; info; info = info->next)
4406        if (info->AutoTunnel && !info->deltime)
4407            UpdateAutoTunnelDomainStatus(m, info);
4408#endif // def NO_SECURITYFRAMEWORK
4409}
4410
4411mDNSlocal void UpdateAnonymousRacoonConfig(mDNS *m)     // Determine whether we need racoon to accept incoming connections
4412{
4413    DomainAuthInfo *info;
4414
4415    for (info = m->AuthInfoList; info; info = info->next)
4416        if (info->AutoTunnel && !info->deltime && (!mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || !mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddr)))
4417            break;
4418
4419    if (info != AnonymousRacoonConfig)
4420    {
4421        AnonymousRacoonConfig = info;
4422        // Create or revert configuration file, and start (or SIGHUP) Racoon
4423        (void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? btmmprefix : mDNSNULL, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL);
4424    }
4425}
4426
4427mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
4428
4429// Caller must hold the lock
4430mDNSlocal mDNSBool DeregisterAutoTunnelRecord(mDNS *m, DomainAuthInfo *info, AuthRecord* record)
4431{
4432    mDNS_CheckLock(m);
4433
4434    LogInfo("DeregisterAutoTunnelRecord %##s %##s", &info->domain.c, record->namestorage.c);
4435
4436    if (record->resrec.RecordType > kDNSRecordTypeDeregistering)
4437    {
4438        mStatus err = mDNS_Deregister_internal(m, record, mDNS_Dereg_normal);
4439        if (err)
4440        {
4441            record->resrec.RecordType = kDNSRecordTypeUnregistered;
4442            LogMsg("DeregisterAutoTunnelRecord error %d deregistering %##s %##s", err, info->domain.c, record->namestorage.c);
4443            return mDNSfalse;
4444        }
4445        else LogInfo("DeregisterAutoTunnelRecord: Deregistered");
4446    }
4447    else LogInfo("DeregisterAutoTunnelRecord: Not deregistering, state:%d", record->resrec.RecordType);
4448
4449    return mDNStrue;
4450}
4451
4452// Caller must hold the lock
4453mDNSlocal void DeregisterAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info)
4454{
4455    if (!DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelHostRecord))
4456    {
4457        info->AutoTunnelHostRecord.namestorage.c[0] = 0;
4458        m->NextSRVUpdate = NonZeroTime(m->timenow);
4459    }
4460}
4461
4462// Caller must hold the lock
4463mDNSlocal void UpdateAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info)
4464{
4465    mStatus err;
4466    mDNSBool NATProblem = mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || m->AutoTunnelNAT.Result;
4467
4468    mDNS_CheckLock(m);
4469
4470    if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime || mDNSIPv6AddressIsZero(info->AutoTunnelInnerAddress) || (m->SleepState != SleepState_Awake && NATProblem))
4471    {
4472        LogInfo("UpdateAutoTunnelHostRecord: Dereg %##s : AutoTunnelServiceStarted(%d) deltime(%d) address(%.16a) sleepstate(%d)",
4473                info->domain.c, info->AutoTunnelServiceStarted, info->deltime, &info->AutoTunnelInnerAddress, m->SleepState);
4474        DeregisterAutoTunnelHostRecord(m, info);
4475    }
4476    else if (info->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered)
4477    {
4478        mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL,
4479                                 kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
4480        info->AutoTunnelHostRecord.namestorage.c[0] = 0;
4481        AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel);
4482        AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain);
4483        info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = info->AutoTunnelInnerAddress;
4484        info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeKnownUnique;
4485
4486        err = mDNS_Register_internal(m, &info->AutoTunnelHostRecord);
4487        if (err) LogMsg("UpdateAutoTunnelHostRecord error %d registering %##s", err, info->AutoTunnelHostRecord.namestorage.c);
4488        else
4489        {
4490            // Make sure we trigger the registration of all SRV records in regState_NoTarget again
4491            m->NextSRVUpdate = NonZeroTime(m->timenow);
4492            LogInfo("UpdateAutoTunnelHostRecord registering %##s", info->AutoTunnelHostRecord.namestorage.c);
4493        }
4494    }
4495    else LogInfo("UpdateAutoTunnelHostRecord: Type %d", info->AutoTunnelHostRecord.resrec.RecordType);
4496}
4497
4498// Caller must hold the lock
4499mDNSlocal void DeregisterAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info)
4500{
4501    LogInfo("DeregisterAutoTunnelServiceRecords %##s", info->domain.c);
4502
4503    DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelTarget);
4504    DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelService);
4505    UpdateAutoTunnelHostRecord(m, info);
4506}
4507
4508// Caller must hold the lock
4509mDNSlocal void UpdateAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info)
4510{
4511    mDNS_CheckLock(m);
4512
4513    if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime || mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || m->AutoTunnelNAT.Result)
4514    {
4515        LogInfo("UpdateAutoTunnelServiceRecords: Dereg %##s : AutoTunnelServiceStarted(%d) deltime(%d) ExtPort(%d) NATResult(%d)", info->domain.c, info->AutoTunnelServiceStarted, info->deltime, mDNSVal16(m->AutoTunnelNAT.ExternalPort), m->AutoTunnelNAT.Result);
4516        DeregisterAutoTunnelServiceRecords(m, info);
4517    }
4518    else
4519    {
4520        if (info->AutoTunnelTarget.resrec.RecordType == kDNSRecordTypeUnregistered)
4521        {
4522            // 1. Set up our address record for the external tunnel address
4523            // (Constructed name, not generally user-visible, used as target in IKE tunnel's SRV record)
4524            mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL,
4525                                     kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
4526            AssignDomainName (&info->AutoTunnelTarget.namestorage, (const domainname*) "\x0B" "_autotunnel");
4527            AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->hostlabel);
4528            AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain);
4529            info->AutoTunnelTarget.resrec.rdata->u.ipv4 = m->AutoTunnelNAT.ExternalAddress;
4530            info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique;
4531
4532            mStatus err = mDNS_Register_internal(m, &info->AutoTunnelTarget);
4533            if (err) LogMsg("UpdateAutoTunnelServiceRecords error %d registering %##s", err, info->AutoTunnelTarget.namestorage.c);
4534            else LogInfo("UpdateAutoTunnelServiceRecords registering %##s", info->AutoTunnelTarget.namestorage.c);
4535        }
4536        else LogInfo("UpdateAutoTunnelServiceRecords: NOOP Target state(%d)", info->AutoTunnelTarget.resrec.RecordType);
4537
4538        if (info->AutoTunnelService.resrec.RecordType == kDNSRecordTypeUnregistered)
4539        {
4540            // 2. Set up IKE tunnel's SRV record: _autotunnel._udp.AutoTunnelHost SRV 0 0 port AutoTunnelTarget
4541            mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV,  kHostNameTTL,
4542                                     kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
4543            AssignDomainName (&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
4544            AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel);
4545            AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain);
4546            info->AutoTunnelService.resrec.rdata->u.srv.priority = 0;
4547            info->AutoTunnelService.resrec.rdata->u.srv.weight   = 0;
4548            info->AutoTunnelService.resrec.rdata->u.srv.port     = m->AutoTunnelNAT.ExternalPort;
4549            AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage);
4550            info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique;
4551
4552            mStatus err = mDNS_Register_internal(m, &info->AutoTunnelService);
4553            if (err) LogMsg("UpdateAutoTunnelServiceRecords error %d registering %##s", err, info->AutoTunnelService.namestorage.c);
4554            else LogInfo("UpdateAutoTunnelServiceRecords registering %##s", info->AutoTunnelService.namestorage.c);
4555        }
4556        else LogInfo("UpdateAutoTunnelServiceRecords: NOOP Service state(%d)", info->AutoTunnelService.resrec.RecordType);
4557
4558        UpdateAutoTunnelHostRecord(m, info);
4559
4560        LogInfo("AutoTunnel server listening for connections on %##s[%.4a]:%d:%##s[%.16a]",
4561                info->AutoTunnelTarget.namestorage.c,     &m->AdvertisedV4.ip.v4, mDNSVal16(m->AutoTunnelNAT.IntPort),
4562                info->AutoTunnelHostRecord.namestorage.c, &info->AutoTunnelInnerAddress);
4563
4564    }
4565}
4566
4567// Caller must hold the lock
4568mDNSlocal void DeregisterAutoTunnelDeviceInfoRecord(mDNS *m, DomainAuthInfo *info)
4569{
4570    DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelDeviceInfo);
4571}
4572
4573// Caller must hold the lock
4574mDNSlocal void UpdateAutoTunnelDeviceInfoRecord(mDNS *m, DomainAuthInfo *info)
4575{
4576    mDNS_CheckLock(m);
4577
4578    if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime)
4579        DeregisterAutoTunnelDeviceInfoRecord(m, info);
4580    else if (info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
4581    {
4582        mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT,  kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
4583        ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain);
4584
4585        info->AutoTunnelDeviceInfo.resrec.rdlength = initializeDeviceInfoTXT(m, info->AutoTunnelDeviceInfo.resrec.rdata->u.data);
4586        info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique;
4587
4588        mStatus err = mDNS_Register_internal(m, &info->AutoTunnelDeviceInfo);
4589        if (err) LogMsg("UpdateAutoTunnelDeviceInfoRecord error %d registering %##s", err, info->AutoTunnelDeviceInfo.namestorage.c);
4590        else LogInfo("UpdateAutoTunnelDeviceInfoRecord registering %##s", info->AutoTunnelDeviceInfo.namestorage.c);
4591    }
4592    else
4593        LogInfo("UpdateAutoTunnelDeviceInfoRecord: not in Unregistered state: %d",info->AutoTunnelDeviceInfo.resrec.RecordType);
4594}
4595
4596// Caller must hold the lock
4597mDNSlocal void DeregisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info)
4598{
4599    LogInfo("DeregisterAutoTunnel6Record %##s", info->domain.c);
4600
4601    DeregisterAutoTunnelRecord(m, info, &info->AutoTunnel6Record);
4602    UpdateAutoTunnelHostRecord(m, info);
4603    UpdateAutoTunnelDomainStatus(m, info);
4604}
4605
4606// Caller must hold the lock
4607mDNSlocal void UpdateAutoTunnel6Record(mDNS *m, DomainAuthInfo *info)
4608{
4609    mDNS_CheckLock(m);
4610
4611    if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime || mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddr) || m->SleepState != SleepState_Awake)
4612        DeregisterAutoTunnel6Record(m, info);
4613    else if (info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered)
4614    {
4615        mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL,
4616                                 kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
4617        AssignDomainName (&info->AutoTunnel6Record.namestorage, (const domainname*) "\x0C" "_autotunnel6");
4618        AppendDomainLabel(&info->AutoTunnel6Record.namestorage, &m->hostlabel);
4619        AppendDomainName (&info->AutoTunnel6Record.namestorage, &info->domain);
4620        info->AutoTunnel6Record.resrec.rdata->u.ipv6 = m->AutoTunnelRelayAddr;
4621        info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeKnownUnique;
4622
4623        mStatus err = mDNS_Register_internal(m, &info->AutoTunnel6Record);
4624        if (err) LogMsg("UpdateAutoTunnel6Record error %d registering %##s", err, info->AutoTunnel6Record.namestorage.c);
4625        else LogInfo("UpdateAutoTunnel6Record registering %##s", info->AutoTunnel6Record.namestorage.c);
4626
4627        UpdateAutoTunnelHostRecord(m, info);
4628
4629        LogInfo("AutoTunnel6 server listening for connections on %##s[%.16a] :%##s[%.16a]",
4630                info->AutoTunnel6Record.namestorage.c,    &m->AutoTunnelRelayAddr,
4631                info->AutoTunnelHostRecord.namestorage.c, &info->AutoTunnelInnerAddress);
4632
4633    }
4634    else LogInfo("UpdateAutoTunnel6Record NOOP state(%d)",info->AutoTunnel6Record.resrec.RecordType);
4635}
4636
4637mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
4638{
4639    DomainAuthInfo *info = (DomainAuthInfo *)rr->RecordContext;
4640    if (result == mStatus_MemFree)
4641    {
4642        LogInfo("AutoTunnelRecordCallback MemFree %s", ARDisplayString(m, rr));
4643
4644        mDNS_Lock(m);
4645
4646        // Reset the host record namestorage to force high-level PTR/SRV/TXT to deregister
4647        if (rr == &info->AutoTunnelHostRecord)
4648        {
4649            rr->namestorage.c[0] = 0;
4650            m->NextSRVUpdate = NonZeroTime(m->timenow);
4651            LogInfo("AutoTunnelRecordCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow);
4652        }
4653        if (m->ShutdownTime)
4654        {
4655            LogInfo("AutoTunnelRecordCallback: Shutdown, returning");
4656            mDNS_Unlock(m);
4657            return;
4658        }
4659        if (rr == &info->AutoTunnelHostRecord)
4660        {
4661            LogInfo("AutoTunnelRecordCallback: calling UpdateAutoTunnelHostRecord");
4662            UpdateAutoTunnelHostRecord(m,info);
4663        }
4664        else if (rr == &info->AutoTunnelDeviceInfo)
4665        {
4666            LogInfo("AutoTunnelRecordCallback: Calling UpdateAutoTunnelDeviceInfoRecord");
4667            UpdateAutoTunnelDeviceInfoRecord(m,info);
4668        }
4669        else if (rr == &info->AutoTunnelService || rr == &info->AutoTunnelTarget)
4670        {
4671            LogInfo("AutoTunnelRecordCallback: Calling UpdateAutoTunnelServiceRecords");
4672            UpdateAutoTunnelServiceRecords(m,info);
4673        }
4674        else if (rr == &info->AutoTunnel6Record)
4675        {
4676            LogInfo("AutoTunnelRecordCallback: Calling UpdateAutoTunnel6Record");
4677            UpdateAutoTunnel6Record(m,info);
4678        }
4679
4680        mDNS_Unlock(m);
4681    }
4682}
4683
4684mDNSlocal void AutoTunnelNATCallback(mDNS *m, NATTraversalInfo *n)
4685{
4686    DomainAuthInfo *info;
4687
4688    LogInfo("AutoTunnelNATCallback Result %d %.4a Internal %d External %d",
4689            n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort));
4690
4691    mDNS_Lock(m);
4692
4693    m->NextSRVUpdate = NonZeroTime(m->timenow);
4694    LogInfo("AutoTunnelNATCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow);
4695
4696    for (info = m->AuthInfoList; info; info = info->next)
4697        if (info->AutoTunnel)
4698            UpdateAutoTunnelServiceRecords(m, info);
4699
4700    UpdateAnonymousRacoonConfig(m);     // Determine whether we need racoon to accept incoming connections
4701
4702    UpdateAutoTunnelDomainStatuses(m);
4703
4704    mDNS_Unlock(m);
4705}
4706
4707mDNSlocal void AutoTunnelHostNameChanged(mDNS *m, DomainAuthInfo *info)
4708{
4709    LogInfo("AutoTunnelHostNameChanged %#s.%##s", m->hostlabel.c, info->domain.c);
4710
4711    mDNS_Lock(m);
4712    // We forcibly deregister the records that are based on the hostname.
4713    // When deregistration of each completes, the MemFree callback will make the
4714    // appropriate Update* call to use the new name to reregister.
4715    DeregisterAutoTunnelHostRecord(m, info);
4716    DeregisterAutoTunnelDeviceInfoRecord(m, info);
4717    DeregisterAutoTunnelServiceRecords(m, info);
4718    DeregisterAutoTunnel6Record(m, info);
4719    m->NextSRVUpdate = NonZeroTime(m->timenow);
4720    mDNS_Unlock(m);
4721}
4722
4723// Must be called with the lock held
4724mDNSexport void StartServerTunnel(mDNS *const m, DomainAuthInfo *const info)
4725{
4726    if (info->deltime) return;
4727
4728    if (info->AutoTunnelServiceStarted)
4729    {
4730        // On wake from sleep, this function will be called when determining SRV targets,
4731        // and needs to re-register the host record for the target to be set correctly
4732        UpdateAutoTunnelHostRecord(m, info);
4733        return;
4734    }
4735
4736    info->AutoTunnelServiceStarted = mDNStrue;
4737
4738    // Now that we have a service in this domain, we need to try to register the
4739    // AutoTunnel records, because the relay connection & NAT-T may have already been
4740    // started for another domain. If the relay connection is not up or the NAT-T has not
4741    // yet succeeded, the Update* functions are smart enough to not register the records.
4742    // Note: This should be done after we set AutoTunnelServiceStarted, as that variable is used to
4743    // decide whether to register the AutoTunnel records in the calls below.
4744    UpdateAutoTunnelServiceRecords(m, info);
4745    UpdateAutoTunnel6Record(m, info);
4746    UpdateAutoTunnelDeviceInfoRecord(m, info);
4747    UpdateAutoTunnelHostRecord(m, info);
4748
4749    // If the global AutoTunnel NAT-T is not yet started, start it.
4750    if (!m->AutoTunnelNAT.clientContext)
4751    {
4752        m->AutoTunnelNAT.clientCallback   = AutoTunnelNATCallback;
4753        m->AutoTunnelNAT.clientContext    = (void*)1; // Means AutoTunnelNAT Traversal is active;
4754        m->AutoTunnelNAT.Protocol         = NATOp_MapUDP;
4755        m->AutoTunnelNAT.IntPort          = IPSECPort;
4756        m->AutoTunnelNAT.RequestedPort    = IPSECPort;
4757        m->AutoTunnelNAT.NATLease         = 0;
4758        mStatus err = mDNS_StartNATOperation_internal(m, &m->AutoTunnelNAT);
4759        if (err) LogMsg("StartServerTunnel: error %d starting NAT mapping", err);
4760    }
4761}
4762
4763mDNSlocal mStatus AutoTunnelSetKeys(ClientTunnel *tun, mDNSBool AddNew)
4764{
4765    mDNSv6Addr loc_outer6;
4766    mDNSv6Addr rmt_outer6;
4767
4768    // When we are tunneling over IPv6 Relay address, the port number is zero
4769    if (mDNSIPPortIsZero(tun->rmt_outer_port))
4770    {
4771        loc_outer6 = tun->loc_outer6;
4772        rmt_outer6 = tun->rmt_outer6;
4773    }
4774    else
4775    {
4776        loc_outer6 = zerov6Addr;
4777        loc_outer6.b[0] = tun->loc_outer.b[0];
4778        loc_outer6.b[1] = tun->loc_outer.b[1];
4779        loc_outer6.b[2] = tun->loc_outer.b[2];
4780        loc_outer6.b[3] = tun->loc_outer.b[3];
4781
4782        rmt_outer6 = zerov6Addr;
4783        rmt_outer6.b[0] = tun->rmt_outer.b[0];
4784        rmt_outer6.b[1] = tun->rmt_outer.b[1];
4785        rmt_outer6.b[2] = tun->rmt_outer.b[2];
4786        rmt_outer6.b[3] = tun->rmt_outer.b[3];
4787    }
4788
4789    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), btmmprefix, SkipLeadingLabels(&tun->dstname, 1)));
4790}
4791
4792// If the EUI-64 part of the IPv6 ULA matches, then that means the two addresses point to the same machine
4793#define mDNSSameClientTunnel(A,B) ((A)->l[2] == (B)->l[2] && (A)->l[3] == (B)->l[3])
4794
4795mDNSlocal void ReissueBlockedQuestionWithType(mDNS *const m, domainname *d, mDNSBool success, mDNSu16 qtype)
4796{
4797    DNSQuestion *q = m->Questions;
4798    while (q)
4799    {
4800        if (q->NoAnswer == NoAnswer_Suspended && q->qtype == qtype && q->AuthInfo && q->AuthInfo->AutoTunnel && SameDomainName(&q->qname, d))
4801        {
4802            LogInfo("Restart %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4803            mDNSQuestionCallback *tmp = q->QuestionCallback;
4804            q->QuestionCallback = AutoTunnelCallback;   // Set QuestionCallback to suppress another call back to AddNewClientTunnel
4805            mDNS_StopQuery(m, q);
4806            mDNS_StartQuery(m, q);
4807            q->QuestionCallback = tmp;                  // Restore QuestionCallback back to the real value
4808            if (!success) q->NoAnswer = NoAnswer_Fail;
4809            // When we call mDNS_StopQuery, it's possible for other subordinate questions like the GetZoneData query to be cancelled too.
4810            // In general we have to assume that the question list might have changed in arbitrary ways.
4811            // This code is itself called from a question callback, so the m->CurrentQuestion mechanism is
4812            // already in use. The safest solution is just to go back to the start of the list and start again.
4813            // In principle this sounds like an n^2 algorithm, but in practice we almost always activate
4814            // just one suspended question, so it's really a 2n algorithm.
4815            q = m->Questions;
4816        }
4817        else
4818            q = q->next;
4819    }
4820}
4821
4822mDNSlocal void ReissueBlockedQuestions(mDNS *const m, domainname *d, mDNSBool success)
4823{
4824    // 1. We deliberately restart AAAA queries before A queries, because in the common case where a BTTM host has
4825    //    a v6 address but no v4 address, we prefer the caller to get the positive AAAA response before the A NXDOMAIN.
4826    // 2. In the case of AAAA queries, if our tunnel setup failed, then we return a deliberate failure indication to the caller --
4827    //    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.
4828    // 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.
4829    ReissueBlockedQuestionWithType(m, d, success, kDNSType_AAAA);
4830    ReissueBlockedQuestionWithType(m, d, mDNStrue, kDNSType_A);
4831}
4832
4833mDNSlocal void UnlinkAndReissueBlockedQuestions(mDNS *const m, ClientTunnel *tun, mDNSBool success)
4834{
4835    ClientTunnel **p = &m->TunnelClients;
4836    while (*p != tun && *p) p = &(*p)->next;
4837    if (*p) *p = tun->next;
4838    ReissueBlockedQuestions(m, &tun->dstname, success);
4839    LogInfo("UnlinkAndReissueBlockedQuestions: Disposing ClientTunnel %p", tun);
4840    freeL("ClientTunnel", tun);
4841}
4842
4843mDNSlocal mDNSBool TunnelClientDeleteMatching(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel)
4844{
4845    ClientTunnel **p;
4846    mDNSBool needSetKeys = mDNStrue;
4847
4848    p = &tun->next;
4849    while (*p)
4850    {
4851        // Is this a tunnel to the same host that we are trying to setup now?
4852        if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next;
4853        else
4854        {
4855            ClientTunnel *old = *p;
4856            if (v6Tunnel)
4857            {
4858                if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; }
4859                LogInfo("TunnelClientDeleteMatching: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4860                if (old->q.ThisQInterval >= 0)
4861                {
4862                    LogInfo("TunnelClientDeleteMatching: Stopping query on IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4863                    mDNS_StopQuery(m, &old->q);
4864                }
4865                else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) ||
4866                         !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner)   ||
4867                         !mDNSSameIPv6Address(old->loc_outer6, tun->loc_outer6) ||
4868                         !mDNSSameIPv6Address(old->rmt_outer6, tun->rmt_outer6))
4869                {
4870                    // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or
4871                    // the other parameters of the tunnel are different
4872                    LogInfo("TunnelClientDeleteMatching: Deleting existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4873                    AutoTunnelSetKeys(old, mDNSfalse);
4874                }
4875                else
4876                {
4877                    // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old
4878                    // as "tun" and "old" are identical
4879                    LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c,
4880                            &old->rmt_inner);
4881                    needSetKeys = mDNSfalse;
4882                }
4883            }
4884            else
4885            {
4886                if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; }
4887                LogInfo("TunnelClientDeleteMatching: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4888                if (old->q.ThisQInterval >= 0)
4889                {
4890                    LogInfo("TunnelClientDeleteMatching: Stopping query on IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4891                    mDNS_StopQuery(m, &old->q);
4892                }
4893                else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) ||
4894                         !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner)   ||
4895                         !mDNSSameIPv4Address(old->loc_outer, tun->loc_outer)   ||
4896                         !mDNSSameIPv4Address(old->rmt_outer, tun->rmt_outer)   ||
4897                         !mDNSSameIPPort(old->rmt_outer_port, tun->rmt_outer_port))
4898                {
4899                    // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or
4900                    // the other parameters of the tunnel are different
4901                    LogInfo("TunnelClientDeleteMatching: Deleting existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4902                    AutoTunnelSetKeys(old, mDNSfalse);
4903                }
4904                else
4905                {
4906                    // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old
4907                    // as "tun" and "old" are identical
4908                    LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c,
4909                            &old->rmt_inner);
4910                    needSetKeys = mDNSfalse;
4911                }
4912            }
4913
4914            *p = old->next;
4915            LogInfo("TunnelClientDeleteMatching: Disposing ClientTunnel %p", old);
4916            freeL("ClientTunnel", old);
4917        }
4918    }
4919    return needSetKeys;
4920}
4921
4922// v6Tunnel indicates whether to delete a tunnel whose outer header is IPv6. If false, outer IPv4
4923// tunnel will be deleted
4924mDNSlocal void TunnelClientDeleteAny(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel)
4925{
4926    ClientTunnel **p;
4927
4928    p = &tun->next;
4929    while (*p)
4930    {
4931        // If there is more than one client tunnel to the same host, delete all of them.
4932        // We do this by just checking against the EUI64 rather than the full address
4933        if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next;
4934        else
4935        {
4936            ClientTunnel *old = *p;
4937            if (v6Tunnel)
4938            {
4939                if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;}
4940                LogInfo("TunnelClientDeleteAny: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4941            }
4942            else
4943            {
4944                if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;}
4945                LogInfo("TunnelClientDeleteAny: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4946            }
4947            if (old->q.ThisQInterval >= 0)
4948            {
4949                LogInfo("TunnelClientDeleteAny: Stopping query on AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4950                mDNS_StopQuery(m, &old->q);
4951            }
4952            else
4953            {
4954                LogInfo("TunnelClientDeleteAny: Deleting existing AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
4955                AutoTunnelSetKeys(old, mDNSfalse);
4956            }
4957            *p = old->next;
4958            LogInfo("TunnelClientDeleteAny: Disposing ClientTunnel %p", old);
4959            freeL("ClientTunnel", old);
4960        }
4961    }
4962}
4963
4964mDNSlocal void TunnelClientFinish(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
4965{
4966    mDNSBool needSetKeys = mDNStrue;
4967    ClientTunnel *tun = (ClientTunnel *)question->QuestionContext;
4968    mDNSBool v6Tunnel = mDNSfalse;
4969    DomainAuthInfo *info;
4970
4971    // If the port is zero, then we have a relay address of the peer
4972    if (mDNSIPPortIsZero(tun->rmt_outer_port))
4973        v6Tunnel = mDNStrue;
4974
4975    if (v6Tunnel)
4976    {
4977        LogInfo("TunnelClientFinish: Relay address %.16a", &answer->rdata->u.ipv6);
4978        tun->rmt_outer6 = answer->rdata->u.ipv6;
4979        tun->loc_outer6 = m->AutoTunnelRelayAddr;
4980    }
4981    else
4982    {
4983        LogInfo("TunnelClientFinish: SRV target address %.4a", &answer->rdata->u.ipv4);
4984        tun->rmt_outer = answer->rdata->u.ipv4;
4985        mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} };
4986        tmpDst.ip.v4 = tun->rmt_outer;
4987        mDNSAddr tmpSrc = zeroAddr;
4988        mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst);
4989        if (tmpSrc.type == mDNSAddrType_IPv4) tun->loc_outer = tmpSrc.ip.v4;
4990        else tun->loc_outer = m->AdvertisedV4.ip.v4;
4991    }
4992
4993    question->ThisQInterval = -1;       // So we know this tunnel setup has completed
4994
4995    info = GetAuthInfoForName(m, &tun->dstname);
4996    if (!info)
4997    {
4998        LogMsg("TunnelClientFinish: Could not get AuthInfo for %##s", tun->dstname.c);
4999        ReissueBlockedQuestions(m, &tun->dstname, mDNSfalse);
5000        return;
5001    }
5002
5003    tun->loc_inner = info->AutoTunnelInnerAddress;
5004
5005    // If we found a v6Relay address for our peer, delete all the v4Tunnels for our peer and
5006    // look for existing tunnels to see whether they have the same information for our peer.
5007    // If not, delete them and need to create a new tunnel. If they are same, just use the
5008    // same tunnel. Do the similar thing if we found a v4Tunnel end point for our peer.
5009    TunnelClientDeleteAny(m, tun, !v6Tunnel);
5010    needSetKeys = TunnelClientDeleteMatching(m, tun, v6Tunnel);
5011
5012    if (needSetKeys) LogInfo("TunnelClientFinish: New %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner);
5013    else LogInfo("TunnelClientFinish: Reusing exiting %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner);
5014
5015    mStatus result = needSetKeys ? AutoTunnelSetKeys(tun, mDNStrue) : mStatus_NoError;
5016    static char msgbuf[32];
5017    mDNS_snprintf(msgbuf, sizeof(msgbuf), "Tunnel setup - %d", result);
5018    mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", result ? "failure" : "success", msgbuf, "");
5019    // Kick off any questions that were held pending this tunnel setup
5020    ReissueBlockedQuestions(m, &tun->dstname, (result == mStatus_NoError) ? mDNStrue : mDNSfalse);
5021}
5022
5023mDNSexport void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
5024{
5025    ClientTunnel *tun = (ClientTunnel *)question->QuestionContext;
5026    DomainAuthInfo *info;
5027
5028    LogInfo("AutoTunnelCallback tun %p AddRecord %d rdlength %d qtype %d", tun, AddRecord, answer->rdlength, question->qtype);
5029
5030    if (!AddRecord) return;
5031    mDNS_StopQuery(m, question);
5032
5033    // If we are looking up the AAAA record for _autotunnel6, don't consider it as failure.
5034    // The code below will look for _autotunnel._udp SRV record followed by A record
5035    if (tun->tc_state != TC_STATE_AAAA_PEER_RELAY && !answer->rdlength)
5036    {
5037        LogInfo("AutoTunnelCallback NXDOMAIN %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5038        static char msgbuf[16];
5039        mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s lookup", DNSTypeName(question->qtype));
5040        mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", "failure", msgbuf, "");
5041        UnlinkAndReissueBlockedQuestions(m, tun, mDNSfalse);
5042        return;
5043    }
5044
5045    switch (tun->tc_state)
5046    {
5047    case TC_STATE_AAAA_PEER:
5048        if (question->qtype != kDNSType_AAAA)
5049        {
5050            LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER", question->qtype);
5051        }
5052        info = GetAuthInfoForName(m, &tun->dstname);
5053        if (!info)
5054        {
5055            LogMsg("AutoTunnelCallback: Could not get AuthInfo for %##s", tun->dstname.c);
5056            UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue);
5057            return;
5058        }
5059        if (mDNSSameIPv6Address(answer->rdata->u.ipv6, info->AutoTunnelInnerAddress))
5060        {
5061            LogInfo("AutoTunnelCallback: suppressing tunnel to self %.16a", &answer->rdata->u.ipv6);
5062            UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue);
5063            return;
5064        }
5065        if (info && mDNSSameIPv6NetworkPart(answer->rdata->u.ipv6, info->AutoTunnelInnerAddress))
5066        {
5067            LogInfo("AutoTunnelCallback: suppressing tunnel to peer %.16a", &answer->rdata->u.ipv6);
5068            UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue);
5069            return;
5070        }
5071        tun->rmt_inner = answer->rdata->u.ipv6;
5072        LogInfo("AutoTunnelCallback:TC_STATE_AAAA_PEER: dst host %.16a", &tun->rmt_inner);
5073        if (!mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddr))
5074        {
5075            LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA");
5076            tun->tc_state = TC_STATE_AAAA_PEER_RELAY;
5077            question->qtype = kDNSType_AAAA;
5078            AssignDomainName(&question->qname, (const domainname*) "\x0C" "_autotunnel6");
5079        }
5080        else
5081        {
5082            LogInfo("AutoTunnelCallback: Looking up _autotunnel._udp SRV");
5083            tun->tc_state = TC_STATE_SRV_PEER;
5084            question->qtype = kDNSType_SRV;
5085            AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
5086        }
5087        AppendDomainName(&question->qname, &tun->dstname);
5088        mDNS_StartQuery(m, &tun->q);
5089        return;
5090    case TC_STATE_AAAA_PEER_RELAY:
5091        if (question->qtype != kDNSType_AAAA)
5092        {
5093            LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER_RELAY", question->qtype);
5094        }
5095        // If it failed, look for the SRV record.
5096        if (!answer->rdlength)
5097        {
5098            LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA failed, trying SRV");
5099            tun->tc_state = TC_STATE_SRV_PEER;
5100            AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
5101            AppendDomainName(&question->qname, &tun->dstname);
5102            question->qtype = kDNSType_SRV;
5103            mDNS_StartQuery(m, &tun->q);
5104            return;
5105        }
5106        TunnelClientFinish(m, question, answer);
5107        return;
5108    case TC_STATE_SRV_PEER:
5109        if (question->qtype != kDNSType_SRV)
5110        {
5111            LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_SRV_PEER", question->qtype);
5112        }
5113        LogInfo("AutoTunnelCallback: SRV target name %##s", answer->rdata->u.srv.target.c);
5114        tun->tc_state = TC_STATE_ADDR_PEER;
5115        AssignDomainName(&tun->q.qname, &answer->rdata->u.srv.target);
5116        tun->rmt_outer_port = answer->rdata->u.srv.port;
5117        question->qtype = kDNSType_A;
5118        mDNS_StartQuery(m, &tun->q);
5119        return;
5120    case TC_STATE_ADDR_PEER:
5121        if (question->qtype != kDNSType_A)
5122        {
5123            LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_ADDR_PEER", question->qtype);
5124        }
5125        TunnelClientFinish(m, question, answer);
5126        return;
5127    default:
5128        LogMsg("AutoTunnelCallback: Unknown question %p", question);
5129    }
5130}
5131
5132// Must be called with the lock held
5133mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q)
5134{
5135    ClientTunnel *p = mallocL("ClientTunnel", sizeof(ClientTunnel));
5136    if (!p) return;
5137    AssignDomainName(&p->dstname, &q->qname);
5138    p->MarkedForDeletion = mDNSfalse;
5139    p->loc_inner      = zerov6Addr;
5140    p->loc_outer      = zerov4Addr;
5141    p->loc_outer6     = zerov6Addr;
5142    p->rmt_inner      = zerov6Addr;
5143    p->rmt_outer      = zerov4Addr;
5144    p->rmt_outer6     = zerov6Addr;
5145    p->rmt_outer_port = zeroIPPort;
5146    p->tc_state = TC_STATE_AAAA_PEER;
5147    p->next = m->TunnelClients;
5148    m->TunnelClients = p;       // We intentionally build list in reverse order
5149
5150    p->q.InterfaceID      = mDNSInterface_Any;
5151    p->q.flags            = 0;
5152    p->q.Target           = zeroAddr;
5153    AssignDomainName(&p->q.qname, &q->qname);
5154    p->q.qtype            = kDNSType_AAAA;
5155    p->q.qclass           = kDNSClass_IN;
5156    p->q.LongLived        = mDNSfalse;
5157    p->q.ExpectUnique     = mDNStrue;
5158    p->q.ForceMCast       = mDNSfalse;
5159    p->q.ReturnIntermed   = mDNStrue;
5160    p->q.SuppressUnusable = mDNSfalse;
5161    p->q.SearchListIndex  = 0;
5162    p->q.AppendSearchDomains = 0;
5163    p->q.RetryWithSearchDomains = mDNSfalse;
5164    p->q.TimeoutQuestion  = 0;
5165    p->q.WakeOnResolve    = 0;
5166    p->q.UseBackgroundTrafficClass = mDNSfalse;
5167    p->q.ValidationRequired = 0;
5168    p->q.ValidatingResponse = 0;
5169    p->q.ProxyQuestion      = 0;
5170    p->q.qnameOrig        = mDNSNULL;
5171    p->q.AnonInfo         = mDNSNULL;
5172    p->q.pid              = mDNSPlatformGetPID();
5173    p->q.QuestionCallback = AutoTunnelCallback;
5174    p->q.QuestionContext  = p;
5175
5176    LogInfo("AddNewClientTunnel start tun %p %##s (%s)%s", p, &q->qname.c, DNSTypeName(q->qtype), q->LongLived ? " LongLived" : "");
5177    mDNS_StartQuery_internal(m, &p->q);
5178}
5179
5180#endif // APPLE_OSX_mDNSResponder
5181
5182#if COMPILER_LIKES_PRAGMA_MARK
5183#pragma mark -
5184#pragma mark - Power State & Configuration Change Management
5185#endif
5186
5187mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
5188{
5189    mDNSBool foundav4           = mDNSfalse;
5190    mDNSBool foundav6           = mDNSfalse;
5191    struct ifaddrs *ifa         = myGetIfAddrs(1);
5192    struct ifaddrs *v4Loopback  = NULL;
5193    struct ifaddrs *v6Loopback  = NULL;
5194    char defaultname[64];
5195    int InfoSocket              = socket(AF_INET6, SOCK_DGRAM, 0);
5196    if (InfoSocket < 3 && errno != EAFNOSUPPORT)
5197        LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno));
5198
5199    while (ifa)
5200    {
5201#if LIST_ALL_INTERFACES
5202        if (ifa->ifa_addr->sa_family == AF_APPLETALK)
5203            LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_APPLETALK",
5204                   ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
5205        else if (ifa->ifa_addr->sa_family == AF_LINK)
5206            LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_LINK",
5207                   ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
5208        else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)
5209            LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)",
5210                   ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
5211        if (!(ifa->ifa_flags & IFF_UP))
5212            LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_UP",
5213                   ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
5214        if (!(ifa->ifa_flags & IFF_MULTICAST))
5215            LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_MULTICAST",
5216                   ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
5217        if (ifa->ifa_flags & IFF_POINTOPOINT)
5218            LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT",
5219                   ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
5220        if (ifa->ifa_flags & IFF_LOOPBACK)
5221            LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK",
5222                   ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
5223#endif
5224
5225        if (ifa->ifa_addr->sa_family == AF_LINK)
5226        {
5227            struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
5228            if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == sizeof(m->PrimaryMAC) && mDNSSameEthAddress(&m->PrimaryMAC, &zeroEthAddr))
5229                mDNSPlatformMemCopy(m->PrimaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 6);
5230        }
5231
5232        if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr)
5233            if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6)
5234            {
5235                if (!ifa->ifa_netmask)
5236                {
5237                    mDNSAddr ip;
5238                    SetupAddr(&ip, ifa->ifa_addr);
5239                    LogMsg("getifaddrs: ifa_netmask is NULL for %5s(%d) Flags %04X Family %2d %#a",
5240                           ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip);
5241                }
5242                // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be zero, so we don't complain about that
5243                // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
5244                else if (ifa->ifa_netmask->sa_family != ifa->ifa_addr->sa_family && ifa->ifa_netmask->sa_family != 0)
5245                {
5246                    mDNSAddr ip;
5247                    SetupAddr(&ip, ifa->ifa_addr);
5248                    LogMsg("getifaddrs ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d",
5249                           ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip, ifa->ifa_netmask->sa_family);
5250                }
5251                // Currently we use a few internal ones like mDNSInterfaceID_LocalOnly etc. that are negative values (0, -1, -2).
5252                else if ((int)if_nametoindex(ifa->ifa_name) <= 0)
5253                {
5254                    LogMsg("UpdateInterfaceList: if_nametoindex returned zero/negative value for %5s(%d)", ifa->ifa_name, if_nametoindex(ifa->ifa_name));
5255                }
5256                else
5257                {
5258                    // Make sure ifa_netmask->sa_family is set correctly
5259                    // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
5260                    ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family;
5261                    int ifru_flags6 = 0;
5262
5263                    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
5264                    if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0)
5265                    {
5266                        struct in6_ifreq ifr6;
5267                        mDNSPlatformMemZero((char *)&ifr6, sizeof(ifr6));
5268                        strlcpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name));
5269                        ifr6.ifr_addr = *sin6;
5270                        if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1)
5271                            ifru_flags6 = ifr6.ifr_ifru.ifru_flags6;
5272                        verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6);
5273                    }
5274
5275                    if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY)))
5276                    {
5277                        if (ifa->ifa_flags & IFF_LOOPBACK)
5278                        {
5279                            if (ifa->ifa_addr->sa_family == AF_INET)
5280                                v4Loopback = ifa;
5281                            else if (sin6->sin6_addr.s6_addr[0] != 0xFD)
5282                                v6Loopback = ifa;
5283                        }
5284                        else
5285                        {
5286                            NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc);
5287                            if (i && MulticastInterface(i) && i->ifinfo.Advertise)
5288                            {
5289                                if (ifa->ifa_addr->sa_family == AF_INET)
5290                                    foundav4 = mDNStrue;
5291                                else
5292                                    foundav6 = mDNStrue;
5293                            }
5294                        }
5295                    }
5296                }
5297            }
5298        ifa = ifa->ifa_next;
5299    }
5300
5301    // For efficiency, we don't register a loopback interface when other interfaces of that family are available and advertising
5302    if (!foundav4 && v4Loopback) AddInterfaceToList(m, v4Loopback, utc);
5303    if (!foundav6 && v6Loopback) AddInterfaceToList(m, v6Loopback, utc);
5304
5305    // Now the list is complete, set the McastTxRx setting for each interface.
5306    NetworkInterfaceInfoOSX *i;
5307    for (i = m->p->InterfaceList; i; i = i->next)
5308        if (i->Exists)
5309        {
5310            mDNSBool txrx = MulticastInterface(i);
5311            if (i->ifinfo.McastTxRx != txrx)
5312            {
5313                i->ifinfo.McastTxRx = txrx;
5314                i->Exists = 2; // State change; need to deregister and reregister this interface
5315            }
5316        }
5317
5318    if (InfoSocket >= 0)
5319        close(InfoSocket);
5320
5321    mDNS_snprintf(defaultname, sizeof(defaultname), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen, HINFO_HWstring,
5322                  m->PrimaryMAC.b[0], m->PrimaryMAC.b[1], m->PrimaryMAC.b[2], m->PrimaryMAC.b[3], m->PrimaryMAC.b[4], m->PrimaryMAC.b[5]);
5323
5324    // Set up the nice label
5325    domainlabel nicelabel;
5326    nicelabel.c[0] = 0;
5327    GetUserSpecifiedFriendlyComputerName(&nicelabel);
5328    if (nicelabel.c[0] == 0)
5329    {
5330        debugf("Couldn’t read user-specified Computer Name; using default “%s” instead", defaultname);
5331        MakeDomainLabelFromLiteralString(&nicelabel, defaultname);
5332    }
5333
5334    // Set up the RFC 1034-compliant label
5335    domainlabel hostlabel;
5336    hostlabel.c[0] = 0;
5337    GetUserSpecifiedLocalHostName(&hostlabel);
5338    if (hostlabel.c[0] == 0)
5339    {
5340        debugf("Couldn’t read user-specified Local Hostname; using default “%s.local” instead", defaultname);
5341        MakeDomainLabelFromLiteralString(&hostlabel, defaultname);
5342    }
5343
5344    mDNSBool namechange = mDNSfalse;
5345
5346    // We use a case-sensitive comparison here because even though changing the capitalization
5347    // of the name alone is not significant to DNS, it's still a change from the user's point of view
5348    if (SameDomainLabelCS(m->p->usernicelabel.c, nicelabel.c))
5349        debugf("Usernicelabel (%#s) unchanged since last time; not changing m->nicelabel (%#s)", m->p->usernicelabel.c, m->nicelabel.c);
5350    else
5351    {
5352        if (m->p->usernicelabel.c[0])   // Don't show message first time through, when we first read name from prefs on boot
5353            LogMsg("User updated Computer Name from “%#s” to “%#s”", m->p->usernicelabel.c, nicelabel.c);
5354        m->p->usernicelabel = m->nicelabel = nicelabel;
5355        namechange = mDNStrue;
5356    }
5357
5358    if (SameDomainLabelCS(m->p->userhostlabel.c, hostlabel.c))
5359        debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c);
5360    else
5361    {
5362        if (m->p->userhostlabel.c[0])   // Don't show message first time through, when we first read name from prefs on boot
5363            LogMsg("User updated Local Hostname from “%#s” to “%#s”", m->p->userhostlabel.c, hostlabel.c);
5364        m->p->userhostlabel = m->hostlabel = hostlabel;
5365        mDNS_SetFQDN(m);
5366        namechange = mDNStrue;
5367    }
5368
5369#if APPLE_OSX_mDNSResponder
5370    if (namechange)     // If either name has changed, we need to tickle our AutoTunnel state machine to update its registered records
5371    {
5372        DomainAuthInfo *info;
5373        for (info = m->AuthInfoList; info; info = info->next)
5374            if (info->AutoTunnel) AutoTunnelHostNameChanged(m, info);
5375    }
5376#endif // APPLE_OSX_mDNSResponder
5377
5378    return(mStatus_NoError);
5379}
5380
5381// Returns number of leading one-bits in mask: 0-32 for IPv4, 0-128 for IPv6
5382// Returns -1 if all the one-bits are not contiguous
5383mDNSlocal int CountMaskBits(mDNSAddr *mask)
5384{
5385    int i = 0, bits = 0;
5386    int bytes = mask->type == mDNSAddrType_IPv4 ? 4 : mask->type == mDNSAddrType_IPv6 ? 16 : 0;
5387    while (i < bytes)
5388    {
5389        mDNSu8 b = mask->ip.v6.b[i++];
5390        while (b & 0x80) { bits++; b <<= 1; }
5391        if (b) return(-1);
5392    }
5393    while (i < bytes) if (mask->ip.v6.b[i++]) return(-1);
5394    return(bits);
5395}
5396
5397// returns count of non-link local V4 addresses registered
5398mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc)
5399{
5400    NetworkInterfaceInfoOSX *i;
5401    int count = 0;
5402    for (i = m->p->InterfaceList; i; i = i->next)
5403        if (i->Exists)
5404        {
5405            NetworkInterfaceInfo *const n = &i->ifinfo;
5406            NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AF_UNSPEC);
5407            if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname);
5408
5409            if (i->Registered && i->Registered != primary)  // Sanity check
5410            {
5411                LogMsg("SetupActiveInterfaces ERROR! n->Registered %p != primary %p", i->Registered, primary);
5412                i->Registered = mDNSNULL;
5413            }
5414
5415            if (!i->Registered)
5416            {
5417                // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface,
5418                // so we need to make sure we call mDNS_DeregisterInterface() before disposing it.
5419                // If i->Registered is NOT set, then we haven't registered it and we should not try to deregister it
5420                //
5421
5422                i->Registered = primary;
5423
5424                // If i->LastSeen == utc, then this is a brand-new interface, just created, or an interface that never went away.
5425                // If i->LastSeen != utc, then this is an old interface, previously seen, that went away for (utc - i->LastSeen) seconds.
5426                // 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.
5427                i->Occulting = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->LastSeen > 0 && utc - i->LastSeen < 60);
5428
5429                // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address
5430                // everytime it creates a new interface. We think it is a duplicate and hence consider it
5431                // as flashing and occulting, that is, flapping. If an interface is marked as flapping,
5432                // mDNS_RegisterInterface() changes the probe delay from 1/2 second to 5 seconds and
5433                // logs a warning message to system.log noting frequent interface transitions.
5434                // Same logic applies when IFEF_DIRECTLINK flag is set on the interface.
5435                if ((strncmp(i->ifinfo.ifname, "p2p", 3) == 0) || i->ifinfo.DirectLink)
5436                {
5437                    LogInfo("SetupActiveInterfaces: %s interface registering %s %s", i->ifinfo.ifname,
5438                            i->Flashing               ? " (Flashing)"  : "",
5439                            i->Occulting              ? " (Occulting)" : "");
5440                    mDNS_RegisterInterface(m, n, 0);
5441                }
5442                else
5443                {
5444                    mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting);
5445                }
5446
5447                if (!mDNSAddressIsLinkLocal(&n->ip)) count++;
5448                LogInfo("SetupActiveInterfaces:   Registered    %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s",
5449                        i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, &n->ip, CountMaskBits(&n->mask),
5450                        i->Flashing        ? " (Flashing)"  : "",
5451                        i->Occulting       ? " (Occulting)" : "",
5452                        n->InterfaceActive ? " (Primary)"   : "");
5453
5454                if (!n->McastTxRx)
5455                    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);
5456                else
5457                {
5458                    if (i->sa_family == AF_INET)
5459                    {
5460                        struct ip_mreq imr;
5461                        primary->ifa_v4addr.s_addr = n->ip.ip.v4.NotAnInteger;
5462                        imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger;
5463                        imr.imr_interface        = primary->ifa_v4addr;
5464
5465                        // If this is our *first* IPv4 instance for this interface name, we need to do a IP_DROP_MEMBERSHIP first,
5466                        // before trying to join the group, to clear out stale kernel state which may be lingering.
5467                        // In particular, this happens with removable network interfaces like USB Ethernet adapters -- the kernel has stale state
5468                        // from the last time the USB Ethernet adapter was connected, and part of the kernel thinks we've already joined the group
5469                        // on that interface (so we get EADDRINUSE when we try to join again) but a different part of the kernel thinks we haven't
5470                        // joined the group (so we receive no multicasts). Doing an IP_DROP_MEMBERSHIP before joining seems to flush the stale state.
5471                        // Also, trying to make the code leave the group when the adapter is removed doesn't work either,
5472                        // because by the time we get the configuration change notification, the interface is already gone,
5473                        // so attempts to unsubscribe fail with EADDRNOTAVAIL (errno 49 "Can't assign requested address").
5474                        // <rdar://problem/5585972> IP_ADD_MEMBERSHIP fails for previously-connected removable interfaces
5475                        if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET) == i)
5476                        {
5477                            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);
5478                            mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr));
5479                            if (err < 0 && (errno != EADDRNOTAVAIL))
5480                                LogMsg("setsockopt - IP_DROP_MEMBERSHIP error %d errno %d (%s)", err, errno, strerror(errno));
5481                        }
5482
5483                        LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv4 mcast group %.4a on %.4a", i->ifinfo.ifname, i->scope_id, &imr.imr_multiaddr, &imr.imr_interface);
5484                        mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr));
5485                        // Joining same group twice can give "Address already in use" error -- no need to report that
5486                        if (err < 0 && (errno != EADDRINUSE))
5487                            LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err, errno, strerror(errno), &imr.imr_multiaddr, &imr.imr_interface);
5488                    }
5489                    if (i->sa_family == AF_INET6)
5490                    {
5491                        struct ipv6_mreq i6mr;
5492                        i6mr.ipv6mr_interface = primary->scope_id;
5493                        i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6;
5494
5495                        if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET6) == i)
5496                        {
5497                            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);
5498                            mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr));
5499                            if (err < 0 && (errno != EADDRNOTAVAIL))
5500                                LogMsg("setsockopt - IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
5501                        }
5502
5503                        LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv6 mcast group %.16a on %u", i->ifinfo.ifname, i->scope_id, &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
5504                        mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr));
5505                        // Joining same group twice can give "Address already in use" error -- no need to report that
5506                        if (err < 0 && (errno != EADDRINUSE))
5507                            LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
5508                    }
5509                }
5510            }
5511        }
5512
5513    return count;
5514}
5515
5516mDNSlocal void MarkAllInterfacesInactive(mDNS *const m, mDNSs32 utc)
5517{
5518    NetworkInterfaceInfoOSX *i;
5519    for (i = m->p->InterfaceList; i; i = i->next)
5520    {
5521        if (i->Exists) i->LastSeen = utc;
5522        i->Exists = mDNSfalse;
5523    }
5524}
5525
5526// returns count of non-link local V4 addresses deregistered
5527mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc)
5528{
5529    // First pass:
5530    // If an interface is going away, then deregister this from the mDNSCore.
5531    // We also have to deregister it if the primary interface that it's using for its InterfaceID is going away.
5532    // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory
5533    // it refers to has gone away we'll crash.
5534    NetworkInterfaceInfoOSX *i;
5535    int count = 0;
5536    for (i = m->p->InterfaceList; i; i = i->next)
5537    {
5538        // If this interface is no longer active, or its InterfaceID is changing, deregister it
5539        NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AF_UNSPEC);
5540        if (i->Registered)
5541            if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary)
5542            {
5543                i->Flashing = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->AppearanceTime < 60);
5544                LogInfo("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s",
5545                        i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary,
5546                        &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask),
5547                        i->Flashing               ? " (Flashing)"  : "",
5548                        i->Occulting              ? " (Occulting)" : "",
5549                        i->ifinfo.InterfaceActive ? " (Primary)"   : "");
5550
5551                // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address
5552                // everytime it creates a new interface. We think it is a duplicate and hence consider it
5553                // as flashing and occulting. The "core" does not flush the cache for this case. This leads to
5554                // stale data returned to the application even after the interface is removed. The application
5555                // then starts to send data but the new interface is not yet created.
5556                // Same logic applies when IFEF_DIRECTLINK flag is set on the interface.
5557                if ((strncmp(i->ifinfo.ifname, "p2p", 3) == 0) || i->ifinfo.DirectLink)
5558                {
5559                    LogInfo("ClearInactiveInterfaces: %s interface deregistering %s %s", i->ifinfo.ifname,
5560                            i->Flashing               ? " (Flashing)"  : "",
5561                            i->Occulting              ? " (Occulting)" : "");
5562                    mDNS_DeregisterInterface(m, &i->ifinfo, 0);
5563                }
5564                else
5565                {
5566                    mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting);
5567                }
5568                if (!mDNSAddressIsLinkLocal(&i->ifinfo.ip)) count++;
5569                i->Registered = mDNSNULL;
5570                // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface,
5571                // so we need to make sure we call mDNS_DeregisterInterface() before disposing it.
5572                // If i->Registered is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it.
5573
5574                // Caution: If we ever decide to add code here to leave the multicast group, we need to make sure that this
5575                // is the LAST representative of this physical interface, or we'll unsubscribe from the group prematurely.
5576            }
5577    }
5578
5579    // Second pass:
5580    // Now that everything that's going to deregister has done so, we can clean up and free the memory
5581    NetworkInterfaceInfoOSX **p = &m->p->InterfaceList;
5582    while (*p)
5583    {
5584        i = *p;
5585        // If no longer active, delete interface from list and free memory
5586        if (!i->Exists)
5587        {
5588            if (i->LastSeen == utc) i->LastSeen = utc - 1;
5589            mDNSBool delete = (NumCacheRecordsForInterfaceID(m, i->ifinfo.InterfaceID) == 0) && (utc - i->LastSeen >= 60);
5590            LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p(%p) %#a/%d Age %d%s", delete ? "Deleting" : "Holding",
5591                    i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i,
5592                    &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), utc - i->LastSeen,
5593                    i->ifinfo.InterfaceActive ? " (Primary)" : "");
5594#if APPLE_OSX_mDNSResponder
5595            if (i->BPF_fd >= 0) CloseBPF(i);
5596#endif // APPLE_OSX_mDNSResponder
5597            if (delete)
5598            {
5599                *p = i->next;
5600                freeL("NetworkInterfaceInfoOSX", i);
5601                continue;   // After deleting this object, don't want to do the "p = &i->next;" thing at the end of the loop
5602            }
5603        }
5604        p = &i->next;
5605    }
5606    return count;
5607}
5608
5609mDNSlocal void AppendDNameListElem(DNameListElem ***List, mDNSu32 uid, domainname *name)
5610{
5611    DNameListElem *dnle = (DNameListElem*) mallocL("DNameListElem/AppendDNameListElem", sizeof(DNameListElem));
5612    if (!dnle) LogMsg("ERROR: AppendDNameListElem: memory exhausted");
5613    else
5614    {
5615        dnle->next = mDNSNULL;
5616        dnle->uid  = uid;
5617        AssignDomainName(&dnle->name, name);
5618        **List = dnle;
5619        *List = &dnle->next;
5620    }
5621}
5622
5623mDNSlocal int compare_dns_configs(const void *aa, const void *bb)
5624{
5625    dns_resolver_t *a = *(dns_resolver_t**)aa;
5626    dns_resolver_t *b = *(dns_resolver_t**)bb;
5627
5628    return (a->search_order < b->search_order) ? -1 : (a->search_order == b->search_order) ? 0 : 1;
5629}
5630
5631mDNSlocal void UpdateSearchDomainHash(mDNS *const m, MD5_CTX *sdc, char *domain, mDNSInterfaceID InterfaceID)
5632{
5633    char *buf = ".";
5634    mDNSu32 scopeid = 0;
5635    char ifid_buf[16];
5636
5637    if (domain)
5638        buf = domain;
5639    //
5640    // Hash the search domain name followed by the InterfaceID.
5641    // As we have scoped search domains, we also included InterfaceID. If either of them change,
5642    // we will detect it. Even if the order of them change, we will detect it.
5643    //
5644    // Note: We have to handle a few of these tricky cases.
5645    //
5646    // 1) Current: com, apple.com Changing to: comapple.com
5647    // 2) Current: a.com,b.com Changing to a.comb.com
5648    // 3) Current: a.com,b.com (ifid 8), Changing to a.com8b.com (ifid 8)
5649    // 4) Current: a.com (ifid 12), Changing to a.com1 (ifid: 2)
5650    //
5651    // There are more variants of the above. The key thing is if we include the null in each case
5652    // at the end of name and the InterfaceID, it will prevent a new name (which can't include
5653    // NULL as part of the name) to be mistakenly thought of as a old name.
5654
5655    scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue);
5656    // mDNS_snprintf always null terminates
5657    if (mDNS_snprintf(ifid_buf, sizeof(ifid_buf), "%u", scopeid) >= sizeof(ifid_buf))
5658        LogMsg("UpdateSearchDomainHash: mDNS_snprintf failed for scopeid %u", scopeid);
5659
5660    LogInfo("UpdateSearchDomainHash: buf %s, ifid_buf %s", buf, ifid_buf);
5661    MD5_Update(sdc, buf, strlen(buf) + 1);
5662    MD5_Update(sdc, ifid_buf, strlen(ifid_buf) + 1);
5663}
5664
5665mDNSlocal void FinalizeSearchDomainHash(mDNS *const m, MD5_CTX *sdc)
5666{
5667    mDNSu8 md5_hash[MD5_LEN];
5668
5669    MD5_Final(md5_hash, sdc);
5670
5671    if (memcmp(md5_hash, m->SearchDomainsHash, MD5_LEN))
5672    {
5673        // If the hash is different, either the search domains have changed or
5674        // the ordering between them has changed. Restart the questions that
5675        // would be affected by this.
5676        LogInfo("FinalizeSearchDomains: The hash is different");
5677        memcpy(m->SearchDomainsHash, md5_hash, MD5_LEN);
5678        RetrySearchDomainQuestions(m);
5679    }
5680    else { LogInfo("FinalizeSearchDomains: The hash is same"); }
5681}
5682
5683mDNSexport const char *DNSScopeToString(mDNSu32 scope)
5684{
5685    switch (scope)
5686    {
5687        case kScopeNone:
5688            return "Unscoped";
5689        case kScopeInterfaceID:
5690            return "InterfaceScoped";
5691        case kScopeServiceID:
5692            return "ServiceScoped";
5693        default:
5694            return "Unknown";
5695    }
5696}
5697
5698mDNSlocal void ConfigSearchDomains(mDNS *const m, dns_resolver_t *resolver, mDNSInterfaceID interface, mDNSu32 scope, MD5_CTX *sdc)
5699{
5700    const char *scopeString = DNSScopeToString(scope);
5701    int j;
5702
5703    if (scope != kScopeNone)
5704    {
5705        LogInfo("ConfigSearchDomains: (%s) Ignoring search domain for Interface %p", scopeString, interface);
5706        return;
5707    }
5708    for (j = 0; j < resolver->n_search; j++)
5709    {
5710        LogInfo("ConfigSearchDomains: (%s) configuring search list %s", scopeString, resolver->search[j]);
5711        UpdateSearchDomainHash(m, sdc, resolver->search[j], NULL);
5712        mDNS_AddSearchDomain_CString(resolver->search[j], NULL);
5713    }
5714}
5715
5716mDNSlocal mDNSInterfaceID ConfigParseInterfaceID(mDNS *const m, mDNSu32 ifindex)
5717{
5718    NetworkInterfaceInfoOSX *ni;
5719    mDNSInterfaceID interface;
5720
5721    for (ni = m->p->InterfaceList; ni; ni = ni->next)
5722    {
5723        if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex)
5724            break;
5725    }
5726    if (ni != NULL)
5727    {
5728        interface = ni->ifinfo.InterfaceID;
5729    }
5730    else
5731    {
5732        // In rare circumstances, we could potentially hit this case where we cannot parse the InterfaceID
5733        // (see <rdar://problem/13214785>). At this point, we still accept the DNS Config from configd
5734        // Note: We currently ack the whole dns configuration and not individual resolvers or DNS servers.
5735        // As the caller is going to ack the configuration always, we have to add all the DNS servers
5736        // in the configuration. Otherwise, we won't have any DNS servers up until the network change.
5737
5738        LogMsg("ConfigParseInterfaceID: interface specific index %d not found (interface may not be UP)",ifindex);
5739
5740        // Set the correct interface from configd before passing this to mDNS_AddDNSServer() below
5741        interface = (mDNSInterfaceID)(unsigned long)ifindex;
5742    }
5743    return interface;
5744}
5745
5746mDNSlocal void ConfigNonUnicastResolver(mDNS *const m, dns_resolver_t *r)
5747{
5748    char *opt = r->options;
5749    domainname d;
5750
5751    if (opt && !strncmp(opt, "mdns", strlen(opt)))
5752    {
5753        if (!MakeDomainNameFromDNSNameString(&d, r->domain))
5754        {
5755            LogMsg("ConfigNonUnicastResolver: config->resolver bad domain %s", r->domain);
5756            return;
5757        }
5758        mDNS_AddMcastResolver(m, &d, mDNSInterface_Any, r->timeout);
5759    }
5760}
5761
5762mDNSlocal void ConfigDNSServers(mDNS *const m, dns_resolver_t *r, mDNSInterfaceID interface, mDNSu32 scope, mDNSu16 resGroupID)
5763{
5764    int n;
5765    domainname d;
5766    int serviceID = 0;
5767    mDNSBool cellIntf = mDNSfalse;
5768    mDNSBool scopedDNS = mDNSfalse;
5769    mDNSBool reqA, reqAAAA;
5770
5771    if (!r->domain || !*r->domain)
5772    {
5773        d.c[0] = 0;
5774    }
5775    else if (!MakeDomainNameFromDNSNameString(&d, r->domain))
5776    {
5777        LogMsg("ConfigDNSServers: bad domain %s", r->domain);
5778        return;
5779    }
5780    // Parse the resolver specific attributes that affects all the DNS servers.
5781    if (scope == kScopeInterfaceID)
5782    {
5783        scopedDNS = mDNStrue;
5784    }
5785    else if (scope == kScopeServiceID)
5786    {
5787        serviceID = r->service_identifier;
5788    }
5789
5790#if TARGET_OS_IPHONE
5791    cellIntf = (r->reach_flags & kSCNetworkReachabilityFlagsIsWWAN) ? mDNStrue : mDNSfalse;
5792#endif
5793    reqA = (r->flags & DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS ? mDNStrue : mDNSfalse);
5794    reqAAAA = (r->flags & DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS ? mDNStrue : mDNSfalse);
5795
5796    for (n = 0; n < r->n_nameserver; n++)
5797    {
5798        mDNSAddr saddr;
5799        DNSServer *s;
5800
5801        if (r->nameserver[n]->sa_family != AF_INET && r->nameserver[n]->sa_family != AF_INET6)
5802            continue;
5803
5804        if (SetupAddr(&saddr, r->nameserver[n]))
5805        {
5806            LogMsg("ConfigDNSServers: Bad address");
5807            continue;
5808        }
5809
5810        // The timeout value is for all the DNS servers in a given resolver, hence we pass
5811        // the timeout value only for the first DNSServer. If we don't have a value in the
5812        // resolver, then use the core's default value
5813        //
5814        // Note: this assumes that when the core picks a list of DNSServers for a question,
5815        // it takes the sum of all the timeout values for all DNS servers. By doing this, it
5816        // tries all the DNS servers in a specified timeout
5817        s = mDNS_AddDNSServer(m, &d, interface, serviceID, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scope,
5818                              (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0), cellIntf, resGroupID, reqA, reqAAAA, mDNStrue);
5819        if (s)
5820        {
5821            LogInfo("ConfigDNSServers(%s): DNS server %#a:%d for domain %##s", DNSScopeToString(scope), &s->addr, mDNSVal16(s->port), d.c);
5822        }
5823    }
5824}
5825
5826// ConfigResolvers is called for different types of resolvers: Unscoped resolver, Interface scope resolver and
5827// Service scope resolvers. This is indicated by the scope argument.
5828//
5829// "resolver" has entries that should only be used for unscoped questions.
5830//
5831// "scoped_resolver" has entries that should only be used for Interface scoped question i.e., questions that specify an
5832// interface index (q->InterfaceID)
5833//
5834// "service_specific_resolver" has entries that should be used for Service scoped question i.e., questions that specify
5835// a service identifier (q->ServiceID)
5836//
5837mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSu32 scope, mDNSBool setsearch, mDNSBool setservers, MD5_CTX *sdc, mDNSu16 resGroupID)
5838{
5839    int i;
5840    dns_resolver_t **resolver;
5841    int nresolvers;
5842    const char *scopeString = DNSScopeToString(scope);
5843    mDNSInterfaceID interface;
5844
5845    switch (scope)
5846    {
5847        case kScopeNone:
5848            resolver = config->resolver;
5849            nresolvers = config->n_resolver;
5850            break;
5851        case kScopeInterfaceID:
5852            resolver = config->scoped_resolver;
5853            nresolvers = config->n_scoped_resolver;
5854            break;
5855        case kScopeServiceID:
5856            resolver = config->service_specific_resolver;
5857            nresolvers = config->n_service_specific_resolver;
5858            break;
5859        default:
5860            return;
5861    }
5862    qsort(resolver, nresolvers, sizeof(dns_resolver_t*), compare_dns_configs);
5863
5864    for (i = 0; i < nresolvers; i++)
5865    {
5866        dns_resolver_t *r = resolver[i];
5867
5868        LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scopeString, i, r->domain, r->n_nameserver);
5869
5870        interface = mDNSInterface_Any;
5871
5872        // Parse the interface index
5873        if (r->if_index != 0)
5874        {
5875            interface = ConfigParseInterfaceID(m, r->if_index);
5876        }
5877
5878        if (setsearch)
5879        {
5880            ConfigSearchDomains(m, resolver[i], interface, scope, sdc);
5881            // Parse other scoped resolvers for search lists
5882            if (!setservers)
5883                continue;
5884        }
5885
5886        if (r->port == 5353 || r->n_nameserver == 0)
5887        {
5888            ConfigNonUnicastResolver(m, r);
5889        }
5890        else
5891        {
5892            // Each scoped resolver gets its own ID (i.e., they are in their own group) so that responses from the
5893            // scoped resolver are not used by other non-scoped or scoped resolvers.
5894            if (scope != kScopeNone)
5895                resGroupID++;
5896
5897            ConfigDNSServers(m, r, interface, scope, resGroupID);
5898        }
5899    }
5900}
5901
5902#if APPLE_OSX_mDNSResponder
5903mDNSlocal mDNSBool QuestionValidForDNSTrigger(DNSQuestion *q)
5904{
5905    if (QuerySuppressed(q))
5906    {
5907        debugf("QuestionValidForDNSTrigger: Suppressed: %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
5908        return mDNSfalse;
5909    }
5910    if (mDNSOpaque16IsZero(q->TargetQID))
5911    {
5912        debugf("QuestionValidForDNSTrigger: Multicast: %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
5913        return mDNSfalse;
5914    }
5915    // If we answered using LocalOnly records e.g., /etc/hosts, don't consider that a valid response
5916    // for trigger.
5917    if (q->LOAddressAnswers)
5918    {
5919        debugf("QuestionValidForDNSTrigger: LocalOnly answers: %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
5920        return mDNSfalse;
5921    }
5922    return mDNStrue;
5923}
5924#endif
5925
5926// This function is called if we are not delivering unicast answers to "A" or "AAAA" questions.
5927// We set our state appropriately so that if we start receiving answers, trigger the
5928// upper layer to retry DNS questions.
5929#if APPLE_OSX_mDNSResponder
5930mDNSexport void mDNSPlatformUpdateDNSStatus(mDNS *const m, DNSQuestion *q)
5931{
5932    if (!QuestionValidForDNSTrigger(q))
5933        return;
5934
5935    // Ignore applications that start and stop queries for no reason before we ever talk
5936    // to any DNS server.
5937    if (!q->triedAllServersOnce)
5938    {
5939        LogInfo("QuestionValidForDNSTrigger: question %##s (%s) stopped too soon", q->qname.c, DNSTypeName(q->qtype));
5940        return;
5941    }
5942    if (q->qtype == kDNSType_A)
5943        m->p->v4answers = 0;
5944    if (q->qtype == kDNSType_AAAA)
5945        m->p->v6answers = 0;
5946    if (!m->p->v4answers || !m->p->v6answers)
5947    {
5948        LogInfo("mDNSPlatformUpdateDNSStatus: Trigger needed v4 %d, v6 %d, quesiton %##s (%s)", m->p->v4answers, m->p->v6answers, q->qname.c,
5949            DNSTypeName(q->qtype));
5950    }
5951}
5952#endif
5953
5954mDNSlocal void AckConfigd(mDNS *const m, dns_config_t *config)
5955{
5956    mDNS_CheckLock(m);
5957
5958    // Acking the configuration triggers configd to reissue the reachability queries
5959    m->p->DNSTrigger = NonZeroTime(m->timenow);
5960    _dns_configuration_ack(config, "com.apple.mDNSResponder");
5961}
5962
5963// If v4q is non-NULL, it means we have received some answers for "A" type questions
5964// If v6q is non-NULL, it means we have received some answers for "AAAA" type questions
5965#if APPLE_OSX_mDNSResponder
5966mDNSexport void mDNSPlatformTriggerDNSRetry(mDNS *const m, DNSQuestion *v4q, DNSQuestion *v6q)
5967{
5968    mDNSBool trigger = mDNSfalse;
5969    mDNSs32 timenow;
5970
5971    // Don't send triggers too often.
5972    // If we have started delivering answers to questions, we should send a trigger
5973    // if the time permits. If we are delivering answers, we should set the state
5974    // of v4answers/v6answers to 1 and avoid sending a trigger.  But, we don't know
5975    // whether the answers that are being delivered currently is for configd or some
5976    // other application. If we set the v4answers/v6answers to 1 and not deliver a trigger,
5977    // then we won't deliver the trigger later when it is okay to send one as the
5978    // "answers" are already set to 1. Hence, don't affect the state of v4answers and
5979    // v6answers if we are not delivering triggers.
5980    mDNS_Lock(m);
5981    timenow = m->timenow;
5982    if (m->p->DNSTrigger && (timenow - m->p->DNSTrigger) < DNS_TRIGGER_INTERVAL)
5983    {
5984        if (!m->p->v4answers || !m->p->v6answers)
5985        {
5986            debugf("mDNSPlatformTriggerDNSRetry: not triggering, time since last trigger %d ms, v4ans %d, v6ans %d",
5987                (timenow - m->p->DNSTrigger), m->p->v4answers, m->p->v6answers);
5988        }
5989        mDNS_Unlock(m);
5990        return;
5991    }
5992    mDNS_Unlock(m);
5993    if (v4q != NULL && QuestionValidForDNSTrigger(v4q))
5994    {
5995        int old = m->p->v4answers;
5996
5997        m->p->v4answers = 1;
5998
5999        // If there are IPv4 answers now and previously we did not have
6000        // any answers, trigger a DNS change so that reachability
6001        // can retry the queries again.
6002        if (!old)
6003        {
6004            LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv4, last trigger %d ms, %##s (%s)", (timenow - m->p->DNSTrigger),
6005                v4q->qname.c, DNSTypeName(v4q->qtype));
6006            trigger = mDNStrue;
6007        }
6008    }
6009    if (v6q != NULL && QuestionValidForDNSTrigger(v6q))
6010    {
6011        int old = m->p->v6answers;
6012
6013        m->p->v6answers = 1;
6014        // If there are IPv6 answers now and previously we did not have
6015        // any answers, trigger a DNS change so that reachability
6016        // can retry the queries again.
6017        if (!old)
6018        {
6019            LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv6, last trigger %d ms, %##s (%s)", (timenow - m->p->DNSTrigger),
6020                v6q->qname.c, DNSTypeName(v6q->qtype));
6021            trigger = mDNStrue;
6022        }
6023    }
6024    if (trigger)
6025    {
6026        dns_config_t *config = dns_configuration_copy();
6027        if (config)
6028        {
6029            mDNS_Lock(m);
6030            AckConfigd(m, config);
6031            mDNS_Unlock(m);
6032            dns_configuration_free(config);
6033        }
6034        else
6035        {
6036            LogMsg("mDNSPlatformTriggerDNSRetry: ERROR!! configd did not return config");
6037        }
6038    }
6039}
6040
6041mDNSlocal void SetupActiveDirectoryDomain(dns_config_t *config)
6042{
6043    // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up
6044    // by someone using Microsoft Active Directory using "local" as a private internal top-level domain
6045    if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver &&
6046        config->resolver[0]->nameserver[0])
6047    {
6048        MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain);
6049    }
6050    else
6051    {
6052         ActiveDirectoryPrimaryDomain.c[0] = 0;
6053    }
6054
6055    //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local");
6056    ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain);
6057    if (config->n_resolver && config->resolver[0]->n_nameserver &&
6058        SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain))
6059    {
6060        SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]);
6061    }
6062    else
6063    {
6064        AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)"");
6065        ActiveDirectoryPrimaryDomainLabelCount = 0;
6066        ActiveDirectoryPrimaryDomainServer = zeroAddr;
6067    }
6068}
6069#endif
6070
6071mDNSlocal void SetupDDNSDomains(domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains)
6072{
6073    int i;
6074    char buf[MAX_ESCAPED_DOMAIN_NAME];  // Max legal C-string name, including terminating NUL
6075    domainname d;
6076
6077    SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformSetDNSConfig"), NULL, NULL);
6078    if (!store)
6079    {
6080        LogMsg("SetupDDNSDomains: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
6081    }
6082    else
6083    {
6084        CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS);
6085        if (ddnsdict)
6086        {
6087            if (fqdn)
6088            {
6089                CFArrayRef fqdnArray = CFDictionaryGetValue(ddnsdict, CFSTR("HostNames"));
6090                if (fqdnArray && CFArrayGetCount(fqdnArray) > 0)
6091                {
6092                    // for now, we only look at the first array element.  if we ever support multiple configurations, we will walk the list
6093                    CFDictionaryRef fqdnDict = CFArrayGetValueAtIndex(fqdnArray, 0);
6094                    if (fqdnDict && DictionaryIsEnabled(fqdnDict))
6095                    {
6096                        CFStringRef name = CFDictionaryGetValue(fqdnDict, CFSTR("Domain"));
6097                        if (name)
6098                        {
6099                            if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) ||
6100                                !MakeDomainNameFromDNSNameString(fqdn, buf) || !fqdn->c[0])
6101                                LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS host name: %s", buf[0] ? buf : "(unknown)");
6102                            else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS host name: %s", buf);
6103                        }
6104                    }
6105                }
6106            }
6107
6108            if (RegDomains)
6109            {
6110                CFArrayRef regArray = CFDictionaryGetValue(ddnsdict, CFSTR("RegistrationDomains"));
6111                if (regArray && CFArrayGetCount(regArray) > 0)
6112                {
6113                    CFDictionaryRef regDict = CFArrayGetValueAtIndex(regArray, 0);
6114                    if (regDict && DictionaryIsEnabled(regDict))
6115                    {
6116                        CFStringRef name = CFDictionaryGetValue(regDict, CFSTR("Domain"));
6117                        if (name)
6118                        {
6119                            if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) ||
6120                                !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0])
6121                                LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS registration domain: %s", buf[0] ? buf : "(unknown)");
6122                            else
6123                            {
6124                                debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration domain: %s", buf);
6125                                AppendDNameListElem(&RegDomains, 0, &d);
6126                            }
6127                        }
6128                    }
6129                }
6130            }
6131
6132            if (BrowseDomains)
6133            {
6134                CFArrayRef browseArray = CFDictionaryGetValue(ddnsdict, CFSTR("BrowseDomains"));
6135                if (browseArray)
6136                {
6137                    for (i = 0; i < CFArrayGetCount(browseArray); i++)
6138                    {
6139                        CFDictionaryRef browseDict = CFArrayGetValueAtIndex(browseArray, i);
6140                        if (browseDict && DictionaryIsEnabled(browseDict))
6141                        {
6142                            CFStringRef name = CFDictionaryGetValue(browseDict, CFSTR("Domain"));
6143                            if (name)
6144                            {
6145                                if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) ||
6146                                    !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0])
6147                                    LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS browsing domain: %s", buf[0] ? buf : "(unknown)");
6148                                else
6149                                {
6150                                    debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browsing domain: %s", buf);
6151                                    AppendDNameListElem(&BrowseDomains, 0, &d);
6152                                }
6153                            }
6154                        }
6155                    }
6156                }
6157            }
6158            CFRelease(ddnsdict);
6159        }
6160
6161        if (RegDomains)
6162        {
6163            CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac);
6164            if (btmm)
6165            {
6166                CFIndex size = CFDictionaryGetCount(btmm);
6167                const void *key[size];
6168                const void *val[size];
6169                CFDictionaryGetKeysAndValues(btmm, key, val);
6170                for (i = 0; i < size; i++)
6171                {
6172                    LogInfo("BackToMyMac %d", i);
6173                    if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8))
6174                        LogMsg("Can't read BackToMyMac %d key %s", i, buf);
6175                    else
6176                    {
6177                        mDNSu32 uid = atoi(buf);
6178                        if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8))
6179                            LogMsg("Can't read BackToMyMac %d val %s", i, buf);
6180                        else if (MakeDomainNameFromDNSNameString(&d, buf) && d.c[0])
6181                        {
6182                            LogInfo("BackToMyMac %d %d %##s", i, uid, d.c);
6183                            AppendDNameListElem(&RegDomains, uid, &d);
6184                        }
6185                    }
6186                }
6187                CFRelease(btmm);
6188            }
6189        }
6190        CFRelease(store);
6191    }
6192}
6193
6194// Returns mDNSfalse, if it does not set the configuration i.e., if the DNS configuration did not change
6195mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn,
6196    DNameListElem **RegDomains, DNameListElem **BrowseDomains, mDNSBool ackConfig)
6197{
6198    MD5_CTX sdc;    // search domain context
6199    static mDNSu16 resolverGroupID = 0;
6200
6201    // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed
6202    if (fqdn) fqdn->c[0]      = 0;
6203    if (RegDomains   ) *RegDomains     = NULL;
6204    if (BrowseDomains) *BrowseDomains  = NULL;
6205
6206    LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s",
6207            setservers    ? " setservers"    : "",
6208            setsearch     ? " setsearch"     : "",
6209            fqdn          ? " fqdn"          : "",
6210            RegDomains    ? " RegDomains"    : "",
6211            BrowseDomains ? " BrowseDomains" : "");
6212
6213    if (setsearch) MD5_Init(&sdc);
6214
6215    // Add the inferred address-based configuration discovery domains
6216    // (should really be in core code I think, not platform-specific)
6217    if (setsearch)
6218    {
6219        struct ifaddrs *ifa = mDNSNULL;
6220        struct sockaddr_in saddr;
6221        mDNSPlatformMemZero(&saddr, sizeof(saddr));
6222        saddr.sin_len = sizeof(saddr);
6223        saddr.sin_family = AF_INET;
6224        saddr.sin_port = 0;
6225        saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4;
6226
6227        // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation
6228        if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa =  myGetIfAddrs(1);
6229
6230        while (ifa)
6231        {
6232            mDNSAddr a, n;
6233            char buf[64];
6234
6235            if (ifa->ifa_addr->sa_family == AF_INET &&
6236                ifa->ifa_netmask                    &&
6237                !(ifa->ifa_flags & IFF_LOOPBACK)    &&
6238                !SetupAddr(&a, ifa->ifa_addr)       &&
6239                !mDNSv4AddressIsLinkLocal(&a.ip.v4)  )
6240            {
6241                // 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
6242                // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
6243                ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family;     // Make sure ifa_netmask->sa_family is set correctly
6244                SetupAddr(&n, ifa->ifa_netmask);
6245                // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
6246                mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3],
6247                              a.ip.v4.b[2] & n.ip.v4.b[2],
6248                              a.ip.v4.b[1] & n.ip.v4.b[1],
6249                              a.ip.v4.b[0] & n.ip.v4.b[0]);
6250                UpdateSearchDomainHash(m, &sdc, buf, NULL);
6251                mDNS_AddSearchDomain_CString(buf, mDNSNULL);
6252            }
6253            ifa = ifa->ifa_next;
6254        }
6255    }
6256
6257#ifndef MDNS_NO_DNSINFO
6258    if (setservers || setsearch)
6259    {
6260        dns_config_t *config = dns_configuration_copy();
6261        if (!config)
6262        {
6263            // When running on 10.3 (build 7xxx) and earlier, we don't expect dns_configuration_copy() to succeed
6264            // On 10.4, calls to dns_configuration_copy() early in the boot process often fail.
6265            // Apparently this is expected behaviour -- "not a bug".
6266            // Accordingly, we suppress syslog messages for the first three minutes after boot.
6267            // If we are still getting failures after three minutes, then we log them.
6268            if ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180))
6269                LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL");
6270        }
6271        else
6272        {
6273            LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d, generation %llu", config->n_resolver, config->generation);
6274            if (m->p->LastConfigGeneration == config->generation)
6275            {
6276                LogInfo("mDNSPlatformSetDNSConfig: generation number %llu same, not processing", config->generation);
6277                dns_configuration_free(config);
6278                SetupDDNSDomains(fqdn, RegDomains, BrowseDomains);
6279                return mDNSfalse;
6280            }
6281#if APPLE_OSX_mDNSResponder
6282            SetupActiveDirectoryDomain(config);
6283#endif
6284
6285            // With scoped DNS, we don't want to answer a non-scoped question using a scoped cache entry
6286            // and vice-versa. As we compare resolverGroupID for matching cache entry with question, we need
6287            // to make sure that they don't match. We ensure this by always bumping up resolverGroupID between
6288            // the two calls to ConfigResolvers DNSServers for scoped and non-scoped can never have the
6289            // same resolverGroupID.
6290            //
6291            // All non-scoped resolvers use the same resolverGroupID i.e, we treat them all equally.
6292            ConfigResolvers(m, config, kScopeNone, setsearch, setservers, &sdc, ++resolverGroupID);
6293            resolverGroupID += config->n_resolver;
6294
6295            ConfigResolvers(m, config, kScopeInterfaceID, setsearch, setservers, &sdc, resolverGroupID);
6296            resolverGroupID += config->n_scoped_resolver;
6297
6298            ConfigResolvers(m, config, kScopeServiceID, setsearch, setservers, &sdc, resolverGroupID);
6299
6300            // Acking provides a hint that we processed this current configuration and
6301            // we will use that from now on, assuming we don't get another one immediately
6302            // after we return from here.
6303            if (ackConfig)
6304            {
6305                // Note: We have to set the generation number here when we are acking.
6306                // For every DNS configuration change, we do the following:
6307                //
6308                // 1) Copy dns configuration, handle search domains change
6309                // 2) Copy dns configuration, handle dns server change
6310                //
6311                // If we update the generation number at step (1), we won't process the
6312                // DNS servers the second time because generation number would be the same.
6313                // As we ack only when we process dns servers, we set the generation number
6314                // during acking.
6315                m->p->LastConfigGeneration = config->generation;
6316                LogInfo("mDNSPlatformSetDNSConfig: Acking configuration setservers %d, setsearch %d", setservers, setsearch);
6317                AckConfigd(m, config);
6318            }
6319            dns_configuration_free(config);
6320            if (setsearch) FinalizeSearchDomainHash(m, &sdc);
6321            setservers = mDNSfalse;  // Done these now -- no need to fetch the same data from SCDynamicStore
6322            setsearch  = mDNSfalse;
6323        }
6324    }
6325#endif // MDNS_NO_DNSINFO
6326    SetupDDNSDomains(fqdn, RegDomains, BrowseDomains);
6327    return mDNStrue;
6328}
6329
6330
6331mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *r)
6332{
6333    char buf[256];
6334    (void)m; // Unused
6335
6336    SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformGetPrimaryInterface"), NULL, NULL);
6337    if (!store)
6338        LogMsg("mDNSPlatformGetPrimaryInterface: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
6339    else
6340    {
6341        CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_IPv4);
6342        if (dict)
6343        {
6344            r->type  = mDNSAddrType_IPv4;
6345            r->ip.v4 = zerov4Addr;
6346            CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetIPv4Router);
6347            if (string)
6348            {
6349                if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8))
6350                    LogMsg("Could not convert router to CString");
6351                else
6352                {
6353                    struct sockaddr_in saddr;
6354                    saddr.sin_len = sizeof(saddr);
6355                    saddr.sin_family = AF_INET;
6356                    saddr.sin_port = 0;
6357                    inet_aton(buf, &saddr.sin_addr);
6358
6359                    *(in_addr_t *)&r->ip.v4 = saddr.sin_addr.s_addr;
6360                }
6361            }
6362
6363            string = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface);
6364            if (string)
6365            {
6366                mDNSBool HavePrimaryGlobalv6 = mDNSfalse;  // does the primary interface have a global v6 address?
6367                struct ifaddrs *ifa = myGetIfAddrs(1);
6368
6369                *v4 = *v6 = zeroAddr;
6370
6371                if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) { LogMsg("Could not convert router to CString"); goto exit; }
6372
6373                // find primary interface in list
6374                while (ifa && (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4) || !HavePrimaryGlobalv6))
6375                {
6376                    mDNSAddr tmp6 = zeroAddr;
6377                    if (!strcmp(buf, ifa->ifa_name))
6378                    {
6379                        if (ifa->ifa_addr->sa_family == AF_INET)
6380                        {
6381                            if (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4)) SetupAddr(v4, ifa->ifa_addr);
6382                        }
6383                        else if (ifa->ifa_addr->sa_family == AF_INET6)
6384                        {
6385                            SetupAddr(&tmp6, ifa->ifa_addr);
6386                            if (tmp6.ip.v6.b[0] >> 5 == 1)   // global prefix: 001
6387                            { HavePrimaryGlobalv6 = mDNStrue; *v6 = tmp6; }
6388                        }
6389                    }
6390                    else
6391                    {
6392                        // We'll take a V6 address from the non-primary interface if the primary interface doesn't have a global V6 address
6393                        if (!HavePrimaryGlobalv6 && ifa->ifa_addr->sa_family == AF_INET6 && !v6->ip.v6.b[0])
6394                        {
6395                            SetupAddr(&tmp6, ifa->ifa_addr);
6396                            if (tmp6.ip.v6.b[0] >> 5 == 1) *v6 = tmp6;
6397                        }
6398                    }
6399                    ifa = ifa->ifa_next;
6400                }
6401
6402                // Note that while we advertise v6, we still require v4 (possibly NAT'd, but not link-local) because we must use
6403                // V4 to communicate w/ our DNS server
6404            }
6405
6406exit:
6407            CFRelease(dict);
6408        }
6409        CFRelease(store);
6410    }
6411    return mStatus_NoError;
6412}
6413
6414mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status)
6415{
6416    LogInfo("mDNSPlatformDynDNSHostNameStatusChanged %d %##s", status, dname->c);
6417    char uname[MAX_ESCAPED_DOMAIN_NAME];    // Max legal C-string name, including terminating NUL
6418    ConvertDomainNameToCString(dname, uname);
6419
6420    char *p = uname;
6421    while (*p)
6422    {
6423        *p = tolower(*p);
6424        if (!(*(p+1)) && *p == '.') *p = 0; // if last character, strip trailing dot
6425        p++;
6426    }
6427
6428    // We need to make a CFDictionary called "State:/Network/DynamicDNS" containing (at present) a single entity.
6429    // That single entity is a CFDictionary with name "HostNames".
6430    // The "HostNames" CFDictionary contains a set of name/value pairs, where the each name is the FQDN
6431    // in question, and the corresponding value is a CFDictionary giving the state for that FQDN.
6432    // (At present we only support a single FQDN, so this dictionary holds just a single name/value pair.)
6433    // The CFDictionary for each FQDN holds (at present) a single name/value pair,
6434    // where the name is "Status" and the value is a CFNumber giving an errror code (with zero meaning success).
6435
6436    const CFStringRef StateKeys [1] = { CFSTR("HostNames") };
6437    const CFStringRef HostKeys  [1] = { CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8) };
6438    const CFStringRef StatusKeys[1] = { CFSTR("Status") };
6439    if (!HostKeys[0]) LogMsg("SetDDNSNameStatus: CFStringCreateWithCString(%s) failed", uname);
6440    else
6441    {
6442        const CFNumberRef StatusVals[1] = { CFNumberCreate(NULL, kCFNumberSInt32Type, &status) };
6443        if (!StatusVals[0]) LogMsg("SetDDNSNameStatus: CFNumberCreate(%d) failed", status);
6444        else
6445        {
6446            const CFDictionaryRef HostVals[1] = { CFDictionaryCreate(NULL, (void*)StatusKeys, (void*)StatusVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) };
6447            if (HostVals[0])
6448            {
6449                const CFDictionaryRef StateVals[1] = { CFDictionaryCreate(NULL, (void*)HostKeys, (void*)HostVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) };
6450                if (StateVals[0])
6451                {
6452                    CFDictionaryRef StateDict = CFDictionaryCreate(NULL, (void*)StateKeys, (void*)StateVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
6453                    if (StateDict)
6454                    {
6455                        mDNSDynamicStoreSetConfig(kmDNSDynamicConfig, mDNSNULL, StateDict);
6456                        CFRelease(StateDict);
6457                    }
6458                    CFRelease(StateVals[0]);
6459                }
6460                CFRelease(HostVals[0]);
6461            }
6462            CFRelease(StatusVals[0]);
6463        }
6464        CFRelease(HostKeys[0]);
6465    }
6466}
6467
6468#if APPLE_OSX_mDNSResponder
6469#if !NO_AWACS
6470
6471// checks whether a domain is present in Setup:/Network/BackToMyMac. Just because there is a key in the
6472// keychain for a domain, it does not become a valid BTMM domain. If things get inconsistent, this will
6473// help catch it
6474mDNSlocal mDNSBool IsBTMMDomain(domainname *d)
6475{
6476    SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:IsBTMMDomain"), NULL, NULL);
6477    if (!store)
6478    {
6479        LogMsg("IsBTMMDomain: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
6480        return mDNSfalse;
6481    }
6482    CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac);
6483    if (btmm)
6484    {
6485        CFIndex size = CFDictionaryGetCount(btmm);
6486        char buf[MAX_ESCAPED_DOMAIN_NAME];  // Max legal C-string name, including terminating NUL
6487        const void *key[size];
6488        const void *val[size];
6489        domainname dom;
6490        int i;
6491        CFDictionaryGetKeysAndValues(btmm, key, val);
6492        for (i = 0; i < size; i++)
6493        {
6494            LogInfo("BackToMyMac %d", i);
6495            if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8))
6496                LogMsg("IsBTMMDomain: ERROR!! Can't read BackToMyMac %d key %s", i, buf);
6497            else
6498            {
6499                mDNSu32 uid = atoi(buf);
6500                if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8))
6501                    LogMsg("IsBTMMDomain: Can't read BackToMyMac %d val %s", i, buf);
6502                else if (MakeDomainNameFromDNSNameString(&dom, buf) && dom.c[0])
6503                {
6504                    if (SameDomainName(&dom, d))
6505                    {
6506                        LogInfo("IsBTMMDomain: Domain %##s is a btmm domain, uid %u", d->c, uid);
6507                        CFRelease(btmm);
6508                        CFRelease(store);
6509                        return mDNStrue;
6510                    }
6511                }
6512            }
6513        }
6514        CFRelease(btmm);
6515    }
6516    CFRelease(store);
6517    LogInfo("IsBTMMDomain: Domain %##s not a btmm domain", d->c);
6518    return mDNSfalse;
6519}
6520
6521// Appends data to the buffer
6522mDNSlocal int AddOneItem(char *buf, int bufsz, char *data, int *currlen)
6523{
6524    int len;
6525
6526    len = strlcpy(buf + *currlen, data, bufsz - *currlen);
6527    if (len >= (bufsz - *currlen))
6528    {
6529        // if we have exceeded the space in buf, it has already been NULL terminated
6530        // and we have nothing more to do. Set currlen to the last byte so that the caller
6531        // knows to do the right thing
6532        LogMsg("AddOneItem: Exceeded the max buffer size currlen %d, len %d", *currlen, len);
6533        *currlen = bufsz - 1;
6534        return -1;
6535    }
6536    else { (*currlen) += len; }
6537
6538    buf[*currlen] = ',';
6539    if (*currlen >= bufsz)
6540    {
6541        LogMsg("AddOneItem: ERROR!! How can currlen be %d", *currlen);
6542        *currlen = bufsz - 1;
6543        buf[*currlen] = 0;
6544        return -1;
6545    }
6546    // if we have filled up the buffer exactly, then there is no more work to do
6547    if (*currlen == bufsz - 1) { buf[*currlen] = 0; return -1; }
6548    (*currlen)++;
6549    return *currlen;
6550}
6551
6552// If we have at least one BTMM domain, then trigger the connection to the relay. If we have no
6553// BTMM domains, then bring down the connection to the relay.
6554mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m)
6555{
6556    DomainAuthInfo *BTMMDomain = mDNSNULL;
6557    DomainAuthInfo *FoundInList;
6558    static mDNSBool AWACSDConnected = mDNSfalse;
6559    char AllUsers[1024];    // maximum size of mach message
6560    char AllPass[1024];     // maximum size of mach message
6561    char username[MAX_DOMAIN_LABEL + 1];
6562    int currulen = 0;
6563    int currplen = 0;
6564
6565    // if a domain is being deleted, we want to send a disconnect. If we send a disconnect now,
6566    // we may not be able to send the dns queries over the relay connection which may be needed
6567    // for sending the deregistrations. Hence, we need to delay sending the disconnect. But we
6568    // need to make sure that we send the disconnect before attempting the next connect as the
6569    // awacs connections are redirected based on usernames.
6570    //
6571    // For now we send a disconnect immediately. When we start sending dns queries over the relay
6572    // connection, we will need to fix this.
6573
6574    for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
6575        if (!FoundInList->deltime && FoundInList->AutoTunnel && IsBTMMDomain(&FoundInList->domain))
6576        {
6577            // We need the passwd from the first domain.
6578            BTMMDomain = FoundInList;
6579            ConvertDomainLabelToCString_unescaped((domainlabel *)BTMMDomain->domain.c, username);
6580            LogInfo("UpdateBTMMRelayConnection: user %s for domain %##s", username, BTMMDomain->domain.c);
6581            if (AddOneItem(AllUsers, sizeof(AllUsers), username, &currulen) == -1) break;
6582            if (AddOneItem(AllPass, sizeof(AllPass), BTMMDomain->b64keydata, &currplen) == -1) break;
6583        }
6584
6585    if (BTMMDomain)
6586    {
6587        // In the normal case (where we neither exceed the buffer size nor write bytes that
6588        // fit exactly into the buffer), currulen/currplen should be a different size than
6589        // (AllUsers - 1) / (AllPass - 1). In that case, we need to override the "," with a NULL byte.
6590
6591        if (currulen != (int)(sizeof(AllUsers) - 1)) AllUsers[currulen - 1] = 0;
6592        if (currplen != (int)(sizeof(AllPass) - 1)) AllPass[currplen - 1] = 0;
6593
6594        LogInfo("UpdateBTMMRelayConnection: AWS_Connect for user %s", AllUsers);
6595        AWACS_Connect(AllUsers, AllPass, "hello.connectivity.me.com");
6596        AWACSDConnected = mDNStrue;
6597    }
6598    else
6599    {
6600        // Disconnect only if we connected previously
6601        if (AWACSDConnected)
6602        {
6603            LogInfo("UpdateBTMMRelayConnection: AWS_Disconnect");
6604            AWACS_Disconnect();
6605            AWACSDConnected = mDNSfalse;
6606        }
6607        else LogInfo("UpdateBTMMRelayConnection: Not calling AWS_Disconnect");
6608    }
6609}
6610#else
6611mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m)
6612{
6613    (void) m; // Unused
6614    LogInfo("UpdateBTMMRelayConnection: AWACS connection not started, no AWACS library");
6615}
6616#endif // ! NO_AWACS
6617
6618mDNSlocal void ProcessConndConfigChanges(mDNS *const m);
6619
6620#endif // APPLE_OSX_mDNSResponder
6621
6622// MUST be called holding the lock
6623mDNSexport void SetDomainSecrets(mDNS *m)
6624{
6625#ifdef NO_SECURITYFRAMEWORK
6626        (void) m;
6627    LogMsg("Note: SetDomainSecrets: no keychain support");
6628#else
6629    mDNSBool haveAutoTunnels = mDNSfalse;
6630
6631    LogInfo("SetDomainSecrets");
6632
6633    // Rather than immediately deleting all keys now, we mark them for deletion in ten seconds.
6634    // In the case where the user simultaneously removes their DDNS host name and the key
6635    // for it, this gives mDNSResponder ten seconds to gracefully delete the name from the
6636    // server before it loses access to the necessary key. Otherwise, we'd leave orphaned
6637    // address records behind that we no longer have permission to delete.
6638    DomainAuthInfo *ptr;
6639    for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
6640        ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10);
6641
6642#if APPLE_OSX_mDNSResponder
6643    {
6644        // Mark all TunnelClients for deletion
6645        ClientTunnel *client;
6646        for (client = m->TunnelClients; client; client = client->next)
6647        {
6648            LogInfo("SetDomainSecrets: tunnel to %##s marked for deletion", client->dstname.c);
6649            client->MarkedForDeletion = mDNStrue;
6650        }
6651    }
6652#endif // APPLE_OSX_mDNSResponder
6653
6654    // String Array used to write list of private domains to Dynamic Store
6655    CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
6656    if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; }
6657    CFIndex i;
6658    CFDataRef data = NULL;
6659    const int itemsPerEntry = 4; // domain name, key name, key value, Name value
6660    CFArrayRef secrets = NULL;
6661    int err = mDNSKeychainGetSecrets(&secrets);
6662    if (err || !secrets)
6663        LogMsg("SetDomainSecrets: mDNSKeychainGetSecrets failed error %d CFArrayRef %p", err, secrets);
6664    else
6665    {
6666        CFIndex ArrayCount = CFArrayGetCount(secrets);
6667        // Iterate through the secrets
6668        for (i = 0; i < ArrayCount; ++i)
6669        {
6670            mDNSBool AutoTunnel;
6671            int j, offset;
6672            CFArrayRef entry = CFArrayGetValueAtIndex(secrets, i);
6673            if (CFArrayGetTypeID() != CFGetTypeID(entry) || itemsPerEntry != CFArrayGetCount(entry))
6674            { LogMsg("SetDomainSecrets: malformed entry %d, itemsPerEntry %d", i, itemsPerEntry); continue; }
6675            for (j = 0; j < CFArrayGetCount(entry); ++j)
6676                if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j)))
6677                { LogMsg("SetDomainSecrets: malformed entry item %d", j); continue; }
6678
6679            // The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness
6680
6681            // Max legal domainname as C-string, including space for btmmprefix and terminating NUL
6682            // Get DNS domain this key is for (kmDNSKcWhere)
6683            char stringbuf[MAX_ESCAPED_DOMAIN_NAME + sizeof(btmmprefix)];
6684            data = CFArrayGetValueAtIndex(entry, kmDNSKcWhere);
6685            if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
6686            { LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; }
6687            CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
6688            stringbuf[CFDataGetLength(data)] = '\0';
6689
6690            AutoTunnel = mDNSfalse;
6691            offset = 0;
6692            if (!strncmp(stringbuf, dnsprefix, strlen(dnsprefix)))
6693                offset = strlen(dnsprefix);
6694            else if (!strncmp(stringbuf, btmmprefix, strlen(btmmprefix)))
6695            {
6696                AutoTunnel = mDNStrue;
6697                offset = strlen(btmmprefix);
6698            }
6699            domainname domain;
6700            if (!MakeDomainNameFromDNSNameString(&domain, stringbuf + offset)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; }
6701
6702            // Get key name (kmDNSKcAccount)
6703            data = CFArrayGetValueAtIndex(entry, kmDNSKcAccount);
6704            if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
6705            { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; }
6706            CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf);
6707            stringbuf[CFDataGetLength(data)] = '\0';
6708
6709            domainname keyname;
6710            if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; }
6711
6712            // Get key data (kmDNSKcKey)
6713            data = CFArrayGetValueAtIndex(entry, kmDNSKcKey);
6714            if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
6715            {
6716                LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data));
6717                continue;
6718            }
6719            CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
6720            stringbuf[CFDataGetLength(data)] = '\0';    // mDNS_SetSecretForDomain requires NULL-terminated C string for key
6721
6722            // Get the Name of the keychain entry (kmDNSKcName) host or host:port
6723            // The hostname also has the port number and ":". It should take a maximum of 6 bytes.
6724            char hostbuf[MAX_ESCAPED_DOMAIN_NAME + 6];  // Max legal domainname as C-string, including terminating NUL
6725            data = CFArrayGetValueAtIndex(entry, kmDNSKcName);
6726            if (CFDataGetLength(data) >= (int)sizeof(hostbuf))
6727            {
6728                LogMsg("SetDomainSecrets: host:port data too long: %d", CFDataGetLength(data));
6729                continue;
6730            }
6731            CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)hostbuf);
6732            hostbuf[CFDataGetLength(data)] = '\0';
6733
6734            domainname hostname;
6735            mDNSIPPort port;
6736            char *hptr;
6737            hptr = strchr(hostbuf, ':');
6738
6739            port.NotAnInteger = 0;
6740            if (hptr)
6741            {
6742                mDNSu8 *p;
6743                mDNSu16 val = 0;
6744
6745                *hptr++ = '\0';
6746                while(hptr && *hptr != 0)
6747                {
6748                    if (*hptr < '0' || *hptr > '9')
6749                    { LogMsg("SetDomainSecrets: Malformed Port number %d, val %d", *hptr, val); val = 0; break;}
6750                    val = val * 10 + *hptr - '0';
6751                    hptr++;
6752                }
6753                if (!val) continue;
6754                p = (mDNSu8 *)&val;
6755                port.NotAnInteger = p[0] << 8 | p[1];
6756            }
6757            // The hostbuf is of the format dsid@hostname:port. We don't care about the dsid.
6758            hptr = strchr(hostbuf, '@');
6759            if (hptr)
6760                hptr++;
6761            else
6762                hptr = hostbuf;
6763            if (!MakeDomainNameFromDNSNameString(&hostname, hptr)) { LogMsg("SetDomainSecrets: bad host name %s", hptr); continue; }
6764
6765            DomainAuthInfo *FoundInList;
6766            for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
6767                if (SameDomainName(&FoundInList->domain, &domain)) break;
6768
6769#if APPLE_OSX_mDNSResponder
6770            if (FoundInList)
6771            {
6772                // If any client tunnel destination is in this domain, set deletion flag to false
6773                ClientTunnel *client;
6774                for (client = m->TunnelClients; client; client = client->next)
6775                    if (FoundInList == GetAuthInfoForName_internal(m, &client->dstname))
6776                    {
6777                        LogInfo("SetDomainSecrets: tunnel to %##s no longer marked for deletion", client->dstname.c);
6778                        client->MarkedForDeletion = mDNSfalse;
6779                    }
6780            }
6781
6782#endif // APPLE_OSX_mDNSResponder
6783
6784            // Uncomment the line below to view the keys as they're read out of the system keychain
6785            // DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE!
6786            //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]));
6787            LogInfo("SetDomainSecrets: domain %##s keyname %##s hostname %##s port %d", &domain.c, &keyname.c, hostname.c, (port.b[0] << 8 | port.b[1]));
6788
6789            // If didn't find desired domain in the list, make a new entry
6790            ptr = FoundInList;
6791            if (FoundInList && FoundInList->AutoTunnel && haveAutoTunnels == mDNSfalse) haveAutoTunnels = mDNStrue;
6792            if (!FoundInList)
6793            {
6794                ptr = (DomainAuthInfo*)mallocL("DomainAuthInfo", sizeof(*ptr));
6795                if (!ptr) { LogMsg("SetDomainSecrets: No memory"); continue; }
6796            }
6797
6798            //LogInfo("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain);
6799
6800            // It is an AutoTunnel if the keychains tells us so (with btmm prefix) or if it is a TunnelModeDomain
6801            if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, &hostname, &port, AutoTunnel) == mStatus_BadParamErr)
6802            {
6803                if (!FoundInList) mDNSPlatformMemFree(ptr);     // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately
6804                continue;
6805            }
6806
6807            ConvertDomainNameToCString(&domain, stringbuf);
6808            CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8);
6809            if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); }
6810        }
6811        CFRelease(secrets);
6812    }
6813
6814    if (!privateDnsArray || !CFEqual(privateDnsArray, sa))
6815    {
6816        if (privateDnsArray)
6817            CFRelease(privateDnsArray);
6818
6819        privateDnsArray = sa;
6820        CFRetain(privateDnsArray);
6821        mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, privateDnsArray);
6822    }
6823    CFRelease(sa);
6824
6825#if APPLE_OSX_mDNSResponder
6826    {
6827        // clean up ClientTunnels
6828        ClientTunnel **pp = &m->TunnelClients;
6829        while (*pp)
6830        {
6831            if ((*pp)->MarkedForDeletion)
6832            {
6833                ClientTunnel *cur = *pp;
6834                LogInfo("SetDomainSecrets: removing client %p %##s from list", cur, cur->dstname.c);
6835                if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q);
6836                AutoTunnelSetKeys(cur, mDNSfalse);
6837                *pp = cur->next;
6838                freeL("ClientTunnel", cur);
6839            }
6840            else
6841                pp = &(*pp)->next;
6842        }
6843
6844        mDNSBool needAutoTunnelNAT = mDNSfalse;
6845        DomainAuthInfo *info;
6846        for (info = m->AuthInfoList; info; info = info->next)
6847        {
6848            if (info->AutoTunnel)
6849            {
6850                UpdateAutoTunnelDeviceInfoRecord(m, info);
6851                UpdateAutoTunnelHostRecord(m, info);
6852                UpdateAutoTunnelServiceRecords(m, info);
6853                UpdateAutoTunnel6Record(m, info);
6854                if (info->deltime)
6855                {
6856                    if (info->AutoTunnelServiceStarted) info->AutoTunnelServiceStarted = mDNSfalse;
6857                }
6858                else if (info->AutoTunnelServiceStarted)
6859                    needAutoTunnelNAT = true;
6860
6861	            UpdateAutoTunnelDomainStatus(m, info);
6862            }
6863        }
6864
6865        // If the AutoTunnel NAT-T is no longer needed (& is currently running), stop it
6866        if (!needAutoTunnelNAT && m->AutoTunnelNAT.clientContext)
6867        {
6868            // stop the NAT operation, reset port, cleanup state
6869            mDNS_StopNATOperation_internal(m, &m->AutoTunnelNAT);
6870            m->AutoTunnelNAT.ExternalAddress = zerov4Addr;
6871            m->AutoTunnelNAT.NewAddress      = zerov4Addr;
6872            m->AutoTunnelNAT.ExternalPort    = zeroIPPort;
6873            m->AutoTunnelNAT.RequestedPort   = zeroIPPort;
6874            m->AutoTunnelNAT.Lifetime        = 0;
6875            m->AutoTunnelNAT.Result          = mStatus_NoError;
6876            m->AutoTunnelNAT.clientContext   = mDNSNULL;
6877        }
6878
6879        UpdateAnonymousRacoonConfig(m);     // Determine whether we need racoon to accept incoming connections
6880        ProcessConndConfigChanges(m);       // Update AutoTunnelInnerAddress values and default ipsec policies as necessary
6881    }
6882#endif // APPLE_OSX_mDNSResponder
6883
6884    CheckSuppressUnusableQuestions(m);
6885
6886#endif /* NO_SECURITYFRAMEWORK */
6887}
6888
6889mDNSlocal void SetLocalDomains(void)
6890{
6891    CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
6892    if (!sa) { LogMsg("SetLocalDomains: CFArrayCreateMutable failed"); return; }
6893
6894    CFArrayAppendValue(sa, CFSTR("local"));
6895    CFArrayAppendValue(sa, CFSTR("254.169.in-addr.arpa"));
6896    CFArrayAppendValue(sa, CFSTR("8.e.f.ip6.arpa"));
6897    CFArrayAppendValue(sa, CFSTR("9.e.f.ip6.arpa"));
6898    CFArrayAppendValue(sa, CFSTR("a.e.f.ip6.arpa"));
6899    CFArrayAppendValue(sa, CFSTR("b.e.f.ip6.arpa"));
6900
6901    mDNSDynamicStoreSetConfig(kmDNSMulticastConfig, mDNSNULL, sa);
6902    CFRelease(sa);
6903}
6904
6905mDNSlocal void GetCurrentPMSetting(const CFStringRef name, mDNSs32 *val)
6906{
6907#if USE_IOPMCOPYACTIVEPMPREFERENCES
6908    CFTypeRef blob = NULL;
6909    CFStringRef str = NULL;
6910    CFDictionaryRef odict = NULL;
6911    CFDictionaryRef idict = NULL;
6912    CFNumberRef number = NULL;
6913
6914    blob = IOPSCopyPowerSourcesInfo();
6915    if (!blob) { LogMsg("GetCurrentPMSetting: IOPSCopyPowerSourcesInfo failed!"); goto end; }
6916
6917    odict = IOPMCopyActivePMPreferences();
6918    if (!odict) { LogMsg("GetCurrentPMSetting: IOPMCopyActivePMPreferences failed!"); goto end; }
6919
6920    str = IOPSGetProvidingPowerSourceType(blob);
6921    if (!str) { LogMsg("GetCurrentPMSetting: IOPSGetProvidingPowerSourceType failed!"); goto end; }
6922
6923    idict = CFDictionaryGetValue(odict, str);
6924    if (!idict)
6925    {
6926        char buf[256];
6927        if (!CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
6928        LogMsg("GetCurrentPMSetting: CFDictionaryGetValue (%s) failed!", buf);
6929        goto end;
6930    }
6931
6932    number = CFDictionaryGetValue(idict, name);
6933    if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val))
6934        *val = 0;
6935end:
6936    if (blob) CFRelease(blob);
6937    if (odict) CFRelease(odict);
6938
6939#else
6940
6941    SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetCurrentPMSetting"), NULL, NULL);
6942    if (!store) LogMsg("GetCurrentPMSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
6943    else
6944    {
6945        CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings);
6946        if (!dict) LogSPS("GetCurrentPMSetting: Could not get IOPM CurrentSettings dict");
6947        else
6948        {
6949            CFNumberRef number = CFDictionaryGetValue(dict, name);
6950            if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val))
6951                *val = 0;
6952            CFRelease(dict);
6953        }
6954        CFRelease(store);
6955    }
6956
6957#endif
6958}
6959
6960#if APPLE_OSX_mDNSResponder
6961
6962static CFMutableDictionaryRef spsStatusDict = NULL;
6963static const CFStringRef kMetricRef = CFSTR("Metric");
6964
6965mDNSlocal void SPSStatusPutNumber(CFMutableDictionaryRef dict, const mDNSu8* const ptr, CFStringRef key)
6966{
6967    mDNSu8 tmp = (ptr[0] - '0') * 10 + ptr[1] - '0';
6968    CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt8Type, &tmp);
6969    if (!num)
6970        LogMsg("SPSStatusPutNumber: Could not create CFNumber");
6971    else
6972    {
6973        CFDictionarySetValue(dict, key, num);
6974        CFRelease(num);
6975    }
6976}
6977
6978mDNSlocal CFMutableDictionaryRef SPSCreateDict(const mDNSu8* const ptr)
6979{
6980    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
6981    if (!dict) { LogMsg("SPSCreateDict: Could not create CFDictionary dict"); return dict; }
6982
6983    char buffer[1024];
6984    buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", ptr) - 1] = 0;
6985    CFStringRef spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
6986    if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname full"); CFRelease(dict); return NULL; }
6987    CFDictionarySetValue(dict, CFSTR("FullName"), spsname);
6988    CFRelease(spsname);
6989
6990    if (ptr[0] >=  2) SPSStatusPutNumber(dict, ptr + 1, CFSTR("Type"));
6991    if (ptr[0] >=  5) SPSStatusPutNumber(dict, ptr + 4, CFSTR("Portability"));
6992    if (ptr[0] >=  8) SPSStatusPutNumber(dict, ptr + 7, CFSTR("MarginalPower"));
6993    if (ptr[0] >= 11) SPSStatusPutNumber(dict, ptr +10, CFSTR("TotalPower"));
6994
6995    mDNSu32 tmp = SPSMetric(ptr);
6996    CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tmp);
6997    if (!num)
6998        LogMsg("SPSCreateDict: Could not create CFNumber");
6999    else
7000    {
7001        CFDictionarySetValue(dict, kMetricRef, num);
7002        CFRelease(num);
7003    }
7004
7005    if (ptr[0] >= 12)
7006    {
7007        memcpy(buffer, ptr + 13, ptr[0] - 12);
7008        buffer[ptr[0] - 12] = 0;
7009        spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
7010        if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname"); CFRelease(dict); return NULL; }
7011        else
7012        {
7013            CFDictionarySetValue(dict, CFSTR("PrettyName"), spsname);
7014            CFRelease(spsname);
7015        }
7016    }
7017
7018    return dict;
7019}
7020
7021mDNSlocal CFComparisonResult CompareSPSEntries(const void *val1, const void *val2, void *context)
7022{
7023    (void)context;
7024    return CFNumberCompare((CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val1, kMetricRef),
7025                           (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val2, kMetricRef),
7026                           NULL);
7027}
7028
7029mDNSlocal void UpdateSPSStatus(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
7030{
7031    NetworkInterfaceInfo* info = (NetworkInterfaceInfo*)question->QuestionContext;
7032    debugf("UpdateSPSStatus: %s %##s %s %s", info->ifname, question->qname.c, AddRecord ? "Add" : "Rmv", answer ? RRDisplayString(m, answer) : "<null>");
7033
7034    mDNS_Lock(m);
7035    mDNS_UpdateAllowSleep(m);
7036    mDNS_Unlock(m);
7037
7038    if (answer && SPSMetric(answer->rdata->u.name.c) > 999999) return;  // Ignore instances with invalid names
7039
7040    if (!spsStatusDict)
7041    {
7042        spsStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
7043        if (!spsStatusDict) { LogMsg("UpdateSPSStatus: Could not create CFDictionary spsStatusDict"); return; }
7044    }
7045
7046    CFStringRef ifname = CFStringCreateWithCString(NULL, info->ifname, kCFStringEncodingUTF8);
7047    if (!ifname) { LogMsg("UpdateSPSStatus: Could not create CFString ifname"); return; }
7048
7049    CFMutableArrayRef array = NULL;
7050
7051    if (!CFDictionaryGetValueIfPresent(spsStatusDict, ifname, (const void**) &array))
7052    {
7053        array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
7054        if (!array) { LogMsg("UpdateSPSStatus: Could not create CFMutableArray"); CFRelease(ifname); return; }
7055        CFDictionarySetValue(spsStatusDict, ifname, array);
7056        CFRelease(array); // let go of our reference, now that the dict has one
7057    }
7058    else
7059    if (!array) { LogMsg("UpdateSPSStatus: Could not get CFMutableArray for %s", info->ifname); CFRelease(ifname); return; }
7060
7061    if (!answer) // special call that means the question has been stopped (because the interface is going away)
7062        CFArrayRemoveAllValues(array);
7063    else
7064    {
7065        CFMutableDictionaryRef dict = SPSCreateDict(answer->rdata->u.name.c);
7066        if (!dict) { CFRelease(ifname); return; }
7067
7068        if (AddRecord)
7069        {
7070            if (!CFArrayContainsValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict))
7071            {
7072                int i=0;
7073                for (i=0; i<CFArrayGetCount(array); i++)
7074                    if (CompareSPSEntries(CFArrayGetValueAtIndex(array, i), dict, NULL) != kCFCompareLessThan)
7075                        break;
7076                CFArrayInsertValueAtIndex(array, i, dict);
7077            }
7078            else LogMsg("UpdateSPSStatus: %s array already contains %##s", info->ifname, answer->rdata->u.name.c);
7079        }
7080        else
7081        {
7082            CFIndex i = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict);
7083            if (i != -1) CFArrayRemoveValueAtIndex(array, i);
7084            else LogMsg("UpdateSPSStatus: %s array does not contain %##s", info->ifname, answer->rdata->u.name.c);
7085        }
7086
7087        CFRelease(dict);
7088    }
7089
7090    if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, info->ifname, array);
7091
7092    CFRelease(ifname);
7093}
7094
7095mDNSlocal mDNSs32 GetSystemSleepTimerSetting(void)
7096{
7097    mDNSs32 val = -1;
7098    SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetSystemSleepTimerSetting"), NULL, NULL);
7099    if (!store)
7100        LogMsg("GetSystemSleepTimerSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
7101    else
7102    {
7103        CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings);
7104        if (dict)
7105        {
7106            CFNumberRef number = CFDictionaryGetValue(dict, CFSTR("System Sleep Timer"));
7107            if (number) CFNumberGetValue(number, kCFNumberSInt32Type, &val);
7108            CFRelease(dict);
7109        }
7110        CFRelease(store);
7111    }
7112    return val;
7113}
7114
7115mDNSlocal void SetSPS(mDNS *const m)
7116{
7117
7118    // If we ever want to know InternetSharing status in the future, use DNSXEnableProxy()
7119    mDNSu8 sps = (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0;
7120
7121    // For devices that are not running NAT, but are set to never sleep, we may choose to act
7122    // as a Sleep Proxy, but only for non-portable Macs (Portability > 35 means nominal weight < 3kg)
7123    //if (sps > mDNSSleepProxyMetric_PrimarySoftware && SPMetricPortability > 35) sps = 0;
7124
7125    // If we decide to let laptops act as Sleep Proxy, we should do it only when running on AC power, not on battery
7126
7127    // For devices that are unable to sleep at all to save power, or save 1W or less by sleeping,
7128    // it makes sense for them to offer low-priority Sleep Proxy service on the network.
7129    // We rate such a device as metric 70 ("Incidentally Available Hardware")
7130    if (SPMetricMarginalPower <= 60 && !sps) sps = mDNSSleepProxyMetric_IncidentalHardware;
7131
7132    // If the launchd plist specifies an explicit value for the Intent Metric, then use that instead of the
7133    // computed value (currently 40 "Primary Network Infrastructure Software" or 80 "Incidentally Available Software")
7134    if (sps && OfferSleepProxyService && OfferSleepProxyService < 100) sps = OfferSleepProxyService;
7135
7136#ifdef NO_APPLETV_SLEEP_PROXY_ON_WIFI
7137    // AppleTVs are not reliable sleep proxy servers on WiFi. Do not offer to be a BSP if the WiFi interface is active.
7138    if (IsAppleTV())
7139    {
7140        NetworkInterfaceInfo *intf  = mDNSNULL;
7141        mDNSEthAddr           bssid = zeroEthAddr;
7142        for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
7143        {
7144            bssid = GetBSSID(intf->ifname);
7145            if (!mDNSSameEthAddress(&bssid, &zeroEthAddr))
7146            {
7147                LogMsg("SetSPS: AppleTV on WiFi - not advertising BSP services");
7148                sps = 0;
7149                break;
7150            }
7151        }
7152    }
7153#endif  //  NO_APPLETV_SLEEP_PROXY_ON_WIFI
7154
7155    mDNSCoreBeSleepProxyServer(m, sps, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower, SPMetricFeatures);
7156}
7157
7158// The definitions below should eventually come from some externally-supplied header file.
7159// However, since these definitions can't really be changed without breaking binary compatibility,
7160// they should never change, so in practice it should not be a big problem to have them defined here.
7161
7162enum
7163{                               // commands from the daemon to the driver
7164    cmd_mDNSOffloadRR = 21,     // give the mdns update buffer to the driver
7165};
7166
7167typedef union { void *ptr; mDNSOpaque64 sixtyfourbits; } FatPtr;
7168
7169typedef struct
7170{                                       // cmd_mDNSOffloadRR structure
7171    uint32_t command;                 // set to OffloadRR
7172    uint32_t rrBufferSize;            // number of bytes of RR records
7173    uint32_t numUDPPorts;             // number of SRV UDP ports
7174    uint32_t numTCPPorts;             // number of SRV TCP ports
7175    uint32_t numRRRecords;            // number of RR records
7176    uint32_t compression;             // rrRecords - compression is base for compressed strings
7177    FatPtr rrRecords;                 // address of array of pointers to the rr records
7178    FatPtr udpPorts;                  // address of udp port list (SRV)
7179    FatPtr tcpPorts;                  // address of tcp port list (SRV)
7180} mDNSOffloadCmd;
7181
7182#include <IOKit/IOKitLib.h>
7183#include <dns_util.h>
7184
7185mDNSlocal mDNSu16 GetPortArray(mDNS *const m, int trans, mDNSIPPort *portarray)
7186{
7187    const domainlabel *const tp = (trans == mDNSTransport_UDP) ? (const domainlabel *)"\x4_udp" : (const domainlabel *)"\x4_tcp";
7188    int count = 0;
7189    AuthRecord *rr;
7190    for (rr = m->ResourceRecords; rr; rr=rr->next)
7191        if (rr->resrec.rrtype == kDNSType_SRV && SameDomainLabel(ThirdLabel(rr->resrec.name)->c, tp->c))
7192        {
7193            if (portarray) portarray[count] = rr->resrec.rdata->u.srv.port;
7194            count++;
7195        }
7196
7197    // If Back to My Mac is on, also wake for packets to the IPSEC UDP port (4500)
7198    if (trans == mDNSTransport_UDP && m->AutoTunnelNAT.clientContext)
7199    {
7200        LogSPS("GetPortArray Back to My Mac at %d", count);
7201        if (portarray) portarray[count] = IPSECPort;
7202        count++;
7203    }
7204    return(count);
7205}
7206
7207#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
7208mDNSlocal mDNSBool SupportsTCPKeepAlive()
7209{
7210    IOReturn  ret      = kIOReturnSuccess;
7211    CFTypeRef obj      = NULL;
7212    mDNSBool  supports = mDNSfalse;
7213
7214    ret = IOPlatformCopyFeatureActive(CFSTR("TCPKeepAliveDuringSleep"), &obj);
7215    if ((kIOReturnSuccess == ret) && (obj != NULL))
7216    {
7217        supports = (obj ==  kCFBooleanTrue)? mDNStrue : mDNSfalse;
7218        CFRelease(obj);
7219    }
7220    LogSPS("%s: The hardware %s TCP Keep Alive", __func__, (supports ? "supports" : "does not support"));
7221    return supports;
7222}
7223
7224mDNSlocal mDNSBool OnBattery(void)
7225{
7226    CFTypeRef powerInfo = IOPSCopyPowerSourcesInfo();
7227    CFTypeRef powerSrc  = IOPSGetProvidingPowerSourceType(powerInfo);
7228    mDNSBool  result    = mDNSfalse;
7229
7230    if (powerInfo != NULL)
7231    {
7232        result = CFEqual(CFSTR(kIOPSBatteryPowerValue), powerSrc);
7233        CFRelease(powerInfo);
7234    }
7235    LogSPS("%s: The system is on %s", __func__, (result)? "Battery" : "AC Power");
7236    return result;
7237}
7238
7239#endif // !TARGET_OS_EMBEDDED
7240
7241#define TfrRecordToNIC(RR) \
7242    ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name))))
7243
7244mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes, NetworkInterfaceInfo *const intf, mDNSBool TCPKAOnly, mDNSBool supportsTCPKA)
7245{
7246    *numbytes = 0;
7247    int count = 0;
7248
7249    AuthRecord *rr;
7250
7251    for (rr = m->ResourceRecords; rr; rr=rr->next)
7252    {
7253        if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering)
7254        {
7255#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
7256            mDNSBool   isKeepAliveRecord = mDNS_KeepaliveRecord(&rr->resrec);
7257            // Skip over all other records if we are registering TCP KeepAlive records only
7258            // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive.
7259            if ((TCPKAOnly && !isKeepAliveRecord) || (isKeepAliveRecord && !supportsTCPKA))
7260                continue;
7261
7262            // Update the record before calculating the number of bytes required
7263            // We offload the TCP Keepalive record even if the update fails. When the driver gets the record, it will
7264            // attempt to update the record again.
7265            if (isKeepAliveRecord && (UpdateKeepaliveRData(m, rr, intf, mDNSfalse, mDNSNULL) != mStatus_NoError))
7266                LogSPS("CountProxyRecords: Failed to update keepalive record - %s", ARDisplayString(m, rr));
7267#else
7268            (void) TCPKAOnly;     // unused
7269            (void) supportsTCPKA; // unused
7270            (void) intf;          // unused
7271#endif // APPLE_OSX_mDNSResponder
7272            if (TfrRecordToNIC(rr))
7273            {
7274                *numbytes += DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate;
7275                LogSPS("CountProxyRecords: %3d size %5d total %5d %s",
7276                       count, DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate, *numbytes, ARDisplayString(m,rr));
7277                count++;
7278            }
7279        }
7280    }
7281    return(count);
7282}
7283
7284mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *const numbytes, FatPtr *const records, mDNSBool TCPKAOnly, mDNSBool supportsTCPKA)
7285{
7286    mDNSu8 *p = msg->data;
7287    const mDNSu8 *const limit = p + *numbytes;
7288    InitializeDNSMessage(&msg->h, zeroID, zeroID);
7289
7290    int count = 0;
7291    AuthRecord *rr;
7292
7293    for (rr = m->ResourceRecords; rr; rr=rr->next)
7294    {
7295        if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering)
7296        {
7297#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
7298            mDNSBool   isKeepAliveRecord = mDNS_KeepaliveRecord(&rr->resrec);
7299
7300            // Skip over all other records if we are registering TCP KeepAlive records only
7301            // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive
7302            if ((TCPKAOnly && !isKeepAliveRecord) || (isKeepAliveRecord && !supportsTCPKA))
7303                continue;
7304#else
7305            (void) TCPKAOnly;     // unused
7306            (void) supportsTCPKA; // unused
7307#endif // APPLE_OSX_mDNSResponder
7308
7309            if (TfrRecordToNIC(rr))
7310            {
7311                records[count].sixtyfourbits = zeroOpaque64;
7312                records[count].ptr = p;
7313                if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
7314                    rr->resrec.rrclass |= kDNSClass_UniqueRRSet;    // Temporarily set the 'unique' bit so PutResourceRecord will set it
7315                p = PutResourceRecordTTLWithLimit(msg, p, &msg->h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit);
7316                rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;       // Make sure to clear 'unique' bit back to normal state
7317                LogSPS("GetProxyRecords: %3d start %p end %p size %5d total %5d %s",
7318                       count, records[count].ptr, p, p - (mDNSu8 *)records[count].ptr, p - msg->data, ARDisplayString(m,rr));
7319                count++;
7320            }
7321        }
7322    }
7323    *numbytes = p - msg->data;
7324}
7325
7326// If compiling with old headers and libraries (pre 10.5) that don't include IOConnectCallStructMethod
7327// then we declare a dummy version here so that the code at least compiles
7328#ifndef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
7329static kern_return_t
7330IOConnectCallStructMethod(
7331    mach_port_t connection,         // In
7332    uint32_t selector,              // In
7333    const void  *inputStruct,       // In
7334    size_t inputStructCnt,          // In
7335    void        *outputStruct,      // Out
7336    size_t      *outputStructCnt)   // In/Out
7337{
7338    (void)connection;
7339    (void)selector;
7340    (void)inputStruct;
7341    (void)inputStructCnt;
7342    (void)outputStruct;
7343    (void)outputStructCnt;
7344    LogMsg("Compiled without IOConnectCallStructMethod");
7345    return(KERN_FAILURE);
7346}
7347#endif
7348
7349mDNSexport mDNSBool SupportsInNICProxy(NetworkInterfaceInfo *const intf)
7350{
7351    if(!UseInternalSleepProxy)
7352    {
7353        LogSPS("SupportsInNICProxy: Internal Sleep Proxy is disabled");
7354        return mDNSfalse;
7355    }
7356    return CheckInterfaceSupport(intf, mDNS_IOREG_KEY);
7357}
7358
7359mDNSexport mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf)  // Called with the lock held
7360{
7361    mStatus      result        = mStatus_UnknownErr;
7362    mDNSBool     TCPKAOnly     = mDNSfalse;
7363    mDNSBool     supportsTCPKA = mDNSfalse;
7364    mDNSBool     onbattery     = mDNSfalse;
7365    io_service_t service       = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, intf->ifname));
7366
7367#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
7368    onbattery = OnBattery();
7369    // Check if the interface supports TCP Keepalives and the system policy says it is ok to offload TCP Keepalive records
7370    supportsTCPKA = (InterfaceSupportsKeepAlive(intf) && SupportsTCPKeepAlive());
7371
7372    // Only TCP Keepalive records are to be offloaded if
7373    // - The system is on battery
7374    // - OR wake for network access is not set but powernap is enabled
7375    TCPKAOnly     = supportsTCPKA && ((m->SystemWakeOnLANEnabled == mDNS_WakeOnBattery) || onbattery);
7376#else
7377    (void) onbattery; // unused;
7378#endif
7379    if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", intf->ifname); return(mStatus_UnknownErr); }
7380
7381    io_name_t n1, n2;
7382    IOObjectGetClass(service, n1);
7383    io_object_t parent;
7384    kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
7385    if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", intf->ifname, n1, kr);
7386    else
7387    {
7388        IOObjectGetClass(parent, n2);
7389        LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", intf->ifname, n1, n2);
7390        const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, CFSTR(mDNS_IOREG_KEY), kCFAllocatorDefault, mDNSNULL);
7391        if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", intf->ifname, n1, n2);
7392        else
7393        {
7394            if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE)))
7395                LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s",
7396                       intf->ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE);
7397            else if (!UseInternalSleepProxy)
7398                LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", intf->ifname);
7399            else
7400            {
7401                io_connect_t conObj;
7402                kr = IOServiceOpen(parent, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE, &conObj);
7403                if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", intf->ifname, n1, n2, kr);
7404                else
7405                {
7406                    mDNSOffloadCmd cmd;
7407                    mDNSPlatformMemZero(&cmd, sizeof(cmd)); // When compiling 32-bit, make sure top 32 bits of 64-bit pointers get initialized to zero
7408                    cmd.command       = cmd_mDNSOffloadRR;
7409                    cmd.numUDPPorts   = GetPortArray(m, mDNSTransport_UDP, mDNSNULL);
7410                    cmd.numTCPPorts   = GetPortArray(m, mDNSTransport_TCP, mDNSNULL);
7411                    cmd.numRRRecords  = CountProxyRecords(m, &cmd.rrBufferSize, intf, TCPKAOnly, supportsTCPKA);
7412                    cmd.compression   = sizeof(DNSMessageHeader);
7413
7414                    DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize);
7415                    cmd.rrRecords.ptr = mallocL("mDNSOffloadCmd rrRecords", cmd.numRRRecords * sizeof(FatPtr));
7416                    cmd.udpPorts.ptr = mallocL("mDNSOffloadCmd udpPorts",  cmd.numUDPPorts  * sizeof(mDNSIPPort));
7417                    cmd.tcpPorts.ptr = mallocL("mDNSOffloadCmd tcpPorts",  cmd.numTCPPorts  * sizeof(mDNSIPPort));
7418
7419                    LogSPS("ActivateLocalProxy: msg %p %d RR %p %d, UDP %p %d, TCP %p %d",
7420                           msg, cmd.rrBufferSize,
7421                           cmd.rrRecords.ptr, cmd.numRRRecords,
7422                           cmd.udpPorts.ptr, cmd.numUDPPorts,
7423                           cmd.tcpPorts.ptr, cmd.numTCPPorts);
7424
7425                    if (!msg || !cmd.rrRecords.ptr || !cmd.udpPorts.ptr || !cmd.tcpPorts.ptr)
7426                        LogMsg("ActivateLocalProxy: Failed to allocate memory: msg %p %d RR %p %d, UDP %p %d, TCP %p %d",
7427                               msg, cmd.rrBufferSize,
7428                               cmd.rrRecords.ptr, cmd.numRRRecords,
7429                               cmd.udpPorts.ptr, cmd.numUDPPorts,
7430                               cmd.tcpPorts.ptr, cmd.numTCPPorts);
7431                    else
7432                    {
7433                        GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr, TCPKAOnly, supportsTCPKA);
7434                        GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr);
7435                        GetPortArray(m, mDNSTransport_TCP, cmd.tcpPorts.ptr);
7436                        char outputData[2];
7437                        size_t outputDataSize = sizeof(outputData);
7438                        kr = IOConnectCallStructMethod(conObj, 0, &cmd, sizeof(cmd), outputData, &outputDataSize);
7439                        LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", intf->ifname, n1, n2, kr);
7440                        if (kr == KERN_SUCCESS) result = mStatus_NoError;
7441                    }
7442
7443                    if (cmd.tcpPorts.ptr) freeL("mDNSOffloadCmd udpPorts",  cmd.tcpPorts.ptr);
7444                    if (cmd.udpPorts.ptr) freeL("mDNSOffloadCmd tcpPorts",  cmd.udpPorts.ptr);
7445                    if (cmd.rrRecords.ptr) freeL("mDNSOffloadCmd rrRecords", cmd.rrRecords.ptr);
7446                    if (msg) freeL("mDNSOffloadCmd msg",       msg);
7447                    IOServiceClose(conObj);
7448                }
7449            }
7450            CFRelease(ref);
7451        }
7452        IOObjectRelease(parent);
7453    }
7454    IOObjectRelease(service);
7455    return result;
7456}
7457
7458#endif // APPLE_OSX_mDNSResponder
7459
7460mDNSlocal mDNSu8 SystemWakeForNetworkAccess(void)
7461{
7462    mDNSs32 val = 0;
7463    mDNSu8  ret = (mDNSu8)mDNS_NoWake;
7464
7465    if (DisableSleepProxyClient)
7466    {
7467       LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option");
7468       return mDNSfalse;
7469    }
7470
7471    GetCurrentPMSetting(CFSTR("Wake On LAN"), &val);
7472
7473    ret = (mDNSu8)(val != 0) ? mDNS_WakeOnAC : mDNS_NoWake;
7474
7475#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
7476    // If we have TCP Keepalive support, system is capable of registering for TCP Keepalives.
7477    // Further policy decisions on whether to offload the records is handled during sleep processing.
7478    if ((ret == mDNS_NoWake) && SupportsTCPKeepAlive())
7479        ret = (mDNSu8)mDNS_WakeOnBattery;
7480#endif // APPLE_OSX_mDNSResponder
7481
7482    LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", ret);
7483    return ret;
7484}
7485
7486mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void)
7487{
7488    mDNSs32 val = 0;
7489    GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val);
7490    return val != 0 ? mDNStrue : mDNSfalse;
7491}
7492
7493#if APPLE_OSX_mDNSResponder
7494// When sleeping, we always ensure that the _autotunnel6 record (if connected to RR relay)
7495// gets deregistered, so that older peers are forced to connect over direct UDP instead of
7496// the RR relay.
7497//
7498// When sleeping w/o a successful AutoTunnel NAT Mapping, we ensure that all our BTMM
7499// service records are deregistered, so they do not appear in peers' Finder sidebars.
7500// We do this by checking for the (non-autotunnel) SRV records, as the PTR and TXT records
7501// depend on their associated SRV record and therefore will be deregistered together in a
7502// single update with the SRV record.
7503//
7504// Also, the per-zone _kerberos TXT record is always there, including while sleeping, so
7505// its presence shouldn't delay sleep.
7506//
7507// Note that the order of record deregistration is: first _autotunnel6 (if connected to RR
7508// relay) and host records get deregistered, then SRV (UpdateAllSrvRecords), PTR and TXT.
7509//
7510// Also note that returning false here will not delay sleep past the maximum of 10 seconds.
7511mDNSexport mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr)
7512{
7513    if (!AuthRecord_uDNS(rr)) return mDNStrue;
7514
7515    if ((rr->resrec.rrtype == kDNSType_AAAA) && SameDomainLabel(rr->namestorage.c, (const mDNSu8 *)"\x0c_autotunnel6"))
7516    {
7517        LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr));
7518        return mDNSfalse;
7519    }
7520
7521    if ((mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || m->AutoTunnelNAT.Result))
7522    {
7523        if (rr->resrec.rrtype == kDNSType_SRV && rr->state != regState_NoTarget && rr->zone
7524            && !SameDomainLabel(rr->namestorage.c, (const mDNSu8 *)"\x0b_autotunnel"))
7525        {
7526            DomainAuthInfo *info = GetAuthInfoForName_internal(m, rr->zone);
7527            if (info && info->AutoTunnel)
7528            {
7529                LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr));
7530                return mDNSfalse;
7531            }
7532        }
7533    }
7534
7535    return mDNStrue;
7536}
7537
7538// Caller must hold the lock
7539mDNSexport void RemoveAutoTunnel6Record(mDNS *const m)
7540{
7541    DomainAuthInfo *info;
7542    // Set the address to zero before calling UpdateAutoTunnel6Record, so that it will
7543    // deregister the record, and the MemFree callback won't re-register.
7544    m->AutoTunnelRelayAddr = zerov6Addr;
7545    for (info = m->AuthInfoList; info; info = info->next)
7546        if (info->AutoTunnel)
7547            UpdateAutoTunnel6Record(m, info);
7548}
7549
7550mDNSlocal mDNSBool IPv6AddressIsOnInterface(mDNSv6Addr ipv6Addr, char *ifname)
7551{
7552    struct ifaddrs  *ifa;
7553    struct ifaddrs  *ifaddrs;
7554    mDNSAddr addr;
7555
7556    if (if_nametoindex(ifname) == 0) {LogInfo("IPv6AddressIsOnInterface: Invalid name %s", ifname); return mDNSfalse;}
7557
7558    if (getifaddrs(&ifaddrs) < 0) {LogInfo("IPv6AddressIsOnInterface: getifaddrs failed"); return mDNSfalse;}
7559
7560    for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next)
7561    {
7562        if (strncmp(ifa->ifa_name, ifname, IFNAMSIZ) != 0)
7563            continue;
7564        if ((ifa->ifa_flags & IFF_UP) == 0 || !ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6)
7565            continue;
7566        if (SetupAddr(&addr, ifa->ifa_addr) != mStatus_NoError)
7567        {
7568            LogInfo("IPv6AddressIsOnInterface: SetupAddr error, continuing to the next address");
7569            continue;
7570        }
7571        if (mDNSSameIPv6Address(ipv6Addr, *(mDNSv6Addr*)&addr.ip.v6))
7572        {
7573            LogInfo("IPv6AddressIsOnInterface: found %.16a", &ipv6Addr);
7574            break;
7575        }
7576    }
7577    freeifaddrs(ifaddrs);
7578    return ifa != NULL;
7579}
7580
7581mDNSlocal mDNSv6Addr IPv6AddressFromString(char* buf)
7582{
7583    mDNSv6Addr retVal;
7584    struct addrinfo hints;
7585    struct addrinfo *res0;
7586
7587    memset(&hints, 0, sizeof(hints));
7588    hints.ai_family = AF_INET6;
7589    hints.ai_flags = AI_NUMERICHOST;
7590
7591    int err = getaddrinfo(buf, NULL, &hints, &res0);
7592    if (err)
7593        return zerov6Addr;
7594
7595    retVal = *(mDNSv6Addr*)&((struct sockaddr_in6*)res0->ai_addr)->sin6_addr;
7596
7597    freeaddrinfo(res0);
7598
7599    return retVal;
7600}
7601
7602mDNSlocal CFDictionaryRef CopyConnectivityBackToMyMacDict()
7603{
7604    SCDynamicStoreRef store = NULL;
7605    CFDictionaryRef connd = NULL;
7606    CFDictionaryRef BTMMDict = NULL;
7607
7608    store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:CopyConnectivityBackToMyMacDict"), NULL, NULL);
7609    if (!store)
7610    {
7611        LogMsg("CopyConnectivityBackToMyMacDict: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
7612        goto end;
7613    }
7614
7615    connd = SCDynamicStoreCopyValue(store, NetworkChangedKey_BTMMConnectivity);
7616    if (!connd)
7617    {
7618        LogInfo("CopyConnectivityBackToMyMacDict: SCDynamicStoreCopyValue failed: %s", SCErrorString(SCError()));
7619        goto end;
7620    }
7621
7622    BTMMDict = CFDictionaryGetValue(connd, CFSTR("BackToMyMac"));
7623    if (!BTMMDict)
7624    {
7625        LogInfo("CopyConnectivityBackToMyMacDict: CFDictionaryGetValue: No value for BackToMyMac");
7626        goto end;
7627    }
7628
7629    // Non-dictionary is treated as non-existent dictionary
7630    if (CFGetTypeID(BTMMDict) != CFDictionaryGetTypeID())
7631    {
7632        BTMMDict = NULL;
7633        LogMsg("CopyConnectivityBackToMyMacDict: BackToMyMac not a dictionary");
7634        goto end;
7635    }
7636
7637    CFRetain(BTMMDict);
7638
7639end:
7640    if (connd) CFRelease(connd);
7641    if (store) CFRelease(store);
7642
7643    return BTMMDict;
7644}
7645
7646#define MAX_IPV6_TEXTUAL 40
7647
7648mDNSlocal mDNSv6Addr ParseBackToMyMacAddr(CFDictionaryRef BTMMDict, CFStringRef ifKey, CFStringRef addrKey)
7649{
7650    mDNSv6Addr retVal = zerov6Addr;
7651    CFTypeRef string = NULL;
7652    char ifname[IFNAMSIZ];
7653    char address[MAX_IPV6_TEXTUAL];
7654
7655    if (!BTMMDict)
7656        return zerov6Addr;
7657
7658    if (!CFDictionaryGetValueIfPresent(BTMMDict, ifKey, &string))
7659    {
7660        LogInfo("ParseBackToMyMacAddr: interface key does not exist");
7661        return zerov6Addr;
7662    }
7663
7664    if (!CFStringGetCString(string, ifname, IFNAMSIZ, kCFStringEncodingUTF8))
7665    {
7666        LogMsg("ParseBackToMyMacAddr: Could not convert interface to CString");
7667        return zerov6Addr;
7668    }
7669
7670    if (!CFDictionaryGetValueIfPresent(BTMMDict, addrKey, &string))
7671    {
7672        LogMsg("ParseBackToMyMacAddr: address key does not exist, but interface key does");
7673        return zerov6Addr;
7674    }
7675
7676    if (!CFStringGetCString(string, address, sizeof(address), kCFStringEncodingUTF8))
7677    {
7678        LogMsg("ParseBackToMyMacAddr: Could not convert address to CString");
7679        return zerov6Addr;
7680    }
7681
7682    retVal = IPv6AddressFromString(address);
7683    LogInfo("ParseBackToMyMacAddr: %s (%s) %.16a", ifname, address, &retVal);
7684
7685    if (mDNSIPv6AddressIsZero(retVal))
7686        return zerov6Addr;
7687
7688    if (!IPv6AddressIsOnInterface(retVal, ifname))
7689    {
7690        LogMsg("ParseBackToMyMacAddr: %.16a is not on %s", &retVal, ifname);
7691        return zerov6Addr;
7692    }
7693
7694    return retVal;
7695}
7696
7697mDNSlocal CFDictionaryRef GetBackToMyMacZones(CFDictionaryRef BTMMDict)
7698{
7699    CFTypeRef zones = NULL;
7700
7701    if (!BTMMDict)
7702        return NULL;
7703
7704    if (!CFDictionaryGetValueIfPresent(BTMMDict, CFSTR("Zones"), &zones))
7705    {
7706        LogInfo("CopyBTMMZones: Zones key does not exist");
7707        return NULL;
7708    }
7709
7710    return zones;
7711}
7712
7713mDNSlocal mDNSv6Addr ParseBackToMyMacZone(CFDictionaryRef zones, DomainAuthInfo* info)
7714{
7715    mDNSv6Addr addr = zerov6Addr;
7716    char buffer[MAX_ESCAPED_DOMAIN_NAME];
7717    CFStringRef domain = NULL;
7718    CFTypeRef theZone = NULL;
7719
7720    if (!zones)
7721        return addr;
7722
7723    ConvertDomainNameToCString(&info->domain, buffer);
7724    domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
7725    if (!domain)
7726        return addr;
7727
7728    if (CFDictionaryGetValueIfPresent(zones, domain, &theZone))
7729        addr = ParseBackToMyMacAddr(theZone, CFSTR("Interface"), CFSTR("Address"));
7730
7731    CFRelease(domain);
7732
7733    return addr;
7734}
7735
7736mDNSlocal void SetupBackToMyMacInnerAddresses(mDNS *const m, CFDictionaryRef BTMMDict)
7737{
7738    DomainAuthInfo* info;
7739    CFDictionaryRef zones = GetBackToMyMacZones(BTMMDict);
7740    mDNSv6Addr newAddr;
7741
7742    for (info = m->AuthInfoList; info; info = info->next)
7743    {
7744        if (!info->AutoTunnel)
7745            continue;
7746
7747        newAddr = ParseBackToMyMacZone(zones, info);
7748
7749        if (mDNSSameIPv6Address(newAddr, info->AutoTunnelInnerAddress))
7750            continue;
7751
7752        info->AutoTunnelInnerAddress = newAddr;
7753        DeregisterAutoTunnelHostRecord(m, info);
7754        UpdateAutoTunnelHostRecord(m, info);
7755        UpdateAutoTunnelDomainStatus(m, info);
7756    }
7757}
7758
7759// MUST be called holding the lock
7760mDNSlocal void ProcessConndConfigChanges(mDNS *const m)
7761{
7762    CFDictionaryRef dict = CopyConnectivityBackToMyMacDict();
7763    if (!dict)
7764        LogInfo("ProcessConndConfigChanges: No BTMM dictionary");
7765    mDNSv6Addr relayAddr = ParseBackToMyMacAddr(dict, CFSTR("RelayInterface"), CFSTR("RelayAddress"));
7766
7767    LogInfo("ProcessConndConfigChanges: relay %.16a", &relayAddr);
7768
7769    SetupBackToMyMacInnerAddresses(m, dict);
7770
7771    if (dict) CFRelease(dict);
7772
7773    if (!mDNSSameIPv6Address(relayAddr, m->AutoTunnelRelayAddr))
7774    {
7775        m->AutoTunnelRelayAddr = relayAddr;
7776
7777        DomainAuthInfo* info;
7778        for (info = m->AuthInfoList; info; info = info->next)
7779            if (info->AutoTunnel)
7780            {
7781                DeregisterAutoTunnel6Record(m, info);
7782                UpdateAutoTunnel6Record(m, info);
7783                UpdateAutoTunnelDomainStatus(m, info);
7784            }
7785
7786        // Determine whether we need racoon to accept incoming connections
7787        UpdateAnonymousRacoonConfig(m);
7788    }
7789
7790    // If awacsd crashes or exits for some reason, restart it
7791    UpdateBTMMRelayConnection(m);
7792}
7793#endif /* APPLE_OSX_mDNSResponder */
7794
7795mDNSlocal mDNSBool IsAppleNetwork(mDNS *const m)
7796{
7797    DNSServer *s;
7798    // Determine if we're on AppleNW based on DNSServer having 17.x.y.z IPv4 addr
7799    for (s = m->DNSServers; s; s = s->next)
7800    {
7801        if (s->addr.ip.v4.b[0] == 17)
7802        {
7803            LogInfo("IsAppleNetwork: Found 17.x.y.z DNSServer concluding that we are on AppleNW: %##s %#a", s->domain.c, &s->addr);
7804            return mDNStrue;
7805        }
7806    }
7807    return mDNSfalse;
7808}
7809
7810mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m)
7811{
7812    LogInfo("***   Network Configuration Change   ***  (%d)%s",
7813            m->p->NetworkChanged ? mDNS_TimeNow(m) - m->p->NetworkChanged : 0,
7814            m->p->NetworkChanged ? "" : " (no scheduled configuration change)");
7815    m->p->NetworkChanged = 0;       // If we received a network change event and deferred processing, we're now dealing with it
7816    mDNSs32 utc = mDNSPlatformUTC();
7817    m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess();
7818    m->SystemSleepOnlyIfWakeOnLAN = SystemSleepOnlyIfWakeOnLAN();
7819    MarkAllInterfacesInactive(m, utc);
7820    UpdateInterfaceList(m, utc);
7821    ClearInactiveInterfaces(m, utc);
7822    SetupActiveInterfaces(m, utc);
7823
7824#if APPLE_OSX_mDNSResponder
7825
7826    mDNS_Lock(m);
7827    ProcessConndConfigChanges(m);
7828    mDNS_Unlock(m);
7829
7830    // Scan to find client tunnels whose questions have completed,
7831    // but whose local inner/outer addresses have changed since the tunnel was set up
7832    ClientTunnel *p;
7833    for (p = m->TunnelClients; p; p = p->next)
7834        if (p->q.ThisQInterval < 0)
7835        {
7836            DomainAuthInfo* info = GetAuthInfoForName(m, &p->dstname);
7837            if (!info)
7838            {
7839                LogMsg("mDNSMacOSXNetworkChanged: Could not get AuthInfo for %##s, removing tunnel keys", p->dstname.c);
7840                AutoTunnelSetKeys(p, mDNSfalse);
7841            }
7842            else
7843            {
7844                mDNSv6Addr inner = info->AutoTunnelInnerAddress;
7845
7846                if (!mDNSIPPortIsZero(p->rmt_outer_port))
7847                {
7848                    mDNSAddr tmpSrc = zeroAddr;
7849                    mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} };
7850                    tmpDst.ip.v4 = p->rmt_outer;
7851                    mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst);
7852                    if (!mDNSSameIPv6Address(p->loc_inner, inner) ||
7853                        !mDNSSameIPv4Address(p->loc_outer, tmpSrc.ip.v4))
7854                    {
7855                        AutoTunnelSetKeys(p, mDNSfalse);
7856                        p->loc_inner = inner;
7857                        p->loc_outer = tmpSrc.ip.v4;
7858                        AutoTunnelSetKeys(p, mDNStrue);
7859                    }
7860                }
7861                else
7862                {
7863                    if (!mDNSSameIPv6Address(p->loc_inner, inner) ||
7864                        !mDNSSameIPv6Address(p->loc_outer6, m->AutoTunnelRelayAddr))
7865                    {
7866                        AutoTunnelSetKeys(p, mDNSfalse);
7867                        p->loc_inner = inner;
7868                        p->loc_outer6 = m->AutoTunnelRelayAddr;
7869                        AutoTunnelSetKeys(p, mDNStrue);
7870                    }
7871                }
7872            }
7873        }
7874
7875    SetSPS(m);
7876
7877    NetworkInterfaceInfoOSX *i;
7878    for (i = m->p->InterfaceList; i; i = i->next)
7879    {
7880        if (!m->SPSSocket)      // Not being Sleep Proxy Server; close any open BPF fds
7881        {
7882            if (i->BPF_fd >= 0 && CountProxyTargets(m, i, mDNSNULL, mDNSNULL) == 0) CloseBPF(i);
7883        }
7884        else                                // else, we're Sleep Proxy Server; open BPF fds
7885        {
7886            if (i->Exists && i->Registered == i && i->ifinfo.McastTxRx && !(i->ifa_flags & IFF_LOOPBACK) && i->BPF_fd == -1)
7887            { LogSPS("%s requesting BPF", i->ifinfo.ifname); i->BPF_fd = -2; mDNSRequestBPF(); }
7888        }
7889    }
7890
7891#endif // APPLE_OSX_mDNSResponder
7892
7893    uDNS_SetupDNSConfig(m);
7894    mDNS_ConfigChanged(m);
7895
7896    if (IsAppleNetwork(m) != mDNS_McastTracingEnabled)
7897    {
7898        mDNS_McastTracingEnabled = mDNS_McastTracingEnabled ? mDNSfalse : mDNStrue;
7899        LogMsg("mDNSMacOSXNetworkChanged: Multicast Tracing %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled");
7900        UpdateDebugState();
7901    }
7902
7903}
7904
7905// Called with KQueueLock & mDNS lock
7906mDNSlocal void SetNetworkChanged(mDNS *const m, mDNSs32 delay)
7907{
7908    if (!m->p->NetworkChanged || m->p->NetworkChanged - NonZeroTime(m->timenow + delay) < 0)
7909    {
7910        m->p->NetworkChanged = NonZeroTime(m->timenow + delay);
7911        LogInfo("SetNetworkChanged: scheduling in %d msec", delay);
7912    }
7913}
7914
7915// Called with KQueueLock & mDNS lock
7916mDNSlocal void SetKeyChainTimer(mDNS *const m, mDNSs32 delay)
7917{
7918    // If it's not set or it needs to happen sooner than when it's currently set
7919    if (!m->p->KeyChainTimer || m->p->KeyChainTimer - NonZeroTime(m->timenow + delay) > 0)
7920    {
7921        m->p->KeyChainTimer = NonZeroTime(m->timenow + delay);
7922        LogInfo("SetKeyChainTimer: %d", delay);
7923    }
7924}
7925
7926// Copy the fourth slash-delimited element from either:
7927//   State:/Network/Interface/<bsdname>/IPv4
7928// or
7929//   Setup:/Network/Service/<servicename>/Interface
7930mDNSlocal CFStringRef CopyNameFromKey(CFStringRef key)
7931{
7932    CFArrayRef a;
7933    CFStringRef name = NULL;
7934
7935    a = CFStringCreateArrayBySeparatingStrings(NULL, key, CFSTR("/"));
7936    if (a && CFArrayGetCount(a) == 5) name = CFRetain(CFArrayGetValueAtIndex(a, 3));
7937    if (a != NULL) CFRelease(a);
7938
7939    return name;
7940}
7941
7942// Whether a key from a network change notification corresponds to
7943// an IP service that is explicitly configured for IPv4 Link Local
7944mDNSlocal mDNSBool ChangedKeysHaveIPv4LL(CFArrayRef inkeys)
7945{
7946    SCDynamicStoreRef store = NULL;
7947    CFDictionaryRef dict = NULL;
7948    CFMutableArrayRef a;
7949    const void **keys = NULL, **vals = NULL;
7950    CFStringRef pattern = NULL;
7951    int i, ic, j, jc;
7952    mDNSBool found = mDNSfalse;
7953
7954    jc = CFArrayGetCount(inkeys);
7955    if (!jc) goto done;
7956
7957    store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:ChangedKeysHaveIPv4LL"), NULL, NULL);
7958    if (store == NULL) goto done;
7959
7960    a = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
7961    if (a == NULL) goto done;
7962
7963    // Setup:/Network/Service/[^/]+/Interface
7964    pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetInterface);
7965    if (pattern == NULL) goto done;
7966    CFArrayAppendValue(a, pattern);
7967    CFRelease(pattern);
7968
7969    // Setup:/Network/Service/[^/]+/IPv4
7970    pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetIPv4);
7971    if (pattern == NULL) goto done;
7972    CFArrayAppendValue(a, pattern);
7973    CFRelease(pattern);
7974
7975    dict = SCDynamicStoreCopyMultiple(store, NULL, a);
7976    CFRelease(a);
7977
7978    if (!dict)
7979    {
7980        LogMsg("ChangedKeysHaveIPv4LL: Empty dictionary");
7981        goto done;
7982    }
7983
7984    ic = CFDictionaryGetCount(dict);
7985    vals = mDNSPlatformMemAllocate(sizeof (void *) * ic);
7986    keys = mDNSPlatformMemAllocate(sizeof (void *) * ic);
7987    CFDictionaryGetKeysAndValues(dict, keys, vals);
7988
7989    for (j = 0; j < jc && !found; j++)
7990    {
7991        CFStringRef key = CFArrayGetValueAtIndex(inkeys, j);
7992        CFStringRef ifname = NULL;
7993
7994        char buf[256];
7995
7996        // It would be nice to use a regex here
7997        if (!CFStringHasPrefix(key, CFSTR("State:/Network/Interface/")) || !CFStringHasSuffix(key, kSCEntNetIPv4)) continue;
7998
7999        if ((ifname = CopyNameFromKey(key)) == NULL) continue;
8000        if (mDNS_LoggingEnabled)
8001        {
8002            if (!CFStringGetCString(ifname, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
8003            LogInfo("ChangedKeysHaveIPv4LL: potential ifname %s", buf);
8004        }
8005
8006        for (i = 0; i < ic; i++)
8007        {
8008            CFDictionaryRef ipv4dict;
8009            CFStringRef name;
8010            CFStringRef serviceid;
8011            CFStringRef configmethod;
8012
8013            if (!CFStringHasSuffix(keys[i], kSCEntNetInterface)) continue;
8014
8015            if (CFDictionaryGetTypeID() != CFGetTypeID(vals[i])) continue;
8016
8017            if ((name = CFDictionaryGetValue(vals[i], kSCPropNetInterfaceDeviceName)) == NULL) continue;
8018
8019            if (!CFEqual(ifname, name)) continue;
8020
8021            if ((serviceid = CopyNameFromKey(keys[i])) == NULL) continue;
8022            if (mDNS_LoggingEnabled)
8023            {
8024                if (!CFStringGetCString(serviceid, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
8025                LogInfo("ChangedKeysHaveIPv4LL: found serviceid %s", buf);
8026            }
8027
8028            pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceid, kSCEntNetIPv4);
8029            CFRelease(serviceid);
8030            if (pattern == NULL) continue;
8031
8032            ipv4dict = CFDictionaryGetValue(dict, pattern);
8033            CFRelease(pattern);
8034            if (!ipv4dict || CFDictionaryGetTypeID() != CFGetTypeID(ipv4dict)) continue;
8035
8036            configmethod = CFDictionaryGetValue(ipv4dict, kSCPropNetIPv4ConfigMethod);
8037            if (!configmethod) continue;
8038
8039            if (mDNS_LoggingEnabled)
8040            {
8041                if (!CFStringGetCString(configmethod, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
8042                LogInfo("ChangedKeysHaveIPv4LL: configmethod %s", buf);
8043            }
8044
8045            if (CFEqual(configmethod, kSCValNetIPv4ConfigMethodLinkLocal)) { found = mDNStrue; break; }
8046        }
8047
8048        CFRelease(ifname);
8049    }
8050
8051done:
8052    if (vals != NULL) mDNSPlatformMemFree(vals);
8053    if (keys != NULL) mDNSPlatformMemFree(keys);
8054    if (dict != NULL) CFRelease(dict);
8055    if (store != NULL) CFRelease(store);
8056
8057    return found;
8058}
8059
8060mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
8061{
8062    (void)store;        // Parameter not used
8063    mDNSBool changeNow = mDNSfalse;
8064    mDNS *const m = (mDNS *const)context;
8065    KQueueLock(m);
8066    mDNS_Lock(m);
8067
8068    mDNSs32 delay = mDNSPlatformOneSecond * 2;              // Start off assuming a two-second delay
8069
8070    int c = CFArrayGetCount(changedKeys);                   // Count changes
8071    CFRange range = { 0, c };
8072    int c1 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Hostnames   ) != 0);
8073    int c2 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0);
8074    int c3 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS  ) != 0);
8075    int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS         ) != 0);
8076    if (c && c - c1 - c2 - c3 - c4 == 0)
8077        delay = mDNSPlatformOneSecond/10;  // If these were the only changes, shorten delay
8078
8079    // Do immediate network changed processing for "p2p*" interfaces and
8080    // for interfaces with the IFEF_DIRECTLINK flag set.
8081    {
8082        CFArrayRef  labels;
8083        CFIndex     n;
8084        for (int i = 0; i < c; i++)
8085        {
8086            CFStringRef key = CFArrayGetValueAtIndex(changedKeys, i);
8087
8088            // Only look at keys with prefix "State:/Network/Interface/"
8089            if (!CFStringHasPrefix(key, NetworkChangedKey_StateInterfacePrefix))
8090                continue;
8091
8092            // And suffix "IPv6" or "IPv4".
8093            if (!CFStringHasSuffix(key, kSCEntNetIPv6) && !CFStringHasSuffix(key, kSCEntNetIPv4))
8094                continue;
8095
8096            labels = CFStringCreateArrayBySeparatingStrings(NULL, key, CFSTR("/"));
8097            if (labels == NULL)
8098                break;
8099            n = CFArrayGetCount(labels);
8100
8101            // Interface changes will have keys of the form:
8102            //     State:/Network/Interface/<interfaceName>/IPv6
8103            // Thus five '/' seperated fields, the 4th one being the <interfaceName> string.
8104            if (n == 5)
8105            {
8106                char buf[256];
8107
8108                // The 4th label (index = 3) should be the interface name.
8109                if (CFStringGetCString(CFArrayGetValueAtIndex(labels, 3), buf, sizeof(buf), kCFStringEncodingUTF8)
8110                    && (strstr(buf, "p2p") || (getExtendedFlags(buf) & IFEF_DIRECTLINK)))
8111                {
8112                    LogInfo("NetworkChanged: interface %s, not delaying network change", buf);
8113                    changeNow = mDNStrue;
8114                    CFRelease(labels);
8115                    break;
8116                }
8117            }
8118            CFRelease(labels);
8119        }
8120    }
8121
8122    mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac);
8123    if (btmmChanged) delay = 0;
8124
8125    if (mDNS_LoggingEnabled)
8126    {
8127        int i;
8128        for (i=0; i<c; i++)
8129        {
8130            char buf[256];
8131            if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
8132            LogInfo("***   NetworkChanged SC key: %s", buf);
8133        }
8134        LogInfo("***   NetworkChanged   *** %d change%s %s%s%s%sdelay %d",
8135                c, c>1 ? "s" : "",
8136                c1 ? "(Local Hostname) " : "",
8137                c2 ? "(Computer Name) "  : "",
8138                c3 ? "(DynamicDNS) "     : "",
8139                c4 ? "(DNS) "            : "",
8140                changeNow ? 0 : delay);
8141    }
8142
8143    if (!changeNow)
8144        SetNetworkChanged(m, delay);
8145
8146    // Other software might pick up these changes to register or browse in WAB or BTMM domains,
8147    // so in order for secure updates to be made to the server, make sure to read the keychain and
8148    // setup the DomainAuthInfo before handing the network change.
8149    // If we don't, then we will first try to register services in the clear, then later setup the
8150    // DomainAuthInfo, which is incorrect.
8151    if (c3 || btmmChanged)
8152        SetKeyChainTimer(m, delay);
8153
8154    mDNS_Unlock(m);
8155
8156    // If DNS settings changed, immediately force a reconfig (esp. cache flush)
8157    // Similarly, if an interface changed that is explicitly IPv4 link local, immediately force a reconfig
8158    if (c4 || ChangedKeysHaveIPv4LL(changedKeys) || changeNow) mDNSMacOSXNetworkChanged(m);
8159
8160    KQueueUnlock(m, "NetworkChanged");
8161}
8162
8163#if APPLE_OSX_mDNSResponder
8164mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *context)
8165{
8166    (void)context;
8167    char buf[IFNAMSIZ];
8168
8169    CFStringRef ifnameStr = (CFStringRef)key;
8170    CFArrayRef array = (CFArrayRef)value;
8171    if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8))
8172        buf[0] = 0;
8173
8174    LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array));
8175    mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value);
8176}
8177#endif
8178
8179mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info)
8180{
8181    mDNS *const m = (mDNS *const)info;
8182    (void)store;
8183
8184    LogInfo("DynamicStoreReconnected: Reconnected");
8185
8186    // State:/Network/MulticastDNS
8187    SetLocalDomains();
8188
8189    // State:/Network/DynamicDNS
8190    if (m->FQDN.c[0])
8191        mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1);
8192
8193    // Note: PrivateDNS and BackToMyMac are automatically populated when configd is restarted
8194    // as we receive network change notifications and thus not necessary. But we leave it here
8195    // so that if things are done differently in the future, this code still works.
8196
8197    // State:/Network/PrivateDNS
8198    if (privateDnsArray)
8199        mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, privateDnsArray);
8200
8201#if APPLE_OSX_mDNSResponder
8202    mDNS_Lock(m);
8203    // State:/Network/BackToMyMac
8204    UpdateAutoTunnelDomainStatuses(m);
8205    mDNS_Unlock(m);
8206
8207    // State:/Network/Interface/en0/SleepProxyServers
8208    if (spsStatusDict)
8209        CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL);
8210#endif
8211}
8212
8213mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m)
8214{
8215    mStatus err = -1;
8216    SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL };
8217    SCDynamicStoreRef store    = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:WatchForNetworkChanges"), NetworkChanged, &context);
8218    CFMutableArrayRef keys     = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
8219    CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
8220    CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
8221    CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
8222
8223    if (!store) { LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); goto error; }
8224    if (!keys || !pattern1 || !pattern2 || !patterns) goto error;
8225
8226    CFArrayAppendValue(keys, NetworkChangedKey_IPv4);
8227    CFArrayAppendValue(keys, NetworkChangedKey_IPv6);
8228    CFArrayAppendValue(keys, NetworkChangedKey_Hostnames);
8229    CFArrayAppendValue(keys, NetworkChangedKey_Computername);
8230    CFArrayAppendValue(keys, NetworkChangedKey_DNS);
8231    CFArrayAppendValue(keys, NetworkChangedKey_DynamicDNS);
8232    CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac);
8233    CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); // should remove as part of <rdar://problem/6751656>
8234    CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity);
8235    CFArrayAppendValue(patterns, pattern1);
8236    CFArrayAppendValue(patterns, pattern2);
8237    CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort"));
8238    if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns))
8239    { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; }
8240
8241#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
8242    if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue()))
8243    { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; }
8244#else
8245    m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
8246    if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; }
8247    CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode);
8248#endif
8249    SCDynamicStoreSetDisconnectCallBack(store, DynamicStoreReconnected);
8250    m->p->Store = store;
8251    err = 0;
8252    goto exit;
8253
8254error:
8255    if (store) CFRelease(store);
8256
8257exit:
8258    if (patterns) CFRelease(patterns);
8259    if (pattern2) CFRelease(pattern2);
8260    if (pattern1) CFRelease(pattern1);
8261    if (keys) CFRelease(keys);
8262
8263    return(err);
8264}
8265
8266#if 0 // <rdar://problem/6751656>
8267mDNSlocal void PMChanged(void *context)
8268{
8269    mDNS *const m = (mDNS *const)context;
8270
8271    KQueueLock(m);
8272    mDNS_Lock(m);
8273
8274    LogSPS("PMChanged");
8275
8276    SetNetworkChanged(m, mDNSPlatformOneSecond * 2);
8277
8278    mDNS_Unlock(m);
8279    KQueueUnlock(m, "PMChanged");
8280}
8281
8282mDNSlocal mStatus WatchForPMChanges(mDNS *const m)
8283{
8284    m->p->PMRLS = IOPMPrefsNotificationCreateRunLoopSource(PMChanged, m);
8285    if (!m->p->PMRLS) { LogMsg("IOPMPrefsNotificationCreateRunLoopSource failed!"); return mStatus_UnknownErr; }
8286
8287    CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode);
8288
8289    return mStatus_NoError;
8290}
8291#endif
8292
8293#if !TARGET_OS_EMBEDDED     // don't setup packet filter rules on embedded
8294
8295mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname, const ResourceRecord *const excludeRecord)
8296{
8297    AuthRecord  *rr;
8298    pfArray_t portArray;
8299    pfArray_t protocolArray;
8300    uint32_t count = 0;
8301
8302    for (rr = m->ResourceRecords; rr; rr=rr->next)
8303    {
8304        if ((rr->resrec.rrtype == kDNSServiceType_SRV)
8305            && ((rr->ARType == AuthRecordAnyIncludeP2P) || (rr->ARType == AuthRecordAnyIncludeAWDLandP2P)))
8306        {
8307            const mDNSu8    *p;
8308
8309            if (count >= PFPortArraySize)
8310            {
8311                LogMsg("mDNSSetPacketFilterRules: %d service limit, skipping %s", PFPortArraySize, ARDisplayString(m, rr));
8312                continue;
8313            }
8314
8315            if (excludeRecord && IdenticalResourceRecord(&rr->resrec, excludeRecord))
8316            {
8317                LogInfo("mDNSSetPacketFilterRules: record being removed, skipping %s", ARDisplayString(m, rr));
8318                continue;
8319            }
8320
8321            LogInfo("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr));
8322
8323            portArray[count] = rr->resrec.rdata->u.srv.port.NotAnInteger;
8324
8325            // Assume <Service Instance>.<App Protocol>.<Transport Protocol>.<Name>
8326            p = rr->resrec.name->c;
8327
8328            // Skip to App Protocol
8329            if (p[0]) p += 1 + p[0];
8330
8331            // Skip to Transport Protocol
8332            if (p[0]) p += 1 + p[0];
8333
8334            if      (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocolArray[count] = IPPROTO_TCP;
8335            else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocolArray[count] = IPPROTO_UDP;
8336            else
8337            {
8338                LogMsg("mDNSSetPacketFilterRules: could not determine transport protocol of service");
8339                LogMsg("mDNSSetPacketFilterRules: %s", ARDisplayString(m, rr));
8340                return;
8341            }
8342            count++;
8343        }
8344    }
8345    mDNSPacketFilterControl(PF_SET_RULES, ifname, count, portArray, protocolArray);
8346}
8347
8348// If the p2p interface already exists, update the Bonjour packet filter rules for it.
8349mDNSexport void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord)
8350{
8351    mDNS *const m = &mDNSStorage;
8352
8353    NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
8354    while (intf)
8355    {
8356        if (strncmp(intf->ifname, "p2p", 3) == 0)
8357        {
8358            LogInfo("mDNSInitPacketFilter: Setting rules for ifname %s", intf->ifname);
8359            mDNSSetPacketFilterRules(m, intf->ifname, excludeRecord);
8360            break;
8361        }
8362        intf = GetFirstActiveInterface(intf->next);
8363    }
8364}
8365
8366#else // !TARGET_OS_EMBEDDED
8367
8368// Currently no packet filter setup required on embedded platforms.
8369mDNSexport void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord)
8370{
8371    (void) excludeRecord; // unused
8372}
8373
8374#endif // !TARGET_OS_EMBEDDED
8375
8376// Handle AWDL KEV_DL_MASTER_ELECTED event by restarting queries and advertisements
8377// marked to include the AWDL interface.
8378mDNSlocal void newMasterElected(mDNS *const m, struct net_event_data * ptr)
8379{
8380    char        ifname[IFNAMSIZ];
8381    mDNSu32     interfaceIndex;
8382    DNSQuestion *q;
8383    AuthRecord  *rr;
8384    NetworkInterfaceInfoOSX *infoOSX;
8385    mDNSInterfaceID InterfaceID;
8386
8387    snprintf(ifname, IFNAMSIZ, "%s%d", ptr->if_name, ptr->if_unit);
8388    interfaceIndex  = if_nametoindex(ifname);
8389
8390    if (!interfaceIndex)
8391    {
8392        LogMsg("newMasterElected: if_nametoindex(%s) failed", ifname);
8393        return;
8394    }
8395
8396    LogInfo("newMasterElected: ifname = %s, interfaceIndex = %d", ifname, interfaceIndex);
8397    infoOSX = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)interfaceIndex);
8398
8399    // Can get an KEV_DL_MASTER_ELECTED event prior to the interface existing
8400    // when it is first brought up.
8401    if (!infoOSX)
8402    {
8403        LogInfo("newMasterElected: interface not yet active");
8404        return;
8405    }
8406    InterfaceID = infoOSX->ifinfo.InterfaceID;
8407
8408    for (q = m->Questions; q; q=q->next)
8409    {
8410        if ((!q->InterfaceID && (q->flags & kDNSServiceFlagsIncludeAWDL))
8411            || q->InterfaceID == InterfaceID)
8412        {
8413            LogInfo("newMasterElected: restarting %s query for %##s", DNSTypeName(q->qtype), q->qname.c);
8414            mDNSCoreRestartQuestion(m, q);
8415        }
8416    }
8417
8418    for (rr = m->ResourceRecords; rr; rr=rr->next)
8419    {
8420        if ((!rr->resrec.InterfaceID
8421            && ((rr->ARType == AuthRecordAnyIncludeAWDL) || ((rr->ARType == AuthRecordAnyIncludeAWDLandP2P))))
8422           || rr->resrec.InterfaceID == InterfaceID)
8423        {
8424            LogInfo("newMasterElected: restarting %s announcements for %##s", DNSTypeName(rr->resrec.rrtype), rr->namestorage.c);
8425            mDNSCoreRestartRegistration(m, rr, -1);
8426        }
8427    }
8428}
8429
8430// An ssth array of all zeroes indicates the peer has no services registered.
8431mDNSlocal mDNSBool allZeroSSTH(struct opaque_presence_indication *op)
8432{
8433    int i;
8434    int *intp = (int *) op->ssth;
8435
8436    // MAX_SSTH_SIZE should always be a multiple of sizeof(int), if
8437    // it's not, print an error message and return false so that
8438    // corresponding peer records are not flushed when KEV_DL_NODE_PRESENCE event
8439    // is received.
8440    if (MAX_SSTH_SIZE % sizeof(int))
8441    {
8442        LogInfo("allZeroSSTH: MAX_SSTH_SIZE = %d not a multiple of sizeof(int)", MAX_SSTH_SIZE);
8443        return mDNSfalse;
8444    }
8445
8446    for (i = 0; i < (int)(MAX_SSTH_SIZE / sizeof(int)); i++, intp++)
8447    {
8448        if (*intp)
8449            return mDNSfalse;
8450    }
8451    return mDNStrue;
8452}
8453
8454// mDNS_Reconfirm_internal() adds 33% to this interval, so the records should
8455// be removed in 4 seconds.
8456#define kAWDLReconfirmTime ((mDNSu32)mDNSPlatformOneSecond *  3)
8457
8458// Mark records from this peer for deletion from the cache.
8459mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr *ap)
8460{
8461    mDNSu32     slot;
8462    CacheGroup  *cg;
8463    CacheRecord *cr;
8464    mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(m, ifindex);
8465
8466    if (!InterfaceID)
8467    {
8468        LogInfo("removeCachedPeerRecords: Invalid ifindex: %d", ifindex);
8469        return;
8470    }
8471
8472    FORALL_CACHERECORDS(slot, cg, cr)
8473    {
8474        if ((InterfaceID == cr->resrec.InterfaceID) && mDNSSameAddress(ap, & cr->sourceAddress))
8475        {
8476            LogInfo("removeCachedPeerRecords: %s %##s marking for deletion",
8477                 DNSTypeName(cr->resrec.rrtype), cr->resrec.name->c);
8478            mDNS_Reconfirm_internal(m, cr, kAWDLReconfirmTime);
8479        }
8480    }
8481}
8482
8483// Handle KEV_DL_NODE_PRESENCE event.
8484mDNSlocal void nodePresence(mDNS *const m, struct kev_dl_node_presence * p)
8485{
8486    char buf[INET6_ADDRSTRLEN];
8487    struct opaque_presence_indication *op = (struct opaque_presence_indication *) p->node_service_info;
8488
8489    if (inet_ntop(AF_INET6, & p->sin6_node_address.sin6_addr, buf, sizeof(buf)))
8490        LogInfo("nodePresence:  IPv6 address: %s, SUI %d", buf, op->SUI);
8491    else
8492        LogInfo("nodePresence:  inet_ntop() error");
8493
8494    // AWDL will generate a KEV_DL_NODE_PRESENCE event with SSTH field of
8495    // all zeroes when a node is present and has no services registered.
8496    if (allZeroSSTH(op))
8497    {
8498        mDNSAddr    peerAddr;
8499
8500        peerAddr.type = mDNSAddrType_IPv6;
8501        peerAddr.ip.v6 = *(mDNSv6Addr*)&p->sin6_node_address.sin6_addr;
8502
8503        LogInfo("nodePresence: ssth is all zeroes, delete cached records from this peer");
8504        removeCachedPeerRecords(m, p->sdl_node_address.sdl_index, & peerAddr);
8505    }
8506}
8507
8508// Handle KEV_DL_NODE_ABSENCE event.
8509mDNSlocal void nodeAbsence(mDNS *const m, struct kev_dl_node_absence * p)
8510{
8511    mDNSAddr    peerAddr;
8512    char buf[INET6_ADDRSTRLEN];
8513
8514    if (inet_ntop(AF_INET6, & p->sin6_node_address.sin6_addr, buf, sizeof(buf)))
8515        LogInfo("nodeAbsence:  IPv6 address: %s", buf);
8516    else
8517        LogInfo("nodeAbsence:  inet_ntop() error");
8518
8519    peerAddr.type = mDNSAddrType_IPv6;
8520    peerAddr.ip.v6 = *(mDNSv6Addr*)&p->sin6_node_address.sin6_addr;
8521
8522    LogInfo("nodeAbsence: delete cached records from this peer");
8523    removeCachedPeerRecords(m, p->sdl_node_address.sdl_index, & peerAddr);
8524}
8525
8526mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context)
8527{
8528    mDNS *const m = (mDNS *const)context;
8529
8530    mDNS_Lock(m);
8531
8532    struct { struct kern_event_msg k; char extra[256]; } msg;
8533    int bytes = recv(s1, &msg, sizeof(msg), 0);
8534    if (bytes < 0)
8535        LogMsg("SysEventCallBack: recv error %d errno %d (%s)", bytes, errno, strerror(errno));
8536    else
8537    {
8538        LogInfo("SysEventCallBack got %d bytes size %d %X %s %X %s %X %s id %d code %d %s",
8539                bytes, msg.k.total_size,
8540                msg.k.vendor_code, msg.k.vendor_code  == KEV_VENDOR_APPLE  ? "KEV_VENDOR_APPLE"  : "?",
8541                msg.k.kev_class, msg.k.kev_class    == KEV_NETWORK_CLASS ? "KEV_NETWORK_CLASS" : "?",
8542                msg.k.kev_subclass, msg.k.kev_subclass == KEV_DL_SUBCLASS   ? "KEV_DL_SUBCLASS"   : "?",
8543                msg.k.id, msg.k.event_code,
8544                msg.k.event_code == KEV_DL_SIFFLAGS             ? "KEV_DL_SIFFLAGS"             :
8545                msg.k.event_code == KEV_DL_SIFMETRICS           ? "KEV_DL_SIFMETRICS"           :
8546                msg.k.event_code == KEV_DL_SIFMTU               ? "KEV_DL_SIFMTU"               :
8547                msg.k.event_code == KEV_DL_SIFPHYS              ? "KEV_DL_SIFPHYS"              :
8548                msg.k.event_code == KEV_DL_SIFMEDIA             ? "KEV_DL_SIFMEDIA"             :
8549                msg.k.event_code == KEV_DL_SIFGENERIC           ? "KEV_DL_SIFGENERIC"           :
8550                msg.k.event_code == KEV_DL_ADDMULTI             ? "KEV_DL_ADDMULTI"             :
8551                msg.k.event_code == KEV_DL_DELMULTI             ? "KEV_DL_DELMULTI"             :
8552                msg.k.event_code == KEV_DL_IF_ATTACHED          ? "KEV_DL_IF_ATTACHED"          :
8553                msg.k.event_code == KEV_DL_IF_DETACHING         ? "KEV_DL_IF_DETACHING"         :
8554                msg.k.event_code == KEV_DL_IF_DETACHED          ? "KEV_DL_IF_DETACHED"          :
8555                msg.k.event_code == KEV_DL_LINK_OFF             ? "KEV_DL_LINK_OFF"             :
8556                msg.k.event_code == KEV_DL_LINK_ON              ? "KEV_DL_LINK_ON"              :
8557                msg.k.event_code == KEV_DL_PROTO_ATTACHED       ? "KEV_DL_PROTO_ATTACHED"       :
8558                msg.k.event_code == KEV_DL_PROTO_DETACHED       ? "KEV_DL_PROTO_DETACHED"       :
8559                msg.k.event_code == KEV_DL_LINK_ADDRESS_CHANGED ? "KEV_DL_LINK_ADDRESS_CHANGED" :
8560                msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED    ? "KEV_DL_WAKEFLAGS_CHANGED"    :
8561                msg.k.event_code == KEV_DL_IF_IDLE_ROUTE_REFCNT ? "KEV_DL_IF_IDLE_ROUTE_REFCNT" :
8562                msg.k.event_code == KEV_DL_IFCAP_CHANGED        ? "KEV_DL_IFCAP_CHANGED"        :
8563                msg.k.event_code == KEV_DL_LINK_QUALITY_METRIC_CHANGED    ? "KEV_DL_LINK_QUALITY_METRIC_CHANGED"    :
8564                msg.k.event_code == KEV_DL_NODE_PRESENCE        ? "KEV_DL_NODE_PRESENCE"        :
8565                msg.k.event_code == KEV_DL_NODE_ABSENCE         ? "KEV_DL_NODE_ABSENCE"         :
8566                msg.k.event_code == KEV_DL_MASTER_ELECTED       ? "KEV_DL_MASTER_ELECTED"       :
8567                 "?");
8568
8569        if (msg.k.event_code == KEV_DL_NODE_PRESENCE)
8570            nodePresence(m, (struct kev_dl_node_presence *) &msg.k.event_data);
8571
8572        if (msg.k.event_code == KEV_DL_NODE_ABSENCE)
8573            nodeAbsence(m, (struct kev_dl_node_absence *) &msg.k.event_data);
8574
8575        if (msg.k.event_code == KEV_DL_MASTER_ELECTED)
8576            newMasterElected(m, (struct net_event_data *) &msg.k.event_data);
8577
8578        // We receive network change notifications both through configd and through SYSPROTO_EVENT socket.
8579        // Configd may not generate network change events for manually configured interfaces (i.e., non-DHCP)
8580        // always during sleep/wakeup due to some race conditions (See radar:8666757). At the same time, if
8581        // "Wake on Network Access" is not turned on, the notification will not have KEV_DL_WAKEFLAGS_CHANGED.
8582        // Hence, during wake up, if we see a KEV_DL_LINK_ON (i.e., link is UP), we trigger a network change.
8583
8584        if (msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED || msg.k.event_code == KEV_DL_LINK_ON)
8585            SetNetworkChanged(m, mDNSPlatformOneSecond * 2);
8586
8587#if !TARGET_OS_EMBEDDED     // don't setup packet filter rules on embedded
8588
8589        // For p2p interfaces, need to open the advertised service port in the firewall.
8590        if (msg.k.event_code == KEV_DL_IF_ATTACHED)
8591        {
8592            struct net_event_data   * p;
8593            p = (struct net_event_data *) &msg.k.event_data;
8594
8595            if (strncmp(p->if_name, "p2p", 3) == 0)
8596            {
8597                char ifname[IFNAMSIZ];
8598                snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit);
8599
8600                LogInfo("SysEventCallBack: KEV_DL_IF_ATTACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name);
8601
8602                mDNSSetPacketFilterRules(m, ifname, NULL);
8603            }
8604        }
8605
8606        // For p2p interfaces, need to clear the firewall rules on interface detach
8607        if (msg.k.event_code == KEV_DL_IF_DETACHED)
8608        {
8609            struct net_event_data   * p;
8610            p = (struct net_event_data *) &msg.k.event_data;
8611
8612            if (strncmp(p->if_name, "p2p", 3) == 0)
8613            {
8614                pfArray_t portArray, protocolArray; // not initialized since count is 0 for PF_CLEAR_RULES
8615                char ifname[IFNAMSIZ];
8616                snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit);
8617
8618                LogInfo("SysEventCallBack: KEV_DL_IF_DETACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name);
8619
8620                mDNSPacketFilterControl(PF_CLEAR_RULES, ifname, 0, portArray, protocolArray);
8621            }
8622        }
8623#endif // !TARGET_OS_EMBEDDED
8624
8625    }
8626
8627    mDNS_Unlock(m);
8628}
8629
8630mDNSlocal mStatus WatchForSysEvents(mDNS *const m)
8631{
8632    m->p->SysEventNotifier = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT);
8633    if (m->p->SysEventNotifier < 0)
8634    { LogMsg("WatchForSysEvents: socket failed error %d errno %d (%s)", m->p->SysEventNotifier, errno, strerror(errno)); return(mStatus_NoMemoryErr); }
8635
8636    struct kev_request kev_req = { KEV_VENDOR_APPLE, KEV_NETWORK_CLASS, KEV_DL_SUBCLASS };
8637    int err = ioctl(m->p->SysEventNotifier, SIOCSKEVFILT, &kev_req);
8638    if (err < 0)
8639    {
8640        LogMsg("WatchForSysEvents: SIOCSKEVFILT failed error %d errno %d (%s)", err, errno, strerror(errno));
8641        close(m->p->SysEventNotifier);
8642        m->p->SysEventNotifier = -1;
8643        return(mStatus_UnknownErr);
8644    }
8645
8646    m->p->SysEventKQueue.KQcallback = SysEventCallBack;
8647    m->p->SysEventKQueue.KQcontext  = m;
8648    m->p->SysEventKQueue.KQtask     = "System Event Notifier";
8649    KQueueSet(m->p->SysEventNotifier, EV_ADD, EVFILT_READ, &m->p->SysEventKQueue);
8650
8651    return(mStatus_NoError);
8652}
8653
8654#ifndef NO_SECURITYFRAMEWORK
8655mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCallbackInfo *info, void *context)
8656{
8657    LogInfo("***   Keychain Changed   ***");
8658    mDNS *const m = (mDNS *const)context;
8659    SecKeychainRef skc;
8660    OSStatus err = SecKeychainCopyDefault(&skc);
8661    if (!err)
8662    {
8663        if (info->keychain == skc)
8664        {
8665            // 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
8666            mDNSBool relevant = (keychainEvent == kSecDeleteEvent);
8667            if (!relevant)
8668            {
8669                UInt32 tags[3] = { kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr };
8670                SecKeychainAttributeInfo attrInfo = { 3, tags, NULL };  // Count, array of tags, array of formats
8671                SecKeychainAttributeList *a = NULL;
8672                err = SecKeychainItemCopyAttributesAndData(info->item, &attrInfo, NULL, &a, NULL, NULL);
8673                if (!err)
8674                {
8675                    relevant = ((a->attr[0].length == 4 && (!strncasecmp(a->attr[0].data, "ddns", 4) || !strncasecmp(a->attr[0].data, "sndd", 4))) ||
8676                                (a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix)))) ||
8677                                (a->attr[1].length >= mDNSPlatformStrLen(btmmprefix) && (!strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix)))));
8678                    SecKeychainItemFreeAttributesAndData(a, NULL);
8679                }
8680            }
8681            if (relevant)
8682            {
8683                LogInfo("***   Keychain Changed   *** KeychainEvent=%d %s",
8684                        keychainEvent,
8685                        keychainEvent == kSecAddEvent    ? "kSecAddEvent"    :
8686                        keychainEvent == kSecDeleteEvent ? "kSecDeleteEvent" :
8687                        keychainEvent == kSecUpdateEvent ? "kSecUpdateEvent" : "<Unknown>");
8688                // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
8689                KQueueLock(m);
8690                mDNS_Lock(m);
8691
8692                // To not read the keychain twice: when BTMM is enabled, changes happen to the keychain
8693                // then the BTMM DynStore dictionary, so delay reading the keychain for a second.
8694                // NetworkChanged() will reset the keychain timer to fire immediately when the DynStore changes.
8695                //
8696                // In the "fixup" case where the BTMM DNS servers aren't accepting the key mDNSResponder has,
8697                // the DynStore dictionary won't change (because the BTMM zone won't change).  In that case,
8698                // a one second delay is ok, as we'll still converge to correctness, and there's no race
8699                // condition between the RegistrationDomain and the DomainAuthInfo.
8700                //
8701                // Lastly, non-BTMM WAB cases can use the keychain but not the DynStore, so we need to set
8702                // the timer here, as it will not get set by NetworkChanged().
8703                SetKeyChainTimer(m, mDNSPlatformOneSecond);
8704
8705                mDNS_Unlock(m);
8706                KQueueUnlock(m, "KeychainChanged");
8707            }
8708        }
8709        CFRelease(skc);
8710    }
8711
8712    return 0;
8713}
8714#endif
8715
8716mDNSlocal void PowerOn(mDNS *const m)
8717{
8718    mDNSCoreMachineSleep(m, false);     // Will set m->SleepState = SleepState_Awake;
8719    if (m->p->WakeAtUTC)
8720    {
8721        long utc = mDNSPlatformUTC();
8722        mDNSPowerRequest(-1,-1);        // Need to explicitly clear any previous power requests -- they're not cleared automatically on wake
8723        if (m->p->WakeAtUTC - utc > 30)
8724        {
8725            LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc);
8726        }
8727        else if (utc - m->p->WakeAtUTC > 30)
8728        {
8729            LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC);
8730        }
8731        else if (IsAppleTV())
8732        {
8733            LogSPS("PowerChanged PowerOn %d seconds late, device is an AppleTV running iOS so not re-sleeping", utc - m->p->WakeAtUTC);
8734        }
8735        else
8736        {
8737            LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc);
8738            m->p->RequestReSleep = mDNS_TimeNow(m) + 20 * mDNSPlatformOneSecond;
8739        }
8740    }
8741}
8742
8743mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument)
8744{
8745    mDNS *const m = (mDNS *const)refcon;
8746    KQueueLock(m);
8747    (void)service;    // Parameter not used
8748    debugf("PowerChanged %X %lX", messageType, messageArgument);
8749
8750    // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting
8751    m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess();
8752
8753    switch(messageType)
8754    {
8755    case kIOMessageCanSystemPowerOff:       LogSPS("PowerChanged kIOMessageCanSystemPowerOff     (no action)"); break;          // E0000240
8756    case kIOMessageSystemWillPowerOff:      LogSPS("PowerChanged kIOMessageSystemWillPowerOff");                                // E0000250
8757        mDNSCoreMachineSleep(m, true);
8758        if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m);
8759        break;
8760    case kIOMessageSystemWillNotPowerOff:   LogSPS("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break;          // E0000260
8761    case kIOMessageCanSystemSleep:          LogSPS("PowerChanged kIOMessageCanSystemSleep        (no action)"); break;          // E0000270
8762    case kIOMessageSystemWillSleep:         LogSPS("PowerChanged kIOMessageSystemWillSleep");                                   // E0000280
8763        mDNSCoreMachineSleep(m, true);
8764        break;
8765    case kIOMessageSystemWillNotSleep:      LogSPS("PowerChanged kIOMessageSystemWillNotSleep    (no action)"); break;          // E0000290
8766    case kIOMessageSystemHasPoweredOn:      LogSPS("PowerChanged kIOMessageSystemHasPoweredOn");                                // E0000300
8767        // If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now
8768        if (m->SleepState)
8769        {
8770            LogMsg("PowerChanged kIOMessageSystemHasPoweredOn: ERROR m->SleepState %d", m->SleepState);
8771            PowerOn(m);
8772        }
8773        // Just to be safe, schedule a mDNSMacOSXNetworkChanged(), in case we never received
8774        // the System Configuration Framework "network changed" event that we expect
8775        // to receive some time shortly after the kIOMessageSystemWillPowerOn message
8776        mDNS_Lock(m);
8777        if (!m->p->NetworkChanged ||
8778            m->p->NetworkChanged - NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2) < 0)
8779            m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2);
8780        mDNS_Unlock(m);
8781
8782        break;
8783    case kIOMessageSystemWillRestart:       LogSPS("PowerChanged kIOMessageSystemWillRestart     (no action)"); break;          // E0000310
8784    case kIOMessageSystemWillPowerOn:       LogSPS("PowerChanged kIOMessageSystemWillPowerOn");                                 // E0000320
8785
8786        // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake
8787        if (m->SleepState != SleepState_Sleeping)
8788        {
8789            LogMsg("kIOMessageSystemWillPowerOn: ERROR m->SleepState %d", m->SleepState);
8790            m->SleepState = SleepState_Sleeping;
8791            mDNSMacOSXNetworkChanged(m);
8792        }
8793        PowerOn(m);
8794        break;
8795    default:                                LogSPS("PowerChanged unknown message %X", messageType); break;
8796    }
8797
8798    if (messageType == kIOMessageSystemWillSleep) m->p->SleepCookie = (long)messageArgument;
8799    else IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument);
8800
8801    KQueueUnlock(m, "PowerChanged Sleep/Wake");
8802}
8803
8804// iPhone OS doesn't currently have SnowLeopard's IO Power Management
8805// but it does define kIOPMAcknowledgmentOptionSystemCapabilityRequirements
8806#if defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) && !TARGET_OS_EMBEDDED
8807mDNSlocal void SnowLeopardPowerChanged(void *refcon, IOPMConnection connection, IOPMConnectionMessageToken token, IOPMSystemPowerStateCapabilities eventDescriptor)
8808{
8809    mDNS *const m = (mDNS *const)refcon;
8810    KQueueLock(m);
8811    LogSPS("SnowLeopardPowerChanged %X %X %X%s%s%s%s%s",
8812           connection, token, eventDescriptor,
8813           eventDescriptor & kIOPMSystemPowerStateCapabilityCPU     ? " CPU"     : "",
8814           eventDescriptor & kIOPMSystemPowerStateCapabilityVideo   ? " Video"   : "",
8815           eventDescriptor & kIOPMSystemPowerStateCapabilityAudio   ? " Audio"   : "",
8816           eventDescriptor & kIOPMSystemPowerStateCapabilityNetwork ? " Network" : "",
8817           eventDescriptor & kIOPMSystemPowerStateCapabilityDisk    ? " Disk"    : "");
8818
8819    // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting
8820    m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess();
8821
8822    if (eventDescriptor & kIOPMSystemPowerStateCapabilityCPU)
8823    {
8824        // We might be in Sleeping or Transferring state. When we go from "wakeup" to "sleep" state, we don't
8825        // go directly to sleep state, but transfer in to the sleep state during which SleepState is set to
8826        // SleepState_Transferring. During that time, we might get another wakeup before we transition to Sleeping
8827        // state. In that case, we need to acknowledge the previous "sleep" before we acknowledge the wakeup.
8828        if (m->SleepLimit)
8829        {
8830            LogSPS("SnowLeopardPowerChanged: Waking up, Acking old Sleep, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState);
8831            IOPMConnectionAcknowledgeEvent(connection, m->p->SleepCookie);
8832            m->SleepLimit = 0;
8833        }
8834        LogSPS("SnowLeopardPowerChanged: Waking up, Acking Wakeup, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState);
8835        // If the network notifications have already come before we got the wakeup, we ignored them and
8836        // in case we get no more, we need to trigger one.
8837        mDNS_Lock(m);
8838        SetNetworkChanged(m, 2 * mDNSPlatformOneSecond);
8839        mDNS_Unlock(m);
8840        // CPU Waking. Note: Can get this message repeatedly, as other subsystems power up or down.
8841        if (m->SleepState != SleepState_Awake) PowerOn(m);
8842        IOPMConnectionAcknowledgeEvent(connection, token);
8843    }
8844    else
8845    {
8846        // CPU sleeping. Should not get this repeatedly -- once we're told that the CPU is halting
8847        // we should hear nothing more until we're told that the CPU has started executing again.
8848        if (m->SleepState) LogMsg("SnowLeopardPowerChanged: Sleep Error %X m->SleepState %d", eventDescriptor, m->SleepState);
8849        //sleep(5);
8850        //mDNSMacOSXNetworkChanged(m);
8851        mDNSCoreMachineSleep(m, true);
8852        //if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m);
8853        m->p->SleepCookie = token;
8854    }
8855
8856    KQueueUnlock(m, "SnowLeopardPowerChanged Sleep/Wake");
8857}
8858#endif
8859
8860#if COMPILER_LIKES_PRAGMA_MARK
8861#pragma mark -
8862#pragma mark - /etc/hosts support
8863#endif
8864
8865// Implementation Notes
8866//
8867// As /etc/hosts file can be huge (1000s of entries - when this comment was written, the test file had about
8868// 23000 entries with about 4000 duplicates), we can't use a linked list to store these entries. So, we parse
8869// them into a hash table. The implementation need to be able to do the following things efficiently
8870//
8871// 1. Detect duplicates e.g., two entries with "1.2.3.4 foo"
8872// 2. Detect whether /etc/hosts has changed and what has changed since the last read from the disk
8873// 3. Ability to support multiple addresses per name e.g., "1.2.3.4 foo, 2.3.4.5 foo". To support this, we
8874//    need to be able set the RRSet of a resource record to the first one in the list and also update when
8875//    one of them go away. This is needed so that the core thinks that they are all part of the same RRSet and
8876//    not a duplicate
8877// 4. Don't maintain any local state about any records registered with the core to detect changes to /etc/hosts
8878//
8879// CFDictionary is not a suitable candidate because it does not support duplicates and even if we use a custom
8880// "hash" function to solve this, the others are hard to solve. Hence, we share the hash (AuthHash) implementation
8881// of the core layer which does all of the above very efficiently
8882
8883#define ETCHOSTS_BUFSIZE    1024    // Buffer size to parse a single line in /etc/hosts
8884
8885mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
8886{
8887    (void)m;  // unused
8888    (void)rr;
8889    (void)result;
8890    if (result == mStatus_MemFree)
8891    {
8892        LogInfo("FreeEtcHosts: %s", ARDisplayString(m, rr));
8893        freeL("etchosts", rr);
8894    }
8895}
8896
8897// Returns true on success and false on failure
8898mDNSlocal mDNSBool mDNSMacOSXCreateEtcHostsEntry(mDNS *const m, const domainname *domain, const struct sockaddr *sa, const domainname *cname, char *ifname, AuthHash *auth)
8899{
8900    AuthRecord *rr;
8901    mDNSu32 slot;
8902    mDNSu32 namehash;
8903    AuthGroup *ag;
8904    mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly;
8905    mDNSu16 rrtype;
8906
8907    if (!domain)
8908    {
8909        LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! name NULL");
8910        return mDNSfalse;
8911    }
8912    if (!sa && !cname)
8913    {
8914        LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa and cname both NULL");
8915        return mDNSfalse;
8916    }
8917
8918    if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
8919    {
8920        LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa with bad family %d", sa->sa_family);
8921        return mDNSfalse;
8922    }
8923
8924
8925    if (ifname)
8926    {
8927        mDNSu32 ifindex = if_nametoindex(ifname);
8928        if (!ifindex)
8929        {
8930            LogMsg("mDNSMacOSXCreateEtcHostsEntry: hosts entry %##s with invalid ifname %s", domain->c, ifname);
8931            return mDNSfalse;
8932        }
8933        InterfaceID = (mDNSInterfaceID)(uintptr_t)ifindex;
8934    }
8935
8936    if (sa)
8937        rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA);
8938    else
8939        rrtype = kDNSType_CNAME;
8940
8941    // Check for duplicates. See whether we parsed an entry before like this ?
8942    slot = AuthHashSlot(domain);
8943    namehash = DomainNameHashValue(domain);
8944    ag = AuthGroupForName(auth, slot, namehash, domain);
8945    if (ag)
8946    {
8947        rr = ag->members;
8948        while (rr)
8949        {
8950            if (rr->resrec.rrtype == rrtype)
8951            {
8952                if (rrtype == kDNSType_A)
8953                {
8954                    mDNSv4Addr ip;
8955                    ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
8956                    if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip))
8957                    {
8958                        LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv4 address for name %##s", domain->c);
8959                        return mDNSfalse;
8960                    }
8961                }
8962                else if (rrtype == kDNSType_AAAA)
8963                {
8964                    mDNSv6Addr ip6;
8965                    ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
8966                    ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
8967                    ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
8968                    ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
8969                    if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6))
8970                    {
8971                        LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv6 address for name %##s", domain->c);
8972                        return mDNSfalse;
8973                    }
8974                }
8975                else if (rrtype == kDNSType_CNAME)
8976                {
8977                    if (SameDomainName(&rr->resrec.rdata->u.name, cname))
8978                    {
8979                        LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same cname %##s for name %##s", cname->c, domain->c);
8980                        return mDNSfalse;
8981                    }
8982                }
8983            }
8984            rr = rr->next;
8985        }
8986    }
8987    rr= mallocL("etchosts", sizeof(*rr));
8988    if (rr == NULL) return mDNSfalse;
8989    mDNSPlatformMemZero(rr, sizeof(*rr));
8990    mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL);
8991    AssignDomainName(&rr->namestorage, domain);
8992
8993    if (sa)
8994    {
8995        rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr);
8996        if (sa->sa_family == AF_INET)
8997            rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
8998        else
8999        {
9000            rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
9001            rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
9002            rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
9003            rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
9004        }
9005    }
9006    else
9007    {
9008        rr->resrec.rdlength = DomainNameLength(cname);
9009        rr->resrec.rdata->u.name.c[0] = 0;
9010        AssignDomainName(&rr->resrec.rdata->u.name, cname);
9011    }
9012    rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
9013    SetNewRData(&rr->resrec, mDNSNULL, 0);  // Sets rr->rdatahash for us
9014    LogInfo("mDNSMacOSXCreateEtcHostsEntry: Adding resource record %s", ARDisplayString(m, rr));
9015    InsertAuthRecord(m, auth, rr);
9016    return mDNStrue;
9017}
9018
9019mDNSlocal int EtcHostsParseOneName(int start, int length, char *buffer, char **name)
9020{
9021    int i;
9022
9023    *name = NULL;
9024    for (i = start; i < length; i++)
9025    {
9026        if (buffer[i] == '#')
9027            return -1;
9028        if (buffer[i] != ' ' && buffer[i] != ',' && buffer[i] != '\t')
9029        {
9030            *name = &buffer[i];
9031
9032            // Found the start of a name, find the end and null terminate
9033            for (i++; i < length; i++)
9034            {
9035                if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t')
9036                {
9037                    buffer[i] = 0;
9038                    break;
9039                }
9040            }
9041            return i;
9042        }
9043    }
9044    return -1;
9045}
9046
9047mDNSlocal void mDNSMacOSXParseEtcHostsLine(mDNS *const m, char *buffer, ssize_t length, AuthHash *auth)
9048{
9049    int i;
9050    int ifStart = 0;
9051    char *ifname = NULL;
9052    domainname name1d;
9053    domainname name2d;
9054    char *name1;
9055    char *name2;
9056    int aliasIndex;
9057
9058    //Ignore leading whitespaces and tabs
9059    while (*buffer == ' ' || *buffer == '\t')
9060    {
9061        buffer++;
9062        length--;
9063    }
9064
9065    // Find the end of the address string
9066    for (i = 0; i < length; i++)
9067    {
9068        if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t' || buffer[i] == '%')
9069        {
9070            if (buffer[i] == '%')
9071                ifStart = i + 1;
9072            buffer[i] = 0;
9073            break;
9074        }
9075    }
9076
9077    // Convert the address string to an address
9078    struct addrinfo hints;
9079    bzero(&hints, sizeof(hints));
9080    hints.ai_flags = AI_NUMERICHOST;
9081    struct addrinfo *gairesults = NULL;
9082    if (getaddrinfo(buffer, NULL, &hints, &gairesults) != 0)
9083    {
9084        LogInfo("mDNSMacOSXParseEtcHostsLine: getaddrinfo returning null");
9085        return;
9086    }
9087
9088    if (ifStart)
9089    {
9090        // Parse the interface
9091        ifname = &buffer[ifStart];
9092        for (i = ifStart + 1; i < length; i++)
9093        {
9094            if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t')
9095            {
9096                buffer[i] = 0;
9097                break;
9098            }
9099        }
9100    }
9101
9102    i = EtcHostsParseOneName(i + 1, length, buffer, &name1);
9103    if (i == length)
9104    {
9105        // Common case (no aliases) : The entry is of the form "1.2.3.4 somehost" with no trailing white spaces/tabs etc.
9106        if (!MakeDomainNameFromDNSNameString(&name1d, name1))
9107        {
9108            LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1);
9109            freeaddrinfo(gairesults);
9110            return;
9111        }
9112        mDNSMacOSXCreateEtcHostsEntry(m, &name1d, gairesults->ai_addr, mDNSNULL, ifname, auth);
9113    }
9114    else if (i != -1)
9115    {
9116        domainname first;
9117        // We might have some extra white spaces at the end for the common case of "1.2.3.4 somehost".
9118        // When we parse again below, EtchHostsParseOneName would return -1 and we will end up
9119        // doing the right thing.
9120        if (!MakeDomainNameFromDNSNameString(&first, name1))
9121        {
9122            LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1);
9123            freeaddrinfo(gairesults);
9124            return;
9125        }
9126        // If the /etc/hosts has an entry like this
9127        //
9128        // 1.2.3.4 sun star bright
9129        //
9130        // star and bright are aliases (gethostbyname h_alias should point to these) and sun is the canonical
9131        // name (getaddrinfo ai_cannonname and gethostbyname h_name points to "sun")
9132        //
9133        // To achieve this, we need to add the entry like this:
9134        //
9135        // star CNAME bright
9136        // bright CNAME sun
9137        // sun A 1.2.3.4
9138        //
9139        // We store the first name we parsed in "first". Then we parse additional names adding CNAME records
9140        // till we reach the end. When we reach the end, we wrap around and add one final CNAME with the last
9141        // entry and the first entry. Finally, we add the Address (A/AAAA) record.
9142        aliasIndex = 0;
9143        while (i <= length)
9144        {
9145            // Parse a name. If there are no names, we need to know whether we
9146            // parsed CNAMEs before or not. If we parsed CNAMEs before, then we
9147            // add a CNAME with the last name and the first name. Otherwise, this
9148            // is same as the common case above where the line has just one name
9149            // but with trailing white spaces.
9150            i = EtcHostsParseOneName(i + 1, length, buffer, &name2);
9151            if (name2)
9152            {
9153                if (!MakeDomainNameFromDNSNameString(&name2d, name2))
9154                {
9155                    LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name2);
9156                    freeaddrinfo(gairesults);
9157                    return;
9158                }
9159                aliasIndex++;
9160            }
9161            else if (!aliasIndex)
9162            {
9163                // We have never parsed any aliases. This case happens if there
9164                // is just one name and some extra white spaces at the end.
9165                LogInfo("mDNSMacOSXParseEtcHostsLine: White space at the end of %##s", first.c);
9166                break;
9167            }
9168            else
9169            {
9170                // We have parsed at least one alias before and we reached the end of the line.
9171                // Setup a CNAME for the last name with "first" name as its RDATA
9172                name2d.c[0] = 0;
9173                AssignDomainName(&name2d, &first);
9174            }
9175
9176            // Don't add a CNAME for the first alias we parse (see the example above).
9177            // As we parse more, we might discover that there are no more aliases, in
9178            // which case we would have set "name2d" to "first" above. We need to add
9179            // the CNAME in that case.
9180
9181            if (aliasIndex > 1 || SameDomainName(&name2d, &first))
9182            {
9183                // Ignore if it points to itself
9184                if (!SameDomainName(&name1d, &name2d))
9185                {
9186                    if (!mDNSMacOSXCreateEtcHostsEntry(m, &name1d, mDNSNULL, &name2d, ifname, auth))
9187                    {
9188                        freeaddrinfo(gairesults);
9189                        return;
9190                    }
9191                }
9192                else
9193                    LogMsg("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names name1 %##s, name2 %##s", name1d.c, name2d.c);
9194            }
9195
9196            // If we have already wrapped around, we just need to add the A/AAAA record alone
9197            // which is done below
9198            if (SameDomainName(&name2d, &first)) break;
9199
9200            // Remember the current name so that we can set the CNAME record if we parse one
9201            // more name
9202            name1d.c[0] = 0;
9203            AssignDomainName(&name1d, &name2d);
9204        }
9205        // Added all the CNAMEs if any, add the "A/AAAA" record
9206        mDNSMacOSXCreateEtcHostsEntry(m, &first, gairesults->ai_addr, mDNSNULL, ifname, auth);
9207    }
9208    freeaddrinfo(gairesults);
9209}
9210
9211mDNSlocal void mDNSMacOSXParseEtcHosts(mDNS *const m, int fd, AuthHash *auth)
9212{
9213    mDNSBool good;
9214    char buf[ETCHOSTS_BUFSIZE];
9215    ssize_t len;
9216    FILE *fp;
9217
9218    if (fd == -1) { LogInfo("mDNSMacOSXParseEtcHosts: fd is -1"); return; }
9219
9220    fp = fopen("/etc/hosts", "r");
9221    if (!fp) { LogInfo("mDNSMacOSXParseEtcHosts: fp is NULL"); return; }
9222
9223    while (1)
9224    {
9225        good = (fgets(buf, ETCHOSTS_BUFSIZE, fp) != NULL);
9226        if (!good) break;
9227
9228        // skip comment and empty lines
9229        if (buf[0] == '#' || buf[0] == '\r' || buf[0] == '\n')
9230            continue;
9231
9232        len = strlen(buf);
9233        if (!len) break;    // sanity check
9234        //Check for end of line code(mostly only \n but pre-OS X Macs could have only \r)
9235        if (buf[len - 1] == '\r' || buf[len - 1] == '\n')
9236        {
9237            buf[len - 1] = '\0';
9238            len = len - 1;
9239        }
9240        // fgets always null terminates and hence even if we have no
9241        // newline at the end, it is null terminated. The callee
9242        // (mDNSMacOSXParseEtcHostsLine) expects the length to be such that
9243        // buf[length] is zero and hence we decrement len to reflect that.
9244        if (len)
9245        {
9246            //Additional check when end of line code is 2 chars ie\r\n(DOS, other old OSes)
9247            //here we need to check for just \r but taking extra caution.
9248            if (buf[len - 1] == '\r' || buf[len - 1] == '\n')
9249            {
9250                buf[len - 1] = '\0';
9251                len = len - 1;
9252            }
9253        }
9254        if (!len) //Sanity Check: len should never be zero
9255        {
9256            LogMsg("mDNSMacOSXParseEtcHosts: Length is zero!");
9257            continue;
9258        }
9259        mDNSMacOSXParseEtcHostsLine(m, buf, len, auth);
9260    }
9261    fclose(fp);
9262}
9263
9264mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m);
9265
9266mDNSlocal int mDNSMacOSXGetEtcHostsFD(mDNS *const m)
9267{
9268#ifdef __DISPATCH_GROUP__
9269    // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch
9270    static dispatch_queue_t etcq     = 0;
9271    static dispatch_source_t etcsrc   = 0;
9272    static dispatch_source_t hostssrc = 0;
9273
9274    // First time through? just schedule ourselves on the main queue and we'll do the work later
9275    if (!etcq)
9276    {
9277        etcq = dispatch_get_main_queue();
9278        if (etcq)
9279        {
9280            // Do this work on the queue, not here - solves potential synchronization issues
9281            dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
9282        }
9283        return -1;
9284    }
9285
9286    if (hostssrc) return dispatch_source_get_handle(hostssrc);
9287#endif
9288
9289    int fd = open("/etc/hosts", O_RDONLY);
9290
9291#ifdef __DISPATCH_GROUP__
9292    // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch
9293    if (fd == -1)
9294    {
9295        // If the open failed and we're already watching /etc, we're done
9296        if (etcsrc) { LogInfo("mDNSMacOSXGetEtcHostsFD: Returning etcfd because no etchosts"); return fd; }
9297
9298        // we aren't watching /etc, we should be
9299        fd = open("/etc", O_RDONLY);
9300        if (fd == -1) { LogInfo("mDNSMacOSXGetEtcHostsFD: etc does not exist"); return -1; }
9301        etcsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME, etcq);
9302        if (etcsrc == NULL)
9303        {
9304            close(fd);
9305            return -1;
9306        }
9307        dispatch_source_set_event_handler(etcsrc,
9308                                          ^{
9309                                              u_int32_t flags = dispatch_source_get_data(etcsrc);
9310                                              LogMsg("mDNSMacOSXGetEtcHostsFD: /etc changed 0x%x", flags);
9311                                              if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0)
9312                                              {
9313                                                  dispatch_source_cancel(etcsrc);
9314                                                  dispatch_release(etcsrc);
9315                                                  etcsrc = NULL;
9316                                                  dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
9317                                                  return;
9318                                              }
9319                                              if ((flags & DISPATCH_VNODE_WRITE) != 0 && hostssrc == NULL)
9320                                              {
9321                                                  mDNSMacOSXUpdateEtcHosts(m);
9322                                              }
9323                                          });
9324        dispatch_source_set_cancel_handler(etcsrc, ^{close(fd);});
9325        dispatch_resume(etcsrc);
9326
9327        // Try and open /etc/hosts once more now that we're watching /etc, in case we missed the creation
9328        fd = open("/etc/hosts", O_RDONLY | O_EVTONLY);
9329        if (fd == -1) { LogMsg("mDNSMacOSXGetEtcHostsFD etc hosts does not exist, watching etc"); return -1; }
9330    }
9331
9332    // create a dispatch source to watch for changes to hosts file
9333    hostssrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd,
9334                                      (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME |
9335                                       DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_LINK | DISPATCH_VNODE_REVOKE), etcq);
9336    if (hostssrc == NULL)
9337    {
9338        close(fd);
9339        return -1;
9340    }
9341    dispatch_source_set_event_handler(hostssrc,
9342                                      ^{
9343                                          u_int32_t flags = dispatch_source_get_data(hostssrc);
9344                                          LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts changed 0x%x", flags);
9345                                          if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0)
9346                                          {
9347                                              dispatch_source_cancel(hostssrc);
9348                                              dispatch_release(hostssrc);
9349                                              hostssrc = NULL;
9350                                              // Bug in LibDispatch: wait a second before scheduling the block. If we schedule
9351                                              // the block immediately, we try to open the file and the file may not exist and may
9352                                              // fail to get a notification in the future. When the file does not exist and
9353                                              // we start to monitor the directory, on "dispatch_resume" of that source, there
9354                                              // is no guarantee that the file creation will be notified always because when
9355                                              // the dispatch_resume returns, the kevent manager may not have registered the
9356                                              // kevent yet but the file may have been created
9357                                              usleep(1000000);
9358                                              dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
9359                                              return;
9360                                          }
9361                                          if ((flags & DISPATCH_VNODE_WRITE) != 0)
9362                                          {
9363                                              mDNSMacOSXUpdateEtcHosts(m);
9364                                          }
9365                                      });
9366    dispatch_source_set_cancel_handler(hostssrc, ^{LogInfo("mDNSMacOSXGetEtcHostsFD: Closing etchosts fd %d", fd); close(fd);});
9367    dispatch_resume(hostssrc);
9368
9369    // Cleanup /etc source, no need to watch it if we already have /etc/hosts
9370    if (etcsrc)
9371    {
9372        dispatch_source_cancel(etcsrc);
9373        dispatch_release(etcsrc);
9374        etcsrc = NULL;
9375    }
9376
9377    LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts being monitored, and not etc");
9378    return hostssrc ? (int)dispatch_source_get_handle(hostssrc) : -1;
9379#else
9380    (void)m;
9381    return fd;
9382#endif
9383}
9384
9385// When /etc/hosts is modified, flush all the cache records as there may be local
9386// authoritative answers now
9387mDNSlocal void FlushAllCacheRecords(mDNS *const m)
9388{
9389    CacheRecord *cr;
9390    mDNSu32 slot;
9391    CacheGroup *cg;
9392
9393    FORALL_CACHERECORDS(slot, cg, cr)
9394    {
9395        // Skip multicast.
9396        if (cr->resrec.InterfaceID) continue;
9397
9398        // If a resource record can answer A or AAAA, they need to be flushed so that we will
9399        // never used to deliver an ADD or RMV
9400        if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) ||
9401            RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA))
9402        {
9403            LogInfo("FlushAllCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr));
9404            mDNS_PurgeCacheResourceRecord(m, cr);
9405        }
9406    }
9407}
9408
9409// Add new entries to the core. If justCheck is set, this function does not add, just returns true
9410mDNSlocal mDNSBool EtcHostsAddNewEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck)
9411{
9412    AuthGroup *ag;
9413    mDNSu32 slot;
9414    AuthRecord *rr, *primary, *rrnext;
9415    for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
9416        for (ag = newhosts->rrauth_hash[slot]; ag; ag = ag->next)
9417        {
9418            primary = NULL;
9419            for (rr = ag->members; rr; rr = rrnext)
9420            {
9421                rrnext = rr->next;
9422                AuthGroup *ag1;
9423                AuthRecord *rr1;
9424                mDNSBool found = mDNSfalse;
9425                ag1 = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec);
9426                if (ag1 && ag1->members)
9427                {
9428                    if (!primary) primary = ag1->members;
9429                    rr1 = ag1->members;
9430                    while (rr1)
9431                    {
9432                        // We are not using InterfaceID in checking for duplicates. This means,
9433                        // if there are two addresses for a given name e.g., fe80::1%en0 and
9434                        // fe80::1%en1, we only add the first one. It is not clear whether
9435                        // this is a common case. To fix this, we also need to modify
9436                        // mDNS_Register_internal in how it handles duplicates. If it becomes a
9437                        // common case, we will fix it then.
9438                        if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec))
9439                        {
9440                            LogInfo("EtcHostsAddNewEntries: Skipping, not adding %s", ARDisplayString(m, rr1));
9441                            found = mDNStrue;
9442                            break;
9443                        }
9444                        rr1 = rr1->next;
9445                    }
9446                }
9447                if (!found)
9448                {
9449                    if (justCheck)
9450                    {
9451                        LogInfo("EtcHostsAddNewEntries: Entry %s not registered with core yet", ARDisplayString(m, rr));
9452                        return mDNStrue;
9453                    }
9454                    RemoveAuthRecord(m, newhosts, rr);
9455                    // if there is no primary, point to self
9456                    rr->RRSet = (primary ? primary : rr);
9457                    rr->next = NULL;
9458                    LogInfo("EtcHostsAddNewEntries: Adding %s", ARDisplayString(m, rr));
9459                    if (mDNS_Register_internal(m, rr) != mStatus_NoError)
9460                        LogMsg("EtcHostsAddNewEntries: mDNS_Register failed for %s", ARDisplayString(m, rr));
9461                }
9462            }
9463        }
9464    return mDNSfalse;
9465}
9466
9467// Delete entries from the core that are no longer needed. If justCheck is set, this function
9468// does not delete, just returns true
9469mDNSlocal mDNSBool EtcHostsDeleteOldEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck)
9470{
9471    AuthGroup *ag;
9472    mDNSu32 slot;
9473    AuthRecord *rr, *primary, *rrnext;
9474    for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
9475        for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
9476            for (rr = ag->members; rr; rr = rrnext)
9477            {
9478                mDNSBool found = mDNSfalse;
9479                AuthGroup *ag1;
9480                AuthRecord *rr1;
9481                rrnext = rr->next;
9482                if (rr->RecordCallback != FreeEtcHosts) continue;
9483                ag1 = AuthGroupForRecord(newhosts, slot, &rr->resrec);
9484                if (ag1)
9485                {
9486                    primary = rr1 = ag1->members;
9487                    while (rr1)
9488                    {
9489                        if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec))
9490                        {
9491                            LogInfo("EtcHostsDeleteOldEntries: Old record %s found in new, skipping", ARDisplayString(m, rr));
9492                            found = mDNStrue;
9493                            break;
9494                        }
9495                        rr1 = rr1->next;
9496                    }
9497                }
9498                // there is no corresponding record in newhosts for the same name. This means
9499                // we should delete this from the core.
9500                if (!found)
9501                {
9502                    if (justCheck)
9503                    {
9504                        LogInfo("EtcHostsDeleteOldEntries: Record %s not found in new, deleting", ARDisplayString(m, rr));
9505                        return mDNStrue;
9506                    }
9507                    // if primary is going away, make sure that the rest of the records
9508                    // point to the new primary
9509                    if (rr == ag->members)
9510                    {
9511                        AuthRecord *new_primary = rr->next;
9512                        AuthRecord *r = new_primary;
9513                        while (r)
9514                        {
9515                            if (r->RRSet == rr)
9516                            {
9517                                LogInfo("EtcHostsDeleteOldEntries: Updating Resource Record %s to primary", ARDisplayString(m, r));
9518                                r->RRSet = new_primary;
9519                            }
9520                            else LogMsg("EtcHostsDeleteOldEntries: ERROR!! Resource Record %s not pointing to primary %##s", ARDisplayString(m, r), r->resrec.name);
9521                            r = r->next;
9522                        }
9523                    }
9524                    LogInfo("EtcHostsDeleteOldEntries: Deleting %s", ARDisplayString(m, rr));
9525                    mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
9526                }
9527            }
9528    return mDNSfalse;
9529}
9530
9531mDNSlocal void UpdateEtcHosts(mDNS *const m, void *context)
9532{
9533    AuthHash *newhosts = (AuthHash *)context;
9534
9535    mDNS_CheckLock(m);
9536
9537    //Delete old entries from the core if they are not present in the newhosts
9538    EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse);
9539    // Add the new entries to the core if not already present in the core
9540    EtcHostsAddNewEntries(m, newhosts, mDNSfalse);
9541}
9542
9543mDNSlocal void FreeNewHosts(AuthHash *newhosts)
9544{
9545    mDNSu32 slot;
9546    AuthGroup *ag, *agnext;
9547    AuthRecord *rr, *rrnext;
9548
9549    for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
9550        for (ag = newhosts->rrauth_hash[slot]; ag; ag = agnext)
9551        {
9552            agnext = ag->next;
9553            for (rr = ag->members; rr; rr = rrnext)
9554            {
9555                rrnext = rr->next;
9556                freeL("etchosts", rr);
9557            }
9558            freeL("AuthGroups", ag);
9559        }
9560}
9561
9562mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m)
9563{
9564    AuthHash newhosts;
9565
9566    // As we will be modifying the core, we can only have one thread running at
9567    // any point in time.
9568    KQueueLock(m);
9569
9570    mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
9571
9572    // Get the file desecriptor (will trigger us to start watching for changes)
9573    int fd = mDNSMacOSXGetEtcHostsFD(m);
9574    if (fd != -1)
9575    {
9576        LogInfo("mDNSMacOSXUpdateEtcHosts: Parsing /etc/hosts fd %d", fd);
9577        mDNSMacOSXParseEtcHosts(m, fd, &newhosts);
9578    }
9579    else LogInfo("mDNSMacOSXUpdateEtcHosts: /etc/hosts is not present");
9580
9581    // Optimization: Detect whether /etc/hosts changed or not.
9582    //
9583    // 1. Check to see if there are any new entries. We do this by seeing whether any entries in
9584    //    newhosts is already registered with core.  If we find at least one entry that is not
9585    //    registered with core, then it means we have work to do.
9586    //
9587    // 2. Next, we check to see if any of the entries that are registered with core is not present
9588    //   in newhosts. If we find at least one entry that is not present, it means we have work to
9589    //   do.
9590    //
9591    // Note: We may not have to hold the lock right here as KQueueLock is held which prevents any
9592    // other thread from running. But mDNS_Lock is needed here as we will be traversing the core
9593    // data structure in EtcHostsDeleteOldEntries/NewEntries which might expect the lock to be held
9594    // in the future and this code does not have to change.
9595    mDNS_Lock(m);
9596    // Add the new entries to the core if not already present in the core
9597    if (!EtcHostsAddNewEntries(m, &newhosts, mDNStrue))
9598    {
9599        // No new entries to add, check to see if we need to delete any old entries from the
9600        // core if they are not present in the newhosts
9601        if (!EtcHostsDeleteOldEntries(m, &newhosts, mDNStrue))
9602        {
9603            LogInfo("mDNSMacOSXUpdateEtcHosts: No work");
9604            mDNS_Unlock(m);
9605            KQueueUnlock(m, "/etc/hosts changed");
9606            FreeNewHosts(&newhosts);
9607            return;
9608        }
9609    }
9610
9611    // This will flush the cache, stop and start the query so that the queries
9612    // can look at the /etc/hosts again
9613    //
9614    // Notes:
9615    //
9616    // We can't delete and free the records here. We wait for the mDNSCoreRestartAddressQueries to
9617    // deliver RMV events. It has to be done in a deferred way because we can't deliver RMV
9618    // events for local records *before* the RMV events for cache records. mDNSCoreRestartAddressQueries
9619    // delivers these events in the right order and then calls us back to delete them.
9620    //
9621    // Similarly, we do a deferred Registration of the record because mDNSCoreRestartAddressQueries
9622    // is a common function that looks at all local auth records and delivers a RMV including
9623    // the records that we might add here. If we deliver a ADD here, it will get a RMV and then when
9624    // the query is restarted, it will get another ADD. To avoid this (ADD-RMV-ADD), we defer registering
9625    // the record until the RMVs are delivered in mDNSCoreRestartAddressQueries after which UpdateEtcHosts
9626    // is called back where we do the Registration of the record. This results in RMV followed by ADD which
9627    // looks normal.
9628    mDNSCoreRestartAddressQueries(m, mDNSfalse, FlushAllCacheRecords, UpdateEtcHosts, &newhosts);
9629    mDNS_Unlock(m);
9630
9631    KQueueUnlock(m, "/etc/hosts changed");
9632    FreeNewHosts(&newhosts);
9633}
9634
9635#if COMPILER_LIKES_PRAGMA_MARK
9636#pragma mark -
9637#pragma mark - Initialization & Teardown
9638#endif
9639
9640CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void);
9641CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey;
9642CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey;
9643CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey;
9644
9645// Major version 13 is 10.9.x
9646mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
9647{
9648    int major = 0, minor = 0;
9649    char letter = 0, prodname[256]="<Unknown>", prodvers[256]="<Unknown>", buildver[256]="<Unknown>";
9650    CFDictionaryRef vers = _CFCopySystemVersionDictionary();
9651    if (vers)
9652    {
9653        CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey);
9654        CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey);
9655        CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey);
9656        if (cfprodname)
9657            CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8);
9658        if (cfprodvers)
9659            CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8);
9660        if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8))
9661            sscanf(buildver, "%d%c%d", &major, &letter, &minor);
9662        CFRelease(vers);
9663    }
9664    if (!major)
9665    {
9666        major = 13;
9667        LogMsg("Note: No Major Build Version number found; assuming 13");
9668    }
9669    if (HINFO_SWstring)
9670        mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion));
9671    //LogMsg("%s %s (%s), %d %c %d", prodname, prodvers, buildver, major, letter, minor);
9672
9673    // If product name is "Mac OS X" (or similar) we set OSXVers, else we set iOSVers;
9674    if ((prodname[0] & 0xDF) == 'M')
9675        OSXVers = major;
9676    else
9677        iOSVers = major;
9678}
9679
9680// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT.
9681// If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses --
9682// we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses.
9683mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void)
9684{
9685    int err = -1;
9686    int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
9687    if (s < 3)
9688        LogMsg("mDNSPlatformInit_CanReceiveUnicast: socket error %d errno %d (%s)", s, errno, strerror(errno));
9689    else
9690    {
9691        struct sockaddr_in s5353;
9692        s5353.sin_family      = AF_INET;
9693        s5353.sin_port        = MulticastDNSPort.NotAnInteger;
9694        s5353.sin_addr.s_addr = 0;
9695        err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353));
9696        close(s);
9697    }
9698
9699    if (err) LogMsg("No unicast UDP responses");
9700    else debugf("Unicast UDP responses okay");
9701    return(err == 0);
9702}
9703
9704mDNSlocal void CreatePTRRecord(mDNS *const m, const domainname *domain)
9705{
9706    AuthRecord *rr;
9707    const domainname *pname = (domainname *)"\x9" "localhost";
9708
9709    rr= mallocL("localhosts", sizeof(*rr));
9710    if (rr == NULL) return;
9711    mDNSPlatformMemZero(rr, sizeof(*rr));
9712
9713    mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, mDNSNULL, mDNSNULL);
9714    AssignDomainName(&rr->namestorage, domain);
9715
9716    rr->resrec.rdlength = DomainNameLength(pname);
9717    rr->resrec.rdata->u.name.c[0] = 0;
9718    AssignDomainName(&rr->resrec.rdata->u.name, pname);
9719
9720    rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
9721    SetNewRData(&rr->resrec, mDNSNULL, 0);  // Sets rr->rdatahash for us
9722    mDNS_Register(m, rr);
9723}
9724
9725// Setup PTR records for 127.0.0.1 and ::1. This helps answering them locally rather than relying
9726// on the external DNS server to answer this. Sometimes, the DNS servers don't respond in a timely
9727// fashion and applications depending on this e.g., telnetd, times out after 30 seconds creating
9728// a bad user experience. For now, we specifically create only localhosts to handle radar://9354225
9729//
9730// Note: We could have set this up while parsing the entries in /etc/hosts. But this is kept separate
9731// intentionally to avoid adding to the complexity of code handling /etc/hosts.
9732mDNSlocal void SetupLocalHostRecords(mDNS *const m)
9733{
9734    char buffer[MAX_REVERSE_MAPPING_NAME];
9735    domainname name;
9736    int i;
9737    struct in6_addr addr;
9738    mDNSu8 *ptr = addr.__u6_addr.__u6_addr8;
9739
9740    if (inet_pton(AF_INET, "127.0.0.1", &addr) == 1)
9741    {
9742        mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.",
9743                      ptr[3], ptr[2], ptr[1], ptr[0]);
9744        MakeDomainNameFromDNSNameString(&name, buffer);
9745        CreatePTRRecord(m, &name);
9746    }
9747    else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET failed");
9748
9749    if (inet_pton(AF_INET6, "::1", &addr) == 1)
9750    {
9751        for (i = 0; i < 16; i++)
9752        {
9753            static const char hexValues[] = "0123456789ABCDEF";
9754            buffer[i * 4    ] = hexValues[ptr[15 - i] & 0x0F];
9755            buffer[i * 4 + 1] = '.';
9756            buffer[i * 4 + 2] = hexValues[ptr[15 - i] >> 4];
9757            buffer[i * 4 + 3] = '.';
9758        }
9759        mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
9760        MakeDomainNameFromDNSNameString(&name, buffer);
9761        CreatePTRRecord(m, &name);
9762    }
9763    else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET6 failed");
9764}
9765
9766// Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows:
9767// 1) query for b._dns-sd._udp.local on LocalOnly interface
9768//    (.local manually generated via explicit callback)
9769// 2) for each search domain (from prefs pane), query for b._dns-sd._udp.<searchdomain>.
9770// 3) for each result from (2), register LocalOnly PTR record b._dns-sd._udp.local. -> <result>
9771// 4) result above should generate a callback from question in (1).  result added to global list
9772// 5) global list delivered to client via GetSearchDomainList()
9773// 6) client calls to enumerate domains now go over LocalOnly interface
9774//    (!!!KRS may add outgoing interface in addition)
9775
9776mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
9777{
9778    mStatus err;
9779    m->p->CFRunLoop = CFRunLoopGetCurrent();
9780
9781    char HINFO_SWstring[256] = "";
9782    mDNSMacOSXSystemBuildNumber(HINFO_SWstring);
9783
9784    err = mDNSHelperInit();
9785    if (err)
9786        return err;
9787
9788    DynamicStoreQueue = dispatch_queue_create("com.apple.mDNSResponder.DynamicStoreQueue", NULL);
9789    if (DynamicStoreQueue == NULL)
9790    {
9791        LogMsg("dispatch_queue_create: DynamicStoreQueue NULL!");
9792        return mStatus_NoMemoryErr;
9793    }
9794
9795    // Store mDNSResponder Platform
9796    if (OSXVers)
9797    {
9798        m->mDNS_plat = platform_OSX;
9799    }
9800    else if (iOSVers)
9801    {
9802        if (IsAppleTV())
9803            m->mDNS_plat = platform_Atv;
9804        else
9805            m->mDNS_plat = platform_iOS;
9806    }
9807    else
9808    {
9809        m->mDNS_plat = platform_NonApple;
9810    }
9811
9812    // In 10.4, mDNSResponder is launched very early in the boot process, while other subsystems are still in the process of starting up.
9813    // 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.
9814    int i;
9815    for (i=0; i<100; i++)
9816    {
9817        domainlabel testlabel;
9818        testlabel.c[0] = 0;
9819        GetUserSpecifiedLocalHostName(&testlabel);
9820        if (testlabel.c[0]) break;
9821        usleep(50000);
9822    }
9823
9824    m->hostlabel.c[0]        = 0;
9825
9826    int get_model[2] = { CTL_HW, HW_MODEL };
9827    size_t len_model = sizeof(HINFO_HWstring_buffer);
9828
9829    // Normal Apple model names are of the form "iPhone2,1", and
9830    // internal code names are strings containing no commas, e.g. "N88AP".
9831    // We used to ignore internal code names, but Apple now uses these internal code names
9832    // even in released shipping products, so we no longer ignore strings containing no commas.
9833//	if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0 && strchr(HINFO_HWstring_buffer, ','))
9834    if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0)
9835        HINFO_HWstring = HINFO_HWstring_buffer;
9836
9837    // For names of the form "iPhone2,1" we use "iPhone" as the prefix for automatic name generation.
9838    // For names of the form "N88AP" containg no comma, we use the entire string.
9839    HINFO_HWstring_prefixlen = strchr(HINFO_HWstring_buffer, ',') ? strcspn(HINFO_HWstring, "0123456789") : strlen(HINFO_HWstring);
9840
9841    if (mDNSPlatformInit_CanReceiveUnicast())
9842        m->CanReceiveUnicastOn5353 = mDNStrue;
9843
9844    mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring);
9845    mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring);
9846    if (hlen + slen < 254)
9847    {
9848        m->HIHardware.c[0] = hlen;
9849        m->HISoftware.c[0] = slen;
9850        mDNSPlatformMemCopy(&m->HIHardware.c[1], HINFO_HWstring, hlen);
9851        mDNSPlatformMemCopy(&m->HISoftware.c[1], HINFO_SWstring, slen);
9852    }
9853
9854    m->p->permanentsockets.port  = MulticastDNSPort;
9855    m->p->permanentsockets.m     = m;
9856    m->p->permanentsockets.sktv4 = -1;
9857    m->p->permanentsockets.kqsv4.KQcallback = myKQSocketCallBack;
9858    m->p->permanentsockets.kqsv4.KQcontext  = &m->p->permanentsockets;
9859    m->p->permanentsockets.kqsv4.KQtask     = "UDP packet reception";
9860    m->p->permanentsockets.sktv6 = -1;
9861    m->p->permanentsockets.kqsv6.KQcallback = myKQSocketCallBack;
9862    m->p->permanentsockets.kqsv6.KQcontext  = &m->p->permanentsockets;
9863    m->p->permanentsockets.kqsv6.KQtask     = "UDP packet reception";
9864
9865    err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL);
9866    err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL);
9867
9868    struct sockaddr_in s4;
9869    socklen_t n4 = sizeof(s4);
9870    if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0)
9871        LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno));
9872    else
9873        m->UnicastPort4.NotAnInteger = s4.sin_port;
9874
9875    if (m->p->permanentsockets.sktv6 >= 0)
9876    {
9877        struct sockaddr_in6 s6;
9878        socklen_t n6 = sizeof(s6);
9879        if (getsockname(m->p->permanentsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno));
9880        else m->UnicastPort6.NotAnInteger = s6.sin6_port;
9881    }
9882
9883    m->p->InterfaceList      = mDNSNULL;
9884    m->p->userhostlabel.c[0] = 0;
9885    m->p->usernicelabel.c[0] = 0;
9886    m->p->prevoldnicelabel.c[0] = 0;
9887    m->p->prevnewnicelabel.c[0] = 0;
9888    m->p->prevoldhostlabel.c[0] = 0;
9889    m->p->prevnewhostlabel.c[0] = 0;
9890    m->p->NotifyUser         = 0;
9891    m->p->KeyChainTimer      = 0;
9892    m->p->WakeAtUTC          = 0;
9893    m->p->RequestReSleep     = 0;
9894    // Assume that everything is good to begin with. If something is not working,
9895    // we will detect that when we start sending questions.
9896    m->p->v4answers          = 1;
9897    m->p->v6answers          = 1;
9898    m->p->DNSTrigger         = 0;
9899    m->p->LastConfigGeneration = 0;
9900
9901#if APPLE_OSX_mDNSResponder
9902    uuid_generate(m->asl_uuid);
9903#endif
9904
9905    m->AutoTunnelRelayAddr = zerov6Addr;
9906
9907    NetworkChangedKey_IPv4         = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
9908    NetworkChangedKey_IPv6         = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6);
9909    NetworkChangedKey_Hostnames    = SCDynamicStoreKeyCreateHostNames(NULL);
9910    NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL);
9911    NetworkChangedKey_DNS          = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS);
9912    NetworkChangedKey_StateInterfacePrefix = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, CFSTR(""), NULL);
9913    if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS || !NetworkChangedKey_StateInterfacePrefix)
9914    { LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr); }
9915
9916    err = WatchForNetworkChanges(m);
9917    if (err) { LogMsg("mDNSPlatformInit_setup: WatchForNetworkChanges failed %d", err); return(err); }
9918
9919#if 0 // <rdar://problem/6751656>
9920    err = WatchForPMChanges(m);
9921    if (err) { LogMsg("mDNSPlatformInit_setup: WatchForPMChanges failed %d", err); return(err); }
9922#endif
9923
9924    err = WatchForSysEvents(m);
9925    if (err) { LogMsg("mDNSPlatformInit_setup: WatchForSysEvents failed %d", err); return(err); }
9926
9927    mDNSs32 utc = mDNSPlatformUTC();
9928    m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess();
9929    UpdateInterfaceList(m, utc);
9930    SetupActiveInterfaces(m, utc);
9931
9932    // Explicitly ensure that our Keychain operations utilize the system domain.
9933#ifndef NO_SECURITYFRAMEWORK
9934    SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
9935#endif
9936
9937    mDNS_Lock(m);
9938    SetDomainSecrets(m);
9939    SetLocalDomains();
9940    mDNS_Unlock(m);
9941
9942#ifndef NO_SECURITYFRAMEWORK
9943    err = SecKeychainAddCallback(KeychainChanged, kSecAddEventMask|kSecDeleteEventMask|kSecUpdateEventMask, m);
9944    if (err) { LogMsg("mDNSPlatformInit_setup: SecKeychainAddCallback failed %d", err); return(err); }
9945#endif
9946
9947#if !defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) || TARGET_OS_EMBEDDED
9948    LogMsg("Note: Compiled without SnowLeopard Fine-Grained Power Management support");
9949#else
9950    IOPMConnection c;
9951    IOReturn iopmerr = IOPMConnectionCreate(CFSTR("mDNSResponder"), kIOPMSystemPowerStateCapabilityCPU, &c);
9952    if (iopmerr) LogMsg("IOPMConnectionCreate failed %d", iopmerr);
9953    else
9954    {
9955        iopmerr = IOPMConnectionSetNotification(c, m, SnowLeopardPowerChanged);
9956        if (iopmerr) LogMsg("IOPMConnectionSetNotification failed %d", iopmerr);
9957        else
9958        {
9959#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
9960            IOPMConnectionSetDispatchQueue(c, dispatch_get_main_queue());
9961            LogInfo("IOPMConnectionSetDispatchQueue is now running");
9962#else
9963            iopmerr = IOPMConnectionScheduleWithRunLoop(c, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
9964            if (iopmerr) LogMsg("IOPMConnectionScheduleWithRunLoop failed %d", iopmerr);
9965            LogInfo("IOPMConnectionScheduleWithRunLoop is now running");
9966#endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */
9967        }
9968    }
9969    m->p->IOPMConnection = iopmerr ? mDNSNULL : c;
9970    if (iopmerr) // If IOPMConnectionCreate unavailable or failed, proceed with old-style power notification code below
9971#endif // kIOPMAcknowledgmentOptionSystemCapabilityRequirements
9972    {
9973        m->p->PowerConnection = IORegisterForSystemPower(m, &m->p->PowerPortRef, PowerChanged, &m->p->PowerNotifier);
9974        if (!m->p->PowerConnection) { LogMsg("mDNSPlatformInit_setup: IORegisterForSystemPower failed"); return(-1); }
9975        else
9976        {
9977#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
9978            IONotificationPortSetDispatchQueue(m->p->PowerPortRef, dispatch_get_main_queue());
9979#else
9980            CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode);
9981#endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */
9982        }
9983    }
9984
9985#if APPLE_OSX_mDNSResponder
9986    // Note: We use SPMetricPortability > 35 to indicate a laptop of some kind
9987    // SPMetricPortability <= 35 means nominally a non-portable machine (i.e. Mac mini or better)
9988    // Apple TVs, AirPort base stations, and Time Capsules do not actually weigh 3kg, but we assign them
9989    // higher 'nominal' masses to indicate they should be treated as being relatively less portable than a laptop
9990    if      (!strncasecmp(HINFO_HWstring, "Xserve",       6)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; }
9991    else if (!strncasecmp(HINFO_HWstring, "RackMac",      7)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; }
9992    else if (!strncasecmp(HINFO_HWstring, "MacPro",       6)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; }
9993    else if (!strncasecmp(HINFO_HWstring, "PowerMac",     8)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 82 /* 160W */; SPMetricTotalPower = 83 /* 200W */; }
9994    else if (!strncasecmp(HINFO_HWstring, "iMac",         4)) { SPMetricPortability = 30 /* 10kg */; SPMetricMarginalPower = 77 /*  50W */; SPMetricTotalPower = 78 /*  60W */; }
9995    else if (!strncasecmp(HINFO_HWstring, "Macmini",      7)) { SPMetricPortability = 33 /*  5kg */; SPMetricMarginalPower = 73 /*  20W */; SPMetricTotalPower = 74 /*  25W */; }
9996    else if (!strncasecmp(HINFO_HWstring, "TimeCapsule", 11)) { SPMetricPortability = 34 /*  4kg */; SPMetricMarginalPower = 10 /*  ~0W */; SPMetricTotalPower = 70 /*  13W */; }
9997    else if (!strncasecmp(HINFO_HWstring, "AirPort",      7)) { SPMetricPortability = 35 /*  3kg */; SPMetricMarginalPower = 10 /*  ~0W */; SPMetricTotalPower = 70 /*  12W */; }
9998    else if (  IsAppleTV()  )                                 { SPMetricPortability = 35 /*  3kg */; SPMetricMarginalPower = 60 /*   1W */; SPMetricTotalPower = 63 /*   2W */; }
9999    else if (!strncasecmp(HINFO_HWstring, "MacBook",      7)) { SPMetricPortability = 37 /*  2kg */; SPMetricMarginalPower = 71 /*  13W */; SPMetricTotalPower = 72 /*  15W */; }
10000    else if (!strncasecmp(HINFO_HWstring, "PowerBook",    9)) { SPMetricPortability = 37 /*  2kg */; SPMetricMarginalPower = 71 /*  13W */; SPMetricTotalPower = 72 /*  15W */; }
10001    LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d Features %d",
10002           HINFO_HWstring_prefixlen, HINFO_HWstring, HINFO_HWstring, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower, SPMetricFeatures);
10003#endif // APPLE_OSX_mDNSResponder
10004
10005    // Currently this is not defined. SSL code will eventually fix this. If it becomes
10006    // critical, we will define this to workaround the bug in SSL.
10007#ifdef __SSL_NEEDS_SERIALIZATION__
10008    SSLqueue = dispatch_queue_create("com.apple.mDNSResponder.SSLQueue", NULL);
10009#else
10010    SSLqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
10011#endif
10012    if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL");
10013
10014    mDNSMacOSXUpdateEtcHosts(m);
10015    SetupLocalHostRecords(m);
10016    CUPInit(m);
10017
10018    return(mStatus_NoError);
10019}
10020
10021mDNSexport mStatus mDNSPlatformInit(mDNS *const m)
10022{
10023#if MDNS_NO_DNSINFO
10024    LogMsg("Note: Compiled without Apple-specific Split-DNS support");
10025#endif
10026
10027    // Adding interfaces will use this flag, so set it now.
10028    m->DivertMulticastAdvertisements = !m->AdvertiseLocalAddresses;
10029
10030#if APPLE_OSX_mDNSResponder
10031    m->SPSBrowseCallback = UpdateSPSStatus;
10032#endif // APPLE_OSX_mDNSResponder
10033
10034    mStatus result = mDNSPlatformInit_setup(m);
10035
10036    // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already
10037    // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately
10038    if (result == mStatus_NoError)
10039    {
10040        mDNSCoreInitComplete(m, mStatus_NoError);
10041
10042#if !NO_D2D
10043        // We only initialize if mDNSCore successfully initialized.
10044        if (D2DInitialize)
10045        {
10046            D2DStatus ds = D2DInitialize(m->p->CFRunLoop, xD2DServiceCallback, m) ;
10047            if (ds != kD2DSuccess)
10048                LogMsg("D2DInitialiize failed: %d", ds);
10049            else
10050                LogMsg("D2DInitialize succeeded");
10051        }
10052#endif // ! NO_D2D
10053
10054    }
10055    result = DNSSECCryptoInit(m);
10056    return(result);
10057}
10058
10059mDNSexport void mDNSPlatformClose(mDNS *const m)
10060{
10061    if (m->p->PowerConnection)
10062    {
10063#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
10064        IONotificationPortSetDispatchQueue(m->p->PowerPortRef, NULL);
10065#else
10066        CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode);
10067#endif
10068        // According to <http://developer.apple.com/qa/qa2004/qa1340.html>, a single call
10069        // to IORegisterForSystemPower creates *three* objects that need to be disposed individually:
10070        IODeregisterForSystemPower(&m->p->PowerNotifier);
10071        IOServiceClose            ( m->p->PowerConnection);
10072        IONotificationPortDestroy ( m->p->PowerPortRef);
10073        m->p->PowerConnection = 0;
10074    }
10075
10076    if (m->p->Store)
10077    {
10078#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
10079        if (!SCDynamicStoreSetDispatchQueue(m->p->Store, NULL))
10080            LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed");
10081#else
10082        CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode);
10083        CFRunLoopSourceInvalidate(m->p->StoreRLS);
10084        CFRelease(m->p->StoreRLS);
10085        m->p->StoreRLS = NULL;
10086#endif
10087        CFRelease(m->p->Store);
10088        m->p->Store    = NULL;
10089    }
10090
10091    if (m->p->PMRLS)
10092    {
10093        CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode);
10094        CFRunLoopSourceInvalidate(m->p->PMRLS);
10095        CFRelease(m->p->PMRLS);
10096        m->p->PMRLS = NULL;
10097    }
10098
10099    if (m->p->SysEventNotifier >= 0) { close(m->p->SysEventNotifier); m->p->SysEventNotifier = -1; }
10100
10101#if !NO_D2D
10102    if (D2DTerminate)
10103    {
10104        D2DStatus ds = D2DTerminate();
10105        if (ds != kD2DSuccess)
10106            LogMsg("D2DTerminate failed: %d", ds);
10107        else
10108            LogMsg("D2DTerminate succeeded");
10109    }
10110#endif // ! NO_D2D
10111
10112    mDNSs32 utc = mDNSPlatformUTC();
10113    MarkAllInterfacesInactive(m, utc);
10114    ClearInactiveInterfaces(m, utc);
10115    CloseSocketSet(&m->p->permanentsockets);
10116
10117#if APPLE_OSX_mDNSResponder
10118    // clean up tunnels
10119    while (m->TunnelClients)
10120    {
10121        ClientTunnel *cur = m->TunnelClients;
10122        LogInfo("mDNSPlatformClose: removing client tunnel %p %##s from list", cur, cur->dstname.c);
10123        if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q);
10124        AutoTunnelSetKeys(cur, mDNSfalse);
10125        m->TunnelClients = cur->next;
10126        freeL("ClientTunnel", cur);
10127    }
10128
10129    if (AnonymousRacoonConfig)
10130    {
10131        AnonymousRacoonConfig = mDNSNULL;
10132        LogInfo("mDNSPlatformClose: Deconfiguring autotunnel");
10133        (void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL);
10134    }
10135#endif // APPLE_OSX_mDNSResponder
10136}
10137
10138#if COMPILER_LIKES_PRAGMA_MARK
10139#pragma mark -
10140#pragma mark - General Platform Support Layer functions
10141#endif
10142
10143mDNSexport mDNSu32 mDNSPlatformRandomNumber(void)
10144{
10145    return(arc4random());
10146}
10147
10148mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000;
10149mDNSexport mDNSu32 mDNSPlatformClockDivisor = 0;
10150
10151mDNSexport mStatus mDNSPlatformTimeInit(void)
10152{
10153    // Notes: Typical values for mach_timebase_info:
10154    // tbi.numer = 1000 million
10155    // tbi.denom =   33 million
10156    // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds;
10157    //          numer  / denom = nanoseconds per hardware clock tick (e.g. 30);
10158    //          denom  / numer = hardware clock ticks per nanosecond (e.g. 0.033)
10159    // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333)
10160    // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds
10161    //
10162    // Arithmetic notes:
10163    // tbi.denom is at least 1, and not more than 2^32-1.
10164    // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t.
10165    // tbi.denom is at least 1, and not more than 2^32-1.
10166    // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9.
10167    // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz,
10168    // which is unlikely on any current or future Macintosh.
10169    // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz.
10170    // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code.
10171    struct mach_timebase_info tbi;
10172    kern_return_t result = mach_timebase_info(&tbi);
10173    if (result == KERN_SUCCESS) mDNSPlatformClockDivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer;
10174    return(result);
10175}
10176
10177mDNSexport mDNSs32 mDNSPlatformRawTime(void)
10178{
10179    if (mDNSPlatformClockDivisor == 0) { LogMsg("mDNSPlatformRawTime called before mDNSPlatformTimeInit"); return(0); }
10180
10181    static uint64_t last_mach_absolute_time = 0;
10182    //static uint64_t last_mach_absolute_time = 0x8000000000000000LL;	// Use this value for testing the alert display
10183    uint64_t this_mach_absolute_time = mach_absolute_time();
10184    if ((int64_t)this_mach_absolute_time - (int64_t)last_mach_absolute_time < 0)
10185    {
10186        LogMsg("mDNSPlatformRawTime: last_mach_absolute_time %08X%08X", last_mach_absolute_time);
10187        LogMsg("mDNSPlatformRawTime: this_mach_absolute_time %08X%08X", this_mach_absolute_time);
10188        // Update last_mach_absolute_time *before* calling NotifyOfElusiveBug()
10189        last_mach_absolute_time = this_mach_absolute_time;
10190        // Note: This bug happens all the time on 10.3
10191        NotifyOfElusiveBug("mach_absolute_time went backwards!",
10192                           "This error occurs from time to time, often on newly released hardware, "
10193                           "and usually the exact cause is different in each instance.\r\r"
10194                           "Please file a new Radar bug report with the title “mach_absolute_time went backwards” "
10195                           "and assign it to Radar Component “Kernel” Version “X”.");
10196    }
10197    last_mach_absolute_time = this_mach_absolute_time;
10198
10199    return((mDNSs32)(this_mach_absolute_time / mDNSPlatformClockDivisor));
10200}
10201
10202mDNSexport mDNSs32 mDNSPlatformUTC(void)
10203{
10204    return time(NULL);
10205}
10206
10207// Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves
10208mDNSexport void     mDNSPlatformLock   (const mDNS *const m) { (void)m; }
10209mDNSexport void     mDNSPlatformUnlock (const mDNS *const m) { (void)m; }
10210mDNSexport void     mDNSPlatformStrCopy(      void *dst, const void *src)              { strcpy((char *)dst, (char *)src); }
10211mDNSexport mDNSu32  mDNSPlatformStrLen (                 const void *src)              { return(strlen((char*)src)); }
10212mDNSexport void     mDNSPlatformMemCopy(      void *dst, const void *src, mDNSu32 len) { memcpy(dst, src, len); }
10213mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len) { return(memcmp(dst, src, len) == 0); }
10214mDNSexport int      mDNSPlatformMemCmp(const void *dst, const void *src, mDNSu32 len) { return(memcmp(dst, src, len)); }
10215mDNSexport void     mDNSPlatformMemZero(      void *dst,                  mDNSu32 len) { memset(dst, 0, len); }
10216mDNSexport void     mDNSPlatformQsort  (      void *base, int nel, int width, int (*compar)(const void *, const void *))
10217{
10218    return (qsort(base, nel, width, compar));
10219}
10220#if !(APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING)
10221mDNSexport void *   mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); }
10222#endif
10223mDNSexport void     mDNSPlatformMemFree    (void *mem)   { freeL("mDNSPlatformMemFree", mem); }
10224
10225mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
10226{
10227    if (allowSleep && m->p->IOPMAssertion)
10228    {
10229        LogInfo("%s Destroying NoIdleSleep power assertion", __FUNCTION__);
10230        IOPMAssertionRelease(m->p->IOPMAssertion);
10231        m->p->IOPMAssertion = 0;
10232    }
10233    else if (!allowSleep)
10234    {
10235#ifdef kIOPMAssertionTypeNoIdleSleep
10236        if (m->p->IOPMAssertion)
10237        {
10238            IOPMAssertionRelease(m->p->IOPMAssertion);
10239            m->p->IOPMAssertion = 0;
10240        }
10241
10242        CFStringRef assertionName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%d %s"), getprogname(), getpid(), reason ? reason : "");
10243        IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, assertionName ? assertionName : CFSTR("mDNSResponder"), &m->p->IOPMAssertion);
10244        if (assertionName) CFRelease(assertionName);
10245        LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__);
10246#endif
10247    }
10248}
10249
10250mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration)
10251{
10252    mDNSu32 ifindex;
10253
10254    // Sanity check
10255    ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue);
10256    if (ifindex <= 0)
10257    {
10258        LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex);
10259        return;
10260    }
10261    mDNSSendWakeupPacket(ifindex, EthAddr, IPAddr, iteration);
10262}
10263
10264mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID)
10265{
10266    NetworkInterfaceInfoOSX *info;
10267
10268    if (InterfaceID == mDNSInterface_P2P)
10269        return mDNStrue;
10270
10271    if (   (InterfaceID == mDNSInterface_Any)
10272        || (InterfaceID == mDNSInterfaceMark)
10273        || (InterfaceID == mDNSInterface_LocalOnly)
10274        || (InterfaceID == mDNSInterface_Unicast))
10275        return mDNSfalse;
10276
10277    info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID);
10278    if (info == NULL)
10279    {
10280        // this log message can print when operations are stopped on an interface that has gone away
10281        LogInfo("mDNSPlatformInterfaceIsD2D: Invalid interface index %d", InterfaceID);
10282        return mDNSfalse;
10283    }
10284
10285    return (mDNSBool) info->D2DInterface;
10286}
10287
10288// Filter records send over P2P (D2D) type interfaces
10289// Note that the terms P2P and D2D are used synonymously in the current code and comments.
10290mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
10291{
10292    // For an explicit match to a valid interface ID, return true.
10293    if (rr->resrec.InterfaceID == intf->InterfaceID)
10294        return mDNStrue;
10295
10296    // Only filtering records for D2D type interfaces, return true for all other interface types.
10297    if (!mDNSPlatformInterfaceIsD2D(intf->InterfaceID))
10298        return mDNStrue;
10299
10300    // If it's an AWDL interface the record must be explicitly marked to include AWDL.
10301    if (intf->InterfaceID == AWDLInterfaceID)
10302    {
10303        if (rr->ARType == AuthRecordAnyIncludeAWDL || rr->ARType == AuthRecordAnyIncludeAWDLandP2P)
10304            return mDNStrue;
10305        else
10306            return mDNSfalse;
10307    }
10308
10309    // Send record if it is explicitly marked to include all other P2P type interfaces.
10310    if (rr->ARType == AuthRecordAnyIncludeP2P || rr->ARType == AuthRecordAnyIncludeAWDLandP2P)
10311        return mDNStrue;
10312
10313    // Don't send the record over this interface.
10314    return mDNSfalse;
10315}
10316
10317// Filter questions send over P2P (D2D) type interfaces.
10318mDNSexport mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf)
10319{
10320    // For an explicit match to a valid interface ID, return true.
10321    if (q->InterfaceID == intf->InterfaceID)
10322        return mDNStrue;
10323
10324    // Only filtering questions for D2D type interfaces
10325    if (!mDNSPlatformInterfaceIsD2D(intf->InterfaceID))
10326        return mDNStrue;
10327
10328    // If it's an AWDL interface the question must be explicitly marked to include AWDL.
10329    if (intf->InterfaceID == AWDLInterfaceID)
10330    {
10331        if (q->flags & kDNSServiceFlagsIncludeAWDL)
10332            return mDNStrue;
10333        else
10334            return mDNSfalse;
10335    }
10336
10337    // Sent question if it is explicitly marked to include all other P2P type interfaces.
10338    if (q->flags & kDNSServiceFlagsIncludeP2P)
10339        return mDNStrue;
10340
10341    // Don't send the question over this interface.
10342    return mDNSfalse;
10343}
10344
10345// Returns true unless record was received over the AWDL interface and
10346// the question was not specific to the AWDL interface or did not specify kDNSServiceInterfaceIndexAny
10347// with the kDNSServiceFlagsIncludeAWDL flag set.
10348mDNSexport mDNSBool   mDNSPlatformValidRecordForQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
10349{
10350    if (!rr->InterfaceID || (rr->InterfaceID == q->InterfaceID))
10351        return mDNStrue;
10352
10353    if ((rr->InterfaceID == AWDLInterfaceID) && !(q->flags & kDNSServiceFlagsIncludeAWDL))
10354    {
10355        LogInfo("mDNSPlatformValidRecordForQuestion: Record recieved over AWDL not returned for %s %##s query",
10356                DNSTypeName(q->qtype), q->qname.c);
10357        return mDNSfalse;
10358    }
10359
10360    return mDNStrue;
10361}
10362
10363// formating time to RFC 4034 format
10364mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsize)
10365{
10366    struct tm tmTime;
10367    time_t t = (time_t)te;
10368    // Time since epoch : strftime takes "tm". Convert seconds to "tm" using
10369    // gmtime_r first and then use strftime
10370    gmtime_r(&t, &tmTime);
10371    strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime);
10372}
10373
10374mDNSexport mDNSs32 mDNSPlatformGetPID()
10375{
10376    return getpid();
10377}
10378
10379// Schedule a function asynchronously on the main queue
10380mDNSexport void mDNSPlatformDispatchAsync(mDNS *const m, void *context, AsyncDispatchFunc func)
10381{
10382    // KQueueLock/Unlock is used for two purposes
10383    //
10384    // 1. We can't be running along with the KQueue thread and hence acquiring the lock
10385    //    serializes the access to the "core"
10386    //
10387    // 2. KQueueUnlock also sends a message wake up the KQueue thread which in turn wakes
10388    //    up and calls udsserver_idle which schedules the messages across the uds socket.
10389    //    If "func" delivers something to the uds socket from the dispatch thread, it will
10390    //    not be delivered immediately if not for the Unlock.
10391    dispatch_async(dispatch_get_main_queue(), ^{
10392        KQueueLock(m);
10393        func(m, context);
10394        KQueueUnlock(m, "mDNSPlatformDispatchAsync");
10395#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
10396        // KQueueUnlock is a noop. Hence, we need to run kick off the idle loop
10397        // to handle any message that "func" might deliver.
10398        TriggerEventCompletion();
10399#endif
10400    });
10401}
10402
10403// definitions for device-info record construction
10404#define DEVINFO_MODEL       "model="
10405#define DEVINFO_MODEL_LEN   strlen(DEVINFO_MODEL)
10406
10407#define OSX_VER         "osxvers="
10408#define OSX_VER_LEN     strlen(OSX_VER)
10409#define VER_NUM_LEN     2  // 2 digits of version number added to base string
10410
10411// Bytes available in TXT record for model name after subtracting space for other
10412// fixed size strings and their length bytes.
10413#define MAX_MODEL_NAME_LEN   (256 - (DEVINFO_MODEL_LEN + 1) - (OSX_VER_LEN + VER_NUM_LEN + 1))
10414
10415// Initialize device-info TXT record contents and return total length of record data.
10416mDNSexport mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *ptr)
10417{
10418    mDNSu8 *bufferStart = ptr;
10419    mDNSu8 len = m->HIHardware.c[0] < MAX_MODEL_NAME_LEN ? m->HIHardware.c[0] : MAX_MODEL_NAME_LEN;
10420
10421    *ptr = DEVINFO_MODEL_LEN + len; // total length of DEVINFO_MODEL string plus the hardware name string
10422    ptr++;
10423    mDNSPlatformMemCopy(ptr, DEVINFO_MODEL, DEVINFO_MODEL_LEN);
10424    ptr += DEVINFO_MODEL_LEN;
10425    mDNSPlatformMemCopy(ptr, m->HIHardware.c + 1, len);
10426    ptr += len;
10427
10428    // only include this string for OSX
10429    if (OSXVers)
10430    {
10431        char    ver_num[VER_NUM_LEN + 1]; // version digits + null written by snprintf
10432        *ptr = OSX_VER_LEN + VER_NUM_LEN; // length byte
10433        ptr++;
10434        mDNSPlatformMemCopy(ptr, OSX_VER, OSX_VER_LEN);
10435        ptr += OSX_VER_LEN;
10436        // convert version number to ASCII, add 1 for terminating null byte written by snprintf()
10437        snprintf(ver_num, VER_NUM_LEN + 1, "%d", OSXVers);
10438        mDNSPlatformMemCopy(ptr, ver_num, VER_NUM_LEN);
10439        ptr += VER_NUM_LEN;
10440    }
10441
10442    return (ptr - bufferStart);
10443}
10444