130 lines
3.8 KiB
Objective-C
130 lines
3.8 KiB
Objective-C
//
|
|
// QNDnsResolver.m
|
|
// HappyDNS
|
|
//
|
|
// Created by yangsen on 2021/7/28.
|
|
// Copyright © 2021 Qiniu Cloud Storage. All rights reserved.
|
|
//
|
|
|
|
#import "QNRecord.h"
|
|
#import "QNDomain.h"
|
|
#import "QNDnsError.h"
|
|
#import "QNDnsResponse.h"
|
|
#import "QNDnsResolver.h"
|
|
|
|
@interface QNDnsResolver()
|
|
|
|
@property(nonatomic, strong)dispatch_queue_t timerQueue;
|
|
|
|
@end
|
|
@implementation QNDnsResolver
|
|
+ (dispatch_queue_t)timeoutQueue {
|
|
static dispatch_once_t onceToken;
|
|
static dispatch_queue_t timerQueue;
|
|
dispatch_once(&onceToken, ^{
|
|
timerQueue = dispatch_queue_create("com.happyDns.timeoutQueue", DISPATCH_QUEUE_CONCURRENT);
|
|
});
|
|
return timerQueue;
|
|
}
|
|
|
|
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
|
|
NSError *err = nil;
|
|
QNDnsResponse *response = [self lookupHost:domain.domain error:&err];
|
|
if (err != nil) {
|
|
*error = err;
|
|
return @[];
|
|
}
|
|
|
|
NSMutableArray *records = [NSMutableArray array];
|
|
for (QNRecord *record in response.answerArray) {
|
|
if (record.type == kQNTypeA || record.type == kQNTypeAAAA || record.type == kQNTypeCname) {
|
|
[records addObject:record];
|
|
}
|
|
}
|
|
return [records copy];
|
|
}
|
|
|
|
- (QNDnsResponse *)lookupHost:(NSString *)host error:(NSError *__autoreleasing _Nullable *)error {
|
|
|
|
// 异步转同步
|
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
|
|
__block NSError *errorP = nil;
|
|
__block QNDnsResponse *dnsResponse = nil;
|
|
[self request:host recordType:self.recordType complete:^(QNDnsResponse *response, NSError *err) {
|
|
errorP = err;
|
|
dnsResponse = response;
|
|
dispatch_semaphore_signal(semaphore);
|
|
}];
|
|
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, self.timeout * NSEC_PER_SEC));
|
|
|
|
if (error != NULL) {
|
|
*error = errorP;
|
|
}
|
|
|
|
return dnsResponse;
|
|
}
|
|
|
|
- (void)request:(NSString *)host
|
|
recordType:(int)recordType
|
|
complete:(void(^)(QNDnsResponse *response, NSError *error))complete {
|
|
if (complete == nil) {
|
|
return;
|
|
}
|
|
|
|
if (self.servers == nil || self.servers.count == 0) {
|
|
complete(nil, kQNDnsInvalidParamError(@"server can not empty"));
|
|
return;
|
|
}
|
|
|
|
NSLock *locker = [[NSLock alloc] init];
|
|
__block BOOL hasCallBack = false;
|
|
__block BOOL completeCount = 0;
|
|
|
|
// 超时处理
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.timeout * NSEC_PER_SEC)), [QNDnsResolver timeoutQueue], ^{
|
|
BOOL shouldCallBack = false;
|
|
[locker lock];
|
|
if (!hasCallBack) {
|
|
shouldCallBack = true;
|
|
hasCallBack = true;
|
|
}
|
|
[locker unlock];
|
|
|
|
if (shouldCallBack) {
|
|
NSString *error = [NSString stringWithFormat:@"resolver timeout for server:%@ host:%@",[self.servers description], host];
|
|
complete(nil, kQNDnsInvalidParamError(error));
|
|
}
|
|
});
|
|
|
|
for (NSString *server in self.servers) {
|
|
[self request:server host:host recordType:recordType complete:^(QNDnsResponse *response, NSError *error) {
|
|
BOOL shouldCallBack = false;
|
|
|
|
[locker lock];
|
|
completeCount++;
|
|
if (completeCount == self.servers.count || (response != nil && response.rCode == 0 && !hasCallBack)) {
|
|
shouldCallBack = true;
|
|
hasCallBack = true;
|
|
}
|
|
[locker unlock];
|
|
|
|
if (shouldCallBack) {
|
|
complete(response, error);
|
|
}
|
|
}];
|
|
}
|
|
|
|
}
|
|
|
|
- (void)request:(NSString *)server
|
|
host:(NSString *)host
|
|
recordType:(int)recordType
|
|
complete:(void(^)(QNDnsResponse *response, NSError *error))complete {
|
|
if (complete != nil) {
|
|
complete(nil, kQNDnsMethodError(@"use sub class of QNDnsResolver"));
|
|
}
|
|
}
|
|
|
|
@end
|