/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2015 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mDNSEmbeddedAPI.h"
#include <arpa/inet.h>
#include <dlfcn.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/socket.h>
#include <AvailabilityMacros.h>
#include <TargetConditionals.h>
#define TARGET_OS_MACOSX (TARGET_OS_MAC && !TARGET_OS_IPHONE)
#if (!TARGET_OS_MACOSX || (MAC_OS_X_VERSION_MAX_ALLOWED >= 101200))
#include <SymptomReporter/SymptomReporter.h>
#else
#include <Symptoms/SymptomReporter.h>
#endif
#define SYMPTOM_REPORTER_mDNSResponder_NUMERIC_ID 101
#define SYMPTOM_REPORTER_mDNSResponder_TEXT_ID "com.apple.mDNSResponder"
#define SYMPTOM_DNS_NO_REPLIES 0x00065001
#define SYMPTOM_DNS_RESUMED_RESPONDING 0x00065002
static symptom_framework_t symptomReporter = mDNSNULL;
static symptom_framework_t (*symptom_framework_init_f)(symptom_ident_t id, const char *originator_string) = mDNSNULL;
static symptom_t (*symptom_new_f)(symptom_framework_t framework, symptom_ident_t id) = mDNSNULL;
static int (*symptom_set_additional_qualifier_f)(symptom_t symptom, uint32_t qualifier_type, size_t qualifier_len, void *qualifier_data) = mDNSNULL;
static int (*symptom_send_f)(symptom_t symptom) = mDNSNULL;
mDNSlocal mStatus SymptomReporterInitCheck(void)
{
mStatus err;
static mDNSBool triedInit = mDNSfalse;
static void *symptomReporterLib = mDNSNULL;
#if (!TARGET_OS_MACOSX || (MAC_OS_X_VERSION_MAX_ALLOWED >= 101200))
static const char path[] = "/System/Library/PrivateFrameworks/SymptomReporter.framework/SymptomReporter";
#else
static const char path[] = "/System/Library/PrivateFrameworks/Symptoms.framework/Frameworks/SymptomReporter.framework/SymptomReporter";
#endif
if (!triedInit)
{
triedInit = mDNStrue;
symptomReporterLib = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
if (!symptomReporterLib)
goto exit;
symptom_framework_init_f = dlsym(symptomReporterLib, "symptom_framework_init");
if (!symptom_framework_init_f)
goto exit;
symptom_new_f = dlsym(symptomReporterLib, "symptom_new");
if (!symptom_new_f)
goto exit;
symptom_set_additional_qualifier_f = dlsym(symptomReporterLib, "symptom_set_additional_qualifier");
if (!symptom_set_additional_qualifier_f)
goto exit;
symptom_send_f = dlsym(symptomReporterLib, "symptom_send");
if (!symptom_send_f)
goto exit;
symptomReporter = symptom_framework_init_f(SYMPTOM_REPORTER_mDNSResponder_NUMERIC_ID, SYMPTOM_REPORTER_mDNSResponder_TEXT_ID);
}
exit:
err = symptomReporter ? mStatus_NoError : mStatus_NotInitializedErr;
return err;
}
mDNSlocal mStatus SymptomReporterReportDNSReachability(const mDNSAddr *addr, mDNSBool isReachable)
{
mStatus err;
symptom_t symptom;
struct sockaddr_storage sockAddr;
size_t sockAddrSize;
LogInfo("SymptomReporterReportDNSReachability: DNS server %#a is %sreachable", addr, isReachable ? "" : "un");
if (addr->type == mDNSAddrType_IPv4)
{
struct sockaddr_in *sin = (struct sockaddr_in *)&sockAddr;
sockAddrSize = sizeof(*sin);
mDNSPlatformMemZero(sin, sockAddrSize);
sin->sin_len = sockAddrSize;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = addr->ip.v4.NotAnInteger;
}
else if (addr->type == mDNSAddrType_IPv6)
{
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&sockAddr;
sockAddrSize = sizeof(*sin6);
mDNSPlatformMemZero(sin6, sockAddrSize);
sin6->sin6_len = sockAddrSize;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = *(struct in6_addr *)&addr->ip.v6;
}
else
{
LogMsg("SymptomReporterReportDNSReachability: addr is not an IPv4 or IPv6 address!");
err = mStatus_BadParamErr;
goto exit;
}
symptom = symptom_new_f(symptomReporter, isReachable ? SYMPTOM_DNS_RESUMED_RESPONDING : SYMPTOM_DNS_NO_REPLIES);
symptom_set_additional_qualifier_f(symptom, 1, sockAddrSize, (void *)&sockAddr);
symptom_send_f(symptom);
err = mStatus_NoError;
exit:
return err;
}
mDNSexport mStatus SymptomReporterDNSServerReachable(mDNS *const m, const mDNSAddr *addr)
{
mStatus err;
DNSServer *s;
mDNSBool found = mDNSfalse;
err = SymptomReporterInitCheck();
if (err != mStatus_NoError)
goto exit;
for (s = m->DNSServers; s; s = s->next)
{
if (s->flags & DNSServer_FlagDelete)
continue;
if ((s->flags & DNSServer_FlagUnreachable) && mDNSSameAddress(addr, &s->addr))
{
s->flags &= ~DNSServer_FlagUnreachable;
NumUnreachableDNSServers--;
found = mDNStrue;
}
}
if (!found)
{
err = mStatus_NoSuchNameErr;
goto exit;
}
err = SymptomReporterReportDNSReachability(addr, mDNStrue);
exit:
return err;
}
mDNSexport mStatus SymptomReporterDNSServerUnreachable(DNSServer *s)
{
mStatus err;
err = SymptomReporterInitCheck();
if (err != mStatus_NoError)
goto exit;
if ((s->flags & DNSServer_FlagDelete) || (s->flags & DNSServer_FlagUnreachable))
goto exit;
s->flags |= DNSServer_FlagUnreachable;
NumUnreachableDNSServers++;
err = SymptomReporterReportDNSReachability(&s->addr, mDNSfalse);
exit:
return err;
}