Index: src/OFDNSResolver.h ================================================================== --- src/OFDNSResolver.h +++ src/OFDNSResolver.h @@ -14,12 +14,13 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #import "OFObject.h" -#import "OFString.h" #import "OFDNSResourceRecord.h" +#import "OFRunLoop.h" +#import "OFString.h" OF_ASSUME_NONNULL_BEGIN @class OFArray OF_GENERIC(ObjectType); @class OFDNSResolverQuery; @@ -241,10 +242,58 @@ recordType: (of_dns_resource_record_type_t)recordType target: (id)target selector: (SEL)selector context: (nullable id)context; +/*! + * @brief Asynchronously resolves the specified host. + * + * @param host The host to resolve + * @param recordClass The desired class of the records to query + * @param recordType The desired type of the records to query + * @param runLoopMode The run loop mode in which to resolve + * @param target The target to call with the result once resolving is done + * @param selector The selector to call on the target. The signature must be + * the following: + * @parblock + * + * void (OFDNSResolver *resolver, OFString *domainName, + * OFArray *> + * *_Nullable answerRecords, + * OFArray *> + * *_Nullable authorityRecords, + * OFArray *> + * *_Nullable additionalRecords, + * id _Nullable context, id _Nullable exception) + * + * `resolver` is the acting resolver.@n + * `domainName` is the fully qualified domain name used to + * resolve the host.@n + * `answerRecords` are the answer records from the name server, + * grouped by domain name. + * @n + * `authorityRecords` are the authority records from the name + * server, grouped by domain name.@n + * `additionalRecords` are additional records sent by the name + * server, grouped by domain name.@n + * `context` is the context object originally passed.@n + * `exception` is an exception that happened during resolving, + * otherwise nil. + * @endparblock + * @param context A context object to pass along to the target + */ +- (void)asyncResolveHost: (OFString *)host + recordClass: (of_dns_resource_record_class_t)recordClass + recordType: (of_dns_resource_record_type_t)recordType + runLoopMode: (of_run_loop_mode_t)runLoopMode + target: (id)target + selector: (SEL)selector + context: (nullable id)context; + /*! * @brief Asynchronously resolves the specified host to socket addresses. * * @param host The host to resolve * @param target The target to call with the result once resolving is done @@ -299,10 +348,44 @@ */ - (void)asyncResolveSocketAddressesForHost: (OFString *)host addressFamily: (of_socket_address_family_t) addressFamily target: (id)target + selector: (SEL)selector + context: (nullable id)context; + +/*! + * @brief Asynchronously resolves the specified host to socket addresses. + * + * @param host The host to resolve + * @param addressFamily The desired socket address family + * @param runLoopMode The run loop mode in which to resolve + * @param target The target to call with the result once resolving is done + * @param selector The selector to call on the target. The signature must be + * the following: + * @parblock + * + * void (OFDNSResolver *resolver, OFString *domainName, + * OFData *_Nullable socketAddresses, + * id _Nullable context, id _Nullable exception) + * + * `resolver` is the acting resolver.@n + * `domainName` is the fully qualified domain name used to + * resolve the host.@n + * `socketAddresses` is OFData containing several + * of_socket_address_t.@n + * `context` is the context object originally passed.@n + * `exception` is an exception that happened during resolving, + * otherwise nil. + * @endparblock + * @param context A context object to pass along to the target + */ +- (void)asyncResolveSocketAddressesForHost: (OFString *)host + addressFamily: (of_socket_address_family_t) + addressFamily + runLoopMode: (of_run_loop_mode_t)runLoopMode + target: (id)target selector: (SEL)selector context: (nullable id)context; /*! * @brief Closes all sockets and cancels all ongoing requests. Index: src/OFDNSResolver.m ================================================================== --- src/OFDNSResolver.m +++ src/OFDNSResolver.m @@ -230,14 +230,16 @@ recordClass: (of_dns_resource_record_class_t)recordClass recordType: (of_dns_resource_record_type_t)recordType settings: (OFDNSResolverSettings *)settings nameServersIndex: (size_t)nameServersIndex searchDomainsIndex: (size_t)searchDomainsIndex + runLoopMode: (of_run_loop_mode_t)runLoopMode target: (id)target selector: (SEL)selector context: (id)context; -- (void)of_sendQuery: (OFDNSResolverQuery *)query; +- (void)of_sendQuery: (OFDNSResolverQuery *)query + runLoopMode: (of_run_loop_mode_t)runLoopMode; - (void)of_queryWithIDTimedOut: (OFDNSResolverQuery *)query; - (size_t)of_socket: (OFUDPSocket *)sock didSendBuffer: (void **)buffer bytesSent: (size_t)bytesSent receiver: (of_socket_address_t *)receiver @@ -915,21 +917,25 @@ recursion: recursion - 1 result: result]) found = true; if (!found) { + of_run_loop_mode_t runLoopMode = + [[OFRunLoop currentRunLoop] currentMode]; OFNumber *recordTypeNumber = [OFNumber numberWithInt: recordType]; + _expectedResponses++; [result addObject: [OFPair pairWithFirstObject: CNAME secondObject: recordTypeNumber]]; [_resolver asyncResolveHost: alias recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN recordType: recordType + runLoopMode: runLoopMode target: self selector: @selector(resolver: didResolveCNAME: answerRecords:authorityRecords: additionalRecords:context: @@ -1589,29 +1595,17 @@ _lastConfigReload = nil; [self of_obtainSystemConfig]; } -- (void)asyncResolveHost: (OFString *)host - target: (id)target - selector: (SEL)selector - context: (id)context -{ - [self asyncResolveHost: host - recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN - recordType: OF_DNS_RESOURCE_RECORD_TYPE_ALL - target: target - selector: selector - context: context]; -} - - (void)of_resolveHost: (OFString *)host recordClass: (of_dns_resource_record_class_t)recordClass recordType: (of_dns_resource_record_type_t)recordType settings: (OFDNSResolverSettings *)settings nameServersIndex: (size_t)nameServersIndex searchDomainsIndex: (size_t)searchDomainsIndex + runLoopMode: (of_run_loop_mode_t)runLoopMode target: (id)target selector: (SEL)selector context: (id)context { void *pool = objc_autoreleasePoolPush(); @@ -1655,18 +1649,50 @@ selector: selector context: context] autorelease]; [_queries setObject: query forKey: ID]; - [self of_sendQuery: query]; + [self of_sendQuery: query + runLoopMode: runLoopMode]; objc_autoreleasePoolPop(pool); } + +- (void)asyncResolveHost: (OFString *)host + target: (id)target + selector: (SEL)selector + context: (id)context +{ + [self asyncResolveHost: host + recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN + recordType: OF_DNS_RESOURCE_RECORD_TYPE_ALL + runLoopMode: of_run_loop_mode_default + target: target + selector: selector + context: context]; +} + +- (void)asyncResolveHost: (OFString *)host + recordClass: (of_dns_resource_record_class_t)recordClass + recordType: (of_dns_resource_record_type_t)recordType + target: (id)target + selector: (SEL)selector + context: (id)context +{ + [self asyncResolveHost: host + recordClass: recordClass + recordType: recordType + runLoopMode: of_run_loop_mode_default + target: target + selector: selector + context: context]; +} - (void)asyncResolveHost: (OFString *)host recordClass: (of_dns_resource_record_class_t)recordClass recordType: (of_dns_resource_record_type_t)recordType + runLoopMode: (of_run_loop_mode_t)runLoopMode target: (id)target selector: (SEL)selector context: (id)context { void *pool = objc_autoreleasePoolPush(); @@ -1682,31 +1708,37 @@ recordClass: recordClass recordType: recordType settings: settings nameServersIndex: 0 searchDomainsIndex: 0 + runLoopMode: runLoopMode target: target selector: selector context: context]; objc_autoreleasePoolPop(pool); } - (void)of_sendQuery: (OFDNSResolverQuery *)query + runLoopMode: (of_run_loop_mode_t)runLoopMode { OFUDPSocket *sock; OFString *nameServer; [query->_cancelTimer invalidate]; [query->_cancelTimer release]; query->_cancelTimer = nil; - query->_cancelTimer = [[OFTimer - scheduledTimerWithTimeInterval: query->_settings->_timeout - target: self - selector: @selector(of_queryWithIDTimedOut:) - object: query - repeats: false] retain]; + query->_cancelTimer = [[OFTimer alloc] + initWithFireDate: [OFDate dateWithTimeIntervalSinceNow: + query->_settings->_timeout] + interval: query->_settings->_timeout + target: self + selector: @selector(of_queryWithIDTimedOut:) + object: query + repeats: false]; + [[OFRunLoop currentRunLoop] addTimer: query->_cancelTimer + forMode: runLoopMode]; nameServer = [query->_settings->_nameServers objectAtIndex: query->_nameServersIndex]; query->_usedNameServer = of_socket_address_parse_ip(nameServer, 53); @@ -1738,10 +1770,11 @@ } [sock asyncSendBuffer: [query->_queryData items] length: [query->_queryData count] receiver: query->_usedNameServer + runLoopMode: runLoopMode target: self selector: @selector(of_socket:didSendBuffer:bytesSent: receiver:context:exception:) context: query]; } @@ -1754,18 +1787,20 @@ return; if (query->_nameServersIndex + 1 < [query->_settings->_nameServers count]) { query->_nameServersIndex++; - [self of_sendQuery: query]; + [self of_sendQuery: query + runLoopMode: [[OFRunLoop currentRunLoop] currentMode]]; return; } if (query->_attempt < query->_settings->_maxAttempts) { query->_attempt++; query->_nameServersIndex = 0; - [self of_sendQuery: query]; + [self of_sendQuery: query + runLoopMode: [[OFRunLoop currentRunLoop] currentMode]]; return; } query = [[query retain] autorelease]; [_queries removeObjectForKey: query->_ID]; @@ -1798,10 +1833,11 @@ return 0; } [sock asyncReceiveIntoBuffer: [query allocMemoryWithSize: 512] length: 512 + runLoopMode: [[OFRunLoop currentRunLoop] currentMode] target: self selector: @selector(of_socket:didReceiveIntoBuffer: length:sender:context:exception:) context: nil]; @@ -1884,18 +1920,22 @@ error = OF_DNS_RESOLVER_ERROR_SERVER_FAILURE; break; case 3: if (query->_searchDomainsIndex + 1 < [query->_settings->_searchDomains count]) { + of_run_loop_mode_t runLoopMode = + [[OFRunLoop currentRunLoop] currentMode]; + query->_searchDomainsIndex++; [self of_resolveHost: query->_host recordClass: query->_recordClass recordType: query->_recordType settings: query->_settings nameServersIndex: query->_nameServersIndex searchDomainsIndex: query->_searchDomainsIndex + runLoopMode: runLoopMode target: query->_target selector: query->_selector context: query->_context]; return false; @@ -1958,30 +1998,39 @@ } - (void)asyncResolveSocketAddressesForHost: (OFString *)host target: (id)target selector: (SEL)selector - context: (nullable id)context + context: (id)context { -#ifdef OF_HAVE_IPV6 [self asyncResolveSocketAddressesForHost: host addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY + runLoopMode: of_run_loop_mode_default target: target selector: selector context: context]; -#else +} + +- (void)asyncResolveSocketAddressesForHost: (OFString *)host + addressFamily: (of_socket_address_family_t) + addressFamily + target: (id)target + selector: (SEL)selector + context: (id)context +{ [self asyncResolveSocketAddressesForHost: host - addressFamily: OF_SOCKET_ADDRESS_FAMILY_IPV4 + addressFamily: addressFamily + runLoopMode: of_run_loop_mode_default target: target selector: selector context: context]; -#endif } - (void)asyncResolveSocketAddressesForHost: (OFString *)host addressFamily: (of_socket_address_family_t) addressFamily + runLoopMode: (of_run_loop_mode_t)runLoopMode target: (id)target selector: (SEL)selector context: (id)userContext { void *pool = objc_autoreleasePoolPush(); @@ -1993,38 +2042,48 @@ selector: selector context: userContext] autorelease]; switch (addressFamily) { case OF_SOCKET_ADDRESS_FAMILY_IPV4: +#ifdef OF_HAVE_IPV6 case OF_SOCKET_ADDRESS_FAMILY_IPV6: +#endif context->_expectedResponses = 1; break; case OF_SOCKET_ADDRESS_FAMILY_ANY: +#ifdef OF_HAVE_IPV6 context->_expectedResponses = 2; +#else + context->_expectedResponses = 1; +#endif break; default: @throw [OFInvalidArgumentException exception]; } +#ifdef OF_HAVE_IPV6 if (addressFamily == OF_SOCKET_ADDRESS_FAMILY_IPV6 || addressFamily == OF_SOCKET_ADDRESS_FAMILY_ANY) [self asyncResolveHost: host recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN recordType: OF_DNS_RESOURCE_RECORD_TYPE_AAAA + runLoopMode: runLoopMode target: context selector: @selector(resolver:didResolveDomainName: answerRecords:authorityRecords: additionalRecords:context: exception:) context: [OFNumber numberWithInt: OF_DNS_RESOURCE_RECORD_TYPE_AAAA]]; +#endif if (addressFamily == OF_SOCKET_ADDRESS_FAMILY_IPV4 || addressFamily == OF_SOCKET_ADDRESS_FAMILY_ANY) [self asyncResolveHost: host recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN recordType: OF_DNS_RESOURCE_RECORD_TYPE_A + runLoopMode: runLoopMode target: context selector: @selector(resolver:didResolveDomainName: answerRecords:authorityRecords: additionalRecords:context: exception:)