From f01edf10244ccd53e098abdc1773c1aa0e4c5f8d Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 19 Sep 2018 08:53:26 +0200 Subject: mDNSResponder: Update to v765.1.2 The sources can be obtained via: https://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-765.1.2.tar.gz Move mDNS_StartResolveService() and mDNS_StopResolveService() to an RTEMS-specific file (rtemsbsd/mdns/mDNSResolveService.c) using the v576.30.4 implementation. Apple removed these functions without explanation. Update #3522. --- mDNSResponder/mDNSMacOSX/Metrics.m | 2051 +++++++++++++++++++++++++++++++----- 1 file changed, 1765 insertions(+), 286 deletions(-) (limited to 'mDNSResponder/mDNSMacOSX/Metrics.m') diff --git a/mDNSResponder/mDNSMacOSX/Metrics.m b/mDNSResponder/mDNSMacOSX/Metrics.m index 56467dfc..8dae3f5f 100644 --- a/mDNSResponder/mDNSMacOSX/Metrics.m +++ b/mDNSResponder/mDNSMacOSX/Metrics.m @@ -17,12 +17,18 @@ #import "Metrics.h" -#if TARGET_OS_EMBEDDED +#if (TARGET_OS_EMBEDDED) #import #import #import +#import +#import +#import +#import +#import #import #import +#import #import "DNSCommon.h" #import "mDNSMacOSX.h" @@ -34,44 +40,116 @@ SOFT_LINK_FRAMEWORK(PrivateFrameworks, WirelessDiagnostics) -SOFT_LINK_CLASS(WirelessDiagnostics, AWDServerConnection) -#define AWDServerConnectionSoft getAWDServerConnectionClass() - -SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSStatistics) -#define AWDMDNSResponderDNSStatisticsSoft getAWDMDNSResponderDNSStatisticsClass() - SOFT_LINK_CLASS(WirelessDiagnostics, AWDDNSDomainStats) -#define AWDDNSDomainStatsSoft getAWDDNSDomainStatsClass() +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSStatistics) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStats) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsDNSServer) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsDomain) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsHostname) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsResult) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDServerConnection) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMetricManager) + +#define AWDDNSDomainStatsSoft getAWDDNSDomainStatsClass() +#define AWDMDNSResponderDNSStatisticsSoft getAWDMDNSResponderDNSStatisticsClass() +#define AWDMDNSResponderResolveStatsSoft getAWDMDNSResponderResolveStatsClass() +#define AWDMDNSResponderResolveStatsResultSoft getAWDMDNSResponderResolveStatsResultClass() +#define AWDMDNSResponderResolveStatsDNSServerSoft getAWDMDNSResponderResolveStatsDNSServerClass() +#define AWDMDNSResponderResolveStatsDomainSoft getAWDMDNSResponderResolveStatsDomainClass() +#define AWDMDNSResponderResolveStatsHostnameSoft getAWDMDNSResponderResolveStatsHostnameClass() +#define AWDServerConnectionSoft getAWDServerConnectionClass() +#define AWDMetricManagerSoft getAWDMetricManagerClass() //=========================================================================================================================== // Macros //=========================================================================================================================== -#define countof(X) (sizeof(X) / sizeof(X[0])) +#define countof(X) (sizeof(X) / sizeof(X[0])) +#define countof_field(TYPE, FIELD) countof(((TYPE *)0)->FIELD) +#define increment_saturate(VAR, MAX) do {if ((VAR) < (MAX)) {++(VAR);}} while (0) +#define ForgetMem(X) do {if(*(X)) {free(*(X)); *(X) = NULL;}} while(0) //=========================================================================================================================== // Constants //=========================================================================================================================== -#define kUDNSStatsMaxQuerySendCount 10 +#define kQueryStatsMaxQuerySendCount 10 +#define kQueryStatsSendCountBinCount (kQueryStatsMaxQuerySendCount + 1) +#define kQueryStatsLatencyBinCount 55 +#define kResolveStatsMaxObjCount 2000 + +//=========================================================================================================================== +// Data structures +//=========================================================================================================================== + +typedef struct +{ + const char * cstr; // Name of domain as a c-string. + const domainname * name; // Name of domain as length-prefixed labels. + int labelCount; // Number of labels in domain name. Used for domain name comparisons. + +} Domain; -// Important: Do not update this list without getting privacy approval. See . +// Important: Do not add to this list without getting privacy approval beforehand. See . +// If you get approval and do add a domain to this list, make sure it passes ValidateDNSStatsDomains() below. -static const char * const kUDNSStatsDomains[] = +static const Domain kQueryStatsDomains[] = { - ".", - "apple.com.", - "icloud.com.", - "me.com.", - "google.com.", - "facebook.com.", - "youtube.com.", - "baidu.com.", - "amazon.com.", - "yahoo.com.", - "wikipedia.org." + { ".", (domainname *)"", 0 }, + { "apple.com.", (domainname *)"\x5" "apple" "\x3" "com", 2 }, + { "icloud.com.", (domainname *)"\x6" "icloud" "\x3" "com", 2 }, + { "mzstatic.com.", (domainname *)"\x8" "mzstatic" "\x3" "com", 2 }, + { "me.com.", (domainname *)"\x2" "me" "\x3" "com", 2 }, + { "google.com.", (domainname *)"\x6" "google" "\x3" "com", 2 }, + { "youtube.com.", (domainname *)"\x7" "youtube" "\x3" "com", 2 }, + { "facebook.com.", (domainname *)"\x8" "facebook" "\x3" "com", 2 }, + { "baidu.com.", (domainname *)"\x5" "baidu" "\x3" "com", 2 }, + { "yahoo.com.", (domainname *)"\x5" "yahoo" "\x3" "com", 2 }, + { "qq.com.", (domainname *)"\x2" "qq" "\x3" "com", 2 }, }; +check_compile_time(countof(kQueryStatsDomains) == 11); + +// DNSHist contains the per domain per network type histogram data that goes in a DNSDomainStats protobuf message. See +// MDNSResponder.proto update. +// +// answeredQuerySendCountBins +// +// An array of 11 histogram bins. The value at index i, for 0 <= i <= 9, is the number of times that an answered DNS query +// was sent i times. The value at index 10 is the number of times that an answered query was sent 10+ times. +// +// unansweredQuerySendCountBins +// +// An array of 11 histogram bins. The value at index i, for 0 <= i <= 9, is the number of times that an unanswered DNS query +// was sent i times. The value at index 10 is the number of times that an unanswered query was sent 10+ times. +// +// responseLatencyBins +// +// An array of 55 histogram bins. Each array value is the number of DNS queries that were answered in a paricular time +// interval. The 55 consecutive non-overlapping time intervals have the following non-inclusive upper bounds (all values are +// in milliseconds): 1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, +// 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1500, 2000, 2500, 3000, 3500, 4000, +// 4500, 5000, 6000, 7000, 8000, 9000, 10000, ∞. + +typedef struct +{ + uint16_t unansweredQuerySendCountBins[kQueryStatsSendCountBinCount]; + uint16_t unansweredQueryDurationBins[kQueryStatsLatencyBinCount]; + uint16_t answeredQuerySendCountBins[kQueryStatsSendCountBinCount]; + uint16_t responseLatencyBins[kQueryStatsLatencyBinCount]; + uint16_t negAnsweredQuerySendCountBins[kQueryStatsSendCountBinCount]; + uint16_t negResponseLatencyBins[kQueryStatsLatencyBinCount]; + +} DNSHist; + +check_compile_time(sizeof(DNSHist) <= 512); +check_compile_time(countof_field(DNSHist, unansweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1)); +check_compile_time(countof_field(DNSHist, answeredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1)); +check_compile_time(countof_field(DNSHist, negAnsweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1)); + +// Important: Do not modify kResponseLatencyMsLimits because the code used to generate AWD reports expects the response +// latency histogram bins to observe these time interval upper bounds. + static const mDNSu32 kResponseLatencyMsLimits[] = { 1, 2, 3, 4, 5, @@ -83,51 +161,200 @@ static const mDNSu32 kResponseLatencyMsLimits[] = 10000 }; -//=========================================================================================================================== -// Data structures -//=========================================================================================================================== +check_compile_time(countof(kResponseLatencyMsLimits) == 54); +check_compile_time(countof_field(DNSHist, unansweredQueryDurationBins) == (countof(kResponseLatencyMsLimits) + 1)); +check_compile_time(countof_field(DNSHist, responseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1)); +check_compile_time(countof_field(DNSHist, negResponseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1)); typedef struct { - uint32_t answeredQuerySendCountBins[kUDNSStatsMaxQuerySendCount + 1]; - uint32_t unansweredQuerySendCountBins[kUDNSStatsMaxQuerySendCount + 1]; - uint32_t responseLatencyBins[countof(kResponseLatencyMsLimits) + 1]; + DNSHist * histAny; // Histogram data for queries of any resource record type. + DNSHist * histA; // Histogram data for queries for A resource records. + DNSHist * histAAAA; // Histogram data for queries for AAAA resource records. -} DNSStats; +} DNSHistSet; -typedef struct DNSDomainStats * DNSDomainStatsRef; +typedef struct DNSDomainStats DNSDomainStats; struct DNSDomainStats { - DNSDomainStatsRef next; - domainname domain; - int domainLabelCount; - char * domainStr; - DNSStats stats; - DNSStats statsCellular; + DNSDomainStats * next; // Pointer to next domain stats in list. + const Domain * domain; // Domain for which these stats are collected. + DNSHistSet * nonCellular; // Query stats for queries sent over non-cellular interfaces. + DNSHistSet * cellular; // Query stats for queries sent over cellular interfaces. +}; + +check_compile_time(sizeof(struct DNSDomainStats) <= 32); + +static const Domain kResolveStatsDomains[] = +{ + { "apple.com.", (domainname *)"\x5" "apple" "\x3" "com", 2 }, + { "icloud.com.", (domainname *)"\x6" "icloud" "\x3" "com", 2 }, + { "mzstatic.com.", (domainname *)"\x8" "mzstatic" "\x3" "com", 2 }, + { "me.com.", (domainname *)"\x2" "me" "\x3" "com", 2 }, +}; + +check_compile_time(countof(kResolveStatsDomains) == 4); + +typedef struct ResolveStatsDomain ResolveStatsDomain; +typedef struct ResolveStatsHostname ResolveStatsHostname; +typedef struct ResolveStatsDNSServer ResolveStatsDNSServer; +typedef struct ResolveStatsIPv4AddrSet ResolveStatsIPv4AddrSet; +typedef struct ResolveStatsIPv6Addr ResolveStatsIPv6Addr; +typedef struct ResolveStatsNegAAAASet ResolveStatsNegAAAASet; + +struct ResolveStatsDomain +{ + ResolveStatsDomain * next; // Next domain object in list. + ResolveStatsHostname * hostnameList; // List of hostname objects in this domain. + const Domain * domainInfo; // Pointer to domain info. +}; + +struct ResolveStatsHostname +{ + ResolveStatsHostname * next; // Next hostname object in list. + ResolveStatsIPv4AddrSet * addrV4List; // List of IPv4 addresses to which this hostname resolved. + ResolveStatsIPv6Addr * addrV6List; // List of IPv6 addresses to which this hostname resolved. + ResolveStatsNegAAAASet * negV6List; // List of negative AAAA response objects. + uint8_t name[1]; // Variable length storage for hostname as length-prefixed labels. +}; + +check_compile_time(sizeof(ResolveStatsHostname) <= 64); + +struct ResolveStatsDNSServer +{ + ResolveStatsDNSServer * next; // Next DNS server object in list. + uint8_t id; // 8-bit ID assigned to this DNS server used by IP address objects. + mDNSBool isForCell; // True if this DNS server belongs to a cellular interface. + mDNSBool isAddrV6; // True if this DNS server has an IPv6 address instead of IPv4. + uint8_t addrBytes[1]; // Variable length storage for DNS server's IP address. +}; + +check_compile_time(sizeof(ResolveStatsDNSServer) <= 32); + +typedef struct +{ + uint16_t count; // Number of times this IPv4 address was provided as a resolution result. + uint8_t serverID; // 8-bit ID of the DNS server from which this IPv4 address came. + uint8_t isNegative; + uint8_t addrBytes[4]; // IPv4 address bytes. + +} IPv4AddrCounter; + +check_compile_time(sizeof(IPv4AddrCounter) <= 8); + +struct ResolveStatsIPv4AddrSet +{ + ResolveStatsIPv4AddrSet * next; // Next set of IPv4 address counters in list. + IPv4AddrCounter counters[3]; // Array of IPv4 address counters. +}; + +check_compile_time(sizeof(ResolveStatsIPv4AddrSet) <= 32); + +struct ResolveStatsIPv6Addr +{ + ResolveStatsIPv6Addr * next; // Next IPv6 address object in list. + uint16_t count; // Number of times this IPv6 address was provided as a resolution result. + uint8_t serverID; // 8-bit ID of the DNS server from which this IPv6 address came. + uint8_t addrBytes[16]; // IPv6 address bytes. +}; + +check_compile_time(sizeof(ResolveStatsIPv6Addr) <= 32); + +typedef struct +{ + uint16_t count; // Number of times that a negative response was returned by a DNS server. + uint8_t serverID; // 8-bit ID of the DNS server that sent the negative responses. + +} NegAAAACounter; + +check_compile_time(sizeof(NegAAAACounter) <= 4); + +struct ResolveStatsNegAAAASet +{ + ResolveStatsNegAAAASet * next; // Next set of negative AAAA response counters in list. + NegAAAACounter counters[6]; // Array of negative AAAA response counters. }; +check_compile_time(sizeof(ResolveStatsNegAAAASet) <= 32); + +typedef enum +{ + kResponseType_IPv4Addr = 1, + kResponseType_IPv6Addr = 2, + kResponseType_NegA = 3, + kResponseType_NegAAAA = 4 + +} ResponseType; + +typedef struct +{ + ResponseType type; + const uint8_t * data; + +} Response; + //=========================================================================================================================== // Globals //=========================================================================================================================== extern mDNS mDNSStorage; -static DNSDomainStatsRef gDomainStatsList = NULL; -static AWDServerConnection * gAWDServerConnection = nil; +static DNSDomainStats * gDomainStatsList = NULL; +static ResolveStatsDomain * gResolveStatsList = NULL; +static ResolveStatsDNSServer * gResolveStatsServerList = NULL; +static unsigned int gResolveStatsNextServerID = 0; +static int gResolveStatsObjCount = 0; +static AWDServerConnection * gAWDServerConnection = nil; //=========================================================================================================================== // Local Prototypes //=========================================================================================================================== -mDNSlocal mStatus DNSDomainStatsCreate(const char *inDomain, DNSDomainStatsRef *outStats); -mDNSlocal void DNSDomainStatsFree(DNSDomainStatsRef inStats); -mDNSlocal void DNSDomainStatsFreeList(DNSDomainStatsRef inList); - -mDNSlocal mStatus CreateDomainStatsList(DNSDomainStatsRef *outList); -mDNSlocal void UpdateDNSStats(DNSStats *inStats, mDNSBool inAnswered, mDNSu32 inQuerySendCount, mDNSu32 inLatencyMs); -mDNSlocal mStatus SubmitAWDMetric(void); -mDNSlocal mStatus CreateAWDDNSDomainStats(DNSStats *inStats, const char *inDomain, mDNSBool inIsForCellular, AWDDNSDomainStats **outAWDStats); -mDNSlocal void LogDNSStats(const DNSStats *inStats); +mDNSlocal mStatus DNSDomainStatsCreate(const Domain *inDomain, DNSDomainStats **outStats); +mDNSlocal void DNSDomainStatsFree(DNSDomainStats *inStats); +mDNSlocal void DNSDomainStatsFreeList(DNSDomainStats *inList); +mDNSlocal mStatus DNSDomainStatsUpdate(DNSDomainStats *inStats, uint16_t inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, mDNSu32 inLatencyMs, mDNSBool inForCell); + +mDNSlocal mStatus ResolveStatsDomainCreate(const Domain *inDomain, ResolveStatsDomain **outDomain); +mDNSlocal void ResolveStatsDomainFree(ResolveStatsDomain *inDomain); +mDNSlocal mStatus ResolveStatsDomainUpdate(ResolveStatsDomain *inDomain, const domainname *inHostname, const Response *inResp, const mDNSAddr *inDNSAddr, mDNSBool inForCell); +mDNSlocal mStatus ResolveStatsDomainCreateAWDVersion(const ResolveStatsDomain *inDomain, AWDMDNSResponderResolveStatsDomain **outDomain); + +mDNSlocal mStatus ResolveStatsHostnameCreate(const domainname *inName, ResolveStatsHostname **outHostname); +mDNSlocal void ResolveStatsHostnameFree(ResolveStatsHostname *inHostname); +mDNSlocal mStatus ResolveStatsHostnameUpdate(ResolveStatsHostname *inHostname, const Response *inResp, uint8_t inServerID); +mDNSlocal mStatus ResolveStatsHostnameCreateAWDVersion(const ResolveStatsHostname *inHostname, AWDMDNSResponderResolveStatsHostname **outHostname); + +mDNSlocal mStatus ResolveStatsDNSServerCreate(const mDNSAddr *inAddr, mDNSBool inForCell, ResolveStatsDNSServer **outServer); +mDNSlocal void ResolveStatsDNSServerFree(ResolveStatsDNSServer *inServer); +mDNSlocal mStatus ResolveStatsDNSServerCreateAWDVersion(const ResolveStatsDNSServer *inServer, AWDMDNSResponderResolveStatsDNSServer **outServer); + +mDNSlocal mStatus ResolveStatsIPv4AddrSetCreate(ResolveStatsIPv4AddrSet **outSet); +mDNSlocal void ResolveStatsIPv4AddrSetFree(ResolveStatsIPv4AddrSet *inSet); + +mDNSlocal mStatus ResolveStatsIPv6AddressCreate(uint8_t inServerID, const uint8_t inAddrBytes[16], ResolveStatsIPv6Addr **outAddr); +mDNSlocal void ResolveStatsIPv6AddressFree(ResolveStatsIPv6Addr *inAddr); + +mDNSlocal mStatus ResolveStatsNegAAAASetCreate(ResolveStatsNegAAAASet **outSet); +mDNSlocal void ResolveStatsNegAAAASetFree(ResolveStatsNegAAAASet *inSet); +mDNSlocal mStatus ResolveStatsGetServerID(const mDNSAddr *inServerAddr, mDNSBool inForCell, uint8_t *outServerID); + +mDNSlocal mStatus CreateDomainStatsList(DNSDomainStats **outList); +mDNSlocal mStatus CreateResolveStatsList(ResolveStatsDomain **outList); +mDNSlocal void FreeResolveStatsList(ResolveStatsDomain *inList); +mDNSlocal void FreeResolveStatsServerList(ResolveStatsDNSServer *inList); +mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID); +mDNSlocal mStatus SubmitAWDMetricQueryStats(void); +mDNSlocal mStatus SubmitAWDMetricResolveStats(void); +mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats); +mDNSlocal mStatus AddAWDDNSDomainStats(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell); +mDNSlocal void LogDNSHistSet(const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell); +mDNSlocal void LogDNSHist(const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType); +mDNSlocal void LogDNSHistSendCounts(const uint16_t inSendCountBins[kQueryStatsSendCountBinCount]); +mDNSlocal void LogDNSHistLatencies(const uint16_t inLatencyBins[kQueryStatsLatencyBinCount]); +#if (METRICS_VALIDATE_DNS_STATS_DOMAINS) +mDNSlocal void ValidateDNSStatsDomains(void); +#endif //=========================================================================================================================== // MetricsInit @@ -137,28 +364,48 @@ mStatus MetricsInit(void) { mStatus err; +#if (METRICS_VALIDATE_DNS_STATS_DOMAINS) + ValidateDNSStatsDomains(); +#endif + err = CreateDomainStatsList(&gDomainStatsList); require_noerr_quiet(err, exit); + err = CreateResolveStatsList(&gResolveStatsList); + require_noerr_quiet(err, exit); + @autoreleasepool { gAWDServerConnection = [[AWDServerConnectionSoft alloc] - initWithComponentId: AWDComponentId_MDNSResponder - andBlockOnConfiguration: NO]; + initWithComponentId: AWDComponentId_MDNSResponder + andBlockOnConfiguration: NO]; if (gAWDServerConnection) { [gAWDServerConnection - registerQueriableMetricCallback: ^(UInt32 metricId) + registerQueriableMetricCallback: ^(UInt32 inMetricID) { - mStatus localErr; - - (void) metricId; - - localErr = SubmitAWDMetric(); - if (localErr) LogMsg("SubmitAWDMetric failed with error %d", localErr); + SubmitAWDMetric(inMetricID); } forIdentifier: (UInt32)AWDMetricId_MDNSResponder_DNSStatistics]; + + [gAWDServerConnection + registerQueriableMetricCallback: ^(UInt32 inMetricID) + { + SubmitAWDMetric(inMetricID); + } + forIdentifier: (UInt32)AWDMetricId_MDNSResponder_ResolveStats]; + + [gAWDServerConnection + registerQueriableMetricCallback: ^(UInt32 inMetricID) + { + SubmitAWDMetric(inMetricID); + } + forIdentifier: (UInt32)AWDMetricId_MDNSResponder_ServicesStats]; + } + else + { + LogMsg("MetricsInit: failed to create AWD server connection."); } } exit: @@ -166,194 +413,239 @@ exit: } //=========================================================================================================================== -// MetricsUpdateUDNSStats +// MetricsUpdateUDNSQueryStats //=========================================================================================================================== -mDNSexport void MetricsUpdateUDNSStats(const domainname *inQueryName, mDNSBool inAnswered, mDNSu32 inSendCount, mDNSu32 inLatencyMs, mDNSBool inForCellular) +mDNSexport void MetricsUpdateUDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, mDNSu32 inLatencyMs, mDNSBool inForCell) { - DNSStats * stats; - DNSDomainStatsRef domainStats; + DNSDomainStats * stats; int queryLabelCount; + const domainname * queryParentDomain; mDNSBool isQueryInDomain; + int skipCount; + int skipCountLast = -1; queryLabelCount = CountLabels(inQueryName); - for (domainStats = gDomainStatsList; domainStats; domainStats = domainStats->next) + for (stats = gDomainStatsList; stats; stats = stats->next) { isQueryInDomain = mDNSfalse; - if (strcmp(domainStats->domainStr, ".") == 0) + if (strcmp(stats->domain->cstr, ".") == 0) { // All queries are in the root domain. isQueryInDomain = mDNStrue; } else { - int skipCount; - const domainname * queryParentDomain; - - skipCount = queryLabelCount - domainStats->domainLabelCount; + skipCount = queryLabelCount - stats->domain->labelCount; if (skipCount >= 0) { - queryParentDomain = SkipLeadingLabels(inQueryName, skipCount); - isQueryInDomain = SameDomainName(queryParentDomain, &domainStats->domain); + if (skipCount != skipCountLast) + { + queryParentDomain = SkipLeadingLabels(inQueryName, skipCount); + skipCountLast = skipCount; + } + isQueryInDomain = SameDomainName(queryParentDomain, stats->domain->name); } } if (isQueryInDomain) { - stats = inForCellular ? &domainStats->statsCellular : &domainStats->stats; - UpdateDNSStats(stats, inAnswered, inSendCount, inLatencyMs); + DNSDomainStatsUpdate(stats, inType, inRR, inSendCount, inLatencyMs, inForCell); } } + } //=========================================================================================================================== -// CreateDomainStatsList +// MetricsUpdateUDNSResolveStats //=========================================================================================================================== -mDNSlocal mStatus CreateDomainStatsList(DNSDomainStatsRef *outList) +mDNSexport void MetricsUpdateUDNSResolveStats(const domainname *inQueryName, const ResourceRecord *inRR, mDNSBool inForCell) { - mStatus err; - size_t i; - DNSDomainStatsRef domainStats; - DNSDomainStatsRef * p; - DNSDomainStatsRef list = NULL; + ResolveStatsDomain * domain; + domainname hostname; + size_t hostnameLen; + mDNSBool isQueryInDomain; + int skipCount; + int skipCountLast = -1; + int queryLabelCount; + const domainname * queryParentDomain; + Response response; + + require_quiet((inRR->rrtype == kDNSType_A) || (inRR->rrtype == kDNSType_AAAA), exit); + require_quiet(inRR->rDNSServer, exit); - p = &list; - for (i = 0; i < countof(kUDNSStatsDomains); ++i) + queryLabelCount = CountLabels(inQueryName); + + for (domain = gResolveStatsList; domain; domain = domain->next) { - err = DNSDomainStatsCreate(kUDNSStatsDomains[i], &domainStats); - require_noerr_quiet(err, exit); + isQueryInDomain = mDNSfalse; + skipCount = queryLabelCount - domain->domainInfo->labelCount; + if (skipCount >= 0) + { + if (skipCount != skipCountLast) + { + queryParentDomain = SkipLeadingLabels(inQueryName, skipCount); + skipCountLast = skipCount; + } + isQueryInDomain = SameDomainName(queryParentDomain, domain->domainInfo->name); + } + if (!isQueryInDomain) continue; - *p = domainStats; - p = &domainStats->next; - } + hostnameLen = (size_t)(queryParentDomain->c - inQueryName->c); + if (hostnameLen >= sizeof(hostname.c)) continue; - *outList = list; - list = NULL; + memcpy(hostname.c, inQueryName->c, hostnameLen); + hostname.c[hostnameLen] = 0; + + if (inRR->RecordType == kDNSRecordTypePacketNegative) + { + response.type = (inRR->rrtype == kDNSType_A) ? kResponseType_NegA : kResponseType_NegAAAA; + response.data = NULL; + } + else + { + response.type = (inRR->rrtype == kDNSType_A) ? kResponseType_IPv4Addr : kResponseType_IPv6Addr; + response.data = (inRR->rrtype == kDNSType_A) ? inRR->rdata->u.ipv4.b : inRR->rdata->u.ipv6.b; + } + ResolveStatsDomainUpdate(domain, &hostname, &response, &inRR->rDNSServer->addr, inForCell); + } exit: - DNSDomainStatsFreeList(list); - return (err); + return; } //=========================================================================================================================== -// UpdateDNSStats +// LogMetrics //=========================================================================================================================== -mDNSlocal void UpdateDNSStats(DNSStats *inStats, mDNSBool inAnswered, mDNSu32 inQuerySendCount, mDNSu32 inLatencyMs) +mDNSexport void LogMetrics(void) { - size_t i; - - if (inAnswered) + DNSDomainStats * stats; + const ResolveStatsDomain * domain; + const ResolveStatsHostname * hostname; + const ResolveStatsDNSServer * server; + const ResolveStatsIPv4AddrSet * addrV4; + const ResolveStatsIPv6Addr * addrV6; + const ResolveStatsNegAAAASet * negV6; + int hostnameCount; + int i; + unsigned int serverID; + int serverObjCount = 0; + int hostnameObjCount = 0; + int addrObjCount = 0; + + LogMsgNoIdent("---- DNS query stats by domain -----"); + + for (stats = gDomainStatsList; stats; stats = stats->next) { - i = (inQuerySendCount <= kUDNSStatsMaxQuerySendCount) ? inQuerySendCount : kUDNSStatsMaxQuerySendCount; - inStats->answeredQuerySendCountBins[i]++; - - if (inQuerySendCount > 0) + if (!stats->nonCellular && !stats->cellular) { - for (i = 0; (i < countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {} - inStats->responseLatencyBins[i]++; + LogMsgNoIdent("No data for %s", stats->domain->cstr); + continue; } + if (stats->nonCellular) LogDNSHistSet(stats->nonCellular, stats->domain->cstr, mDNSfalse); + if (stats->cellular) LogDNSHistSet(stats->cellular, stats->domain->cstr, mDNStrue); } - else if (inQuerySendCount > 0) - { - i = (inQuerySendCount <= kUDNSStatsMaxQuerySendCount) ? inQuerySendCount : kUDNSStatsMaxQuerySendCount; - inStats->unansweredQuerySendCountBins[i]++; - } -} - -//=========================================================================================================================== -// SubmitAWDMetric -//=========================================================================================================================== - -mDNSlocal mStatus SubmitAWDMetric(void) -{ - mStatus err; - BOOL success; - DNSDomainStatsRef domainStats; - DNSDomainStatsRef newDomainStatsList; - DNSDomainStatsRef domainStatsList = NULL; - AWDMetricContainer * container = nil; - AWDMDNSResponderDNSStatistics * metric = nil; - AWDDNSDomainStats * awdDomainStats = nil; - - err = CreateDomainStatsList(&newDomainStatsList); - require_noerr_quiet(err, exit); - - domainStatsList = gDomainStatsList; - - KQueueLock(&mDNSStorage); - gDomainStatsList = newDomainStatsList; - KQueueUnlock(&mDNSStorage, "SubmitAWDMetric"); - - container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_DNSStatistics]; - require_action_quiet(container, exit, err = mStatus_UnknownErr); - metric = [[AWDMDNSResponderDNSStatisticsSoft alloc] init]; - require_action_quiet(metric, exit, err = mStatus_UnknownErr); + LogMsgNoIdent("---- DNS resolve stats by domain -----"); - for (domainStats = domainStatsList; domainStats; domainStats = domainStats->next) + LogMsgNoIdent("Servers:"); + for (server = gResolveStatsServerList; server; server = server->next) { - err = CreateAWDDNSDomainStats(&domainStats->stats, domainStats->domainStr, mDNSfalse, &awdDomainStats); - require_noerr_quiet(err, exit); + serverObjCount++; + LogMsgNoIdent(server->isAddrV6 ? "%2u: %s %.16a" : "%2u: %s %.4a", + server->id, server->isForCell ? " C" : "NC", server->addrBytes); + } - [metric addStats:awdDomainStats]; - [awdDomainStats release]; - awdDomainStats = nil; + for (domain = gResolveStatsList; domain; domain = domain->next) + { + hostnameCount = 0; + for (hostname = domain->hostnameList; hostname; hostname = hostname->next) { hostnameCount++; } + hostnameObjCount += hostnameCount; - err = CreateAWDDNSDomainStats(&domainStats->statsCellular, domainStats->domainStr, mDNStrue, &awdDomainStats); - require_noerr_quiet(err, exit); + LogMsgNoIdent("%##s (%d hostname%s)", domain->domainInfo->name, hostnameCount, (hostnameCount == 1) ? "" : "s"); - [metric addStats:awdDomainStats]; - [awdDomainStats release]; - awdDomainStats = nil; + for (hostname = domain->hostnameList; hostname; hostname = hostname->next) + { + LogMsgNoIdent(" %##s", hostname->name); + for (serverID = 0; serverID < gResolveStatsNextServerID; ++serverID) + { + for (addrV4 = hostname->addrV4List; addrV4; addrV4 = addrV4->next) + { + if (serverID == 0) addrObjCount++; + for (i = 0; i < (int)countof(addrV4->counters); ++i) + { + const IPv4AddrCounter * counter; + + counter = &addrV4->counters[i]; + if (counter->count == 0) break; + if (counter->serverID == serverID) + { + if (counter->isNegative) + { + LogMsgNoIdent("%10u: %3u negative A", counter->serverID, counter->count); + } + else + { + LogMsgNoIdent("%10u: %3u %.4a", counter->serverID, counter->count, counter->addrBytes); + } + } + } + } + for (addrV6 = hostname->addrV6List; addrV6; addrV6 = addrV6->next) + { + if (serverID == 0) addrObjCount++; + if (addrV6->serverID == serverID) + { + LogMsgNoIdent("%10u: %3u %.16a", addrV6->serverID, addrV6->count, addrV6->addrBytes); + } + } + for (negV6 = hostname->negV6List; negV6; negV6 = negV6->next) + { + if (serverID == 0) addrObjCount++; + for (i = 0; i < (int)countof(negV6->counters); ++i) + { + const NegAAAACounter * counter; + + counter = &negV6->counters[i]; + if (counter->count == 0) break; + if (counter->serverID == serverID) + { + LogMsgNoIdent("%10u: %3u negative AAAA", counter->serverID, counter->count); + } + } + } + } + } } - - container.metric = metric; - success = [gAWDServerConnection submitMetric:container]; - LogMsg("SubmitAWDMetric: metric submission %s.", success ? "succeeded" : "failed" ); - err = success ? mStatus_NoError : mStatus_UnknownErr; - -exit: - [awdDomainStats release]; - [metric release]; - [container release]; - DNSDomainStatsFreeList(domainStatsList); - return (err); + LogMsgNoIdent("Total object count: %3d (server %d hostname %d address %d)", + serverObjCount + hostnameObjCount + addrObjCount, serverObjCount, hostnameObjCount, addrObjCount); + + LogMsgNoIdent("---- Num of Services Registered -----"); + LogMsgNoIdent("Current_number_of_services_registered :[%d], Max_number_of_services_registered :[%d]", + curr_num_regservices, max_num_regservices); } //=========================================================================================================================== // DNSDomainStatsCreate //=========================================================================================================================== -mDNSlocal mStatus DNSDomainStatsCreate(const char *inDomain, DNSDomainStatsRef *outStats) +mDNSlocal mStatus DNSDomainStatsCreate(const Domain *inDomain, DNSDomainStats **outStats) { mStatus err; - DNSDomainStatsRef obj; - mDNSu8 * ptr; - - obj = (DNSDomainStatsRef) calloc(1, sizeof(*obj)); - require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + DNSDomainStats * obj; - obj->domainStr = strdup(inDomain); + obj = (DNSDomainStats *)calloc(1, sizeof(*obj)); require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); - // Initialize domainname for non-root domains. - - if (strcmp(obj->domainStr, ".") != 0) - { - ptr = MakeDomainNameFromDNSNameString(&obj->domain, obj->domainStr); - require_action_quiet(ptr, exit, err = mStatus_Invalid); - obj->domainLabelCount = CountLabels(&obj->domain); - } + obj->domain = inDomain; *outStats = obj; - obj = NULL; err = mStatus_NoError; exit: - if (obj) DNSDomainStatsFree(obj); return (err); } @@ -361,9 +653,24 @@ exit: // DNSDomainStatsFree //=========================================================================================================================== -mDNSlocal void DNSDomainStatsFree(DNSDomainStatsRef inStats) +mDNSlocal void DNSDomainStatsFree(DNSDomainStats *inStats) { - if (inStats->domainStr) free(inStats->domainStr); + if (inStats->nonCellular) + { + ForgetMem(&inStats->nonCellular->histAny); + ForgetMem(&inStats->nonCellular->histA); + ForgetMem(&inStats->nonCellular->histAAAA); + free(inStats->nonCellular); + inStats->nonCellular = NULL; + } + if (inStats->cellular) + { + ForgetMem(&inStats->cellular->histAny); + ForgetMem(&inStats->cellular->histA); + ForgetMem(&inStats->cellular->histAAAA); + free(inStats->cellular); + inStats->cellular = NULL; + } free(inStats); } @@ -371,9 +678,9 @@ mDNSlocal void DNSDomainStatsFree(DNSDomainStatsRef inStats) // DNSDomainStatsFreeList //=========================================================================================================================== -mDNSlocal void DNSDomainStatsFreeList(DNSDomainStatsRef inList) +mDNSlocal void DNSDomainStatsFreeList(DNSDomainStats *inList) { - DNSDomainStatsRef stats; + DNSDomainStats * stats; while ((stats = inList) != NULL) { @@ -383,149 +690,1321 @@ mDNSlocal void DNSDomainStatsFreeList(DNSDomainStatsRef inList) } //=========================================================================================================================== -// CreateAWDDNSDomainStats +// DNSDomainStatsUpdate //=========================================================================================================================== -mDNSlocal mStatus CreateAWDDNSDomainStats(DNSStats *inStats, const char *inDomain, mDNSBool inIsCellType, AWDDNSDomainStats **outAWDStats) +mDNSlocal mStatus DNSDomainStatsUpdate(DNSDomainStats *inStats, uint16_t inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, mDNSu32 inLatencyMs, mDNSBool inForCell) { - mStatus err; - AWDDNSDomainStats * awdStats = nil; - NSString * domain = nil; - - awdStats = [[AWDDNSDomainStatsSoft alloc] init]; - require_action_quiet(awdStats, exit, err = mStatus_UnknownErr); - - domain = [[NSString alloc] initWithUTF8String:inDomain]; - require_action_quiet(domain, exit, err = mStatus_UnknownErr); - - awdStats.domain = domain; - awdStats.networkType = inIsCellType ? AWDDNSDomainStats_NetworkType_Cellular : AWDDNSDomainStats_NetworkType_NonCellular; - - [awdStats - setAnsweredQuerySendCounts: inStats->answeredQuerySendCountBins - count: (NSUInteger)countof(inStats->answeredQuerySendCountBins)]; - - [awdStats - setUnansweredQuerySendCounts: inStats->unansweredQuerySendCountBins - count: (NSUInteger)countof(inStats->unansweredQuerySendCountBins)]; + mStatus err; + DNSHistSet ** p; + DNSHistSet * set; + DNSHist * histAny; + DNSHist * hist; + int i; - [awdStats - setResponseLatencyMs: inStats->responseLatencyBins - count: (NSUInteger)countof(inStats->responseLatencyBins)]; + require_action_quiet(inRR || (inQuerySendCount > 0), exit, err = mStatus_NoError); - *outAWDStats = awdStats; - awdStats = nil; - err = mStatus_NoError; - -exit: - [domain release]; - [awdStats release]; - return (err); -} - -//=========================================================================================================================== -// LogDNSStats -//=========================================================================================================================== - -#define Percent(N, D) ((N) * 100) / (D), (((N) * 10000) / (D)) % 100 -#define PercentFmt "%3u.%02u" -#define LogStat(LABEL, COUNT, ACCUMULATOR, TOTAL) \ - LogMsgNoIdent("%s %5u " PercentFmt " " PercentFmt, (LABEL), (COUNT), Percent(COUNT, TOTAL), Percent(ACCUMULATOR, TOTAL)) - -mDNSlocal void LogDNSStats(const DNSStats *inStats) -{ - uint32_t total; - uint32_t totalUnanswered; - size_t i; - char label[16]; - - totalUnanswered = 0; - for (i = 0; i < countof(inStats->unansweredQuerySendCountBins); ++i) + p = inForCell ? &inStats->cellular : &inStats->nonCellular; + if ((set = *p) == NULL) { - totalUnanswered += inStats->unansweredQuerySendCountBins[i]; + set = (DNSHistSet *)calloc(1, sizeof(*set)); + require_action_quiet(set, exit, err = mStatus_NoMemoryErr); + *p = set; } - - total = 0; - for (i = 0; i <= countof(inStats->answeredQuerySendCountBins); ++i) + if ((histAny = set->histAny) == NULL) { - total += inStats->answeredQuerySendCountBins[i]; + histAny = (DNSHist *)calloc(1, sizeof(*histAny)); + require_action_quiet(histAny, exit, err = mStatus_NoMemoryErr); + set->histAny = histAny; } - - LogMsgNoIdent("Answered questions %5u", total); - LogMsgNoIdent("Unanswered questions %5u", totalUnanswered); - LogMsgNoIdent("+++ Number of queries sent +++"); - if (total > 0) + if (inType == kDNSType_A) { - uint32_t accumulator = 0; - - for (i = 0; i < countof(inStats->answeredQuerySendCountBins); ++i) + if ((hist = set->histA) == NULL) { - uint32_t count; - const char * suffix; - - count = inStats->answeredQuerySendCountBins[i]; - accumulator += count; - suffix = (i < (countof(inStats->answeredQuerySendCountBins) - 1)) ? " " : "+"; - snprintf(label, sizeof(label), "%2d%s", (int)i, suffix); - LogStat(label, count, accumulator, total); + hist = (DNSHist *)calloc(1, sizeof(*hist)); + require_action_quiet(hist, exit, err = mStatus_NoMemoryErr); + set->histA = hist; } } - else + else if (inType == kDNSType_AAAA) { - LogMsgNoIdent("No data."); + if ((hist = set->histAAAA) == NULL) + { + hist = (DNSHist *)calloc(1, sizeof(*hist)); + require_action_quiet(hist, exit, err = mStatus_NoMemoryErr); + set->histAAAA = hist; + } } - - total = 0; - for (i = 0; i < countof(inStats->responseLatencyBins); ++i) + else { - total += inStats->responseLatencyBins[i]; + hist = NULL; } - LogMsgNoIdent("+++++++ Response times +++++++"); - if (total > 0) + if (inRR) { - uint32_t accumulator = 0; + uint16_t * sendCountBins; + uint16_t * latencyBins; + const mDNSBool isNegative = (inRR->RecordType == kDNSRecordTypePacketNegative); - for (i = 0; i < countof(inStats->responseLatencyBins); ++i) + i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount); + + sendCountBins = isNegative ? histAny->negAnsweredQuerySendCountBins : histAny->answeredQuerySendCountBins; + increment_saturate(sendCountBins[i], UINT16_MAX); + if (hist) { - uint32_t count; + sendCountBins = isNegative ? hist->negAnsweredQuerySendCountBins : hist->answeredQuerySendCountBins; + increment_saturate(sendCountBins[i], UINT16_MAX); + } - count = inStats->responseLatencyBins[i]; - accumulator += count; - if (i < countof(kResponseLatencyMsLimits)) - { - snprintf(label, sizeof(label), "< %5u ms", kResponseLatencyMsLimits[i]); - } - else + if (inQuerySendCount > 0) + { + for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {} + latencyBins = isNegative ? histAny->negResponseLatencyBins : histAny->responseLatencyBins; + increment_saturate(latencyBins[i], UINT16_MAX); + if (hist) { - snprintf(label, sizeof(label), "< ∞ ms"); + latencyBins = isNegative ? hist->negResponseLatencyBins : hist->responseLatencyBins; + increment_saturate(latencyBins[i], UINT16_MAX); } - LogStat(label, count, accumulator, total); - if (accumulator == total) break; } } else { - LogMsgNoIdent("No data."); + i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount); + increment_saturate(histAny->unansweredQuerySendCountBins[i], UINT16_MAX); + if (hist) increment_saturate(hist->unansweredQuerySendCountBins[i], UINT16_MAX); + + for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {} + increment_saturate(histAny->unansweredQueryDurationBins[i], UINT16_MAX); + if (hist) increment_saturate(hist->unansweredQueryDurationBins[i], UINT16_MAX); } + err = mStatus_NoError; + +exit: + return (err); } //=========================================================================================================================== -// LogMetrics +// ResolveStatsDomainCreate //=========================================================================================================================== -mDNSexport void LogMetrics(void) +mDNSlocal mStatus ResolveStatsDomainCreate(const Domain *inDomain, ResolveStatsDomain **outDomain) { - DNSDomainStatsRef domainStats; + mStatus err; + ResolveStatsDomain * obj; - LogMsgNoIdent("---- DNS stats by domain -----"); + obj = (ResolveStatsDomain *)calloc(1, sizeof(*obj)); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); - for (domainStats = gDomainStatsList; domainStats; domainStats = domainStats->next) - { - LogMsgNoIdent("Domain: %s (non-cellular)", domainStats->domainStr); - LogDNSStats(&domainStats->stats); - LogMsgNoIdent("Domain: %s (cellular)", domainStats->domainStr); - LogDNSStats(&domainStats->statsCellular); - } + obj->domainInfo = inDomain; + + *outDomain = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsDomainFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsDomainFree(ResolveStatsDomain *inDomain) +{ + ResolveStatsHostname * hostname; + + while ((hostname = inDomain->hostnameList) != NULL) + { + inDomain->hostnameList = hostname->next; + ResolveStatsHostnameFree(hostname); + } + free(inDomain); +} + +//=========================================================================================================================== +// ResolveStatsDomainUpdate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsDomainUpdate(ResolveStatsDomain *inDomain, const domainname *inHostname, const Response *inResp, const mDNSAddr *inDNSAddr, mDNSBool inForCell) +{ + mStatus err; + ResolveStatsHostname ** p; + ResolveStatsHostname * hostname; + uint8_t serverID; + + for (p = &inDomain->hostnameList; (hostname = *p) != NULL; p = &hostname->next) + { + if (SameDomainName((domainname *)hostname->name, inHostname)) break; + } + + if (!hostname) + { + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsHostnameCreate(inHostname, &hostname); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + *p = hostname; + } + + err = ResolveStatsGetServerID(inDNSAddr, inForCell, &serverID); + require_noerr_quiet(err, exit); + + err = ResolveStatsHostnameUpdate(hostname, inResp, serverID); + require_noerr_quiet(err, exit); + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsHostnameCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsHostnameCreate(const domainname *inName, ResolveStatsHostname **outHostname) +{ + mStatus err; + ResolveStatsHostname * obj; + size_t nameLen; + + nameLen = DomainNameLength(inName); + require_action_quiet(nameLen > 0, exit, err = mStatus_Invalid); + + obj = (ResolveStatsHostname *)calloc(1, sizeof(*obj) - 1 + nameLen); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + memcpy(obj->name, inName, nameLen); + + *outHostname = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsDomainCreateAWDVersion +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsDomainCreateAWDVersion(const ResolveStatsDomain *inDomain, AWDMDNSResponderResolveStatsDomain **outDomain) +{ + mStatus err; + AWDMDNSResponderResolveStatsDomain * domain; + ResolveStatsHostname * hostname; + AWDMDNSResponderResolveStatsHostname * awdHostname; + NSString * name; + + domain = [[AWDMDNSResponderResolveStatsDomainSoft alloc] init]; + require_action_quiet(domain, exit, err = mStatus_UnknownErr); + + name = [[NSString alloc] initWithUTF8String:inDomain->domainInfo->cstr]; + require_action_quiet(name, exit, err = mStatus_UnknownErr); + + domain.name = name; + [name release]; + name = nil; + + for (hostname = inDomain->hostnameList; hostname; hostname = hostname->next) + { + err = ResolveStatsHostnameCreateAWDVersion(hostname, &awdHostname); + require_noerr_quiet(err, exit); + + [domain addHostname:awdHostname]; + [awdHostname release]; + awdHostname = nil; + } + + *outDomain = domain; + domain = nil; + err = mStatus_NoError; + +exit: + [domain release]; + return (err); +} + +//=========================================================================================================================== +// ResolveStatsHostnameFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsHostnameFree(ResolveStatsHostname *inHostname) +{ + ResolveStatsIPv4AddrSet * addrV4; + ResolveStatsIPv6Addr * addrV6; + ResolveStatsNegAAAASet * negV6; + + while ((addrV4 = inHostname->addrV4List) != NULL) + { + inHostname->addrV4List = addrV4->next; + ResolveStatsIPv4AddrSetFree(addrV4); + } + while ((addrV6 = inHostname->addrV6List) != NULL) + { + inHostname->addrV6List = addrV6->next; + ResolveStatsIPv6AddressFree(addrV6); + } + while ((negV6 = inHostname->negV6List) != NULL) + { + inHostname->negV6List = negV6->next; + ResolveStatsNegAAAASetFree(negV6); + } + free(inHostname); +} + +//=========================================================================================================================== +// ResolveStatsHostnameUpdate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsHostnameUpdate(ResolveStatsHostname *inHostname, const Response *inResp, uint8_t inServerID) +{ + mStatus err; + + if ((inResp->type == kResponseType_IPv4Addr) || (inResp->type == kResponseType_NegA)) + { + ResolveStatsIPv4AddrSet ** p; + ResolveStatsIPv4AddrSet * addrV4; + int i; + IPv4AddrCounter * counter; + + for (p = &inHostname->addrV4List; (addrV4 = *p) != NULL; p = &addrV4->next) + { + for (i = 0; i < (int)countof(addrV4->counters); ++i) + { + counter = &addrV4->counters[i]; + if (counter->count == 0) break; + if (counter->serverID != inServerID) continue; + if (inResp->type == kResponseType_NegA) + { + if (counter->isNegative) break; + } + else + { + if (memcmp(counter->addrBytes, inResp->data, 4) == 0) break; + } + } + if (i < (int)countof(addrV4->counters)) break; + } + if (!addrV4) + { + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsIPv4AddrSetCreate(&addrV4); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + + *p = addrV4; + counter = &addrV4->counters[0]; + } + if (counter->count == 0) + { + counter->serverID = inServerID; + if (inResp->type == kResponseType_NegA) + { + counter->isNegative = 1; + } + else + { + counter->isNegative = 0; + memcpy(counter->addrBytes, inResp->data, 4); + } + } + increment_saturate(counter->count, UINT16_MAX); + err = mStatus_NoError; + } + else if (inResp->type == kResponseType_IPv6Addr) + { + ResolveStatsIPv6Addr ** p; + ResolveStatsIPv6Addr * addrV6; + + for (p = &inHostname->addrV6List; (addrV6 = *p) != NULL; p = &addrV6->next) + { + if ((addrV6->serverID == inServerID) && (memcmp(addrV6->addrBytes, inResp->data, 16) == 0)) break; + } + if (!addrV6) + { + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsIPv6AddressCreate(inServerID, inResp->data, &addrV6); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + + *p = addrV6; + } + increment_saturate(addrV6->count, UINT16_MAX); + err = mStatus_NoError; + } + else if (inResp->type == kResponseType_NegAAAA) + { + ResolveStatsNegAAAASet ** p; + ResolveStatsNegAAAASet * negV6; + int i; + NegAAAACounter * counter; + + for (p = &inHostname->negV6List; (negV6 = *p) != NULL; p = &negV6->next) + { + for (i = 0; i < (int)countof(negV6->counters); ++i) + { + counter = &negV6->counters[i]; + if ((counter->count == 0) || (counter->serverID == inServerID)) break; + } + if (i < (int)countof(negV6->counters)) break; + } + if (!negV6) + { + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsNegAAAASetCreate(&negV6); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + + *p = negV6; + counter = &negV6->counters[0]; + } + if (counter->count == 0) counter->serverID = inServerID; + increment_saturate(counter->count, UINT16_MAX); + err = mStatus_NoError; + } + else + { + err = mStatus_Invalid; + } + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsHostnameCreateAWDVersion +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsHostnameCreateAWDVersion(const ResolveStatsHostname *inHostname, AWDMDNSResponderResolveStatsHostname **outHostname) +{ + mStatus err; + AWDMDNSResponderResolveStatsHostname * hostname; + NSString * name; + char nameBuf[MAX_ESCAPED_DOMAIN_NAME]; + const char * ptr; + ResolveStatsIPv4AddrSet * addrV4; + ResolveStatsIPv6Addr * addrV6; + ResolveStatsNegAAAASet * negV6; + AWDMDNSResponderResolveStatsResult * result = nil; + int i; + + hostname = [[AWDMDNSResponderResolveStatsHostnameSoft alloc] init]; + require_action_quiet(hostname, exit, err = mStatus_UnknownErr); + + ptr = ConvertDomainNameToCString((domainname *)inHostname->name, nameBuf); + require_action_quiet(ptr, exit, err = mStatus_UnknownErr); + + name = [[NSString alloc] initWithUTF8String:nameBuf]; + require_action_quiet(name, exit, err = mStatus_UnknownErr); + + hostname.name = name; + [name release]; + name = nil; + + for (addrV4 = inHostname->addrV4List; addrV4; addrV4 = addrV4->next) + { + for (i = 0; i < (int)countof(addrV4->counters); ++i) + { + const IPv4AddrCounter * counter; + NSData * addrBytes; + + counter = &addrV4->counters[i]; + if (counter->count == 0) break; + + result = [[AWDMDNSResponderResolveStatsResultSoft alloc] init]; + require_action_quiet(result, exit, err = mStatus_UnknownErr); + + if (counter->isNegative) + { + result.type = AWDMDNSResponderResolveStatsResult_ResultType_NegA; + } + else + { + addrBytes = [[NSData alloc] initWithBytes:counter->addrBytes length:4]; + require_action_quiet(addrBytes, exit, err = mStatus_UnknownErr); + + result.type = AWDMDNSResponderResolveStatsResult_ResultType_IPv4Addr; + result.data = addrBytes; + [addrBytes release]; + } + result.count = counter->count; + result.serverID = counter->serverID; + + [hostname addResult:result]; + [result release]; + result = nil; + } + } + + for (addrV6 = inHostname->addrV6List; addrV6; addrV6 = addrV6->next) + { + NSData * addrBytes; + + result = [[AWDMDNSResponderResolveStatsResultSoft alloc] init]; + require_action_quiet(result, exit, err = mStatus_UnknownErr); + + addrBytes = [[NSData alloc] initWithBytes:addrV6->addrBytes length:16]; + require_action_quiet(addrBytes, exit, err = mStatus_UnknownErr); + + result.type = AWDMDNSResponderResolveStatsResult_ResultType_IPv6Addr; + result.count = addrV6->count; + result.serverID = addrV6->serverID; + result.data = addrBytes; + + [addrBytes release]; + + [hostname addResult:result]; + [result release]; + result = nil; + } + + for (negV6 = inHostname->negV6List; negV6; negV6 = negV6->next) + { + for (i = 0; i < (int)countof(negV6->counters); ++i) + { + const NegAAAACounter * counter; + + counter = &negV6->counters[i]; + if (counter->count == 0) break; + + result = [[AWDMDNSResponderResolveStatsResultSoft alloc] init]; + require_action_quiet(result, exit, err = mStatus_UnknownErr); + + result.type = AWDMDNSResponderResolveStatsResult_ResultType_NegAAAA; + result.count = counter->count; + result.serverID = counter->serverID; + + [hostname addResult:result]; + [result release]; + result = nil; + } + } + + *outHostname = hostname; + hostname = nil; + err = mStatus_NoError; + +exit: + [result release]; + [hostname release]; + return (err); +} + +//=========================================================================================================================== +// ResolveStatsDNSServerCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsDNSServerCreate(const mDNSAddr *inAddr, mDNSBool inForCell, ResolveStatsDNSServer **outServer) +{ + mStatus err; + ResolveStatsDNSServer * obj; + size_t addrLen; + + require_action_quiet((inAddr->type == mDNSAddrType_IPv4) || (inAddr->type == mDNSAddrType_IPv6), exit, err = mStatus_Invalid); + + addrLen = (inAddr->type == mDNSAddrType_IPv4) ? 4 : 16; + obj = (ResolveStatsDNSServer *)calloc(1, sizeof(*obj) - 1 + addrLen); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + obj->isForCell = inForCell; + if (inAddr->type == mDNSAddrType_IPv4) + { + obj->isAddrV6 = mDNSfalse; + memcpy(obj->addrBytes, inAddr->ip.v4.b, addrLen); + } + else + { + obj->isAddrV6 = mDNStrue; + memcpy(obj->addrBytes, inAddr->ip.v6.b, addrLen); + } + + *outServer = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsDNSServerFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsDNSServerFree(ResolveStatsDNSServer *inServer) +{ + free(inServer); +} + +//=========================================================================================================================== +// ResolveStatsDNSServerCreateAWDVersion +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsDNSServerCreateAWDVersion(const ResolveStatsDNSServer *inServer, AWDMDNSResponderResolveStatsDNSServer **outServer) +{ + mStatus err; + AWDMDNSResponderResolveStatsDNSServer * server; + NSData * addrBytes = nil; + + server = [[AWDMDNSResponderResolveStatsDNSServerSoft alloc] init]; + require_action_quiet(server, exit, err = mStatus_UnknownErr); + + addrBytes = [[NSData alloc] initWithBytes:inServer->addrBytes length:(inServer->isAddrV6 ? 16 : 4)]; + require_action_quiet(addrBytes, exit, err = mStatus_UnknownErr); + + server.serverID = inServer->id; + server.address = addrBytes; + if (inServer->isForCell) + { + server.networkType = AWDMDNSResponderResolveStatsDNSServer_NetworkType_Cellular; + } + else + { + server.networkType = AWDMDNSResponderResolveStatsDNSServer_NetworkType_NonCellular; + } + + *outServer = server; + server = nil; + err = mStatus_NoError; + +exit: + [addrBytes release]; + [server release]; + return (err); +} + +//=========================================================================================================================== +// ResolveStatsIPv4AddrSetCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsIPv4AddrSetCreate(ResolveStatsIPv4AddrSet **outSet) +{ + mStatus err; + ResolveStatsIPv4AddrSet * obj; + + obj = (ResolveStatsIPv4AddrSet *)calloc(1, sizeof(*obj)); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + *outSet = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsIPv4AddrSetFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsIPv4AddrSetFree(ResolveStatsIPv4AddrSet *inSet) +{ + free(inSet); +} + +//=========================================================================================================================== +// ResolveStatsIPv6AddressCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsIPv6AddressCreate(uint8_t inServerID, const uint8_t inAddrBytes[16], ResolveStatsIPv6Addr **outAddr) +{ + mStatus err; + ResolveStatsIPv6Addr * obj; + + obj = (ResolveStatsIPv6Addr *)calloc(1, sizeof(*obj)); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + obj->serverID = inServerID; + memcpy(obj->addrBytes, inAddrBytes, 16); + + *outAddr = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsIPv6AddressFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsIPv6AddressFree(ResolveStatsIPv6Addr *inAddr) +{ + free(inAddr); +} + +//=========================================================================================================================== +// ResolveStatsNegAAAASetCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsNegAAAASetCreate(ResolveStatsNegAAAASet **outSet) +{ + mStatus err; + ResolveStatsNegAAAASet * obj; + + obj = (ResolveStatsNegAAAASet *)calloc(1, sizeof(*obj)); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + *outSet = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsNegAAAASetFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsNegAAAASetFree(ResolveStatsNegAAAASet *inSet) +{ + free(inSet); +} + +//=========================================================================================================================== +// ResolveStatsGetServerID +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsGetServerID(const mDNSAddr *inServerAddr, mDNSBool inForCell, uint8_t *outServerID) +{ + mStatus err; + ResolveStatsDNSServer ** p; + ResolveStatsDNSServer * server; + + require_action_quiet((inServerAddr->type == mDNSAddrType_IPv4) || (inServerAddr->type == mDNSAddrType_IPv6), exit, err = mStatus_Invalid); + + for (p = &gResolveStatsServerList; (server = *p) != NULL; p = &server->next) + { + if ((inForCell && server->isForCell) || (!inForCell && !server->isForCell)) + { + if (inServerAddr->type == mDNSAddrType_IPv4) + { + if (!server->isAddrV6 && (memcmp(server->addrBytes, inServerAddr->ip.v4.b, 4) == 0)) break; + } + else + { + if (server->isAddrV6 && (memcmp(server->addrBytes, inServerAddr->ip.v6.b, 16) == 0)) break; + } + } + } + + if (!server) + { + require_action_quiet(gResolveStatsNextServerID <= UINT8_MAX, exit, err = mStatus_Refused); + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsDNSServerCreate(inServerAddr, inForCell, &server); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + + server->id = gResolveStatsNextServerID++; + server->next = gResolveStatsServerList; + gResolveStatsServerList = server; + } + else if (gResolveStatsServerList != server) + { + *p = server->next; + server->next = gResolveStatsServerList; + gResolveStatsServerList = server; + } + + *outServerID = server->id; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// CreateDomainStatsList +//=========================================================================================================================== + +mDNSlocal mStatus CreateDomainStatsList(DNSDomainStats **outList) +{ + mStatus err; + int i; + DNSDomainStats * stats; + DNSDomainStats ** p; + DNSDomainStats * list = NULL; + + p = &list; + for (i = 0; i < (int)countof(kQueryStatsDomains); ++i) + { + err = DNSDomainStatsCreate(&kQueryStatsDomains[i], &stats); + require_noerr_quiet(err, exit); + + *p = stats; + p = &stats->next; + } + + *outList = list; + list = NULL; + +exit: + DNSDomainStatsFreeList(list); + return (err); +} + +//=========================================================================================================================== +// CreateResolveStatsList +//=========================================================================================================================== + +mDNSlocal mStatus CreateResolveStatsList(ResolveStatsDomain **outList) +{ + mStatus err; + int i; + ResolveStatsDomain * domain; + ResolveStatsDomain ** p; + ResolveStatsDomain * list = NULL; + + p = &list; + for (i = 0; i < (int)countof(kResolveStatsDomains); ++i) + { + err = ResolveStatsDomainCreate(&kResolveStatsDomains[i], &domain); + require_noerr_quiet(err, exit); + + *p = domain; + p = &domain->next; + } + + *outList = list; + list = NULL; + +exit: + FreeResolveStatsList(list); + return (err); +} + +//=========================================================================================================================== +// FreeResolveStatsList +//=========================================================================================================================== + +mDNSlocal void FreeResolveStatsList(ResolveStatsDomain *inList) +{ + ResolveStatsDomain * domain; + + while ((domain = inList) != NULL) + { + inList = domain->next; + ResolveStatsDomainFree(domain); + } +} + +//=========================================================================================================================== +// FreeResolveStatsServerList +//=========================================================================================================================== + +mDNSlocal void FreeResolveStatsServerList(ResolveStatsDNSServer *inList) +{ + ResolveStatsDNSServer * server; + + while ((server = inList) != NULL) + { + inList = server->next; + ResolveStatsDNSServerFree(server); + } +} + +//=========================================================================================================================== +// SubmitAWDMetric +//=========================================================================================================================== + +mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID) +{ + mStatus err = mStatus_NoError; + + switch (inMetricID) + { + case AWDMetricId_MDNSResponder_DNSStatistics: + err = SubmitAWDMetricQueryStats(); + break; + + case AWDMetricId_MDNSResponder_ResolveStats: + err = SubmitAWDMetricResolveStats(); + break; + + case AWDMetricId_MDNSResponder_ServicesStats: + [AWDMetricManagerSoft postMetricWithId:AWDMetricId_MDNSResponder_ServicesStats integerValue:max_num_regservices]; + KQueueLock(&mDNSStorage); + // reset the no of max services since we want to collect the max no of services registered per AWD submission period + max_num_regservices = curr_num_regservices; + KQueueUnlock(&mDNSStorage, "SubmitAWDSimpleMetricServiceStats"); + break; + + default: + err = mStatus_UnsupportedErr; + break; + } + + if (err) + LogMsg("SubmitAWDMetric for metric ID 0x%08X failed with error %d", inMetricID, err); + return (err); +} + +//=========================================================================================================================== +// SubmitAWDMetricQueryStats +//=========================================================================================================================== + +mDNSlocal mStatus SubmitAWDMetricQueryStats(void) +{ + mStatus err; + BOOL success; + DNSDomainStats * stats; + DNSDomainStats * newDomainStatsList; + DNSDomainStats * domainStatsList = NULL; + AWDMetricContainer * container = nil; + AWDMDNSResponderDNSStatistics * metric = nil; + + err = CreateDomainStatsList(&newDomainStatsList); + require_noerr_quiet(err, exit); + + domainStatsList = gDomainStatsList; + + KQueueLock(&mDNSStorage); + gDomainStatsList = newDomainStatsList; + KQueueUnlock(&mDNSStorage, "SubmitAWDMetricQueryStats"); + + container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_DNSStatistics]; + require_action_quiet(container, exit, err = mStatus_UnknownErr); + + metric = [[AWDMDNSResponderDNSStatisticsSoft alloc] init]; + require_action_quiet(metric, exit, err = mStatus_UnknownErr); + + while ((stats = domainStatsList) != NULL) + { + if (stats->nonCellular) + { + err = AddAWDDNSDomainStats(metric, stats->nonCellular, stats->domain->cstr, mDNSfalse); + require_noerr_quiet(err, exit); + } + if (stats->cellular) + { + err = AddAWDDNSDomainStats(metric, stats->cellular, stats->domain->cstr, mDNStrue); + require_noerr_quiet(err, exit); + } + domainStatsList = stats->next; + DNSDomainStatsFree(stats); + } + + container.metric = metric; + success = [gAWDServerConnection submitMetric:container]; + LogMsg("SubmitAWDMetricQueryStats: metric submission %s.", success ? "succeeded" : "failed" ); + err = success ? mStatus_NoError : mStatus_UnknownErr; + +exit: + [metric release]; + [container release]; + DNSDomainStatsFreeList(domainStatsList); + return (err); +} + +//=========================================================================================================================== +// SubmitAWDMetricResolveStats +//=========================================================================================================================== + +mDNSlocal mStatus SubmitAWDMetricResolveStats(void) +{ + mStatus err; + ResolveStatsDomain * newResolveStatsList; + ResolveStatsDomain * domainList = NULL; + ResolveStatsDNSServer * serverList = NULL; + AWDMetricContainer * container = nil; + AWDMDNSResponderResolveStats * metric = nil; + ResolveStatsDNSServer * server; + ResolveStatsDomain * domain; + BOOL success; + + err = CreateResolveStatsList(&newResolveStatsList); + require_noerr_quiet(err, exit); + + domainList = gResolveStatsList; + serverList = gResolveStatsServerList; + + KQueueLock(&mDNSStorage); + gResolveStatsList = newResolveStatsList; + gResolveStatsServerList = NULL; + gResolveStatsNextServerID = 0; + gResolveStatsObjCount = 0; + KQueueUnlock(&mDNSStorage, "SubmitAWDMetricResolveStats"); + + container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_ResolveStats]; + require_action_quiet(container, exit, err = mStatus_UnknownErr); + + metric = [[AWDMDNSResponderResolveStatsSoft alloc] init]; + require_action_quiet(metric, exit, err = mStatus_UnknownErr); + + while ((server = serverList) != NULL) + { + AWDMDNSResponderResolveStatsDNSServer * awdServer; + + serverList = server->next; + err = ResolveStatsDNSServerCreateAWDVersion(server, &awdServer); + ResolveStatsDNSServerFree(server); + require_noerr_quiet(err, exit); + + [metric addServer:awdServer]; + [awdServer release]; + } + + while ((domain = domainList) != NULL) + { + AWDMDNSResponderResolveStatsDomain * awdDomain; + + domainList = domain->next; + err = ResolveStatsDomainCreateAWDVersion(domain, &awdDomain); + ResolveStatsDomainFree(domain); + require_noerr_quiet(err, exit); + + [metric addDomain:awdDomain]; + [awdDomain release]; + } + + container.metric = metric; + success = [gAWDServerConnection submitMetric:container]; + LogMsg("SubmitAWDMetricResolveStats: metric submission %s.", success ? "succeeded" : "failed" ); + err = success ? mStatus_NoError : mStatus_UnknownErr; + +exit: + [metric release]; + [container release]; + FreeResolveStatsList(domainList); + FreeResolveStatsServerList(serverList); + return (err); +} + +//=========================================================================================================================== +// CreateAWDDNSDomainStats +//=========================================================================================================================== + +mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats) +{ + mStatus err; + AWDDNSDomainStats * awdStats = nil; + NSString * domain = nil; + uint32_t sendCountBins[kQueryStatsSendCountBinCount]; + uint32_t latencyBins[kQueryStatsLatencyBinCount]; + int i; + unsigned int totalAnswered; + unsigned int totalNegAnswered; + unsigned int totalUnanswered; + + awdStats = [[AWDDNSDomainStatsSoft alloc] init]; + require_action_quiet(awdStats, exit, err = mStatus_UnknownErr); + + domain = [[NSString alloc] initWithUTF8String:inDomain]; + require_action_quiet(domain, exit, err = mStatus_UnknownErr); + + awdStats.domain = domain; + awdStats.networkType = inForCell ? AWDDNSDomainStats_NetworkType_Cellular : AWDDNSDomainStats_NetworkType_NonCellular; + awdStats.recordType = inType; + + totalAnswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + sendCountBins[i] = inHist->answeredQuerySendCountBins[i]; + totalAnswered += inHist->answeredQuerySendCountBins[i]; + } + [awdStats setAnsweredQuerySendCounts:sendCountBins count:kQueryStatsSendCountBinCount]; + + totalNegAnswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + sendCountBins[i] = inHist->negAnsweredQuerySendCountBins[i]; + totalNegAnswered += inHist->negAnsweredQuerySendCountBins[i]; + } + [awdStats setNegAnsweredQuerySendCounts:sendCountBins count:kQueryStatsSendCountBinCount]; + + totalUnanswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + sendCountBins[i] = inHist->unansweredQuerySendCountBins[i]; + totalUnanswered += inHist->unansweredQuerySendCountBins[i]; + } + [awdStats setUnansweredQuerySendCounts:sendCountBins count:kQueryStatsSendCountBinCount]; + + if (totalAnswered > inHist->answeredQuerySendCountBins[0]) + { + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + latencyBins[i] = inHist->responseLatencyBins[i]; + } + [awdStats setResponseLatencyMs:latencyBins count:kQueryStatsLatencyBinCount]; + } + + if (totalNegAnswered > inHist->negAnsweredQuerySendCountBins[0]) + { + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + latencyBins[i] = inHist->negResponseLatencyBins[i]; + } + [awdStats setNegResponseLatencyMs:latencyBins count:kQueryStatsLatencyBinCount]; + } + + if (totalUnanswered > 0) + { + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + latencyBins[i] = inHist->unansweredQueryDurationBins[i]; + } + [awdStats setUnansweredQueryDurationMs:latencyBins count:kQueryStatsLatencyBinCount]; + } + + *outStats = awdStats; + awdStats = nil; + err = mStatus_NoError; + +exit: + [domain release]; + [awdStats release]; + return (err); +} + +//=========================================================================================================================== +// AddAWDDNSDomainStats +//=========================================================================================================================== + +mDNSlocal mStatus AddAWDDNSDomainStats(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell) +{ + mStatus err; + AWDDNSDomainStats * awdStats; + + if (inSet->histAny) + { + err = CreateAWDDNSDomainStats(inSet->histAny, inDomain, inForCell, AWDDNSDomainStats_RecordType_Any, &awdStats); + require_noerr_quiet(err, exit); + + [inMetric addStats:awdStats]; + [awdStats release]; + } + if (inSet->histA) + { + err = CreateAWDDNSDomainStats(inSet->histA, inDomain, inForCell, AWDDNSDomainStats_RecordType_A, &awdStats); + require_noerr_quiet(err, exit); + + [inMetric addStats:awdStats]; + [awdStats release]; + } + if (inSet->histAAAA) + { + err = CreateAWDDNSDomainStats(inSet->histAAAA, inDomain, inForCell, AWDDNSDomainStats_RecordType_AAAA, &awdStats); + require_noerr_quiet(err, exit); + + [inMetric addStats:awdStats]; + [awdStats release]; + } + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// LogDNSHistSet +//=========================================================================================================================== + +mDNSlocal void LogDNSHistSet(const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell) +{ + if (inSet->histAny) LogDNSHist(inSet->histAny, inDomain, inForCell, "Any"); + if (inSet->histA) LogDNSHist(inSet->histA, inDomain, inForCell, "A"); + if (inSet->histAAAA) LogDNSHist(inSet->histAAAA, inDomain, inForCell, "AAAA"); +} + +//=========================================================================================================================== +// LogDNSHist +//=========================================================================================================================== + +#define Percent(N, D) ((N) * 100) / (D), (((N) * 10000) / (D)) % 100 +#define PercentFmt "%3u.%02u" +#define LogStat(LABEL, COUNT, ACCUMULATOR, TOTAL) \ + LogMsgNoIdent("%s %5u " PercentFmt " " PercentFmt, (LABEL), (COUNT), Percent(COUNT, TOTAL), Percent(ACCUMULATOR, TOTAL)) + +mDNSlocal void LogDNSHist(const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType) +{ + unsigned int totalAnswered; + unsigned int totalNegAnswered; + unsigned int totalUnanswered; + int i; + + totalAnswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + totalAnswered += inHist->answeredQuerySendCountBins[i]; + } + + totalNegAnswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + totalNegAnswered += inHist->negAnsweredQuerySendCountBins[i]; + } + + totalUnanswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + totalUnanswered += inHist->unansweredQuerySendCountBins[i]; + } + + LogMsgNoIdent("Domain: %s (%s, %s)", inDomain, inForCell ? "C" : "NC", inType); + LogMsgNoIdent("Answered questions %4u", totalAnswered); + LogMsgNoIdent("Negatively answered questions %4u", totalNegAnswered); + LogMsgNoIdent("Unanswered questions %4u", totalUnanswered); + LogMsgNoIdent("-- Query send counts ---------"); + LogDNSHistSendCounts(inHist->answeredQuerySendCountBins); + LogMsgNoIdent("-- Query send counts (NAQs) --"); + LogDNSHistSendCounts(inHist->negAnsweredQuerySendCountBins); + + if (totalAnswered > inHist->answeredQuerySendCountBins[0]) + { + LogMsgNoIdent("--- Response times -----------"); + LogDNSHistLatencies(inHist->responseLatencyBins); + } + + if (totalNegAnswered > inHist->negAnsweredQuerySendCountBins[0]) + { + LogMsgNoIdent("--- Response times (NAQs) ----"); + LogDNSHistLatencies(inHist->negResponseLatencyBins); + } + + if (totalUnanswered > 0) + { + LogMsgNoIdent("--- Unanswered query times ---"); + LogDNSHistLatencies(inHist->unansweredQueryDurationBins); + } +} + +//=========================================================================================================================== +// LogDNSHistSendCounts +//=========================================================================================================================== + +mDNSlocal void LogDNSHistSendCounts(const uint16_t inSendCountBins[kQueryStatsSendCountBinCount]) +{ + uint32_t total; + char label[16]; + int i; + + total = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + total += inSendCountBins[i]; + } + + if (total > 0) + { + uint32_t accumulator = 0; + + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + accumulator += inSendCountBins[i]; + if (i < (kQueryStatsSendCountBinCount - 1)) + { + snprintf(label, sizeof(label), "%2d ", i); + } + else + { + snprintf(label, sizeof(label), "%2d+", i); + } + LogStat(label, inSendCountBins[i], accumulator, total); + if (accumulator == total) break; + } + } + else + { + LogMsgNoIdent("No data."); + } +} + +//=========================================================================================================================== +// LogDNSHistLatencies +//=========================================================================================================================== + +mDNSlocal void LogDNSHistLatencies(const uint16_t inLatencyBins[kQueryStatsLatencyBinCount]) +{ + uint32_t total; + int i; + char label[16]; + + total = 0; + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + total += inLatencyBins[i]; + } + + if (total > 0) + { + uint32_t accumulator = 0; + + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + accumulator += inLatencyBins[i]; + if (i < (int)countof(kResponseLatencyMsLimits)) + { + snprintf(label, sizeof(label), "< %5u ms", kResponseLatencyMsLimits[i]); + } + else + { + snprintf(label, sizeof(label), "< ∞ ms"); + } + LogStat(label, inLatencyBins[i], accumulator, total); + if (accumulator == total) break; + } + } + else + { + LogMsgNoIdent("No data."); + } +} + +//=========================================================================================================================== +// ValidateDNSStatsDomains +//=========================================================================================================================== + +#if (METRICS_VALIDATE_DNS_STATS_DOMAINS) +#warning "Do not include ValidateDNSStatsDomains() in customer release!" +mDNSlocal void ValidateDNSStatsDomains(void) +{ + int i; + const Domain * domain; + mDNSu8 * ptr; + domainname domainNameExpected; + int labelCountExpected; + mDNSBool domainNamesEqual; + mDNSBool failed = mDNSfalse; + + for (i = 0; i < countof(kQueryStatsDomains); ++i) + { + domain = &kQueryStatsDomains[i]; + + if (strcmp(domain->cstr, ".") == 0) + { + domainNameExpected.c[0] = 0; + } + else + { + ptr = MakeDomainNameFromDNSNameString(&domainNameExpected, domain->cstr); + if (!ptr) + { + LogMsg("ValidateDNSStatsDomains: Failed to make domain name for \"%s\".", domain->cstr); + failed = mDNStrue; + goto exit; + } + } + + domainNamesEqual = SameDomainName(domain->name, &domainNameExpected); + labelCountExpected = CountLabels(&domainNameExpected); + if (domainNamesEqual && (domain->labelCount == labelCountExpected)) + { + LogMsg("ValidateDNSStatsDomains: \"%s\" passed.", domain->cstr); + } + else + { + if (!domainNamesEqual) + { + LogMsg("ValidateDNSStatsDomains: \"%s\" failed: incorrect domain name.", domain->cstr); + } + if (domain->labelCount != labelCountExpected) + { + LogMsg("ValidateDNSStatsDomains: \"%s\" failed: incorrect label count. Actual %d, expected %d.", + domain->cstr, domain->labelCount, labelCountExpected); + } + failed = mDNStrue; + } + } + +exit: + if (failed) abort(); } +#endif #endif // TARGET_OS_EMBEDDED -- cgit v1.2.3