Index: src/OFDNSResolver.h ================================================================== --- src/OFDNSResolver.h +++ src/OFDNSResolver.h @@ -30,10 +30,11 @@ @class OFDNSResolverSettings; @class OFDate; @class OFDictionary OF_GENERIC(KeyType, ObjectType); @class OFMutableDictionary OF_GENERIC(KeyType, ObjectType); @class OFNumber; +@class OFPair OF_GENERIC(FirstType, SecondType); @class OFTCPSocket; @class OFUDPSocket; /** * @enum OFDNSResolverErrorCode OFDNSResolver.h ObjFW/OFDNSResolver.h @@ -132,10 +133,12 @@ char _buffer[OFDNSResolverBufferLength]; OFMutableDictionary OF_GENERIC(OFNumber *, OFDNSResolverContext *) *_queries; OFMutableDictionary OF_GENERIC(OFTCPSocket *, OFDNSResolverContext *) *_TCPQueries; + OFMutableDictionary OF_GENERIC(OFDNSQuery *, + OFPair OF_GENERIC(OFDate *, OFDNSResponse *) *) *_cache; } /** * @brief A dictionary of static hosts. * Index: src/OFDNSResolver.m ================================================================== --- src/OFDNSResolver.m +++ src/OFDNSResolver.m @@ -464,10 +464,24 @@ [ret makeImmutable]; return ret; } + +static bool +containsExpiredRecord(OFDNSResponseRecords responseRecords, uint32_t age) +{ + OFEnumerator *enumerator = [responseRecords objectEnumerator]; + OFArray OF_GENERIC(OFDNSResourceRecord *) *records; + + while ((records = [enumerator nextObject]) != nil) + for (OFDNSResourceRecord *record in records) + if (record.TTL < age) + return true; + + return false; +} @implementation OFDNSResolverContext - (instancetype)initWithQuery: (OFDNSQuery *)query ID: (OFNumber *)ID settings: (OFDNSResolverSettings *)settings @@ -576,10 +590,11 @@ @try { _settings = [[OFDNSResolverSettings alloc] init]; _queries = [[OFMutableDictionary alloc] init]; _TCPQueries = [[OFMutableDictionary alloc] init]; + _cache = [[OFMutableDictionary alloc] init]; [_settings reload]; } @catch (id e) { [self release]; @throw e; @@ -599,10 +614,11 @@ [_IPv6Socket cancelAsyncRequests]; [_IPv6Socket release]; #endif [_queries release]; [_TCPQueries release]; + [_cache release]; [super dealloc]; } - (OFDictionary *)staticHosts @@ -802,10 +818,29 @@ delegate: (id )delegate { void *pool = objc_autoreleasePoolPush(); OFNumber *ID; OFDNSResolverContext *context; + OFPair OF_GENERIC(OFDate *, OFDNSResponse *) *cacheEntry; + + if ((cacheEntry = [_cache objectForKey: query]) != nil) { + uint32_t age = + (uint32_t)-[cacheEntry.firstObject timeIntervalSinceNow]; + OFDNSResponse *response = cacheEntry.secondObject; + + if (!containsExpiredRecord(response.answerRecords, age) && + !containsExpiredRecord(response.authorityRecords, age) && + !containsExpiredRecord(response.additionalRecords, age)) { + [delegate resolver: self + didPerformQuery: query + response: response + exception: nil]; + + objc_autoreleasePoolPop(pool); + return; + } + } /* Random, unused ID */ do { ID = [OFNumber numberWithUnsignedShort: OFRandom16()]; } while ([_queries objectForKey: ID] != nil); @@ -1038,10 +1073,17 @@ } if (exception != nil) response = nil; + if (response != nil) + [_cache setObject: [OFPair pairWithFirstObject: [OFDate date] + secondObject: response] + forKey: context->_query]; + else + [_cache removeObjectForKey: context->_query]; + [context->_delegate resolver: self didPerformQuery: context->_query response: response exception: exception];