blob: 954279b885fcd1ea191bebf4b0ee8ee2e4ebcdc0 [file] [log] [blame] [edit]
/*
* Copyright (c) 2019-2024 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
*
* https://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 "dnssd_analytics.h"
#include "mDNSMacOSX.h"
#include "uds_daemon.h"
#if MDNSRESPONDER_SUPPORTS(APPLE, ANALYTICS)
#include <xpc/xpc.h>
#include <CoreAnalytics/CoreAnalytics.h>
#include <CoreUtils/DebugServices.h>
#include "mdns_strict.h"
#endif // ANALYTICS
#if MDNSRESPONDER_SUPPORTS(APPLE, DNS_ANALYTICS)
// MARK: - Private Types
enum : uint8_t {
e_index_standard_noncell = 0,
e_index_standard_cell,
e_index_encrypted_noncell,
e_index_encrypted_cell,
e_index_count,
e_index_invalid = -1
};
typedef struct {
uint64_t queries;
uint64_t reply_pos;
uint64_t reply_neg;
} qtype_count_t;
typedef struct {
uint64_t latency_count; // For calulating average
uint64_t latency_total; // For calulating average
uint64_t query_bytes;
uint64_t reply_bytes;
qtype_count_t v4;
qtype_count_t v6;
qtype_count_t https;
} dns_analytic_t;
static dns_analytic_t s_dns_analytics[e_index_count] = {0};
// MARK: - Private Functions
static inline const char *
_index_to_transport_string(uint8_t index)
{
switch (index) {
case e_index_standard_noncell:
case e_index_standard_cell:
return "standard";
case e_index_encrypted_noncell:
case e_index_encrypted_cell:
return "encrypted";
default:
return NULL;
}
}
static inline const char *
_index_to_network_string(uint8_t index)
{
switch (index) {
case e_index_standard_noncell:
case e_index_encrypted_noncell:
return "non-cell";
case e_index_standard_cell:
case e_index_encrypted_cell:
return "cellular";
default:
return NULL;
}
}
static void
_post_dns_analytic(dns_analytic_t * const _Nonnull analytic, const char * _Nonnull network, const char * _Nonnull transport)
{
bool posted;
posted = analytics_send_event_lazy("com.apple.mDNSResponder.dnsqueryinfo", ^{
xpc_object_t dict;
dict = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(dict, "network", network);
xpc_dictionary_set_string(dict, "transport", transport);
xpc_dictionary_set_uint64(dict, "latency_ms", analytic->latency_total / analytic->latency_count);
xpc_dictionary_set_uint64(dict, "query_bytes", analytic->query_bytes);
xpc_dictionary_set_uint64(dict, "reply_bytes", analytic->reply_bytes);
xpc_dictionary_set_uint64(dict, "v4_queries", analytic->v4.queries);
xpc_dictionary_set_uint64(dict, "v4_reply_pos", analytic->v4.reply_pos);
xpc_dictionary_set_uint64(dict, "v4_reply_neg", analytic->v4.reply_neg);
xpc_dictionary_set_uint64(dict, "v6_queries", analytic->v6.queries);
xpc_dictionary_set_uint64(dict, "v6_reply_pos", analytic->v6.reply_pos);
xpc_dictionary_set_uint64(dict, "v6_reply_neg", analytic->v6.reply_neg);
xpc_dictionary_set_uint64(dict, "https_queries", analytic->https.queries);
xpc_dictionary_set_uint64(dict, "https_reply_pos", analytic->https.reply_pos);
xpc_dictionary_set_uint64(dict, "https_reply_neg", analytic->https.reply_neg);
return (dict);
});
if (!posted) {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_WARNING, "com.apple.mDNSResponder.dnsqueryinfo: Analytic not posted");
}
}
static void
_post_dns_query_info(void)
{
for (uint8_t i = 0 ; i < e_index_count ; i++ ) {
if (s_dns_analytics[i].latency_count > 0) {
_post_dns_analytic(&s_dns_analytics[i], _index_to_network_string(i), _index_to_transport_string(i));
}
}
memset(&s_dns_analytics, 0, sizeof(s_dns_analytics));
}
static qtype_count_t*
_qtype_count_for_analytic(dns_analytic_t * const _Nonnull analytic, uint16_t qtype)
{
qtype_count_t *qtype_ptr = NULL;
switch (qtype) {
case kDNSType_A:
qtype_ptr = &analytic->v4;
break;
case kDNSType_AAAA:
qtype_ptr = &analytic->v6;
break;
case kDNSType_HTTPS:
qtype_ptr = &analytic->https;
break;
default:
break;
}
return qtype_ptr;
}
static dns_analytic_t*
_analytic_for_event(bool is_cellular, dns_transport_t transport)
{
dns_analytic_t *analytic = NULL;
uint8_t index = e_index_invalid;
require_quiet(transport != dns_transport_Undefined, exit);
if (is_cellular) {
if (transport == dns_transport_Do53) {
index = e_index_standard_cell;
} else {
index = e_index_encrypted_cell;
}
} else {
if (transport == dns_transport_Do53) {
index = e_index_standard_noncell;
} else {
index = e_index_encrypted_noncell;
}
}
require_quiet(index != e_index_invalid && index < e_index_count, exit);
analytic = &s_dns_analytics[index];
exit:
return analytic;
}
// MARK: - Public Functions
void
dnssd_analytics_update_dns_query_info(bool is_cellular, dns_transport_t transport, uint16_t qtype, uint32_t num_queries,
uint32_t latency_ms, bool is_positive_answer)
{
require_quiet(num_queries > 0, exit);
dns_analytic_t *analytic = _analytic_for_event(is_cellular, transport);
require_quiet(analytic, exit);
qtype_count_t *qtype_count = _qtype_count_for_analytic(analytic, qtype);
require_quiet(qtype_count, exit);
analytic->latency_count++;
analytic->latency_total += latency_ms;
qtype_count->queries += num_queries;
if (is_positive_answer) {
qtype_count->reply_pos++;
} else {
qtype_count->reply_neg++;
}
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_DEBUG, "dnssd_analytics_update_dns_query_info cell %d qtype %d queries %u latency %d pos %d",
is_cellular, qtype, num_queries, latency_ms, is_positive_answer);
exit:
return;
}
void
dnssd_analytics_update_dns_query_size(bool is_cellular, dns_transport_t transport, uint32_t send_bytes)
{
dns_analytic_t *analytic = _analytic_for_event(is_cellular, transport);
require_quiet(analytic, exit);
analytic->query_bytes += send_bytes;
exit:
return;
}
void
dnssd_analytics_update_dns_reply_size(bool is_cellular, dns_transport_t transport, uint32_t recv_bytes)
{
dns_analytic_t *analytic = _analytic_for_event(is_cellular, transport);
require_quiet(analytic, exit);
analytic->reply_bytes += recv_bytes;
exit:
return;
}
#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
dns_transport_t
dnssd_analytics_dns_transport_for_resolver_type(mdns_resolver_type_t type)
{
dns_transport_t transport;
mdns_clang_ignore_warning_begin(-Wswitch-default);
switch (type) {
case mdns_resolver_type_https:
transport = dns_transport_DoH;
break;
case mdns_resolver_type_tls:
transport = dns_transport_DoT;
break;
case mdns_resolver_type_normal:
case mdns_resolver_type_tcp:
transport = dns_transport_Do53;
break;
case mdns_resolver_type_null:
transport = dns_transport_Undefined;
break;
}
mdns_clang_ignore_warning_end();
return transport;
}
#endif // QUERIER
#endif // DNS_ANALYTICS
#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS)
// Local aggregate counters to track request counts
static uint64_t sCacheUsage_UnicastHitCount = 0;
static uint64_t sCacheUsage_UnicastMissCount = 0;
static uint64_t sCacheUsage_MulticastHitCount = 0;
static uint64_t sCacheUsage_MulticastMissCount = 0;
static uint64_t sCacheRequest_UnicastHitCount = 0;
static uint64_t sCacheRequest_UnicastMissCount = 0;
static uint64_t sCacheRequest_MulticastHitCount = 0;
static uint64_t sCacheRequest_MulticastMissCount = 0;
// MARK: - Private CacheUsage Functions
static void
_post_cache_request_count(CacheRequestType inType, CacheState inState, uint64_t inRequestCount)
{
bool posted;
posted = analytics_send_event_lazy("com.apple.mDNSResponder.CacheUsage.request", ^{
xpc_object_t dict;
dict = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(dict, "requestType", inType == CacheRequestType_multicast ? "multicast" : "unicast");
xpc_dictionary_set_string(dict, "cacheState", inState == CacheState_hit ? "hit" : "miss");
xpc_dictionary_set_uint64(dict, "requestCount", inRequestCount);
return (dict);
});
if (!posted) {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_WARNING, "com.apple.mDNSResponder.CacheUsage.request: Analytic not posted");
}
}
static void
_post_cache_request_counts(void)
{
if (sCacheRequest_UnicastHitCount > 0) {
_post_cache_request_count(CacheRequestType_unicast, CacheState_hit, sCacheRequest_UnicastHitCount);
sCacheRequest_UnicastHitCount = 0;
}
if (sCacheRequest_UnicastMissCount > 0) {
_post_cache_request_count(CacheRequestType_unicast, CacheState_miss, sCacheRequest_UnicastMissCount);
sCacheRequest_UnicastMissCount = 0;
}
if (sCacheRequest_MulticastHitCount > 0) {
_post_cache_request_count(CacheRequestType_multicast, CacheState_hit, sCacheRequest_MulticastHitCount);
sCacheRequest_MulticastHitCount = 0;
}
if (sCacheRequest_MulticastMissCount > 0) {
_post_cache_request_count(CacheRequestType_multicast, CacheState_miss, sCacheRequest_MulticastMissCount);
sCacheRequest_MulticastMissCount = 0;
}
}
static void
_post_cache_usage_counts_for_type(CacheRequestType inType, uint64_t inHitCount, uint64_t inMissCount)
{
bool posted;
posted = analytics_send_event_lazy("com.apple.mDNSResponder.CacheUsage.entries", ^{
xpc_object_t dict;
dict = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(dict, "requestType", inType == CacheRequestType_multicast ? "multicast" : "unicast");
xpc_dictionary_set_uint64(dict, "hitCount", inHitCount);
xpc_dictionary_set_uint64(dict, "missCount", inMissCount);
return (dict);
});
if (!posted) {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_WARNING, "com.apple.mDNSResponder.CacheUsage.entries: Analytic not posted");
}
}
static void
_post_cache_usage_counts(void)
{
if (sCacheUsage_MulticastHitCount || sCacheUsage_MulticastMissCount) {
_post_cache_usage_counts_for_type(CacheRequestType_multicast, sCacheUsage_MulticastHitCount, sCacheUsage_MulticastMissCount);
sCacheUsage_MulticastHitCount = 0;
sCacheUsage_MulticastMissCount = 0;
}
if (sCacheUsage_UnicastHitCount || sCacheUsage_UnicastMissCount) {
_post_cache_usage_counts_for_type(CacheRequestType_unicast, sCacheUsage_UnicastHitCount, sCacheUsage_UnicastMissCount);
sCacheUsage_UnicastHitCount = 0;
sCacheUsage_UnicastMissCount = 0;
}
}
// MARK: - Exported CacheUsage Functions
void
dnssd_analytics_update_cache_request(CacheRequestType inType, CacheState inState)
{
if (inType == CacheRequestType_unicast) {
if (inState == CacheState_hit) {
sCacheRequest_UnicastHitCount++;
} else if (inState == CacheState_miss) {
sCacheRequest_UnicastMissCount++;
} else {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_WARNING, "dnssd_analytics_update_cache_request: unknown CacheState %d for unicast", inState);
}
} else if (inType == CacheRequestType_multicast) {
if (inState == CacheState_hit) {
sCacheRequest_MulticastHitCount++;
} else if (inState == CacheState_miss) {
sCacheRequest_MulticastMissCount++;
} else {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_WARNING, "dnssd_analytics_update_cache_request: unknown CacheState %d for multicast", inState);
}
} else {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_WARNING, "dnssd_analytics_update_cache_request: unknown CacheRequestType %d", inType);
}
}
void
dnssd_analytics_update_cache_usage_counts(uint32_t inHitMulticastCount, uint32_t inMissMulticastCount, uint32_t inHitUnicastCount, uint32_t inMissUnicastCount)
{
sCacheUsage_MulticastHitCount += inHitMulticastCount;
sCacheUsage_MulticastMissCount += inMissMulticastCount;
sCacheUsage_UnicastHitCount += inHitUnicastCount;
sCacheUsage_UnicastMissCount += inMissUnicastCount;
}
#endif // CACHE_ANALYTICS
#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_ASSIST_ANALYTICS)
static uint64_t sUnicastAssist_UnicastCount = 0;
static uint64_t sUnicastAssist_MulticastCount = 0;
static uint64_t sNonUnicastAssist_UnicastCount = 0;
static uint64_t sNonUnicastAssist_MulticastCount = 0;
static void
_post_unicast_assist(void)
{
bool posted;
posted = analytics_send_event_lazy("com.apple.mDNSResponder.unicastassist", ^{
xpc_object_t dict;
dict = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_uint64(dict, "unicast", sUnicastAssist_UnicastCount);
xpc_dictionary_set_uint64(dict, "multicast", sUnicastAssist_MulticastCount);
xpc_dictionary_set_uint64(dict, "non_unicast", sNonUnicastAssist_UnicastCount);
xpc_dictionary_set_uint64(dict, "non_multicast", sNonUnicastAssist_MulticastCount);
return (dict);
});
if (!posted) {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_WARNING, "com.apple.mDNSResponder.unicastassist: Analytic not posted");
}
sUnicastAssist_UnicastCount = 0;
sUnicastAssist_MulticastCount = 0;
sNonUnicastAssist_UnicastCount = 0;
sNonUnicastAssist_MulticastCount = 0;
}
void
dnssd_analytics_update_unicast_assist(bool assist, bool unicast)
{
if (assist) {
if (unicast) {
sUnicastAssist_UnicastCount++;
} else {
sUnicastAssist_MulticastCount++;
}
} else {
if (unicast) {
sNonUnicastAssist_UnicastCount++;
} else {
sNonUnicastAssist_MulticastCount++;
}
}
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_DEBUG,
"dnssd_analytics_update_unicast_assist Assist(unicast %s%lld, multicast %s%lld) "
"NonAssist(unicast %s%lld, multicast %s%lld)",
assist ? unicast ? "*" : "" : "", sUnicastAssist_UnicastCount,
assist ? unicast ? "" : "*" : "", sUnicastAssist_MulticastCount,
!assist ? unicast ? "*" : "" : "", sNonUnicastAssist_UnicastCount,
!assist ? unicast ? "" : "*" : "", sNonUnicastAssist_MulticastCount);
}
#endif // UNICAST_ASSIST_ANALYTICS
#if MDNSRESPONDER_SUPPORTS(APPLE, WAB_ANALYTICS)
#define UNSET_STR "unset"
// MARK: - Exported WABUsage Functions
void
dnssd_analytics_post_WAB_usage_event_count(WABUsageKind inKind, WABUsageType inType, WABUsageEvent inEvent, uint64_t inEventCount)
{
bool posted;
char * kind = UNSET_STR;
char * type = UNSET_STR;
char * event = UNSET_STR;
if (analytics_send_event_lazy) {
switch (inKind) {
case WABUsageKind_results: {
kind = "results";
break;
}
case WABUsageKind_session: {
kind = "session";
break;
}
case WABUsageKind_operation: {
kind = "operation";
break;
}
}
switch (inType) {
case WABUsageType_enumeration: {
type = "enumeration";
break;
}
case WABUsageType_query: {
type = "query";
break;
}
case WABUsageType_push: {
type = "push";
break;
}
case WABUsageType_llq: {
type = "llq";
break;
}
}
switch (inEvent) {
case WABUsageEvent_positive: {
event = "positive";
break;
}
case WABUsageEvent_negative: {
event = "negative";
break;
}
case WABUsageEvent_null: {
event = "null";
break;
}
case WABUsageEvent_error: {
event = "error";
break;
}
case WABUsageEvent_connected: {
event = "connected";
break;
}
case WABUsageEvent_session: {
event = "session";
break;
}
case WABUsageEvent_reset: {
event = "reset";
break;
}
case WABUsageEvent_idledOut: {
event = "idledOut";
break;
}
case WABUsageEvent_goAway: {
event = "goAway";
break;
}
case WABUsageEvent_resumedGood: {
event = "resumedGood";
break;
}
case WABUsageEvent_resumedBad: {
event = "resumedBad";
break;
}
case WABUsageEvent_succeeded: {
event = "succeeded";
break;
}
case WABUsageEvent_rejected: {
event = "rejected";
break;
}
case WABUsageEvent_dsoni: {
event = "dsoni";
break;
}
case WABUsageEvent_answered: {
event = "answered";
break;
}
}
posted = analytics_send_event_lazy("com.apple.mDNSResponder.CacheUsage.entries", ^{
xpc_object_t dict;
dict = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(dict, "kind", kind);
xpc_dictionary_set_string(dict, "type", type);
xpc_dictionary_set_string(dict, "event", event);
xpc_dictionary_set_uint64(dict, "eventCount", inEventCount);
return (dict);
});
if (!posted) {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_WARNING, "com.apple.mDNSResponder.CacheUsage.entries: Analytic not posted");
}
}
}
#endif // WAB_ANALYTICS
#if MDNSRESPONDER_SUPPORTS(APPLE, ANALYTICS)
// MARK: - Exported Analytics Functions
void
dnssd_analytics_init(void)
{
static dispatch_once_t sInitAnalyticsOnce = 0;
static dispatch_queue_t sAnalyticsQueue = NULL;
dispatch_once(&sInitAnalyticsOnce, ^{
sAnalyticsQueue = dispatch_queue_create("com.apple.mDNSResponder.analytics-reporting-queue", DISPATCH_QUEUE_SERIAL);
xpc_object_t criteria = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_REPEATING, true);
xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_ALLOW_BATTERY, true);
xpc_dictionary_set_int64(criteria, XPC_ACTIVITY_DELAY, XPC_ACTIVITY_INTERVAL_1_DAY);
xpc_dictionary_set_int64(criteria, XPC_ACTIVITY_GRACE_PERIOD, XPC_ACTIVITY_INTERVAL_5_MIN);
xpc_dictionary_set_string(criteria, XPC_ACTIVITY_PRIORITY, XPC_ACTIVITY_PRIORITY_MAINTENANCE);
xpc_activity_register("com.apple.mDNSResponder.analytics.daily", criteria, ^(xpc_activity_t activity) {
if (xpc_activity_should_defer(activity)) {
if (xpc_activity_set_state(activity, XPC_ACTIVITY_STATE_DEFER)) {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_DEFAULT, "com.apple.mDNSResponder.analytics.daily: Asked to defer");
} else {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_ERROR, "com.apple.mDNSResponder.analytics.daily: Asked to defer but failed to set state");
}
} else {
dispatch_async(sAnalyticsQueue, ^{
#if MDNSRESPONDER_SUPPORTS(APPLE, ANALYTICS)
KQueueLock();
mDNS_Lock(&mDNSStorage);
#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS)
_post_cache_request_counts();
_post_cache_usage_counts();
#endif // CACHE_ANALYTICS
#if MDNSRESPONDER_SUPPORTS(APPLE, DNS_ANALYTICS)
_post_dns_query_info();
#endif // DNS_ANALYTICS
#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_ASSIST_ANALYTICS)
_post_unicast_assist();
#endif // UNICAST_ASSIST_ANALYTICS
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_DEFAULT, "com.apple.mDNSResponder.analytics.daily Complete");
mDNS_Unlock(&mDNSStorage);
KQueueUnlock("Analytics Update");
#endif // ANALYTICS
});
if (!xpc_activity_set_state(activity, XPC_ACTIVITY_STATE_DONE)) {
LogRedact(MDNS_LOG_CATEGORY_ANALYTICS, MDNS_LOG_ERROR, "com.apple.mDNSResponder.analytics.daily: Analytics XPC_ACTIVITY_STATE_DONE failed");
}
}
});
MDNS_DISPOSE_XPC(criteria);
});
}
void
dnssd_analytics_log(int fd)
{
#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS)
LogToFD(fd, "---- DNS Cache Analytics -----");
LogToFD(fd, "---- Unicast Requests");
LogToFD(fd, "Cache Hit: %llu", sCacheRequest_UnicastHitCount);
LogToFD(fd, "Cache Miss: %llu", sCacheRequest_UnicastMissCount);
LogToFD(fd, "---- Unicast Usage");
LogToFD(fd, "Cache Hit: %llu", sCacheUsage_UnicastHitCount);
LogToFD(fd, "Cache Miss: %llu", sCacheUsage_UnicastMissCount);
LogToFD(fd, "---- Multicast Requests");
LogToFD(fd, "Cache Hit: %llu", sCacheRequest_MulticastHitCount);
LogToFD(fd, "Cache Miss: %llu", sCacheRequest_MulticastMissCount);
LogToFD(fd, "---- Multicast Usage");
LogToFD(fd, "Cache Hit: %llu", sCacheUsage_MulticastHitCount);
LogToFD(fd, "Cache Miss: %llu", sCacheUsage_MulticastMissCount);
#endif // CACHE_ANALYTICS
#if MDNSRESPONDER_SUPPORTS(APPLE, DNS_ANALYTICS)
LogToFD(fd, "---- DNS Query Analytics -----");
for (uint8_t i = 0 ; i < e_index_count ; i++ ) {
if (s_dns_analytics[i].latency_count > 0) {
LogToFD(fd, "---- Network: %s\n Transport: %s", _index_to_network_string(i), _index_to_transport_string(i));
LogToFD(fd, "Latency: %llums", s_dns_analytics[i].latency_total / s_dns_analytics[i].latency_count);
LogToFD(fd, "Query Bytes: %llu", s_dns_analytics[i].query_bytes);
LogToFD(fd, "Reply Bytes: %llu", s_dns_analytics[i].reply_bytes);
LogToFD(fd, "---- V4");
LogToFD(fd, "Queries: %llu", s_dns_analytics[i].v4.queries);
LogToFD(fd, "Reply Pos: %llu", s_dns_analytics[i].v4.reply_pos);
LogToFD(fd, "Reply Neg: %llu", s_dns_analytics[i].v4.reply_neg);
LogToFD(fd, "---- V6");
LogToFD(fd, "Queries: %llu", s_dns_analytics[i].v6.queries);
LogToFD(fd, "Reply Pos: %llu", s_dns_analytics[i].v6.reply_pos);
LogToFD(fd, "Reply Neg: %llu", s_dns_analytics[i].v6.reply_neg);
LogToFD(fd, "---- HTTPS");
LogToFD(fd, "Queries: %llu", s_dns_analytics[i].https.queries);
LogToFD(fd, "Reply Pos: %llu", s_dns_analytics[i].https.reply_pos);
LogToFD(fd, "Reply Neg: %llu", s_dns_analytics[i].https.reply_neg);
}
}
#endif // DNS_ANALYTICS
#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_ASSIST_ANALYTICS)
LogToFD(fd, "---- Unicast Assist");
LogToFD(fd, "Assist Unicast: %llu", sUnicastAssist_UnicastCount);
LogToFD(fd, "Assist Multicast: %llu", sUnicastAssist_MulticastCount);
LogToFD(fd, "Non-assist Unicast: %llu", sNonUnicastAssist_UnicastCount);
LogToFD(fd, "Non-assist Multicast: %llu", sNonUnicastAssist_MulticastCount);
#endif // UNICAST_ASSIST_ANALYTICS
}
#endif // ANALYTICS