@@ -77,26 +77,39 @@ #endif /* * TODO: * - * - Resolve with each search domain * - Fallback to TCP */ + +@interface OFDNSResolverSettings: OFObject +{ +@public + OFArray OF_GENERIC(OFString *) *_nameServers, *_searchDomains; + of_time_interval_t _timeout; + unsigned int _maxAttempts, _minNumberOfDotsInAbsoluteName; +} + +- (instancetype) + initWithNameServers: (OFArray *)nameServers + searchDomains: (OFArray *)searchDomains + timeout: (of_time_interval_t)timeout + maxAttempts: (unsigned int)maxAttempts + minNumberOfDotsInAbsoluteName: (unsigned int)minNumberOfDotsInAbsoluteName; +@end @interface OFDNSResolverQuery: OFObject { @public OFString *_host, *_domainName; of_dns_resource_record_class_t _recordClass; of_dns_resource_record_type_t _recordType; OFNumber *_ID; - OFArray OF_GENERIC(OFString *) *_nameServers, *_searchDomains; + OFDNSResolverSettings *_settings; size_t _nameServersIndex, _searchDomainsIndex; - of_time_interval_t _timeout; - unsigned int _maxAttempts; - size_t _attempt; + unsigned int _attempt; id _target; SEL _selector; id _context; OFData *_queryData; of_socket_address_t _usedNameServer; @@ -106,16 +119,13 @@ - (instancetype)initWithHost: (OFString *)host domainName: (OFString *)domainName recordClass: (of_dns_resource_record_class_t)recordClass recordType: (of_dns_resource_record_type_t)recordType ID: (OFNumber *)ID - nameServers: (OFArray OF_GENERIC(OFString *) *)nameServers + settings: (OFDNSResolverSettings *)settings nameServersIndex: (size_t)nameServersIndex - searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains searchDomainsIndex: (size_t)searchDomainsIndex - timeout: (of_time_interval_t)timeout - maxAttempts: (unsigned int)maxAttempts target: (id)target selector: (SEL)selector context: (id)context; @end @@ -134,16 +144,13 @@ #endif - (void)of_reloadConfig; - (void)of_resolveHost: (OFString *)host recordClass: (of_dns_resource_record_class_t)recordClass recordType: (of_dns_resource_record_type_t)recordType - nameServers: (OFArray OF_GENERIC(OFString *) *)nameServers + settings: (OFDNSResolverSettings *)settings nameServersIndex: (size_t)nameServersIndex - searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains searchDomainsIndex: (size_t)searchDomainsIndex - timeout: (unsigned int)timeout - maxAttempts: (unsigned int)maxAttempts target: (id)target selector: (SEL)selector context: (id)context; - (void)of_sendQuery: (OFDNSResolverQuery *)query; - (void)of_queryWithIDTimedOut: (OFDNSResolverQuery *)query; @@ -176,11 +183,11 @@ return [OFString stringWithCString: domain + 1 encoding: [OFLocale encoding]]; } static bool -isFQDN(OFString *host, unsigned int minNumberOfDotsInAbsoluteName) +isFQDN(OFString *host, OFDNSResolverSettings *settings) { const char *UTF8String = [host UTF8String]; size_t length = [host UTF8StringLength]; unsigned int dots = 0; @@ -189,11 +196,11 @@ for (size_t i = 0; i < length; i++) if (UTF8String[i] == '.') dots++; - return (dots >= minNumberOfDotsInAbsoluteName); + return (dots >= settings->_minNumberOfDotsInAbsoluteName); } static OFString * parseString(const unsigned char *buffer, size_t length, size_t *i) { @@ -555,23 +562,52 @@ [target methodForSelector: selector]; method(target, selector, resolver, domainName, answerRecords, authorityRecords, additionalRecords, context, exception); } + +@implementation OFDNSResolverSettings +- (instancetype)initWithNameServers: (OFArray *)nameServers + searchDomains: (OFArray *)searchDomains + timeout: (of_time_interval_t)timeout + maxAttempts: (unsigned int)maxAttempts + minNumberOfDotsInAbsoluteName: (unsigned int)minNumberOfDotsInAbsoluteName +{ + self = [super init]; + + @try { + _nameServers = [nameServers copy]; + _searchDomains = [searchDomains copy]; + _timeout = timeout; + _maxAttempts = maxAttempts; + _minNumberOfDotsInAbsoluteName = minNumberOfDotsInAbsoluteName; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_nameServers release]; + [_searchDomains release]; + + [super dealloc]; +} +@end @implementation OFDNSResolverQuery - (instancetype)initWithHost: (OFString *)host domainName: (OFString *)domainName recordClass: (of_dns_resource_record_class_t)recordClass recordType: (of_dns_resource_record_type_t)recordType ID: (OFNumber *)ID - nameServers: (OFArray OF_GENERIC(OFString *) *)nameServers + settings: (OFDNSResolverSettings *)settings nameServersIndex: (size_t)nameServersIndex - searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains searchDomainsIndex: (size_t)searchDomainsIndex - timeout: (of_time_interval_t)timeout - maxAttempts: (unsigned int)maxAttempts target: (id)target selector: (SEL)selector context: (id)context { self = [super init]; @@ -584,16 +620,13 @@ _host = [host copy]; _domainName = [domainName copy]; _recordClass = recordClass; _recordType = recordType; _ID = [ID retain]; - _nameServers = [nameServers copy]; + _settings = [settings retain]; _nameServersIndex = nameServersIndex; - _searchDomains = [searchDomains copy]; _searchDomainsIndex = searchDomainsIndex; - _timeout = timeout; - _maxAttempts = maxAttempts; _target = [target retain]; _selector = selector; _context = [context retain]; queryData = [OFMutableData dataWithCapacity: 512]; @@ -660,12 +693,11 @@ - (void)dealloc { [_host release]; [_domainName release]; [_ID release]; - [_nameServers release]; - [_searchDomains release]; + [_settings release]; [_target release]; [_context release]; [_queryData release]; [_cancelTimer release]; @@ -1064,16 +1096,13 @@ } - (void)of_resolveHost: (OFString *)host recordClass: (of_dns_resource_record_class_t)recordClass recordType: (of_dns_resource_record_type_t)recordType - nameServers: (OFArray OF_GENERIC(OFString *) *)nameServers + settings: (OFDNSResolverSettings *)settings nameServersIndex: (size_t)nameServersIndex - searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains searchDomainsIndex: (size_t)searchDomainsIndex - timeout: (unsigned int)timeout - maxAttempts: (unsigned int)maxAttempts target: (id)target selector: (SEL)selector context: (id)context { void *pool = objc_autoreleasePoolPush(); @@ -1086,15 +1115,22 @@ /* Random, unused ID */ do { ID = [OFNumber numberWithUInt16: (uint16_t)of_random()]; } while ([_queries objectForKey: ID] != nil); - if (isFQDN(host, _minNumberOfDotsInAbsoluteName)) + if (isFQDN(host, settings)) { domainName = host; - else + + if (![domainName hasSuffix: @"."]) + domainName = [domainName stringByAppendingString: @"."]; + } else { + OFString *searchDomain = [settings->_searchDomains + objectAtIndex: searchDomainsIndex]; + domainName = [OFString stringWithFormat: @"%@.%@.", - host, [searchDomains objectAtIndex: searchDomainsIndex]]; + host, searchDomain]; + } if ([domainName UTF8StringLength] > 253) @throw [OFOutOfRangeException exception]; query = [[[OFDNSResolverQuery alloc] @@ -1101,16 +1137,13 @@ initWithHost: host domainName: domainName recordClass: recordClass recordType: recordType ID: ID - nameServers: _nameServers + settings: settings nameServersIndex: nameServersIndex - searchDomains: _searchDomains searchDomainsIndex: searchDomainsIndex - timeout: _timeout - maxAttempts: _maxAttempts target: target selector: selector context: context] autorelease]; [_queries setObject: query forKey: ID]; @@ -1125,40 +1158,50 @@ recordType: (of_dns_resource_record_type_t)recordType target: (id)target selector: (SEL)selector context: (id)context { + void *pool = objc_autoreleasePoolPush(); + OFDNSResolverSettings *settings = [[[OFDNSResolverSettings alloc] + initWithNameServers: _nameServers + searchDomains: _searchDomains + timeout: _timeout + maxAttempts: _maxAttempts + minNumberOfDotsInAbsoluteName: _minNumberOfDotsInAbsoluteName] + autorelease]; + [self of_resolveHost: host recordClass: recordClass recordType: recordType - nameServers: _nameServers + settings: settings nameServersIndex: 0 - searchDomains: _searchDomains searchDomainsIndex: 0 - timeout: _timeout - maxAttempts: _maxAttempts target: target selector: selector context: context]; + + objc_autoreleasePoolPop(pool); } - (void)of_sendQuery: (OFDNSResolverQuery *)query { OFUDPSocket *sock; + OFString *nameServer; [query->_cancelTimer invalidate]; [query->_cancelTimer release]; query->_cancelTimer = nil; query->_cancelTimer = [[OFTimer - scheduledTimerWithTimeInterval: query->_timeout + scheduledTimerWithTimeInterval: query->_settings->_timeout target: self selector: @selector(of_queryWithIDTimedOut:) object: query repeats: false] retain]; - query->_usedNameServer = of_socket_address_parse_ip( - [query->_nameServers objectAtIndex: query->_nameServersIndex], 53); + nameServer = [query->_settings->_nameServers + objectAtIndex: query->_nameServersIndex]; + query->_usedNameServer = of_socket_address_parse_ip(nameServer, 53); switch (query->_usedNameServer.family) { #ifdef OF_HAVE_IPV6 case OF_SOCKET_ADDRESS_FAMILY_IPV6: if (_IPv6Socket == nil) { @@ -1199,17 +1242,18 @@ OFResolveHostFailedException *exception; if (query == nil) return; - if (query->_nameServersIndex + 1 < [query->_nameServers count]) { + if (query->_nameServersIndex + 1 < + [query->_settings->_nameServers count]) { query->_nameServersIndex++; [self of_sendQuery: query]; return; } - if (query->_attempt < query->_maxAttempts) { + if (query->_attempt < query->_settings->_maxAttempts) { query->_attempt++; query->_nameServersIndex = 0; [self of_sendQuery: query]; return; } @@ -1330,22 +1374,19 @@ case 2: error = OF_DNS_RESOLVER_ERROR_SERVER_FAILURE; break; case 3: if (query->_searchDomainsIndex + 1 < - [query->_searchDomains count]) { + [query->_settings->_searchDomains count]) { query->_searchDomainsIndex++; [self of_resolveHost: query->_host recordClass: query->_recordClass recordType: query->_recordType - nameServers: query->_nameServers + settings: query->_settings nameServersIndex: query->_nameServersIndex - searchDomains: query->_searchDomains searchDomainsIndex: query->_searchDomainsIndex - timeout: query->_timeout - maxAttempts: query->_maxAttempts target: query->_target selector: query->_selector context: query->_context]; return false;