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