2 * Copyright (c) 2016-2019 Apple Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #if MDNSRESPONDER_SUPPORTS(APPLE, METRICS)
20 #import <CoreUtils/SoftLinking.h>
21 #import <WirelessDiagnostics/AWDDNSDomainStats.h>
22 #import <WirelessDiagnostics/AWDMDNSResponderDNSMessageSizeStats.h>
23 #import <WirelessDiagnostics/AWDMDNSResponderDNSStatistics.h>
24 #import <WirelessDiagnostics/AWDMDNSResponderServicesStats.h>
25 #import <WirelessDiagnostics/AWDMetricIds_MDNSResponder.h>
26 #import <WirelessDiagnostics/WirelessDiagnostics.h>
29 #import "mDNSMacOSX.h"
30 #import "DebugServices.h"
32 //===========================================================================================================================
33 // External Frameworks
34 //===========================================================================================================================
36 SOFT_LINK_FRAMEWORK(PrivateFrameworks, WirelessDiagnostics)
38 // AWDServerConnection class
40 SOFT_LINK_CLASS(WirelessDiagnostics, AWDServerConnection)
42 #define AWDServerConnectionSoft getAWDServerConnectionClass()
44 // Classes for query stats
46 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSStatistics)
47 SOFT_LINK_CLASS(WirelessDiagnostics, AWDDNSDomainStats)
49 #define AWDMDNSResponderDNSStatisticsSoft getAWDMDNSResponderDNSStatisticsClass()
50 #define AWDDNSDomainStatsSoft getAWDDNSDomainStatsClass()
52 // Classes for services stats
54 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMetricManager)
56 #define AWDMetricManagerSoft getAWDMetricManagerClass()
58 // Classes for DNS message size stats
60 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSMessageSizeStats)
62 #define AWDMDNSResponderDNSMessageSizeStatsSoft getAWDMDNSResponderDNSMessageSizeStatsClass()
64 //===========================================================================================================================
66 //===========================================================================================================================
68 #define countof(X) (sizeof(X) / sizeof(X[0]))
69 #define countof_field(TYPE, FIELD) countof(((TYPE *)0)->FIELD)
70 #define ForgetMem(X) do {if(*(X)) {free(*(X)); *(X) = NULL;}} while(0)
72 //===========================================================================================================================
74 //===========================================================================================================================
76 #define kQueryStatsMaxQuerySendCount 10
77 #define kQueryStatsSendCountBinCount (kQueryStatsMaxQuerySendCount + 1)
78 #define kQueryStatsLatencyBinCount 55
79 #define kQueryStatsExpiredAnswerStateCount (ExpiredAnswer_EnumCount)
80 #define kQueryStatsDNSOverTCPStateCount (DNSOverTCP_EnumCount)
82 //===========================================================================================================================
84 //===========================================================================================================================
86 // Data structures for query stats.
88 typedef struct QueryStats QueryStats;
89 typedef struct DNSHistSet DNSHistSet;
90 typedef mDNSBool (*QueryNameTest_f)(const QueryStats *inStats, const domainname *inQueryName);
94 QueryStats * next; // Pointer to next domain stats in list.
95 const char * domainStr; // Domain (see below) as a C string.
96 uint8_t * domain; // Domain for which these stats are collected.
97 const char * altDomainStr; // Alt domain string to use in the AWD version of the stats instead of domainStr.
98 DNSHistSet * nonCellular; // Query stats for queries sent over non-cellular interfaces.
99 DNSHistSet * cellular; // Query stats for queries sent over cellular interfaces.
100 QueryNameTest_f test; // Function that tests whether a given query's stats belong based on the query name.
101 int labelCount; // Number of labels in domain name. Used for domain name comparisons.
102 mDNSBool terminal; // If true and test passes, then no other QueryStats on the list should be visited.
105 check_compile_time(sizeof(QueryStats) <= 64);
107 // DNSHist contains the per domain per network type histogram data that goes in a DNSDomainStats protobuf message. See
108 // <rdar://problem/23980546> MDNSResponder.proto update.
110 // answeredQuerySendCountBins
112 // 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
113 // was sent i times. The value at index 10 is the number of times that an answered query was sent 10+ times.
115 // unansweredQuerySendCountBins
117 // 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
118 // was sent i times. The value at index 10 is the number of times that an unanswered query was sent 10+ times.
120 // responseLatencyBins
122 // An array of 55 histogram bins. Each array value is the number of DNS queries that were answered in a paricular time
123 // interval. The 55 consecutive non-overlapping time intervals have the following non-inclusive upper bounds (all values are
124 // 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,
125 // 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1500, 2000, 2500, 3000, 3500, 4000,
126 // 4500, 5000, 6000, 7000, 8000, 9000, 10000, ∞.
130 uint16_t unansweredQuerySendCountBins[kQueryStatsSendCountBinCount];
131 uint16_t unansweredQueryDurationBins[kQueryStatsLatencyBinCount];
132 uint16_t answeredQuerySendCountBins[kQueryStatsSendCountBinCount];
133 uint16_t responseLatencyBins[kQueryStatsLatencyBinCount];
134 uint16_t negAnsweredQuerySendCountBins[kQueryStatsSendCountBinCount];
135 uint16_t negResponseLatencyBins[kQueryStatsLatencyBinCount];
136 uint32_t expiredAnswerStateBins[kQueryStatsExpiredAnswerStateCount];
137 uint32_t dnsOverTCPStateBins[kQueryStatsDNSOverTCPStateCount];
141 check_compile_time(sizeof(DNSHist) <= 512);
142 check_compile_time(countof_field(DNSHist, unansweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1));
143 check_compile_time(countof_field(DNSHist, answeredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1));
144 check_compile_time(countof_field(DNSHist, negAnsweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1));
145 check_compile_time(countof_field(DNSHist, expiredAnswerStateBins) == (kQueryStatsExpiredAnswerStateCount));
146 check_compile_time(countof_field(DNSHist, dnsOverTCPStateBins) == (kQueryStatsDNSOverTCPStateCount));
148 // Important: Do not modify kResponseLatencyMsLimits because the code used to generate AWD reports expects the response
149 // latency histogram bins to observe these time interval upper bounds.
151 static const mDNSu32 kResponseLatencyMsLimits[] =
154 10, 20, 30, 40, 50, 60, 70, 80, 90,
155 100, 110, 120, 130, 140, 150, 160, 170, 180, 190,
156 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950,
157 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500,
158 5000, 6000, 7000, 8000, 9000,
162 check_compile_time(countof(kResponseLatencyMsLimits) == 54);
163 check_compile_time(countof_field(DNSHist, unansweredQueryDurationBins) == (countof(kResponseLatencyMsLimits) + 1));
164 check_compile_time(countof_field(DNSHist, responseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1));
165 check_compile_time(countof_field(DNSHist, negResponseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1));
169 DNSHist * histA; // Histogram data for queries for A resource records.
170 DNSHist * histAAAA; // Histogram data for queries for AAAA resource records.
175 const char * domainStr;
176 const char * altDomainStr;
177 QueryNameTest_f test;
182 // Data structures for DNS message size stats.
184 #define kQuerySizeBinWidth 16
185 #define kQuerySizeBinMax 512
186 #define kQuerySizeBinCount ((kQuerySizeBinMax / kQuerySizeBinWidth) + 1)
188 check_compile_time(kQuerySizeBinWidth > 0);
189 check_compile_time(kQuerySizeBinCount > 0);
190 check_compile_time((kQuerySizeBinMax % kQuerySizeBinWidth) == 0);
192 #define kResponseSizeBinWidth 16
193 #define kResponseSizeBinMax 512
194 #define kResponseSizeBinCount ((kResponseSizeBinMax / kResponseSizeBinWidth) + 1)
196 check_compile_time(kResponseSizeBinWidth > 0);
197 check_compile_time(kResponseSizeBinCount > 0);
198 check_compile_time((kResponseSizeBinMax % kResponseSizeBinWidth) == 0);
202 uint32_t querySizeBins[kQuerySizeBinCount];
203 uint32_t responseSizeBins[kResponseSizeBinCount];
205 } DNSMessageSizeStats;
207 check_compile_time(sizeof(DNSMessageSizeStats) <= 264);
209 //===========================================================================================================================
211 //===========================================================================================================================
215 mDNSlocal mStatus QueryStatsCreate(const char *inDomainStr, const char *inAltDomainStr, QueryNameTest_f inTest, mDNSBool inTerminal, QueryStats **outStats);
216 mDNSlocal void QueryStatsFree(QueryStats *inStats);
217 mDNSlocal void QueryStatsFreeList(QueryStats *inList);
218 mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, ExpiredAnswerMetric inExpiredAnswerState, DNSOverTCPMetric inDNSOverTCPState, mDNSu32 inLatencyMs, mDNSBool inForCell);
219 mDNSlocal const char * QueryStatsGetDomainString(const QueryStats *inStats);
220 mDNSlocal mDNSBool QueryStatsDomainTest(const QueryStats *inStats, const domainname *inQueryName);
221 mDNSlocal mDNSBool QueryStatsHostnameTest(const QueryStats *inStats, const domainname *inQueryName);
222 mDNSlocal mDNSBool QueryStatsContentiCloudTest(const QueryStats *inStats, const domainname *inQueryName);
223 mDNSlocal mDNSBool QueryStatsCourierPushTest(const QueryStats *inStats, const domainname *inQueryName);
225 // DNS message size stats
227 mDNSlocal mStatus DNSMessageSizeStatsCreate(DNSMessageSizeStats **outStats);
228 mDNSlocal void DNSMessageSizeStatsFree(DNSMessageSizeStats *inStats);
230 mDNSlocal mStatus CreateQueryStatsList(QueryStats **outList);
231 mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID);
232 mDNSlocal mStatus SubmitAWDMetricQueryStats(void);
233 mDNSlocal mStatus SubmitAWDMetricDNSMessageSizeStats(void);
234 mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats);
235 mDNSlocal void LogDNSHistSetToFD(int fd, const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell);
236 mDNSlocal void LogDNSHistToFD(int fd, const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType);
237 mDNSlocal void LogDNSHistSendCountsToFD(int fd, const uint16_t inSendCountBins[kQueryStatsSendCountBinCount]);
238 mDNSlocal void LogDNSHistLatenciesToFD(int fd, const uint16_t inLatencyBins[kQueryStatsLatencyBinCount]);
239 mDNSlocal void LogDNSMessageSizeStatsToFD(int fd, const uint32_t *inBins, size_t inBinCount, unsigned int inBinWidth);
241 //===========================================================================================================================
242 // Histogram Bin Helpers
243 //===========================================================================================================================
245 #define INCREMENT_BIN_DEFINITION(BIN_SIZE) \
246 mDNSlocal void IncrementBin ## BIN_SIZE (uint ## BIN_SIZE ## _t *inBin) \
248 if (*inBin < UINT ## BIN_SIZE ## _MAX) ++(*inBin); \
250 extern int _MetricsDummyVariable
252 INCREMENT_BIN_DEFINITION(16);
253 INCREMENT_BIN_DEFINITION(32);
255 // Note: The return value is the size (in number of elements) of the smallest contiguous sub-array that contains the first
256 // bin and all bins with non-zero values.
258 #define COPY_BINS_DEFINITION(BIN_SIZE) \
259 mDNSlocal size_t CopyBins ## BIN_SIZE (uint32_t *inDstBins, uint ## BIN_SIZE ## _t *inSrcBins, size_t inBinCount) \
261 if (inBinCount == 0) return (0); \
262 size_t minCount = 1; \
263 for (size_t i = 0; i < inBinCount; ++i) \
265 inDstBins[i] = inSrcBins[i]; \
266 if (inDstBins[i] > 0) minCount = i + 1; \
270 extern int _MetricsDummyVariable
272 COPY_BINS_DEFINITION(16);
273 COPY_BINS_DEFINITION(32);
275 //===========================================================================================================================
277 //===========================================================================================================================
279 static AWDServerConnection * gAWDServerConnection = nil;
280 static QueryStats * gQueryStatsList = NULL;
281 static DNSMessageSizeStats * gDNSMessageSizeStats = NULL;
283 // Important: Do not add to this list without getting privacy approval. See <rdar://problem/24155761&26397203&34763471>.
285 static const QueryStatsArgs kQueryStatsArgs[] =
287 { ".", NULL, QueryStatsDomainTest, mDNSfalse },
288 { "", "alt:*-courier.push.apple.com.", QueryStatsCourierPushTest, mDNSfalse },
289 { "apple.com.", NULL, QueryStatsDomainTest, mDNStrue },
290 { "gateway.icloud.com.", "alt:gateway.icloud.com", QueryStatsHostnameTest, mDNSfalse },
291 { "", "alt:*-content.icloud.com.", QueryStatsContentiCloudTest, mDNSfalse },
292 { "icloud.com.", NULL, QueryStatsDomainTest, mDNStrue },
293 { "mzstatic.com.", NULL, QueryStatsDomainTest, mDNStrue },
294 { "google.com.", NULL, QueryStatsDomainTest, mDNStrue },
295 { "baidu.com.", NULL, QueryStatsDomainTest, mDNStrue },
296 { "yahoo.com.", NULL, QueryStatsDomainTest, mDNStrue },
297 { "qq.com.", NULL, QueryStatsDomainTest, mDNStrue }
300 check_compile_time(countof(kQueryStatsArgs) == 11);
302 //===========================================================================================================================
304 //===========================================================================================================================
306 mStatus MetricsInit(void)
310 gAWDServerConnection = [[AWDServerConnectionSoft alloc]
311 initWithComponentId: AWDComponentId_MDNSResponder
312 andBlockOnConfiguration: NO];
314 if (gAWDServerConnection)
316 [gAWDServerConnection
317 registerQueriableMetricCallback: ^(UInt32 inMetricID)
319 SubmitAWDMetric(inMetricID);
321 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_DNSStatistics];
323 [gAWDServerConnection
324 registerQueriableMetricCallback: ^(UInt32 inMetricID)
326 SubmitAWDMetric(inMetricID);
328 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_ServicesStats];
330 [gAWDServerConnection
331 registerQueriableMetricCallback: ^(UInt32 inMetricID)
333 SubmitAWDMetric(inMetricID);
335 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_DNSMessageSizeStats];
339 LogMsg("MetricsInit: failed to create AWD server connection.");
343 if( gAWDServerConnection )
345 CreateQueryStatsList(&gQueryStatsList);
346 DNSMessageSizeStatsCreate(&gDNSMessageSizeStats);
349 return (mStatus_NoError);
352 //===========================================================================================================================
353 // MetricsUpdateDNSQueryStats
354 //===========================================================================================================================
356 mDNSexport void MetricsUpdateDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, ExpiredAnswerMetric inExpiredAnswerState, DNSOverTCPMetric inDNSOverTCPState, mDNSu32 inLatencyMs, mDNSBool inForCell)
361 require_quiet(gAWDServerConnection, exit);
362 require_quiet((inType == kDNSType_A) || (inType == kDNSType_AAAA), exit);
364 for (stats = gQueryStatsList; stats; stats = stats->next)
366 match = stats->test(stats, inQueryName);
369 QueryStatsUpdate(stats, inType, inRR, inSendCount, inExpiredAnswerState, inDNSOverTCPState, inLatencyMs, inForCell);
370 if (stats->terminal) break;
378 //===========================================================================================================================
379 // MetricsUpdateDNSQuerySize
380 //===========================================================================================================================
382 mDNSlocal void UpdateMessageSizeCounts(uint32_t *inBins, size_t inBinCount, unsigned int inBinWidth, uint32_t inSize);
384 mDNSexport void MetricsUpdateDNSQuerySize(mDNSu32 inSize)
386 if (!gDNSMessageSizeStats) return;
387 UpdateMessageSizeCounts(gDNSMessageSizeStats->querySizeBins, kQuerySizeBinCount, kQuerySizeBinWidth, inSize);
390 mDNSlocal void UpdateMessageSizeCounts(uint32_t *inBins, size_t inBinCount, unsigned int inBinWidth, uint32_t inSize)
394 if (inSize == 0) return;
395 i = (inSize - 1) / inBinWidth;
396 if (i >= inBinCount) i = inBinCount - 1;
397 IncrementBin32(&inBins[i]);
400 //===========================================================================================================================
401 // MetricsUpdateDNSResponseSize
402 //===========================================================================================================================
404 mDNSexport void MetricsUpdateDNSResponseSize(mDNSu32 inSize)
406 if (!gDNSMessageSizeStats) return;
407 UpdateMessageSizeCounts(gDNSMessageSizeStats->responseSizeBins, kResponseSizeBinCount, kResponseSizeBinWidth, inSize);
410 //===========================================================================================================================
412 //===========================================================================================================================
414 mDNSexport void LogMetricsToFD(int fd)
418 LogToFD(fd, "gAWDServerConnection %p", gAWDServerConnection);
419 LogToFD(fd, "---- DNS query stats by domain -----");
421 for (stats = gQueryStatsList; stats; stats = stats->next)
423 if (!stats->nonCellular && !stats->cellular)
425 LogToFD(fd, "No data for %s", QueryStatsGetDomainString(stats));
428 if (stats->nonCellular) LogDNSHistSetToFD(fd, stats->nonCellular, QueryStatsGetDomainString(stats), mDNSfalse);
429 if (stats->cellular) LogDNSHistSetToFD(fd, stats->cellular, QueryStatsGetDomainString(stats), mDNStrue);
432 LogToFD(fd, "---- Num of Services Registered -----");
433 LogToFD(fd, "Current_number_of_services_registered :[%d], Max_number_of_services_registered :[%d]",
434 curr_num_regservices, max_num_regservices);
436 if (gDNSMessageSizeStats)
438 LogToFD(fd, "---- DNS query size stats ---");
439 LogDNSMessageSizeStatsToFD(fd, gDNSMessageSizeStats->querySizeBins, kQuerySizeBinCount, kQuerySizeBinWidth);
441 LogToFD(fd, "-- DNS response size stats --");
442 LogDNSMessageSizeStatsToFD(fd, gDNSMessageSizeStats->responseSizeBins, kResponseSizeBinCount, kResponseSizeBinWidth);
446 LogToFD(fd, "No DNS message size stats.");
450 //===========================================================================================================================
452 //===========================================================================================================================
454 mDNSlocal mStatus StringToDomainName(const char *inString, uint8_t **outDomainName);
456 mDNSlocal mStatus QueryStatsCreate(const char *inDomainStr, const char *inAltDomainStr, QueryNameTest_f inTest, mDNSBool inTerminal, QueryStats **outStats)
461 obj = (QueryStats *)calloc(1, sizeof(*obj));
462 require_action_quiet(obj, exit, err = mStatus_NoMemoryErr);
464 obj->domainStr = inDomainStr;
465 err = StringToDomainName(obj->domainStr, &obj->domain);
466 require_noerr_quiet(err, exit);
468 obj->altDomainStr = inAltDomainStr;
470 obj->labelCount = CountLabels((const domainname *)obj->domain);
471 obj->terminal = inTerminal;
475 err = mStatus_NoError;
478 if (obj) QueryStatsFree(obj);
482 mDNSlocal mStatus StringToDomainName(const char *inString, uint8_t **outDomainName)
485 uint8_t * domainPtr = NULL;
490 if (strcmp(inString, ".") == 0)
496 ptr = MakeDomainNameFromDNSNameString(&domain, inString);
497 require_action_quiet(ptr, exit, err = mStatus_BadParamErr);
499 domainLen = DomainNameLength(&domain);
501 domainPtr = (uint8_t *)malloc(domainLen);
502 require_action_quiet(domainPtr, exit, err = mStatus_NoMemoryErr);
504 memcpy(domainPtr, domain.c, domainLen);
506 *outDomainName = domainPtr;
508 err = mStatus_NoError;
514 //===========================================================================================================================
516 //===========================================================================================================================
518 mDNSlocal void QueryStatsFree(QueryStats *inStats)
520 ForgetMem(&inStats->domain);
521 if (inStats->nonCellular)
523 ForgetMem(&inStats->nonCellular->histA);
524 ForgetMem(&inStats->nonCellular->histAAAA);
525 free(inStats->nonCellular);
526 inStats->nonCellular = NULL;
528 if (inStats->cellular)
530 ForgetMem(&inStats->cellular->histA);
531 ForgetMem(&inStats->cellular->histAAAA);
532 free(inStats->cellular);
533 inStats->cellular = NULL;
538 //===========================================================================================================================
539 // QueryStatsFreeList
540 //===========================================================================================================================
542 mDNSlocal void QueryStatsFreeList(QueryStats *inList)
546 while ((stats = inList) != NULL)
548 inList = stats->next;
549 QueryStatsFree(stats);
553 //===========================================================================================================================
555 //===========================================================================================================================
557 mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, ExpiredAnswerMetric inExpiredAnswerState, DNSOverTCPMetric inDNSOverTCPState, mDNSu32 inLatencyMs, mDNSBool inForCell)
566 require_action_quiet(inRR || (inQuerySendCount > 0), exit, err = mStatus_NoError);
567 require_action_quiet((inType == kDNSType_A) || (inType == kDNSType_AAAA), exit, err = mStatus_NoError);
569 pSet = inForCell ? &inStats->cellular : &inStats->nonCellular;
570 if ((set = *pSet) == NULL)
572 set = (DNSHistSet *)calloc(1, sizeof(*set));
573 require_action_quiet(set, exit, err = mStatus_NoMemoryErr);
576 pHist = (inType == kDNSType_A) ? &set->histA : &set->histAAAA;
577 if ((hist = *pHist) == NULL)
579 hist = (DNSHist *)calloc(1, sizeof(*hist));
580 require_action_quiet(hist, exit, err = mStatus_NoMemoryErr);
586 uint16_t * sendCountBins;
587 uint16_t * latencyBins;
588 const mDNSBool isNegative = (inRR->RecordType == kDNSRecordTypePacketNegative);
590 i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount);
592 sendCountBins = isNegative ? hist->negAnsweredQuerySendCountBins : hist->answeredQuerySendCountBins;
593 IncrementBin16(&sendCountBins[i]);
595 if (inQuerySendCount > 0)
597 for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {}
598 latencyBins = isNegative ? hist->negResponseLatencyBins : hist->responseLatencyBins;
599 IncrementBin16(&latencyBins[i]);
604 i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount);
605 IncrementBin16(&hist->unansweredQuerySendCountBins[i]);
607 for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {}
608 IncrementBin16(&hist->unansweredQueryDurationBins[i]);
610 IncrementBin32(&hist->expiredAnswerStateBins[Min(inExpiredAnswerState, (kQueryStatsExpiredAnswerStateCount - 1))]);
611 IncrementBin32(&hist->dnsOverTCPStateBins[Min(inDNSOverTCPState, (kQueryStatsDNSOverTCPStateCount - 1))]);
612 err = mStatus_NoError;
618 //===========================================================================================================================
619 // QueryStatsGetDomainString
620 //===========================================================================================================================
622 mDNSlocal const char * QueryStatsGetDomainString(const QueryStats *inStats)
624 return (inStats->altDomainStr ? inStats->altDomainStr : inStats->domainStr);
627 //===========================================================================================================================
628 // QueryStatsDomainTest
629 //===========================================================================================================================
631 mDNSlocal mDNSBool QueryStatsDomainTest(const QueryStats *inStats, const domainname *inQueryName)
633 const domainname * parentDomain;
636 if (inStats->domain[0] == 0) return (mDNStrue);
638 labelCount = CountLabels(inQueryName);
639 if (labelCount < inStats->labelCount) return (mDNSfalse);
641 parentDomain = SkipLeadingLabels(inQueryName, labelCount - inStats->labelCount);
642 return (SameDomainName(parentDomain, (const domainname *)inStats->domain));
645 //===========================================================================================================================
646 // QueryStatsHostnameTest
647 //===========================================================================================================================
649 mDNSlocal mDNSBool QueryStatsHostnameTest(const QueryStats *inStats, const domainname *inQueryName)
651 return (SameDomainName(inQueryName, (const domainname *)inStats->domain));
654 //===========================================================================================================================
655 // QueryStatsContentiCloudTest
656 //===========================================================================================================================
658 mDNSlocal const uint8_t *LocateLabelSuffix(const uint8_t *inLabel, const uint8_t *inSuffixPtr, size_t inSuffixLen);
660 #define kContentSuffixStr "-content"
662 mDNSlocal mDNSBool QueryStatsContentiCloudTest(const QueryStats *inStats, const domainname *inQueryName)
664 const mDNSu8 * const firstLabel = inQueryName->c;
665 const uint8_t * suffix;
666 const domainname * parentDomain;
669 (void) inStats; // Unused.
671 labelCount = CountLabels(inQueryName);
672 if (labelCount != 3) return (mDNSfalse);
674 suffix = LocateLabelSuffix(firstLabel, (const uint8_t *)kContentSuffixStr, sizeof_string(kContentSuffixStr));
675 if (suffix && (suffix > &firstLabel[1]))
677 parentDomain = SkipLeadingLabels(inQueryName, 1);
678 if (SameDomainName(parentDomain, (const domainname *)"\x6" "icloud" "\x3" "com"))
687 mDNSlocal const uint8_t *LocateLabelSuffix(const uint8_t *inLabel, const uint8_t *inSuffixPtr, size_t inSuffixLen)
693 const size_t labelLen = inLabel[0];
695 if (labelLen < inSuffixLen) return (NULL);
697 ptr = &inLabel[1 + labelLen - inSuffixLen];
700 for (len = inSuffixLen; len > 0; --len)
702 if (tolower(*lp) != tolower(*sp)) return (NULL);
710 //===========================================================================================================================
711 // QueryStatsCourierPushTest
712 //===========================================================================================================================
714 #define kCourierSuffixStr "-courier"
716 mDNSlocal mDNSBool QueryStatsCourierPushTest(const QueryStats *inStats, const domainname *inQueryName)
718 const mDNSu8 * const firstLabel = inQueryName->c;
719 const uint8_t * suffix;
721 const domainname * parentDomain;
724 (void) inStats; // Unused.
726 labelCount = CountLabels(inQueryName);
727 if (labelCount != 4) return (mDNSfalse);
729 suffix = LocateLabelSuffix(firstLabel, (const mDNSu8 *)kCourierSuffixStr, sizeof_string(kCourierSuffixStr));
730 if (suffix && (suffix > &firstLabel[1]))
732 for (ptr = &firstLabel[1]; ptr < suffix; ++ptr)
734 if (!isdigit(*ptr)) break;
738 parentDomain = SkipLeadingLabels(inQueryName, 1);
739 if (SameDomainName(parentDomain, (const domainname *)"\x4" "push" "\x5" "apple" "\x3" "com"))
749 //===========================================================================================================================
750 // DNSMessageSizeStatsCreate
751 //===========================================================================================================================
753 mDNSlocal mStatus DNSMessageSizeStatsCreate(DNSMessageSizeStats **outStats)
756 DNSMessageSizeStats * stats;
758 stats = (DNSMessageSizeStats *)calloc(1, sizeof(*stats));
759 require_action_quiet(stats, exit, err = mStatus_NoMemoryErr);
762 err = mStatus_NoError;
768 //===========================================================================================================================
769 // DNSMessageSizeStatsFree
770 //===========================================================================================================================
772 mDNSlocal void DNSMessageSizeStatsFree(DNSMessageSizeStats *inStats)
777 //===========================================================================================================================
778 // CreateQueryStatsList
779 //===========================================================================================================================
781 mDNSlocal mStatus CreateQueryStatsList(QueryStats **outList)
786 const QueryStatsArgs * args;
787 const QueryStatsArgs * const end = kQueryStatsArgs + countof(kQueryStatsArgs);
788 QueryStats * list = NULL;
791 for (args = kQueryStatsArgs; args < end; ++args)
793 err = QueryStatsCreate(args->domainStr, args->altDomainStr, args->test, args->terminal, &stats);
794 require_noerr_quiet(err, exit);
802 err = mStatus_NoError;
805 QueryStatsFreeList(list);
809 //===========================================================================================================================
811 //===========================================================================================================================
813 mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID)
819 case AWDMetricId_MDNSResponder_DNSStatistics:
820 err = SubmitAWDMetricQueryStats();
823 case AWDMetricId_MDNSResponder_ServicesStats:
824 [AWDMetricManagerSoft postMetricWithId:AWDMetricId_MDNSResponder_ServicesStats unsignedIntegerValue:max_num_regservices];
826 // reset the no of max services since we want to collect the max no of services registered per AWD submission period
827 max_num_regservices = curr_num_regservices;
828 KQueueUnlock("SubmitAWDSimpleMetricServiceStats");
829 err = mStatus_NoError;
832 case AWDMetricId_MDNSResponder_DNSMessageSizeStats:
833 err = SubmitAWDMetricDNSMessageSizeStats();
837 err = mStatus_UnsupportedErr;
841 if (err) LogMsg("SubmitAWDMetric for metric ID 0x%08X failed with error %d", inMetricID, err);
845 //===========================================================================================================================
846 // SubmitAWDMetricQueryStats
847 //===========================================================================================================================
849 mDNSlocal mStatus AddQueryStats(AWDMDNSResponderDNSStatistics *inMetric, const QueryStats *inStats);
850 mDNSlocal mStatus AddDNSHistSet(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell);
852 mDNSlocal mStatus SubmitAWDMetricQueryStats(void)
857 QueryStats * statsList;
858 QueryStats * newStatsList;
859 AWDMetricContainer * container = nil;
860 AWDMDNSResponderDNSStatistics * metric = nil;
863 CreateQueryStatsList(&newStatsList);
866 statsList = gQueryStatsList;
867 gQueryStatsList = newStatsList;
868 KQueueUnlock("SubmitAWDMetricQueryStats");
870 container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_DNSStatistics];
871 require_action_quiet(container, exit, err = mStatus_UnknownErr);
873 metric = [[AWDMDNSResponderDNSStatisticsSoft alloc] init];
874 require_action_quiet(metric, exit, err = mStatus_UnknownErr);
876 while ((stats = statsList) != NULL)
878 err = AddQueryStats(metric, stats);
879 require_noerr_quiet(err, exit);
881 statsList = stats->next;
882 QueryStatsFree(stats);
885 container.metric = metric;
886 success = [gAWDServerConnection submitMetric:container];
887 LogMsg("SubmitAWDMetricQueryStats: metric submission %s.", success ? "succeeded" : "failed");
888 err = success ? mStatus_NoError : mStatus_UnknownErr;
891 QueryStatsFreeList(statsList);
895 mDNSlocal mStatus AddQueryStats(AWDMDNSResponderDNSStatistics *inMetric, const QueryStats *inStats)
899 if (inStats->nonCellular)
901 err = AddDNSHistSet(inMetric, inStats->nonCellular, QueryStatsGetDomainString(inStats), mDNSfalse);
902 require_noerr_quiet(err, exit);
904 if (inStats->cellular)
906 err = AddDNSHistSet(inMetric, inStats->cellular, QueryStatsGetDomainString(inStats), mDNStrue);
907 require_noerr_quiet(err, exit);
909 err = mStatus_NoError;
915 mDNSlocal mStatus AddDNSHistSet(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell)
918 AWDDNSDomainStats * awdStats;
922 err = CreateAWDDNSDomainStats(inSet->histA, inDomain, inForCell, AWDDNSDomainStats_RecordType_A, &awdStats);
923 require_noerr_quiet(err, exit);
925 [inMetric addStats:awdStats];
929 err = CreateAWDDNSDomainStats(inSet->histAAAA, inDomain, inForCell, AWDDNSDomainStats_RecordType_AAAA, &awdStats);
930 require_noerr_quiet(err, exit);
932 [inMetric addStats:awdStats];
934 err = mStatus_NoError;
940 //===========================================================================================================================
941 // SubmitAWDMetricDNSMessageSizeStats
942 //===========================================================================================================================
944 mDNSlocal mStatus SubmitAWDMetricDNSMessageSizeStats(void)
947 DNSMessageSizeStats * stats;
948 DNSMessageSizeStats * newStats;
949 AWDMetricContainer * container;
950 AWDMDNSResponderDNSMessageSizeStats * metric = nil;
954 DNSMessageSizeStatsCreate(&newStats);
957 stats = gDNSMessageSizeStats;
958 gDNSMessageSizeStats = newStats;
959 KQueueUnlock("SubmitAWDMetricDNSMessageSizeStats");
961 container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_DNSMessageSizeStats];
962 require_action_quiet(container, exit, err = mStatus_UnknownErr);
964 metric = [[AWDMDNSResponderDNSMessageSizeStatsSoft alloc] init];
965 require_action_quiet(metric, exit, err = mStatus_UnknownErr);
970 uint32_t bins[Max(kQuerySizeBinCount, kResponseSizeBinCount)];
972 // Set query size counts.
974 binCount = CopyBins32(bins, stats->querySizeBins, kQuerySizeBinCount);
975 [metric setQuerySizeCounts:bins count:(NSUInteger)binCount];
977 // Set response size counts.
979 binCount = CopyBins32(bins, stats->responseSizeBins, kResponseSizeBinCount);
980 [metric setResponseSizeCounts:bins count:(NSUInteger)binCount];
983 container.metric = metric;
984 success = [gAWDServerConnection submitMetric:container];
985 LogMsg("SubmitAWDMetricDNSMessageSizeStats: metric submission %s.", success ? "succeeded" : "failed");
986 err = success ? mStatus_NoError : mStatus_UnknownErr;
989 if (stats) DNSMessageSizeStatsFree(stats);
993 //===========================================================================================================================
994 // CreateAWDDNSDomainStats
995 //===========================================================================================================================
997 mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats)
1000 AWDDNSDomainStats * awdStats = nil;
1001 NSString * domain = nil;
1003 uint32_t sendCountBins[kQueryStatsSendCountBinCount];
1004 uint32_t latencyBins[kQueryStatsLatencyBinCount];
1005 uint32_t expiredAnswerBins[kQueryStatsExpiredAnswerStateCount];
1006 uint32_t dnsOverTCPBins[kQueryStatsDNSOverTCPStateCount];
1008 awdStats = [[AWDDNSDomainStatsSoft alloc] init];
1009 require_action_quiet(awdStats, exit, err = mStatus_UnknownErr);
1011 domain = [[NSString alloc] initWithUTF8String:inDomain];
1012 require_action_quiet(domain, exit, err = mStatus_UnknownErr);
1014 awdStats.domain = domain;
1015 awdStats.networkType = inForCell ? AWDDNSDomainStats_NetworkType_Cellular : AWDDNSDomainStats_NetworkType_NonCellular;
1016 awdStats.recordType = inType;
1018 // Positively answered query send counts
1020 binCount = CopyBins16(sendCountBins, inHist->answeredQuerySendCountBins, kQueryStatsSendCountBinCount);
1021 [awdStats setAnsweredQuerySendCounts:sendCountBins count:(NSUInteger)binCount];
1023 // binCount > 1 means that at least one of the non-zero send count bins had a non-zero count, i.e., at least one query
1024 // was sent out on the wire. In that case, include the associated latency bins as well.
1028 binCount = CopyBins16(latencyBins, inHist->responseLatencyBins, kQueryStatsLatencyBinCount);
1029 [awdStats setResponseLatencyMs:latencyBins count:(NSUInteger)binCount];
1032 // Negatively answered query send counts
1034 binCount = CopyBins16(sendCountBins, inHist->negAnsweredQuerySendCountBins, kQueryStatsSendCountBinCount);
1035 [awdStats setNegAnsweredQuerySendCounts:sendCountBins count:(NSUInteger)binCount];
1039 binCount = CopyBins16(latencyBins, inHist->negResponseLatencyBins, kQueryStatsLatencyBinCount);
1040 [awdStats setNegResponseLatencyMs:latencyBins count:(NSUInteger)binCount];
1043 // Unanswered query send counts
1045 binCount = CopyBins16(sendCountBins, inHist->unansweredQuerySendCountBins, kQueryStatsSendCountBinCount);
1046 [awdStats setUnansweredQuerySendCounts:sendCountBins count:(NSUInteger)binCount];
1050 binCount = CopyBins16(latencyBins, inHist->unansweredQueryDurationBins, kQueryStatsLatencyBinCount);
1051 [awdStats setUnansweredQueryDurationMs:latencyBins count:(NSUInteger)binCount];
1054 // Expired answers states
1056 binCount = CopyBins32(expiredAnswerBins, inHist->expiredAnswerStateBins, kQueryStatsExpiredAnswerStateCount);
1057 [awdStats setExpiredAnswerStates:expiredAnswerBins count:(NSUInteger)binCount];
1059 // DNS Over TCP states
1061 binCount = CopyBins32(dnsOverTCPBins, inHist->dnsOverTCPStateBins, kQueryStatsDNSOverTCPStateCount);
1062 [awdStats setDnsOverTCPStates:dnsOverTCPBins count:(NSUInteger)binCount];
1064 *outStats = awdStats;
1065 err = mStatus_NoError;
1071 //===========================================================================================================================
1072 // LogDNSHistSetToFD
1073 //===========================================================================================================================
1075 mDNSlocal void LogDNSHistSetToFD(int fd, const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell)
1077 if (inSet->histA) LogDNSHistToFD(fd, inSet->histA, inDomain, inForCell, "A");
1078 if (inSet->histAAAA) LogDNSHistToFD(fd, inSet->histAAAA, inDomain, inForCell, "AAAA");
1081 //===========================================================================================================================
1083 //===========================================================================================================================
1085 #define Percent(N, D) (((N) * 100) / (D)), ((((N) * 10000) / (D)) % 100)
1086 #define PercentFmt "%3u.%02u"
1087 #define LogStatToFD(FILE_DESCRIPTOR, LABEL, COUNT, ACCUMULATOR, TOTAL) \
1088 LogToFD((FILE_DESCRIPTOR), "%s %5u " PercentFmt " " PercentFmt, (LABEL), (COUNT), Percent(COUNT, TOTAL), Percent(ACCUMULATOR, TOTAL))
1090 mDNSlocal void LogDNSHistToFD(int fd, const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType)
1092 unsigned int totalAnswered;
1093 unsigned int totalNegAnswered;
1094 unsigned int totalUnanswered;
1098 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1100 totalAnswered += inHist->answeredQuerySendCountBins[i];
1103 totalNegAnswered = 0;
1104 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1106 totalNegAnswered += inHist->negAnsweredQuerySendCountBins[i];
1109 totalUnanswered = 0;
1110 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1112 totalUnanswered += inHist->unansweredQuerySendCountBins[i];
1115 LogToFD(fd, "Domain: %s (%s, %s)", inDomain, inForCell ? "C" : "NC", inType);
1116 LogToFD(fd, "Answered questions %10u", totalAnswered);
1117 LogToFD(fd, "Negatively answered questions %10u", totalNegAnswered);
1118 LogToFD(fd, "Unanswered questions %10u", totalUnanswered);
1119 LogToFD(fd, "Expired - no cached answer %10u", inHist->expiredAnswerStateBins[ExpiredAnswer_Allowed]);
1120 LogToFD(fd, "Expired - answered from cache %10u", inHist->expiredAnswerStateBins[ExpiredAnswer_AnsweredWithCache]);
1121 LogToFD(fd, "Expired - answered expired %10u", inHist->expiredAnswerStateBins[ExpiredAnswer_AnsweredWithExpired]);
1122 LogToFD(fd, "Expired - cache changed %10u", inHist->expiredAnswerStateBins[ExpiredAnswer_ExpiredAnswerChanged]);
1123 LogToFD(fd, "DNSoTCP - truncated %10u", inHist->dnsOverTCPStateBins[DNSOverTCP_Truncated]);
1124 LogToFD(fd, "DNSoTCP - suspicious %10u", inHist->dnsOverTCPStateBins[DNSOverTCP_Suspicious]);
1125 LogToFD(fd, "DNSoTCP - suspicious defense %10u", inHist->dnsOverTCPStateBins[DNSOverTCP_SuspiciousDefense]);
1126 LogToFD(fd, "-- Query send counts ---------");
1127 LogDNSHistSendCountsToFD(fd, inHist->answeredQuerySendCountBins);
1128 LogToFD(fd, "-- Query send counts (NAQs) --");
1129 LogDNSHistSendCountsToFD(fd, inHist->negAnsweredQuerySendCountBins);
1131 if (totalAnswered > inHist->answeredQuerySendCountBins[0])
1133 LogToFD(fd, "--- Response times -----------");
1134 LogDNSHistLatenciesToFD(fd, inHist->responseLatencyBins);
1137 if (totalNegAnswered > inHist->negAnsweredQuerySendCountBins[0])
1139 LogToFD(fd, "--- Response times (NAQs) ----");
1140 LogDNSHistLatenciesToFD(fd, inHist->negResponseLatencyBins);
1143 if (totalUnanswered > 0)
1145 LogToFD(fd, "--- Unanswered query times ---");
1146 LogDNSHistLatenciesToFD(fd, inHist->unansweredQueryDurationBins);
1150 //===========================================================================================================================
1151 // LogDNSHistSendCountsToFD
1152 //===========================================================================================================================
1154 mDNSlocal void LogDNSHistSendCountsToFD(int fd, const uint16_t inSendCountBins[kQueryStatsSendCountBinCount])
1161 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1163 total += inSendCountBins[i];
1168 uint32_t accumulator = 0;
1170 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1172 accumulator += inSendCountBins[i];
1173 if (i < (kQueryStatsSendCountBinCount - 1))
1175 snprintf(label, sizeof(label), "%2d ", i);
1179 snprintf(label, sizeof(label), "%2d+", i);
1181 LogStatToFD(fd, label, inSendCountBins[i], accumulator, total);
1182 if (accumulator == total) break;
1187 LogToFD(fd, "No data.");
1191 //===========================================================================================================================
1192 // LogDNSHistLatenciesToFD
1193 //===========================================================================================================================
1195 mDNSlocal void LogDNSHistLatenciesToFD(int fd,
1196 const uint16_t inLatencyBins[kQueryStatsLatencyBinCount])
1203 for (i = 0; i < kQueryStatsLatencyBinCount; ++i)
1205 total += inLatencyBins[i];
1210 uint32_t accumulator = 0;
1212 for (i = 0; i < kQueryStatsLatencyBinCount; ++i)
1214 accumulator += inLatencyBins[i];
1215 if (i < (int)countof(kResponseLatencyMsLimits))
1217 snprintf(label, sizeof(label), "< %5u ms", kResponseLatencyMsLimits[i]);
1221 snprintf(label, sizeof(label), "< ∞ ms");
1223 LogStatToFD(fd, label, inLatencyBins[i], accumulator, total);
1224 if (accumulator == total) break;
1229 LogToFD(fd, "No data.");
1233 //===========================================================================================================================
1234 // LogDNSMessageSizeStatsToFD
1235 //===========================================================================================================================
1237 mDNSlocal void LogDNSMessageSizeStatsToFD(int fd, const uint32_t *inBins, size_t inBinCount, unsigned int inBinWidth)
1243 for (i = 0; i < inBinCount; ++i)
1250 uint32_t accumulator;
1251 unsigned int lower, upper;
1256 for (i = 0; i < inBinCount; ++i)
1258 accumulator += inBins[i];
1260 if (i < (inBinCount - 1))
1262 upper += inBinWidth;
1263 snprintf(label, sizeof(label), "%3u - %-3u", lower, upper);
1267 snprintf(label, sizeof(label), "%3u+ ", lower);
1269 LogStatToFD(fd, label, inBins[i], accumulator, total);
1270 if (accumulator == total) break;
1275 LogToFD(fd, "No data.");
1279 #endif // MDNSRESPONDER_SUPPORTS(APPLE, METRICS)