Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -135,10 +135,11 @@ OFSettings.m \ OFString+PathAdditions.m SRCS_PLUGINS = OFPlugin.m SRCS_SOCKETS = OFDNSResolver.m \ OFDNSResourceRecord.m \ + OFDNSResponse.m \ OFHTTPClient.m \ OFHTTPCookie.m \ OFHTTPCookieManager.m \ OFHTTPRequest.m \ OFHTTPResponse.m \ Index: src/OFDNSResolver.h ================================================================== --- src/OFDNSResolver.h +++ src/OFDNSResolver.h @@ -15,10 +15,11 @@ * file. */ #import "OFObject.h" #import "OFDNSResourceRecord.h" +#import "OFDNSResponse.h" #import "OFRunLoop.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN @@ -62,13 +63,10 @@ OF_DNS_RESOLVER_ERROR_SERVER_NOT_IMPLEMENTED, /*! The server refused the query */ OF_DNS_RESOLVER_ERROR_SERVER_REFUSED } of_dns_resolver_error_t; -typedef OFDictionary OF_GENERIC(OFString *, - OFArray OF_GENERIC(OFDNSResourceRecord *) *) *of_dns_resolver_records_t; - /*! * @protocol OFDNSResolverDelegate OFDNSResolver.h ObjFW/OFDNSResolver.h * * @brief A delegate for OFDNSResolver. */ @@ -77,24 +75,17 @@ /*! * @brief This method is called when a DNS resolver resolved a domain name. * * @param resolver The acting resolver * @param domainName The fully qualified domain name used to resolve the host - * @param answerRecords The answer records from the name server, grouped by - * domain name - * @param authorityRecords The authority records from the name server, grouped - * by domain name - * @param additionalRecords Additional records sent by the name server, grouped - * by domain name + * @param response The response from the DNS server, or nil on error * @param exception An exception that happened during resolving, or nil on * success */ - (void)resolver: (OFDNSResolver *)resolver didResolveDomainName: (OFString *)domainName - answerRecords: (nullable of_dns_resolver_records_t)answerRecords - authorityRecords: (nullable of_dns_resolver_records_t)authorityRecords - additionalRecords: (nullable of_dns_resolver_records_t)additionalRecords + response: (nullable OFDNSResponse *)response exception: (nullable id)exception; /*! * @brief This method is called when a DNS resolver resolved a domain name to * socket addresses. Index: src/OFDNSResolver.m ================================================================== --- src/OFDNSResolver.m +++ src/OFDNSResolver.m @@ -161,34 +161,28 @@ } - (instancetype)initWithHost: (OFString *)host delegate: (id)delegate; - (bool)parseRecords: (OFArray *)records - answerRecords: (OFDictionary *)answerRecords - additionalRecords: (OFDictionary *)additionalRecords + response: (OFDNSResponse *)response recordType: (of_dns_resource_record_type_t)recordType recursion: (unsigned int)recursion result: (OFMutableArray *)result; - (void)resolveCNAME: (OFCNAMEDNSResourceRecord *)CNAME - answerRecords: (OFDictionary *)answerRecords - additionalRecords: (OFDictionary *)additionalRecords + response: (OFDNSResponse *)response recordType: (of_dns_resource_record_type_t)recordType recursion: (unsigned int)recursion result: (OFMutableArray *)result; -- (void)resolver: (OFDNSResolver *)resolver - didResolveCNAME: (OFString *)CNAME - answerRecords: (OFDictionary *)answerRecords - authorityRecords: (OFDictionary *)authorityRecords - additionalRecords: (OFDictionary *)additionalRecords - context: (OFNumber *)context - exception: (id)exception; +- (void)resolver: (OFDNSResolver *)resolver + didResolveCNAME: (OFString *)CNAME + response: (OFDNSResponse *)response + context: (OFNumber *)context + exception: (id)exception; - (void)done; - (void)resolver: (OFDNSResolver *)resolver didResolveDomainName: (OFString *)domainName - answerRecords: (OFDictionary *)answerRecords - authorityRecords: (OFDictionary *)authorityRecords - additionalRecords: (OFDictionary *)additionalRecords + response: (OFDNSResponse *)response context: (OFNumber *)context exception: (id)exception; @end @interface OFDNSResolverResolveSocketAddressesDelegate: OFObject @@ -657,22 +651,20 @@ [ret makeImmutable]; return ret; } -static void callback(id target, SEL selector, OFDNSResolver *resolver, - OFString *domainName, OFDictionary *answerRecords, - OFDictionary *authorityRecords, OFDictionary *additionalRecords, id context, - id exception) -{ - void (*method)(id, SEL, OFDNSResolver *, OFString *, OFDictionary *, - OFDictionary *, OFDictionary *, id, id) = (void (*)(id, SEL, - OFDNSResolver *, OFString *, OFDictionary *, OFDictionary *, - OFDictionary *, id, id))[target methodForSelector: selector]; - - method(target, selector, resolver, domainName, answerRecords, - authorityRecords, additionalRecords, context, exception); +static void +callback(id target, SEL selector, OFDNSResolver *resolver, OFString *domainName, + OFDNSResponse *response, id context, id exception) +{ + void (*method)(id, SEL, OFDNSResolver *, OFString *, OFDNSResponse *, + id, id) = (void (*)(id, SEL, OFDNSResolver *, OFString *, + OFDNSResponse *, id, id))[target methodForSelector: selector]; + + method(target, selector, resolver, domainName, response, context, + exception); } @implementation OFDNSResolverSettings - (instancetype)initWithNameServers: (OFArray *)nameServers searchDomains: (OFArray *)searchDomains @@ -842,43 +834,40 @@ [super dealloc]; } - (bool)parseRecords: (OFArray *)records - answerRecords: (OFDictionary *)answerRecords - additionalRecords: (OFDictionary *)additionalRecords + response: (OFDNSResponse *)response recordType: (of_dns_resource_record_type_t)recordType recursion: (unsigned int)recursion result: (OFMutableArray *)result { bool found = false; - for (OFDNSResourceRecord *record in records) { - if (record.recordClass != OF_DNS_RESOURCE_RECORD_CLASS_IN) + for (OF_KINDOF(OFDNSResourceRecord *) record in records) { + if ([record recordClass] != OF_DNS_RESOURCE_RECORD_CLASS_IN) continue; - if (record.recordType == recordType) { + if ([record recordType] == recordType) { [result addObject: record]; found = true; - } else if (record.recordType == + } else if ([record recordType] == OF_DNS_RESOURCE_RECORD_TYPE_CNAME) { - [self resolveCNAME: (OFCNAMEDNSResourceRecord *)record - answerRecords: answerRecords - additionalRecords: additionalRecords - recordType: recordType - recursion: recursion - result: result]; + [self resolveCNAME: record + response: response + recordType: recordType + recursion: recursion + result: result]; found = true; } } return found; } - (void)resolveCNAME: (OFCNAMEDNSResourceRecord *)CNAME - answerRecords: (OFDictionary *)answerRecords - additionalRecords: (OFDictionary *)additionalRecords + response: (OFDNSResponse *)response recordType: (of_dns_resource_record_type_t)recordType recursion: (unsigned int)recursion result: (OFMutableArray *)result { OFString *alias = CNAME.alias; @@ -885,21 +874,19 @@ bool found = false; if (recursion == 0) return; - if ([self parseRecords: [answerRecords objectForKey: alias] - answerRecords: answerRecords - additionalRecords: additionalRecords + if ([self parseRecords: [response.answerRecords objectForKey: alias] + response: response recordType: recordType recursion: recursion - 1 result: result]) found = true; - if ([self parseRecords: [additionalRecords objectForKey: alias] - answerRecords: answerRecords - additionalRecords: additionalRecords + if ([self parseRecords: [response.additionalRecords objectForKey: alias] + response: response recordType: recordType recursion: recursion - 1 result: result]) found = true; @@ -919,26 +906,21 @@ recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN recordType: recordType runLoopMode: runLoopMode target: self selector: @selector(resolver: - didResolveCNAME: - answerRecords: - authorityRecords: - additionalRecords:context: - exception:) + didResolveCNAME:response: + context:exception:) context: recordTypeNumber]; } } -- (void)resolver: (OFDNSResolver *)resolver - didResolveCNAME: (OFString *)CNAME - answerRecords: (OFDictionary *)answerRecords - authorityRecords: (OFDictionary *)authorityRecords - additionalRecords: (OFDictionary *)additionalRecords - context: (OFNumber *)context - exception: (id)exception +- (void)resolver: (OFDNSResolver *)resolver + didResolveCNAME: (OFString *)CNAME + response: (OFDNSResponse *)response + context: (OFNumber *)context + exception: (id)exception { /* * TODO: Error handling could be improved. Ignore error if there are * responses, otherwise propagate error. */ @@ -959,21 +941,19 @@ return; } records = [OFMutableArray array]; - if ([self parseRecords: [answerRecords objectForKey: CNAME] - answerRecords: answerRecords - additionalRecords: additionalRecords + if ([self parseRecords: [response.answerRecords objectForKey: CNAME] + response: response recordType: recordType recursion: CNAME_RECURSION result: records]) found = true; - if ([self parseRecords: [additionalRecords objectForKey: CNAME] - answerRecords: answerRecords - additionalRecords: additionalRecords + if ([self parseRecords: [response.additionalRecords objectForKey: CNAME] + response: response recordType: recordType recursion: CNAME_RECURSION result: records]) found = true; @@ -1044,13 +1024,11 @@ exception: exception]; } - (void)resolver: (OFDNSResolver *)resolver didResolveDomainName: (OFString *)domainName - answerRecords: (OFDictionary *)answerRecords - authorityRecords: (OFDictionary *)authorityRecords - additionalRecords: (OFDictionary *)additionalRecords + response: (OFDNSResponse *)response context: (OFNumber *)context exception: (id)exception { /* * TODO: Error handling could be improved. Ignore error if there are @@ -1078,13 +1056,12 @@ [self done]; return; } - [self parseRecords: [answerRecords objectForKey: _domainName] - answerRecords: answerRecords - additionalRecords: additionalRecords + [self parseRecords: [response.answerRecords objectForKey: _domainName] + response: response recordType: recordType recursion: CNAME_RECURSION result: _records]; if (_expectedResponses == 0) @@ -1652,24 +1629,19 @@ objc_autoreleasePoolPop(pool); } - (void)of_resolver: (OFDNSResolver *)resolver didResolveDomainName: (OFString *)domainName - answerRecords: (of_dns_resolver_records_t)answerRecords - authorityRecords: (of_dns_resolver_records_t)authorityRecords - additionalRecords: (of_dns_resolver_records_t)additionalRecords + response: (OFDNSResponse *)response context: (id)delegate exception: (id)exception { if ([delegate respondsToSelector: @selector(resolver: - didResolveDomainName:answerRecords:authorityRecords: - additionalRecords:exception:)]) + didResolveDomainName:response:exception:)]) [delegate resolver: resolver didResolveDomainName: domainName - answerRecords: answerRecords - authorityRecords: authorityRecords - additionalRecords: additionalRecords + response: response exception: exception]; } - (void)asyncResolveHost: (OFString *)host delegate: (id )delegate @@ -1678,12 +1650,11 @@ recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN recordType: OF_DNS_RESOURCE_RECORD_TYPE_ALL runLoopMode: of_run_loop_mode_default target: self selector: @selector(of_resolver:didResolveDomainName: - answerRecords:authorityRecords: - additionalRecords:context:exception:) + response:context:exception:) context: delegate]; } - (void)asyncResolveHost: (OFString *)host recordClass: (of_dns_resource_record_class_t)recordClass @@ -1694,12 +1665,11 @@ recordClass: recordClass recordType: recordType runLoopMode: of_run_loop_mode_default target: self selector: @selector(of_resolver:didResolveDomainName: - answerRecords:authorityRecords: - additionalRecords:context:exception:) + response:context:exception:) context: delegate]; } - (void)asyncResolveHost: (OFString *)host recordClass: (of_dns_resource_record_class_t)recordClass @@ -1711,12 +1681,11 @@ recordClass: recordClass recordType: recordType runLoopMode: runLoopMode target: self selector: @selector(of_resolver:didResolveDomainName: - answerRecords:authorityRecords: - additionalRecords:context:exception:) + response:context:exception:) context: delegate]; } - (void)of_asyncResolveHost: (OFString *)host recordClass: (of_dns_resource_record_class_t)recordClass @@ -1861,11 +1830,11 @@ recordClass: query->_recordClass recordType: query->_recordType error: OF_DNS_RESOLVER_ERROR_TIMEOUT]; callback(query->_target, query->_selector, self, query->_domainName, - nil, nil, nil, query->_context, exception); + nil, query->_context, exception); } - (bool)socket: (OFUDPSocket *)sock didReceiveIntoBuffer: (void *)buffer_ length: (size_t)length @@ -1873,10 +1842,11 @@ exception: (id)exception { unsigned char *buffer = buffer_; OFDictionary *answerRecords = nil, *authorityRecords = nil; OFDictionary *additionalRecords = nil; + OFDNSResponse *response = nil; OFNumber *ID; OFDNSResolverQuery *query; if (exception != nil) return true; @@ -2000,19 +1970,22 @@ answerRecords = parseSection(buffer, length, &i, numAnswers); authorityRecords = parseSection(buffer, length, &i, numAuthorityRecords); additionalRecords = parseSection(buffer, length, &i, numAdditionalRecords); + response = [OFDNSResponse + responseWithAnswerRecords: answerRecords + authorityRecords: authorityRecords + additionalRecords: additionalRecords]; } @catch (id e) { callback(query->_target, query->_selector, self, - query->_domainName, nil, nil, nil, query->_context, e); + query->_domainName, nil, query->_context, e); return true; } callback(query->_target, query->_selector, self, query->_domainName, - answerRecords, authorityRecords, additionalRecords, - query->_context, nil); + response, query->_context, nil); return true; } - (void)asyncResolveSocketAddressesForHost: (OFString *)host @@ -2195,13 +2168,11 @@ recordType: OF_DNS_RESOURCE_RECORD_TYPE_AAAA runLoopMode: runLoopMode target: context selector: @selector(resolver: didResolveDomainName: - answerRecords:authorityRecords: - additionalRecords:context: - exception:) + response:context:exception:) context: recordTypeNumber]; } #endif if (addressFamily == OF_SOCKET_ADDRESS_FAMILY_IPV4 || @@ -2211,13 +2182,11 @@ recordType: OF_DNS_RESOURCE_RECORD_TYPE_A runLoopMode: runLoopMode target: context selector: @selector(resolver: didResolveDomainName: - answerRecords:authorityRecords: - additionalRecords:context: - exception:) + response:context:exception:) context: [OFNumber numberWithInt: OF_DNS_RESOURCE_RECORD_TYPE_A]]; objc_autoreleasePoolPop(pool); } @@ -2282,14 +2251,13 @@ recordClass: query->_recordClass recordType: query->_recordType error: OF_DNS_RESOLVER_ERROR_CANCELED]; callback(query->_target, query->_selector, self, - query->_domainName, nil, nil, nil, query->_context, - exception); + query->_domainName, nil, query->_context, exception); } [_queries removeAllObjects]; objc_autoreleasePoolPop(pool); } @end ADDED src/OFDNSResponse.h Index: src/OFDNSResponse.h ================================================================== --- src/OFDNSResponse.h +++ src/OFDNSResponse.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFObject.h" +#import "OFDNSResourceRecord.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFArray OF_GENERIC(ObjectType); +@class OFDictionary OF_GENERIC(KeyType, ObjectType); + +typedef OFDictionary OF_GENERIC(OFString *, OFArray OF_GENERIC( + OF_KINDOF(OFDNSResourceRecord *)) *) *of_dns_response_records_t; + +/*! + * @class OFDNSResponse OFDNSResponse.h ObjFW/OFDNSResponse.h + * + * @brief A class storing a response from @ref OFDNSResolver. + */ +@interface OFDNSResponse: OFObject +{ + of_dns_response_records_t _Nullable _answerRecords; + of_dns_response_records_t _Nullable _authorityRecords; + of_dns_response_records_t _Nullable _additionalRecords; + OF_RESERVE_IVARS(4) +} + +/*! + * @brief The answer records of the response. + */ +@property OF_NULLABLE_PROPERTY (nonatomic, readonly) + of_dns_response_records_t answerRecords; + +/*! + * @brief The authority records of the response. + */ +@property OF_NULLABLE_PROPERTY (nonatomic, readonly) + of_dns_response_records_t authorityRecords; + +/*! + * @brief The additional records of the response. + */ +@property OF_NULLABLE_PROPERTY (nonatomic, readonly) + of_dns_response_records_t additionalRecords; + +/*! + * @brief Creates a new, autoreleased OFDNSResponse. + * + * @param answerRecords The answer records of the response + * @param authorityRecords The authority records of the response + * @param additionalRecords The additional records of the response + * @return A new, autoreleased OFDNSResponse + */ ++ (instancetype) + responseWithAnswerRecords: (nullable of_dns_response_records_t)answerRecords + authorityRecords: (nullable of_dns_response_records_t) + authorityRecords + additionalRecords: (nullable of_dns_response_records_t) + additionalRecords; + +/*! + * @brief Initializes an already allocated OFDNSResponse. + * + * @param answerRecords The answer records of the response + * @param authorityRecords The authority records of the response + * @param additionalRecords The additional records of the response + * @return An initialized OFDNSResponse + */ +- (instancetype) + initWithAnswerRecords: (nullable of_dns_response_records_t)answerRecords + authorityRecords: (nullable of_dns_response_records_t)authorityRecords + additionalRecords: (nullable of_dns_response_records_t)additionalRecords + OF_DESIGNATED_INITIALIZER; + +- (instancetype)init OF_UNAVAILABLE; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFDNSResponse.m Index: src/OFDNSResponse.m ================================================================== --- src/OFDNSResponse.m +++ src/OFDNSResponse.m @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFDNSResponse.h" +#import "OFDictionary.h" +#import "OFString.h" + +@implementation OFDNSResponse +@synthesize answerRecords = _answerRecords; +@synthesize authorityRecords = _authorityRecords; +@synthesize additionalRecords = _additionalRecords; + ++ (instancetype) + responseWithAnswerRecords: (of_dns_response_records_t)answerRecords + authorityRecords: (of_dns_response_records_t)authorityRecords + additionalRecords: (of_dns_response_records_t)additionalRecords +{ + return [[[self alloc] + initWithAnswerRecords: answerRecords + authorityRecords: authorityRecords + additionalRecords: additionalRecords] autorelease]; +} + +- (instancetype) + initWithAnswerRecords: (of_dns_response_records_t)answerRecords + authorityRecords: (of_dns_response_records_t)authorityRecords + additionalRecords: (of_dns_response_records_t)additionalRecords +{ + self = [super init]; + + @try { + _answerRecords = [answerRecords copy]; + _authorityRecords = [authorityRecords copy]; + _additionalRecords = [additionalRecords copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)init OF_UNAVAILABLE +{ + OF_INVALID_INIT_METHOD +} + +- (void)dealloc +{ + [_answerRecords release]; + [_authorityRecords release]; + [_additionalRecords release]; + + [super dealloc]; +} + +- (bool)isEqual: (id)object +{ + OFDNSResponse *other; + + if (![object isKindOfClass: [OFDNSResponse class]]) + return false; + + other = object; + + if (other->_answerRecords != _answerRecords && + ![other->_answerRecords isEqual: _answerRecords]) + return false; + + if (other->_authorityRecords != _authorityRecords && + ![other->_authorityRecords isEqual: _authorityRecords]) + return false; + + if (other->_additionalRecords != _additionalRecords && + ![other->_additionalRecords isEqual: _additionalRecords]) + return false; + + return true; +} + +- (uint32_t)hash +{ + uint32_t hash; + + OF_HASH_INIT(hash); + OF_HASH_ADD_HASH(hash, [_answerRecords hash]); + OF_HASH_ADD_HASH(hash, [_authorityRecords hash]); + OF_HASH_ADD_HASH(hash, [_additionalRecords hash]); + OF_HASH_FINALIZE(hash); + + return hash; +} + +- (OFString *)description +{ + OFString *answerRecords = [_answerRecords.description + stringByReplacingOccurrencesOfString: @"\n" + withString: @"\n\t"]; + OFString *authorityRecords = [_authorityRecords.description + stringByReplacingOccurrencesOfString: @"\n" + withString: @"\n\t"]; + OFString *additionalRecords = [_additionalRecords.description + stringByReplacingOccurrencesOfString: @"\n" + withString: @"\n\t"]; + + return [OFString stringWithFormat: + @"", + answerRecords, authorityRecords, additionalRecords]; +} +@end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -73,12 +73,13 @@ # import "OFStreamSocket.h" # import "OFTCPSocket.h" # import "OFUDPSocket.h" # import "OFTLSSocket.h" # import "OFKernelEventObserver.h" -# import "OFDNSResolver.h" # import "OFDNSResourceRecord.h" +# import "OFDNSResponse.h" +# import "OFDNSResolver.h" #endif #ifdef OF_HAVE_SOCKETS # ifdef OF_HAVE_THREADS # import "OFHTTPClient.h" # endif Index: utils/ofdns/OFDNS.m ================================================================== --- utils/ofdns/OFDNS.m +++ utils/ofdns/OFDNS.m @@ -29,26 +29,21 @@ OF_APPLICATION_DELEGATE(OFDNS) @implementation OFDNS - (void)resolver: (OFDNSResolver *)resolver didResolveDomainName: (OFString *)domainName - answerRecords: (of_dns_resolver_records_t)answerRecords - authorityRecords: (of_dns_resolver_records_t)authorityRecords - additionalRecords: (of_dns_resolver_records_t)additionalRecords + response: (OFDNSResponse *)response exception: (id)exception { if (exception != nil) { [of_stderr writeFormat: @"Failed to resolve: %@\n", exception]; [OFApplication terminateWithStatus: 1]; } [of_stdout writeFormat: @"FQDN: %@\n" - @"Answer records: %@\n" - @"Authority records: %@\n" - @"Additional records: %@\n", - domainName, answerRecords, authorityRecords, - additionalRecords]; + @"Response: %@\n", + domainName, response]; [OFApplication terminate]; } - (void)applicationDidFinishLaunching