@@ -27,10 +27,11 @@ #import "OFDictionary.h" #import "OFFile.h" #import "OFLocale.h" #import "OFNumber.h" #import "OFString.h" +#import "OFTimer.h" #import "OFUDPSocket.h" #ifdef OF_WINDOWS # import "OFWindowsRegistryKey.h" #endif @@ -53,14 +54,15 @@ * also want to limit it to avoid DoS. Limiting it to 16 levels of pointers and * immediately rejecting pointers to itself seems like a fair balance. */ #define MAX_ALLOWED_POINTERS 16 +#define TIMEOUT 2 + /* * TODO: * - * - Timeouts * - Resolve with each search domain * - Iterate through name servers * - Fallback to TCP */ @@ -74,10 +76,11 @@ size_t _nameServersIndex, _searchDomainsIndex; OFMutableData *_queryData; id _target; SEL _selector; id _userContext; + OFTimer *_timer; } @property (readonly, nonatomic) OFString *host; @property (readonly, nonatomic) of_dns_resource_record_class_t recordClass; @property (readonly, nonatomic) of_dns_resource_record_type_t recordType; @@ -88,10 +91,11 @@ @property (nonatomic) size_t searchDomainsIndex; @property (readonly, nonatomic) OFMutableData *queryData; @property (readonly, nonatomic) id target; @property (readonly, nonatomic) SEL selector; @property (readonly, nonatomic) id userContext; +@property (readonly, nonatomic) OFTimer *timer; - (instancetype)initWithHost: (OFString *)host recordClass: (of_dns_resource_record_class_t)recordClass recordType: (of_dns_resource_record_type_t)recordType ID: (OFNumber *)ID @@ -98,11 +102,12 @@ nameServers: (OFArray OF_GENERIC(OFString *) *)nameServers searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains queryData: (OFMutableData *)queryData target: (id)target selector: (SEL)selector - userContext: (id)userContext; + userContext: (id)userContext + timer: (OFTimer *)timer; @end @interface OFDNSResolver () #ifdef OF_HAVE_FILES - (void)of_parseHosts: (OFString *)path; @@ -501,10 +506,11 @@ @synthesize ID = _ID, nameServers = _nameServers; @synthesize searchDomains = _searchDomains; @synthesize nameServersIndex = _nameServersIndex; @synthesize searchDomainsIndex = _searchDomainsIndex, queryData = _queryData; @synthesize target = _target, selector = _selector, userContext = _userContext; +@synthesize timer = _timer; - (instancetype)initWithHost: (OFString *)host recordClass: (of_dns_resource_record_class_t)recordClass recordType: (of_dns_resource_record_type_t)recordType ID: (OFNumber *)ID @@ -512,10 +518,11 @@ searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains queryData: (OFMutableData *)queryData target: (id)target selector: (SEL)selector userContext: (id)userContext + timer: (OFTimer *)timer { self = [super init]; @try { _host = [host copy]; @@ -526,10 +533,11 @@ _searchDomains = [searchDomains copy]; _queryData = [queryData retain]; _target = [target retain]; _selector = selector; _userContext = [userContext retain]; + _timer = [timer retain]; } @catch (id e) { [self release]; @throw e; } @@ -543,10 +551,11 @@ [_nameServers release]; [_searchDomains release]; [_queryData release]; [_target release]; [_userContext release]; + [_timer release]; [super dealloc]; } @end @@ -904,10 +913,11 @@ DNSResolverContext = [[[_queries objectForKey: ID] retain] autorelease]; if (DNSResolverContext == nil) return false; + [[DNSResolverContext timer] invalidate]; [_queries removeObjectForKey: ID]; target = [DNSResolverContext target]; selector = [DNSResolverContext selector]; callback = (void (*)(id, SEL, OFArray *, id, id)) @@ -1027,10 +1037,38 @@ callback(target, selector, answers, [DNSResolverContext userContext], nil); return false; } + +- (void)of_queryWithIDTimedOut: (OFNumber *)ID +{ + OFDNSResolver_context *DNSResolverContext = [_queries objectForKey: ID]; + id target; + SEL selector; + void (*callback)(id, SEL, OFArray *, id, id); + OFResolveHostFailedException *exception; + + if (DNSResolverContext == nil) + return; + + target = [[[DNSResolverContext target] retain] autorelease]; + selector = [DNSResolverContext selector]; + callback = (void (*)(id, SEL, OFArray *, id, id)) + [target methodForSelector: selector]; + + exception = [OFResolveHostFailedException + exceptionWithHost: [DNSResolverContext host] + recordClass: [DNSResolverContext recordClass] + recordType: [DNSResolverContext recordType] + error: OF_DNS_RESOLVER_ERROR_TIMEOUT]; + + [_queries removeObjectForKey: [DNSResolverContext ID]]; + + callback(target, selector, nil, + [DNSResolverContext userContext], exception); +} - (size_t)of_socket: (OFUDPSocket *)sock didSendBuffer: (void **)buffer bytesSent: (size_t)bytesSent receiver: (of_socket_address_t *)receiver @@ -1082,10 +1120,11 @@ selector: (SEL)selector context: (id)context { void *pool = objc_autoreleasePoolPush(); OFMutableData *data = [OFMutableData dataWithCapacity: 512]; + OFTimer *timer; OFDNSResolver_context *DNSResolverContext; OFNumber *ID; uint16_t tmp; OFUDPSocket *sock; of_socket_address_t address; @@ -1145,10 +1184,17 @@ /* QCLASS */ tmp = OF_BSWAP16_IF_LE(recordClass); [data addItems: &tmp count: 2]; + timer = [OFTimer + scheduledTimerWithTimeInterval: TIMEOUT + target: self + selector: @selector(of_queryWithIDTimedOut:) + object: ID + repeats: false]; + DNSResolverContext = [[[OFDNSResolver_context alloc] initWithHost: host recordClass: recordClass recordType: recordType ID: ID @@ -1155,11 +1201,12 @@ nameServers: _nameServers searchDomains: _searchDomains queryData: data target: target selector: selector - userContext: context] autorelease]; + userContext: context + timer: timer] autorelease]; [_queries setObject: DNSResolverContext forKey: ID]; address = of_socket_address_parse_ip( [[DNSResolverContext nameServers] firstObject], 53);