This document describes how to obtain DNS resolution time and LocalDNS resolution results when making network requests on iOS.
1. Overview
For standard network requests on iOS, you can obtain the LocalDNS resolution time from the delegate methods in NSURLSessionDelegate. If you integrate the HTTPDNS software development kit (SDK), the SDK bypasses the LocalDNS for domain name resolution. To obtain the DNS resolution time in this case, retrieve the timestamps before and after you call the SDK's resolution API and then calculate the difference.
On iOS, you can use the getaddrinfo method to obtain the resolution results of the local DNS. You can compare these with the expected resolution results to determine if any hijacking has occurred.
2. Methods to obtain DNS resolution time
If your network request interface uses the https://domain/path format, you can refer to the following code to obtain the DNS resolution time:
- Note
If you have used the iOS14 native encryption solution to set up custom DNS, the DNS resolution time obtained using the code below will be the resolution time of the custom DNS. If not used, the DNS resolution time obtained will be the LocalDNS resolution time.
#pragma mark URLSession Delegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
if ([metrics.transactionMetrics count] <= 0) return;
[metrics.transactionMetrics enumerateObjectsUsingBlock:^(NSURLSessionTaskTransactionMetrics *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
NSLog(@"%@",[NSString stringWithFormat:@"Request URL:%@",[obj.request.URL absoluteString]]);
NSLog(@"%@",[NSString stringWithFormat:@"Server IP:%@",obj.remoteAddress]);
NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol;
NSLog(@"%@",[NSString stringWithFormat:@"DNS type is %ld", (long)dnsProtocol]);
NSLog(@"%@",[NSString stringWithFormat:@"0:Unknown, 1:UDP, 2:TCP, 3:TLS, 4:HTTPS"]);
if (obj.domainLookupStartDate && obj.domainLookupEndDate) {
int dnsLookupTime = ceil([obj.domainLookupEndDate timeIntervalSinceDate:obj.domainLookupStartDate] * 1000);
NSLog(@"%@",[NSString stringWithFormat:@"DNS start time: %@, DNS end time: %@", obj.domainLookupStartDate, obj.domainLookupEndDate]);
NSLog(@"%@",[NSString stringWithFormat:@"DNS resolution time in ms: %d",dnsLookupTime]);
}
}
}];
}If you have integrated the HTTPDNS SDK, use the following code to obtain the domain name resolution time from the HTTPDNS SDK:
CFTimeInterval startTimer = CACurrentMediaTime();
//Replace with the resolution method of the SDK you are using
[[DNSResolver share] getIpv4DataWithDomain:@"main.m.taobao.com" complete:^(NSArray<NSString *> *dataArray) {
CFTimeInterval endTimer = CACurrentMediaTime();
UInt32 rtt = (endTimer - startTimer) * 1000;
NSLog(@"%@",[NSString stringWithFormat:@"DNS resolution time in ms: %d",rtt]);
}];
3. Obtaining LocalDNS resolution results and performing hijacking detection comparison
You can use the following code to obtain LocalDNS resolution results and compare them with your expected resolution results to determine if any hijacking has occurred.
#import <Foundation/Foundation.h>
#import <netdb.h>
#import <arpa/inet.h>
- (void)resolveDomain:(NSString *)domain expectedIPs:(NSArray<NSString *> *)expectedIPs {
struct addrinfo hints, *result, *p;
memset(&hints, 0, sizeof(hints));
// Initialize hints structure
hints.ai_family = AF_UNSPEC; // IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP
// Execute DNS query
int s = getaddrinfo([domain UTF8String], NULL, &hints, &result);
if (s != 0) {
NSLog(@"getaddrinfo error: %s", gai_strerror(s));
return;
}
BOOL isHijacked = NO;
// Traverse results to obtain IPv4 and IPv6 addresses
for (p = result; p != NULL; p = p->ai_next) {
char ipBuffer[INET6_ADDRSTRLEN]; // Buffer for formatting IP addresses
if (p->ai_family == AF_INET) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
inet_ntop(AF_INET, &ipv4->sin_addr, ipBuffer, sizeof(ipBuffer));
NSLog(@"IPv4 address: %s", ipBuffer);
} else if (p->ai_family == AF_INET6) {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
inet_ntop(AF_INET6, &ipv6->sin6_addr, ipBuffer, sizeof(ipBuffer));
NSLog(@"IPv6 address: %s", ipBuffer);
}
// Compare resolution results with expected IP addresses
if (![expectedIPs containsObject:[NSString stringWithUTF8String:ipBuffer]]) {
isHijacked = YES;
}
}
// Determine if hijacking exists
if (isHijacked) {
NSLog(@"Warning: DNS hijacking may exist!");
} else {
NSLog(@"No DNS hijacking detected.");
}
// Release the memory
freeaddrinfo(result);
}