本文档介绍了在 iOS 上进行网络请求时,获取 DNS 解析时间和LocalDNS的解析结果的方法。
1.概述
在iOS端正常进行网络请求时,可以通过NSURLSessionDelegate中的代理方法获得LocalDns的解析时间,如果您接入了阿里云公共DNS SDK,那么SDK会绕过LocalDns进行域名解析,此时获取DNS解析时间,只需在SDK的解析API前后分别获取时间然后取差值即可。
在iOS上可以使用getaddrinfo
方法获取本地DNS的解析结果,用户可以和期望的解析结果进行比对来判断是否有劫持情况。
2.获取DNS解析时间方案
如果您的网络请求接口使用https://domain/path方式,可以参考下面代码获取DNS解析时间:
- 说明
如果您没有使用iOS14原生加密方案进行自定义DNS设置,那么参考下面代码获取到的DNS解析时间为LocalDNS解析时间;如果您使用了iOS14原生加密方案进行自定义DNS设置,那么参考下面代码获取到的DNS解析时间为自定义DNS的解析时间。
#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:@"请求URL:%@",[obj.request.URL absoluteString]]);
NSLog(@"%@",[NSString stringWithFormat:@"服务器IP:%@",obj.remoteAddress]);
NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol;
NSLog(@"%@",[NSString stringWithFormat:@"DNS类型为 %ld", (long)dnsProtocol]);
NSLog(@"%@",[NSString stringWithFormat:@"0:未知,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开始时间:%@,DNS结束时间:%@", obj.domainLookupStartDate, obj.domainLookupEndDate]);
NSLog(@"%@",[NSString stringWithFormat:@"DNS解析时长单位ms:%d",dnsLookupTime]);
}
}
}];
}
如果您集成了公共DNS SDK,那么您可以参考以下代码获取阿里云公共DNS SDK的域名解析时间:
CFTimeInterval startTimer = CACurrentMediaTime();
//替换为您使用的SDK解析方法
[[DNSResolver share] getIpv4DataWithDomain:@"main.m.taobao.com" complete:^(NSArray<NSString *> *dataArray) {
CFTimeInterval endTimer = CACurrentMediaTime();
UInt32 rtt = (endTimer - startTimer) * 1000;
NSLog(@"%@",[NSString stringWithFormat:@"DNS解析时长单位ms:%d",rtt]);
}];
3.获取LocalDNS的解析结果并进行劫持检测对比
您可以通过以下代码获取LocalDNS解析结果用来和您期望的解析结果进行比对,以判断是否有劫持情况。
#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));
// 初始化 hints 结构
hints.ai_family = AF_UNSPEC; // IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP
// 执行 DNS 查询
int s = getaddrinfo([domain UTF8String], NULL, &hints, &result);
if (s != 0) {
NSLog(@"getaddrinfo error: %s", gai_strerror(s));
return;
}
BOOL isHijacked = NO;
// 遍历结果以获取 IPv4 和 IPv6 地址
for (p = result; p != NULL; p = p->ai_next) {
char ipBuffer[INET6_ADDRSTRLEN]; // 缓冲区用于格式化 IP 地址
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 地址: %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 地址: %s", ipBuffer);
}
// 比对解析结果与期望的 IP 地址
if (![expectedIPs containsObject:[NSString stringWithUTF8String:ipBuffer]]) {
isHijacked = YES;
}
}
// 判断是否存在劫持
if (isHijacked) {
NSLog(@"警告: 可能存在 DNS 劫持!");
} else {
NSLog(@"未检测到 DNS 劫持.");
}
// 释放内存
freeaddrinfo(result);
}
文档内容是否对您有帮助?