#define LIST_ALL_INTERFACES 0
#define AAAA_OVER_V4 1
#define USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 0
#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above
#include "DNSCommon.h"
#include "uDNS.h"
#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
#include "dns_sd.h" // For mDNSInterface_LocalOnly etc.
#include "PlatformCommon.h"
#include "uds_daemon.h"
#include <stdio.h>
#include <stdarg.h> // For va_list support
#include <stdlib.h> // For arc4random
#include <net/if.h>
#include <net/if_types.h> // For IFT_ETHER
#include <net/if_dl.h>
#include <net/bpf.h> // For BIOCSETIF etc.
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/event.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <time.h> // platform support for UTC time
#include <arpa/inet.h> // for inet_aton
#include <pthread.h>
#include <netdb.h> // for getaddrinfo
#include <netinet/in.h> // For IP_RECVTTL
#ifndef IP_RECVTTL
#define IP_RECVTTL 24 // bool; receive reception TTL w/dgram
#endif
#include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
#include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
#include <netinet6/in6_var.h> // For IN6_IFF_NOTREADY etc.
#include <netinet6/nd6.h> // For ND6_INFINITE_LIFETIME etc.
#if TARGET_OS_EMBEDDED
#define NO_SECURITYFRAMEWORK 1
#define NO_CFUSERNOTIFICATION 1
#endif
#ifndef NO_SECURITYFRAMEWORK
#include <Security/SecureTransport.h>
#include <Security/Security.h>
#endif
#include <DebugServices.h>
#include "dnsinfo.h"
#define RUN_ON_PUMA_WITHOUT_IFADDRS 0
#if RUN_ON_PUMA_WITHOUT_IFADDRS
#include "mDNSMacOSXPuma.c"
#else
#include <ifaddrs.h>
#endif
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_queue_t queue);
#endif
#if USE_IOPMCOPYACTIVEPMPREFERENCES
#include <IOKit/ps/IOPowerSources.h>
#include <IOKit/ps/IOPowerSourcesPrivate.h>
#endif
#include <mach/mach_error.h>
#include <mach/mach_port.h>
#include <mach/mach_time.h>
#include "helper.h"
#include "P2PPacketFilter.h"
#include <asl.h>
#include <SystemConfiguration/SCDynamicStorePrivate.h>
#if APPLE_OSX_mDNSResponder
#include <DeviceToDeviceManager/DeviceToDeviceManager.h>
#include <AWACS.h>
#if ! NO_D2D
D2DStatus D2DInitialize(CFRunLoopRef runLoop, D2DServiceCallback serviceCallback, void* userData) __attribute__((weak_import));
D2DStatus D2DTerminate() __attribute__((weak_import));
D2DStatus D2DStartAdvertisingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import));
D2DStatus D2DStopAdvertisingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import));
D2DStatus D2DStartBrowsingForKey(const Byte *key, const size_t keySize) __attribute__((weak_import));
D2DStatus D2DStopBrowsingForKey(const Byte *key, const size_t keySize) __attribute__((weak_import));
void D2DStartResolvingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import));
void D2DStopResolvingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import));
D2DStatus D2DRetain(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
#define CHECK_D2D_FUNCTION(X) if (X)
#endif // ! NO_D2D
#else
#define NO_D2D 1
#define NO_AWACS 1
#endif // APPLE_OSX_mDNSResponder
#define kInterfaceSpecificOption "interface="
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Globals
#endif
mDNSexport int OfferSleepProxyService = 0;
mDNSexport int DisableSleepProxyClient = 0;
mDNSexport int UseInternalSleepProxy = 1;
mDNSBool DisableInboundRelayConnection = mDNSfalse;
mDNSexport int OSXVers, iOSVers;
mDNSexport int KQueueFD;
#ifndef NO_SECURITYFRAMEWORK
static CFArrayRef ServerCerts;
OSStatus SSLSetAllowAnonymousCiphers(SSLContextRef context, Boolean enable);
#endif
static CFStringRef NetworkChangedKey_IPv4;
static CFStringRef NetworkChangedKey_IPv6;
static CFStringRef NetworkChangedKey_Hostnames;
static CFStringRef NetworkChangedKey_Computername;
static CFStringRef NetworkChangedKey_DNS;
static CFStringRef NetworkChangedKey_DynamicDNS = CFSTR("Setup:/Network/DynamicDNS");
static CFStringRef NetworkChangedKey_BackToMyMac = CFSTR("Setup:/Network/BackToMyMac");
static CFStringRef NetworkChangedKey_BTMMConnectivity = CFSTR("State:/Network/Connectivity");
static CFStringRef NetworkChangedKey_PowerSettings = CFSTR("State:/IOKit/PowerManagement/CurrentSettings");
static char HINFO_HWstring_buffer[32];
static char *HINFO_HWstring = "Device";
static int HINFO_HWstring_prefixlen = 6;
mDNSexport int WatchDogReportingThreshold = 250;
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
dispatch_queue_t SSLqueue;
#endif
#if APPLE_OSX_mDNSResponder
static mDNSu8 SPMetricPortability = 99;
static mDNSu8 SPMetricMarginalPower = 99;
static mDNSu8 SPMetricTotalPower = 99;
mDNSexport domainname ActiveDirectoryPrimaryDomain;
mDNSexport int ActiveDirectoryPrimaryDomainLabelCount;
mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer;
#endif // APPLE_OSX_mDNSResponder
const char btmmprefix[] = "btmmdns:";
const char dnsprefix[] = "dns:";
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - D2D Support
#endif
#if ! NO_D2D
static const mDNSu8 compression_packet_v1 = 0x01;
static DNSMessage compression_base_msg = { { {{0}}, {{0}}, 2, 0, 0, 0 }, "\x04_tcp\x05local\x00\x00\x0C\x00\x01\x04_udp\xC0\x11\x00\x0C\x00\x01" };
static mDNSu8 *const compression_limit = (mDNSu8 *) &compression_base_msg + sizeof(DNSMessage);
static mDNSu8 *const compression_lhs = (mDNSu8 *const) compression_base_msg.data + 27;
mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len);
static ARListElem *D2DRecords = NULL;
typedef struct D2DBrowseListElem
{
struct D2DBrowseListElem *next;
domainname name;
mDNSu16 type;
unsigned int refCount;
} D2DBrowseListElem;
D2DBrowseListElem* D2DBrowseList = NULL;
mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
{
ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
ptr[1] = (mDNSu8)((val ) & 0xFF);
return ptr + sizeof(mDNSu16);
}
mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
{
ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
ptr[2] = (mDNSu8)((val >> 8) & 0xFF);
ptr[3] = (mDNSu8)((val ) & 0xFF);
return ptr + sizeof(mDNSu32);
}
mDNSlocal void DomainnameToLower(const domainname * const in, domainname * const out)
{
const mDNSu8 * const start = (const mDNSu8 * const)in;
mDNSu8 *ptr = (mDNSu8*)start;
while(*ptr)
{
mDNSu8 c = *ptr;
out->c[ptr-start] = *ptr;
ptr++;
for (;c;c--,ptr++) out->c[ptr-start] = mDNSIsUpperCase(*ptr) ? (*ptr - 'A' + 'a') : *ptr;
}
out->c[ptr-start] = *ptr;
}
mDNSlocal mStatus DNSNameCompressionParseBytes(mDNS *const m, const mDNSu8 *const lhs, const mDNSu16 lhs_len, const mDNSu8 *const rhs, const mDNSu16 rhs_len, AuthRecord *rr)
{
if (mDNS_LoggingEnabled)
{
LogInfo("%s", __func__);
LogInfo(" Static Bytes: ");
PrintHex((mDNSu8*)&compression_base_msg, compression_lhs - (mDNSu8*)&compression_base_msg);
}
mDNSu8 *ptr = compression_lhs;
if (ptr + lhs_len - 7 + rhs_len >= compression_limit) return mStatus_NoMemoryErr;
mDNSPlatformMemCopy(ptr, lhs, lhs_len);
ptr += lhs_len - 1;
if (*ptr != compression_packet_v1) return mStatus_Incompatible;
ptr = putVal16(ptr, kDNSClass_IN | kDNSClass_UniqueRRSet);
ptr = putVal32(ptr, 120);
ptr = putVal16(ptr, rhs_len);
mDNSPlatformMemCopy(ptr, rhs, rhs_len);
ptr += rhs_len;
if (mDNS_LoggingEnabled)
{
LogInfo(" Our Bytes %d: ", __LINE__);
PrintHex(compression_lhs, ptr - compression_lhs);
}
ptr = (mDNSu8 *) GetLargeResourceRecord(m, &compression_base_msg, compression_lhs, ptr, mDNSInterface_Any, kDNSRecordTypePacketAns, &m->rec);
if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative)
{ LogMsg("DNSNameCompressionParseBytes: failed to get large RR"); m->rec.r.resrec.RecordType = 0; return mStatus_UnknownErr; }
else LogInfo("DNSNameCompressionParseBytes: got rr: %s", CRDisplayString(m, &m->rec.r));
mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, AuthRecordP2P, FreeD2DARElemCallback, NULL);
AssignDomainName(&rr->namestorage, &m->rec.namestorage);
rr->resrec.rdlength = m->rec.r.resrec.rdlength;
rr->resrec.rdata->MaxRDLength = m->rec.r.resrec.rdlength;
mDNSPlatformMemCopy(rr->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, m->rec.r.resrec.rdlength);
rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
SetNewRData(&rr->resrec, mDNSNULL, 0);
m->rec.r.resrec.RecordType = 0;
return mStatus_NoError;
}
mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainname const *typeDomain, DNS_TypeValues qtype)
{
mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain);
if (!ptr) return ptr;
*ptr = (qtype >> 8) & 0xff;
ptr += 1;
*ptr = qtype & 0xff;
ptr += 1;
*ptr = compression_packet_v1;
return ptr + 1;
}
mDNSlocal mDNSu8 * DNSNameCompressionBuildRHS(mDNSu8 *start, const ResourceRecord *const resourceRecord)
{
return putRData(&compression_base_msg, start, compression_limit, resourceRecord);
}
mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len)
{
mDNSu8 *end = data + len;
char buffer[49] = {0};
char *bufend = buffer + sizeof(buffer);
while(data<end)
{
char *ptr = buffer;
for(; data < end && ptr < bufend-1; ptr+=3,data++)
mDNS_snprintf(ptr, bufend - ptr, "%02X ", *data);
LogInfo(" %s", buffer);
}
}
mDNSlocal void PrintHelper(const char *const tag, mDNSu8 *lhs, mDNSu16 lhs_len, mDNSu8 *rhs, mDNSu16 rhs_len)
{
if (!mDNS_LoggingEnabled) return;
LogInfo("%s:", tag);
LogInfo(" LHS: ");
PrintHex(lhs, lhs_len);
if (!rhs) return;
LogInfo(" RHS: ");
PrintHex(rhs, rhs_len);
}
mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
{
(void)m; if (result == mStatus_MemFree)
{
ARListElem **ptr = &D2DRecords;
ARListElem *tmp;
while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next;
if (!*ptr) { LogMsg("FreeD2DARElemCallback: Could not find in D2DRecords: %s", ARDisplayString(m, rr)); return; }
LogInfo("FreeD2DARElemCallback: Found in D2DRecords: %s", ARDisplayString(m, rr));
tmp = *ptr;
*ptr = (*ptr)->next;
mDNSPlatformMemFree(tmp);
}
}
mDNSlocal void xD2DClearCache(mDNS *const m, const domainname *regType)
{
ARListElem *ptr = D2DRecords;
for ( ; ptr ; ptr = ptr->next)
{
if (SameDomainName(&ptr->ar.namestorage, regType))
{
char buffer[MAX_ESCAPED_DOMAIN_NAME];
mDNS_Deregister(m, &ptr->ar);
ConvertDomainNameToCString(regType, buffer);
LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", buffer);
}
}
}
mDNSlocal D2DBrowseListElem ** D2DFindInBrowseList(const domainname *const name, mDNSu16 type)
{
D2DBrowseListElem **ptr = &D2DBrowseList;
for ( ; *ptr; ptr = &(*ptr)->next)
if ((*ptr)->type == type && SameDomainName(&(*ptr)->name, name))
break;
return ptr;
}
mDNSlocal unsigned int D2DBrowseListRefCount(const domainname *const name, mDNSu16 type)
{
D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type);
return *ptr ? (*ptr)->refCount : 0;
}
mDNSlocal void D2DBrowseListRetain(const domainname *const name, mDNSu16 type)
{
D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type);
if (!*ptr)
{
*ptr = mDNSPlatformMemAllocate(sizeof(**ptr));
mDNSPlatformMemZero(*ptr, sizeof(**ptr));
(*ptr)->type = type;
AssignDomainName(&(*ptr)->name, name);
}
(*ptr)->refCount += 1;
LogInfo("D2DBrowseListRetain: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount);
}
mDNSlocal void D2DBrowseListRelease(const domainname *const name, mDNSu16 type)
{
D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type);
if (!*ptr) { LogMsg("D2DBrowseListRelease: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return; }
(*ptr)->refCount -= 1;
LogInfo("D2DBrowseListRelease: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount);
if (!(*ptr)->refCount)
{
D2DBrowseListElem *tmp = *ptr;
*ptr = (*ptr)->next;
mDNSPlatformMemFree(tmp);
}
}
mDNSlocal mStatus xD2DParse(mDNS *const m, const mDNSu8 * const lhs, const mDNSu16 lhs_len, const mDNSu8 * const rhs, const mDNSu16 rhs_len, AuthRecord *rr)
{
if (*(lhs + (lhs_len - 1)) == compression_packet_v1)
return DNSNameCompressionParseBytes(m, lhs, lhs_len, rhs, rhs_len, rr);
else
return mStatus_Incompatible;
}
mDNSlocal void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
{
(void)transportType; (void)instanceHandle;
if (result == kD2DSuccess)
{
if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DAddToCache: NULL Byte * passed in or length == 0"); return; }
mStatus err;
ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(ARListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData)));
if (ptr == NULL) { LogMsg("xD2DAddToCache: memory allocation failure"); return; }
err = xD2DParse(m, (const mDNSu8 * const)key, (const mDNSu16)keySize, (const mDNSu8 * const)value, (const mDNSu16)valueSize, &ptr->ar);
if (err)
{
LogMsg("xD2DAddToCache: xD2DParse returned error: %d", err);
PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize);
mDNSPlatformMemFree(ptr);
return;
}
err = mDNS_Register(m, &ptr->ar);
if (err)
{
LogMsg("xD2DAddToCache: mDNS_Register returned error %d for %s", err, ARDisplayString(m, &ptr->ar));
mDNSPlatformMemFree(ptr);
return;
}
LogInfo("xD2DAddToCache: mDNS_Register succeeded for %s", ARDisplayString(m, &ptr->ar));
ptr->next = D2DRecords;
D2DRecords = ptr;
}
else
LogMsg("xD2DAddToCache: Unexpected result %d", result);
}
mDNSlocal ARListElem * xD2DFindInList(mDNS *const m, const Byte *const key, const size_t keySize, const Byte *const value, const size_t valueSize)
{
ARListElem *ptr = D2DRecords;
ARListElem *arptr;
if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DFindInList: NULL Byte * passed in or length == 0"); return NULL; }
arptr = mDNSPlatformMemAllocate(sizeof(ARListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData)));
if (arptr == NULL) { LogMsg("xD2DFindInList: memory allocation failure"); return NULL; }
if (xD2DParse(m, (const mDNSu8 *const)key, (const mDNSu16)keySize, (const mDNSu8 *const)value, (const mDNSu16)valueSize, &arptr->ar) != mStatus_NoError)
{
LogMsg("xD2DFindInList: xD2DParse failed for key: %p (%u) value: %p (%u)", key, keySize, value, valueSize);
mDNSPlatformMemFree(arptr);
return NULL;
}
while (ptr)
{
if (IdenticalResourceRecord(&arptr->ar.resrec, &ptr->ar.resrec)) break;
ptr = ptr->next;
}
if (!ptr) LogMsg("xD2DFindInList: Could not find in D2DRecords: %s", ARDisplayString(m, &arptr->ar));
mDNSPlatformMemFree(arptr);
return ptr;
}
mDNSlocal void xD2DRemoveFromCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
{
(void)transportType; (void)instanceHandle;
if (result == kD2DSuccess)
{
ARListElem *ptr = xD2DFindInList(m, key, keySize, value, valueSize);
if (ptr)
{
LogInfo("xD2DRemoveFromCache: Remove from cache: %s", ARDisplayString(m, &ptr->ar));
mDNS_Deregister(m, &ptr->ar);
}
}
else
LogMsg("xD2DRemoveFromCache: Unexpected result %d", result);
}
mDNSlocal void xD2DServiceResolved(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
{
(void)m;
(void)key;
(void)keySize;
(void)value;
(void)valueSize;
if (result == kD2DSuccess)
{
LogInfo("xD2DServiceResolved: Starting up PAN connection for %p", instanceHandle);
CHECK_D2D_FUNCTION(D2DRetain) D2DRetain(instanceHandle, transportType);
}
else LogMsg("xD2DServiceResolved: Unexpected result %d", result);
}
mDNSlocal void xD2DRetainHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
{
(void)m;
(void)instanceHandle;
(void)transportType;
(void)key;
(void)keySize;
(void)value;
(void)valueSize;
if (result == kD2DSuccess) LogInfo("xD2DRetainHappened: Opening up PAN connection for %p", instanceHandle);
else LogMsg("xD2DRetainHappened: Unexpected result %d", result);
}
mDNSlocal void xD2DReleaseHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
{
(void)m;
(void)instanceHandle;
(void)transportType;
(void)key;
(void)keySize;
(void)value;
(void)valueSize;
if (result == kD2DSuccess) LogInfo("xD2DReleaseHappened: Closing PAN connection for %p", instanceHandle);
else LogMsg("xD2DReleaseHappened: Unexpected result %d", result);
}
mDNSlocal void xD2DServiceCallback(D2DServiceEvent event, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize, void *userData)
{
mDNS *m = (mDNS *) userData;
const char *eventString = "unknown";
KQueueLock(m);
if (keySize > 0xFFFF) LogMsg("xD2DServiceCallback: keySize too large: %u", keySize);
if (valueSize > 0xFFFF) LogMsg("xD2DServiceCallback: valueSize too large: %u", valueSize);
switch (event)
{
case D2DServiceFound:
eventString = "D2DServiceFound";
break;
case D2DServiceLost:
eventString = "D2DServiceLost";
break;
case D2DServiceResolved:
eventString = "D2DServiceResolved";
break;
case D2DServiceRetained:
eventString = "D2DServiceRetained";
break;
case D2DServiceReleased:
eventString = "D2DServiceReleased";
break;
default:
break;
}
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);
PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize);
switch (event)
{
case D2DServiceFound:
xD2DAddToCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
break;
case D2DServiceLost:
xD2DRemoveFromCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
break;
case D2DServiceResolved:
xD2DServiceResolved(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
break;
case D2DServiceRetained:
xD2DRetainHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
break;
case D2DServiceReleased:
xD2DReleaseHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize);
break;
default:
break;
}
KQueueUnlock(m, "xD2DServiceCallback");
}
mDNSexport void external_start_browsing_for_service(mDNS *const m, const domainname *const typeDomain, DNS_TypeValues qtype)
{
(void)m;
domainname lower;
if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA)
{
LogInfo("external_start_browsing_for_service: ignoring address record");
return;
}
DomainnameToLower(typeDomain, &lower);
if (!D2DBrowseListRefCount(&lower, qtype))
{
LogInfo("external_start_browsing_for_service: Starting browse for: %##s %s", lower.c, DNSTypeName(qtype));
mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype);
PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0);
CHECK_D2D_FUNCTION(D2DStartBrowsingForKey) D2DStartBrowsingForKey(compression_lhs, end - compression_lhs);
}
D2DBrowseListRetain(&lower, qtype);
}
mDNSexport void external_stop_browsing_for_service(mDNS *const m, const domainname *const typeDomain, DNS_TypeValues qtype)
{
domainname lower;
if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA)
{
LogInfo("external_stop_browsing_for_service: ignoring address record");
return;
}
DomainnameToLower(typeDomain, &lower);
D2DBrowseListRelease(&lower, qtype);
if (!D2DBrowseListRefCount(&lower, qtype))
{
LogInfo("external_stop_browsing_for_service: Stopping browse for: %##s %s", lower.c, DNSTypeName(qtype));
mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype);
PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0);
CHECK_D2D_FUNCTION(D2DStopBrowsingForKey) D2DStopBrowsingForKey(compression_lhs, end - compression_lhs);
xD2DClearCache(m, &lower);
}
}
mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord)
{
domainname lower;
mDNSu8 *rhs = NULL;
mDNSu8 *end = NULL;
DomainnameToLower(resourceRecord->name, &lower);
LogInfo("external_start_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord));
if (resourceRecord->rrtype == kDNSType_SRV)
mDNSInitPacketFilter();
if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA)
{
LogInfo("external_start_advertising_service: ignoring address record");
return;
}
rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype);
end = DNSNameCompressionBuildRHS(rhs, resourceRecord);
PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
CHECK_D2D_FUNCTION(D2DStartAdvertisingPair) D2DStartAdvertisingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs);
}
mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord)
{
domainname lower;
mDNSu8 *rhs = NULL;
mDNSu8 *end = NULL;
DomainnameToLower(resourceRecord->name, &lower);
LogInfo("external_stop_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord));
if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA)
{
LogInfo("external_stop_advertising_service: ignoring address record");
return;
}
rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype);
end = DNSNameCompressionBuildRHS(rhs, resourceRecord);
PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
CHECK_D2D_FUNCTION(D2DStopAdvertisingPair) D2DStopAdvertisingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs);
}
mDNSexport void external_start_resolving_service(const domainname *const fqdn)
{
domainname lower;
mDNSu8 *rhs = NULL;
mDNSu8 *end = NULL;
DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
LogInfo("external_start_resolving_service: %##s", fqdn->c);
rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR);
end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn);
PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
CHECK_D2D_FUNCTION(D2DStartResolvingPair) D2DStartResolvingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs);
}
mDNSexport void external_stop_resolving_service(const domainname *const fqdn)
{
domainname lower;
mDNSu8 *rhs = NULL;
mDNSu8 *end = NULL;
DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
LogInfo("external_stop_resolving_service: %##s", fqdn->c);
rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR);
end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn);
PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
CHECK_D2D_FUNCTION(D2DStopResolvingPair) D2DStopResolvingPair(compression_lhs, rhs - compression_lhs, rhs, end - rhs);
}
#elif APPLE_OSX_mDNSResponder
mDNSexport void external_start_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype) { (void)m; (void)type; (void)qtype; }
mDNSexport void external_stop_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype) { (void)m; (void)type; (void)qtype; }
mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord) { (void)resourceRecord; }
mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord) { (void)resourceRecord; }
mDNSexport void external_start_resolving_service(const domainname *const fqdn) { (void)fqdn; }
mDNSexport void external_stop_resolving_service(const domainname *const fqdn) { (void)fqdn; }
#endif // ! NO_D2D
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Utility Functions
#endif
#define MulticastInterface(i) (((i)->ifa_flags & IFF_MULTICAST) && !((i)->ifa_flags & IFF_POINTOPOINT))
mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg) {
static int notifyCount = 0;
if (notifyCount) return;
if ((mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return;
#if !ForceAlerts
{
extern mDNS mDNSStorage;
NetworkInterfaceInfoOSX *i;
for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && i->ifinfo.ip.ip.v4.b[0] == 17)
break;
if (!i) return; }
#endif
LogMsg("%s", title);
LogMsg("%s", msg);
notifyCount++;
#ifndef NO_CFUSERNOTIFICATION
mDNSNotify(title, msg);
#endif
}
mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh)
{
static struct ifaddrs *ifa = NULL;
if (refresh && ifa)
{
freeifaddrs(ifa);
ifa = NULL;
}
if (ifa == NULL) getifaddrs(&ifa);
return ifa;
}
mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const char *ifname, int type)
{
NetworkInterfaceInfoOSX *i;
for (i = m->p->InterfaceList; i; i = i->next)
if (i->Exists && !strcmp(i->ifinfo.ifname, ifname) &&
((type == AF_UNSPEC ) ||
(type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) ||
(type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6))) return(i);
return(NULL);
}
mDNSlocal int myIfIndexToName(u_short ifindex, char *name)
{
struct ifaddrs *ifa;
for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next)
if (ifa->ifa_addr->sa_family == AF_LINK)
if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == ifindex)
{ strlcpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; }
return -1;
}
mDNSexport NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex)
{
mDNSu32 scope_id = (mDNSu32)(uintptr_t)ifindex;
NetworkInterfaceInfoOSX *i;
for (i = m->p->InterfaceList; i; i = i->next)
if (i->Registered && i->scope_id == scope_id) return(i);
return mDNSNULL;
}
mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex)
{
if (ifindex == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly);
if (ifindex == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P);
if (ifindex == kDNSServiceInterfaceIndexAny ) return(mDNSNULL);
NetworkInterfaceInfoOSX* ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex);
if (!ifi)
{
LogInfo("mDNSPlatformInterfaceIDfromInterfaceIndex: InterfaceID for interface index %d not found; Updating interface list", ifindex);
mDNSMacOSXNetworkChanged(m);
ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex);
}
if (!ifi) return(mDNSNULL);
return(ifi->ifinfo.InterfaceID);
}
mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange)
{
NetworkInterfaceInfoOSX *i;
if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly);
if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P);
if (id == mDNSInterface_Any ) return(0);
mDNSu32 scope_id = (mDNSu32)(uintptr_t)id;
for (i = m->p->InterfaceList; i; i = i->next)
if (i->scope_id == scope_id) return(i->scope_id);
if (suppressNetworkChange) return scope_id;
LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id);
mDNSMacOSXNetworkChanged(m);
for (i = m->p->InterfaceList; i; i = i->next)
if (i->scope_id == scope_id) return(i->scope_id);
return(0);
}
#if APPLE_OSX_mDNSResponder
mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...)
{
if (OSXVers < OSXVers_10_6_SnowLeopard) return;
static char buffer[512];
aslmsg asl_msg = asl_new(ASL_TYPE_MSG);
if (!asl_msg) { LogMsg("mDNSASLLog: asl_new failed"); return; }
if (uuid)
{
char uuidStr[37];
uuid_unparse(*uuid, uuidStr);
asl_set (asl_msg, "com.apple.message.uuid", uuidStr);
}
static char domainBase[] = "com.apple.mDNSResponder.%s";
mDNS_snprintf (buffer, sizeof(buffer), domainBase, subdomain);
asl_set (asl_msg, "com.apple.message.domain", buffer);
if (result) asl_set(asl_msg, "com.apple.message.result", result);
if (signature) asl_set(asl_msg, "com.apple.message.signature", signature);
va_list ptr;
va_start(ptr,fmt);
mDNS_vsnprintf(buffer, sizeof(buffer), fmt, ptr);
va_end(ptr);
int old_filter = asl_set_filter(NULL,ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
asl_log(NULL, asl_msg, ASL_LEVEL_DEBUG, "%s", buffer);
asl_set_filter(NULL, old_filter);
asl_free(asl_msg);
}
#endif // APPLE_OSX_mDNSResponder
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - UDP & TCP send & receive
#endif
mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr)
{
mDNSBool result = mDNSfalse;
SCNetworkConnectionFlags flags;
SCNetworkReachabilityRef ReachRef = NULL;
ReachRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, addr);
if (!ReachRef) { LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithAddress"); goto end; }
if (!SCNetworkReachabilityGetFlags(ReachRef, &flags)) { LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags"); goto end; }
result = flags & kSCNetworkFlagsConnectionRequired;
end:
if (ReachRef) CFRelease(ReachRef);
return result;
}
mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end,
mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstPort)
{
NetworkInterfaceInfoOSX *info = mDNSNULL;
struct sockaddr_storage to;
int s = -1, err;
mStatus result = mStatus_NoError;
if (InterfaceID)
{
info = IfindexToInterfaceInfoOSX(m, InterfaceID);
if (info == NULL)
{
LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID);
return mStatus_BadParamErr;
}
}
char *ifa_name = InterfaceID ? info->ifinfo.ifname : "unicast";
if (dst->type == mDNSAddrType_IPv4)
{
struct sockaddr_in *sin_to = (struct sockaddr_in*)&to;
sin_to->sin_len = sizeof(*sin_to);
sin_to->sin_family = AF_INET;
sin_to->sin_port = dstPort.NotAnInteger;
sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger;
s = (src ? src->ss : m->p->permanentsockets).sktv4;
if (info) {
if (!mDNSAddrIsDNSMulticast(dst))
{
#ifdef IP_BOUND_IF
if (info->scope_id == 0)
LogInfo("IP_BOUND_IF socket option not set -- info %p (%s) scope_id is zero", info, ifa_name);
else
setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id));
#else
{
static int displayed = 0;
if (displayed < 1000)
{
displayed++;
LogInfo("IP_BOUND_IF socket option not defined -- cannot specify interface for unicast packets");
}
}
#endif
}
else
#ifdef IP_MULTICAST_IFINDEX
{
err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IFINDEX, &info->scope_id, sizeof(info->scope_id));
if (err < 0)
{
if (errno != ENOPROTOOPT) LogInfo("mDNSPlatformSendUDP: setsockopt: IP_MUTLTICAST_IFINDEX returned %d", errno);
err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr));
if (err < 0 && !m->p->NetworkChanged)
LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno));
}
}
#else
{
err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr));
if (err < 0 && !m->p->NetworkChanged)
LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno));
}
#endif
}
}
#ifndef NO_IPV6
else if (dst->type == mDNSAddrType_IPv6)
{
struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to;
sin6_to->sin6_len = sizeof(*sin6_to);
sin6_to->sin6_family = AF_INET6;
sin6_to->sin6_port = dstPort.NotAnInteger;
sin6_to->sin6_flowinfo = 0;
sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6;
sin6_to->sin6_scope_id = info ? info->scope_id : 0;
s = (src ? src->ss : m->p->permanentsockets).sktv6;
if (info && mDNSAddrIsDNSMulticast(dst)) {
err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &info->scope_id, sizeof(info->scope_id));
if (err < 0)
{
char name[IFNAMSIZ];
if (if_indextoname(info->scope_id, name) != NULL)
LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno));
else
LogInfo("setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface", info->scope_id);
}
}
}
#endif
else
{
LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!");
#if ForceAlerts
*(long*)0 = 0;
#endif
return mStatus_BadParamErr;
}
if (s >= 0)
verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %p %5s/%ld to %#a:%d skt %d",
InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s);
else
verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %p %5s/%ld (socket of this type not available)",
InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort));
if (s < 0) return(mStatus_Invalid);
err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len);
if (err < 0)
{
static int MessageCount = 0;
if (!mDNSAddressIsAllDNSLinkGroup(dst))
if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr);
if (errno == EHOSTUNREACH && (mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(mStatus_TransientErr);
if (errno == EADDRNOTAVAIL && m->p->NetworkChanged) return(mStatus_TransientErr);
if (MessageCount < 1000)
{
MessageCount++;
if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN)
LogInfo("mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu",
s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow));
else
LogMsg("mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu",
s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow));
}
result = mStatus_UnknownErr;
}
#ifdef IP_BOUND_IF
if (dst->type == mDNSAddrType_IPv4 && info && !mDNSAddrIsDNSMulticast(dst))
{
static const mDNSu32 ifindex = 0;
setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex));
}
#endif
return(result);
}
mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl)
{
static unsigned int numLogMessages = 0;
struct iovec databuffers = { (char *)buffer, max };
struct msghdr msg;
ssize_t n;
struct cmsghdr *cmPtr;
char ancillary[1024];
*ttl = 255;
msg.msg_name = (caddr_t)from;
msg.msg_namelen = *fromlen;
msg.msg_iov = &databuffers;
msg.msg_iovlen = 1;
msg.msg_control = (caddr_t)&ancillary;
msg.msg_controllen = sizeof(ancillary);
msg.msg_flags = 0;
n = recvmsg(s, &msg, 0);
if (n<0)
{
if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned error %d errno %d", s, n, errno);
return(-1);
}
if (msg.msg_controllen < (int)sizeof(struct cmsghdr))
{
if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu",
s, n, msg.msg_controllen, sizeof(struct cmsghdr));
return(-1);
}
if (msg.msg_flags & MSG_CTRUNC)
{
if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s);
return(-1);
}
*fromlen = msg.msg_namelen;
for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr))
{
if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR)
{
dstaddr->type = mDNSAddrType_IPv4;
dstaddr->ip.v4 = *(mDNSv4Addr*)CMSG_DATA(cmPtr);
}
if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF)
{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr);
if (sdl->sdl_nlen < IF_NAMESIZE)
{
mDNSPlatformMemCopy(ifname, sdl->sdl_data, sdl->sdl_nlen);
ifname[sdl->sdl_nlen] = 0;
}
}
if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL)
*ttl = *(u_char*)CMSG_DATA(cmPtr);
if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO)
{
struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr);
dstaddr->type = mDNSAddrType_IPv6;
dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr;
myIfIndexToName(ip6_info->ipi6_ifindex, ifname);
}
if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT)
*ttl = *(int*)CMSG_DATA(cmPtr);
}
return(n);
}
mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context)
{
KQSocketSet *const ss = (KQSocketSet *)context;
mDNS *const m = ss->m;
int err = 0, count = 0, closed = 0;
if (filter != EVFILT_READ)
LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter, EVFILT_READ);
if (s1 != ss->sktv4
#ifndef NO_IPV6
&& s1 != ss->sktv6
#endif
)
{
LogMsg("myKQSocketCallBack: native socket %d", s1);
LogMsg("myKQSocketCallBack: sktv4 %d", ss->sktv4);
#ifndef NO_IPV6
LogMsg("myKQSocketCallBack: sktv6 %d", ss->sktv6);
#endif
}
while (!closed)
{
mDNSAddr senderAddr, destAddr;
mDNSIPPort senderPort;
struct sockaddr_storage from;
size_t fromlen = sizeof(from);
char packetifname[IF_NAMESIZE] = "";
mDNSu8 ttl;
err = myrecvfrom(s1, &m->imsg, sizeof(m->imsg), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl);
if (err < 0) break;
count++;
if (from.ss_family == AF_INET)
{
struct sockaddr_in *s = (struct sockaddr_in*)&from;
senderAddr.type = mDNSAddrType_IPv4;
senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr;
senderPort.NotAnInteger = s->sin_port;
}
else if (from.ss_family == AF_INET6)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from;
senderAddr.type = mDNSAddrType_IPv6;
senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr;
senderPort.NotAnInteger = sin6->sin6_port;
}
else
{
LogMsg("myKQSocketCallBack from is unknown address family %d", from.ss_family);
return;
}
mDNSInterfaceID InterfaceID = mDNSNULL;
NetworkInterfaceInfoOSX *intf = m->p->InterfaceList;
while (intf && strcmp(intf->ifinfo.ifname, packetifname)) intf = intf->next;
if (intf) InterfaceID = intf->ifinfo.InterfaceID;
else if (mDNSAddrIsDNSMulticast(&destAddr)) continue;
ss->closeFlag = &closed;
mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID);
if (!closed) ss->closeFlag = mDNSNULL;
}
if (err < 0 && (errno != EWOULDBLOCK || count == 0))
{
static unsigned int numLogMessages = 0;
int save_errno = errno;
int so_error = -1;
int so_nread = -1;
int fionread = -1;
socklen_t solen = sizeof(int);
fd_set readfds;
struct timeval timeout;
int selectresult;
FD_ZERO(&readfds);
FD_SET(s1, &readfds);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
selectresult = select(s1+1, &readfds, NULL, NULL, &timeout);
if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1)
LogMsg("myKQSocketCallBack getsockopt(SO_ERROR) error %d", errno);
if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1)
LogMsg("myKQSocketCallBack getsockopt(SO_NREAD) error %d", errno);
if (ioctl(s1, FIONREAD, &fionread) == -1)
LogMsg("myKQSocketCallBack ioctl(FIONREAD) error %d", errno);
if (numLogMessages++ < 100)
LogMsg("myKQSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d",
s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count);
if (numLogMessages > 5)
NotifyOfElusiveBug("Flaw in Kernel (select/recvfrom mismatch)",
"Congratulations, you've reproduced an elusive bug.\r"
"Please contact the current assignee of <rdar://problem/3375328>.\r"
"Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r"
"If possible, please leave your machine undisturbed so that someone can come to investigate the problem.");
sleep(1); }
}
typedef enum
{
handshake_required,
handshake_in_progress,
handshake_completed,
handshake_to_be_closed
} handshakeStatus;
struct TCPSocket_struct
{
TCPSocketFlags flags; TCPConnectionCallback callback;
int fd;
KQueueEntry *kqEntry;
KQSocketSet ss;
#ifndef NO_SECURITYFRAMEWORK
SSLContextRef tlsContext;
pthread_t handshake_thread;
#endif
domainname hostname;
void *context;
mDNSBool setup;
mDNSBool connected;
handshakeStatus handshake;
mDNS *m; mStatus err;
};
mDNSlocal void doTcpSocketCallback(TCPSocket *sock)
{
mDNSBool c = !sock->connected;
sock->connected = mDNStrue;
sock->callback(sock, sock->context, c, sock->err);
}
#ifndef NO_SECURITYFRAMEWORK
mDNSlocal OSStatus tlsWriteSock(SSLConnectionRef connection, const void *data, size_t *dataLength)
{
int ret = send(((TCPSocket *)connection)->fd, data, *dataLength, 0);
if (ret >= 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); }
if (ret >= 0) { *dataLength = ret; return(noErr); }
*dataLength = 0;
if (errno == EAGAIN ) return(errSSLWouldBlock);
if (errno == ENOENT ) return(errSSLClosedGraceful);
if (errno == EPIPE || errno == ECONNRESET) return(errSSLClosedAbort);
LogMsg("ERROR: tlsWriteSock: %d error %d (%s)\n", ((TCPSocket *)connection)->fd, errno, strerror(errno));
return(errSSLClosedAbort);
}
mDNSlocal OSStatus tlsReadSock(SSLConnectionRef connection, void *data, size_t *dataLength)
{
int ret = recv(((TCPSocket *)connection)->fd, data, *dataLength, 0);
if (ret > 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); }
if (ret > 0) { *dataLength = ret; return(noErr); }
*dataLength = 0;
if (ret == 0 || errno == ENOENT ) return(errSSLClosedGraceful);
if ( errno == EAGAIN ) return(errSSLWouldBlock);
if ( errno == ECONNRESET) return(errSSLClosedAbort);
LogMsg("ERROR: tlsSockRead: error %d (%s)\n", errno, strerror(errno));
return(errSSLClosedAbort);
}
mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, mDNSBool server)
{
char domname_cstr[MAX_ESCAPED_DOMAIN_NAME];
mStatus err = SSLNewContext(server, &sock->tlsContext);
if (err) { LogMsg("ERROR: tlsSetupSock: SSLNewContext failed with error code: %d", err); return(err); }
err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock);
if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err); return(err); }
err = SSLSetConnection(sock->tlsContext, (SSLConnectionRef) sock);
if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err); return(err); }
err = SSLSetAllowAnonymousCiphers(sock->tlsContext, 0);
if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err); return(err); }
if (!sock->hostname.c[0]) {LogMsg("ERROR: tlsSetupSock: hostname NULL"); return -1; }
ConvertDomainNameToCString(&sock->hostname, domname_cstr);
err = SSLSetPeerDomainName(sock->tlsContext, domname_cstr, strlen(domname_cstr));
if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err); return(err); }
return(err);
}
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void doSSLHandshake(void *ctx)
{
TCPSocket *sock = (TCPSocket*)ctx;
mStatus err = SSLHandshake(sock->tlsContext);
dispatch_async(dispatch_get_main_queue(), ^{
LogInfo("doSSLHandshake %p: got lock", sock);
if (sock->handshake == handshake_to_be_closed)
{
LogInfo("SSLHandshake completed after close");
mDNSPlatformTCPCloseConnection(sock);
}
else
{
if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry);
else LogMsg("doSSLHandshake: sock->fd is -1");
if (err == errSSLWouldBlock)
sock->handshake = handshake_required;
else
{
if (err)
{
LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : "");
SSLDisposeContext(sock->tlsContext);
sock->tlsContext = NULL;
}
sock->err = err ? mStatus_ConnFailed : 0;
sock->handshake = handshake_completed;
LogInfo("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd);
doTcpSocketCallback(sock);
}
}
LogInfo("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd);
return;
});
}
#else
mDNSlocal void *doSSLHandshake(void *ctx)
{
TCPSocket *sock = (TCPSocket*)ctx;
mDNS * const m = sock->m; mStatus err = SSLHandshake(sock->tlsContext);
KQueueLock(m);
debugf("doSSLHandshake %p: got lock", sock);
if (sock->handshake == handshake_to_be_closed)
{
LogInfo("SSLHandshake completed after close");
mDNSPlatformTCPCloseConnection(sock);
}
else
{
if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry);
else LogMsg("doSSLHandshake: sock->fd is -1");
if (err == errSSLWouldBlock)
sock->handshake = handshake_required;
else
{
if (err)
{
LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : "");
SSLDisposeContext(sock->tlsContext);
sock->tlsContext = NULL;
}
sock->err = err ? mStatus_ConnFailed : 0;
sock->handshake = handshake_completed;
debugf("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd);
doTcpSocketCallback(sock);
}
}
debugf("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd);
KQueueUnlock(m, "doSSLHandshake");
return NULL;
}
#endif
mDNSlocal mStatus spawnSSLHandshake(TCPSocket* sock)
{
debugf("spawnSSLHandshake %p: entry", sock);
mStatus err;
if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake);
sock->handshake = handshake_in_progress;
KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, sock->kqEntry);
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
dispatch_async(SSLqueue, ^{doSSLHandshake(sock);});
err = 0;
#else
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
err = pthread_create(&sock->handshake_thread, &attr, doSSLHandshake, sock);
pthread_attr_destroy(&attr);
if (err)
{
LogMsg("Could not start SSLHandshake thread: (%d) %s", err, strerror(err));
sock->handshake = handshake_completed;
sock->err = err;
KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry);
}
#endif
debugf("spawnSSLHandshake %p: done for %d", sock, sock->fd);
return err;
}
mDNSlocal mDNSBool IsTunnelModeDomain(const domainname *d)
{
static const domainname *mmc = (const domainname*) "\x7" "members" "\x3" "mac" "\x3" "com";
const domainname *d1 = mDNSNULL; const domainname *d2 = mDNSNULL; const domainname *d3 = mDNSNULL;
while (d->c[0]) { d3 = d2; d2 = d1; d1 = d; d = (const domainname*)(d->c + 1 + d->c[0]); }
return(d3 && SameDomainName(d3, mmc));
}
#endif
mDNSlocal void tcpKQSocketCallback(__unused int fd, short filter, void *context)
{
TCPSocket *sock = context;
sock->err = mStatus_NoError;
if (filter == EVFILT_WRITE) KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry);
if (sock->flags & kTCPSocketFlags_UseTLS)
{
#ifndef NO_SECURITYFRAMEWORK
if (!sock->setup) { sock->setup = mDNStrue; tlsSetupSock(sock, mDNSfalse); }
if (sock->handshake == handshake_required) { if (spawnSSLHandshake(sock) == 0) return; }
else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed) return;
else if (sock->handshake != handshake_completed)
{
if (!sock->err) sock->err = mStatus_UnknownErr;
LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock->handshake);
}
#else
sock->err = mStatus_UnsupportedErr;
#endif
}
doTcpSocketCallback(sock);
}
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSexport int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef)
{
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_source_t source;
if (flags == EV_DELETE)
{
if (filter == EVFILT_READ)
{
dispatch_source_cancel(entryRef->readSource);
dispatch_release(entryRef->readSource);
entryRef->readSource = mDNSNULL;
debugf("KQueueSet: source cancel for read %p, %p", entryRef->readSource, entryRef->writeSource);
}
else if (filter == EVFILT_WRITE)
{
dispatch_source_cancel(entryRef->writeSource);
dispatch_release(entryRef->writeSource);
entryRef->writeSource = mDNSNULL;
debugf("KQueueSet: source cancel for write %p, %p", entryRef->readSource, entryRef->writeSource);
}
else
LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_DELETE", filter);
return 0;
}
if (flags != EV_ADD) LogMsg("KQueueSet: Invalid flags %d", flags);
if (filter == EVFILT_READ)
{
source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue);
}
else if (filter == EVFILT_WRITE)
{
source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, queue);
}
else
{
LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_ADD", filter);
return -1;
}
if (!source) return -1;
dispatch_source_set_event_handler(source, ^{
mDNSs32 stime = mDNSPlatformRawTime();
entryRef->KQcallback(fd, filter, entryRef->KQcontext);
mDNSs32 etime = mDNSPlatformRawTime();
if (etime - stime >= WatchDogReportingThreshold)
LogInfo("KQEntryCallback Block: WARNING: took %dms to complete", etime - stime);
TriggerEventCompletion();
});
dispatch_source_set_cancel_handler(source, ^{
if (entryRef->fdClosed)
{
close(fd);
}
});
dispatch_resume(source);
if (filter == EVFILT_READ)
entryRef->readSource = source;
else
entryRef->writeSource = source;
return 0;
}
mDNSexport void KQueueLock(mDNS *const m)
{
(void)m; }
mDNSexport void KQueueUnlock(mDNS *const m, const char const *task)
{
(void)m; (void)task; }
#else
mDNSexport int KQueueSet(int fd, u_short flags, short filter, const KQueueEntry *const entryRef)
{
struct kevent new_event;
EV_SET(&new_event, fd, filter, flags, 0, 0, (void*)entryRef);
return (kevent(KQueueFD, &new_event, 1, NULL, 0, NULL) < 0) ? errno : 0;
}
mDNSexport void KQueueLock(mDNS *const m)
{
pthread_mutex_lock(&m->p->BigMutex);
m->p->BigMutexStartTime = mDNSPlatformRawTime();
}
mDNSexport void KQueueUnlock(mDNS *const m, const char const *task)
{
mDNSs32 end = mDNSPlatformRawTime();
(void)task;
if (end - m->p->BigMutexStartTime >= WatchDogReportingThreshold)
LogInfo("WARNING: %s took %dms to complete", task, end - m->p->BigMutexStartTime);
pthread_mutex_unlock(&m->p->BigMutex);
char wake = 1;
if (send(m->p->WakeKQueueLoopFD, &wake, sizeof(wake), 0) == -1)
LogMsg("ERROR: KQueueWake: send failed with error code: %d (%s)", errno, strerror(errno));
}
#endif
mDNSexport void mDNSPlatformCloseFD(KQueueEntry *kq, int fd)
{
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
(void)fd; if (kq->readSource)
{
dispatch_source_cancel(kq->readSource);
kq->readSource = mDNSNULL;
}
if (kq->writeSource)
{
dispatch_source_cancel(kq->writeSource);
kq->writeSource = mDNSNULL;
}
debugf("mDNSPlatformCloseFD: resetting sources for %d", fd);
kq->fdClosed = mDNStrue;
#else
(void)kq; close(fd);
#endif
}
mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort *port)
{
KQSocketSet *cp = &sock->ss;
#ifndef NO_IPV6
int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
#else
int *s = &cp->sktv4;
KQueueEntry *k = &cp->kqsv4;
#endif
const int on = 1; mStatus err;
int skt = socket(sa_family, SOCK_STREAM, IPPROTO_TCP);
if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupTCPSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); }
if (sa_family == AF_INET)
{
struct sockaddr_in addr;
mDNSPlatformMemZero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = port->NotAnInteger;
err = bind(skt, (struct sockaddr*) &addr, sizeof(addr));
if (err < 0) { LogMsg("ERROR: bind %s", strerror(errno)); return err; }
err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
if (err < 0) { LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); return err; }
mDNSPlatformMemZero(&addr, sizeof(addr));
socklen_t len = sizeof(addr);
err = getsockname(skt, (struct sockaddr*) &addr, &len);
if (err < 0) { LogMsg("getsockname - %s", strerror(errno)); return err; }
port->NotAnInteger = addr.sin_port;
}
else
{
struct sockaddr_in6 addr6;
mDNSPlatformMemZero(&addr6, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = port->NotAnInteger;
err = bind(skt, (struct sockaddr*) &addr6, sizeof(addr6));
if (err < 0) { LogMsg("ERROR: bind6 %s", strerror(errno)); return err; }
err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
if (err < 0) { LogMsg("ERROR: setsockopt IPV6_RECVPKTINFO %s", strerror(errno)); return err; }
mDNSPlatformMemZero(&addr6, sizeof(addr6));
socklen_t len = sizeof(addr6);
err = getsockname(skt, (struct sockaddr *) &addr6, &len);
if (err < 0) { LogMsg("getsockname6 - %s", strerror(errno)); return err; }
port->NotAnInteger = addr6.sin6_port;
}
*s = skt;
k->KQcallback = tcpKQSocketCallback;
k->KQcontext = sock;
k->KQtask = "mDNSPlatformTCPSocket";
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
k->readSource = mDNSNULL;
k->writeSource = mDNSNULL;
k->fdClosed = mDNSfalse;
#endif
return mStatus_NoError;
}
mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port)
{
mStatus err;
(void) m;
TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPSocket", sizeof(TCPSocket));
if (!sock) { LogMsg("mDNSPlatformTCPSocket: memory allocation failure"); return(mDNSNULL); }
mDNSPlatformMemZero(sock, sizeof(TCPSocket));
sock->ss.m = m;
sock->ss.sktv4 = -1;
#ifndef NO_IPV6
sock->ss.sktv6 = -1;
#endif
err = SetupTCPSocket(sock, AF_INET, port);
#ifndef NO_IPV6
if (!err)
{
err = SetupTCPSocket(sock, AF_INET6, port);
if (err) { mDNSPlatformCloseFD(&sock->ss.kqsv4, sock->ss.sktv4); sock->ss.sktv4 = -1; }
}
#endif
if (err)
{
LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock->fd, errno, strerror(errno));
freeL("TCPSocket/mDNSPlatformTCPSocket", sock);
return(mDNSNULL);
}
sock->callback = mDNSNULL;
sock->flags = flags;
sock->context = mDNSNULL;
sock->setup = mDNSfalse;
sock->connected = mDNSfalse;
sock->handshake = handshake_required;
sock->m = m;
sock->err = mStatus_NoError;
return sock;
}
mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context)
{
KQSocketSet *cp = &sock->ss;
#ifndef NO_IPV6
int *s = (dst->type == mDNSAddrType_IPv4) ? &cp->sktv4 : &cp->sktv6;
KQueueEntry *k = (dst->type == mDNSAddrType_IPv4) ? &cp->kqsv4 : &cp->kqsv6;
#else
int *s = &cp->sktv4;
KQueueEntry *k = &cp->kqsv4;
#endif
mStatus err = mStatus_NoError;
struct sockaddr_storage ss;
sock->callback = callback;
sock->context = context;
sock->setup = mDNSfalse;
sock->connected = mDNSfalse;
sock->handshake = handshake_required;
sock->err = mStatus_NoError;
if (hostname) { debugf("mDNSPlatformTCPConnect: hostname %##s", hostname->c); AssignDomainName(&sock->hostname, hostname); }
if (dst->type == mDNSAddrType_IPv4)
{
struct sockaddr_in *saddr = (struct sockaddr_in *)&ss;
mDNSPlatformMemZero(saddr, sizeof(*saddr));
saddr->sin_family = AF_INET;
saddr->sin_port = dstport.NotAnInteger;
saddr->sin_len = sizeof(*saddr);
saddr->sin_addr.s_addr = dst->ip.v4.NotAnInteger;
}
else
{
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&ss;
mDNSPlatformMemZero(saddr6, sizeof(*saddr6));
saddr6->sin6_family = AF_INET6;
saddr6->sin6_port = dstport.NotAnInteger;
saddr6->sin6_len = sizeof(*saddr6);
saddr6->sin6_addr = *(struct in6_addr *)&dst->ip.v6;
}
if (KQueueSet(*s, EV_ADD , EVFILT_WRITE, k))
{
LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed");
return errno;
}
if (KQueueSet(*s, EV_ADD, EVFILT_READ, k))
{
LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed");
return errno;
}
if (fcntl(*s, F_SETFL, fcntl(*s, F_GETFL, 0) | O_NONBLOCK) < 0) {
LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno));
return mStatus_UnknownErr;
}
if (InterfaceID && InterfaceID != mDNSInterface_Unicast)
{
extern mDNS mDNSStorage;
NetworkInterfaceInfoOSX *info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID);
if (dst->type == mDNSAddrType_IPv4)
{
#ifdef IP_BOUND_IF
if (info) setsockopt(*s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id));
else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; }
#else
(void)InterfaceID; (void)info; #endif
}
else
{
#ifdef IPV6_BOUND_IF
if (info) setsockopt(*s, IPPROTO_IPV6, IPV6_BOUND_IF, &info->scope_id, sizeof(info->scope_id));
else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; }
#else
(void)InterfaceID; (void)info; #endif
}
}
sock->fd = *s;
sock->kqEntry = k;
if (connect(*s, (struct sockaddr *)&ss, ss.ss_len) < 0)
{
if (errno == EINPROGRESS) return mStatus_ConnPending;
if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN)
LogInfo("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s)", sock->fd, errno, strerror(errno));
else
LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s) length %d", sock->fd, errno, strerror(errno), ss.ss_len);
return mStatus_ConnFailed;
}
LogMsg("NOTE: mDNSPlatformTCPConnect completed synchronously");
return err;
}
mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int fd)
{
mStatus err = mStatus_NoError;
TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPAccept", sizeof(TCPSocket));
if (!sock) return(mDNSNULL);
mDNSPlatformMemZero(sock, sizeof(*sock));
sock->fd = fd;
sock->flags = flags;
if (flags & kTCPSocketFlags_UseTLS)
{
#ifndef NO_SECURITYFRAMEWORK
if (!ServerCerts) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err = mStatus_UnknownErr; goto exit; }
err = tlsSetupSock(sock, mDNStrue);
if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err); goto exit; }
err = SSLSetCertificate(sock->tlsContext, ServerCerts);
if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: SSLSetCertificate failed with error code: %d", err); goto exit; }
#else
err = mStatus_UnsupportedErr;
#endif
}
#ifndef NO_SECURITYFRAMEWORK
exit:
#endif
if (err) { freeL("TCPSocket/mDNSPlatformTCPAccept", sock); return(mDNSNULL); }
return(sock);
}
mDNSlocal void CloseSocketSet(KQSocketSet *ss)
{
if (ss->sktv4 != -1)
{
mDNSPlatformCloseFD(&ss->kqsv4, ss->sktv4);
ss->sktv4 = -1;
}
#ifndef NO_IPV6
if (ss->sktv6 != -1)
{
mDNSPlatformCloseFD(&ss->kqsv6, ss->sktv6);
ss->sktv6 = -1;
}
#endif
if (ss->closeFlag) *ss->closeFlag = 1;
}
mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock)
{
if (sock)
{
#ifndef NO_SECURITYFRAMEWORK
if (sock->tlsContext)
{
if (sock->handshake == handshake_in_progress) {
LogInfo("mDNSPlatformTCPCloseConnection: called while handshake in progress");
sock->handshake = handshake_to_be_closed;
return;
}
SSLClose(sock->tlsContext);
SSLDisposeContext(sock->tlsContext);
sock->tlsContext = NULL;
}
#endif
if (sock->ss.sktv4 != -1) shutdown(sock->ss.sktv4, 2);
#ifndef NO_IPV6
if (sock->ss.sktv6 != -1) shutdown(sock->ss.sktv6, 2);
#endif
CloseSocketSet(&sock->ss);
sock->fd = -1;
freeL("TCPSocket/mDNSPlatformTCPCloseConnection", sock);
}
}
mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed)
{
size_t nread = 0;
*closed = mDNSfalse;
if (sock->flags & kTCPSocketFlags_UseTLS)
{
#ifndef NO_SECURITYFRAMEWORK
if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformReadTCP called while handshake required"); return 0; }
else if (sock->handshake == handshake_in_progress) return 0;
else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock->handshake);
mStatus err = SSLRead(sock->tlsContext, buf, buflen, &nread);
if (err == errSSLClosedGraceful) { nread = 0; *closed = mDNStrue; }
else if (err && err != errSSLWouldBlock)
{ LogMsg("ERROR: mDNSPlatformReadTCP - SSLRead: %d", err); nread = -1; *closed = mDNStrue; }
#else
nread = -1;
*closed = mDNStrue;
#endif
}
else
{
static int CLOSEDcount = 0;
static int EAGAINcount = 0;
nread = recv(sock->fd, buf, buflen, 0);
if (nread > 0) { CLOSEDcount = 0; EAGAINcount = 0; } else if (nread == 0)
{
*closed = mDNStrue;
if ((++CLOSEDcount % 1000) == 0) { LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got CLOSED %d times", sock->fd, CLOSEDcount); sleep(1); }
}
else if (errno == ECONNRESET) { nread = 0; *closed = mDNStrue; }
else if (errno != EAGAIN) { LogMsg("ERROR: mDNSPlatformReadTCP - recv: %d (%s)", errno, strerror(errno)); nread = -1; }
else {
nread = 0;
if ((++EAGAINcount % 1000) == 0) { LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got EAGAIN %d times", sock->fd, EAGAINcount); sleep(1); }
}
}
return nread;
}
mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len)
{
int nsent;
if (sock->flags & kTCPSocketFlags_UseTLS)
{
#ifndef NO_SECURITYFRAMEWORK
size_t processed;
if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformWriteTCP called while handshake required"); return 0; }
if (sock->handshake == handshake_in_progress) return 0;
else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformWriteTCP called with unexpected SSLHandshake status: %d", sock->handshake);
mStatus err = SSLWrite(sock->tlsContext, msg, len, &processed);
if (!err) nsent = (int) processed;
else if (err == errSSLWouldBlock) nsent = 0;
else { LogMsg("ERROR: mDNSPlatformWriteTCP - SSLWrite returned %d", err); nsent = -1; }
#else
nsent = -1;
#endif
}
else
{
nsent = send(sock->fd, msg, len, 0);
if (nsent < 0)
{
if (errno == EAGAIN) nsent = 0;
else { LogMsg("ERROR: mDNSPlatformWriteTCP - send %s", strerror(errno)); nsent = -1; }
}
}
return nsent;
}
mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock)
{
return sock->fd;
}
mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa_family, mDNSIPPort *const outport)
{
#ifndef NO_IPV6
int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
#else
int *s = &cp->sktv4;
KQueueEntry *k = &cp->kqsv4;
#endif
const int on = 1;
const int twofivefive = 255;
mStatus err = mStatus_NoError;
char *errstr = mDNSNULL;
#ifdef NO_IPV6
if (sa_family != AF_INET) return -1;
#endif
cp->closeFlag = mDNSNULL;
int skt = socket(sa_family, SOCK_DGRAM, IPPROTO_UDP);
if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); }
if (mDNSSameIPPort(port, MulticastDNSPort) || mDNSSameIPPort(port, NATPMPAnnouncementPort)) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; }
if (sa_family == AF_INET)
{
err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; }
err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; }
err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on));
err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive));
if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; }
err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive));
if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; }
struct sockaddr_in listening_sockaddr;
listening_sockaddr.sin_family = AF_INET;
listening_sockaddr.sin_port = port.NotAnInteger; listening_sockaddr.sin_addr.s_addr = mDNSSameIPPort(port, NATPMPAnnouncementPort) ? AllHosts_v4.NotAnInteger : 0;
err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr));
if (err) { errstr = "bind"; goto fail; }
if (outport) outport->NotAnInteger = listening_sockaddr.sin_port;
}
#ifndef NO_IPV6
else if (sa_family == AF_INET6)
{
if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort; return mStatus_NoError; }
err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
if (err < 0) { errstr = "setsockopt - IPV6_RECVPKTINFO"; goto fail; }
err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
if (err < 0) { errstr = "setsockopt - IPV6_RECVHOPLIMIT"; goto fail; }
err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; }
err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive));
if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; }
err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive));
if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; }
err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on));
if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; }
struct sockaddr_in6 listening_sockaddr6;
mDNSPlatformMemZero(&listening_sockaddr6, sizeof(listening_sockaddr6));
listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6);
listening_sockaddr6.sin6_family = AF_INET6;
listening_sockaddr6.sin6_port = port.NotAnInteger; listening_sockaddr6.sin6_flowinfo = 0;
listening_sockaddr6.sin6_addr = in6addr_any; listening_sockaddr6.sin6_scope_id = 0;
err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6));
if (err) { errstr = "bind"; goto fail; }
if (outport) outport->NotAnInteger = listening_sockaddr6.sin6_port;
}
#endif
fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); fcntl(skt, F_SETFD, 1); *s = skt;
k->KQcallback = myKQSocketCallBack;
k->KQcontext = cp;
k->KQtask = "UDP packet reception";
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
k->readSource = mDNSNULL;
k->writeSource = mDNSNULL;
k->fdClosed = mDNSfalse;
#endif
KQueueSet(*s, EV_ADD, EVFILT_READ, k);
return(err);
fail:
if (strcmp(errstr, "bind") || mDNSSameIPPort(port, MulticastDNSPort) || mDNSIPPortIsZero(port))
LogMsg("%s skt %d port %d error %d errno %d (%s)", errstr, skt, mDNSVal16(port), err, errno, strerror(errno));
if (!strcmp(errstr, "bind") && errno == EADDRINUSE)
{
err = EADDRINUSE;
if (mDNSSameIPPort(port, MulticastDNSPort))
NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed",
"Congratulations, you've reproduced an elusive bug.\r"
"Please contact the current assignee of <rdar://problem/3814904>.\r"
"Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r"
"If possible, please leave your machine undisturbed so that someone can come to investigate the problem.");
}
mDNSPlatformCloseFD(k, skt);
return(err);
}
mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport)
{
mStatus err;
mDNSIPPort port = requestedport;
mDNSBool randomizePort = mDNSIPPortIsZero(requestedport);
int i = 10000; UDPSocket *p = mallocL("UDPSocket", sizeof(UDPSocket));
if (!p) { LogMsg("mDNSPlatformUDPSocket: memory exhausted"); return(mDNSNULL); }
mDNSPlatformMemZero(p, sizeof(UDPSocket));
p->ss.port = zeroIPPort;
p->ss.m = m;
p->ss.sktv4 = -1;
#ifndef NO_IPV6
p->ss.sktv6 = -1;
#endif
do
{
if (randomizePort) port = mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF));
err = SetupSocket(&p->ss, port, AF_INET, &p->ss.port);
#ifndef NO_IPV6
if (!err)
{
err = SetupSocket(&p->ss, port, AF_INET6, &p->ss.port);
if (err) { mDNSPlatformCloseFD(&p->ss.kqsv4, p->ss.sktv4); p->ss.sktv4 = -1; }
}
#endif
i--;
} while (err == EADDRINUSE && randomizePort && i);
if (err)
{
if (mDNSSameIPPort(requestedport, NATPMPPort) || mDNSSameIPPort(requestedport, NATPMPAnnouncementPort))
LogInfo("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno));
else LogMsg("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno));
freeL("UDPSocket", p);
return(mDNSNULL);
}
return(p);
}
mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock)
{
CloseSocketSet(&sock->ss);
freeL("UDPSocket", sock);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - BPF Raw packet sending/receiving
#endif
#if APPLE_OSX_mDNSResponder
mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
{
if (!InterfaceID) { LogMsg("mDNSPlatformSendRawPacket: No InterfaceID specified"); return; }
NetworkInterfaceInfoOSX *info;
extern mDNS mDNSStorage;
info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID);
if (info == NULL)
{
LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID);
return;
}
if (info->BPF_fd < 0)
LogMsg("mDNSPlatformSendRawPacket: %s BPF_fd %d not ready", info->ifinfo.ifname, info->BPF_fd);
else
{
if (write(info->BPF_fd, msg, end - (mDNSu8 *)msg) < 0)
LogMsg("mDNSPlatformSendRawPacket: BPF write(%d) failed %d (%s)", info->BPF_fd, errno, strerror(errno));
}
}
mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID)
{
if (!InterfaceID) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: No InterfaceID specified"); return; }
NetworkInterfaceInfoOSX *info;
info = IfindexToInterfaceInfoOSX(m, InterfaceID);
if (info == NULL) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID); return; }
if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa))
LogSPS("Don't need address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha);
else
{
int result = mDNSSetLocalAddressCacheEntry(info->scope_id, tpa->type, tpa->ip.v6.b, tha->b);
if (result) LogMsg("Set local address cache entry for %s %#a %.6a failed: %d", info->ifinfo.ifname, tpa, tha, result);
else LogSPS("Set local address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha);
}
}
mDNSlocal void CloseBPF(NetworkInterfaceInfoOSX *const i)
{
LogSPS("%s closing BPF fd %d", i->ifinfo.ifname, i->BPF_fd);
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
dispatch_source_cancel(i->BPF_source);
#else
CFRunLoopRemoveSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode);
CFRelease(i->BPF_rls);
CFSocketInvalidate(i->BPF_cfs);
CFRelease(i->BPF_cfs);
#endif
i->BPF_fd = -1;
if (i->BPF_mcfd >= 0) { close(i->BPF_mcfd); i->BPF_mcfd = -1; }
}
mDNSlocal void bpf_callback_common(NetworkInterfaceInfoOSX *info)
{
KQueueLock(info->m);
if (info->BPF_fd < 0) goto exit;
ssize_t n = read(info->BPF_fd, &info->m->imsg, info->BPF_len);
const mDNSu8 *ptr = (const mDNSu8 *)&info->m->imsg;
const mDNSu8 *end = (const mDNSu8 *)&info->m->imsg + n;
debugf("%3d: bpf_callback got %d bytes on %s", info->BPF_fd, n, info->ifinfo.ifname);
if (n<0)
{
LogMsg("Closing %s BPF fd %d due to error %d (%s)", info->ifinfo.ifname, info->BPF_fd, errno, strerror(errno));
CloseBPF(info);
goto exit;
}
while (ptr < end)
{
const struct bpf_hdr *const bh = (const struct bpf_hdr *)ptr;
debugf("%3d: bpf_callback ptr %p bh_hdrlen %d data %p bh_caplen %4d bh_datalen %4d next %p remaining %4d",
info->BPF_fd, ptr, bh->bh_hdrlen, ptr + bh->bh_hdrlen, bh->bh_caplen, bh->bh_datalen,
ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen), end - (ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen)));
mDNSCoreReceiveRawPacket(info->m, ptr + bh->bh_hdrlen, ptr + bh->bh_hdrlen + bh->bh_caplen, info->ifinfo.InterfaceID);
ptr += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen);
}
exit:
KQueueUnlock(info->m, "bpf_callback");
}
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void bpf_callback_dispatch(NetworkInterfaceInfoOSX *const info)
{
bpf_callback_common(info);
}
#else
mDNSlocal void bpf_callback(const CFSocketRef cfs, const CFSocketCallBackType CallBackType, const CFDataRef address, const void *const data, void *const context)
{
(void)cfs;
(void)CallBackType;
(void)address;
(void)data;
bpf_callback_common((NetworkInterfaceInfoOSX *)context);
}
#endif
#define BPF_SetOffset(from, cond, to) (from)->cond = (to) - 1 - (from)
mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int *p4, int *p6)
{
int numv4 = 0, numv6 = 0;
AuthRecord *rr;
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4)
{
if (p4) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.4a", x->BPF_fd, x->ifinfo.ifname, numv4, &rr->AddressProxy.ip.v4);
numv4++;
}
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6)
{
if (p6) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.16a", x->BPF_fd, x->ifinfo.ifname, numv6, &rr->AddressProxy.ip.v6);
numv6++;
}
if (p4) *p4 = numv4;
if (p6) *p6 = numv6;
return(numv4 + numv6);
}
mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID)
{
NetworkInterfaceInfoOSX *x;
for (x = m->p->InterfaceList; x; x = x->next) if (x->ifinfo.InterfaceID == InterfaceID) break;
if (!x) { LogMsg("mDNSPlatformUpdateProxyList: ERROR InterfaceID %p not found", InterfaceID); return; }
#define MAX_BPF_ADDRS 250
int numv4 = 0, numv6 = 0;
if (CountProxyTargets(m, x, &numv4, &numv6) > MAX_BPF_ADDRS)
{
LogMsg("mDNSPlatformUpdateProxyList: ERROR Too many address proxy records v4 %d v6 %d", numv4, numv6);
if (numv4 > MAX_BPF_ADDRS) numv4 = MAX_BPF_ADDRS;
numv6 = MAX_BPF_ADDRS - numv4;
}
LogSPS("mDNSPlatformUpdateProxyList: fd %d %-7s MAC %.6a %d v4 %d v6", x->BPF_fd, x->ifinfo.ifname, &x->ifinfo.MAC, numv4, numv6);
static struct bpf_insn filter[17 + MAX_BPF_ADDRS] =
{
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0806, 0, 1), BPF_STMT(BPF_RET + BPF_K, 42),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0800, 4, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x86DD, 0, 9), BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x3AFF, 0, 9), BPF_STMT(BPF_RET + BPF_K, 86),
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 30), };
struct bpf_insn *pc = &filter[9];
struct bpf_insn *chk6 = pc + numv4 + 1; struct bpf_insn *fail = chk6 + 1 + numv6; struct bpf_insn *ret4 = fail + 1;
struct bpf_insn *ret6 = ret4 + 4;
static const struct bpf_insn rf = BPF_STMT(BPF_RET + BPF_K, 0);
static const struct bpf_insn g6 = BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 50);
static const struct bpf_insn r4a = BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14); static const struct bpf_insn r4b = BPF_STMT(BPF_LD + BPF_IMM, 54); static const struct bpf_insn r4c = BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0); static const struct bpf_insn r4d = BPF_STMT(BPF_RET + BPF_A, 0);
static const struct bpf_insn r6a = BPF_STMT(BPF_RET + BPF_K, 94);
BPF_SetOffset(&filter[4], jf, fail); BPF_SetOffset(&filter[6], jf, chk6);
AuthRecord *rr;
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4)
{
mDNSv4Addr a = rr->AddressProxy.ip.v4;
pc->code = BPF_JMP + BPF_JEQ + BPF_K;
BPF_SetOffset(pc, jt, ret4);
pc->jf = 0;
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];
pc++;
}
*pc++ = rf;
if (pc != chk6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != chk6 %p", pc, chk6);
*pc++ = g6;
if (x->BPF_mcfd >= 0) close(x->BPF_mcfd);
x->BPF_mcfd = socket(AF_INET6, SOCK_DGRAM, 0);
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6)
{
const mDNSv6Addr *const a = &rr->AddressProxy.ip.v6;
pc->code = BPF_JMP + BPF_JEQ + BPF_K;
BPF_SetOffset(pc, jt, ret6);
pc->jf = 0;
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];
pc++;
struct ipv6_mreq i6mr;
i6mr.ipv6mr_interface = x->scope_id;
i6mr.ipv6mr_multiaddr = *(const struct in6_addr*)&NDP_prefix;
i6mr.ipv6mr_multiaddr.s6_addr[0xD] = a->b[0xD];
i6mr.ipv6mr_multiaddr.s6_addr[0xE] = a->b[0xE];
i6mr.ipv6mr_multiaddr.s6_addr[0xF] = a->b[0xF];
mStatus err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr));
if (err < 0 && (errno != EADDRNOTAVAIL))
LogMsg("mDNSPlatformUpdateProxyList: IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr));
if (err < 0 && (errno != EADDRINUSE)) LogMsg("mDNSPlatformUpdateProxyList: IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
LogSPS("Joined IPv6 ND multicast group %.16a for %.16a", &i6mr.ipv6mr_multiaddr, a);
}
if (pc != fail) LogMsg("mDNSPlatformUpdateProxyList: pc %p != fail %p", pc, fail);
*pc++ = rf;
if (pc != ret4) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret4 %p", pc, ret4);
*pc++ = r4a; *pc++ = r4b;
*pc++ = r4c;
*pc++ = r4d;
if (pc != ret6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret6 %p", pc, ret6);
*pc++ = r6a;
struct bpf_program prog = { pc - filter, filter };
#if 0
unsigned int q;
for (q=0; q<prog.bf_len; q++)
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);
#endif
if (!numv4 && !numv6)
{
LogSPS("mDNSPlatformUpdateProxyList: No need for filter");
if (m->timenow == 0) LogMsg("mDNSPlatformUpdateProxyList: m->timenow == 0");
if (!m->p->NetworkChanged) m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2);
if (x->BPF_fd < 0) return; }
if (ioctl(x->BPF_fd, BIOCSETF, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETF(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno));
else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETF(%d) successful", prog.bf_len);
}
mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd)
{
mDNS_Lock(m);
NetworkInterfaceInfoOSX *i;
for (i = m->p->InterfaceList; i; i = i->next) if (i->BPF_fd == -2) break;
if (!i) { LogSPS("mDNSPlatformReceiveBPF_fd: No Interfaces awaiting BPF fd %d; closing", fd); close(fd); }
else
{
LogSPS("%s using BPF fd %d", i->ifinfo.ifname, fd);
struct bpf_version v;
if (ioctl(fd, BIOCVERSION, &v) < 0)
LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
else if (BPF_MAJOR_VERSION != v.bv_major || BPF_MINOR_VERSION != v.bv_minor)
LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION header %d.%d kernel %d.%d",
fd, i->ifinfo.ifname, BPF_MAJOR_VERSION, BPF_MINOR_VERSION, v.bv_major, v.bv_minor);
if (ioctl(fd, BIOCGBLEN, &i->BPF_len) < 0)
LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCGBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
if (i->BPF_len > sizeof(m->imsg))
{
i->BPF_len = sizeof(m->imsg);
if (ioctl(fd, BIOCSBLEN, &i->BPF_len) < 0)
LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
else
LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", fd, i->ifinfo.ifname, i->BPF_len);
}
static const u_int opt_one = 1;
if (ioctl(fd, BIOCIMMEDIATE, &opt_one) < 0)
LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCIMMEDIATE failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
struct ifreq ifr;
mDNSPlatformMemZero(&ifr, sizeof(ifr));
strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCSETIF, &ifr) < 0)
{ LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSETIF failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); i->BPF_fd = -3; }
else
{
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
i->BPF_fd = fd;
i->BPF_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue());
if (!i->BPF_source) {LogMsg("mDNSPlatformReceiveBPF_fd: dispatch source create failed");return;}
dispatch_source_set_event_handler(i->BPF_source, ^{bpf_callback_dispatch(i);});
dispatch_source_set_cancel_handler(i->BPF_source, ^{close(fd);});
dispatch_resume(i->BPF_source);
#else
CFSocketContext myCFSocketContext = { 0, i, NULL, NULL, NULL };
i->BPF_fd = fd;
i->BPF_cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, bpf_callback, &myCFSocketContext);
i->BPF_rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, i->BPF_cfs, 0);
CFRunLoopAddSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode);
#endif
mDNSPlatformUpdateProxyList(m, i->ifinfo.InterfaceID);
}
}
mDNS_Unlock(m);
}
#endif // APPLE_OSX_mDNSResponder
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Key Management
#endif
#ifndef NO_SECURITYFRAMEWORK
mDNSlocal CFArrayRef GetCertChain(SecIdentityRef identity)
{
CFMutableArrayRef certChain = NULL;
if (!identity) { LogMsg("getCertChain: identity is NULL"); return(NULL); }
SecCertificateRef cert;
OSStatus err = SecIdentityCopyCertificate(identity, &cert);
if (err || !cert) LogMsg("getCertChain: SecIdentityCopyCertificate() returned %d", (int) err);
else
{
SecPolicySearchRef searchRef;
err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef);
if (err || !searchRef) LogMsg("getCertChain: SecPolicySearchCreate() returned %d", (int) err);
else
{
SecPolicyRef policy;
err = SecPolicySearchCopyNext(searchRef, &policy);
if (err || !policy) LogMsg("getCertChain: SecPolicySearchCopyNext() returned %d", (int) err);
else
{
CFArrayRef wrappedCert = CFArrayCreate(NULL, (const void**) &cert, 1, &kCFTypeArrayCallBacks);
if (!wrappedCert) LogMsg("getCertChain: wrappedCert is NULL");
else
{
SecTrustRef trust;
err = SecTrustCreateWithCertificates(wrappedCert, policy, &trust);
if (err || !trust) LogMsg("getCertChain: SecTrustCreateWithCertificates() returned %d", (int) err);
else
{
err = SecTrustEvaluate(trust, NULL);
if (err) LogMsg("getCertChain: SecTrustEvaluate() returned %d", (int) err);
else
{
CFArrayRef rawCertChain;
CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL;
err = SecTrustGetResult(trust, NULL, &rawCertChain, &statusChain);
if (err || !rawCertChain || !statusChain) LogMsg("getCertChain: SecTrustGetResult() returned %d", (int) err);
else
{
certChain = CFArrayCreateMutableCopy(NULL, 0, rawCertChain);
if (!certChain) LogMsg("getCertChain: certChain is NULL");
else
{
CFArraySetValueAtIndex(certChain, 0, identity);
if (CFArrayGetCount(certChain) > 1) CFArrayRemoveValueAtIndex(certChain, CFArrayGetCount(certChain) - 1);
}
CFRelease(rawCertChain);
}
}
CFRelease(trust);
}
CFRelease(wrappedCert);
}
CFRelease(policy);
}
CFRelease(searchRef);
}
CFRelease(cert);
}
return certChain;
}
#endif
mDNSexport mStatus mDNSPlatformTLSSetupCerts(void)
{
#ifdef NO_SECURITYFRAMEWORK
return mStatus_UnsupportedErr;
#else
SecIdentityRef identity = nil;
SecIdentitySearchRef srchRef = nil;
OSStatus err;
err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_DECRYPT, &srchRef);
if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCreate returned %d", (int) err); return err; }
err = SecIdentitySearchCopyNext(srchRef, &identity);
if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext returned %d", (int) err); return err; }
if (CFGetTypeID(identity) != SecIdentityGetTypeID())
{ LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext CFTypeID failure"); return mStatus_UnknownErr; }
ServerCerts = GetCertChain(identity);
if (ServerCerts == nil) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: getCertChain error"); return mStatus_UnknownErr; }
return mStatus_NoError;
#endif
}
mDNSexport void mDNSPlatformTLSTearDownCerts(void)
{
#ifndef NO_SECURITYFRAMEWORK
if (ServerCerts) { CFRelease(ServerCerts); ServerCerts = NULL; }
#endif
}
mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel)
{
CFStringEncoding encoding = kCFStringEncodingUTF8;
CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding);
if (cfs)
{
CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8);
CFRelease(cfs);
}
}
mDNSlocal void GetUserSpecifiedLocalHostName(domainlabel *const namelabel)
{
CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL);
if (cfs)
{
CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8);
CFRelease(cfs);
}
}
mDNSexport mDNSBool DictionaryIsEnabled(CFDictionaryRef dict)
{
mDNSs32 val;
CFNumberRef state = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("Enabled"));
if (!state) return mDNSfalse;
if (!CFNumberGetValue(state, kCFNumberSInt32Type, &val))
{ LogMsg("ERROR: DictionaryIsEnabled - CFNumberGetValue"); return mDNSfalse; }
return val ? mDNStrue : mDNSfalse;
}
mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa)
{
if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); }
if (sa->sa_family == AF_INET)
{
struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa;
ip->type = mDNSAddrType_IPv4;
ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr;
return(mStatus_NoError);
}
if (sa->sa_family == AF_INET6)
{
struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa;
if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
ip->type = mDNSAddrType_IPv6;
ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr;
return(mStatus_NoError);
}
LogMsg("SetupAddr invalid sa_family %d", sa->sa_family);
return(mStatus_Invalid);
}
mDNSlocal mDNSEthAddr GetBSSID(char *ifa_name)
{
mDNSEthAddr eth = zeroEthAddr;
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetBSSID"), NULL, NULL);
if (!store)
LogMsg("GetBSSID: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
else
{
CFStringRef entityname = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Interface/%s/AirPort"), ifa_name);
if (entityname)
{
CFDictionaryRef dict = SCDynamicStoreCopyValue(store, entityname);
if (dict)
{
CFRange range = { 0, 6 }; CFDataRef data = CFDictionaryGetValue(dict, CFSTR("BSSID"));
if (data && CFDataGetLength(data) == 6) CFDataGetBytes(data, range, eth.b);
CFRelease(dict);
}
CFRelease(entityname);
}
CFRelease(store);
}
return(eth);
}
mDNSlocal int GetMAC(mDNSEthAddr *eth, u_short ifindex)
{
struct ifaddrs *ifa;
for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next)
if (ifa->ifa_addr->sa_family == AF_LINK)
{
const struct sockaddr_dl *const sdl = (const struct sockaddr_dl *)ifa->ifa_addr;
if (sdl->sdl_index == ifindex)
{ mDNSPlatformMemCopy(eth->b, sdl->sdl_data + sdl->sdl_nlen, 6); return 0; }
}
*eth = zeroEthAddr;
return -1;
}
#ifndef SIOCGIFWAKEFLAGS
#define SIOCGIFWAKEFLAGS _IOWR('i', 136, struct ifreq)
#endif
#ifndef IF_WAKE_ON_MAGIC_PACKET
#define IF_WAKE_ON_MAGIC_PACKET 0x01
#endif
#ifndef ifr_wake_flags
#define ifr_wake_flags ifr_ifru.ifru_intval
#endif
mDNSlocal mDNSBool NetWakeInterface(NetworkInterfaceInfoOSX *i)
{
if (!MulticastInterface(i) ) return(mDNSfalse); if (i->ifa_flags & IFF_LOOPBACK) return(mDNSfalse);
int s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i->ifinfo.ifname, s, errno, strerror(errno)); return(mDNSfalse); }
struct ifreq ifr;
strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFWAKEFLAGS, &ifr) < 0)
{
#define KERNEL_EOPNOTSUPP 102
if (errno != KERNEL_EOPNOTSUPP) LogMsg("NetWakeInterface SIOCGIFWAKEFLAGS %s errno %d (%s)", i->ifinfo.ifname, errno, strerror(errno));
ifr.ifr_wake_flags = (errno == KERNEL_EOPNOTSUPP && !(i)->BSSID.l[0] && i->m->SystemWakeOnLANEnabled) ? IF_WAKE_ON_MAGIC_PACKET : 0;
}
close(s);
LogSPS("%-6s %#-14a %s WOMP", i->ifinfo.ifname, &i->ifinfo.ip, (ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) ? "supports" : "no");
return((ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) != 0);
}
mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa, mDNSs32 utc)
{
mDNSu32 scope_id = if_nametoindex(ifa->ifa_name);
mDNSEthAddr bssid = GetBSSID(ifa->ifa_name);
mDNSAddr ip, mask;
if (SetupAddr(&ip, ifa->ifa_addr ) != mStatus_NoError) return(NULL);
if (SetupAddr(&mask, ifa->ifa_netmask) != mStatus_NoError) return(NULL);
NetworkInterfaceInfoOSX **p;
for (p = &m->p->InterfaceList; *p; p = &(*p)->next)
if (scope_id == (*p)->scope_id &&
mDNSSameAddress(&ip, &(*p)->ifinfo.ip) &&
mDNSSameEthAddress(&bssid, &(*p)->BSSID))
{
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);
strlcpy((*p)->ifinfo.ifname, ifa->ifa_name, sizeof((*p)->ifinfo.ifname));
(*p)->Exists = mDNStrue;
if ((*p)->LastSeen != utc) (*p)->AppearanceTime = utc;
const mDNSBool NetWake = NetWakeInterface(*p);
if ((*p)->ifinfo.NetWake != NetWake)
{
(*p)->ifinfo.NetWake = NetWake;
if ((*p)->Registered)
{
mDNS_Lock(m);
if (NetWake) mDNS_ActivateNetWake_internal (m, &(*p)->ifinfo);
else mDNS_DeactivateNetWake_internal(m, &(*p)->ifinfo);
mDNS_Unlock(m);
}
}
return(*p);
}
NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i));
debugf("AddInterfaceToList: Making new interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, i);
if (!i) return(mDNSNULL);
mDNSPlatformMemZero(i, sizeof(NetworkInterfaceInfoOSX));
i->ifinfo.InterfaceID = (mDNSInterfaceID)(uintptr_t)scope_id;
i->ifinfo.ip = ip;
i->ifinfo.mask = mask;
strlcpy(i->ifinfo.ifname, ifa->ifa_name, sizeof(i->ifinfo.ifname));
i->ifinfo.ifname[sizeof(i->ifinfo.ifname)-1] = 0;
i->ifinfo.Advertise = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses;
i->ifinfo.McastTxRx = mDNSfalse; i->ifinfo.Loopback = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse;
i->next = mDNSNULL;
i->m = m;
i->Exists = mDNStrue;
i->Flashing = mDNSfalse;
i->Occulting = mDNSfalse;
i->AppearanceTime = utc; i->LastSeen = utc;
i->ifa_flags = ifa->ifa_flags;
i->scope_id = scope_id;
i->BSSID = bssid;
i->sa_family = ifa->ifa_addr->sa_family;
i->BPF_fd = -1;
i->BPF_mcfd = -1;
i->BPF_len = 0;
i->Registered = mDNSNULL;
i->ifinfo.NetWake = NetWakeInterface(i);
GetMAC(&i->ifinfo.MAC, scope_id);
if (i->ifinfo.NetWake && !i->ifinfo.MAC.l[0])
LogMsg("AddInterfaceToList: Bad MAC address %.6a for %d %s %#a", &i->ifinfo.MAC, scope_id, i->ifinfo.ifname, &ip);
*p = i;
return(i);
}
#if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4
mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id)
{
NetworkInterfaceInfoOSX *i;
for (i = m->p->InterfaceList; i; i = i->next)
if (i->Exists && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4)
if (!mDNSv4AddressIsLinkLocal(&i->ifinfo.ip.ip.v4))
return(i);
return(mDNSNULL);
}
#endif
#if APPLE_OSX_mDNSResponder
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - AutoTunnel
#endif
#define kRacoonPort 4500
static DomainAuthInfo* AnonymousRacoonConfig = mDNSNULL;
#ifndef NO_SECURITYFRAMEWORK
static CFMutableDictionaryRef domainStatusDict = NULL;
mDNSlocal void RemoveAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info)
{
char buffer[1024];
mDNSu32 buflen;
CFStringRef domain;
LogInfo("RemoveAutoTunnelDomainStatus: %##s", info->domain.c);
if (!domainStatusDict) { LogMsg("RemoveAutoTunnelDomainStatus: No domainStatusDict"); return; }
buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c);
if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
if (!domain) { LogMsg("RemoveAutoTunnelDomainStatus: Could not create CFString domain"); return; }
if (CFDictionaryContainsKey(domainStatusDict, domain))
{
CFDictionaryRemoveValue(domainStatusDict, domain);
if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict);
}
CFRelease(domain);
}
mDNSlocal mStatus CheckQuestionForStatus(const DNSQuestion *const q)
{
if (q->LongLived)
{
if (q->servAddr.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes(q->servAddr.ip.v4))
return mStatus_NoSuchRecord;
else if (q->state == LLQ_Poll)
return mStatus_PollingMode;
else if (q->state != LLQ_Established && !q->DuplicateOf)
return mStatus_TransientErr;
}
return mStatus_NoError;
}
mDNSlocal mStatus UpdateLLQStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info)
{
mStatus status = mStatus_NoError;
DNSQuestion* q, *worst_q = mDNSNULL;
for (q = m->Questions; q; q=q->next)
if (q->AuthInfo == info)
{
mStatus newStatus = CheckQuestionForStatus(q);
if (newStatus == mStatus_NoSuchRecord) { status = newStatus; worst_q = q; break; }
else if (newStatus == mStatus_PollingMode) { status = newStatus; worst_q = q; }
else if (newStatus == mStatus_TransientErr && status == mStatus_NoError) { status = newStatus; worst_q = q; }
}
if (status == mStatus_NoError) mDNS_snprintf(buffer, bufsz, "Success");
else if (status == mStatus_NoSuchRecord) mDNS_snprintf(buffer, bufsz, "GetZoneData %s: %##s", worst_q->nta ? "not yet complete" : "failed", worst_q->qname.c);
else if (status == mStatus_PollingMode) mDNS_snprintf(buffer, bufsz, "Query polling %##s", worst_q->qname.c);
else if (status == mStatus_TransientErr) mDNS_snprintf(buffer, bufsz, "Query not yet established %##s", worst_q->qname.c);
return status;
}
mDNSlocal mStatus UpdateRRStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info)
{
AuthRecord *r;
if (info->deltime) return mStatus_NoError;
for (r = m->ResourceRecords; r; r = r->next)
{
const domainname *n = r->resrec.name;
while (n->c[0])
{
DomainAuthInfo *ptr;
for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
if (SameDomainName(&ptr->domain, n))
{
if (ptr == info && (r->updateError == mStatus_BadSig || r->updateError == mStatus_BadKey))
{
mDNS_snprintf(buffer, bufsz, "Resource record update failed for %##s", r->resrec.name);
return r->updateError;
}
}
n = (const domainname *)(n->c + 1 + n->c[0]);
}
}
return mStatus_NoError;
}
#endif // ndef NO_SECURITYFRAMEWORK
mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info)
{
#ifdef NO_SECURITYFRAMEWORK
(void)m;
(void)info;
#else
const NATTraversalInfo *const llq = m->LLQNAT.clientContext ? &m->LLQNAT : mDNSNULL;
const NATTraversalInfo *const tun = info->AutoTunnelNAT.clientContext ? &info->AutoTunnelNAT : mDNSNULL;
char buffer[1024];
mDNSu32 buflen = 0;
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFStringRef domain = NULL;
CFStringRef tmp = NULL;
CFNumberRef num = NULL;
mStatus status = mStatus_NoError;
mStatus llqStatus = mStatus_NoError;
char llqBuffer[1024];
if (!m->mDNS_busy) LogMsg("UpdateAutoTunnelDomainStatus: ERROR!! Lock not held");
if (!domainStatusDict)
{
domainStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!domainStatusDict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary domainStatusDict"); return; }
}
if (!dict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary dict"); return; }
buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c);
if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
if (!domain) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString domain"); return; }
mDNS_snprintf(buffer, sizeof(buffer), "%#a", &m->Router);
tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
if (!tmp)
LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString RouterAddress");
else
{
CFDictionarySetValue(dict, CFSTR("RouterAddress"), tmp);
CFRelease(tmp);
}
mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &m->ExternalAddress);
tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
if (!tmp)
LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress");
else
{
CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp);
CFRelease(tmp);
}
if (llq)
{
mDNSu32 port = mDNSVal16(llq->ExternalPort);
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port);
if (!num)
LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQExternalPort");
else
{
CFDictionarySetValue(dict, CFSTR("LLQExternalPort"), num);
CFRelease(num);
}
if (llq->Result)
{
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &llq->Result);
if (!num)
LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQNPMStatus");
else
{
CFDictionarySetValue(dict, CFSTR("LLQNPMStatus"), num);
CFRelease(num);
}
}
}
if (tun)
{
mDNSu32 port = mDNSVal16(tun->ExternalPort);
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port);
if (!num)
LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelExternalPort");
else
{
CFDictionarySetValue(dict, CFSTR("AutoTunnelExternalPort"), num);
CFRelease(num);
}
if (tun->Result)
{
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tun->Result);
if (!num)
LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelNPMStatus");
else
{
CFDictionarySetValue(dict, CFSTR("AutoTunnelNPMStatus"), num);
CFRelease(num);
}
}
}
if (tun || llq)
{
mDNSu32 code = m->LastNATMapResultCode;
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &code);
if (!num)
LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LastNATMapResultCode");
else
{
CFDictionarySetValue(dict, CFSTR("LastNATMapResultCode"), num);
CFRelease(num);
}
}
mDNS_snprintf(buffer, sizeof(buffer), "Success");
llqStatus = UpdateLLQStatus(m, llqBuffer, sizeof(llqBuffer), info);
status = UpdateRRStatus(m, buffer, sizeof(buffer), info);
if (status != mStatus_NoError)
{
LogInfo("UpdateAutoTunnelDomainStatus: RR Status %d, %s", status, buffer);
}
else if (m->Router.type == mDNSAddrType_None)
{
status = mStatus_NoRouter;
mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none");
}
else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4))
{
status = mStatus_NoRouter;
mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero");
}
else if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrOut))
{
status = info->AutoTunnel == btmmprefix ? mStatus_ServiceNotRunning : mStatus_PollingMode;
mDNS_snprintf(buffer, sizeof(buffer), "No relay connection");
}
else if (!llq && !tun)
{
status = mStatus_NotInitializedErr;
mDNS_snprintf(buffer, sizeof(buffer), "Neither LLQ nor AutoTunnel NAT port mapping is currently active");
}
else if (llqStatus == mStatus_NoSuchRecord)
{
status = llqStatus;
mDNS_snprintf(buffer, sizeof(buffer), llqBuffer);
}
else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT)))
{
status = mStatus_DoubleNAT;
mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting an external address");
}
else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_NATPortMappingDisabled) || (tun && tun->Result == mStatus_NATPortMappingDisabled) ||
(m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort))))))
{
status = mStatus_NATPortMappingDisabled;
mDNS_snprintf(buffer, sizeof(buffer), "NAT-PMP is disabled on the router");
}
else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result) || (tun && tun->Result)))
{
status = mStatus_NATTraversal;
mDNS_snprintf(buffer, sizeof(buffer), "Error obtaining NAT port mapping from router");
}
else if (info->AutoTunnel == btmmprefix && ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort))))
{
status = mStatus_NATTraversal;
mDNS_snprintf(buffer, sizeof(buffer), "Unable to obtain NAT port mapping from router");
}
else if (info->AutoTunnel == btmmprefix || llqStatus != mStatus_PollingMode)
{
status = llqStatus;
mDNS_snprintf(buffer, sizeof(buffer), llqBuffer);
LogInfo("UpdateAutoTunnelDomainStatus: LLQ Status %d, %s", status, buffer);
}
else
{
mDNS_snprintf(buffer, sizeof(buffer), "Polling success");
}
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &status);
if (!num)
LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber StatusCode");
else
{
CFDictionarySetValue(dict, CFSTR("StatusCode"), num);
CFRelease(num);
}
tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
if (!tmp)
LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString StatusMessage");
else
{
CFDictionarySetValue(dict, CFSTR("StatusMessage"), tmp);
CFRelease(tmp);
}
if (!CFDictionaryContainsKey(domainStatusDict, domain) ||
!CFEqual(dict, (CFMutableDictionaryRef)CFDictionaryGetValue(domainStatusDict, domain)))
{
CFDictionarySetValue(domainStatusDict, domain, dict);
if (!m->ShutdownTime)
{
static char statusBuf[16];
mDNS_snprintf(statusBuf, sizeof(statusBuf), "%d", (int)status);
mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.domainstatus", status ? "failure" : "success", statusBuf, "");
mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict);
}
}
CFRelease(domain);
CFRelease(dict);
debugf("UpdateAutoTunnelDomainStatus: %s", buffer);
#endif // def NO_SECURITYFRAMEWORK
}
mDNSexport void UpdateAutoTunnelDomainStatuses(const mDNS *const m)
{
#ifdef NO_SECURITYFRAMEWORK
(void)m;
#else
if (!m->mDNS_busy) LogMsg("UpdateAutoTunnelDomainStatuses: ERROR!! Lock not held");
DomainAuthInfo* info;
for (info = m->AuthInfoList; info; info = info->next)
if (info->AutoTunnel && !info->deltime)
UpdateAutoTunnelDomainStatus(m, info);
#endif // def NO_SECURITYFRAMEWORK
}
mDNSlocal mDNSBool TunnelServers(mDNS *const m)
{
AuthRecord *r;
for (r = m->ResourceRecords; r; r = r->next)
if (r->resrec.rrtype == kDNSType_SRV)
{
DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, r->resrec.name);
if (AuthInfo && AuthInfo->AutoTunnel && !AuthInfo->deltime) return(mDNStrue);
}
return(mDNSfalse);
}
mDNSlocal mDNSBool TunnelClients(mDNS *const m)
{
ClientTunnel *p;
for (p = m->TunnelClients; p; p = p->next)
if (p->q.ThisQInterval < 0)
return(mDNStrue);
return(mDNSfalse);
}
mDNSlocal void UpdateAnonymousRacoonConfig(mDNS *m) {
DomainAuthInfo *info;
for (info = m->AuthInfoList; info; info = info->next)
if (info->AutoTunnel && !info->deltime && (!mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) || !mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn)))
break;
if (info != AnonymousRacoonConfig)
{
AnonymousRacoonConfig = info;
(void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? AnonymousRacoonConfig->AutoTunnel : mDNSNULL, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL);
}
}
mDNSlocal void RegisterAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info)
{
mStatus err;
mDNSBool NATProblem;
if (!m->mDNS_busy) LogMsg("RegisterAutoTunnelHostRecord: ERROR!! Lock not held");
if (!info->AutoTunnelNAT.clientContext) { LogInfo("RegisterAutoTunnelHostRecord: No services registered, not registering the record\n"); return; }
NATProblem = mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) || info->AutoTunnelNAT.Result;
if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn))
{
if (NATProblem)
{
LogInfo("RegisterAutoTunnelHostRecord %##s, not registering the Host Record, Neither AutoTunnel6 nor NAT is available, ClientContext %p, ExternalPort %d, NAT Result %d", info->domain.c, info->AutoTunnelNAT.clientContext, mDNSVal16(info->AutoTunnelNAT.ExternalPort), info->AutoTunnelNAT.Result);
return;
}
}
else
{
if (m->SleepState != SleepState_Awake && NATProblem)
{
LogInfo("RegisterAutoTunnelHostRecord %##s, not registering the Host Record, Not in awake state(%d), and some NAT Problem, ClientContext %p, ExternalPort %d, NAT Result %d", info->domain.c, m->SleepState, info->AutoTunnelNAT.clientContext, mDNSVal16(info->AutoTunnelNAT.ExternalPort), info->AutoTunnelNAT.Result);
return;
}
}
if (!mDNSIPPortIsZero(info->AutoTunnelNAT.RequestedPort) && info->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered)
{
info->AutoTunnelHostRecord.namestorage.c[0] = 0;
AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel);
AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain);
info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = m->AutoTunnelHostAddr;
info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeKnownUnique;
err = mDNS_Register_internal(m, &info->AutoTunnelHostRecord);
if (err) LogMsg("RegisterAutoTunnelHostRecord error %d registering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c);
else
{
m->NextSRVUpdate = NonZeroTime(m->timenow);
LogInfo("RegisterAutoTunnelHostRecord registering AutoTunnelHostRecord %##s", info->AutoTunnelHostRecord.namestorage.c);
}
}
else LogInfo("RegisterAutoTunnelHostRecord: Not registering Context %p Port %d Type %d", info->AutoTunnelNAT.clientContext, mDNSVal16(info->AutoTunnelNAT.RequestedPort), info->AutoTunnelHostRecord.resrec.RecordType);
}
mDNSlocal void DeregisterAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info)
{
LogInfo("DeregisterAutoTunnelHostRecord %##s", info->domain.c);
if (info->AutoTunnel6Record.resrec.RecordType > kDNSRecordTypeDeregistering ||
info->AutoTunnelService.resrec.RecordType > kDNSRecordTypeDeregistering)
{
LogInfo("DeregisterAutoTunnelHostRecord %##s, not deregistering the Host Record AutoTunnel6 RecordType:%d AutoTunnel RecordType: %d", info->domain.c,
info->AutoTunnel6Record.resrec.RecordType, info->AutoTunnelService.resrec.RecordType);
return;
}
if (info->AutoTunnelHostRecord.resrec.RecordType > kDNSRecordTypeDeregistering)
{
mStatus err = mDNS_Deregister(m, &info->AutoTunnelHostRecord);
if (err)
{
info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered;
LogMsg("DeregisterAutoTunnelHostRecord error %d deregistering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c);
}
else LogInfo("DeregisterAutoTunnelHostRecord: Deregistered AutoTunnel Host Record");
}
else LogInfo("DeregisterAutoTunnelHostRecord: Not deregistering Host Record state:%d", info->AutoTunnelHostRecord.resrec.RecordType);
}
mDNSlocal void RegisterAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info)
{
mStatus err;
if (info->AutoTunnelNAT.clientContext && !info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && info->AutoTunnelTarget.resrec.RecordType == kDNSRecordTypeUnregistered)
{
LogInfo("RegisterAutoTunnelServiceRecords %##s (%#s)", info->domain.c, m->hostlabel.c);
info->AutoTunnelTarget.namestorage.c[0] = 0;
AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->AutoTunnelLabel);
AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain);
info->AutoTunnelTarget.resrec.rdata->u.ipv4 = info->AutoTunnelNAT.ExternalAddress;
info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique;
err = mDNS_Register(m, &info->AutoTunnelTarget);
if (err) LogMsg("RegisterAutoTunnelServiceRecords error %d registering AutoTunnelTarget %##s", err, info->AutoTunnelTarget.namestorage.c);
else LogInfo("RegisterAutoTunnelServiceRecords registering AutoTunnelTarget %##s", info->AutoTunnelTarget.namestorage.c);
}
if (info->AutoTunnelNAT.clientContext && !info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && info->AutoTunnelService.resrec.RecordType == kDNSRecordTypeUnregistered)
{
AssignDomainName (&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel);
AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain);
info->AutoTunnelService.resrec.rdata->u.srv.priority = 0;
info->AutoTunnelService.resrec.rdata->u.srv.weight = 0;
info->AutoTunnelService.resrec.rdata->u.srv.port = info->AutoTunnelNAT.ExternalPort;
AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage);
info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique;
err = mDNS_Register(m, &info->AutoTunnelService);
if (err) LogMsg("RegisterAutoTunnelServiceRecords error %d registering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c);
else LogInfo("RegisterAutoTunnelServiceRecords registering AutoTunnelService %##s", info->AutoTunnelService.namestorage.c);
LogInfo("AutoTunnel server listening for connections on %##s[%.4a]:%d:%##s[%.16a]",
info->AutoTunnelTarget.namestorage.c, &m->AdvertisedV4.ip.v4, mDNSVal16(info->AutoTunnelNAT.IntPort),
info->AutoTunnelHostRecord.namestorage.c, &m->AutoTunnelHostAddr);
}
mDNS_Lock(m);
RegisterAutoTunnelHostRecord(m, info);
mDNS_Unlock(m);
}
mDNSlocal void DeregisterAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info)
{
LogInfo("DeregisterAutoTunnelServiceRecords %##s", info->domain.c);
if (info->AutoTunnelTarget.resrec.RecordType > kDNSRecordTypeDeregistering)
{
mStatus err = mDNS_Deregister(m, &info->AutoTunnelTarget);
if (err)
{
info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeUnregistered;
LogMsg("DeregisterAutoTunnelServiceRecords error %d deregistering AutoTunnelTarget %##s", err, info->AutoTunnelTarget.namestorage.c);
}
else LogInfo("DeregisterAutoTunnelServiceRecords: Deregistered AutoTunnel Target Record");
}
else LogInfo("DeregisterAutoTunnelServiceRecords: Not deregistering Target record state:%d", info->AutoTunnelService.resrec.RecordType);
if (info->AutoTunnelService.resrec.RecordType > kDNSRecordTypeDeregistering)
{
mStatus err = mDNS_Deregister(m, &info->AutoTunnelService);
if (err)
{
info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeUnregistered;
LogMsg("DeregisterAutoTunnelServiceRecords error %d deregistering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c);
}
else LogInfo("DeregisterAutoTunnelServiceRecords: Deregistered AutoTunnel Service Record");
}
else LogInfo("DeregisterAutoTunnelServiceRecords: Not deregistering service records state:%d", info->AutoTunnelService.resrec.RecordType);
DeregisterAutoTunnelHostRecord(m, info);
}
mDNSlocal void RegisterAutoTunnelDevInfoRecord(mDNS *m, DomainAuthInfo *info)
{
mStatus err;
if (!m->mDNS_busy) LogMsg("RegisterAutoTunnelDevInfoRecord: Lock not held");
if (info->AutoTunnelNAT.clientContext && !mDNSIPPortIsZero(info->AutoTunnelNAT.RequestedPort) && info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
{
ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain);
mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len; info->AutoTunnelDeviceInfo.resrec.rdlength = 7 + len; info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique;
err = mDNS_Register_internal(m, &info->AutoTunnelDeviceInfo);
if (err) LogMsg("RegisterAutoTunnelDevInfoRecord error %d registering AutoTunnelDeviceInfo %##s", err, info->AutoTunnelDeviceInfo.namestorage.c);
else LogInfo("RegisterAutoTunnelDevInfoRecord registering AutoTunnelDeviceInfo %##s", info->AutoTunnelDeviceInfo.namestorage.c);
}
}
#ifndef NO_SECURITYFRAMEWORK
mDNSlocal void DeregisterAutoTunnelDevInfoRecord(mDNS *m, DomainAuthInfo *info)
{
LogInfo("DeregisterAutoTunnelDevInfoRecord %##s", info->domain.c);
if (info->AutoTunnelDeviceInfo.resrec.RecordType > kDNSRecordTypeDeregistering)
{
mStatus err = mDNS_Deregister(m, &info->AutoTunnelDeviceInfo);
if (err)
{
info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered;
LogMsg("DeregisterAutoTunnelDevInfoRecord error %d deregistering AutoTunnelDeviceInfo %##s", err, info->AutoTunnelDeviceInfo.namestorage.c);
}
else LogInfo("DeregisterAutoTunnelDevInfoRecord: Deregistered AutoTunnel Device Info");
}
else LogInfo("DeregisterAutoTunnelDevInfoRecord: Not deregistering DeviceInfo Record state:%d", info->AutoTunnelDeviceInfo.resrec.RecordType);
}
#endif
mDNSlocal void RegisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info)
{
mStatus err;
if (!m->mDNS_busy) LogMsg("RegisterAutoTunnel6Record: ERROR!! Lock not held");
if (m->SleepState != SleepState_Awake)
{
LogInfo("RegisterAutoTunnel6Record: Not in awake state, SleepState %d", m->SleepState);
return;
}
if (!m->RegisterAutoTunnel6 || DisableInboundRelayConnection)
{
LogInfo("RegisterAutoTunnel6Record: registration Disabled RegisterAutoTunnel6 %d, DisableInbound %d",
m->RegisterAutoTunnel6, DisableInboundRelayConnection);
return;
}
if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn))
{
LogInfo("RegisterAutoTunnel6Record: Relay address is zero, not registering");
return;
}
if ((info->AutoTunnel6Record.resrec.RecordType > kDNSRecordTypeDeregistering) &&
(mDNSSameIPv6Address(info->AutoTunnel6Record.resrec.rdata->u.ipv6, m->AutoTunnelRelayAddrIn)))
{
LogInfo("RegisterAutoTunnel6Record: Relay address %.16a same, not registering", &m->AutoTunnelRelayAddrIn);
return;
}
if (info->AutoTunnelNAT.clientContext && !mDNSIPPortIsZero(info->AutoTunnelNAT.RequestedPort) &&
info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered)
{
AssignDomainName (&info->AutoTunnel6Record.namestorage, (const domainname*) "\x0C" "_autotunnel6");
AppendDomainLabel(&info->AutoTunnel6Record.namestorage, &m->hostlabel);
AppendDomainName (&info->AutoTunnel6Record.namestorage, &info->domain);
info->AutoTunnel6Record.resrec.rdata->u.ipv6 = m->AutoTunnelRelayAddrIn;
info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeKnownUnique;
err = mDNS_Register_internal(m, &info->AutoTunnel6Record);
if (err) LogMsg("RegisterAutoTunnel6Record error %d registering AutoTunnel6 Record %##s", err, info->AutoTunnel6Record.namestorage.c);
else LogInfo("RegisterAutoTunnel6Record registering AutoTunnel6 Record %##s", info->AutoTunnel6Record.namestorage.c);
LogInfo("AutoTunnel6 server listening for connections on %##s[%.16a] :%##s[%.16a]",
info->AutoTunnel6Record.namestorage.c, &m->AutoTunnelRelayAddrIn,
info->AutoTunnelHostRecord.namestorage.c, &m->AutoTunnelHostAddr);
} else {LogInfo("RegisterAutoTunnel6Record: client context %p, RequestedPort %d, Address %.16a, record type %d", info->AutoTunnelNAT.clientContext, info->AutoTunnelNAT.RequestedPort, &m->AutoTunnelRelayAddrIn, info->AutoTunnel6Record.resrec.RecordType);}
RegisterAutoTunnelHostRecord(m, info);
UpdateAnonymousRacoonConfig(m); UpdateAutoTunnelDomainStatus(m, info);
}
mDNSlocal void DeregisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info)
{
LogInfo("DeregisterAutoTunnel6Record %##s", info->domain.c);
if (info->AutoTunnel6Record.resrec.RecordType > kDNSRecordTypeDeregistering)
{
mStatus err = mDNS_Deregister(m, &info->AutoTunnel6Record);
if (err)
{
info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeUnregistered;
info->AutoTunnel6Record.resrec.rdata->u.ipv6 = zerov6Addr;
LogMsg("DeregisterAutoTunnel6Record error %d deregistering AutoTunnel6Record %##s", err, info->AutoTunnel6Record.namestorage.c);
}
else LogInfo("DeregisterAutoTunnel6Record: Deregistered AutoTunnel6 Record");
}
else LogInfo("DeregisterAutoTunnel6Record: Not deregistering AuoTunnel6 record state:%d", info->AutoTunnel6Record.resrec.RecordType);
DeregisterAutoTunnelHostRecord(m, info);
mDNS_Lock(m);
UpdateAutoTunnelDomainStatus(m, info);
mDNS_Unlock(m);
}
mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
{
DomainAuthInfo *info = (DomainAuthInfo *)rr->RecordContext;
if (result == mStatus_MemFree)
{
LogInfo("AutoTunnelRecordCallback MemFree %s", ARDisplayString(m, rr));
if (rr == &info->AutoTunnelHostRecord)
{
rr->namestorage.c[0] = 0;
m->NextSRVUpdate = NonZeroTime(m->timenow);
LogInfo("AutoTunnelRecordCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow);
}
if (m->ShutdownTime) {LogInfo("AutoTunnelRecordCallback: Shutdown, returning");return;}
if (rr == &info->AutoTunnelHostRecord)
{
LogInfo("AutoTunnelRecordCallback: calling RegisterAutoTunnelHostRecord");
RegisterAutoTunnelHostRecord(m,info);
}
else if (rr == &info->AutoTunnelDeviceInfo)
{
LogInfo("AutoTunnelRecordCallback: Calling RegisterAutoTunnelDevInfoRecord");
RegisterAutoTunnelDevInfoRecord(m,info);
}
else if (rr == &info->AutoTunnelService || rr == &info->AutoTunnelTarget)
{
LogInfo("AutoTunnelRecordCallback: Calling RegisterAutoTunnelServiceRecords");
RegisterAutoTunnelServiceRecords(m,info);
}
else if (rr == &info->AutoTunnel6Record)
{
LogInfo("AutoTunnelRecordCallback: Calling RegisterAutoTunnel6Record");
info->AutoTunnel6Record.resrec.rdata->u.ipv6 = zerov6Addr;
RegisterAutoTunnel6Record(m,info);
}
}
}
#ifndef NO_SECURITYFRAMEWORK
mDNSlocal void AutoTunnelDeleteAuthInfoState(mDNS *m, DomainAuthInfo *info)
{
LogInfo("AutoTunnelDeleteAuthInfoState: Cleaning up state releated to Domain AuthInfo %##s", info->domain.c);
m->NextSRVUpdate = NonZeroTime(m->timenow);
DeregisterAutoTunnelDevInfoRecord(m, info);
DeregisterAutoTunnelServiceRecords(m, info);
DeregisterAutoTunnel6Record(m, info);
UpdateAnonymousRacoonConfig(m); UpdateAutoTunnelDomainStatus(m, info);
}
#endif // ndef NO_SECURITYFRAMEWORK
mDNSlocal void AutoTunnelNATCallback(mDNS *m, NATTraversalInfo *n)
{
DomainAuthInfo *info = (DomainAuthInfo *)n->clientContext;
LogInfo("AutoTunnelNATCallback Result %d %.4a Internal %d External %d %#s.%##s",
n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), m->hostlabel.c, info->domain.c);
m->NextSRVUpdate = NonZeroTime(m->timenow);
LogInfo("AutoTunnelNATCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow);
DeregisterAutoTunnelServiceRecords(m, info);
RegisterAutoTunnelServiceRecords(m, info);
UpdateAnonymousRacoonConfig(m);
UpdateAutoTunnelDomainStatus(m, (DomainAuthInfo *)n->clientContext);
}
mDNSlocal void AbortDeregistration(mDNS *const m, AuthRecord *rr)
{
if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
{
LogInfo("Aborting deregistration of %s", ARDisplayString(m, rr));
CompleteDeregistration(m, rr);
}
else if (rr->resrec.RecordType != kDNSRecordTypeUnregistered)
LogMsg("AbortDeregistration ERROR RecordType %02X for %s", ARDisplayString(m, rr));
}
mDNSlocal void AutoTunnelHostNameChanged(mDNS *m, DomainAuthInfo *info)
{
LogInfo("AutoTunnelHostNameChanged %#s.%##s", m->hostlabel.c, info->domain.c);
#ifndef NO_SECURITYFRAMEWORK
DeregisterAutoTunnelDevInfoRecord(m, info);
#endif
DeregisterAutoTunnelServiceRecords(m, info);
DeregisterAutoTunnel6Record(m, info);
RegisterAutoTunnelServiceRecords(m, info);
mDNS_Lock(m);
RegisterAutoTunnelDevInfoRecord(m, info);
RegisterAutoTunnel6Record(m, info);
m->NextSRVUpdate = NonZeroTime(m->timenow);
mDNS_Unlock(m);
}
mDNSlocal void SetupLocalAutoTunnel6Records(mDNS *const m, DomainAuthInfo *info)
{
AbortDeregistration(m, &info->AutoTunnelDeviceInfo);
AbortDeregistration(m, &info->AutoTunnel6Record);
if (info->AutoTunnelDeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered ||
info->AutoTunnel6Record.resrec.RecordType != kDNSRecordTypeUnregistered)
{
LogInfo("SetupLocalAutoTunnel6Records: AutoTunnel Records not in Unregistered state: Device: %d, AutoTunnel6:%d",
info->AutoTunnelDeviceInfo.resrec.RecordType, info->AutoTunnel6Record.resrec.RecordType);
}
if (info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
{
mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
RegisterAutoTunnelDevInfoRecord(m, info);
}
if (info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered)
{
mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
RegisterAutoTunnel6Record(m, info);
}
UpdateAnonymousRacoonConfig(m);
UpdateAutoTunnelDomainStatus(m, info);
m->NextSRVUpdate = NonZeroTime(m->timenow);
}
mDNSexport void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool servicesStarting)
{
if (!m->AutoTunnelHostAddrActive)
{
m->AutoTunnelHostAddrActive = mDNStrue;
LogInfo("SetupLocalAutoTunnelInterface_internal: Setting up AutoTunnel address %.16a", &m->AutoTunnelHostAddr);
(void)mDNSAutoTunnelInterfaceUpDown(kmDNSUp, m->AutoTunnelHostAddr.b);
}
if (servicesStarting || TunnelServers(m))
{
DomainAuthInfo *info;
for (info = m->AuthInfoList; info; info = info->next)
{
if (info->AutoTunnel && !info->deltime && !info->AutoTunnelNAT.clientContext)
{
AbortDeregistration(m, &info->AutoTunnelTarget);
AbortDeregistration(m, &info->AutoTunnelService);
AbortDeregistration(m, &info->AutoTunnelHostRecord);
mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL,
kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV, kHostNameTTL,
kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL,
kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
info->AutoTunnelNAT.clientCallback = AutoTunnelNATCallback;
info->AutoTunnelNAT.clientContext = info;
info->AutoTunnelNAT.Protocol = NATOp_MapUDP;
info->AutoTunnelNAT.IntPort = IPSECPort;
info->AutoTunnelNAT.RequestedPort = IPSECPort;
info->AutoTunnelNAT.NATLease = 0;
mStatus err = mDNS_StartNATOperation_internal(m, &info->AutoTunnelNAT);
if (err) LogMsg("SetupLocalAutoTunnelInterface_internal: error %d starting NAT mapping", err);
SetupLocalAutoTunnel6Records(m, info);
}
}
}
}
mDNSlocal mStatus AutoTunnelSetKeys(ClientTunnel *tun, mDNSBool AddNew)
{
mDNSv6Addr loc_outer6;
mDNSv6Addr rmt_outer6;
if (mDNSIPPortIsZero(tun->rmt_outer_port))
{
loc_outer6 = tun->loc_outer6;
rmt_outer6 = tun->rmt_outer6;
}
else
{
loc_outer6 = zerov6Addr;
loc_outer6.b[0] = tun->loc_outer.b[0];
loc_outer6.b[1] = tun->loc_outer.b[1];
loc_outer6.b[2] = tun->loc_outer.b[2];
loc_outer6.b[3] = tun->loc_outer.b[3];
rmt_outer6 = zerov6Addr;
rmt_outer6.b[0] = tun->rmt_outer.b[0];
rmt_outer6.b[1] = tun->rmt_outer.b[1];
rmt_outer6.b[2] = tun->rmt_outer.b[2];
rmt_outer6.b[3] = tun->rmt_outer.b[3];
}
return(mDNSAutoTunnelSetKeys(AddNew ? kmDNSAutoTunnelSetKeysReplace : kmDNSAutoTunnelSetKeysDelete, tun->loc_inner.b, loc_outer6.b, kRacoonPort, tun->rmt_inner.b, rmt_outer6.b, mDNSVal16(tun->rmt_outer_port), tun->prefix, SkipLeadingLabels(&tun->dstname, 1)));
}
#define mDNSSameClientTunnel(A,B) ((A)->l[2] == (B)->l[2] && (A)->l[3] == (B)->l[3])
mDNSlocal void ReissueBlockedQuestionWithType(mDNS *const m, domainname *d, mDNSBool success, mDNSu16 qtype)
{
DNSQuestion *q = m->Questions;
while (q)
{
if (q->NoAnswer == NoAnswer_Suspended && q->qtype == qtype && q->AuthInfo && q->AuthInfo->AutoTunnel && SameDomainName(&q->qname, d))
{
LogInfo("Restart %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
mDNSQuestionCallback *tmp = q->QuestionCallback;
q->QuestionCallback = AutoTunnelCallback; mDNS_StopQuery(m, q);
mDNS_StartQuery(m, q);
q->QuestionCallback = tmp; if (!success) q->NoAnswer = NoAnswer_Fail;
q = m->Questions;
}
else
q = q->next;
}
}
mDNSlocal void ReissueBlockedQuestions(mDNS *const m, domainname *d, mDNSBool success)
{
ReissueBlockedQuestionWithType(m, d, success, kDNSType_AAAA);
ReissueBlockedQuestionWithType(m, d, mDNStrue, kDNSType_A);
}
mDNSlocal void UnlinkAndReissueBlockedQuestions(mDNS *const m, ClientTunnel *tun, mDNSBool success)
{
ClientTunnel **p = &m->TunnelClients;
while (*p != tun && *p) p = &(*p)->next;
if (*p) *p = tun->next;
ReissueBlockedQuestions(m, &tun->dstname, success);
LogInfo("UnlinkAndReissueBlockedQuestions: Disposing ClientTunnel %p", tun);
freeL("ClientTunnel", tun);
}
mDNSlocal mDNSBool TunnelClientDeleteMatching(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel)
{
ClientTunnel **p;
mDNSBool needSetKeys = mDNStrue;
p = &tun->next;
while (*p)
{
if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next;
else
{
ClientTunnel *old = *p;
if (v6Tunnel)
{
if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; }
LogInfo("TunnelClientDeleteMatching: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
if (old->q.ThisQInterval >= 0)
{
LogInfo("TunnelClientDeleteMatching: Stopping query on IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
mDNS_StopQuery(m, &old->q);
}
else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) ||
!mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) ||
!mDNSSameIPv6Address(old->loc_outer6, tun->loc_outer6) ||
!mDNSSameIPv6Address(old->rmt_outer6, tun->rmt_outer6))
{
LogInfo("TunnelClientDeleteMatching: Deleting existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
AutoTunnelSetKeys(old, mDNSfalse);
}
else
{
LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c,
&old->rmt_inner);
needSetKeys = mDNSfalse;
}
}
else
{
if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; }
LogInfo("TunnelClientDeleteMatching: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
if (old->q.ThisQInterval >= 0)
{
LogInfo("TunnelClientDeleteMatching: Stopping query on IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
mDNS_StopQuery(m, &old->q);
}
else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) ||
!mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) ||
!mDNSSameIPv4Address(old->loc_outer, tun->loc_outer) ||
!mDNSSameIPv4Address(old->rmt_outer, tun->rmt_outer) ||
!mDNSSameIPPort(old->rmt_outer_port, tun->rmt_outer_port))
{
LogInfo("TunnelClientDeleteMatching: Deleting existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
AutoTunnelSetKeys(old, mDNSfalse);
}
else
{
LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c,
&old->rmt_inner);
needSetKeys = mDNSfalse;
}
}
*p = old->next;
LogInfo("TunnelClientDeleteMatching: Disposing ClientTunnel %p", old);
freeL("ClientTunnel", old);
}
}
return needSetKeys;
}
mDNSlocal void TunnelClientDeleteAny(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel)
{
ClientTunnel **p;
p = &tun->next;
while (*p)
{
if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next;
else
{
ClientTunnel *old = *p;
if (v6Tunnel)
{
if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;}
LogInfo("TunnelClientDeleteAny: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
}
else
{
if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;}
LogInfo("TunnelClientDeleteAny: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
}
if (old->q.ThisQInterval >= 0)
{
LogInfo("TunnelClientDeleteAny: Stopping query on AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
mDNS_StopQuery(m, &old->q);
}
else
{
LogInfo("TunnelClientDeleteAny: Deleting existing AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner);
AutoTunnelSetKeys(old, mDNSfalse);
}
*p = old->next;
LogInfo("TunnelClientDeleteAny: Disposing ClientTunnel %p", old);
freeL("ClientTunnel", old);
}
}
}
mDNSlocal void TunnelClientFinish(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
{
mDNSBool needSetKeys = mDNStrue;
ClientTunnel *tun = (ClientTunnel *)question->QuestionContext;
mDNSBool v6Tunnel = mDNSfalse;
if (mDNSIPPortIsZero(tun->rmt_outer_port))
v6Tunnel = mDNStrue;
if (v6Tunnel)
{
LogInfo("TunnelClientFinish: Relay address %.16a", &answer->rdata->u.ipv6);
tun->rmt_outer6 = answer->rdata->u.ipv6;
tun->loc_outer6 = m->AutoTunnelRelayAddrOut;
}
else
{
LogInfo("TunnelClientFinish: SRV target address %.4a", &answer->rdata->u.ipv4);
tun->rmt_outer = answer->rdata->u.ipv4;
mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} };
tmpDst.ip.v4 = tun->rmt_outer;
mDNSAddr tmpSrc = zeroAddr;
mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst);
if (tmpSrc.type == mDNSAddrType_IPv4) tun->loc_outer = tmpSrc.ip.v4;
else tun->loc_outer = m->AdvertisedV4.ip.v4;
}
question->ThisQInterval = -1; tun->loc_inner = m->AutoTunnelHostAddr;
TunnelClientDeleteAny(m, tun, !v6Tunnel);
needSetKeys = TunnelClientDeleteMatching(m, tun, v6Tunnel);
if (needSetKeys) LogInfo("TunnelClientFinish: New %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner);
else LogInfo("TunnelClientFinish: Reusing exiting %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner);
if (m->AutoTunnelHostAddr.b[0]) { mDNS_Lock(m); SetupLocalAutoTunnelInterface_internal(m, mDNSfalse); mDNS_Unlock(m); };
mStatus result = needSetKeys ? AutoTunnelSetKeys(tun, mDNStrue) : mStatus_NoError;
static char msgbuf[32];
mDNS_snprintf(msgbuf, sizeof(msgbuf), "Tunnel setup - %d", result);
mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", result ? "failure" : "success", msgbuf, "");
ReissueBlockedQuestions(m, &tun->dstname, (result == mStatus_NoError) ? mDNStrue : mDNSfalse);
}
mDNSexport void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
ClientTunnel *tun = (ClientTunnel *)question->QuestionContext;
LogInfo("AutoTunnelCallback tun %p AddRecord %d rdlength %d qtype %d", tun, AddRecord, answer->rdlength, question->qtype);
if (!AddRecord) return;
mDNS_StopQuery(m, question);
if (tun->tc_state != TC_STATE_AAAA_PEER_RELAY && !answer->rdlength)
{
LogInfo("AutoTunnelCallback NXDOMAIN %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
static char msgbuf[16];
mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s lookup", DNSTypeName(question->qtype));
mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", "failure", msgbuf, "");
UnlinkAndReissueBlockedQuestions(m, tun, mDNSfalse);
return;
}
switch (tun->tc_state)
{
case TC_STATE_AAAA_PEER:
if (question->qtype != kDNSType_AAAA)
{
LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER", question->qtype);
}
if (mDNSSameIPv6Address(answer->rdata->u.ipv6, m->AutoTunnelHostAddr))
{
LogInfo("AutoTunnelCallback: suppressing tunnel to self %.16a", &answer->rdata->u.ipv6);
UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue);
return;
}
tun->rmt_inner = answer->rdata->u.ipv6;
LogInfo("AutoTunnelCallback:TC_STATE_AAAA_PEER: dst host %.16a", &tun->rmt_inner);
if (!mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrOut))
{
LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA");
tun->tc_state = TC_STATE_AAAA_PEER_RELAY;
question->qtype = kDNSType_AAAA;
AssignDomainName(&question->qname, (const domainname*) "\x0C" "_autotunnel6");
}
else
{
LogInfo("AutoTunnelCallback: Looking up _autotunnel._udp SRV");
tun->tc_state = TC_STATE_SRV_PEER;
question->qtype = kDNSType_SRV;
AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
}
AppendDomainName(&question->qname, &tun->dstname);
mDNS_StartQuery(m, &tun->q);
return;
case TC_STATE_AAAA_PEER_RELAY:
if (question->qtype != kDNSType_AAAA)
{
LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER_RELAY", question->qtype);
}
if (!answer->rdlength)
{
LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA failed, trying SRV");
tun->tc_state = TC_STATE_SRV_PEER;
AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
AppendDomainName(&question->qname, &tun->dstname);
question->qtype = kDNSType_SRV;
mDNS_StartQuery(m, &tun->q);
return;
}
TunnelClientFinish(m, question, answer);
return;
case TC_STATE_SRV_PEER:
if (question->qtype != kDNSType_SRV)
{
LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_SRV_PEER", question->qtype);
}
LogInfo("AutoTunnelCallback: SRV target name %##s", answer->rdata->u.srv.target.c);
tun->tc_state = TC_STATE_ADDR_PEER;
AssignDomainName(&tun->q.qname, &answer->rdata->u.srv.target);
tun->rmt_outer_port = answer->rdata->u.srv.port;
question->qtype = kDNSType_A;
mDNS_StartQuery(m, &tun->q);
return;
case TC_STATE_ADDR_PEER:
if (question->qtype != kDNSType_A)
{
LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_ADDR_PEER", question->qtype);
}
TunnelClientFinish(m, question, answer);
return;
default:
LogMsg("AutoTunnelCallback: Unknown question %p", question);
}
}
mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q)
{
ClientTunnel *p = mallocL("ClientTunnel", sizeof(ClientTunnel));
if (!p) return;
p->prefix = q->AuthInfo->AutoTunnel;
AssignDomainName(&p->dstname, &q->qname);
p->MarkedForDeletion = mDNSfalse;
p->loc_inner = zerov6Addr;
p->loc_outer = zerov4Addr;
p->loc_outer6 = zerov6Addr;
p->rmt_inner = zerov6Addr;
p->rmt_outer = zerov4Addr;
p->rmt_outer6 = zerov6Addr;
p->rmt_outer_port = zeroIPPort;
p->tc_state = TC_STATE_AAAA_PEER;
p->next = m->TunnelClients;
m->TunnelClients = p;
p->q.InterfaceID = mDNSInterface_Any;
p->q.Target = zeroAddr;
AssignDomainName(&p->q.qname, &q->qname);
p->q.qtype = kDNSType_AAAA;
p->q.qclass = kDNSClass_IN;
p->q.LongLived = mDNSfalse;
p->q.ExpectUnique = mDNStrue;
p->q.ForceMCast = mDNSfalse;
p->q.ReturnIntermed = mDNStrue;
p->q.SuppressUnusable = mDNSfalse;
p->q.SearchListIndex = 0;
p->q.AppendSearchDomains = 0;
p->q.RetryWithSearchDomains = mDNSfalse;
p->q.TimeoutQuestion = 0;
p->q.WakeOnResolve = 0;
p->q.qnameOrig = mDNSNULL;
p->q.QuestionCallback = AutoTunnelCallback;
p->q.QuestionContext = p;
LogInfo("AddNewClientTunnel start tun %p %##s (%s)%s", p, &q->qname.c, DNSTypeName(q->qtype), q->LongLived ? " LongLived" : "");
mDNS_StartQuery_internal(m, &p->q);
}
#endif // APPLE_OSX_mDNSResponder
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Power State & Configuration Change Management
#endif
mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
{
mDNSBool foundav4 = mDNSfalse;
mDNSBool foundav6 = mDNSfalse;
struct ifaddrs *ifa = myGetIfAddrs(1);
struct ifaddrs *v4Loopback = NULL;
struct ifaddrs *v6Loopback = NULL;
char defaultname[64];
#ifndef NO_IPV6
int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0);
if (InfoSocket < 3 && errno != EAFNOSUPPORT) LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno));
#endif
if (m->SleepState == SleepState_Sleeping) ifa = NULL;
while (ifa)
{
#if LIST_ALL_INTERFACES
if (ifa->ifa_addr->sa_family == AF_APPLETALK)
LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_APPLETALK",
ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
else if (ifa->ifa_addr->sa_family == AF_LINK)
LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_LINK",
ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)
LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)",
ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
if (!(ifa->ifa_flags & IFF_UP))
LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_UP",
ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
if (!(ifa->ifa_flags & IFF_MULTICAST))
LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_MULTICAST",
ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
if (ifa->ifa_flags & IFF_POINTOPOINT)
LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT",
ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
if (ifa->ifa_flags & IFF_LOOPBACK)
LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK",
ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
#endif
if (ifa->ifa_addr->sa_family == AF_LINK)
{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == sizeof(m->PrimaryMAC) && mDNSSameEthAddress(&m->PrimaryMAC, &zeroEthAddr))
mDNSPlatformMemCopy(m->PrimaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 6);
}
if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr)
if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6)
{
if (!ifa->ifa_netmask)
{
mDNSAddr ip;
SetupAddr(&ip, ifa->ifa_addr);
LogMsg("getifaddrs: ifa_netmask is NULL for %5s(%d) Flags %04X Family %2d %#a",
ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip);
}
else if (ifa->ifa_netmask->sa_family != ifa->ifa_addr->sa_family && ifa->ifa_netmask->sa_family != 0)
{
mDNSAddr ip;
SetupAddr(&ip, ifa->ifa_addr);
LogMsg("getifaddrs ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d",
ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip, ifa->ifa_netmask->sa_family);
}
else if ((int)if_nametoindex(ifa->ifa_name) <= 0)
{
LogMsg("UpdateInterfaceList: if_nametoindex returned zero/negative value for %5s(%d)", ifa->ifa_name, if_nametoindex(ifa->ifa_name));
}
else
{
ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family;
int ifru_flags6 = 0;
#ifndef NO_IPV6
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0)
{
struct in6_ifreq ifr6;
mDNSPlatformMemZero((char *)&ifr6, sizeof(ifr6));
strlcpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name));
ifr6.ifr_addr = *sin6;
if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1)
ifru_flags6 = ifr6.ifr_ifru.ifru_flags6;
verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6);
}
#endif
if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY)))
{
if (ifa->ifa_flags & IFF_LOOPBACK)
{
if (ifa->ifa_addr->sa_family == AF_INET) v4Loopback = ifa;
#ifndef NO_IPV6
else if (sin6->sin6_addr.s6_addr[0] != 0xFD) v6Loopback = ifa;
#endif
}
else
{
NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc);
if (i && MulticastInterface(i) && i->ifinfo.Advertise)
{
if (ifa->ifa_addr->sa_family == AF_INET) foundav4 = mDNStrue;
else foundav6 = mDNStrue;
}
}
}
}
}
ifa = ifa->ifa_next;
}
if (!foundav4 && v4Loopback) AddInterfaceToList(m, v4Loopback, utc);
if (!foundav6 && v6Loopback) AddInterfaceToList(m, v6Loopback, utc);
NetworkInterfaceInfoOSX *i;
for (i = m->p->InterfaceList; i; i = i->next)
if (i->Exists)
{
mDNSBool txrx = MulticastInterface(i);
#if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4
txrx = txrx && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id));
#endif
if (i->ifinfo.McastTxRx != txrx)
{
i->ifinfo.McastTxRx = txrx;
i->Exists = 2; }
}
#ifndef NO_IPV6
if (InfoSocket >= 0) close(InfoSocket);
#endif
if (!mDNSSameEthAddress(&m->PrimaryMAC, &zeroEthAddr) && m->AutoTunnelHostAddr.b[0] == 0)
{
m->AutoTunnelHostAddr.b[0x0] = 0xFD; m->AutoTunnelHostAddr.b[0x1] = mDNSRandom(255);
m->AutoTunnelHostAddr.b[0x2] = mDNSRandom(255);
m->AutoTunnelHostAddr.b[0x3] = mDNSRandom(255);
m->AutoTunnelHostAddr.b[0x4] = mDNSRandom(255);
m->AutoTunnelHostAddr.b[0x5] = mDNSRandom(255);
m->AutoTunnelHostAddr.b[0x6] = mDNSRandom(255);
m->AutoTunnelHostAddr.b[0x7] = mDNSRandom(255);
m->AutoTunnelHostAddr.b[0x8] = m->PrimaryMAC.b[0] ^ 0x02; m->AutoTunnelHostAddr.b[0x9] = m->PrimaryMAC.b[1];
m->AutoTunnelHostAddr.b[0xA] = m->PrimaryMAC.b[2];
m->AutoTunnelHostAddr.b[0xB] = 0xFF;
m->AutoTunnelHostAddr.b[0xC] = 0xFE;
m->AutoTunnelHostAddr.b[0xD] = m->PrimaryMAC.b[3];
m->AutoTunnelHostAddr.b[0xE] = m->PrimaryMAC.b[4];
m->AutoTunnelHostAddr.b[0xF] = m->PrimaryMAC.b[5];
m->AutoTunnelLabel.c[0] = mDNS_snprintf((char*)m->AutoTunnelLabel.c+1, 254, "AutoTunnel-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
m->AutoTunnelHostAddr.b[0x8], m->AutoTunnelHostAddr.b[0x9], m->AutoTunnelHostAddr.b[0xA], m->AutoTunnelHostAddr.b[0xB],
m->AutoTunnelHostAddr.b[0xC], m->AutoTunnelHostAddr.b[0xD], m->AutoTunnelHostAddr.b[0xE], m->AutoTunnelHostAddr.b[0xF]);
LogInfo("m->AutoTunnelLabel %#s", m->AutoTunnelLabel.c);
}
mDNS_snprintf(defaultname, sizeof(defaultname), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen, HINFO_HWstring,
m->PrimaryMAC.b[0], m->PrimaryMAC.b[1], m->PrimaryMAC.b[2], m->PrimaryMAC.b[3], m->PrimaryMAC.b[4], m->PrimaryMAC.b[5]);
domainlabel nicelabel;
nicelabel.c[0] = 0;
GetUserSpecifiedFriendlyComputerName(&nicelabel);
if (nicelabel.c[0] == 0)
{
debugf("Couldn’t read user-specified Computer Name; using default “%s” instead", defaultname);
MakeDomainLabelFromLiteralString(&nicelabel, defaultname);
}
domainlabel hostlabel;
hostlabel.c[0] = 0;
GetUserSpecifiedLocalHostName(&hostlabel);
if (hostlabel.c[0] == 0)
{
debugf("Couldn’t read user-specified Local Hostname; using default “%s.local” instead", defaultname);
MakeDomainLabelFromLiteralString(&hostlabel, defaultname);
}
mDNSBool namechange = mDNSfalse;
if (SameDomainLabelCS(m->p->usernicelabel.c, nicelabel.c))
debugf("Usernicelabel (%#s) unchanged since last time; not changing m->nicelabel (%#s)", m->p->usernicelabel.c, m->nicelabel.c);
else
{
if (m->p->usernicelabel.c[0]) LogMsg("User updated Computer Name from “%#s” to “%#s”", m->p->usernicelabel.c, nicelabel.c);
m->p->usernicelabel = m->nicelabel = nicelabel;
namechange = mDNStrue;
}
if (SameDomainLabelCS(m->p->userhostlabel.c, hostlabel.c))
debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c);
else
{
if (m->p->userhostlabel.c[0]) LogMsg("User updated Local Hostname from “%#s” to “%#s”", m->p->userhostlabel.c, hostlabel.c);
m->p->userhostlabel = m->hostlabel = hostlabel;
mDNS_SetFQDN(m);
namechange = mDNStrue;
}
#if APPLE_OSX_mDNSResponder
if (namechange) {
DomainAuthInfo *info;
for (info = m->AuthInfoList; info; info = info->next)
if (info->AutoTunnel) AutoTunnelHostNameChanged(m, info);
}
#endif // APPLE_OSX_mDNSResponder
return(mStatus_NoError);
}
mDNSlocal int CountMaskBits(mDNSAddr *mask)
{
int i = 0, bits = 0;
int bytes = mask->type == mDNSAddrType_IPv4 ? 4 : mask->type == mDNSAddrType_IPv6 ? 16 : 0;
while (i < bytes)
{
mDNSu8 b = mask->ip.v6.b[i++];
while (b & 0x80) { bits++; b <<= 1; }
if (b) return(-1);
}
while (i < bytes) if (mask->ip.v6.b[i++]) return(-1);
return(bits);
}
mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc)
{
NetworkInterfaceInfoOSX *i;
int count = 0;
for (i = m->p->InterfaceList; i; i = i->next)
if (i->Exists)
{
NetworkInterfaceInfo *const n = &i->ifinfo;
NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family);
if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname);
if (i->Registered && i->Registered != primary) {
LogMsg("SetupActiveInterfaces ERROR! n->Registered %p != primary %p", i->Registered, primary);
i->Registered = mDNSNULL;
}
if (!i->Registered)
{
i->Registered = primary;
i->Occulting = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->LastSeen > 0 && utc - i->LastSeen < 60);
if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0)
{
LogInfo("SetupActiveInterfaces: P2P %s interface registering %s %s", i->ifinfo.ifname,
i->Flashing ? " (Flashing)" : "",
i->Occulting ? " (Occulting)" : "");
mDNS_RegisterInterface(m, n, 0);
}
else
{
mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting);
}
if (!mDNSAddressIsLinkLocal(&n->ip)) count++;
LogInfo("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s",
i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, &n->ip, CountMaskBits(&n->mask),
i->Flashing ? " (Flashing)" : "",
i->Occulting ? " (Occulting)" : "",
n->InterfaceActive ? " (Primary)" : "");
if (!n->McastTxRx)
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);
else
{
if (i->sa_family == AF_INET)
{
struct ip_mreq imr;
primary->ifa_v4addr.s_addr = n->ip.ip.v4.NotAnInteger;
imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger;
imr.imr_interface = primary->ifa_v4addr;
if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET) == i)
{
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);
mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr));
if (err < 0 && (errno != EADDRNOTAVAIL))
LogMsg("setsockopt - IP_DROP_MEMBERSHIP error %d errno %d (%s)", err, errno, strerror(errno));
}
LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv4 mcast group %.4a on %.4a", i->ifinfo.ifname, i->scope_id, &imr.imr_multiaddr, &imr.imr_interface);
mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr));
if (err < 0 && (errno != EADDRINUSE))
LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err, errno, strerror(errno), &imr.imr_multiaddr, &imr.imr_interface);
}
#ifndef NO_IPV6
if (i->sa_family == AF_INET6)
{
struct ipv6_mreq i6mr;
i6mr.ipv6mr_interface = primary->scope_id;
i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6;
if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET6) == i)
{
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);
mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr));
if (err < 0 && (errno != EADDRNOTAVAIL))
LogMsg("setsockopt - IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
}
LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv6 mcast group %.16a on %u", i->ifinfo.ifname, i->scope_id, &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr));
if (err < 0 && (errno != EADDRINUSE))
LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
}
#endif
}
}
}
return count;
}
mDNSlocal void MarkAllInterfacesInactive(mDNS *const m, mDNSs32 utc)
{
NetworkInterfaceInfoOSX *i;
for (i = m->p->InterfaceList; i; i = i->next)
{
if (i->Exists) i->LastSeen = utc;
i->Exists = mDNSfalse;
}
}
mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc)
{
NetworkInterfaceInfoOSX *i;
int count = 0;
for (i = m->p->InterfaceList; i; i = i->next)
{
NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family);
if (i->Registered)
if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary)
{
i->Flashing = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->AppearanceTime < 60);
LogInfo("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s",
i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary,
&i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask),
i->Flashing ? " (Flashing)" : "",
i->Occulting ? " (Occulting)" : "",
i->ifinfo.InterfaceActive ? " (Primary)" : "");
if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0)
{
LogInfo("ClearInactiveInterfaces: P2P %s interface deregistering %s %s", i->ifinfo.ifname,
i->Flashing ? " (Flashing)" : "",
i->Occulting ? " (Occulting)" : "");
mDNS_DeregisterInterface(m, &i->ifinfo, 0);
}
else
{
mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting);
}
if (!mDNSAddressIsLinkLocal(&i->ifinfo.ip)) count++;
i->Registered = mDNSNULL;
}
}
NetworkInterfaceInfoOSX **p = &m->p->InterfaceList;
while (*p)
{
i = *p;
if (!i->Exists)
{
if (i->LastSeen == utc) i->LastSeen = utc - 1;
mDNSBool delete = (NumCacheRecordsForInterfaceID(m, i->ifinfo.InterfaceID) == 0) && (utc - i->LastSeen >= 60);
LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p(%p) %#a/%d Age %d%s", delete ? "Deleting" : "Holding",
i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i,
&i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), utc - i->LastSeen,
i->ifinfo.InterfaceActive ? " (Primary)" : "");
#if APPLE_OSX_mDNSResponder
if (i->BPF_fd >= 0) CloseBPF(i);
#endif // APPLE_OSX_mDNSResponder
if (delete)
{
*p = i->next;
freeL("NetworkInterfaceInfoOSX", i);
continue; }
}
p = &i->next;
}
return count;
}
mDNSlocal void AppendDNameListElem(DNameListElem ***List, mDNSu32 uid, domainname *name)
{
DNameListElem *dnle = (DNameListElem*) mallocL("DNameListElem/AppendDNameListElem", sizeof(DNameListElem));
if (!dnle) LogMsg("ERROR: AppendDNameListElem: memory exhausted");
else
{
dnle->next = mDNSNULL;
dnle->uid = uid;
AssignDomainName(&dnle->name, name);
**List = dnle;
*List = &dnle->next;
}
}
mDNSlocal int compare_dns_configs(const void *aa, const void *bb)
{
dns_resolver_t *a = *(dns_resolver_t**)aa;
dns_resolver_t *b = *(dns_resolver_t**)bb;
return (a->search_order < b->search_order) ? -1 : (a->search_order == b->search_order) ? 0 : 1;
}
mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool scope, mDNSBool setsearch, mDNSBool setservers)
{
int i, j;
domainname d;
#if DNSINFO_VERSION >= 20091104
dns_resolver_t **resolver = scope ? config->scoped_resolver : config->resolver;
int nresolvers = scope ? config->n_scoped_resolver : config->n_resolver;
#else
(void) scope; dns_resolver_t **resolver = config->resolver;
int nresolvers = config->n_resolver;
#endif
if (setsearch && !scope && nresolvers)
{
if (resolver[0]->n_search == 0)
{
LogInfo("ConfigResolvers: (%s) configuring zeroth domain as search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->domain);
mDNS_AddSearchDomain_CString(resolver[0]->domain, mDNSNULL);
}
else
{
for (i = 0; i < resolver[0]->n_search; i++)
{
LogInfo("ConfigResolvers: (%s) configuring search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->search[i]);
mDNS_AddSearchDomain_CString(resolver[0]->search[i], mDNSNULL);
}
}
}
if (!scope && !setservers) return;
if ((nresolvers != 0) && resolver[0]->domain)
resolver[0]->domain[0] = 0;
qsort(resolver, nresolvers, sizeof(dns_resolver_t*), compare_dns_configs);
for (i = 0; i < nresolvers; i++)
{
int n;
dns_resolver_t *r = resolver[i];
mDNSInterfaceID interface = mDNSInterface_Any;
int disabled = 0;
LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scope ? "Scoped" : "", i, r->domain, r->n_nameserver);
if (r->port == 5353 || r->n_nameserver == 0)
{
char *opt = r->options;
if (opt && !strncmp(opt, "mdns", strlen(opt)))
{
if (!MakeDomainNameFromDNSNameString(&d, r->domain))
{ LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; }
mDNS_AddMcastResolver(m, &d, interface, r->timeout);
}
continue;
}
if (!r->domain || !*r->domain) d.c[0] = 0;
else if (!MakeDomainNameFromDNSNameString(&d, r->domain))
{ LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; }
if (r->options != NULL)
{
char *nextOption = r->options;
char *currentOption = NULL;
while ((currentOption = strsep(&nextOption, " ")) != NULL && currentOption[0] != 0)
{
if (strncmp(currentOption, kInterfaceSpecificOption, sizeof(kInterfaceSpecificOption) - 1) == 0)
{
NetworkInterfaceInfoOSX *ni;
char ifname[IF_NAMESIZE+1];
mDNSu32 ifindex = 0;
strlcpy(ifname, currentOption + sizeof(kInterfaceSpecificOption)-1, sizeof(ifname));
ifindex = if_nametoindex(ifname);
if (ifindex == 0) { disabled = 1; LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - interface %s not found", ifname); continue; }
LogInfo("ConfigResolvers: interface specific entry: %s on %s (%d)", r->domain, ifname, ifindex);
for (ni = m->p->InterfaceList; ni; ni = ni->next)
if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex) break;
if (ni != NULL) interface = ni->ifinfo.InterfaceID;
if (interface == mDNSNULL)
{
disabled = 1;
LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - index %d (%s) not found", ifindex, ifname);
continue;
}
}
}
}
#if DNSINFO_VERSION >= 20091104
if ((interface == mDNSInterface_Any) && (r->if_index != 0))
{
NetworkInterfaceInfoOSX *ni;
interface = mDNSNULL;
for (ni = m->p->InterfaceList; ni; ni = ni->next)
if (ni->ifinfo.InterfaceID && ni->scope_id == r->if_index) break;
if (ni != NULL) interface = ni->ifinfo.InterfaceID;
if (interface == mDNSNULL)
{
disabled = 1;
LogMsg("ConfigResolvers: interface specific index %d not found", r->if_index);
continue;
}
}
#endif
if (setsearch)
{
if (scope)
{
for (j = 0; j < resolver[i]->n_search; j++)
{
LogInfo("ConfigResolvers: (%s) configuring search list %s, Interface %p", scope ? "Scoped" : "Non-scoped", resolver[i]->search[j], interface);
mDNS_AddSearchDomain_CString(resolver[i]->search[j], interface);
}
if (!setservers) continue;
}
}
for (n = 0; n < r->n_nameserver; n++)
if (r->nameserver[n]->sa_family == AF_INET || r->nameserver[n]->sa_family == AF_INET6)
{
mDNSAddr saddr;
if (SetupAddr(&saddr, r->nameserver[n])) LogMsg("RegisterSplitDNS: bad IP address");
else
{
mDNSBool scopedDNS = mDNSfalse;
DNSServer *s;
#if DNSINFO_VERSION >= 20091104
if (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED) && (interface == mDNSNULL))
LogMsg("ConfigResolvers: ERROR: scoped is set but if_index %d is invalid for DNSServer %#a:%d",
r->if_index, &saddr, mDNSVal16(r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort));
else
scopedDNS = (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED)) ? mDNStrue : mDNSfalse;
#endif
s = mDNS_AddDNSServer(m, &d, interface, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scopedDNS,
(n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0));
if (s)
{
if (disabled) s->teststate = DNSServer_Disabled;
LogInfo("ConfigResolvers: DNS server %#a:%d for domain %##s from slot %d, %d",
&s->addr, mDNSVal16(s->port), d.c, i, n);
}
}
}
}
}
mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains)
{
int i;
char buf[MAX_ESCAPED_DOMAIN_NAME]; domainname d;
if (fqdn) fqdn->c[0] = 0;
if (RegDomains ) *RegDomains = NULL;
if (BrowseDomains) *BrowseDomains = NULL;
LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s",
setservers ? " setservers" : "",
setsearch ? " setsearch" : "",
fqdn ? " fqdn" : "",
RegDomains ? " RegDomains" : "",
BrowseDomains ? " BrowseDomains" : "");
if (setsearch)
{
struct ifaddrs *ifa = mDNSNULL;
struct sockaddr_in saddr;
mDNSPlatformMemZero(&saddr, sizeof(saddr));
saddr.sin_len = sizeof(saddr);
saddr.sin_family = AF_INET;
saddr.sin_port = 0;
saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4;
if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa = myGetIfAddrs(1);
while (ifa)
{
mDNSAddr a, n;
if (ifa->ifa_addr->sa_family == AF_INET &&
ifa->ifa_netmask &&
!(ifa->ifa_flags & IFF_LOOPBACK) &&
!SetupAddr(&a, ifa->ifa_addr) &&
!mDNSv4AddressIsLinkLocal(&a.ip.v4) )
{
ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; SetupAddr(&n, ifa->ifa_netmask);
mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3],
a.ip.v4.b[2] & n.ip.v4.b[2],
a.ip.v4.b[1] & n.ip.v4.b[1],
a.ip.v4.b[0] & n.ip.v4.b[0]);
mDNS_AddSearchDomain_CString(buf, mDNSNULL);
}
ifa = ifa->ifa_next;
}
}
#ifndef MDNS_NO_DNSINFO
if (setservers || setsearch)
{
dns_config_t *config = dns_configuration_copy();
if (!config)
{
if ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180))
LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL");
}
else
{
LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d", config->n_resolver);
#if APPLE_OSX_mDNSResponder
if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver && config->resolver[0]->nameserver[0])
MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain);
else ActiveDirectoryPrimaryDomain.c[0] = 0;
ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain);
if (config->n_resolver && config->resolver[0]->n_nameserver && SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain))
SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]);
else
{
AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)"");
ActiveDirectoryPrimaryDomainLabelCount = 0;
ActiveDirectoryPrimaryDomainServer = zeroAddr;
}
#endif
ConfigResolvers(m, config, mDNSfalse, setsearch, setservers);
#if DNSINFO_VERSION >= 20091104
ConfigResolvers(m, config, mDNStrue, setsearch, setservers);
#endif
dns_configuration_free(config);
if (setsearch) RetrySearchDomainQuestions(m);
setservers = mDNSfalse; setsearch = mDNSfalse;
}
}
#endif // MDNS_NO_DNSINFO
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformSetDNSConfig"), NULL, NULL);
if (!store)
LogMsg("mDNSPlatformSetDNSConfig: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
else
{
CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS);
if (ddnsdict)
{
if (fqdn)
{
CFArrayRef fqdnArray = CFDictionaryGetValue(ddnsdict, CFSTR("HostNames"));
if (fqdnArray && CFArrayGetCount(fqdnArray) > 0)
{
CFDictionaryRef fqdnDict = CFArrayGetValueAtIndex(fqdnArray, 0);
if (fqdnDict && DictionaryIsEnabled(fqdnDict))
{
CFStringRef name = CFDictionaryGetValue(fqdnDict, CFSTR("Domain"));
if (name)
{
if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) ||
!MakeDomainNameFromDNSNameString(fqdn, buf) || !fqdn->c[0])
LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS host name: %s", buf[0] ? buf : "(unknown)");
else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS host name: %s", buf);
}
}
}
}
if (RegDomains)
{
CFArrayRef regArray = CFDictionaryGetValue(ddnsdict, CFSTR("RegistrationDomains"));
if (regArray && CFArrayGetCount(regArray) > 0)
{
CFDictionaryRef regDict = CFArrayGetValueAtIndex(regArray, 0);
if (regDict && DictionaryIsEnabled(regDict))
{
CFStringRef name = CFDictionaryGetValue(regDict, CFSTR("Domain"));
if (name)
{
if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) ||
!MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0])
LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS registration domain: %s", buf[0] ? buf : "(unknown)");
else
{
debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration domain: %s", buf);
AppendDNameListElem(&RegDomains, 0, &d);
}
}
}
}
}
if (BrowseDomains)
{
CFArrayRef browseArray = CFDictionaryGetValue(ddnsdict, CFSTR("BrowseDomains"));
if (browseArray)
{
for (i = 0; i < CFArrayGetCount(browseArray); i++)
{
CFDictionaryRef browseDict = CFArrayGetValueAtIndex(browseArray, i);
if (browseDict && DictionaryIsEnabled(browseDict))
{
CFStringRef name = CFDictionaryGetValue(browseDict, CFSTR("Domain"));
if (name)
{
if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) ||
!MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0])
LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS browsing domain: %s", buf[0] ? buf : "(unknown)");
else
{
debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browsing domain: %s", buf);
AppendDNameListElem(&BrowseDomains, 0, &d);
}
}
}
}
}
}
CFRelease(ddnsdict);
}
if (RegDomains)
{
CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac);
if (btmm)
{
CFIndex size = CFDictionaryGetCount(btmm);
const void *key[size];
const void *val[size];
CFDictionaryGetKeysAndValues(btmm, key, val);
for (i = 0; i < size; i++)
{
LogInfo("BackToMyMac %d", i);
if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8))
LogMsg("Can't read BackToMyMac %d key %s", i, buf);
else
{
mDNSu32 uid = atoi(buf);
if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8))
LogMsg("Can't read BackToMyMac %d val %s", i, buf);
else if (MakeDomainNameFromDNSNameString(&d, buf) && d.c[0])
{
LogInfo("BackToMyMac %d %d %##s", i, uid, d.c);
AppendDNameListElem(&RegDomains, uid, &d);
}
}
}
CFRelease(btmm);
}
}
if (setservers || setsearch)
{
CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DNS);
if (dict)
{
if (setservers)
{
CFArrayRef values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses);
if (values)
{
LogInfo("DNS Server Address values: %d", (int)CFArrayGetCount(values));
for (i = 0; i < CFArrayGetCount(values); i++)
{
CFStringRef s = CFArrayGetValueAtIndex(values, i);
mDNSAddr addr = { mDNSAddrType_IPv4, { { { 0 } } } };
if (s && CFStringGetCString(s, buf, 256, kCFStringEncodingUTF8) &&
inet_aton(buf, (struct in_addr *) &addr.ip.v4))
{
LogInfo("Adding DNS server from dict: %s", buf);
mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0);
}
}
}
else LogInfo("No DNS Server Address values");
}
if (setsearch)
{
CFArrayRef searchDomains = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains);
if (searchDomains)
{
for (i = 0; i < CFArrayGetCount(searchDomains); i++)
{
CFStringRef s = CFArrayGetValueAtIndex(searchDomains, i);
if (s && CFStringGetCString(s, buf, sizeof(buf), kCFStringEncodingUTF8))
mDNS_AddSearchDomain_CString(buf, mDNSNULL);
}
}
else {
CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetDNSDomainName);
if (string && CFStringGetCString(string, buf, sizeof(buf), kCFStringEncodingUTF8))
mDNS_AddSearchDomain_CString(buf, mDNSNULL);
}
RetrySearchDomainQuestions(m);
}
CFRelease(dict);
}
}
CFRelease(store);
}
}
mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *r)
{
char buf[256];
(void)m;
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformGetPrimaryInterface"), NULL, NULL);
if (!store)
LogMsg("mDNSPlatformGetPrimaryInterface: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
else
{
CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_IPv4);
if (dict)
{
r->type = mDNSAddrType_IPv4;
r->ip.v4 = zerov4Addr;
CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetIPv4Router);
if (string)
{
if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8))
LogMsg("Could not convert router to CString");
else
{
struct sockaddr_in saddr;
saddr.sin_len = sizeof(saddr);
saddr.sin_family = AF_INET;
saddr.sin_port = 0;
inet_aton(buf, &saddr.sin_addr);
*(in_addr_t *)&r->ip.v4 = saddr.sin_addr.s_addr;
}
}
string = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface);
if (string)
{
mDNSBool HavePrimaryGlobalv6 = mDNSfalse; struct ifaddrs *ifa = myGetIfAddrs(1);
*v4 = *v6 = zeroAddr;
if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) { LogMsg("Could not convert router to CString"); goto exit; }
while (ifa && (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4) || !HavePrimaryGlobalv6))
{
mDNSAddr tmp6 = zeroAddr;
if (!strcmp(buf, ifa->ifa_name))
{
if (ifa->ifa_addr->sa_family == AF_INET)
{
if (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4)) SetupAddr(v4, ifa->ifa_addr);
}
else if (ifa->ifa_addr->sa_family == AF_INET6)
{
SetupAddr(&tmp6, ifa->ifa_addr);
if (tmp6.ip.v6.b[0] >> 5 == 1) { HavePrimaryGlobalv6 = mDNStrue; *v6 = tmp6; }
}
}
else
{
if (!HavePrimaryGlobalv6 && ifa->ifa_addr->sa_family == AF_INET6 && !v6->ip.v6.b[0])
{
SetupAddr(&tmp6, ifa->ifa_addr);
if (tmp6.ip.v6.b[0] >> 5 == 1) *v6 = tmp6;
}
}
ifa = ifa->ifa_next;
}
}
exit:
CFRelease(dict);
}
CFRelease(store);
}
return mStatus_NoError;
}
mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status)
{
LogInfo("mDNSPlatformDynDNSHostNameStatusChanged %d %##s", status, dname->c);
char uname[MAX_ESCAPED_DOMAIN_NAME]; ConvertDomainNameToCString(dname, uname);
char *p = uname;
while (*p)
{
*p = tolower(*p);
if (!(*(p+1)) && *p == '.') *p = 0; p++;
}
const CFStringRef StateKeys [1] = { CFSTR("HostNames") };
const CFStringRef HostKeys [1] = { CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8) };
const CFStringRef StatusKeys[1] = { CFSTR("Status") };
if (!HostKeys[0]) LogMsg("SetDDNSNameStatus: CFStringCreateWithCString(%s) failed", uname);
else
{
const CFNumberRef StatusVals[1] = { CFNumberCreate(NULL, kCFNumberSInt32Type, &status) };
if (!StatusVals[0]) LogMsg("SetDDNSNameStatus: CFNumberCreate(%d) failed", status);
else
{
const CFDictionaryRef HostVals[1] = { CFDictionaryCreate(NULL, (void*)StatusKeys, (void*)StatusVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) };
if (HostVals[0])
{
const CFDictionaryRef StateVals[1] = { CFDictionaryCreate(NULL, (void*)HostKeys, (void*)HostVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) };
if (StateVals[0])
{
CFDictionaryRef StateDict = CFDictionaryCreate(NULL, (void*)StateKeys, (void*)StateVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (StateDict)
{
mDNSDynamicStoreSetConfig(kmDNSDynamicConfig, mDNSNULL, StateDict);
CFRelease(StateDict);
}
CFRelease(StateVals[0]);
}
CFRelease(HostVals[0]);
}
CFRelease(StatusVals[0]);
}
CFRelease(HostKeys[0]);
}
}
#if APPLE_OSX_mDNSResponder
#if ! NO_AWACS
mDNSlocal mDNSBool IsBTMMDomain(domainname *d)
{
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:IsBTMMDomain"), NULL, NULL);
if (!store)
{
LogMsg("IsBTMMDomain: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
return mDNSfalse;
}
CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac);
if (btmm)
{
CFIndex size = CFDictionaryGetCount(btmm);
char buf[MAX_ESCAPED_DOMAIN_NAME]; const void *key[size];
const void *val[size];
domainname dom;
int i;
CFDictionaryGetKeysAndValues(btmm, key, val);
for (i = 0; i < size; i++)
{
LogInfo("BackToMyMac %d", i);
if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8))
LogMsg("IsBTMMDomain: ERROR!! Can't read BackToMyMac %d key %s", i, buf);
else
{
mDNSu32 uid = atoi(buf);
if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8))
LogMsg("IsBTMMDomain: Can't read BackToMyMac %d val %s", i, buf);
else if (MakeDomainNameFromDNSNameString(&dom, buf) && dom.c[0])
{
if (SameDomainName(&dom, d))
{
LogInfo("IsBTMMDomain: Domain %##s is a btmm domain, uid %u", d->c, uid);
CFRelease(btmm);
CFRelease(store);
return mDNStrue;
}
}
}
}
CFRelease(btmm);
}
CFRelease(store);
LogInfo("IsBTMMDomain: Domain %##s not a btmm domain", d->c);
return mDNSfalse;
}
mDNSlocal int AddOneItem(char *buf, int bufsz, char *data, int *currlen)
{
int len;
len = strlcpy(buf + *currlen, data, bufsz - *currlen);
if (len >= (bufsz - *currlen))
{
LogMsg("AddOneItem: Exceeded the max buffer size currlen %d, len %d", *currlen, len);
*currlen = bufsz - 1;
return -1;
}
else { (*currlen) += len; }
buf[*currlen] = ',';
if (*currlen >= bufsz)
{
LogMsg("AddOneItem: ERROR!! How can currlen be %d", *currlen);
*currlen = bufsz - 1;
buf[*currlen] = 0;
return -1;
}
if (*currlen == bufsz - 1) { buf[*currlen] = 0; return -1; }
(*currlen)++;
return *currlen;
}
mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m)
{
DomainAuthInfo *BTMMDomain = mDNSNULL;
DomainAuthInfo *FoundInList;
static mDNSBool AWACSDConnected = mDNSfalse;
char AllUsers[1024]; char AllPass[1024]; char username[MAX_DOMAIN_LABEL + 1];
int currulen = 0;
int currplen = 0;
for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
if (!FoundInList->deltime && FoundInList->AutoTunnel && IsBTMMDomain(&FoundInList->domain))
{
BTMMDomain = FoundInList;
ConvertDomainLabelToCString_unescaped((domainlabel *)BTMMDomain->domain.c, username);
LogInfo("UpdateBTMMRelayConnection: user %s for domain %##s", username, BTMMDomain->domain.c);
if (AddOneItem(AllUsers, sizeof(AllUsers), username, &currulen) == -1) break;
if (AddOneItem(AllPass, sizeof(AllPass), BTMMDomain->b64keydata, &currplen) == -1) break;
}
if (BTMMDomain)
{
if (currulen != (int)(sizeof(AllUsers) - 1)) AllUsers[currulen - 1] = 0;
if (currplen != (int)(sizeof(AllPass) - 1)) AllPass[currplen - 1] = 0;
LogInfo("UpdateBTMMRelayConnection: AWS_Connect for user %s", AllUsers);
AWACS_Connect(AllUsers, AllPass, "hello.connectivity.me.com");
AWACSDConnected = mDNStrue;
}
else
{
if (AWACSDConnected)
{
LogInfo("UpdateBTMMRelayConnection: AWS_Disconnect");
AWACS_Disconnect();
AWACSDConnected = mDNSfalse;
}
else LogInfo("UpdateBTMMRelayConnection: Not calling AWS_Disconnect");
}
}
#else
mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m)
{
(void) m; LogInfo("UpdateBTMMRelayConnection: AWACS connection not started, no AWACS library");
}
#endif // ! NO_AWACS
#endif // APPLE_OSX_mDNSResponder
mDNSexport void SetDomainSecrets(mDNS *m)
{
#ifdef NO_SECURITYFRAMEWORK
(void)m;
LogMsg("Note: SetDomainSecrets: no keychain support");
#else
mDNSBool haveAutoTunnels = mDNSfalse;
LogInfo("SetDomainSecrets");
DomainAuthInfo *ptr;
for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10);
#if APPLE_OSX_mDNSResponder
{
ClientTunnel *client;
for (client = m->TunnelClients; client; client = client->next)
{
LogInfo("SetDomainSecrets: tunnel to %##s marked for deletion", client->dstname.c);
client->MarkedForDeletion = mDNStrue;
}
}
#endif // APPLE_OSX_mDNSResponder
CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; }
CFIndex i;
CFDataRef data = NULL;
const int itemsPerEntry = 4; CFArrayRef secrets = NULL;
int err = mDNSKeychainGetSecrets(&secrets);
if (err || !secrets)
LogMsg("SetDomainSecrets: mDNSKeychainGetSecrets failed error %d CFArrayRef %p", err, secrets);
else
{
CFIndex ArrayCount = CFArrayGetCount(secrets);
for (i = 0; i < ArrayCount; ++i)
{
mDNSBool AutoTunnel;
int j, offset;
CFArrayRef entry = CFArrayGetValueAtIndex(secrets, i);
if (CFArrayGetTypeID() != CFGetTypeID(entry) || itemsPerEntry != CFArrayGetCount(entry))
{ LogMsg("SetDomainSecrets: malformed entry %d, itemsPerEntry %d", i, itemsPerEntry); continue; }
for (j = 0; j < CFArrayGetCount(entry); ++j)
if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j)))
{ LogMsg("SetDomainSecrets: malformed entry item %d", j); continue; }
char stringbuf[MAX_ESCAPED_DOMAIN_NAME + sizeof(btmmprefix)];
data = CFArrayGetValueAtIndex(entry, kmDNSKcWhere);
if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
{ LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; }
CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
stringbuf[CFDataGetLength(data)] = '\0';
AutoTunnel = mDNSfalse;
offset = 0;
if (!strncmp(stringbuf, dnsprefix, strlen(dnsprefix)))
offset = strlen(dnsprefix);
else if (!strncmp(stringbuf, btmmprefix, strlen(btmmprefix)))
{
AutoTunnel = mDNStrue;
offset = strlen(btmmprefix);
}
domainname domain;
if (!MakeDomainNameFromDNSNameString(&domain, stringbuf + offset)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; }
data = CFArrayGetValueAtIndex(entry, kmDNSKcAccount);
if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
{ LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; }
CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf);
stringbuf[CFDataGetLength(data)] = '\0';
domainname keyname;
if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; }
data = CFArrayGetValueAtIndex(entry, kmDNSKcKey);
if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
{ LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; }
CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
stringbuf[CFDataGetLength(data)] = '\0';
char hostbuf[MAX_ESCAPED_DOMAIN_NAME + 6]; data = CFArrayGetValueAtIndex(entry, kmDNSKcName);
if (CFDataGetLength(data) >= (int)sizeof(hostbuf))
{ LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; }
CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)hostbuf);
hostbuf[CFDataGetLength(data)] = '\0';
domainname hostname;
mDNSIPPort port;
char *hptr;
hptr = strchr(hostbuf, ':');
port.NotAnInteger = 0;
if (hptr)
{
mDNSu8 *p;
mDNSu16 val = 0;
*hptr++ = '\0';
while(hptr && *hptr != 0)
{
if (*hptr < '0' || *hptr > '9')
{ LogMsg("SetDomainSecrets: Malformed Port number %d, val %d", *hptr, val); val = 0; break;}
val = val * 10 + *hptr - '0';
hptr++;
}
if (!val) continue;
p = (mDNSu8 *)&val;
port.NotAnInteger = p[0] << 8 | p[1];
}
hptr = strchr(hostbuf, '@');
if (hptr)
hptr++;
else
hptr = hostbuf;
if (!MakeDomainNameFromDNSNameString(&hostname, hptr)) { LogMsg("SetDomainSecrets: bad host name %s", hptr); continue; }
DomainAuthInfo *FoundInList;
for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
if (SameDomainName(&FoundInList->domain, &domain)) break;
#if APPLE_OSX_mDNSResponder
if (FoundInList)
{
ClientTunnel *client;
for (client = m->TunnelClients; client; client = client->next)
if (FoundInList == GetAuthInfoForName_internal(m, &client->dstname))
{
LogInfo("SetDomainSecrets: tunnel to %##s no longer marked for deletion", client->dstname.c);
client->MarkedForDeletion = mDNSfalse;
}
}
#endif // APPLE_OSX_mDNSResponder
LogInfo("SetDomainSecrets: domain %##s keyname %##s hostname %##s port %d", &domain.c, &keyname.c, hostname.c, (port.b[0] << 8 | port.b[1]));
ptr = FoundInList;
if (FoundInList && FoundInList->AutoTunnel && haveAutoTunnels == mDNSfalse) haveAutoTunnels = mDNStrue;
if (!FoundInList)
{
ptr = (DomainAuthInfo*)mallocL("DomainAuthInfo", sizeof(*ptr));
if (!ptr) { LogMsg("SetDomainSecrets: No memory"); continue; }
}
if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, &hostname, &port, (AutoTunnel ? btmmprefix : (IsTunnelModeDomain(&domain) ? dnsprefix : NULL))) == mStatus_BadParamErr)
{
if (!FoundInList) mDNSPlatformMemFree(ptr); continue;
}
#if APPLE_OSX_mDNSResponder
if (ptr->AutoTunnel) UpdateAutoTunnelDomainStatus(m, ptr);
#endif // APPLE_OSX_mDNSResponder
ConvertDomainNameToCString(&domain, stringbuf);
CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8);
if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); }
}
CFRelease(secrets);
}
mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa);
CFRelease(sa);
#if APPLE_OSX_mDNSResponder
{
ClientTunnel **pp = &m->TunnelClients;
while (*pp)
{
if ((*pp)->MarkedForDeletion)
{
ClientTunnel *cur = *pp;
LogInfo("SetDomainSecrets: removing client %p %##s from list", cur, cur->dstname.c);
if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q);
AutoTunnelSetKeys(cur, mDNSfalse);
*pp = cur->next;
freeL("ClientTunnel", cur);
}
else
pp = &(*pp)->next;
}
DomainAuthInfo *info = m->AuthInfoList;
while (info)
{
if (info->AutoTunnel && info->deltime)
{
if (info->AutoTunnelNAT.clientContext)
{
mDNS_StopNATOperation_internal(m, &info->AutoTunnelNAT);
if (info->AutoTunnelNAT.clientCallback)
{
info->AutoTunnelNAT.ExternalAddress = m->ExternalAddress;
info->AutoTunnelNAT.ExternalPort = zeroIPPort;
info->AutoTunnelNAT.RequestedPort = zeroIPPort;
info->AutoTunnelNAT.Lifetime = 0;
info->AutoTunnelNAT.Result = mStatus_NoError;
mDNS_DropLockBeforeCallback(); AutoTunnelDeleteAuthInfoState(m, info);
mDNS_ReclaimLockAfterCallback(); }
info->AutoTunnelNAT.clientContext = mDNSNULL;
}
RemoveAutoTunnelDomainStatus(m, info);
}
info = info->next;
}
if (!haveAutoTunnels && !m->TunnelClients && m->AutoTunnelHostAddrActive)
{
LogInfo("SetDomainSecrets: Bringing tunnel interface DOWN");
m->AutoTunnelHostAddrActive = mDNSfalse;
(void)mDNSAutoTunnelInterfaceUpDown(kmDNSDown, m->AutoTunnelHostAddr.b);
mDNSPlatformMemZero(m->AutoTunnelHostAddr.b, sizeof(m->AutoTunnelHostAddr.b));
}
if (m->AutoTunnelHostAddr.b[0])
if (TunnelClients(m) || TunnelServers(m))
SetupLocalAutoTunnelInterface_internal(m, mDNSfalse);
UpdateAnonymousRacoonConfig(m); UpdateBTMMRelayConnection(m);
}
#endif // APPLE_OSX_mDNSResponder
CheckSuppressUnusableQuestions(m);
#endif
}
mDNSlocal void SetLocalDomains(void)
{
CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (!sa) { LogMsg("SetLocalDomains: CFArrayCreateMutable failed"); return; }
CFArrayAppendValue(sa, CFSTR("local"));
CFArrayAppendValue(sa, CFSTR("254.169.in-addr.arpa"));
CFArrayAppendValue(sa, CFSTR("8.e.f.ip6.arpa"));
CFArrayAppendValue(sa, CFSTR("9.e.f.ip6.arpa"));
CFArrayAppendValue(sa, CFSTR("a.e.f.ip6.arpa"));
CFArrayAppendValue(sa, CFSTR("b.e.f.ip6.arpa"));
mDNSDynamicStoreSetConfig(kmDNSMulticastConfig, mDNSNULL, sa);
CFRelease(sa);
}
mDNSlocal void GetCurrentPMSetting(const CFStringRef name, mDNSs32 *val)
{
#if USE_IOPMCOPYACTIVEPMPREFERENCES
CFTypeRef blob = NULL;
CFStringRef str = NULL;
CFDictionaryRef odict = NULL;
CFDictionaryRef idict = NULL;
CFNumberRef number = NULL;
blob = IOPSCopyPowerSourcesInfo();
if (!blob) { LogMsg("GetCurrentPMSetting: IOPSCopyPowerSourcesInfo failed!"); goto end; }
odict = IOPMCopyActivePMPreferences();
if (!odict) { LogMsg("GetCurrentPMSetting: IOPMCopyActivePMPreferences failed!"); goto end; }
str = IOPSGetProvidingPowerSourceType(blob);
if (!str) { LogMsg("GetCurrentPMSetting: IOPSGetProvidingPowerSourceType failed!"); goto end; }
idict = CFDictionaryGetValue(odict, str);
if (!idict)
{
char buf[256];
if (!CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
LogMsg("GetCurrentPMSetting: CFDictionaryGetValue (%s) failed!", buf);
goto end;
}
number = CFDictionaryGetValue(idict, name);
if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val))
*val = 0;
end:
if (blob) CFRelease(blob);
if (odict) CFRelease(odict);
#else
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetCurrentPMSetting"), NULL, NULL);
if (!store) LogMsg("GetCurrentPMSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
else
{
CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings);
if (!dict) LogSPS("GetCurrentPMSetting: Could not get IOPM CurrentSettings dict");
else
{
CFNumberRef number = CFDictionaryGetValue(dict, name);
if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val))
*val = 0;
CFRelease(dict);
}
CFRelease(store);
}
#endif
}
#if APPLE_OSX_mDNSResponder
static CFMutableDictionaryRef spsStatusDict = NULL;
static const CFStringRef kMetricRef = CFSTR("Metric");
mDNSlocal void SPSStatusPutNumber(CFMutableDictionaryRef dict, const mDNSu8* const ptr, CFStringRef key)
{
mDNSu8 tmp = (ptr[0] - '0') * 10 + ptr[1] - '0';
CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt8Type, &tmp);
if (!num)
LogMsg("SPSStatusPutNumber: Could not create CFNumber");
else
{
CFDictionarySetValue(dict, key, num);
CFRelease(num);
}
}
mDNSlocal CFMutableDictionaryRef SPSCreateDict(const mDNSu8* const ptr)
{
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!dict) { LogMsg("SPSCreateDict: Could not create CFDictionary dict"); return dict; }
char buffer[1024];
buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", ptr) - 1] = 0;
CFStringRef spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname full"); CFRelease(dict); return NULL; }
CFDictionarySetValue(dict, CFSTR("FullName"), spsname);
CFRelease(spsname);
if (ptr[0] >= 2) SPSStatusPutNumber(dict, ptr + 1, CFSTR("Type"));
if (ptr[0] >= 5) SPSStatusPutNumber(dict, ptr + 4, CFSTR("Portability"));
if (ptr[0] >= 8) SPSStatusPutNumber(dict, ptr + 7, CFSTR("MarginalPower"));
if (ptr[0] >= 11) SPSStatusPutNumber(dict, ptr +10, CFSTR("TotalPower"));
mDNSu32 tmp = SPSMetric(ptr);
CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tmp);
if (!num)
LogMsg("SPSCreateDict: Could not create CFNumber");
else
{
CFDictionarySetValue(dict, kMetricRef, num);
CFRelease(num);
}
if (ptr[0] >= 12)
{
memcpy(buffer, ptr + 13, ptr[0] - 12);
buffer[ptr[0] - 12] = 0;
spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname"); CFRelease(dict); return NULL; }
else
{
CFDictionarySetValue(dict, CFSTR("PrettyName"), spsname);
CFRelease(spsname);
}
}
return dict;
}
mDNSlocal CFComparisonResult CompareSPSEntries(const void *val1, const void *val2, void *context)
{
(void)context;
return CFNumberCompare((CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val1, kMetricRef),
(CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val2, kMetricRef),
NULL);
}
mDNSlocal void UpdateSPSStatus(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
NetworkInterfaceInfo* info = (NetworkInterfaceInfo*)question->QuestionContext;
debugf("UpdateSPSStatus: %s %##s %s %s", info->ifname, question->qname.c, AddRecord ? "Add" : "Rmv", answer ? RRDisplayString(m, answer) : "<null>");
mDNS_Lock(m);
mDNS_UpdateAllowSleep(m);
mDNS_Unlock(m);
if (answer && SPSMetric(answer->rdata->u.name.c) > 999999) return;
if (!spsStatusDict)
{
spsStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!spsStatusDict) { LogMsg("UpdateSPSStatus: Could not create CFDictionary spsStatusDict"); return; }
}
CFStringRef ifname = CFStringCreateWithCString(NULL, info->ifname, kCFStringEncodingUTF8);
if (!ifname) { LogMsg("UpdateSPSStatus: Could not create CFString ifname"); return; }
CFMutableArrayRef array = NULL;
if (!CFDictionaryGetValueIfPresent(spsStatusDict, ifname, (const void**) &array))
{
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (!array) { LogMsg("UpdateSPSStatus: Could not create CFMutableArray"); CFRelease(ifname); return; }
CFDictionarySetValue(spsStatusDict, ifname, array);
CFRelease(array); }
else
if (!array) { LogMsg("UpdateSPSStatus: Could not get CFMutableArray for %s", info->ifname); CFRelease(ifname); return; }
if (!answer) CFArrayRemoveAllValues(array);
else
{
CFMutableDictionaryRef dict = SPSCreateDict(answer->rdata->u.name.c);
if (!dict) { CFRelease(ifname); return; }
if (AddRecord)
{
if (!CFArrayContainsValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict))
{
int i=0;
for (i=0; i<CFArrayGetCount(array); i++)
if (CompareSPSEntries(CFArrayGetValueAtIndex(array, i), dict, NULL) != kCFCompareLessThan)
break;
CFArrayInsertValueAtIndex(array, i, dict);
}
else LogMsg("UpdateSPSStatus: %s array already contains %##s", info->ifname, answer->rdata->u.name.c);
}
else
{
CFIndex i = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict);
if (i != -1) CFArrayRemoveValueAtIndex(array, i);
else LogMsg("UpdateSPSStatus: %s array does not contain %##s", info->ifname, answer->rdata->u.name.c);
}
CFRelease(dict);
}
if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, info->ifname, array);
CFRelease(ifname);
}
mDNSlocal mDNSs32 GetSystemSleepTimerSetting(void)
{
mDNSs32 val = -1;
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetSystemSleepTimerSetting"), NULL, NULL);
if (!store)
LogMsg("GetSystemSleepTimerSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
else
{
CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings);
if (dict)
{
CFNumberRef number = CFDictionaryGetValue(dict, CFSTR("System Sleep Timer"));
if (number) CFNumberGetValue(number, kCFNumberSInt32Type, &val);
CFRelease(dict);
}
CFRelease(store);
}
return val;
}
mDNSlocal void SetSPS(mDNS *const m)
{
SCPreferencesSynchronize(m->p->SCPrefs);
CFDictionaryRef dict = SCPreferencesGetValue(m->p->SCPrefs, CFSTR("NAT"));
mDNSBool natenabled = (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID()) && DictionaryIsEnabled(dict));
mDNSu8 sps = natenabled ? mDNSSleepProxyMetric_PrimarySoftware :
(OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0;
if (SPMetricMarginalPower <= 60 && !sps) sps = mDNSSleepProxyMetric_IncidentalHardware;
if (sps && OfferSleepProxyService && OfferSleepProxyService < 100) sps = OfferSleepProxyService;
mDNSCoreBeSleepProxyServer(m, sps, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower);
}
mDNSlocal void InternetSharingChanged(SCPreferencesRef prefs, SCPreferencesNotification notificationType, void *context)
{
(void)prefs; (void)notificationType; mDNS *const m = (mDNS *const)context;
KQueueLock(m);
mDNS_Lock(m);
if (!m->p->NetworkChanged ||
m->p->NetworkChanged - NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2) < 0)
{
m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2);
LogInfo("InternetSharingChanged: Set NetworkChanged to %d (%d)", m->p->NetworkChanged - m->timenow, m->p->NetworkChanged);
}
mDNS_Unlock(m);
KQueueUnlock(m, "InternetSharingChanged");
}
mDNSlocal mStatus WatchForInternetSharingChanges(mDNS *const m)
{
SCPreferencesRef SCPrefs = SCPreferencesCreate(NULL, CFSTR("mDNSResponder:WatchForInternetSharingChanges"), CFSTR("com.apple.nat.plist"));
if (!SCPrefs) { LogMsg("SCPreferencesCreate failed: %s", SCErrorString(SCError())); return(mStatus_NoMemoryErr); }
SCPreferencesContext context = { 0, m, NULL, NULL, NULL };
if (!SCPreferencesSetCallback(SCPrefs, InternetSharingChanged, &context))
{ LogMsg("SCPreferencesSetCallback failed: %s", SCErrorString(SCError())); CFRelease(SCPrefs); return(mStatus_NoMemoryErr); }
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
if (!SCPreferencesSetDispatchQueue( SCPrefs, dispatch_get_main_queue()))
{ LogMsg("SCPreferencesSetDispatchQueue failed: %s", SCErrorString(SCError())); return(mStatus_NoMemoryErr); }
#else
if (!SCPreferencesScheduleWithRunLoop(SCPrefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
{ LogMsg("SCPreferencesScheduleWithRunLoop failed: %s", SCErrorString(SCError())); CFRelease(SCPrefs); return(mStatus_NoMemoryErr); }
#endif
m->p->SCPrefs = SCPrefs;
return(mStatus_NoError);
}
#define mDNS_IOREG_KEY "mDNS_KEY"
#define mDNS_IOREG_VALUE "2009-07-30"
#define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS'
enum
{ cmd_mDNSOffloadRR = 21, };
typedef union { void *ptr; mDNSOpaque64 sixtyfourbits; } FatPtr;
typedef struct
{ uint32_t command; uint32_t rrBufferSize; uint32_t numUDPPorts; uint32_t numTCPPorts; uint32_t numRRRecords; uint32_t compression; FatPtr rrRecords; FatPtr udpPorts; FatPtr tcpPorts; } mDNSOffloadCmd;
#include <IOKit/IOKitLib.h>
#include <dns_util.h>
mDNSlocal mDNSu16 GetPortArray(mDNS *const m, int trans, mDNSIPPort *portarray)
{
const domainlabel *const tp = (trans == mDNSTransport_UDP) ? (const domainlabel *)"\x4_udp" : (const domainlabel *)"\x4_tcp";
int count = 0;
AuthRecord *rr;
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.rrtype == kDNSType_SRV && SameDomainLabel(ThirdLabel(rr->resrec.name)->c, tp->c))
{
if (portarray) portarray[count] = rr->resrec.rdata->u.srv.port;
count++;
}
if (trans == mDNSTransport_UDP && TunnelServers(m))
{
LogSPS("GetPortArray Back to My Mac at %d", count);
if (portarray) portarray[count] = IPSECPort;
count++;
}
return(count);
}
#define TfrRecordToNIC(RR) \
((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name))))
mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes)
{
*numbytes = 0;
int count = 0;
AuthRecord *rr;
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.RecordType > kDNSRecordTypeDeregistering)
if (TfrRecordToNIC(rr))
{
*numbytes += DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate;
LogSPS("CountProxyRecords: %3d size %5d total %5d %s",
count, DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate, *numbytes, ARDisplayString(m,rr));
count++;
}
return(count);
}
mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *const numbytes, FatPtr *const records)
{
mDNSu8 *p = msg->data;
const mDNSu8 *const limit = p + *numbytes;
InitializeDNSMessage(&msg->h, zeroID, zeroID);
int count = 0;
AuthRecord *rr;
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.RecordType > kDNSRecordTypeDeregistering)
if (TfrRecordToNIC(rr))
{
records[count].sixtyfourbits = zeroOpaque64;
records[count].ptr = p;
if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
rr->resrec.rrclass |= kDNSClass_UniqueRRSet; p = PutResourceRecordTTLWithLimit(msg, p, &msg->h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit);
rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; LogSPS("GetProxyRecords: %3d start %p end %p size %5d total %5d %s",
count, records[count].ptr, p, p - (mDNSu8 *)records[count].ptr, p - msg->data, ARDisplayString(m,rr));
count++;
}
*numbytes = p - msg->data;
}
#ifndef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
static kern_return_t
IOConnectCallStructMethod(
mach_port_t connection, uint32_t selector, const void *inputStruct, size_t inputStructCnt, void *outputStruct, size_t *outputStructCnt) {
(void)connection;
(void)selector;
(void)inputStruct;
(void)inputStructCnt;
(void)outputStruct;
(void)outputStructCnt;
LogMsg("Compiled without IOConnectCallStructMethod");
return(KERN_FAILURE);
}
#endif
mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname) {
mStatus result = mStatus_UnknownErr;
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, ifname));
if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", ifname); return(mStatus_UnknownErr); }
io_name_t n1, n2;
IOObjectGetClass(service, n1);
io_object_t parent;
kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", ifname, n1, kr);
else
{
IOObjectGetClass(parent, n2);
LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", ifname, n1, n2);
const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, CFSTR(mDNS_IOREG_KEY), kCFAllocatorDefault, mDNSNULL);
if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", ifname, n1, n2);
else
{
if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE)))
LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s",
ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE);
else if (!UseInternalSleepProxy)
LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", ifname);
else
{
io_connect_t conObj;
kr = IOServiceOpen(parent, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE, &conObj);
if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", ifname, n1, n2, kr);
else
{
mDNSOffloadCmd cmd;
mDNSPlatformMemZero(&cmd, sizeof(cmd)); cmd.command = cmd_mDNSOffloadRR;
cmd.numUDPPorts = GetPortArray(m, mDNSTransport_UDP, mDNSNULL);
cmd.numTCPPorts = GetPortArray(m, mDNSTransport_TCP, mDNSNULL);
cmd.numRRRecords = CountProxyRecords(m, &cmd.rrBufferSize);
cmd.compression = sizeof(DNSMessageHeader);
DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize);
cmd.rrRecords.ptr = mallocL("mDNSOffloadCmd rrRecords", cmd.numRRRecords * sizeof(FatPtr));
cmd.udpPorts .ptr = mallocL("mDNSOffloadCmd udpPorts", cmd.numUDPPorts * sizeof(mDNSIPPort));
cmd.tcpPorts .ptr = mallocL("mDNSOffloadCmd tcpPorts", cmd.numTCPPorts * sizeof(mDNSIPPort));
LogSPS("ActivateLocalProxy: msg %p %d RR %p %d, UDP %p %d, TCP %p %d",
msg, cmd.rrBufferSize,
cmd.rrRecords.ptr, cmd.numRRRecords,
cmd.udpPorts .ptr, cmd.numUDPPorts,
cmd.tcpPorts .ptr, cmd.numTCPPorts);
if (!msg || !cmd.rrRecords.ptr || !cmd.udpPorts.ptr || !cmd.tcpPorts.ptr)
LogMsg("ActivateLocalProxy: Failed to allocate memory: msg %p %d RR %p %d, UDP %p %d, TCP %p %d",
msg, cmd.rrBufferSize,
cmd.rrRecords.ptr, cmd.numRRRecords,
cmd.udpPorts .ptr, cmd.numUDPPorts,
cmd.tcpPorts .ptr, cmd.numTCPPorts);
else
{
GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr);
GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr);
GetPortArray(m, mDNSTransport_TCP, cmd.tcpPorts.ptr);
char outputData[2];
size_t outputDataSize = sizeof(outputData);
kr = IOConnectCallStructMethod(conObj, 0, &cmd, sizeof(cmd), outputData, &outputDataSize);
LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", ifname, n1, n2, kr);
if (kr == KERN_SUCCESS) result = mStatus_NoError;
}
if (cmd.tcpPorts. ptr) freeL("mDNSOffloadCmd udpPorts", cmd.tcpPorts .ptr);
if (cmd.udpPorts. ptr) freeL("mDNSOffloadCmd tcpPorts", cmd.udpPorts .ptr);
if (cmd.rrRecords.ptr) freeL("mDNSOffloadCmd rrRecords", cmd.rrRecords.ptr);
if (msg) freeL("mDNSOffloadCmd msg", msg);
IOServiceClose(conObj);
}
}
CFRelease(ref);
}
IOObjectRelease(parent);
}
IOObjectRelease(service);
return result;
}
#endif // APPLE_OSX_mDNSResponder
static io_service_t g_rootdomain = MACH_PORT_NULL;
mDNSlocal mDNSBool SystemWakeForNetworkAccess(void)
{
mDNSs32 val = 0;
CFBooleanRef clamshellStop = NULL;
mDNSBool retnow = mDNSfalse;
if (DisableSleepProxyClient) { LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); return mDNSfalse; }
GetCurrentPMSetting(CFSTR("Wake On LAN"), &val);
LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", val);
if (!val) return mDNSfalse;
if (!g_rootdomain) g_rootdomain = IORegistryEntryFromPath(MACH_PORT_NULL, kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain");
if (!g_rootdomain) { LogMsg("SystemWakeForNetworkAccess: IORegistryEntryFromPath failed; assuming no clamshell so can WOMP"); return mDNStrue; }
clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellStateKey), kCFAllocatorDefault, 0);
if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; }
retnow = clamshellStop == kCFBooleanFalse;
CFRelease(clamshellStop);
if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey is false; clamshell is open so can WOMP"); return mDNStrue; }
clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellCausesSleepKey), kCFAllocatorDefault, 0);
if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; }
retnow = (clamshellStop == kCFBooleanFalse);
CFRelease(clamshellStop);
if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey is false; clamshell is closed but can WOMP"); return mDNStrue; }
LogSPS("SystemWakeForNetworkAccess: clamshell is closed and can't WOMP");
return mDNSfalse;
}
mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void)
{
mDNSs32 val = 0;
GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val);
return val != 0 ? mDNStrue : mDNSfalse;
}
#if APPLE_OSX_mDNSResponder
mDNSexport mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr)
{
if (!AuthRecord_uDNS(rr)) return mDNStrue;
if (SameDomainLabel(rr->namestorage.c, (const mDNSu8 *)"\x0c_autotunnel6"))
{
LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr));
return mDNSfalse;
}
if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result)
{
if ((rr->resrec.rrtype == kDNSType_SRV) && rr->state != regState_NoTarget && rr->zone)
{
DomainAuthInfo *info = GetAuthInfoForName_internal(m, rr->zone);
if (info && info->AutoTunnel)
{
LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr));
return mDNSfalse;
}
}
}
return mDNStrue;
}
mDNSlocal CFDictionaryRef ParseBackToMyMacKey(CFDictionaryRef connd)
{
CFDictionaryRef BTMMDict = CFDictionaryGetValue(connd, CFSTR("BackToMyMac"));
if (!BTMMDict)
{
LogInfo("ParseBackToMyMacKey: CFDictionaryGetValue No value for BackToMyMac");
return NULL;
}
if (CFGetTypeID(BTMMDict) != CFDictionaryGetTypeID())
{LogMsg("ERROR: ParseBackToMyMacKey: CFDictionaryGetValue BackToMyMac not a dictionary"); CFRelease(BTMMDict); return NULL;}
return BTMMDict;
}
mDNSlocal void ParseBTMMInterfaceKey(CFDictionaryRef BTMMDict, char *buf, int buflen)
{
CFTypeRef string;
mDNSBool ifExists;
ifExists = CFDictionaryGetValueIfPresent(BTMMDict, CFSTR("Interface"), &string);
if (ifExists)
{
if (!CFStringGetCString(string, buf, buflen, kCFStringEncodingUTF8))
{
LogMsg("ERROR: ParseBTMMInterfaceKey: Could not convert Interface to CString");
if (buflen) buf[0] = 0;
return;
}
else
debugf("ParseBTMMInterfaceKey: Interface Key exists %s", buf);
}
else
{
if (buflen) buf[0] = 0;
debugf("ParseBTMMInterfaceKey: Interface Key does not exist");
}
}
mDNSexport void RemoveAutoTunnel6Record(mDNS *const m)
{
DomainAuthInfo *info;
char buf[IFNAMSIZ];
if (!m->p->ConndBTMMDict || (CFDictionaryGetCount(m->p->ConndBTMMDict) == 0))
{
LogInfo("RemoveAutoTunnel6Record: Never registered any records before, not deregistering %p", m->p->ConndBTMMDict);
return;
}
ParseBTMMInterfaceKey(m->p->ConndBTMMDict, buf, sizeof(buf));
if (!strlen(buf))
{
LogInfo("RemoveAutoTunnel6Record: Interface name already NULL, not deregistering");
return;
}
m->AutoTunnelRelayAddrIn = zerov6Addr;
if (!m->AuthInfoList) LogInfo("RemoveAutoTunnel6Record: No Domain AuthInfo");
for (info = m->AuthInfoList; info; info = info->next)
{
if (!info->AutoTunnel) { LogInfo("RemoveAutoTunnel6Record: Domain %##s not an AutoTunnel", info->domain.c); continue;}
if (info->deltime) {LogInfo("RemoveAutoTunnel6Record: Domain %##s about to be deleted", info->domain.c); continue;}
LogInfo("RemoveAutoTunnel6Record: Deregistering records for domain %##s", info->domain.c);
DeregisterAutoTunnel6Record(m, info);
}
CFRelease(m->p->ConndBTMMDict);
m->p->ConndBTMMDict = NULL;
}
mDNSlocal int GetIPv6AddressForIfname(char *ifname, mDNSv6Addr *ipv6Addr)
{
struct ifaddrs *ifa;
struct ifaddrs *ifaddrs;
mDNSAddr addr;
if (if_nametoindex(ifname) == 0) {LogInfo("GetIPv6AddressForIfname: Invalid name %s", ifname); return (-1);}
if (getifaddrs(&ifaddrs) < 0) {LogInfo("GetIPv6AddressForIfname: getifaddrs failed"); return (-1);}
for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next)
{
if (strncmp(ifa->ifa_name, ifname, IFNAMSIZ) != 0)
continue;
if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6)
{
struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)ifa->ifa_addr;
if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr))
continue;
if (SetupAddr(&addr, ifa->ifa_addr) != mStatus_NoError)
{
LogInfo("GetIPv6AddressForIfname: SetupAddr error, continuing to the next address");
continue;
}
else
{
*ipv6Addr = *(mDNSv6Addr *)&addr.ip.v6;
LogInfo("GetIPv6AddressForIfname: Returning IPv6 address %.16a", ipv6Addr);
freeifaddrs(ifaddrs);
return 0;
}
}
}
LogInfo("GetIPv6AddressForIfname: No Valid IPv6 address");
freeifaddrs(ifaddrs);
return (-1);
}
mDNSlocal void AddAutoTunnel6Record(mDNS *const m, char *ifname, CFDictionaryRef BTMMDict)
{
mDNSv6Addr v6addr;
DomainAuthInfo *info;
if (GetIPv6AddressForIfname(ifname, &v6addr) != 0)
{
LogInfo("AddAutoTunnel6Record: No Valid IPv6 addresses found for %s", ifname);
RemoveAutoTunnel6Record(m);
UpdateBTMMRelayConnection(m);
return;
}
m->AutoTunnelRelayAddrOut = v6addr;
if (!m->RegisterAutoTunnel6 || DisableInboundRelayConnection)
{
LogInfo("RegisterAutoTunnel6Record: registration Disabled RegisterAutoTunnel6 %d, DisableInbound %d",
m->RegisterAutoTunnel6, DisableInboundRelayConnection);
return;
}
m->AutoTunnelRelayAddrIn = v6addr;
if (!m->AuthInfoList) LogInfo("AddAutoTunnel6Record: No Domain AuthInfo");
for (info = m->AuthInfoList; info; info = info->next)
{
if (!info->AutoTunnel) { LogInfo("AddAutoTunnel6Record: Domain %##s not an AutoTunnel", info->domain.c); continue;}
if (!info->AutoTunnelNAT.clientContext) {LogInfo("AddAutoTunnel6Record: Domain %##s has no services", info->domain.c); continue;}
if (info->deltime) {LogInfo("AddAutoTunnel6Record: Domain %##s about to be deleted", info->domain.c); continue;}
LogInfo("AddAutoTunnel6Record: Registering records for domain %##s", info->domain.c);
mDNS_Lock(m);
RegisterAutoTunnel6Record(m, info);
mDNS_Unlock(m);
}
if (m->p->ConndBTMMDict) CFRelease(m->p->ConndBTMMDict);
m->p->ConndBTMMDict = CFRetain(BTMMDict);
}
mDNSlocal void ParseBackToMyMac(mDNS *const m, CFDictionaryRef connd)
{
CFDictionaryRef BTMMDict;
char buf[IFNAMSIZ];
BTMMDict = ParseBackToMyMacKey(connd);
if (!BTMMDict)
{
LogInfo("ParseBackToMyMac: CFDictionaryGetValue No value for BackToMyMac, Removing autotunnel6");
RemoveAutoTunnel6Record(m);
m->AutoTunnelRelayAddrOut = zerov6Addr;
mDNS_Lock(m);
UpdateAutoTunnelDomainStatuses(m);
mDNS_Unlock(m);
return;
}
ParseBTMMInterfaceKey(BTMMDict, buf, sizeof(buf));
if (!strlen(buf))
{
LogInfo("ParseBackToMyMac: NULL value for Interface, Removing autotunnel6");
RemoveAutoTunnel6Record(m);
m->AutoTunnelRelayAddrOut = zerov6Addr;
UpdateBTMMRelayConnection(m);
}
else
{
LogInfo("ParseBackToMyMac: non-NULL value for Interface, Adding autotunnel6");
AddAutoTunnel6Record(m, buf, BTMMDict);
}
mDNS_Lock(m);
UpdateAutoTunnelDomainStatuses(m);
mDNS_Unlock(m);
}
mDNSlocal void SetupConndConfigChanges(mDNS *const m)
{
CFDictionaryRef connd;
SCDynamicStoreRef store;
store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:SetupConndConfigChanges"), NULL, NULL);
if (!store) {LogMsg("SetupConndConfigChanges: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); return;}
connd = SCDynamicStoreCopyValue(store, NetworkChangedKey_BTMMConnectivity);
if (!connd)
{LogInfo("SetupConndConfigChanges: SCDynamicStoreCopyValue failed: %s", SCErrorString(SCError())); CFRelease(store); return;}
else
{
ParseBackToMyMac(m, connd);
}
CFRelease(connd);
CFRelease(store);
}
#endif
mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m)
{
LogInfo("*** Network Configuration Change *** (%d)%s",
m->p->NetworkChanged ? mDNS_TimeNow(m) - m->p->NetworkChanged : 0,
m->p->NetworkChanged ? "" : " (no scheduled configuration change)");
m->p->NetworkChanged = 0; mDNSs32 utc = mDNSPlatformUTC();
m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess();
m->SystemSleepOnlyIfWakeOnLAN = SystemSleepOnlyIfWakeOnLAN();
MarkAllInterfacesInactive(m, utc);
UpdateInterfaceList(m, utc);
ClearInactiveInterfaces(m, utc);
SetupActiveInterfaces(m, utc);
#if APPLE_OSX_mDNSResponder
SetupConndConfigChanges(m);
if (m->AutoTunnelHostAddr.b[0])
{
mDNS_Lock(m);
if (TunnelClients(m) || TunnelServers(m))
SetupLocalAutoTunnelInterface_internal(m, mDNSfalse);
mDNS_Unlock(m);
}
ClientTunnel *p;
for (p = m->TunnelClients; p; p = p->next)
if (p->q.ThisQInterval < 0)
{
if (!mDNSIPPortIsZero(p->rmt_outer_port))
{
mDNSAddr tmpSrc = zeroAddr;
mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} };
tmpDst.ip.v4 = p->rmt_outer;
mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst);
if (!mDNSSameIPv6Address(p->loc_inner, m->AutoTunnelHostAddr) ||
!mDNSSameIPv4Address(p->loc_outer, tmpSrc.ip.v4))
{
AutoTunnelSetKeys(p, mDNSfalse);
p->loc_inner = m->AutoTunnelHostAddr;
p->loc_outer = tmpSrc.ip.v4;
AutoTunnelSetKeys(p, mDNStrue);
}
}
else
{
if (!mDNSSameIPv6Address(p->loc_inner, m->AutoTunnelHostAddr) ||
!mDNSSameIPv6Address(p->loc_outer6, m->AutoTunnelRelayAddrOut))
{
AutoTunnelSetKeys(p, mDNSfalse);
p->loc_inner = m->AutoTunnelHostAddr;
p->loc_outer6 = m->AutoTunnelRelayAddrOut;
AutoTunnelSetKeys(p, mDNStrue);
}
}
}
SetSPS(m);
NetworkInterfaceInfoOSX *i;
for (i = m->p->InterfaceList; i; i = i->next)
{
if (!m->SPSSocket) {
if (i->BPF_fd >= 0 && CountProxyTargets(m, i, mDNSNULL, mDNSNULL) == 0) CloseBPF(i);
}
else {
if (i->Exists && i->Registered == i && i->ifinfo.McastTxRx && !(i->ifa_flags & IFF_LOOPBACK) && i->BPF_fd == -1)
{ LogSPS("%s requesting BPF", i->ifinfo.ifname); i->BPF_fd = -2; mDNSRequestBPF(); }
}
}
#endif // APPLE_OSX_mDNSResponder
uDNS_SetupDNSConfig(m);
mDNS_ConfigChanged(m);
}
mDNSlocal void SetNetworkChanged(mDNS *const m, mDNSs32 delay)
{
if (!m->p->NetworkChanged || m->p->NetworkChanged - NonZeroTime(m->timenow + delay) < 0)
{
m->p->NetworkChanged = NonZeroTime(m->timenow + delay);
LogInfo("SetNetworkChanged: setting network changed to %d (%d)", delay, m->p->NetworkChanged);
}
}
mDNSlocal void SetKeyChainTimer(mDNS *const m, mDNSs32 delay)
{
if (!m->p->KeyChainTimer || m->p->KeyChainTimer - NonZeroTime(m->timenow + delay) > 0)
{
m->p->KeyChainTimer = NonZeroTime(m->timenow + delay);
LogInfo("SetKeyChainTimer: %d", delay);
}
}
mDNSlocal CFStringRef CopyNameFromKey(CFStringRef key)
{
CFArrayRef a;
CFStringRef name = NULL;
a = CFStringCreateArrayBySeparatingStrings(NULL, key, CFSTR("/"));
if (a && CFArrayGetCount(a) == 5) name = CFRetain(CFArrayGetValueAtIndex(a, 3));
if (a != NULL) CFRelease(a);
return name;
}
mDNSlocal mDNSBool ChangedKeysHaveIPv4LL(CFArrayRef inkeys)
{
SCDynamicStoreRef store = NULL;
CFDictionaryRef dict = NULL;
CFMutableArrayRef a;
const void **keys = NULL, **vals = NULL;
CFStringRef pattern = NULL;
int i, ic, j, jc;
mDNSBool found = mDNSfalse;
jc = CFArrayGetCount(inkeys);
if (!jc) goto done;
store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:ChangedKeysHaveIPv4LL"), NULL, NULL);
if (store == NULL) goto done;
a = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (a == NULL) goto done;
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetInterface);
if (pattern == NULL) goto done;
CFArrayAppendValue(a, pattern);
CFRelease(pattern);
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetIPv4);
if (pattern == NULL) goto done;
CFArrayAppendValue(a, pattern);
CFRelease(pattern);
dict = SCDynamicStoreCopyMultiple(store, NULL, a);
CFRelease(a);
if (!dict)
{
LogMsg("ChangedKeysHaveIPv4LL: Empty dictionary");
goto done;
}
ic = CFDictionaryGetCount(dict);
vals = mDNSPlatformMemAllocate(sizeof (void *) * ic);
keys = mDNSPlatformMemAllocate(sizeof (void *) * ic);
CFDictionaryGetKeysAndValues(dict, keys, vals);
for (j = 0; j < jc && !found; j++)
{
CFStringRef key = CFArrayGetValueAtIndex(inkeys, j);
CFStringRef ifname = NULL;
char buf[256];
if (!CFStringHasPrefix(key, CFSTR("State:/Network/Interface/")) || !CFStringHasSuffix(key, kSCEntNetIPv4)) continue;
if ((ifname = CopyNameFromKey(key)) == NULL) continue;
if (mDNS_LoggingEnabled)
{
if (!CFStringGetCString(ifname, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
LogInfo("ChangedKeysHaveIPv4LL: potential ifname %s", buf);
}
for (i = 0; i < ic; i++)
{
CFDictionaryRef ipv4dict;
CFStringRef name;
CFStringRef serviceid;
CFStringRef configmethod;
if (!CFStringHasSuffix(keys[i], kSCEntNetInterface)) continue;
if (CFDictionaryGetTypeID() != CFGetTypeID(vals[i])) continue;
if ((name = CFDictionaryGetValue(vals[i], kSCPropNetInterfaceDeviceName)) == NULL) continue;
if (!CFEqual(ifname, name)) continue;
if ((serviceid = CopyNameFromKey(keys[i])) == NULL) continue;
if (mDNS_LoggingEnabled)
{
if (!CFStringGetCString(serviceid, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
LogInfo("ChangedKeysHaveIPv4LL: found serviceid %s", buf);
}
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceid, kSCEntNetIPv4);
CFRelease(serviceid);
if (pattern == NULL) continue;
ipv4dict = CFDictionaryGetValue(dict, pattern);
CFRelease(pattern);
if (!ipv4dict || CFDictionaryGetTypeID() != CFGetTypeID(ipv4dict)) continue;
configmethod = CFDictionaryGetValue(ipv4dict, kSCPropNetIPv4ConfigMethod);
if (!configmethod) continue;
if (mDNS_LoggingEnabled)
{
if (!CFStringGetCString(configmethod, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
LogInfo("ChangedKeysHaveIPv4LL: configmethod %s", buf);
}
if (CFEqual(configmethod, kSCValNetIPv4ConfigMethodLinkLocal)) { found = mDNStrue; break; }
}
CFRelease(ifname);
}
done:
if (vals != NULL) mDNSPlatformMemFree(vals);
if (keys != NULL) mDNSPlatformMemFree(keys);
if (dict != NULL) CFRelease(dict);
if (store != NULL) CFRelease(store);
return found;
}
mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
{
(void)store; mDNSBool changeNow = mDNSfalse;
mDNS *const m = (mDNS *const)context;
KQueueLock(m);
mDNS_Lock(m);
mDNSs32 delay = mDNSPlatformOneSecond * 2;
int c = CFArrayGetCount(changedKeys); CFRange range = { 0, c };
int c1 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Hostnames ) != 0);
int c2 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0);
int c3 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS ) != 0);
int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS ) != 0);
if (c && c - c1 - c2 - c3 - c4 == 0) delay = mDNSPlatformOneSecond/10;
{
int i;
for (i=0; i<c; i++)
{
char buf[256];
if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
if (buf[0])
{
if (strstr(buf, "p2p"))
{
LogInfo("NetworkChanged SC key: %s, not delaying network change", buf);
changeNow = mDNStrue;
break;
}
}
}
}
if (mDNS_LoggingEnabled)
{
int i;
for (i=0; i<c; i++)
{
char buf[256];
if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
LogInfo("*** NetworkChanged SC key: %s", buf);
}
LogInfo("*** NetworkChanged *** %d change%s %s%s%s%sdelay %d",
c, c>1?"s":"",
c1 ? "(Local Hostname) " : "",
c2 ? "(Computer Name) " : "",
c3 ? "(DynamicDNS) " : "",
c4 ? "(DNS) " : "",
delay);
}
mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac);
if (btmmChanged) delay = 0;
SetNetworkChanged(m, delay);
if (c3 || btmmChanged)
SetKeyChainTimer(m, delay);
mDNS_Unlock(m);
if (c4 || ChangedKeysHaveIPv4LL(changedKeys) || changeNow) mDNSMacOSXNetworkChanged(m);
KQueueUnlock(m, "NetworkChanged");
}
#if APPLE_OSX_mDNSResponder
mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *context)
{
(void)context;
char buf[IFNAMSIZ];
CFStringRef ifnameStr = (CFStringRef)key;
CFArrayRef array = (CFArrayRef)value;
if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array));
mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value);
}
#endif
mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info)
{
mDNS *const m = (mDNS *const)info;
(void)store;
LogInfo("DynamicStoreReconnected: Reconnected");
SetLocalDomains();
if (m->FQDN.c[0])
mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1);
CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (!sa)
LogMsg("DynamicStoreReconnected:PrivateDNS: CFArrayCreateMutable failed");
else
{
DomainAuthInfo *FoundInList;
char stringbuf[MAX_ESCAPED_DOMAIN_NAME]; for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
{
ConvertDomainNameToCString(&FoundInList->domain, stringbuf);
CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8);
if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); }
}
mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa);
CFRelease(sa);
}
#if APPLE_OSX_mDNSResponder
mDNS_Lock(m);
UpdateAutoTunnelDomainStatuses(m);
mDNS_Unlock(m);
if (spsStatusDict) CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL);
#endif
}
mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m)
{
mStatus err = -1;
SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL };
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:WatchForNetworkChanges"), NetworkChanged, &context);
CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (!store) { LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); goto error; }
if (!keys || !pattern1 || !pattern2 || !patterns) goto error;
CFArrayAppendValue(keys, NetworkChangedKey_IPv4);
CFArrayAppendValue(keys, NetworkChangedKey_IPv6);
CFArrayAppendValue(keys, NetworkChangedKey_Hostnames);
CFArrayAppendValue(keys, NetworkChangedKey_Computername);
CFArrayAppendValue(keys, NetworkChangedKey_DNS);
CFArrayAppendValue(keys, NetworkChangedKey_DynamicDNS);
CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac);
CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity);
CFArrayAppendValue(patterns, pattern1);
CFArrayAppendValue(patterns, pattern2);
CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort"));
if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns))
{ LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; }
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue()))
{ LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; }
#else
m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; }
CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode);
#endif
SCDynamicStoreSetDisconnectCallBack(store, DynamicStoreReconnected);
m->p->Store = store;
err = 0;
goto exit;
error:
if (store) CFRelease(store);
exit:
if (patterns) CFRelease(patterns);
if (pattern2) CFRelease(pattern2);
if (pattern1) CFRelease(pattern1);
if (keys) CFRelease(keys);
return(err);
}
#if 0 // <rdar://problem/6751656>
mDNSlocal void PMChanged(void *context)
{
mDNS *const m = (mDNS *const)context;
KQueueLock(m);
mDNS_Lock(m);
LogSPS("PMChanged");
SetNetworkChanged(m, mDNSPlatformOneSecond * 2);
mDNS_Unlock(m);
KQueueUnlock(m, "PMChanged");
}
mDNSlocal mStatus WatchForPMChanges(mDNS *const m)
{
m->p->PMRLS = IOPMPrefsNotificationCreateRunLoopSource(PMChanged, m);
if (!m->p->PMRLS) { LogMsg("IOPMPrefsNotificationCreateRunLoopSource failed!"); return mStatus_UnknownErr; }
CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode);
return mStatus_NoError;
}
#endif
#ifndef KEV_DL_WAKEFLAGS_CHANGED
#define KEV_DL_WAKEFLAGS_CHANGED 17
#endif
#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded
mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname)
{
AuthRecord *rr;
int found = 0;
for (rr = m->ResourceRecords; rr; rr=rr->next)
{
if ((rr->resrec.rrtype == kDNSServiceType_SRV) && (rr->ARType == AuthRecordAnyIncludeP2P))
{
uint16_t port = rr->resrec.rdata->u.srv.port.NotAnInteger;
uint16_t protocol;
const mDNSu8 *p;
LogInfo("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr));
found++;
if (found > 1)
{
LogMsg("mDNSSetPacketFilterRules: found service #%d %s", found, ARDisplayString(m, rr));
}
p = rr->resrec.name->c;
if (p[0]) p += 1 + p[0];
if (p[0]) p += 1 + p[0];
if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocol = IPPROTO_TCP;
else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = IPPROTO_UDP;
else
{
LogMsg("mDNSSetPacketFilterRules: could not determine transport protocol of service");
LogMsg("mDNSSetPacketFilterRules: %s", ARDisplayString(m, rr));
return;
}
LogInfo("mDNSSetPacketFilterRules: ifname %s, port(in NBO)0x%X, protocol %d",
ifname, port, protocol);
mDNSPacketFilterControl(PF_SET_RULES, ifname, port, protocol);
}
}
}
#endif // !TARGET_OS_EMBEDDED
#if APPLE_OSX_mDNSResponder
#if !TARGET_OS_EMBEDDED
mDNSexport void mDNSInitPacketFilter(void)
{
mDNS *const m = & mDNSStorage;
NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
while (intf)
{
if (strncmp(intf->ifname, "p2p", 3) == 0)
{
LogInfo("mDNSInitPacketFilter: Setting rules for ifname %s", intf->ifname);
mDNSSetPacketFilterRules(m, intf->ifname);
break;
}
intf = GetFirstActiveInterface(intf->next);
}
}
#else // !TARGET_OS_EMBEDDED
mDNSexport void mDNSInitPacketFilter()
{
}
#endif // !TARGET_OS_EMBEDDED
#endif // APPLE_OSX_mDNSResponder
mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context)
{
mDNS *const m = (mDNS *const)context;
mDNS_Lock(m);
struct { struct kern_event_msg k; char extra[256]; } msg;
int bytes = recv(s1, &msg, sizeof(msg), 0);
if (bytes < 0)
LogMsg("SysEventCallBack: recv error %d errno %d (%s)", bytes, errno, strerror(errno));
else
{
LogInfo("SysEventCallBack got %d bytes size %d %X %s %X %s %X %s id %d code %d %s",
bytes, msg.k.total_size,
msg.k.vendor_code , msg.k.vendor_code == KEV_VENDOR_APPLE ? "KEV_VENDOR_APPLE" : "?",
msg.k.kev_class , msg.k.kev_class == KEV_NETWORK_CLASS ? "KEV_NETWORK_CLASS" : "?",
msg.k.kev_subclass, msg.k.kev_subclass == KEV_DL_SUBCLASS ? "KEV_DL_SUBCLASS" : "?",
msg.k.id, msg.k.event_code,
msg.k.event_code == KEV_DL_SIFFLAGS ? "KEV_DL_SIFFLAGS" :
msg.k.event_code == KEV_DL_SIFMETRICS ? "KEV_DL_SIFMETRICS" :
msg.k.event_code == KEV_DL_SIFMTU ? "KEV_DL_SIFMTU" :
msg.k.event_code == KEV_DL_SIFPHYS ? "KEV_DL_SIFPHYS" :
msg.k.event_code == KEV_DL_SIFMEDIA ? "KEV_DL_SIFMEDIA" :
msg.k.event_code == KEV_DL_SIFGENERIC ? "KEV_DL_SIFGENERIC" :
msg.k.event_code == KEV_DL_ADDMULTI ? "KEV_DL_ADDMULTI" :
msg.k.event_code == KEV_DL_DELMULTI ? "KEV_DL_DELMULTI" :
msg.k.event_code == KEV_DL_IF_ATTACHED ? "KEV_DL_IF_ATTACHED" :
msg.k.event_code == KEV_DL_IF_DETACHING ? "KEV_DL_IF_DETACHING" :
msg.k.event_code == KEV_DL_IF_DETACHED ? "KEV_DL_IF_DETACHED" :
msg.k.event_code == KEV_DL_LINK_OFF ? "KEV_DL_LINK_OFF" :
msg.k.event_code == KEV_DL_LINK_ON ? "KEV_DL_LINK_ON" :
msg.k.event_code == KEV_DL_PROTO_ATTACHED ? "KEV_DL_PROTO_ATTACHED" :
msg.k.event_code == KEV_DL_PROTO_DETACHED ? "KEV_DL_PROTO_DETACHED" :
msg.k.event_code == KEV_DL_LINK_ADDRESS_CHANGED ? "KEV_DL_LINK_ADDRESS_CHANGED" :
msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED ? "KEV_DL_WAKEFLAGS_CHANGED" : "?");
if (msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED || msg.k.event_code == KEV_DL_LINK_ON)
SetNetworkChanged(m, mDNSPlatformOneSecond * 2);
#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded
if (msg.k.event_code == KEV_DL_IF_ATTACHED)
{
struct net_event_data * p;
p = (struct net_event_data *) & msg.k.event_data;
if (strncmp(p->if_name, "p2p", 3) == 0)
{
char ifname[IFNAMSIZ];
snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit);
LogInfo("SysEventCallBack: KEV_DL_IF_ATTACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name);
mDNSSetPacketFilterRules(m, ifname);
}
}
if (msg.k.event_code == KEV_DL_IF_DETACHED)
{
struct net_event_data * p;
p = (struct net_event_data *) & msg.k.event_data;
if (strncmp(p->if_name, "p2p", 3) == 0)
{
char ifname[IFNAMSIZ];
snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit);
LogInfo("SysEventCallBack: KEV_DL_IF_DETACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name);
mDNSPacketFilterControl(PF_CLEAR_RULES, ifname, 0, 0);
}
}
#endif // !TARGET_OS_EMBEDDED
}
mDNS_Unlock(m);
}
mDNSlocal mStatus WatchForSysEvents(mDNS *const m)
{
m->p->SysEventNotifier = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT);
if (m->p->SysEventNotifier < 0)
{ LogMsg("WatchForSysEvents: socket failed error %d errno %d (%s)", m->p->SysEventNotifier, errno, strerror(errno)); return(mStatus_NoMemoryErr); }
struct kev_request kev_req = { KEV_VENDOR_APPLE, KEV_NETWORK_CLASS, KEV_DL_SUBCLASS };
int err = ioctl(m->p->SysEventNotifier, SIOCSKEVFILT, &kev_req);
if (err < 0)
{
LogMsg("WatchForSysEvents: SIOCSKEVFILT failed error %d errno %d (%s)", err, errno, strerror(errno));
close(m->p->SysEventNotifier);
m->p->SysEventNotifier = -1;
return(mStatus_UnknownErr);
}
m->p->SysEventKQueue.KQcallback = SysEventCallBack;
m->p->SysEventKQueue.KQcontext = m;
m->p->SysEventKQueue.KQtask = "System Event Notifier";
KQueueSet(m->p->SysEventNotifier, EV_ADD, EVFILT_READ, &m->p->SysEventKQueue);
return(mStatus_NoError);
}
#ifndef NO_SECURITYFRAMEWORK
mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCallbackInfo *info, void *context)
{
LogInfo("*** Keychain Changed ***");
mDNS *const m = (mDNS *const)context;
SecKeychainRef skc;
OSStatus err = SecKeychainCopyDefault(&skc);
if (!err)
{
if (info->keychain == skc)
{
mDNSBool relevant = (keychainEvent == kSecDeleteEvent);
if (!relevant)
{
UInt32 tags[3] = { kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr };
SecKeychainAttributeInfo attrInfo = { 3, tags, NULL }; SecKeychainAttributeList *a = NULL;
err = SecKeychainItemCopyAttributesAndData(info->item, &attrInfo, NULL, &a, NULL, NULL);
if (!err)
{
relevant = ((a->attr[0].length == 4 && (!strncasecmp(a->attr[0].data, "ddns", 4) || !strncasecmp(a->attr[0].data, "sndd", 4))) ||
(a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix)))) ||
(a->attr[1].length >= mDNSPlatformStrLen(btmmprefix) && (!strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix)))));
SecKeychainItemFreeAttributesAndData(a, NULL);
}
}
if (relevant)
{
LogInfo("*** Keychain Changed *** KeychainEvent=%d %s",
keychainEvent,
keychainEvent == kSecAddEvent ? "kSecAddEvent" :
keychainEvent == kSecDeleteEvent ? "kSecDeleteEvent" :
keychainEvent == kSecUpdateEvent ? "kSecUpdateEvent" : "<Unknown>");
KQueueLock(m);
mDNS_Lock(m);
SetKeyChainTimer(m, mDNSPlatformOneSecond);
mDNS_Unlock(m);
KQueueUnlock(m, "KeychainChanged");
}
}
CFRelease(skc);
}
return 0;
}
#endif
mDNSlocal void PowerOn(mDNS *const m)
{
mDNSCoreMachineSleep(m, false); if (m->p->WakeAtUTC)
{
long utc = mDNSPlatformUTC();
mDNSPowerRequest(-1,-1); if (m->p->WakeAtUTC - utc > 30) LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc);
else if (utc - m->p->WakeAtUTC > 30) LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC);
else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is K66AP so not re-sleeping", utc - m->p->WakeAtUTC);
else
{
LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc);
m->p->RequestReSleep = mDNS_TimeNow(m) + 20 * mDNSPlatformOneSecond;
}
}
}
mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument)
{
mDNS *const m = (mDNS *const)refcon;
KQueueLock(m);
(void)service; debugf("PowerChanged %X %lX", messageType, messageArgument);
m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess();
switch(messageType)
{
case kIOMessageCanSystemPowerOff: LogSPS("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; case kIOMessageSystemWillPowerOff: LogSPS("PowerChanged kIOMessageSystemWillPowerOff"); mDNSCoreMachineSleep(m, true);
if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m);
break;
case kIOMessageSystemWillNotPowerOff: LogSPS("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; case kIOMessageCanSystemSleep: LogSPS("PowerChanged kIOMessageCanSystemSleep (no action)"); break; case kIOMessageSystemWillSleep: LogSPS("PowerChanged kIOMessageSystemWillSleep"); mDNSCoreMachineSleep(m, true);
break;
case kIOMessageSystemWillNotSleep: LogSPS("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; case kIOMessageSystemHasPoweredOn: LogSPS("PowerChanged kIOMessageSystemHasPoweredOn"); if (m->SleepState)
{
LogMsg("PowerChanged kIOMessageSystemHasPoweredOn: ERROR m->SleepState %d", m->SleepState);
PowerOn(m);
}
mDNS_Lock(m);
if (!m->p->NetworkChanged ||
m->p->NetworkChanged - NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2) < 0)
m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2);
mDNS_Unlock(m);
break;
case kIOMessageSystemWillRestart: LogSPS("PowerChanged kIOMessageSystemWillRestart (no action)"); break; case kIOMessageSystemWillPowerOn: LogSPS("PowerChanged kIOMessageSystemWillPowerOn");
if (OSXVers && OSXVers < OSXVers_10_6_SnowLeopard)
{
sleep(5);
LogMsg("Running on Mac OS X version 10.%d earlier than 10.6; "
"PowerChanged did sleep(5) to wait for Ethernet hardware", OSXVers - OSXVers_Base);
}
if (m->SleepState != SleepState_Sleeping)
{
LogMsg("kIOMessageSystemWillPowerOn: ERROR m->SleepState %d", m->SleepState);
m->SleepState = SleepState_Sleeping;
mDNSMacOSXNetworkChanged(m);
}
PowerOn(m);
break;
default: LogSPS("PowerChanged unknown message %X", messageType); break;
}
if (messageType == kIOMessageSystemWillSleep) m->p->SleepCookie = (long)messageArgument;
else IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument);
KQueueUnlock(m, "PowerChanged Sleep/Wake");
}
#if defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) && !TARGET_OS_EMBEDDED
mDNSlocal void SnowLeopardPowerChanged(void *refcon, IOPMConnection connection, IOPMConnectionMessageToken token, IOPMSystemPowerStateCapabilities eventDescriptor)
{
mDNS *const m = (mDNS *const)refcon;
KQueueLock(m);
LogSPS("SnowLeopardPowerChanged %X %X %X%s%s%s%s%s",
connection, token, eventDescriptor,
eventDescriptor & kIOPMSystemPowerStateCapabilityCPU ? " CPU" : "",
eventDescriptor & kIOPMSystemPowerStateCapabilityVideo ? " Video" : "",
eventDescriptor & kIOPMSystemPowerStateCapabilityAudio ? " Audio" : "",
eventDescriptor & kIOPMSystemPowerStateCapabilityNetwork ? " Network" : "",
eventDescriptor & kIOPMSystemPowerStateCapabilityDisk ? " Disk" : "");
m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess();
if (eventDescriptor & kIOPMSystemPowerStateCapabilityCPU)
{
if (m->SleepLimit)
{
LogSPS("SnowLeopardPowerChanged: Waking up, Acking old Sleep, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState);
IOPMConnectionAcknowledgeEvent(connection, m->p->SleepCookie);
m->SleepLimit = 0;
}
LogSPS("SnowLeopardPowerChanged: Waking up, Acking Wakeup, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState);
mDNS_Lock(m);
SetNetworkChanged(m, 2 * mDNSPlatformOneSecond);
mDNS_Unlock(m);
if (m->SleepState != SleepState_Awake) PowerOn(m);
IOPMConnectionAcknowledgeEvent(connection, token);
}
else
{
if (m->SleepState) LogMsg("SnowLeopardPowerChanged: Sleep Error %X m->SleepState %d", eventDescriptor, m->SleepState);
mDNSCoreMachineSleep(m, true);
m->p->SleepCookie = token;
}
KQueueUnlock(m, "SnowLeopardPowerChanged Sleep/Wake");
}
#endif
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - /etc/hosts support
#endif
#define ETCHOSTS_BUFSIZE 1024 // Buffer size to parse a single line in /etc/hosts
mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
{
(void)m; (void)rr;
(void)result;
if (result == mStatus_MemFree)
{
LogInfo("FreeEtcHosts: %s", ARDisplayString(m, rr));
freeL("etchosts", rr);
}
}
mDNSlocal mDNSBool mDNSMacOSXCreateEtcHostsEntry(mDNS *const m, const domainname *domain, const struct sockaddr *sa, const domainname *cname, char *ifname, AuthHash *auth)
{
AuthRecord *rr;
mDNSu32 slot;
mDNSu32 namehash;
AuthGroup *ag;
mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly;
mDNSu16 rrtype;
if (!domain)
{
LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! name NULL");
return mDNSfalse;
}
if (!sa && !cname)
{
LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa and cname both NULL");
return mDNSfalse;
}
if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
{
LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa with bad family %d", sa->sa_family);
return mDNSfalse;
}
if (ifname)
{
mDNSu32 ifindex = if_nametoindex(ifname);
if (!ifindex)
{
LogMsg("mDNSMacOSXCreateEtcHostsEntry: hosts entry %##s with invalid ifname %s", domain->c, ifname);
return mDNSfalse;
}
InterfaceID = (mDNSInterfaceID)(uintptr_t)ifindex;
}
if (sa)
rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA);
else
rrtype = kDNSType_CNAME;
slot = AuthHashSlot(domain);
namehash = DomainNameHashValue(domain);
ag = AuthGroupForName(auth, slot, namehash, domain);
if (ag)
{
rr = ag->members;
while (rr)
{
if (rr->resrec.rrtype == rrtype)
{
if (rrtype == kDNSType_A)
{
mDNSv4Addr ip;
ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip))
{
LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv4 address for name %##s", domain->c);
return mDNSfalse;
}
}
else if (rrtype == kDNSType_AAAA)
{
mDNSv6Addr ip6;
ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6))
{
LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv6 address for name %##s", domain->c);
return mDNSfalse;
}
}
else if (rrtype == kDNSType_CNAME)
{
if (SameDomainName(&rr->resrec.rdata->u.name, cname))
{
LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same cname %##s for name %##s", cname->c, domain->c);
return mDNSfalse;
}
}
}
rr = rr->next;
}
}
rr= mallocL("etchosts", sizeof(*rr));
if (rr == NULL) return mDNSfalse;
mDNSPlatformMemZero(rr, sizeof(*rr));
mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL);
AssignDomainName(&rr->namestorage, domain);
if (sa)
{
rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr);
if (sa->sa_family == AF_INET)
rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
else
{
rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
}
}
else
{
rr->resrec.rdlength = DomainNameLength(cname);
rr->resrec.rdata->u.name.c[0] = 0;
AssignDomainName(&rr->resrec.rdata->u.name, cname);
}
rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
SetNewRData(&rr->resrec, mDNSNULL, 0); LogInfo("mDNSMacOSXCreateEtcHostsEntry: Adding resource record %s", ARDisplayString(m, rr));
InsertAuthRecord(m, auth, rr);
return mDNStrue;
}
mDNSlocal int EtcHostsParseOneName(int start, int length, char *buffer, char **name)
{
int i;
*name = NULL;
for (i = start; i < length; i++)
{
if (buffer[i] == '#')
return -1;
if (buffer[i] != ' ' && buffer[i] != ',' && buffer[i] != '\t')
{
*name = &buffer[i];
for (i++; i < length; i++)
{
if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t')
{
buffer[i] = 0;
break;
}
}
return i;
}
}
return -1;
}
mDNSlocal void mDNSMacOSXParseEtcHostsLine(mDNS *const m, char *buffer, ssize_t length, AuthHash *auth)
{
int i;
int ifStart = 0;
char *ifname = NULL;
domainname name1d;
domainname name2d;
char *name1;
char *name2;
int aliasIndex;
for (i = 0; i < length; i++)
{
if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t' || buffer[i] == '%')
{
if (buffer[i] == '%')
ifStart = i + 1;
buffer[i] = 0;
break;
}
}
struct addrinfo hints;
bzero(&hints, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
struct addrinfo *gairesults = NULL;
if (getaddrinfo(buffer, NULL, &hints, &gairesults) != 0)
{
LogInfo("mDNSMacOSXParseEtcHostsLine: getaddrinfo returning null");
return;
}
if (ifStart)
{
ifname = &buffer[ifStart];
for (i = ifStart + 1; i < length; i++)
{
if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t')
{
buffer[i] = 0;
break;
}
}
}
i = EtcHostsParseOneName(i + 1, length, buffer, &name1);
if (i == length)
{
if (!MakeDomainNameFromDNSNameString(&name1d, name1))
{
LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1);
freeaddrinfo(gairesults);
return;
}
mDNSMacOSXCreateEtcHostsEntry(m, &name1d, gairesults->ai_addr, mDNSNULL, ifname, auth);
}
else if (i != -1)
{
domainname first;
if (!MakeDomainNameFromDNSNameString(&first, name1))
{
LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1);
freeaddrinfo(gairesults);
return;
}
aliasIndex = 0;
while (i <= length)
{
i = EtcHostsParseOneName(i + 1, length, buffer, &name2);
if (name2)
{
if (!MakeDomainNameFromDNSNameString(&name2d, name2))
{
LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name2);
freeaddrinfo(gairesults);
return;
}
aliasIndex++;
}
else if (!aliasIndex)
{
LogInfo("mDNSMacOSXParseEtcHostsLine: White space at the end of %##s", first.c);
break;
}
else
{
name2d.c[0] = 0;
AssignDomainName(&name2d, &first);
}
if (aliasIndex > 1 || SameDomainName(&name2d, &first))
{
if (!SameDomainName(&name1d, &name2d))
{
if (!mDNSMacOSXCreateEtcHostsEntry(m, &name1d, mDNSNULL, &name2d, ifname, auth))
{
freeaddrinfo(gairesults);
return;
}
}
else
LogMsg("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names name1 %##s, name2 %##s", name1d.c, name2d.c);
}
if (SameDomainName(&name2d, &first)) break;
name1d.c[0] = 0;
AssignDomainName(&name1d, &name2d);
}
mDNSMacOSXCreateEtcHostsEntry(m, &first, gairesults->ai_addr, mDNSNULL, ifname, auth);
}
freeaddrinfo(gairesults);
}
mDNSlocal void mDNSMacOSXParseEtcHosts(mDNS *const m, int fd, AuthHash *auth)
{
mDNSBool good;
char buf[ETCHOSTS_BUFSIZE];
int len;
FILE *fp;
if (fd == -1) { LogInfo("mDNSMacOSXParseEtcHosts: fd is -1"); return; }
fp = fopen("/etc/hosts", "r");
if (!fp) { LogInfo("mDNSMacOSXParseEtcHosts: fp is NULL"); return; }
while (1)
{
good = (fgets(buf, ETCHOSTS_BUFSIZE, fp) != NULL);
if (!good) break;
if (buf[0] == '#' || buf[0] == '\r' || buf[0] == '\n')
continue;
len = strlen(buf);
if (!len) break;
if (buf[len - 1] == '\r' || buf[len - 1] == '\n')
buf[len - 1] = '\0';
mDNSMacOSXParseEtcHostsLine(m, buf, len - 1, auth);
}
fclose(fp);
}
mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m);
mDNSlocal int mDNSMacOSXGetEtcHostsFD(mDNS *const m)
{
#ifdef __DISPATCH_GROUP__
static dispatch_queue_t etcq = 0;
static dispatch_source_t etcsrc = 0;
static dispatch_source_t hostssrc = 0;
if (!etcq)
{
etcq = dispatch_get_main_queue();
if (etcq)
{
dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
}
return -1;
}
if (hostssrc) return dispatch_source_get_handle(hostssrc);
#endif
int fd = open("/etc/hosts", O_RDONLY);
#ifdef __DISPATCH_GROUP__
if (fd == -1)
{
if (etcsrc) { LogInfo("mDNSMacOSXGetEtcHostsFD: Returning etcfd because no etchosts"); return fd; }
fd = open("/etc", O_RDONLY);
if (fd == -1) { LogInfo("mDNSMacOSXGetEtcHostsFD: etc does not exist"); return -1; }
etcsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME, etcq);
if (etcsrc == NULL)
{
close(fd);
return -1;
}
dispatch_source_set_event_handler(etcsrc,
^{
u_int32_t flags = dispatch_source_get_data(etcsrc);
LogMsg("mDNSMacOSXGetEtcHostsFD: /etc changed 0x%x", flags);
if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0)
{
dispatch_source_cancel(etcsrc);
dispatch_release(etcsrc);
etcsrc = NULL;
dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
return;
}
if ((flags & DISPATCH_VNODE_WRITE) != 0 && hostssrc == NULL)
{
mDNSMacOSXUpdateEtcHosts(m);
}
});
dispatch_source_set_cancel_handler(etcsrc, ^{close(fd);});
dispatch_resume(etcsrc);
fd = open("/etc/hosts", O_RDONLY | O_EVTONLY);
if (fd == -1) { LogMsg("mDNSMacOSXGetEtcHostsFD etc hosts does not exist, watching etc"); return -1; }
}
hostssrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd,
(DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME |
DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_LINK | DISPATCH_VNODE_REVOKE), etcq);
if (hostssrc == NULL)
{
close(fd);
return -1;
}
dispatch_source_set_event_handler(hostssrc,
^{
u_int32_t flags = dispatch_source_get_data(hostssrc);
LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts changed 0x%x", flags);
if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0)
{
dispatch_source_cancel(hostssrc);
dispatch_release(hostssrc);
hostssrc = NULL;
usleep(1000000);
dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
return;
}
if ((flags & DISPATCH_VNODE_WRITE) != 0)
{
mDNSMacOSXUpdateEtcHosts(m);
}
});
dispatch_source_set_cancel_handler(hostssrc, ^{LogInfo("mDNSMacOSXGetEtcHostsFD: Closing etchosts fd %d", fd); close(fd);});
dispatch_resume(hostssrc);
if (etcsrc)
{
dispatch_source_cancel(etcsrc);
dispatch_release(etcsrc);
etcsrc = NULL;
}
LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts being monitored, and not etc");
return hostssrc ? (int)dispatch_source_get_handle(hostssrc) : -1;
#else
(void)m;
return fd;
#endif;
}
mDNSlocal void FlushAllCacheRecords(mDNS *const m)
{
CacheRecord *cr;
mDNSu32 slot;
CacheGroup *cg;
FORALL_CACHERECORDS(slot, cg, cr)
{
if (cr->resrec.InterfaceID) continue;
if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) ||
RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA))
{
LogInfo("FlushAllCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr));
mDNS_PurgeCacheResourceRecord(m, cr);
}
}
}
mDNSlocal mDNSBool EtcHostsAddNewEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck)
{
AuthGroup *ag;
mDNSu32 slot;
AuthRecord *rr, *primary, *rrnext;
for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
for (ag = newhosts->rrauth_hash[slot]; ag; ag = ag->next)
{
primary = NULL;
for (rr = ag->members; rr; rr = rrnext)
{
rrnext = rr->next;
AuthGroup *ag1;
AuthRecord *rr1;
mDNSBool found = mDNSfalse;
ag1 = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec);
if (ag1 && ag1->members)
{
if (!primary) primary = ag1->members;
rr1 = ag1->members;
while (rr1)
{
if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec))
{
LogInfo("EtcHostsAddNewEntries: Skipping, not adding %s", ARDisplayString(m, rr1));
found = mDNStrue;
break;
}
rr1 = rr1->next;
}
}
if (!found)
{
if (justCheck)
{
LogInfo("EtcHostsAddNewEntries: Entry %s not registered with core yet", ARDisplayString(m, rr));
return mDNStrue;
}
RemoveAuthRecord(m, newhosts, rr);
rr->RRSet = (primary ? primary : rr);
rr->next = NULL;
LogInfo("EtcHostsAddNewEntries: Adding %s", ARDisplayString(m, rr));
if (mDNS_Register_internal(m, rr) != mStatus_NoError)
LogMsg("EtcHostsAddNewEntries: mDNS_Register failed for %s", ARDisplayString(m, rr));
}
}
}
return mDNSfalse;
}
mDNSlocal mDNSBool EtcHostsDeleteOldEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck)
{
AuthGroup *ag;
mDNSu32 slot;
AuthRecord *rr, *primary, *rrnext;
for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
for (rr = ag->members; rr; rr = rrnext)
{
mDNSBool found = mDNSfalse;
AuthGroup *ag1;
AuthRecord *rr1;
rrnext = rr->next;
if (rr->RecordCallback != FreeEtcHosts) continue;
ag1 = AuthGroupForRecord(newhosts, slot, &rr->resrec);
if (ag1)
{
primary = rr1 = ag1->members;
while (rr1)
{
if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec))
{
LogInfo("EtcHostsDeleteOldEntries: Old record %s found in new, skipping", ARDisplayString(m, rr));
found = mDNStrue;
break;
}
rr1 = rr1->next;
}
}
if (!found)
{
if (justCheck)
{
LogInfo("EtcHostsDeleteOldEntries: Record %s not found in new, deleting", ARDisplayString(m, rr));
return mDNStrue;
}
if (rr == ag->members)
{
AuthRecord *new_primary = rr->next;
AuthRecord *r = new_primary;
while (r)
{
if (r->RRSet == rr)
{
LogInfo("EtcHostsDeleteOldEntries: Updating Resource Record %s to primary", ARDisplayString(m, r));
r->RRSet = new_primary;
}
else LogMsg("EtcHostsDeleteOldEntries: ERROR!! Resource Record %s not pointing to primary %##s", ARDisplayString(m, r), r->resrec.name);
r = r->next;
}
}
LogInfo("EtcHostsDeleteOldEntries: Deleting %s", ARDisplayString(m, rr));
mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
}
}
return mDNSfalse;
}
mDNSlocal void UpdateEtcHosts(mDNS *const m, void *context)
{
AuthHash *newhosts = (AuthHash *)context;
if (!m->mDNS_busy) LogMsg("UpdateEtcHosts: ERROR!! Lock not held");
EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse);
EtcHostsAddNewEntries(m, newhosts, mDNSfalse);
}
mDNSlocal void FreeNewHosts(AuthHash *newhosts)
{
mDNSu32 slot;
AuthGroup *ag, *agnext;
AuthRecord *rr, *rrnext;
for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
for (ag = newhosts->rrauth_hash[slot]; ag; ag = agnext)
{
agnext = ag->next;
for (rr = ag->members; rr; rr = rrnext)
{
rrnext = rr->next;
freeL("etchosts", rr);
}
freeL("AuthGroups", ag);
}
}
mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m)
{
AuthHash newhosts;
KQueueLock(m);
mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
int fd = mDNSMacOSXGetEtcHostsFD(m);
if (fd != -1)
{
LogInfo("mDNSMacOSXUpdateEtcHosts: Parsing /etc/hosts fd %d", fd);
mDNSMacOSXParseEtcHosts(m, fd, &newhosts);
}
else LogInfo("mDNSMacOSXUpdateEtcHosts: /etc/hosts is not present");
mDNS_Lock(m);
if (!EtcHostsAddNewEntries(m, &newhosts, mDNStrue))
{
if (!EtcHostsDeleteOldEntries(m, &newhosts, mDNStrue))
{
LogInfo("mDNSMacOSXUpdateEtcHosts: No work");
mDNS_Unlock(m);
KQueueUnlock(m, "/etc/hosts changed");
FreeNewHosts(&newhosts);
return;
}
}
mDNSCoreRestartAddressQueries(m, mDNSfalse, FlushAllCacheRecords, UpdateEtcHosts, &newhosts);
mDNS_Unlock(m);
KQueueUnlock(m, "/etc/hosts changed");
FreeNewHosts(&newhosts);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Initialization & Teardown
#endif
CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void);
CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey;
CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey;
CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey;
mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
{
int major = 0, minor = 0;
char letter = 0, prodname[256]="<Unknown>", prodvers[256]="<Unknown>", buildver[256]="<Unknown>";
CFDictionaryRef vers = _CFCopySystemVersionDictionary();
if (vers)
{
CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey);
CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey);
CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey);
if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8);
if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8);
if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8))
sscanf(buildver, "%d%c%d", &major, &letter, &minor);
CFRelease(vers);
}
if (!major) { major=8; LogMsg("Note: No Major Build Version number found; assuming 8"); }
if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion));
if ((prodname[0] & 0xDF) == 'M') OSXVers = major; else iOSVers = major;
}
mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void)
{
int err = -1;
int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (s < 3)
LogMsg("mDNSPlatformInit_CanReceiveUnicast: socket error %d errno %d (%s)", s, errno, strerror(errno));
else
{
struct sockaddr_in s5353;
s5353.sin_family = AF_INET;
s5353.sin_port = MulticastDNSPort.NotAnInteger;
s5353.sin_addr.s_addr = 0;
err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353));
close(s);
}
if (err) LogMsg("No unicast UDP responses");
else debugf("Unicast UDP responses okay");
return(err == 0);
}
mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
{
mStatus err;
m->p->CFRunLoop = CFRunLoopGetCurrent();
char HINFO_SWstring[256] = "";
mDNSMacOSXSystemBuildNumber(HINFO_SWstring);
int i;
for (i=0; i<100; i++)
{
domainlabel testlabel;
testlabel.c[0] = 0;
GetUserSpecifiedLocalHostName(&testlabel);
if (testlabel.c[0]) break;
usleep(50000);
}
m->hostlabel.c[0] = 0;
int get_model[2] = { CTL_HW, HW_MODEL };
size_t len_model = sizeof(HINFO_HWstring_buffer);
if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0)
HINFO_HWstring = HINFO_HWstring_buffer;
HINFO_HWstring_prefixlen = strchr(HINFO_HWstring_buffer, ',') ? strcspn(HINFO_HWstring, "0123456789") : strlen(HINFO_HWstring);
if (OSXVers && OSXVers <= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LimitedIPv6;
if (OSXVers && OSXVers >= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LossySyslog;
if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue;
mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring);
mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring);
if (hlen + slen < 254)
{
m->HIHardware.c[0] = hlen;
m->HISoftware.c[0] = slen;
mDNSPlatformMemCopy(&m->HIHardware.c[1], HINFO_HWstring, hlen);
mDNSPlatformMemCopy(&m->HISoftware.c[1], HINFO_SWstring, slen);
}
m->p->permanentsockets.port = MulticastDNSPort;
m->p->permanentsockets.m = m;
m->p->permanentsockets.sktv4 = -1;
m->p->permanentsockets.kqsv4.KQcallback = myKQSocketCallBack;
m->p->permanentsockets.kqsv4.KQcontext = &m->p->permanentsockets;
m->p->permanentsockets.kqsv4.KQtask = "UDP packet reception";
#ifndef NO_IPV6
m->p->permanentsockets.sktv6 = -1;
m->p->permanentsockets.kqsv6.KQcallback = myKQSocketCallBack;
m->p->permanentsockets.kqsv6.KQcontext = &m->p->permanentsockets;
m->p->permanentsockets.kqsv6.KQtask = "UDP packet reception";
#endif
err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL);
#ifndef NO_IPV6
err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL);
#endif
struct sockaddr_in s4;
socklen_t n4 = sizeof(s4);
if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno));
else m->UnicastPort4.NotAnInteger = s4.sin_port;
#ifndef NO_IPV6
if (m->p->permanentsockets.sktv6 >= 0)
{
struct sockaddr_in6 s6;
socklen_t n6 = sizeof(s6);
if (getsockname(m->p->permanentsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno));
else m->UnicastPort6.NotAnInteger = s6.sin6_port;
}
#endif
m->p->InterfaceList = mDNSNULL;
m->p->userhostlabel.c[0] = 0;
m->p->usernicelabel.c[0] = 0;
m->p->prevoldnicelabel.c[0] = 0;
m->p->prevnewnicelabel.c[0] = 0;
m->p->prevoldhostlabel.c[0] = 0;
m->p->prevnewhostlabel.c[0] = 0;
m->p->NotifyUser = 0;
m->p->KeyChainTimer = 0;
m->p->WakeAtUTC = 0;
m->p->RequestReSleep = 0;
#if APPLE_OSX_mDNSResponder
uuid_generate(m->asl_uuid);
#endif
m->AutoTunnelHostAddr.b[0] = 0; m->AutoTunnelRelayAddrIn = zerov6Addr;
m->AutoTunnelRelayAddrOut = zerov6Addr;
NetworkChangedKey_IPv4 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
NetworkChangedKey_IPv6 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6);
NetworkChangedKey_Hostnames = SCDynamicStoreKeyCreateHostNames(NULL);
NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL);
NetworkChangedKey_DNS = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS);
if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS)
{ LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr); }
err = WatchForNetworkChanges(m);
if (err) { LogMsg("mDNSPlatformInit_setup: WatchForNetworkChanges failed %d", err); return(err); }
#if 0 // <rdar://problem/6751656>
err = WatchForPMChanges(m);
if (err) { LogMsg("mDNSPlatformInit_setup: WatchForPMChanges failed %d", err); return(err); }
#endif
err = WatchForSysEvents(m);
if (err) { LogMsg("mDNSPlatformInit_setup: WatchForSysEvents failed %d", err); return(err); }
mDNSs32 utc = mDNSPlatformUTC();
m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess();
UpdateInterfaceList(m, utc);
SetupActiveInterfaces(m, utc);
#ifndef NO_SECURITYFRAMEWORK
SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
#endif
mDNS_Lock(m);
SetDomainSecrets(m);
SetLocalDomains();
mDNS_Unlock(m);
#ifndef NO_SECURITYFRAMEWORK
err = SecKeychainAddCallback(KeychainChanged, kSecAddEventMask|kSecDeleteEventMask|kSecUpdateEventMask, m);
if (err) { LogMsg("mDNSPlatformInit_setup: SecKeychainAddCallback failed %d", err); return(err); }
#endif
#if !defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) || TARGET_OS_EMBEDDED
LogMsg("Note: Compiled without SnowLeopard Fine-Grained Power Management support");
#else
IOPMConnection c;
IOReturn iopmerr = IOPMConnectionCreate(CFSTR("mDNSResponder"), kIOPMSystemPowerStateCapabilityCPU, &c);
if (iopmerr) LogMsg("IOPMConnectionCreate failed %d", iopmerr);
else
{
iopmerr = IOPMConnectionSetNotification(c, m, SnowLeopardPowerChanged);
if (iopmerr) LogMsg("IOPMConnectionSetNotification failed %d", iopmerr);
else
{
iopmerr = IOPMConnectionScheduleWithRunLoop(c, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if (iopmerr) LogMsg("IOPMConnectionScheduleWithRunLoop failed %d", iopmerr);
}
}
m->p->IOPMConnection = iopmerr ? mDNSNULL : c;
if (iopmerr) #endif // kIOPMAcknowledgmentOptionSystemCapabilityRequirements
{
m->p->PowerConnection = IORegisterForSystemPower(m, &m->p->PowerPortRef, PowerChanged, &m->p->PowerNotifier);
if (!m->p->PowerConnection) { LogMsg("mDNSPlatformInit_setup: IORegisterForSystemPower failed"); return(-1); }
else
{
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
IONotificationPortSetDispatchQueue(m->p->PowerPortRef, dispatch_get_main_queue());
#else
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode);
#endif
}
}
#if APPLE_OSX_mDNSResponder
if (!strncasecmp(HINFO_HWstring, "Xserve", 6)) { SPMetricPortability = 25 ; SPMetricMarginalPower = 84 ; SPMetricTotalPower = 85 ; }
else if (!strncasecmp(HINFO_HWstring, "RackMac", 7)) { SPMetricPortability = 25 ; SPMetricMarginalPower = 84 ; SPMetricTotalPower = 85 ; }
else if (!strncasecmp(HINFO_HWstring, "MacPro", 6)) { SPMetricPortability = 27 ; SPMetricMarginalPower = 84 ; SPMetricTotalPower = 85 ; }
else if (!strncasecmp(HINFO_HWstring, "PowerMac", 8)) { SPMetricPortability = 27 ; SPMetricMarginalPower = 82 ; SPMetricTotalPower = 83 ; }
else if (!strncasecmp(HINFO_HWstring, "iMac", 4)) { SPMetricPortability = 30 ; SPMetricMarginalPower = 77 ; SPMetricTotalPower = 78 ; }
else if (!strncasecmp(HINFO_HWstring, "Macmini", 7)) { SPMetricPortability = 33 ; SPMetricMarginalPower = 73 ; SPMetricTotalPower = 74 ; }
else if (!strncasecmp(HINFO_HWstring, "TimeCapsule", 11)) { SPMetricPortability = 34 ; SPMetricMarginalPower = 10 ; SPMetricTotalPower = 70 ; }
else if (!strncasecmp(HINFO_HWstring, "AirPort", 7)) { SPMetricPortability = 35 ; SPMetricMarginalPower = 10 ; SPMetricTotalPower = 70 ; }
else if (!strncasecmp(HINFO_HWstring, "AppleTV", 7)) { SPMetricPortability = 35 ; SPMetricMarginalPower = 10 ; SPMetricTotalPower = 73 ; }
else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) { SPMetricPortability = 35 ; SPMetricMarginalPower = 60 ; SPMetricTotalPower = 63 ; }
else if (!strncasecmp(HINFO_HWstring, "MacBook", 7)) { SPMetricPortability = 37 ; SPMetricMarginalPower = 71 ; SPMetricTotalPower = 72 ; }
else if (!strncasecmp(HINFO_HWstring, "PowerBook", 9)) { SPMetricPortability = 37 ; SPMetricMarginalPower = 71 ; SPMetricTotalPower = 72 ; }
LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d",
HINFO_HWstring_prefixlen, HINFO_HWstring, HINFO_HWstring, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower);
err = WatchForInternetSharingChanges(m);
if (err) { LogMsg("WatchForInternetSharingChanges failed %d", err); return(err); }
m->p->ConndBTMMDict = mDNSNULL;
#endif // APPLE_OSX_mDNSResponder
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
#ifdef __SSL_NEEDS_SERIALIZATION__
SSLqueue = dispatch_queue_create("com.apple.mDNSResponder.SSLQueue", NULL);
#else
SSLqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
#endif
if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL");
#endif
mDNSMacOSXUpdateEtcHosts(m);
return(mStatus_NoError);
}
mDNSexport mStatus mDNSPlatformInit(mDNS *const m)
{
#if MDNS_NO_DNSINFO
LogMsg("Note: Compiled without Apple-specific Split-DNS support");
#endif
m->DivertMulticastAdvertisements = !m->AdvertiseLocalAddresses;
#if APPLE_OSX_mDNSResponder
m->SPSBrowseCallback = UpdateSPSStatus;
#endif // APPLE_OSX_mDNSResponder
mStatus result = mDNSPlatformInit_setup(m);
if (result == mStatus_NoError)
{
mDNSCoreInitComplete(m, mStatus_NoError);
#if ! NO_D2D
CHECK_D2D_FUNCTION(D2DInitialize)
{
D2DStatus ds = D2DInitialize(m->p->CFRunLoop, xD2DServiceCallback, m) ;
if (ds != kD2DSuccess)
LogMsg("D2DInitialiize failed: %d", ds);
else
LogMsg("D2DInitialize succeeded");
}
#endif // ! NO_D2D
}
return(result);
}
mDNSexport void mDNSPlatformClose(mDNS *const m)
{
if (m->p->PowerConnection)
{
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
IONotificationPortSetDispatchQueue(m->p->PowerPortRef, NULL);
#else
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode);
#endif
IODeregisterForSystemPower(&m->p->PowerNotifier);
IOServiceClose ( m->p->PowerConnection);
IONotificationPortDestroy ( m->p->PowerPortRef);
m->p->PowerConnection = 0;
}
if (m->p->Store)
{
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
if (!SCDynamicStoreSetDispatchQueue(m->p->Store, NULL))
LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed");
#else
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode);
CFRunLoopSourceInvalidate(m->p->StoreRLS);
CFRelease(m->p->StoreRLS);
m->p->StoreRLS = NULL;
#endif
CFRelease(m->p->Store);
m->p->Store = NULL;
}
if (m->p->PMRLS)
{
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode);
CFRunLoopSourceInvalidate(m->p->PMRLS);
CFRelease(m->p->PMRLS);
m->p->PMRLS = NULL;
}
if (m->p->SysEventNotifier >= 0) { close(m->p->SysEventNotifier); m->p->SysEventNotifier = -1; }
#if ! NO_D2D
CHECK_D2D_FUNCTION(D2DTerminate)
{
D2DStatus ds = D2DTerminate();
if (ds != kD2DSuccess)
LogMsg("D2DTerminate failed: %d", ds);
else
LogMsg("D2DTerminate succeeded");
}
#endif // ! NO_D2D
mDNSs32 utc = mDNSPlatformUTC();
MarkAllInterfacesInactive(m, utc);
ClearInactiveInterfaces(m, utc);
CloseSocketSet(&m->p->permanentsockets);
#if APPLE_OSX_mDNSResponder
while (m->TunnelClients)
{
ClientTunnel *cur = m->TunnelClients;
LogInfo("mDNSPlatformClose: removing client tunnel %p %##s from list", cur, cur->dstname.c);
if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q);
AutoTunnelSetKeys(cur, mDNSfalse);
m->TunnelClients = cur->next;
freeL("ClientTunnel", cur);
}
if (AnonymousRacoonConfig)
{
AnonymousRacoonConfig = mDNSNULL;
LogInfo("mDNSPlatformClose: Deconfiguring autotunnel");
(void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL);
}
if (m->AutoTunnelHostAddrActive && m->AutoTunnelHostAddr.b[0])
{
m->AutoTunnelHostAddrActive = mDNSfalse;
LogInfo("mDNSPlatformClose: Removing AutoTunnel address %.16a", &m->AutoTunnelHostAddr);
(void)mDNSAutoTunnelInterfaceUpDown(kmDNSDown, m->AutoTunnelHostAddr.b);
}
if (m->p->ConndBTMMDict) CFRelease(m->p->ConndBTMMDict);
#endif // APPLE_OSX_mDNSResponder
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - General Platform Support Layer functions
#endif
mDNSexport mDNSu32 mDNSPlatformRandomNumber(void)
{
return(arc4random());
}
mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000;
mDNSexport mDNSu32 mDNSPlatformClockDivisor = 0;
mDNSexport mStatus mDNSPlatformTimeInit(void)
{
struct mach_timebase_info tbi;
kern_return_t result = mach_timebase_info(&tbi);
if (result == KERN_SUCCESS) mDNSPlatformClockDivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer;
return(result);
}
mDNSexport mDNSs32 mDNSPlatformRawTime(void)
{
if (mDNSPlatformClockDivisor == 0) { LogMsg("mDNSPlatformRawTime called before mDNSPlatformTimeInit"); return(0); }
static uint64_t last_mach_absolute_time = 0;
uint64_t this_mach_absolute_time = mach_absolute_time();
if ((int64_t)this_mach_absolute_time - (int64_t)last_mach_absolute_time < 0)
{
LogMsg("mDNSPlatformRawTime: last_mach_absolute_time %08X%08X", last_mach_absolute_time);
LogMsg("mDNSPlatformRawTime: this_mach_absolute_time %08X%08X", this_mach_absolute_time);
last_mach_absolute_time = this_mach_absolute_time;
NotifyOfElusiveBug("mach_absolute_time went backwards!",
"This error occurs from time to time, often on newly released hardware, "
"and usually the exact cause is different in each instance.\r\r"
"Please file a new Radar bug report with the title “mach_absolute_time went backwards” "
"and assign it to Radar Component “Kernel” Version “X”.");
}
last_mach_absolute_time = this_mach_absolute_time;
return((mDNSs32)(this_mach_absolute_time / mDNSPlatformClockDivisor));
}
mDNSexport mDNSs32 mDNSPlatformUTC(void)
{
return time(NULL);
}
mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; }
mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; }
mDNSexport void mDNSPlatformStrCopy( void *dst, const void *src) { strcpy((char *)dst, (char *)src); }
mDNSexport mDNSu32 mDNSPlatformStrLen ( const void *src) { return(strlen((char*)src)); }
mDNSexport void mDNSPlatformMemCopy( void *dst, const void *src, mDNSu32 len) { memcpy(dst, src, len); }
mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len) { return(memcmp(dst, src, len) == 0); }
mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) { memset(dst, 0, len); }
#if !(APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING)
mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); }
#endif
mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); }
mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
{
if (allowSleep && m->p->IOPMAssertion)
{
LogInfo("%s Destroying NoIdleSleep power assertion", __FUNCTION__);
IOPMAssertionRelease(m->p->IOPMAssertion);
m->p->IOPMAssertion = 0;
}
else if (!allowSleep && m->p->IOPMAssertion == 0)
{
#ifdef kIOPMAssertionTypeNoIdleSleep
CFStringRef assertionName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%d %s"), getprogname(), getpid(), reason ? reason : "");
IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, assertionName ? assertionName : CFSTR("mDNSResponder"), &m->p->IOPMAssertion);
if (assertionName) CFRelease(assertionName);
LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__);
#endif
}
}
mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration)
{
mDNSu32 ifindex;
ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue);
if (ifindex <= 0)
{
LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex);
return;
}
mDNSSendWakeupPacket(ifindex, EthAddr, IPAddr, iteration);
}
mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
{
mDNSBool p2pInterface = (strncmp(intf->ifname, "p2p", 3) == 0);
if (!p2pInterface || (rr->ARType == AuthRecordAnyIncludeP2P))
return mDNStrue;
else
return mDNSfalse;
}