]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/Metrics.m
mDNSResponder-878.200.35.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / Metrics.m
1 /*
2 * Copyright (c) 2016-2017 Apple Inc. All rights reserved.
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 #import "Metrics.h"
18
19 #if (TARGET_OS_IOS)
20 #import <CoreUtils/SoftLinking.h>
21 #import <WirelessDiagnostics/AWDDNSDomainStats.h>
22 #import <WirelessDiagnostics/AWDMDNSResponderDNSMessageSizeStats.h>
23 #import <WirelessDiagnostics/AWDMDNSResponderDNSStatistics.h>
24 #import <WirelessDiagnostics/AWDMDNSResponderResolveStats.h>
25 #import <WirelessDiagnostics/AWDMDNSResponderResolveStatsDNSServer.h>
26 #import <WirelessDiagnostics/AWDMDNSResponderResolveStatsDomain.h>
27 #import <WirelessDiagnostics/AWDMDNSResponderResolveStatsHostname.h>
28 #import <WirelessDiagnostics/AWDMDNSResponderResolveStatsResult.h>
29 #import <WirelessDiagnostics/AWDMDNSResponderServicesStats.h>
30 #import <WirelessDiagnostics/AWDMetricIds_MDNSResponder.h>
31 #import <WirelessDiagnostics/WirelessDiagnostics.h>
32
33 #import "DNSCommon.h"
34 #import "mDNSMacOSX.h"
35 #import "DebugServices.h"
36
37 //===========================================================================================================================
38 // External Frameworks
39 //===========================================================================================================================
40
41 SOFT_LINK_FRAMEWORK(PrivateFrameworks, WirelessDiagnostics)
42
43 // AWDServerConnection class
44
45 SOFT_LINK_CLASS(WirelessDiagnostics, AWDServerConnection)
46
47 #define AWDServerConnectionSoft getAWDServerConnectionClass()
48
49 // Classes for query stats
50
51 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSStatistics)
52 SOFT_LINK_CLASS(WirelessDiagnostics, AWDDNSDomainStats)
53
54 #define AWDMDNSResponderDNSStatisticsSoft getAWDMDNSResponderDNSStatisticsClass()
55 #define AWDDNSDomainStatsSoft getAWDDNSDomainStatsClass()
56
57 // Classes for resolve stats
58
59 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStats)
60 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsDNSServer)
61 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsDomain)
62 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsHostname)
63 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsResult)
64
65 #define AWDMDNSResponderResolveStatsSoft getAWDMDNSResponderResolveStatsClass()
66 #define AWDMDNSResponderResolveStatsDNSServerSoft getAWDMDNSResponderResolveStatsDNSServerClass()
67 #define AWDMDNSResponderResolveStatsDomainSoft getAWDMDNSResponderResolveStatsDomainClass()
68 #define AWDMDNSResponderResolveStatsHostnameSoft getAWDMDNSResponderResolveStatsHostnameClass()
69 #define AWDMDNSResponderResolveStatsResultSoft getAWDMDNSResponderResolveStatsResultClass()
70
71 // Classes for services stats
72
73 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMetricManager)
74
75 #define AWDMetricManagerSoft getAWDMetricManagerClass()
76
77 // Classes for DNS message size stats
78
79 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSMessageSizeStats)
80
81 #define AWDMDNSResponderDNSMessageSizeStatsSoft getAWDMDNSResponderDNSMessageSizeStatsClass()
82
83 //===========================================================================================================================
84 // Macros
85 //===========================================================================================================================
86
87 #define countof(X) (sizeof(X) / sizeof(X[0]))
88 #define countof_field(TYPE, FIELD) countof(((TYPE *)0)->FIELD)
89 #define increment_saturate(VAR, MAX) do {if ((VAR) < (MAX)) {++(VAR);}} while (0)
90 #define ForgetMem(X) do {if(*(X)) {free(*(X)); *(X) = NULL;}} while(0)
91
92 //===========================================================================================================================
93 // Constants
94 //===========================================================================================================================
95
96 #define kQueryStatsMaxQuerySendCount 10
97 #define kQueryStatsSendCountBinCount (kQueryStatsMaxQuerySendCount + 1)
98 #define kQueryStatsLatencyBinCount 55
99 #define kQueryStatsExpiredAnswerStateCount (ExpiredAnswer_EnumCount)
100 #define kResolveStatsMaxObjCount 2000
101
102 //===========================================================================================================================
103 // Data structures
104 //===========================================================================================================================
105
106 // Data structures for query stats.
107
108 typedef struct QueryStats QueryStats;
109 typedef struct DNSHistSet DNSHistSet;
110 typedef mDNSBool (*QueryNameTest_f)(const QueryStats *inStats, const domainname *inQueryName);
111
112 struct QueryStats
113 {
114 QueryStats * next; // Pointer to next domain stats in list.
115 const char * domainStr; // Domain (see below) as a C string.
116 uint8_t * domain; // Domain for which these stats are collected.
117 const char * altDomainStr; // Alt domain string to use in the AWD version of the stats instead of domainStr.
118 DNSHistSet * nonCellular; // Query stats for queries sent over non-cellular interfaces.
119 DNSHistSet * cellular; // Query stats for queries sent over cellular interfaces.
120 QueryNameTest_f test; // Function that tests whether a given query's stats belong based on the query name.
121 int labelCount; // Number of labels in domain name. Used for domain name comparisons.
122 mDNSBool terminal; // If true and test passes, then no other QueryStats on the list should be visited.
123 };
124
125 check_compile_time(sizeof(QueryStats) <= 64);
126
127 // DNSHist contains the per domain per network type histogram data that goes in a DNSDomainStats protobuf message. See
128 // <rdar://problem/23980546> MDNSResponder.proto update.
129 //
130 // answeredQuerySendCountBins
131 //
132 // 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
133 // was sent i times. The value at index 10 is the number of times that an answered query was sent 10+ times.
134 //
135 // unansweredQuerySendCountBins
136 //
137 // 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
138 // was sent i times. The value at index 10 is the number of times that an unanswered query was sent 10+ times.
139 //
140 // responseLatencyBins
141 //
142 // An array of 55 histogram bins. Each array value is the number of DNS queries that were answered in a paricular time
143 // interval. The 55 consecutive non-overlapping time intervals have the following non-inclusive upper bounds (all values are
144 // 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,
145 // 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1500, 2000, 2500, 3000, 3500, 4000,
146 // 4500, 5000, 6000, 7000, 8000, 9000, 10000, ∞.
147
148 typedef struct
149 {
150 uint16_t unansweredQuerySendCountBins[kQueryStatsSendCountBinCount];
151 uint16_t unansweredQueryDurationBins[kQueryStatsLatencyBinCount];
152 uint16_t answeredQuerySendCountBins[kQueryStatsSendCountBinCount];
153 uint16_t responseLatencyBins[kQueryStatsLatencyBinCount];
154 uint16_t negAnsweredQuerySendCountBins[kQueryStatsSendCountBinCount];
155 uint16_t negResponseLatencyBins[kQueryStatsLatencyBinCount];
156 uint16_t expiredAnswerStateBins[kQueryStatsExpiredAnswerStateCount];
157
158 } DNSHist;
159
160 check_compile_time(sizeof(DNSHist) <= 512);
161 check_compile_time(countof_field(DNSHist, unansweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1));
162 check_compile_time(countof_field(DNSHist, answeredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1));
163 check_compile_time(countof_field(DNSHist, negAnsweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1));
164 check_compile_time(countof_field(DNSHist, expiredAnswerStateBins) == (kQueryStatsExpiredAnswerStateCount));
165
166 // Important: Do not modify kResponseLatencyMsLimits because the code used to generate AWD reports expects the response
167 // latency histogram bins to observe these time interval upper bounds.
168
169 static const mDNSu32 kResponseLatencyMsLimits[] =
170 {
171 1, 2, 3, 4, 5,
172 10, 20, 30, 40, 50, 60, 70, 80, 90,
173 100, 110, 120, 130, 140, 150, 160, 170, 180, 190,
174 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950,
175 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500,
176 5000, 6000, 7000, 8000, 9000,
177 10000
178 };
179
180 check_compile_time(countof(kResponseLatencyMsLimits) == 54);
181 check_compile_time(countof_field(DNSHist, unansweredQueryDurationBins) == (countof(kResponseLatencyMsLimits) + 1));
182 check_compile_time(countof_field(DNSHist, responseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1));
183 check_compile_time(countof_field(DNSHist, negResponseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1));
184
185 struct DNSHistSet
186 {
187 DNSHist * histA; // Histogram data for queries for A resource records.
188 DNSHist * histAAAA; // Histogram data for queries for AAAA resource records.
189 };
190
191 typedef struct
192 {
193 const char * domainStr;
194 const char * altDomainStr;
195 QueryNameTest_f test;
196 mDNSBool terminal;
197
198 } QueryStatsArgs;
199
200 // Data structures for resolve stats.
201
202 static const char * const kResolveStatsDomains[] =
203 {
204 "apple.com.",
205 "icloud.com.",
206 "mzstatic.com.",
207 "me.com."
208 };
209
210 check_compile_time(countof(kResolveStatsDomains) == 4);
211
212 typedef struct ResolveStatsDomain ResolveStatsDomain;
213 typedef struct ResolveStatsHostname ResolveStatsHostname;
214 typedef struct ResolveStatsDNSServer ResolveStatsDNSServer;
215 typedef struct ResolveStatsIPv4AddrSet ResolveStatsIPv4AddrSet;
216 typedef struct ResolveStatsIPv6Addr ResolveStatsIPv6Addr;
217 typedef struct ResolveStatsNegAAAASet ResolveStatsNegAAAASet;
218
219 struct ResolveStatsDomain
220 {
221 ResolveStatsDomain * next; // Next domain object in list.
222 const char * domainStr;
223 uint8_t * domain; // Domain for which these stats are collected.
224 int labelCount; // Number of labels in domain name. Used for domain name comparisons.
225 ResolveStatsHostname * hostnameList; // List of hostname objects in this domain.
226 };
227
228 check_compile_time(sizeof(ResolveStatsDomain) <= 40);
229
230 struct ResolveStatsHostname
231 {
232 ResolveStatsHostname * next; // Next hostname object in list.
233 ResolveStatsIPv4AddrSet * addrV4List; // List of IPv4 addresses to which this hostname resolved.
234 ResolveStatsIPv6Addr * addrV6List; // List of IPv6 addresses to which this hostname resolved.
235 ResolveStatsNegAAAASet * negV6List; // List of negative AAAA response objects.
236 uint8_t name[1]; // Variable length storage for hostname as length-prefixed labels.
237 };
238
239 check_compile_time(sizeof(ResolveStatsHostname) <= 64);
240
241 struct ResolveStatsDNSServer
242 {
243 ResolveStatsDNSServer * next; // Next DNS server object in list.
244 uint8_t id; // 8-bit ID assigned to this DNS server used by IP address objects.
245 mDNSBool isForCell; // True if this DNS server belongs to a cellular interface.
246 mDNSBool isAddrV6; // True if this DNS server has an IPv6 address instead of IPv4.
247 uint8_t addrBytes[1]; // Variable length storage for DNS server's IP address.
248 };
249
250 check_compile_time(sizeof(ResolveStatsDNSServer) <= 32);
251
252 typedef struct
253 {
254 uint16_t count; // Number of times this IPv4 address was provided as a resolution result.
255 uint8_t serverID; // 8-bit ID of the DNS server from which this IPv4 address came.
256 uint8_t isNegative;
257 uint8_t addrBytes[4]; // IPv4 address bytes.
258
259 } IPv4AddrCounter;
260
261 check_compile_time(sizeof(IPv4AddrCounter) <= 8);
262
263 struct ResolveStatsIPv4AddrSet
264 {
265 ResolveStatsIPv4AddrSet * next; // Next set of IPv4 address counters in list.
266 IPv4AddrCounter counters[3]; // Array of IPv4 address counters.
267 };
268
269 check_compile_time(sizeof(ResolveStatsIPv4AddrSet) <= 32);
270
271 struct ResolveStatsIPv6Addr
272 {
273 ResolveStatsIPv6Addr * next; // Next IPv6 address object in list.
274 uint16_t count; // Number of times this IPv6 address was provided as a resolution result.
275 uint8_t serverID; // 8-bit ID of the DNS server from which this IPv6 address came.
276 uint8_t addrBytes[16]; // IPv6 address bytes.
277 };
278
279 check_compile_time(sizeof(ResolveStatsIPv6Addr) <= 32);
280
281 typedef struct
282 {
283 uint16_t count; // Number of times that a negative response was returned by a DNS server.
284 uint8_t serverID; // 8-bit ID of the DNS server that sent the negative responses.
285
286 } NegAAAACounter;
287
288 check_compile_time(sizeof(NegAAAACounter) <= 4);
289
290 struct ResolveStatsNegAAAASet
291 {
292 ResolveStatsNegAAAASet * next; // Next set of negative AAAA response counters in list.
293 NegAAAACounter counters[6]; // Array of negative AAAA response counters.
294 };
295
296 check_compile_time(sizeof(ResolveStatsNegAAAASet) <= 32);
297
298 typedef enum
299 {
300 kResponseType_IPv4Addr = 1,
301 kResponseType_IPv6Addr = 2,
302 kResponseType_NegA = 3,
303 kResponseType_NegAAAA = 4
304
305 } ResponseType;
306
307 typedef struct
308 {
309 ResponseType type;
310 const uint8_t * data;
311
312 } Response;
313
314 // Data structures for DNS message size stats.
315
316 #define kQuerySizeBinWidth 16
317 #define kQuerySizeBinMax 512
318 #define kQuerySizeBinCount ((kQuerySizeBinMax / kQuerySizeBinWidth) + 1)
319
320 check_compile_time(kQuerySizeBinWidth > 0);
321 check_compile_time(kQuerySizeBinCount > 0);
322 check_compile_time((kQuerySizeBinMax % kQuerySizeBinWidth) == 0);
323
324 #define kResponseSizeBinWidth 16
325 #define kResponseSizeBinMax 512
326 #define kResponseSizeBinCount ((kResponseSizeBinMax / kResponseSizeBinWidth) + 1)
327
328 check_compile_time(kResponseSizeBinWidth > 0);
329 check_compile_time(kResponseSizeBinCount > 0);
330 check_compile_time((kResponseSizeBinMax % kResponseSizeBinWidth) == 0);
331
332 typedef struct
333 {
334 uint16_t querySizeBins[kQuerySizeBinCount];
335 uint16_t responseSizeBins[kResponseSizeBinCount];
336
337 } DNSMessageSizeStats;
338
339 check_compile_time(sizeof(DNSMessageSizeStats) <= 132);
340
341 //===========================================================================================================================
342 // Local Prototypes
343 //===========================================================================================================================
344
345 // Query stats
346
347 mDNSlocal mStatus QueryStatsCreate(const char *inDomainStr, const char *inAltDomainStr, QueryNameTest_f inTest, mDNSBool inTerminal, QueryStats **outStats);
348 mDNSlocal void QueryStatsFree(QueryStats *inStats);
349 mDNSlocal void QueryStatsFreeList(QueryStats *inList);
350 mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, ExpiredAnswerMetric inExpiredAnswerState, mDNSu32 inLatencyMs, mDNSBool inForCell);
351 mDNSlocal const char * QueryStatsGetDomainString(const QueryStats *inStats);
352 mDNSlocal mDNSBool QueryStatsDomainTest(const QueryStats *inStats, const domainname *inQueryName);
353 mDNSlocal mDNSBool QueryStatsHostnameTest(const QueryStats *inStats, const domainname *inQueryName);
354 mDNSlocal mDNSBool QueryStatsContentiCloudTest(const QueryStats *inStats, const domainname *inQueryName);
355 mDNSlocal mDNSBool QueryStatsCourierPushTest(const QueryStats *inStats, const domainname *inQueryName);
356
357 // Resolve stats
358
359 mDNSlocal mStatus ResolveStatsDomainCreate(const char *inDomainStr, ResolveStatsDomain **outDomain);
360 mDNSlocal void ResolveStatsDomainFree(ResolveStatsDomain *inDomain);
361 mDNSlocal mStatus ResolveStatsDomainUpdate(ResolveStatsDomain *inDomain, const domainname *inHostname, const Response *inResp, const mDNSAddr *inDNSAddr, mDNSBool inForCell);
362 mDNSlocal mStatus ResolveStatsDomainCreateAWDVersion(const ResolveStatsDomain *inDomain, AWDMDNSResponderResolveStatsDomain **outDomain);
363
364 mDNSlocal mStatus ResolveStatsHostnameCreate(const domainname *inName, ResolveStatsHostname **outHostname);
365 mDNSlocal void ResolveStatsHostnameFree(ResolveStatsHostname *inHostname);
366 mDNSlocal mStatus ResolveStatsHostnameUpdate(ResolveStatsHostname *inHostname, const Response *inResp, uint8_t inServerID);
367 mDNSlocal mStatus ResolveStatsHostnameCreateAWDVersion(const ResolveStatsHostname *inHostname, AWDMDNSResponderResolveStatsHostname **outHostname);
368
369 mDNSlocal mStatus ResolveStatsDNSServerCreate(const mDNSAddr *inAddr, mDNSBool inForCell, ResolveStatsDNSServer **outServer);
370 mDNSlocal void ResolveStatsDNSServerFree(ResolveStatsDNSServer *inServer);
371 mDNSlocal mStatus ResolveStatsDNSServerCreateAWDVersion(const ResolveStatsDNSServer *inServer, AWDMDNSResponderResolveStatsDNSServer **outServer);
372
373 mDNSlocal mStatus ResolveStatsIPv4AddrSetCreate(ResolveStatsIPv4AddrSet **outSet);
374 mDNSlocal void ResolveStatsIPv4AddrSetFree(ResolveStatsIPv4AddrSet *inSet);
375
376 mDNSlocal mStatus ResolveStatsIPv6AddressCreate(uint8_t inServerID, const uint8_t inAddrBytes[16], ResolveStatsIPv6Addr **outAddr);
377 mDNSlocal void ResolveStatsIPv6AddressFree(ResolveStatsIPv6Addr *inAddr);
378
379 mDNSlocal mStatus ResolveStatsNegAAAASetCreate(ResolveStatsNegAAAASet **outSet);
380 mDNSlocal void ResolveStatsNegAAAASetFree(ResolveStatsNegAAAASet *inSet);
381 mDNSlocal mStatus ResolveStatsGetServerID(const mDNSAddr *inServerAddr, mDNSBool inForCell, uint8_t *outServerID);
382
383 // DNS message size stats
384
385 mDNSlocal mStatus DNSMessageSizeStatsCreate(DNSMessageSizeStats **outStats);
386 mDNSlocal void DNSMessageSizeStatsFree(DNSMessageSizeStats *inStats);
387
388 mDNSlocal mStatus CreateQueryStatsList(QueryStats **outList);
389 mDNSlocal mStatus CreateResolveStatsList(ResolveStatsDomain **outList);
390 mDNSlocal void FreeResolveStatsList(ResolveStatsDomain *inList);
391 mDNSlocal void FreeResolveStatsServerList(ResolveStatsDNSServer *inList);
392 mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID);
393 mDNSlocal mStatus SubmitAWDMetricQueryStats(void);
394 mDNSlocal mStatus SubmitAWDMetricResolveStats(void);
395 mDNSlocal mStatus SubmitAWDMetricDNSMessageSizeStats(void);
396 mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats);
397 mDNSlocal void LogDNSHistSet(const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell);
398 mDNSlocal void LogDNSHist(const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType);
399 mDNSlocal void LogDNSHistSendCounts(const uint16_t inSendCountBins[kQueryStatsSendCountBinCount]);
400 mDNSlocal void LogDNSHistLatencies(const uint16_t inLatencyBins[kQueryStatsLatencyBinCount]);
401 mDNSlocal void LogDNSMessageSizeStats(const uint16_t *inBins, size_t inBinCount, unsigned int inBinWidth);
402
403 mDNSlocal size_t CopyHistogramBins(uint32_t *inDstBins, uint16_t *inSrcBins, size_t inBinCount);
404
405 //===========================================================================================================================
406 // Globals
407 //===========================================================================================================================
408
409 static AWDServerConnection * gAWDServerConnection = nil;
410 static QueryStats * gQueryStatsList = NULL;
411 static ResolveStatsDomain * gResolveStatsList = NULL;
412 static ResolveStatsDNSServer * gResolveStatsServerList = NULL;
413 static unsigned int gResolveStatsNextServerID = 0;
414 static int gResolveStatsObjCount = 0;
415 static DNSMessageSizeStats * gDNSMessageSizeStats = NULL;
416
417 // Important: Do not add to this list without getting privacy approval. See <rdar://problem/24155761&26397203&34763471>.
418
419 static const QueryStatsArgs kQueryStatsArgs[] =
420 {
421 { ".", NULL, QueryStatsDomainTest, mDNSfalse },
422 { "", "alt:*-courier.push.apple.com.", QueryStatsCourierPushTest, mDNSfalse },
423 { "apple.com.", NULL, QueryStatsDomainTest, mDNStrue },
424 { "gateway.icloud.com.", "alt:gateway.icloud.com", QueryStatsHostnameTest, mDNSfalse },
425 { "", "alt:*-content.icloud.com.", QueryStatsContentiCloudTest, mDNSfalse },
426 { "icloud.com.", NULL, QueryStatsDomainTest, mDNStrue },
427 { "mzstatic.com.", NULL, QueryStatsDomainTest, mDNStrue },
428 { "google.com.", NULL, QueryStatsDomainTest, mDNStrue },
429 { "baidu.com.", NULL, QueryStatsDomainTest, mDNStrue },
430 { "yahoo.com.", NULL, QueryStatsDomainTest, mDNStrue },
431 { "qq.com.", NULL, QueryStatsDomainTest, mDNStrue }
432 };
433
434 check_compile_time(countof(kQueryStatsArgs) == 11);
435
436 //===========================================================================================================================
437 // MetricsInit
438 //===========================================================================================================================
439
440 mStatus MetricsInit(void)
441 {
442 @autoreleasepool
443 {
444 gAWDServerConnection = [[AWDServerConnectionSoft alloc]
445 initWithComponentId: AWDComponentId_MDNSResponder
446 andBlockOnConfiguration: NO];
447
448 if (gAWDServerConnection)
449 {
450 [gAWDServerConnection
451 registerQueriableMetricCallback: ^(UInt32 inMetricID)
452 {
453 SubmitAWDMetric(inMetricID);
454 }
455 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_DNSStatistics];
456
457 [gAWDServerConnection
458 registerQueriableMetricCallback: ^(UInt32 inMetricID)
459 {
460 SubmitAWDMetric(inMetricID);
461 }
462 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_ResolveStats];
463
464 [gAWDServerConnection
465 registerQueriableMetricCallback: ^(UInt32 inMetricID)
466 {
467 SubmitAWDMetric(inMetricID);
468 }
469 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_ServicesStats];
470
471 [gAWDServerConnection
472 registerQueriableMetricCallback: ^(UInt32 inMetricID)
473 {
474 SubmitAWDMetric(inMetricID);
475 }
476 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_DNSMessageSizeStats];
477 }
478 else
479 {
480 LogMsg("MetricsInit: failed to create AWD server connection.");
481 }
482 }
483
484 if( gAWDServerConnection )
485 {
486 CreateQueryStatsList(&gQueryStatsList);
487 CreateResolveStatsList(&gResolveStatsList);
488 DNSMessageSizeStatsCreate(&gDNSMessageSizeStats);
489 }
490
491 return (mStatus_NoError);
492 }
493
494 //===========================================================================================================================
495 // MetricsUpdateDNSQueryStats
496 //===========================================================================================================================
497
498 mDNSexport void MetricsUpdateDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, ExpiredAnswerMetric inExpiredAnswerState, mDNSu32 inLatencyMs, mDNSBool inForCell)
499 {
500 QueryStats * stats;
501 mDNSBool match;
502
503 require_quiet(gAWDServerConnection, exit);
504 require_quiet((inType == kDNSType_A) || (inType == kDNSType_AAAA), exit);
505
506 for (stats = gQueryStatsList; stats; stats = stats->next)
507 {
508 match = stats->test(stats, inQueryName);
509 if (match)
510 {
511 QueryStatsUpdate(stats, inType, inRR, inSendCount, inExpiredAnswerState, inLatencyMs, inForCell);
512 if (stats->terminal) break;
513 }
514 }
515
516 exit:
517 return;
518 }
519
520 //===========================================================================================================================
521 // MetricsUpdateDNSResolveStats
522 //===========================================================================================================================
523
524 mDNSexport void MetricsUpdateDNSResolveStats(const domainname *inQueryName, const ResourceRecord *inRR, mDNSBool inForCell)
525 {
526 ResolveStatsDomain * domainStats;
527 domainname hostname;
528 size_t hostnameLen;
529 mDNSBool isQueryInDomain;
530 int skipCount;
531 int skipCountLast = -1;
532 int queryLabelCount;
533 const domainname * queryParentDomain;
534 Response response;
535
536 require_quiet(gAWDServerConnection, exit);
537 require_quiet((inRR->rrtype == kDNSType_A) || (inRR->rrtype == kDNSType_AAAA), exit);
538 require_quiet(inRR->rDNSServer, exit);
539
540 queryLabelCount = CountLabels(inQueryName);
541
542 for (domainStats = gResolveStatsList; domainStats; domainStats = domainStats->next)
543 {
544 isQueryInDomain = mDNSfalse;
545 skipCount = queryLabelCount - domainStats->labelCount;
546 if (skipCount >= 0)
547 {
548 if (skipCount != skipCountLast)
549 {
550 queryParentDomain = SkipLeadingLabels(inQueryName, skipCount);
551 skipCountLast = skipCount;
552 }
553 isQueryInDomain = SameDomainName(queryParentDomain, (const domainname *)domainStats->domain);
554 }
555 if (!isQueryInDomain) continue;
556
557 hostnameLen = (size_t)(queryParentDomain->c - inQueryName->c);
558 if (hostnameLen >= sizeof(hostname.c)) continue;
559
560 memcpy(hostname.c, inQueryName->c, hostnameLen);
561 hostname.c[hostnameLen] = 0;
562
563 if (inRR->RecordType == kDNSRecordTypePacketNegative)
564 {
565 response.type = (inRR->rrtype == kDNSType_A) ? kResponseType_NegA : kResponseType_NegAAAA;
566 response.data = NULL;
567 }
568 else
569 {
570 response.type = (inRR->rrtype == kDNSType_A) ? kResponseType_IPv4Addr : kResponseType_IPv6Addr;
571 response.data = (inRR->rrtype == kDNSType_A) ? inRR->rdata->u.ipv4.b : inRR->rdata->u.ipv6.b;
572 }
573 ResolveStatsDomainUpdate(domainStats, &hostname, &response, &inRR->rDNSServer->addr, inForCell);
574 }
575
576 exit:
577 return;
578 }
579
580 //===========================================================================================================================
581 // MetricsUpdateDNSQuerySize
582 //===========================================================================================================================
583
584 mDNSlocal void UpdateMessageSizeCounts(uint16_t *inBins, size_t inBinCount, unsigned int inBinWidth, uint32_t inSize);
585
586 mDNSexport void MetricsUpdateDNSQuerySize(mDNSu32 inSize)
587 {
588 if (!gDNSMessageSizeStats) return;
589 UpdateMessageSizeCounts(gDNSMessageSizeStats->querySizeBins, kQuerySizeBinCount, kQuerySizeBinWidth, inSize);
590 }
591
592 mDNSlocal void UpdateMessageSizeCounts(uint16_t *inBins, size_t inBinCount, unsigned int inBinWidth, uint32_t inSize)
593 {
594 size_t i;
595
596 if (inSize == 0) return;
597 i = (inSize - 1) / inBinWidth;
598 if (i >= inBinCount) i = inBinCount - 1;
599 increment_saturate(inBins[i], UINT16_MAX);
600 }
601
602 //===========================================================================================================================
603 // MetricsUpdateDNSResponseSize
604 //===========================================================================================================================
605
606 mDNSexport void MetricsUpdateDNSResponseSize(mDNSu32 inSize)
607 {
608 if (!gDNSMessageSizeStats) return;
609 UpdateMessageSizeCounts(gDNSMessageSizeStats->responseSizeBins, kResponseSizeBinCount, kResponseSizeBinWidth, inSize);
610 }
611
612 //===========================================================================================================================
613 // LogMetrics
614 //===========================================================================================================================
615
616 mDNSexport void LogMetrics(void)
617 {
618 QueryStats * stats;
619 const ResolveStatsDomain * domain;
620 const ResolveStatsHostname * hostname;
621 const ResolveStatsDNSServer * server;
622 const ResolveStatsIPv4AddrSet * addrV4;
623 const ResolveStatsIPv6Addr * addrV6;
624 const ResolveStatsNegAAAASet * negV6;
625 int hostnameCount;
626 int i;
627 unsigned int serverID;
628 int serverObjCount = 0;
629 int hostnameObjCount = 0;
630 int addrObjCount = 0;
631
632 LogMsgNoIdent("gAWDServerConnection %p", gAWDServerConnection);
633 LogMsgNoIdent("---- DNS query stats by domain -----");
634
635 for (stats = gQueryStatsList; stats; stats = stats->next)
636 {
637 if (!stats->nonCellular && !stats->cellular)
638 {
639 LogMsgNoIdent("No data for %s", QueryStatsGetDomainString(stats));
640 continue;
641 }
642 if (stats->nonCellular) LogDNSHistSet(stats->nonCellular, QueryStatsGetDomainString(stats), mDNSfalse);
643 if (stats->cellular) LogDNSHistSet(stats->cellular, QueryStatsGetDomainString(stats), mDNStrue);
644 }
645
646 LogMsgNoIdent("---- DNS resolve stats by domain -----");
647
648 LogMsgNoIdent("Servers:");
649 for (server = gResolveStatsServerList; server; server = server->next)
650 {
651 serverObjCount++;
652 LogMsgNoIdent(server->isAddrV6 ? "%2u: %s %.16a" : "%2u: %s %.4a",
653 server->id, server->isForCell ? " C" : "NC", server->addrBytes);
654 }
655
656 for (domain = gResolveStatsList; domain; domain = domain->next)
657 {
658 hostnameCount = 0;
659 for (hostname = domain->hostnameList; hostname; hostname = hostname->next) { hostnameCount++; }
660 hostnameObjCount += hostnameCount;
661
662 LogMsgNoIdent("%s (%d hostname%s)", domain->domainStr, hostnameCount, (hostnameCount == 1) ? "" : "s");
663
664 for (hostname = domain->hostnameList; hostname; hostname = hostname->next)
665 {
666 LogMsgNoIdent(" %##s", hostname->name);
667 for (serverID = 0; serverID < gResolveStatsNextServerID; ++serverID)
668 {
669 for (addrV4 = hostname->addrV4List; addrV4; addrV4 = addrV4->next)
670 {
671 if (serverID == 0) addrObjCount++;
672 for (i = 0; i < (int)countof(addrV4->counters); ++i)
673 {
674 const IPv4AddrCounter * counter;
675
676 counter = &addrV4->counters[i];
677 if (counter->count == 0) break;
678 if (counter->serverID == serverID)
679 {
680 if (counter->isNegative)
681 {
682 LogMsgNoIdent("%10u: %3u negative A", counter->serverID, counter->count);
683 }
684 else
685 {
686 LogMsgNoIdent("%10u: %3u %.4a", counter->serverID, counter->count, counter->addrBytes);
687 }
688 }
689 }
690 }
691 for (addrV6 = hostname->addrV6List; addrV6; addrV6 = addrV6->next)
692 {
693 if (serverID == 0) addrObjCount++;
694 if (addrV6->serverID == serverID)
695 {
696 LogMsgNoIdent("%10u: %3u %.16a", addrV6->serverID, addrV6->count, addrV6->addrBytes);
697 }
698 }
699 for (negV6 = hostname->negV6List; negV6; negV6 = negV6->next)
700 {
701 if (serverID == 0) addrObjCount++;
702 for (i = 0; i < (int)countof(negV6->counters); ++i)
703 {
704 const NegAAAACounter * counter;
705
706 counter = &negV6->counters[i];
707 if (counter->count == 0) break;
708 if (counter->serverID == serverID)
709 {
710 LogMsgNoIdent("%10u: %3u negative AAAA", counter->serverID, counter->count);
711 }
712 }
713 }
714 }
715 }
716 }
717 LogMsgNoIdent("Total object count: %3d (server %d hostname %d address %d)",
718 serverObjCount + hostnameObjCount + addrObjCount, serverObjCount, hostnameObjCount, addrObjCount);
719
720 LogMsgNoIdent("---- Num of Services Registered -----");
721 LogMsgNoIdent("Current_number_of_services_registered :[%d], Max_number_of_services_registered :[%d]",
722 curr_num_regservices, max_num_regservices);
723
724 if (gDNSMessageSizeStats)
725 {
726 LogMsgNoIdent("---- DNS query size stats ---");
727 LogDNSMessageSizeStats(gDNSMessageSizeStats->querySizeBins, kQuerySizeBinCount, kQuerySizeBinWidth);
728
729 LogMsgNoIdent("-- DNS response size stats --");
730 LogDNSMessageSizeStats(gDNSMessageSizeStats->responseSizeBins, kResponseSizeBinCount, kResponseSizeBinWidth);
731 }
732 else
733 {
734 LogMsgNoIdent("No DNS message size stats.");
735 }
736 }
737
738 //===========================================================================================================================
739 // QueryStatsCreate
740 //===========================================================================================================================
741
742 mDNSlocal mStatus StringToDomainName(const char *inString, uint8_t **outDomainName);
743
744 mDNSlocal mStatus QueryStatsCreate(const char *inDomainStr, const char *inAltDomainStr, QueryNameTest_f inTest, mDNSBool inTerminal, QueryStats **outStats)
745 {
746 mStatus err;
747 QueryStats * obj;
748
749 obj = (QueryStats *)calloc(1, sizeof(*obj));
750 require_action_quiet(obj, exit, err = mStatus_NoMemoryErr);
751
752 obj->domainStr = inDomainStr;
753 err = StringToDomainName(obj->domainStr, &obj->domain);
754 require_noerr_quiet(err, exit);
755
756 obj->altDomainStr = inAltDomainStr;
757 obj->test = inTest;
758 obj->labelCount = CountLabels((const domainname *)obj->domain);
759 obj->terminal = inTerminal;
760
761 *outStats = obj;
762 obj = NULL;
763 err = mStatus_NoError;
764
765 exit:
766 if (obj) QueryStatsFree(obj);
767 return (err);
768 }
769
770 mDNSlocal mStatus StringToDomainName(const char *inString, uint8_t **outDomainName)
771 {
772 mStatus err;
773 uint8_t * domainPtr = NULL;
774 size_t domainLen;
775 const mDNSu8 * ptr;
776 domainname domain;
777
778 if (strcmp(inString, ".") == 0)
779 {
780 domain.c[0] = 0;
781 }
782 else
783 {
784 ptr = MakeDomainNameFromDNSNameString(&domain, inString);
785 require_action_quiet(ptr, exit, err = mStatus_BadParamErr);
786 }
787 domainLen = DomainNameLength(&domain);
788
789 domainPtr = (uint8_t *)malloc(domainLen);
790 require_action_quiet(domainPtr, exit, err = mStatus_NoMemoryErr);
791
792 memcpy(domainPtr, domain.c, domainLen);
793
794 *outDomainName = domainPtr;
795 domainPtr = NULL;
796 err = mStatus_NoError;
797
798 exit:
799 return(err);
800 }
801
802 //===========================================================================================================================
803 // QueryStatsFree
804 //===========================================================================================================================
805
806 mDNSlocal void QueryStatsFree(QueryStats *inStats)
807 {
808 ForgetMem(&inStats->domain);
809 if (inStats->nonCellular)
810 {
811 ForgetMem(&inStats->nonCellular->histA);
812 ForgetMem(&inStats->nonCellular->histAAAA);
813 free(inStats->nonCellular);
814 inStats->nonCellular = NULL;
815 }
816 if (inStats->cellular)
817 {
818 ForgetMem(&inStats->cellular->histA);
819 ForgetMem(&inStats->cellular->histAAAA);
820 free(inStats->cellular);
821 inStats->cellular = NULL;
822 }
823 free(inStats);
824 }
825
826 //===========================================================================================================================
827 // QueryStatsFreeList
828 //===========================================================================================================================
829
830 mDNSlocal void QueryStatsFreeList(QueryStats *inList)
831 {
832 QueryStats * stats;
833
834 while ((stats = inList) != NULL)
835 {
836 inList = stats->next;
837 QueryStatsFree(stats);
838 }
839 }
840
841 //===========================================================================================================================
842 // QueryStatsUpdate
843 //===========================================================================================================================
844
845 mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, ExpiredAnswerMetric inExpiredAnswerState, mDNSu32 inLatencyMs, mDNSBool inForCell)
846 {
847 mStatus err;
848 DNSHistSet * set;
849 DNSHistSet ** pSet;
850 DNSHist * hist;
851 DNSHist ** pHist;
852 int i;
853
854 require_action_quiet(inRR || (inQuerySendCount > 0), exit, err = mStatus_NoError);
855 require_action_quiet((inType == kDNSType_A) || (inType == kDNSType_AAAA), exit, err = mStatus_NoError);
856
857 pSet = inForCell ? &inStats->cellular : &inStats->nonCellular;
858 if ((set = *pSet) == NULL)
859 {
860 set = (DNSHistSet *)calloc(1, sizeof(*set));
861 require_action_quiet(set, exit, err = mStatus_NoMemoryErr);
862 *pSet = set;
863 }
864 pHist = (inType == kDNSType_A) ? &set->histA : &set->histAAAA;
865 if ((hist = *pHist) == NULL)
866 {
867 hist = (DNSHist *)calloc(1, sizeof(*hist));
868 require_action_quiet(hist, exit, err = mStatus_NoMemoryErr);
869 *pHist = hist;
870 }
871
872 if (inRR)
873 {
874 uint16_t * sendCountBins;
875 uint16_t * latencyBins;
876 const mDNSBool isNegative = (inRR->RecordType == kDNSRecordTypePacketNegative);
877
878 i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount);
879
880 sendCountBins = isNegative ? hist->negAnsweredQuerySendCountBins : hist->answeredQuerySendCountBins;
881 increment_saturate(sendCountBins[i], UINT16_MAX);
882
883 if (inQuerySendCount > 0)
884 {
885 for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {}
886 latencyBins = isNegative ? hist->negResponseLatencyBins : hist->responseLatencyBins;
887 increment_saturate(latencyBins[i], UINT16_MAX);
888 }
889 }
890 else
891 {
892 i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount);
893 increment_saturate(hist->unansweredQuerySendCountBins[i], UINT16_MAX);
894
895 for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {}
896 increment_saturate(hist->unansweredQueryDurationBins[i], UINT16_MAX);
897 }
898 increment_saturate(hist->expiredAnswerStateBins[Min(inExpiredAnswerState, (kQueryStatsExpiredAnswerStateCount-1))], UINT16_MAX);
899 err = mStatus_NoError;
900
901 exit:
902 return (err);
903 }
904
905 //===========================================================================================================================
906 // QueryStatsGetDomainString
907 //===========================================================================================================================
908
909 mDNSlocal const char * QueryStatsGetDomainString(const QueryStats *inStats)
910 {
911 return (inStats->altDomainStr ? inStats->altDomainStr : inStats->domainStr);
912 }
913
914 //===========================================================================================================================
915 // QueryStatsDomainTest
916 //===========================================================================================================================
917
918 mDNSlocal mDNSBool QueryStatsDomainTest(const QueryStats *inStats, const domainname *inQueryName)
919 {
920 const domainname * parentDomain;
921 int labelCount;
922
923 if (inStats->domain[0] == 0) return (mDNStrue);
924
925 labelCount = CountLabels(inQueryName);
926 if (labelCount < inStats->labelCount) return (mDNSfalse);
927
928 parentDomain = SkipLeadingLabels(inQueryName, labelCount - inStats->labelCount);
929 return (SameDomainName(parentDomain, (const domainname *)inStats->domain));
930 }
931
932 //===========================================================================================================================
933 // QueryStatsHostnameTest
934 //===========================================================================================================================
935
936 mDNSlocal mDNSBool QueryStatsHostnameTest(const QueryStats *inStats, const domainname *inQueryName)
937 {
938 return (SameDomainName(inQueryName, (const domainname *)inStats->domain));
939 }
940
941 //===========================================================================================================================
942 // QueryStatsContentiCloudTest
943 //===========================================================================================================================
944
945 mDNSlocal const uint8_t *LocateLabelSuffix(const uint8_t *inLabel, const uint8_t *inSuffixPtr, size_t inSuffixLen);
946
947 #define kContentSuffixStr "-content"
948
949 mDNSlocal mDNSBool QueryStatsContentiCloudTest(const QueryStats *inStats, const domainname *inQueryName)
950 {
951 const mDNSu8 * const firstLabel = inQueryName->c;
952 const uint8_t * suffix;
953 const domainname * parentDomain;
954 int labelCount;
955
956 (void) inStats; // Unused.
957
958 labelCount = CountLabels(inQueryName);
959 if (labelCount != 3) return (mDNSfalse);
960
961 suffix = LocateLabelSuffix(firstLabel, (const uint8_t *)kContentSuffixStr, sizeof_string(kContentSuffixStr));
962 if (suffix && (suffix > &firstLabel[1]))
963 {
964 parentDomain = SkipLeadingLabels(inQueryName, 1);
965 if (SameDomainName(parentDomain, (const domainname *)"\x6" "icloud" "\x3" "com"))
966 {
967 return (mDNStrue);
968 }
969 }
970
971 return (mDNSfalse);
972 }
973
974 mDNSlocal const uint8_t *LocateLabelSuffix(const uint8_t *inLabel, const uint8_t *inSuffixPtr, size_t inSuffixLen)
975 {
976 const uint8_t * ptr;
977 const uint8_t * lp;
978 const uint8_t * sp;
979 size_t len;
980 const size_t labelLen = inLabel[0];
981
982 if (labelLen < inSuffixLen) return (NULL);
983
984 ptr = &inLabel[1 + labelLen - inSuffixLen];
985 lp = ptr;
986 sp = inSuffixPtr;
987 for (len = inSuffixLen; len > 0; --len)
988 {
989 if (tolower(*lp) != tolower(*sp)) return (NULL);
990 ++lp;
991 ++sp;
992 }
993
994 return (ptr);
995 }
996
997 //===========================================================================================================================
998 // QueryStatsCourierPushTest
999 //===========================================================================================================================
1000
1001 #define kCourierSuffixStr "-courier"
1002
1003 mDNSlocal mDNSBool QueryStatsCourierPushTest(const QueryStats *inStats, const domainname *inQueryName)
1004 {
1005 const mDNSu8 * const firstLabel = inQueryName->c;
1006 const uint8_t * suffix;
1007 const uint8_t * ptr;
1008 const domainname * parentDomain;
1009 int labelCount;
1010
1011 (void) inStats; // Unused.
1012
1013 labelCount = CountLabels(inQueryName);
1014 if (labelCount != 4) return (mDNSfalse);
1015
1016 suffix = LocateLabelSuffix(firstLabel, (const mDNSu8 *)kCourierSuffixStr, sizeof_string(kCourierSuffixStr));
1017 if (suffix && (suffix > &firstLabel[1]))
1018 {
1019 for (ptr = &firstLabel[1]; ptr < suffix; ++ptr)
1020 {
1021 if (!isdigit(*ptr)) break;
1022 }
1023 if (ptr == suffix)
1024 {
1025 parentDomain = SkipLeadingLabels(inQueryName, 1);
1026 if (SameDomainName(parentDomain, (const domainname *)"\x4" "push" "\x5" "apple" "\x3" "com"))
1027 {
1028 return (mDNStrue);
1029 }
1030 }
1031 }
1032
1033 return (mDNSfalse);
1034 }
1035
1036 //===========================================================================================================================
1037 // ResolveStatsDomainCreate
1038 //===========================================================================================================================
1039
1040 mDNSlocal mStatus ResolveStatsDomainCreate(const char *inDomainStr, ResolveStatsDomain **outDomain)
1041 {
1042 mStatus err;
1043 ResolveStatsDomain * obj;
1044
1045 obj = (ResolveStatsDomain *)calloc(1, sizeof(*obj));
1046 require_action_quiet(obj, exit, err = mStatus_NoMemoryErr);
1047
1048 obj->domainStr = inDomainStr;
1049 err = StringToDomainName(obj->domainStr, &obj->domain);
1050 require_noerr_quiet(err, exit);
1051
1052 obj->labelCount = CountLabels((const domainname *)obj->domain);
1053
1054 *outDomain = obj;
1055 obj = NULL;
1056 err = mStatus_NoError;
1057
1058 exit:
1059 if (obj) ResolveStatsDomainFree(obj);
1060 return (err);
1061 }
1062
1063 //===========================================================================================================================
1064 // ResolveStatsDomainFree
1065 //===========================================================================================================================
1066
1067 mDNSlocal void ResolveStatsDomainFree(ResolveStatsDomain *inDomain)
1068 {
1069 ResolveStatsHostname * hostname;
1070
1071 ForgetMem(&inDomain->domain);
1072 while ((hostname = inDomain->hostnameList) != NULL)
1073 {
1074 inDomain->hostnameList = hostname->next;
1075 ResolveStatsHostnameFree(hostname);
1076 }
1077 free(inDomain);
1078 }
1079
1080 //===========================================================================================================================
1081 // ResolveStatsDomainUpdate
1082 //===========================================================================================================================
1083
1084 mDNSlocal mStatus ResolveStatsDomainUpdate(ResolveStatsDomain *inDomain, const domainname *inHostname, const Response *inResp, const mDNSAddr *inDNSAddr, mDNSBool inForCell)
1085 {
1086 mStatus err;
1087 ResolveStatsHostname ** p;
1088 ResolveStatsHostname * hostname;
1089 uint8_t serverID;
1090
1091 for (p = &inDomain->hostnameList; (hostname = *p) != NULL; p = &hostname->next)
1092 {
1093 if (SameDomainName((domainname *)hostname->name, inHostname)) break;
1094 }
1095
1096 if (!hostname)
1097 {
1098 require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused);
1099 err = ResolveStatsHostnameCreate(inHostname, &hostname);
1100 require_noerr_quiet(err, exit);
1101 gResolveStatsObjCount++;
1102 *p = hostname;
1103 }
1104
1105 err = ResolveStatsGetServerID(inDNSAddr, inForCell, &serverID);
1106 require_noerr_quiet(err, exit);
1107
1108 err = ResolveStatsHostnameUpdate(hostname, inResp, serverID);
1109 require_noerr_quiet(err, exit);
1110
1111 exit:
1112 return (err);
1113 }
1114
1115 //===========================================================================================================================
1116 // ResolveStatsHostnameCreate
1117 //===========================================================================================================================
1118
1119 mDNSlocal mStatus ResolveStatsHostnameCreate(const domainname *inName, ResolveStatsHostname **outHostname)
1120 {
1121 mStatus err;
1122 ResolveStatsHostname * obj;
1123 size_t nameLen;
1124
1125 nameLen = DomainNameLength(inName);
1126 require_action_quiet(nameLen > 0, exit, err = mStatus_Invalid);
1127
1128 obj = (ResolveStatsHostname *)calloc(1, sizeof(*obj) - 1 + nameLen);
1129 require_action_quiet(obj, exit, err = mStatus_NoMemoryErr);
1130
1131 memcpy(obj->name, inName, nameLen);
1132
1133 *outHostname = obj;
1134 err = mStatus_NoError;
1135
1136 exit:
1137 return (err);
1138 }
1139
1140 //===========================================================================================================================
1141 // ResolveStatsDomainCreateAWDVersion
1142 //===========================================================================================================================
1143
1144 mDNSlocal mStatus ResolveStatsDomainCreateAWDVersion(const ResolveStatsDomain *inDomain, AWDMDNSResponderResolveStatsDomain **outDomain)
1145 {
1146 mStatus err;
1147 AWDMDNSResponderResolveStatsDomain * domain;
1148 ResolveStatsHostname * hostname;
1149 AWDMDNSResponderResolveStatsHostname * awdHostname;
1150 NSString * name;
1151
1152 domain = [[AWDMDNSResponderResolveStatsDomainSoft alloc] init];
1153 require_action_quiet(domain, exit, err = mStatus_UnknownErr);
1154
1155 name = [[NSString alloc] initWithUTF8String:inDomain->domainStr];
1156 require_action_quiet(name, exit, err = mStatus_UnknownErr);
1157
1158 domain.name = name;
1159 [name release];
1160 name = nil;
1161
1162 for (hostname = inDomain->hostnameList; hostname; hostname = hostname->next)
1163 {
1164 err = ResolveStatsHostnameCreateAWDVersion(hostname, &awdHostname);
1165 require_noerr_quiet(err, exit);
1166
1167 [domain addHostname:awdHostname];
1168 [awdHostname release];
1169 awdHostname = nil;
1170 }
1171
1172 *outDomain = domain;
1173 domain = nil;
1174 err = mStatus_NoError;
1175
1176 exit:
1177 [domain release];
1178 return (err);
1179 }
1180
1181 //===========================================================================================================================
1182 // ResolveStatsHostnameFree
1183 //===========================================================================================================================
1184
1185 mDNSlocal void ResolveStatsHostnameFree(ResolveStatsHostname *inHostname)
1186 {
1187 ResolveStatsIPv4AddrSet * addrV4;
1188 ResolveStatsIPv6Addr * addrV6;
1189 ResolveStatsNegAAAASet * negV6;
1190
1191 while ((addrV4 = inHostname->addrV4List) != NULL)
1192 {
1193 inHostname->addrV4List = addrV4->next;
1194 ResolveStatsIPv4AddrSetFree(addrV4);
1195 }
1196 while ((addrV6 = inHostname->addrV6List) != NULL)
1197 {
1198 inHostname->addrV6List = addrV6->next;
1199 ResolveStatsIPv6AddressFree(addrV6);
1200 }
1201 while ((negV6 = inHostname->negV6List) != NULL)
1202 {
1203 inHostname->negV6List = negV6->next;
1204 ResolveStatsNegAAAASetFree(negV6);
1205 }
1206 free(inHostname);
1207 }
1208
1209 //===========================================================================================================================
1210 // ResolveStatsHostnameUpdate
1211 //===========================================================================================================================
1212
1213 mDNSlocal mStatus ResolveStatsHostnameUpdate(ResolveStatsHostname *inHostname, const Response *inResp, uint8_t inServerID)
1214 {
1215 mStatus err;
1216
1217 if ((inResp->type == kResponseType_IPv4Addr) || (inResp->type == kResponseType_NegA))
1218 {
1219 ResolveStatsIPv4AddrSet ** p;
1220 ResolveStatsIPv4AddrSet * addrV4;
1221 int i;
1222 IPv4AddrCounter * counter;
1223
1224 for (p = &inHostname->addrV4List; (addrV4 = *p) != NULL; p = &addrV4->next)
1225 {
1226 for (i = 0; i < (int)countof(addrV4->counters); ++i)
1227 {
1228 counter = &addrV4->counters[i];
1229 if (counter->count == 0) break;
1230 if (counter->serverID != inServerID) continue;
1231 if (inResp->type == kResponseType_NegA)
1232 {
1233 if (counter->isNegative) break;
1234 }
1235 else
1236 {
1237 if (memcmp(counter->addrBytes, inResp->data, 4) == 0) break;
1238 }
1239 }
1240 if (i < (int)countof(addrV4->counters)) break;
1241 }
1242 if (!addrV4)
1243 {
1244 require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused);
1245 err = ResolveStatsIPv4AddrSetCreate(&addrV4);
1246 require_noerr_quiet(err, exit);
1247 gResolveStatsObjCount++;
1248
1249 *p = addrV4;
1250 counter = &addrV4->counters[0];
1251 }
1252 if (counter->count == 0)
1253 {
1254 counter->serverID = inServerID;
1255 if (inResp->type == kResponseType_NegA)
1256 {
1257 counter->isNegative = 1;
1258 }
1259 else
1260 {
1261 counter->isNegative = 0;
1262 memcpy(counter->addrBytes, inResp->data, 4);
1263 }
1264 }
1265 increment_saturate(counter->count, UINT16_MAX);
1266 err = mStatus_NoError;
1267 }
1268 else if (inResp->type == kResponseType_IPv6Addr)
1269 {
1270 ResolveStatsIPv6Addr ** p;
1271 ResolveStatsIPv6Addr * addrV6;
1272
1273 for (p = &inHostname->addrV6List; (addrV6 = *p) != NULL; p = &addrV6->next)
1274 {
1275 if ((addrV6->serverID == inServerID) && (memcmp(addrV6->addrBytes, inResp->data, 16) == 0)) break;
1276 }
1277 if (!addrV6)
1278 {
1279 require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused);
1280 err = ResolveStatsIPv6AddressCreate(inServerID, inResp->data, &addrV6);
1281 require_noerr_quiet(err, exit);
1282 gResolveStatsObjCount++;
1283
1284 *p = addrV6;
1285 }
1286 increment_saturate(addrV6->count, UINT16_MAX);
1287 err = mStatus_NoError;
1288 }
1289 else if (inResp->type == kResponseType_NegAAAA)
1290 {
1291 ResolveStatsNegAAAASet ** p;
1292 ResolveStatsNegAAAASet * negV6;
1293 int i;
1294 NegAAAACounter * counter;
1295
1296 for (p = &inHostname->negV6List; (negV6 = *p) != NULL; p = &negV6->next)
1297 {
1298 for (i = 0; i < (int)countof(negV6->counters); ++i)
1299 {
1300 counter = &negV6->counters[i];
1301 if ((counter->count == 0) || (counter->serverID == inServerID)) break;
1302 }
1303 if (i < (int)countof(negV6->counters)) break;
1304 }
1305 if (!negV6)
1306 {
1307 require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused);
1308 err = ResolveStatsNegAAAASetCreate(&negV6);
1309 require_noerr_quiet(err, exit);
1310 gResolveStatsObjCount++;
1311
1312 *p = negV6;
1313 counter = &negV6->counters[0];
1314 }
1315 if (counter->count == 0) counter->serverID = inServerID;
1316 increment_saturate(counter->count, UINT16_MAX);
1317 err = mStatus_NoError;
1318 }
1319 else
1320 {
1321 err = mStatus_Invalid;
1322 }
1323
1324 exit:
1325 return (err);
1326 }
1327
1328 //===========================================================================================================================
1329 // ResolveStatsHostnameCreateAWDVersion
1330 //===========================================================================================================================
1331
1332 mDNSlocal mStatus ResolveStatsHostnameCreateAWDVersion(const ResolveStatsHostname *inHostname, AWDMDNSResponderResolveStatsHostname **outHostname)
1333 {
1334 mStatus err;
1335 AWDMDNSResponderResolveStatsHostname * hostname;
1336 NSString * name;
1337 char nameBuf[MAX_ESCAPED_DOMAIN_NAME];
1338 const char * ptr;
1339 ResolveStatsIPv4AddrSet * addrV4;
1340 ResolveStatsIPv6Addr * addrV6;
1341 ResolveStatsNegAAAASet * negV6;
1342 AWDMDNSResponderResolveStatsResult * result = nil;
1343 int i;
1344
1345 hostname = [[AWDMDNSResponderResolveStatsHostnameSoft alloc] init];
1346 require_action_quiet(hostname, exit, err = mStatus_UnknownErr);
1347
1348 ptr = ConvertDomainNameToCString((domainname *)inHostname->name, nameBuf);
1349 require_action_quiet(ptr, exit, err = mStatus_UnknownErr);
1350
1351 name = [[NSString alloc] initWithUTF8String:nameBuf];
1352 require_action_quiet(name, exit, err = mStatus_UnknownErr);
1353
1354 hostname.name = name;
1355 [name release];
1356 name = nil;
1357
1358 for (addrV4 = inHostname->addrV4List; addrV4; addrV4 = addrV4->next)
1359 {
1360 for (i = 0; i < (int)countof(addrV4->counters); ++i)
1361 {
1362 const IPv4AddrCounter * counter;
1363 NSData * addrBytes;
1364
1365 counter = &addrV4->counters[i];
1366 if (counter->count == 0) break;
1367
1368 result = [[AWDMDNSResponderResolveStatsResultSoft alloc] init];
1369 require_action_quiet(result, exit, err = mStatus_UnknownErr);
1370
1371 if (counter->isNegative)
1372 {
1373 result.type = AWDMDNSResponderResolveStatsResult_ResultType_NegA;
1374 }
1375 else
1376 {
1377 addrBytes = [[NSData alloc] initWithBytes:counter->addrBytes length:4];
1378 require_action_quiet(addrBytes, exit, err = mStatus_UnknownErr);
1379
1380 result.type = AWDMDNSResponderResolveStatsResult_ResultType_IPv4Addr;
1381 result.data = addrBytes;
1382 [addrBytes release];
1383 }
1384 result.count = counter->count;
1385 result.serverID = counter->serverID;
1386
1387 [hostname addResult:result];
1388 [result release];
1389 result = nil;
1390 }
1391 }
1392
1393 for (addrV6 = inHostname->addrV6List; addrV6; addrV6 = addrV6->next)
1394 {
1395 NSData * addrBytes;
1396
1397 result = [[AWDMDNSResponderResolveStatsResultSoft alloc] init];
1398 require_action_quiet(result, exit, err = mStatus_UnknownErr);
1399
1400 addrBytes = [[NSData alloc] initWithBytes:addrV6->addrBytes length:16];
1401 require_action_quiet(addrBytes, exit, err = mStatus_UnknownErr);
1402
1403 result.type = AWDMDNSResponderResolveStatsResult_ResultType_IPv6Addr;
1404 result.count = addrV6->count;
1405 result.serverID = addrV6->serverID;
1406 result.data = addrBytes;
1407
1408 [addrBytes release];
1409
1410 [hostname addResult:result];
1411 [result release];
1412 result = nil;
1413 }
1414
1415 for (negV6 = inHostname->negV6List; negV6; negV6 = negV6->next)
1416 {
1417 for (i = 0; i < (int)countof(negV6->counters); ++i)
1418 {
1419 const NegAAAACounter * counter;
1420
1421 counter = &negV6->counters[i];
1422 if (counter->count == 0) break;
1423
1424 result = [[AWDMDNSResponderResolveStatsResultSoft alloc] init];
1425 require_action_quiet(result, exit, err = mStatus_UnknownErr);
1426
1427 result.type = AWDMDNSResponderResolveStatsResult_ResultType_NegAAAA;
1428 result.count = counter->count;
1429 result.serverID = counter->serverID;
1430
1431 [hostname addResult:result];
1432 [result release];
1433 result = nil;
1434 }
1435 }
1436
1437 *outHostname = hostname;
1438 hostname = nil;
1439 err = mStatus_NoError;
1440
1441 exit:
1442 [result release];
1443 [hostname release];
1444 return (err);
1445 }
1446
1447 //===========================================================================================================================
1448 // ResolveStatsDNSServerCreate
1449 //===========================================================================================================================
1450
1451 mDNSlocal mStatus ResolveStatsDNSServerCreate(const mDNSAddr *inAddr, mDNSBool inForCell, ResolveStatsDNSServer **outServer)
1452 {
1453 mStatus err;
1454 ResolveStatsDNSServer * obj;
1455 size_t addrLen;
1456
1457 require_action_quiet((inAddr->type == mDNSAddrType_IPv4) || (inAddr->type == mDNSAddrType_IPv6), exit, err = mStatus_Invalid);
1458
1459 addrLen = (inAddr->type == mDNSAddrType_IPv4) ? 4 : 16;
1460 obj = (ResolveStatsDNSServer *)calloc(1, sizeof(*obj) - 1 + addrLen);
1461 require_action_quiet(obj, exit, err = mStatus_NoMemoryErr);
1462
1463 obj->isForCell = inForCell;
1464 if (inAddr->type == mDNSAddrType_IPv4)
1465 {
1466 obj->isAddrV6 = mDNSfalse;
1467 memcpy(obj->addrBytes, inAddr->ip.v4.b, addrLen);
1468 }
1469 else
1470 {
1471 obj->isAddrV6 = mDNStrue;
1472 memcpy(obj->addrBytes, inAddr->ip.v6.b, addrLen);
1473 }
1474
1475 *outServer = obj;
1476 err = mStatus_NoError;
1477
1478 exit:
1479 return (err);
1480 }
1481
1482 //===========================================================================================================================
1483 // ResolveStatsDNSServerFree
1484 //===========================================================================================================================
1485
1486 mDNSlocal void ResolveStatsDNSServerFree(ResolveStatsDNSServer *inServer)
1487 {
1488 free(inServer);
1489 }
1490
1491 //===========================================================================================================================
1492 // ResolveStatsDNSServerCreateAWDVersion
1493 //===========================================================================================================================
1494
1495 mDNSlocal mStatus ResolveStatsDNSServerCreateAWDVersion(const ResolveStatsDNSServer *inServer, AWDMDNSResponderResolveStatsDNSServer **outServer)
1496 {
1497 mStatus err;
1498 AWDMDNSResponderResolveStatsDNSServer * server;
1499 NSData * addrBytes = nil;
1500
1501 server = [[AWDMDNSResponderResolveStatsDNSServerSoft alloc] init];
1502 require_action_quiet(server, exit, err = mStatus_UnknownErr);
1503
1504 addrBytes = [[NSData alloc] initWithBytes:inServer->addrBytes length:(inServer->isAddrV6 ? 16 : 4)];
1505 require_action_quiet(addrBytes, exit, err = mStatus_UnknownErr);
1506
1507 server.serverID = inServer->id;
1508 server.address = addrBytes;
1509 if (inServer->isForCell)
1510 {
1511 server.networkType = AWDMDNSResponderResolveStatsDNSServer_NetworkType_Cellular;
1512 }
1513 else
1514 {
1515 server.networkType = AWDMDNSResponderResolveStatsDNSServer_NetworkType_NonCellular;
1516 }
1517
1518 *outServer = server;
1519 server = nil;
1520 err = mStatus_NoError;
1521
1522 exit:
1523 [addrBytes release];
1524 [server release];
1525 return (err);
1526 }
1527
1528 //===========================================================================================================================
1529 // ResolveStatsIPv4AddrSetCreate
1530 //===========================================================================================================================
1531
1532 mDNSlocal mStatus ResolveStatsIPv4AddrSetCreate(ResolveStatsIPv4AddrSet **outSet)
1533 {
1534 mStatus err;
1535 ResolveStatsIPv4AddrSet * obj;
1536
1537 obj = (ResolveStatsIPv4AddrSet *)calloc(1, sizeof(*obj));
1538 require_action_quiet(obj, exit, err = mStatus_NoMemoryErr);
1539
1540 *outSet = obj;
1541 err = mStatus_NoError;
1542
1543 exit:
1544 return (err);
1545 }
1546
1547 //===========================================================================================================================
1548 // ResolveStatsIPv4AddrSetFree
1549 //===========================================================================================================================
1550
1551 mDNSlocal void ResolveStatsIPv4AddrSetFree(ResolveStatsIPv4AddrSet *inSet)
1552 {
1553 free(inSet);
1554 }
1555
1556 //===========================================================================================================================
1557 // ResolveStatsIPv6AddressCreate
1558 //===========================================================================================================================
1559
1560 mDNSlocal mStatus ResolveStatsIPv6AddressCreate(uint8_t inServerID, const uint8_t inAddrBytes[16], ResolveStatsIPv6Addr **outAddr)
1561 {
1562 mStatus err;
1563 ResolveStatsIPv6Addr * obj;
1564
1565 obj = (ResolveStatsIPv6Addr *)calloc(1, sizeof(*obj));
1566 require_action_quiet(obj, exit, err = mStatus_NoMemoryErr);
1567
1568 obj->serverID = inServerID;
1569 memcpy(obj->addrBytes, inAddrBytes, 16);
1570
1571 *outAddr = obj;
1572 err = mStatus_NoError;
1573
1574 exit:
1575 return (err);
1576 }
1577
1578 //===========================================================================================================================
1579 // ResolveStatsIPv6AddressFree
1580 //===========================================================================================================================
1581
1582 mDNSlocal void ResolveStatsIPv6AddressFree(ResolveStatsIPv6Addr *inAddr)
1583 {
1584 free(inAddr);
1585 }
1586
1587 //===========================================================================================================================
1588 // ResolveStatsNegAAAASetCreate
1589 //===========================================================================================================================
1590
1591 mDNSlocal mStatus ResolveStatsNegAAAASetCreate(ResolveStatsNegAAAASet **outSet)
1592 {
1593 mStatus err;
1594 ResolveStatsNegAAAASet * obj;
1595
1596 obj = (ResolveStatsNegAAAASet *)calloc(1, sizeof(*obj));
1597 require_action_quiet(obj, exit, err = mStatus_NoMemoryErr);
1598
1599 *outSet = obj;
1600 err = mStatus_NoError;
1601
1602 exit:
1603 return (err);
1604 }
1605
1606 //===========================================================================================================================
1607 // ResolveStatsNegAAAASetFree
1608 //===========================================================================================================================
1609
1610 mDNSlocal void ResolveStatsNegAAAASetFree(ResolveStatsNegAAAASet *inSet)
1611 {
1612 free(inSet);
1613 }
1614
1615 //===========================================================================================================================
1616 // ResolveStatsGetServerID
1617 //===========================================================================================================================
1618
1619 mDNSlocal mStatus ResolveStatsGetServerID(const mDNSAddr *inServerAddr, mDNSBool inForCell, uint8_t *outServerID)
1620 {
1621 mStatus err;
1622 ResolveStatsDNSServer ** p;
1623 ResolveStatsDNSServer * server;
1624
1625 require_action_quiet((inServerAddr->type == mDNSAddrType_IPv4) || (inServerAddr->type == mDNSAddrType_IPv6), exit, err = mStatus_Invalid);
1626
1627 for (p = &gResolveStatsServerList; (server = *p) != NULL; p = &server->next)
1628 {
1629 if ((inForCell && server->isForCell) || (!inForCell && !server->isForCell))
1630 {
1631 if (inServerAddr->type == mDNSAddrType_IPv4)
1632 {
1633 if (!server->isAddrV6 && (memcmp(server->addrBytes, inServerAddr->ip.v4.b, 4) == 0)) break;
1634 }
1635 else
1636 {
1637 if (server->isAddrV6 && (memcmp(server->addrBytes, inServerAddr->ip.v6.b, 16) == 0)) break;
1638 }
1639 }
1640 }
1641
1642 if (!server)
1643 {
1644 require_action_quiet(gResolveStatsNextServerID <= UINT8_MAX, exit, err = mStatus_Refused);
1645 require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused);
1646 err = ResolveStatsDNSServerCreate(inServerAddr, inForCell, &server);
1647 require_noerr_quiet(err, exit);
1648 gResolveStatsObjCount++;
1649
1650 server->id = (uint8_t)gResolveStatsNextServerID++;
1651 server->next = gResolveStatsServerList;
1652 gResolveStatsServerList = server;
1653 }
1654 else if (gResolveStatsServerList != server)
1655 {
1656 *p = server->next;
1657 server->next = gResolveStatsServerList;
1658 gResolveStatsServerList = server;
1659 }
1660
1661 *outServerID = server->id;
1662 err = mStatus_NoError;
1663
1664 exit:
1665 return (err);
1666 }
1667
1668 //===========================================================================================================================
1669 // DNSMessageSizeStatsCreate
1670 //===========================================================================================================================
1671
1672 mDNSlocal mStatus DNSMessageSizeStatsCreate(DNSMessageSizeStats **outStats)
1673 {
1674 mStatus err;
1675 DNSMessageSizeStats * stats;
1676
1677 stats = (DNSMessageSizeStats *)calloc(1, sizeof(*stats));
1678 require_action_quiet(stats, exit, err = mStatus_NoMemoryErr);
1679
1680 *outStats = stats;
1681 err = mStatus_NoError;
1682
1683 exit:
1684 return (err);
1685 }
1686
1687 //===========================================================================================================================
1688 // DNSMessageSizeStatsFree
1689 //===========================================================================================================================
1690
1691 mDNSlocal void DNSMessageSizeStatsFree(DNSMessageSizeStats *inStats)
1692 {
1693 free(inStats);
1694 }
1695
1696 //===========================================================================================================================
1697 // CreateQueryStatsList
1698 //===========================================================================================================================
1699
1700 mDNSlocal mStatus CreateQueryStatsList(QueryStats **outList)
1701 {
1702 mStatus err;
1703 QueryStats ** p;
1704 QueryStats * stats;
1705 const QueryStatsArgs * args;
1706 const QueryStatsArgs * const end = kQueryStatsArgs + countof(kQueryStatsArgs);
1707 QueryStats * list = NULL;
1708
1709 p = &list;
1710 for (args = kQueryStatsArgs; args < end; ++args)
1711 {
1712 err = QueryStatsCreate(args->domainStr, args->altDomainStr, args->test, args->terminal, &stats);
1713 require_noerr_quiet(err, exit);
1714
1715 *p = stats;
1716 p = &stats->next;
1717 }
1718
1719 *outList = list;
1720 list = NULL;
1721 err = mStatus_NoError;
1722
1723 exit:
1724 QueryStatsFreeList(list);
1725 return (err);
1726 }
1727
1728 //===========================================================================================================================
1729 // CreateResolveStatsList
1730 //===========================================================================================================================
1731
1732 mDNSlocal mStatus CreateResolveStatsList(ResolveStatsDomain **outList)
1733 {
1734 mStatus err;
1735 unsigned int i;
1736 ResolveStatsDomain * domain;
1737 ResolveStatsDomain ** p;
1738 ResolveStatsDomain * list = NULL;
1739
1740 p = &list;
1741 for (i = 0; i < (unsigned int)countof(kResolveStatsDomains); ++i)
1742 {
1743 err = ResolveStatsDomainCreate(kResolveStatsDomains[i], &domain);
1744 require_noerr_quiet(err, exit);
1745
1746 *p = domain;
1747 p = &domain->next;
1748 }
1749
1750 *outList = list;
1751 list = NULL;
1752 err = mStatus_NoError;
1753
1754 exit:
1755 FreeResolveStatsList(list);
1756 return (err);
1757 }
1758
1759 //===========================================================================================================================
1760 // FreeResolveStatsList
1761 //===========================================================================================================================
1762
1763 mDNSlocal void FreeResolveStatsList(ResolveStatsDomain *inList)
1764 {
1765 ResolveStatsDomain * domain;
1766
1767 while ((domain = inList) != NULL)
1768 {
1769 inList = domain->next;
1770 ResolveStatsDomainFree(domain);
1771 }
1772 }
1773
1774 //===========================================================================================================================
1775 // FreeResolveStatsServerList
1776 //===========================================================================================================================
1777
1778 mDNSlocal void FreeResolveStatsServerList(ResolveStatsDNSServer *inList)
1779 {
1780 ResolveStatsDNSServer * server;
1781
1782 while ((server = inList) != NULL)
1783 {
1784 inList = server->next;
1785 ResolveStatsDNSServerFree(server);
1786 }
1787 }
1788
1789 //===========================================================================================================================
1790 // SubmitAWDMetric
1791 //===========================================================================================================================
1792
1793 mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID)
1794 {
1795 mStatus err;
1796
1797 switch (inMetricID)
1798 {
1799 case AWDMetricId_MDNSResponder_DNSStatistics:
1800 err = SubmitAWDMetricQueryStats();
1801 break;
1802
1803 case AWDMetricId_MDNSResponder_ResolveStats:
1804 err = SubmitAWDMetricResolveStats();
1805 break;
1806
1807 case AWDMetricId_MDNSResponder_ServicesStats:
1808 [AWDMetricManagerSoft postMetricWithId:AWDMetricId_MDNSResponder_ServicesStats unsignedIntegerValue:max_num_regservices];
1809 KQueueLock();
1810 // reset the no of max services since we want to collect the max no of services registered per AWD submission period
1811 max_num_regservices = curr_num_regservices;
1812 KQueueUnlock("SubmitAWDSimpleMetricServiceStats");
1813 err = mStatus_NoError;
1814 break;
1815
1816 case AWDMetricId_MDNSResponder_DNSMessageSizeStats:
1817 err = SubmitAWDMetricDNSMessageSizeStats();
1818 break;
1819
1820 default:
1821 err = mStatus_UnsupportedErr;
1822 break;
1823 }
1824
1825 if (err) LogMsg("SubmitAWDMetric for metric ID 0x%08X failed with error %d", inMetricID, err);
1826 return (err);
1827 }
1828
1829 //===========================================================================================================================
1830 // SubmitAWDMetricQueryStats
1831 //===========================================================================================================================
1832
1833 mDNSlocal mStatus AddQueryStats(AWDMDNSResponderDNSStatistics *inMetric, const QueryStats *inStats);
1834 mDNSlocal mStatus AddDNSHistSet(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell);
1835
1836 mDNSlocal mStatus SubmitAWDMetricQueryStats(void)
1837 {
1838 mStatus err;
1839 BOOL success;
1840 QueryStats * stats;
1841 QueryStats * statsList;
1842 QueryStats * newStatsList;
1843 AWDMetricContainer * container = nil;
1844 AWDMDNSResponderDNSStatistics * metric = nil;
1845
1846 newStatsList = NULL;
1847 CreateQueryStatsList(&newStatsList);
1848
1849 KQueueLock();
1850 statsList = gQueryStatsList;
1851 gQueryStatsList = newStatsList;
1852 KQueueUnlock("SubmitAWDMetricQueryStats");
1853
1854 container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_DNSStatistics];
1855 require_action_quiet(container, exit, err = mStatus_UnknownErr);
1856
1857 metric = [[AWDMDNSResponderDNSStatisticsSoft alloc] init];
1858 require_action_quiet(metric, exit, err = mStatus_UnknownErr);
1859
1860 while ((stats = statsList) != NULL)
1861 {
1862 err = AddQueryStats(metric, stats);
1863 require_noerr_quiet(err, exit);
1864
1865 statsList = stats->next;
1866 QueryStatsFree(stats);
1867 }
1868
1869 container.metric = metric;
1870 success = [gAWDServerConnection submitMetric:container];
1871 LogMsg("SubmitAWDMetricQueryStats: metric submission %s.", success ? "succeeded" : "failed");
1872 err = success ? mStatus_NoError : mStatus_UnknownErr;
1873
1874 exit:
1875 [metric release];
1876 [container release];
1877 QueryStatsFreeList(statsList);
1878 return (err);
1879 }
1880
1881 mDNSlocal mStatus AddQueryStats(AWDMDNSResponderDNSStatistics *inMetric, const QueryStats *inStats)
1882 {
1883 mStatus err;
1884
1885 if (inStats->nonCellular)
1886 {
1887 err = AddDNSHistSet(inMetric, inStats->nonCellular, QueryStatsGetDomainString(inStats), mDNSfalse);
1888 require_noerr_quiet(err, exit);
1889 }
1890 if (inStats->cellular)
1891 {
1892 err = AddDNSHistSet(inMetric, inStats->cellular, QueryStatsGetDomainString(inStats), mDNStrue);
1893 require_noerr_quiet(err, exit);
1894 }
1895 err = mStatus_NoError;
1896
1897 exit:
1898 return (err);
1899 }
1900
1901 mDNSlocal mStatus AddDNSHistSet(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell)
1902 {
1903 mStatus err;
1904 AWDDNSDomainStats * awdStats;
1905
1906 if (inSet->histA)
1907 {
1908 err = CreateAWDDNSDomainStats(inSet->histA, inDomain, inForCell, AWDDNSDomainStats_RecordType_A, &awdStats);
1909 require_noerr_quiet(err, exit);
1910
1911 [inMetric addStats:awdStats];
1912 [awdStats release];
1913 }
1914 if (inSet->histAAAA)
1915 {
1916 err = CreateAWDDNSDomainStats(inSet->histAAAA, inDomain, inForCell, AWDDNSDomainStats_RecordType_AAAA, &awdStats);
1917 require_noerr_quiet(err, exit);
1918
1919 [inMetric addStats:awdStats];
1920 [awdStats release];
1921 }
1922 err = mStatus_NoError;
1923
1924 exit:
1925 return (err);
1926 }
1927
1928 //===========================================================================================================================
1929 // SubmitAWDMetricResolveStats
1930 //===========================================================================================================================
1931
1932 mDNSlocal mStatus SubmitAWDMetricResolveStats(void)
1933 {
1934 mStatus err;
1935 ResolveStatsDomain * newResolveStatsList;
1936 ResolveStatsDomain * domainList = NULL;
1937 ResolveStatsDNSServer * serverList = NULL;
1938 AWDMetricContainer * container = nil;
1939 AWDMDNSResponderResolveStats * metric = nil;
1940 ResolveStatsDNSServer * server;
1941 ResolveStatsDomain * domain;
1942 BOOL success;
1943
1944 err = CreateResolveStatsList(&newResolveStatsList);
1945 require_noerr_quiet(err, exit);
1946
1947 KQueueLock();
1948 domainList = gResolveStatsList;
1949 serverList = gResolveStatsServerList;
1950 gResolveStatsList = newResolveStatsList;
1951 gResolveStatsServerList = NULL;
1952 gResolveStatsNextServerID = 0;
1953 gResolveStatsObjCount = 0;
1954 KQueueUnlock("SubmitAWDMetricResolveStats");
1955
1956 container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_ResolveStats];
1957 require_action_quiet(container, exit, err = mStatus_UnknownErr);
1958
1959 metric = [[AWDMDNSResponderResolveStatsSoft alloc] init];
1960 require_action_quiet(metric, exit, err = mStatus_UnknownErr);
1961
1962 while ((server = serverList) != NULL)
1963 {
1964 AWDMDNSResponderResolveStatsDNSServer * awdServer;
1965
1966 serverList = server->next;
1967 err = ResolveStatsDNSServerCreateAWDVersion(server, &awdServer);
1968 ResolveStatsDNSServerFree(server);
1969 require_noerr_quiet(err, exit);
1970
1971 [metric addServer:awdServer];
1972 [awdServer release];
1973 }
1974
1975 while ((domain = domainList) != NULL)
1976 {
1977 AWDMDNSResponderResolveStatsDomain * awdDomain;
1978
1979 domainList = domain->next;
1980 err = ResolveStatsDomainCreateAWDVersion(domain, &awdDomain);
1981 ResolveStatsDomainFree(domain);
1982 require_noerr_quiet(err, exit);
1983
1984 [metric addDomain:awdDomain];
1985 [awdDomain release];
1986 }
1987
1988 container.metric = metric;
1989 success = [gAWDServerConnection submitMetric:container];
1990 LogMsg("SubmitAWDMetricResolveStats: metric submission %s.", success ? "succeeded" : "failed");
1991 err = success ? mStatus_NoError : mStatus_UnknownErr;
1992
1993 exit:
1994 [metric release];
1995 [container release];
1996 FreeResolveStatsList(domainList);
1997 FreeResolveStatsServerList(serverList);
1998 return (err);
1999 }
2000
2001 //===========================================================================================================================
2002 // SubmitAWDMetricDNSMessageSizeStats
2003 //===========================================================================================================================
2004
2005 mDNSlocal mStatus SubmitAWDMetricDNSMessageSizeStats(void)
2006 {
2007 mStatus err;
2008 DNSMessageSizeStats * stats;
2009 DNSMessageSizeStats * newStats;
2010 AWDMetricContainer * container;
2011 AWDMDNSResponderDNSMessageSizeStats * metric = nil;
2012 BOOL success;
2013
2014 newStats = NULL;
2015 DNSMessageSizeStatsCreate(&newStats);
2016
2017 KQueueLock();
2018 stats = gDNSMessageSizeStats;
2019 gDNSMessageSizeStats = newStats;
2020 KQueueUnlock("SubmitAWDMetricDNSMessageSizeStats");
2021
2022 container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_DNSMessageSizeStats];
2023 require_action_quiet(container, exit, err = mStatus_UnknownErr);
2024
2025 metric = [[AWDMDNSResponderDNSMessageSizeStatsSoft alloc] init];
2026 require_action_quiet(metric, exit, err = mStatus_UnknownErr);
2027
2028 if (stats)
2029 {
2030 size_t binCount;
2031 uint32_t bins[Max(kQuerySizeBinCount, kResponseSizeBinCount)];
2032
2033 // Set query size counts.
2034
2035 binCount = CopyHistogramBins(bins, stats->querySizeBins, kQuerySizeBinCount);
2036 [metric setQuerySizeCounts:bins count:(NSUInteger)binCount];
2037
2038 // Set response size counts.
2039
2040 binCount = CopyHistogramBins(bins, stats->responseSizeBins, kResponseSizeBinCount);
2041 [metric setResponseSizeCounts:bins count:(NSUInteger)binCount];
2042 }
2043
2044 container.metric = metric;
2045 success = [gAWDServerConnection submitMetric:container];
2046 LogMsg("SubmitAWDMetricDNSMessageSizeStats: metric submission %s.", success ? "succeeded" : "failed");
2047 err = success ? mStatus_NoError : mStatus_UnknownErr;
2048
2049 exit:
2050 [metric release];
2051 [container release];
2052 if (stats) DNSMessageSizeStatsFree(stats);
2053 return (err);
2054 }
2055
2056 //===========================================================================================================================
2057 // CreateAWDDNSDomainStats
2058 //===========================================================================================================================
2059
2060 mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats)
2061 {
2062 mStatus err;
2063 AWDDNSDomainStats * awdStats = nil;
2064 NSString * domain = nil;
2065 size_t binCount;
2066 uint32_t sendCountBins[kQueryStatsSendCountBinCount];
2067 uint32_t latencyBins[kQueryStatsLatencyBinCount];
2068 uint32_t expiredAnswerBins[kQueryStatsExpiredAnswerStateCount];
2069
2070 awdStats = [[AWDDNSDomainStatsSoft alloc] init];
2071 require_action_quiet(awdStats, exit, err = mStatus_UnknownErr);
2072
2073 domain = [[NSString alloc] initWithUTF8String:inDomain];
2074 require_action_quiet(domain, exit, err = mStatus_UnknownErr);
2075
2076 awdStats.domain = domain;
2077 awdStats.networkType = inForCell ? AWDDNSDomainStats_NetworkType_Cellular : AWDDNSDomainStats_NetworkType_NonCellular;
2078 awdStats.recordType = inType;
2079
2080 // Positively answered query send counts
2081
2082 binCount = CopyHistogramBins(sendCountBins, inHist->answeredQuerySendCountBins, kQueryStatsSendCountBinCount);
2083 [awdStats setAnsweredQuerySendCounts:sendCountBins count:(NSUInteger)binCount];
2084
2085 // 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
2086 // was sent out on the wire. In that case, include the associated latency bins as well.
2087
2088 if (binCount > 1)
2089 {
2090 binCount = CopyHistogramBins(latencyBins, inHist->responseLatencyBins, kQueryStatsLatencyBinCount);
2091 [awdStats setResponseLatencyMs:latencyBins count:(NSUInteger)binCount];
2092 }
2093
2094 // Negatively answered query send counts
2095
2096 binCount = CopyHistogramBins(sendCountBins, inHist->negAnsweredQuerySendCountBins, kQueryStatsSendCountBinCount);
2097 [awdStats setNegAnsweredQuerySendCounts:sendCountBins count:(NSUInteger)binCount];
2098
2099 if (binCount > 1)
2100 {
2101 binCount = CopyHistogramBins(latencyBins, inHist->negResponseLatencyBins, kQueryStatsLatencyBinCount);
2102 [awdStats setNegResponseLatencyMs:latencyBins count:(NSUInteger)binCount];
2103 }
2104
2105 // Unanswered query send counts
2106
2107 binCount = CopyHistogramBins(sendCountBins, inHist->unansweredQuerySendCountBins, kQueryStatsSendCountBinCount);
2108 [awdStats setUnansweredQuerySendCounts:sendCountBins count:(NSUInteger)binCount];
2109
2110 if (binCount > 1)
2111 {
2112 binCount = CopyHistogramBins(latencyBins, inHist->unansweredQueryDurationBins, kQueryStatsLatencyBinCount);
2113 [awdStats setUnansweredQueryDurationMs:latencyBins count:(NSUInteger)binCount];
2114 }
2115
2116 // Expired answers states
2117
2118 binCount = CopyHistogramBins(expiredAnswerBins, inHist->expiredAnswerStateBins, kQueryStatsExpiredAnswerStateCount);
2119 [awdStats setExpiredAnswerStates:expiredAnswerBins count:(NSUInteger)binCount];
2120
2121 *outStats = awdStats;
2122 awdStats = nil;
2123 err = mStatus_NoError;
2124
2125 exit:
2126 [domain release];
2127 [awdStats release];
2128 return (err);
2129 }
2130
2131 //===========================================================================================================================
2132 // LogDNSHistSet
2133 //===========================================================================================================================
2134
2135 mDNSlocal void LogDNSHistSet(const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell)
2136 {
2137 if (inSet->histA) LogDNSHist(inSet->histA, inDomain, inForCell, "A");
2138 if (inSet->histAAAA) LogDNSHist(inSet->histAAAA, inDomain, inForCell, "AAAA");
2139 }
2140
2141 //===========================================================================================================================
2142 // LogDNSHist
2143 //===========================================================================================================================
2144
2145 #define Percent(N, D) (((N) * 100) / (D)), ((((N) * 10000) / (D)) % 100)
2146 #define PercentFmt "%3u.%02u"
2147 #define LogStat(LABEL, COUNT, ACCUMULATOR, TOTAL) \
2148 LogMsgNoIdent("%s %5u " PercentFmt " " PercentFmt, (LABEL), (COUNT), Percent(COUNT, TOTAL), Percent(ACCUMULATOR, TOTAL))
2149
2150 mDNSlocal void LogDNSHist(const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType)
2151 {
2152 unsigned int totalAnswered;
2153 unsigned int totalNegAnswered;
2154 unsigned int totalUnanswered;
2155 int i;
2156
2157 totalAnswered = 0;
2158 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
2159 {
2160 totalAnswered += inHist->answeredQuerySendCountBins[i];
2161 }
2162
2163 totalNegAnswered = 0;
2164 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
2165 {
2166 totalNegAnswered += inHist->negAnsweredQuerySendCountBins[i];
2167 }
2168
2169 totalUnanswered = 0;
2170 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
2171 {
2172 totalUnanswered += inHist->unansweredQuerySendCountBins[i];
2173 }
2174
2175 LogMsgNoIdent("Domain: %s (%s, %s)", inDomain, inForCell ? "C" : "NC", inType);
2176 LogMsgNoIdent("Answered questions %4u", totalAnswered);
2177 LogMsgNoIdent("Negatively answered questions %4u", totalNegAnswered);
2178 LogMsgNoIdent("Unanswered questions %4u", totalUnanswered);
2179 LogMsgNoIdent("Expired - no cached answer %4u", inHist->expiredAnswerStateBins[ExpiredAnswer_Allowed]);
2180 LogMsgNoIdent("Expired - answered from cache %4u", inHist->expiredAnswerStateBins[ExpiredAnswer_AnsweredWithExpired]);
2181 LogMsgNoIdent("Expired - cache changed %4u", inHist->expiredAnswerStateBins[ExpiredAnswer_ExpiredAnswerChanged]);
2182 LogMsgNoIdent("-- Query send counts ---------");
2183 LogDNSHistSendCounts(inHist->answeredQuerySendCountBins);
2184 LogMsgNoIdent("-- Query send counts (NAQs) --");
2185 LogDNSHistSendCounts(inHist->negAnsweredQuerySendCountBins);
2186
2187 if (totalAnswered > inHist->answeredQuerySendCountBins[0])
2188 {
2189 LogMsgNoIdent("--- Response times -----------");
2190 LogDNSHistLatencies(inHist->responseLatencyBins);
2191 }
2192
2193 if (totalNegAnswered > inHist->negAnsweredQuerySendCountBins[0])
2194 {
2195 LogMsgNoIdent("--- Response times (NAQs) ----");
2196 LogDNSHistLatencies(inHist->negResponseLatencyBins);
2197 }
2198
2199 if (totalUnanswered > 0)
2200 {
2201 LogMsgNoIdent("--- Unanswered query times ---");
2202 LogDNSHistLatencies(inHist->unansweredQueryDurationBins);
2203 }
2204 }
2205
2206 //===========================================================================================================================
2207 // LogDNSHistSendCounts
2208 //===========================================================================================================================
2209
2210 mDNSlocal void LogDNSHistSendCounts(const uint16_t inSendCountBins[kQueryStatsSendCountBinCount])
2211 {
2212 uint32_t total;
2213 char label[16];
2214 int i;
2215
2216 total = 0;
2217 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
2218 {
2219 total += inSendCountBins[i];
2220 }
2221
2222 if (total > 0)
2223 {
2224 uint32_t accumulator = 0;
2225
2226 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
2227 {
2228 accumulator += inSendCountBins[i];
2229 if (i < (kQueryStatsSendCountBinCount - 1))
2230 {
2231 snprintf(label, sizeof(label), "%2d ", i);
2232 }
2233 else
2234 {
2235 snprintf(label, sizeof(label), "%2d+", i);
2236 }
2237 LogStat(label, inSendCountBins[i], accumulator, total);
2238 if (accumulator == total) break;
2239 }
2240 }
2241 else
2242 {
2243 LogMsgNoIdent("No data.");
2244 }
2245 }
2246
2247 //===========================================================================================================================
2248 // LogDNSHistLatencies
2249 //===========================================================================================================================
2250
2251 mDNSlocal void LogDNSHistLatencies(const uint16_t inLatencyBins[kQueryStatsLatencyBinCount])
2252 {
2253 uint32_t total;
2254 int i;
2255 char label[16];
2256
2257 total = 0;
2258 for (i = 0; i < kQueryStatsLatencyBinCount; ++i)
2259 {
2260 total += inLatencyBins[i];
2261 }
2262
2263 if (total > 0)
2264 {
2265 uint32_t accumulator = 0;
2266
2267 for (i = 0; i < kQueryStatsLatencyBinCount; ++i)
2268 {
2269 accumulator += inLatencyBins[i];
2270 if (i < (int)countof(kResponseLatencyMsLimits))
2271 {
2272 snprintf(label, sizeof(label), "< %5u ms", kResponseLatencyMsLimits[i]);
2273 }
2274 else
2275 {
2276 snprintf(label, sizeof(label), "< ∞ ms");
2277 }
2278 LogStat(label, inLatencyBins[i], accumulator, total);
2279 if (accumulator == total) break;
2280 }
2281 }
2282 else
2283 {
2284 LogMsgNoIdent("No data.");
2285 }
2286 }
2287
2288 //===========================================================================================================================
2289 // LogDNSMessageSizeStats
2290 //===========================================================================================================================
2291
2292 mDNSlocal void LogDNSMessageSizeStats(const uint16_t *inBins, size_t inBinCount, unsigned int inBinWidth)
2293 {
2294 size_t i;
2295 uint32_t total;
2296
2297 total = 0;
2298 for (i = 0; i < inBinCount; ++i)
2299 {
2300 total += inBins[i];
2301 }
2302
2303 if (total > 0)
2304 {
2305 uint32_t accumulator;
2306 unsigned int lower, upper;
2307 char label[16];
2308
2309 accumulator = 0;
2310 upper = 0;
2311 for (i = 0; i < inBinCount; ++i)
2312 {
2313 accumulator += inBins[i];
2314 lower = upper + 1;
2315 if (i < (inBinCount - 1))
2316 {
2317 upper += inBinWidth;
2318 snprintf(label, sizeof(label), "%3u - %-3u", lower, upper);
2319 }
2320 else
2321 {
2322 snprintf(label, sizeof(label), "%3u+ ", lower);
2323 }
2324 LogStat(label, inBins[i], accumulator, total);
2325 if (accumulator == total) break;
2326 }
2327 }
2328 else
2329 {
2330 LogMsgNoIdent("No data.");
2331 }
2332 }
2333
2334 //===========================================================================================================================
2335 // CopyHistogramBins
2336 //
2337 // Note: The return value is the size (in number of elements) of the smallest contiguous sub-array that contains the first
2338 // bin and all bins with non-zero values.
2339 //===========================================================================================================================
2340
2341 mDNSlocal size_t CopyHistogramBins(uint32_t *inDstBins, uint16_t *inSrcBins, size_t inBinCount)
2342 {
2343 size_t i;
2344 size_t minCount;
2345
2346 if (inBinCount == 0) return (0);
2347
2348 minCount = 1;
2349 for (i = 0; i < inBinCount; ++i)
2350 {
2351 inDstBins[i] = inSrcBins[i];
2352 if (inDstBins[i] > 0) minCount = i + 1;
2353 }
2354
2355 return (minCount);
2356 }
2357 #endif // TARGET_OS_IOS