Index: src/OFDNSResolver.m ================================================================== --- src/OFDNSResolver.m +++ src/OFDNSResolver.m @@ -182,10 +182,54 @@ *idx = i; return [components componentsJoinedByString: @"."]; } + +static id +parseData(const unsigned char *buffer, size_t length, size_t i, + size_t dataLength, of_dns_resource_record_class_t recordClass, + of_dns_resource_record_type_t recordType) +{ + id data; + + if (recordClass == OF_DNS_RESOURCE_RECORD_CLASS_IN) { + size_t j; + + switch (recordType) { + case OF_DNS_RESOURCE_RECORD_TYPE_A: + if (dataLength != 4) + @throw [OFInvalidServerReplyException + exception]; + + data = [OFString stringWithFormat: + @"%u.%u.%u.%u", + buffer[i], buffer[i + 1], + buffer[i + 2], buffer[i + 3]]; + break; + case OF_DNS_RESOURCE_RECORD_TYPE_CNAME: + j = i; + + data = parseName(buffer, length, &j, + ALLOWED_POINTER_LEVELS); + + if (j != i + dataLength) + @throw [OFInvalidServerReplyException + exception]; + + break; + default: + data = [OFData dataWithItems: &buffer[i] + count: dataLength]; + break; + } + } else + data = [OFData dataWithItems: &buffer[i] + count: dataLength]; + + return data; +} @implementation OFDNSResolver_context @synthesize host = _host, nameServers = _nameServers; @synthesize searchDomains = _searchDomains; @synthesize nameServersIndex = _nameServersIndex; @@ -617,11 +661,10 @@ numQuestions = (buffer[4] << 8) | buffer[5]; numAnswers = (buffer[6] << 8) | buffer[7]; answers = [OFMutableArray arrayWithCapacity: numAnswers]; - /* "Consume" headers */ i = 12; /* * Skip over the questions - we use the ID to identify the * query. @@ -634,38 +677,39 @@ } for (uint_fast16_t j = 0; j < numAnswers; j++) { OFString *name = parseName(buffer, length, &i, ALLOWED_POINTER_LEVELS); - uint16_t type, dataClass; + of_dns_resource_record_class_t recordClass; + of_dns_resource_record_type_t recordType; uint32_t TTL; uint16_t dataLength; - OFData *data; + id data; OFDNSResourceRecord *record; if (i + 10 > length) @throw [OFTruncatedDataException exception]; - type = (buffer[i] << 16) | buffer[i + 1]; - dataClass = (buffer[i + 2] << 16) | buffer[i + 3]; + recordType = (buffer[i] << 16) | buffer[i + 1]; + recordClass = (buffer[i + 2] << 16) | buffer[i + 3]; TTL = (buffer[i + 4] << 24) | (buffer[i + 5] << 16) | (buffer[i + 6] << 8) | buffer[i + 7]; dataLength = (buffer[i + 8] << 16) | buffer[i + 9]; i += 10; if (i + dataLength > length) @throw [OFTruncatedDataException exception]; - data = [OFData dataWithItems: &buffer[i] - count: dataLength]; + data = parseData(buffer, length, i, dataLength, + recordClass, recordType); i += dataLength; record = [[[OFDNSResourceRecord alloc] initWithName: name - type: type - dataClass: dataClass + recordClass: recordClass + recordType: recordType data: data TTL: TTL] autorelease]; [answers addObject: record]; } @@ -760,16 +804,16 @@ [data addItems: [component UTF8String] count: length]; } /* QTYPE */ - tmp = OF_BSWAP16_IF_LE(1); /* A */ + tmp = OF_BSWAP16_IF_LE(OF_DNS_RESOURCE_RECORD_TYPE_A); [data addItems: &tmp count: 2]; /* QCLASS */ - tmp = OF_BSWAP16_IF_LE(1); /* IN */ + tmp = OF_BSWAP16_IF_LE(OF_DNS_RESOURCE_RECORD_CLASS_IN); [data addItems: &tmp count: 2]; DNSResolverContext = [[[OFDNSResolver_context alloc] initWithHost: host Index: src/OFDNSResourceRecord.h ================================================================== --- src/OFDNSResourceRecord.h +++ src/OFDNSResourceRecord.h @@ -17,62 +17,94 @@ #import "OFObject.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN + +/*! @file */ @class OFData; +/*! + * @brief The class of a DNS resource record. + */ +typedef enum { + OF_DNS_RESOURCE_RECORD_CLASS_IN = 1 +} of_dns_resource_record_class_t; + +/*! + * @brief The type of a DNS resource record. + */ +typedef enum { + OF_DNS_RESOURCE_RECORD_TYPE_A = 1, + OF_DNS_RESOURCE_RECORD_TYPE_NS = 2, + OF_DNS_RESOURCE_RECORD_TYPE_CNAME = 5, + OF_DNS_RESOURCE_RECORD_TYPE_SOA = 6, + OF_DNS_RESOURCE_RECORD_TYPE_PTR = 12, + OF_DNS_RESOURCE_RECORD_TYPE_MX = 15, + OF_DNS_RESOURCE_RECORD_TYPE_TXT = 16, + OF_DNS_RESOURCE_RECORD_TYPE_AAAA = 28 +} of_dns_resource_record_type_t; + /*! * @class OFDNSResourceRecord OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h * * @brief A class represenging a DNS resource record. */ @interface OFDNSResourceRecord: OFObject { OFString *_name; - uint16_t _type; - uint16_t _dataClass; - OFData *_data; + of_dns_resource_record_class_t _recordClass; + of_dns_resource_record_type_t _recordType; + id _data; uint32_t _TTL; } /** * @brief The domain name to which the resource record belongs. */ @property (readonly, nonatomic) OFString *name; /*! - * @brief The resource record type code. + * @brief The class of the data. */ -@property (readonly, nonatomic) uint16_t type; +@property (readonly, nonatomic) of_dns_resource_record_class_t recordClass; /*! - * @brief The class of the data. + * @brief The resource record type code. */ -@property (readonly, nonatomic) uint16_t dataClass; +@property (readonly, nonatomic) of_dns_resource_record_type_t recordType; /*! - * The data of the resource. + * The class and type-dependent data of the resource. + * + * For A and AAAA records, this is a string with the IP address. + * For CNAME records, this is a string with the alias. + * For anything else, this is OFData. */ -@property (readonly, nonatomic) OFData *data; +@property (readonly, nonatomic) id data; /*! * @brief The number of seconds after which the resource record should be * discarded from the cache. */ @property (readonly, nonatomic) uint32_t TTL; -/*! - * @brief If the resource record is an A or AAAA record, this contains the data - * interpreted as an IP address. - */ -@property (readonly, nonatomic) OFString *IPAddress; - - (instancetype)initWithName: (OFString *)name - type: (uint16_t)type - dataClass: (uint16_t)dataClass - data: (OFData *)data + recordClass: (of_dns_resource_record_class_t)recordClass + recordType: (of_dns_resource_record_type_t)recordType + data: (id)data TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER; @end + +#ifdef __cplusplus +extern "C" { +#endif +extern OFString *_Nonnull of_dns_resource_record_class_to_string( + of_dns_resource_record_class_t recordClass); +extern OFString *_Nonnull of_dns_resource_record_type_to_string( + of_dns_resource_record_type_t recordType); +#ifdef __cplusplus +} +#endif OF_ASSUME_NONNULL_END Index: src/OFDNSResourceRecord.m ================================================================== --- src/OFDNSResourceRecord.m +++ src/OFDNSResourceRecord.m @@ -19,27 +19,64 @@ #import "OFDNSResourceRecord.h" #import "OFData.h" #import "OFInvalidFormatException.h" + +OFString * +of_dns_resource_record_class_to_string( + of_dns_resource_record_class_t recordClass) +{ + switch (recordClass) { + case OF_DNS_RESOURCE_RECORD_CLASS_IN: + return @"IN"; + default: + return [OFString stringWithFormat: @"%u", recordClass]; + } +} + +OFString * +of_dns_resource_record_type_to_string(of_dns_resource_record_type_t recordType) +{ + switch (recordType) { + case OF_DNS_RESOURCE_RECORD_TYPE_A: + return @"A"; + case OF_DNS_RESOURCE_RECORD_TYPE_NS: + return @"NS"; + case OF_DNS_RESOURCE_RECORD_TYPE_CNAME: + return @"CNAME"; + case OF_DNS_RESOURCE_RECORD_TYPE_SOA: + return @"SOA"; + case OF_DNS_RESOURCE_RECORD_TYPE_PTR: + return @"PTR"; + case OF_DNS_RESOURCE_RECORD_TYPE_MX: + return @"MX"; + case OF_DNS_RESOURCE_RECORD_TYPE_TXT: + return @"TXT"; + case OF_DNS_RESOURCE_RECORD_TYPE_AAAA: + return @"AAAA"; + default: + return [OFString stringWithFormat: @"%u", recordType]; + } +} @implementation OFDNSResourceRecord -@synthesize name = _name, type = _type, dataClass = _dataClass, data = _data; -@synthesize TTL = _TTL; +@synthesize name = _name, recordClass = _recordClass, recordType = _recordType; +@synthesize data = _data, TTL = _TTL; - (instancetype)initWithName: (OFString *)name - type: (uint16_t)type - dataClass: (uint16_t)dataClass - data: (OFData *)data + recordClass: (of_dns_resource_record_class_t)recordClass + recordType: (of_dns_resource_record_type_t)recordType + data: (id)data TTL: (uint32_t)TTL { self = [super init]; @try { _name = [name copy]; - _type = type; - _dataClass = dataClass; + _recordClass = recordClass; + _recordType = recordType; _data = [data copy]; _TTL = TTL; } @catch (id e) { [self release]; @throw e; @@ -66,14 +103,14 @@ otherRecord = otherObject; if (otherRecord->_name != _name && ![otherRecord->_name isEqual: _name]) return false; - if (otherRecord->_type != _type) + if (otherRecord->_recordClass != _recordClass) return false; - if (otherRecord->_dataClass != _dataClass) + if (otherRecord->_recordType != _recordType) return false; if (otherRecord->_data != _data && ![otherRecord->_data isEqual: _data]) return false; @@ -88,14 +125,14 @@ uint32_t hash; OF_HASH_INIT(hash); OF_HASH_ADD_HASH(hash, [_name hash]); - OF_HASH_ADD(hash, _type >> 8); - OF_HASH_ADD(hash, _type); - OF_HASH_ADD(hash, _dataClass >> 8); - OF_HASH_ADD(hash, _dataClass); + OF_HASH_ADD(hash, _recordClass >> 8); + OF_HASH_ADD(hash, _recordClass); + OF_HASH_ADD(hash, _recordType >> 8); + OF_HASH_ADD(hash, _recordType); OF_HASH_ADD_HASH(hash, [_data hash]); OF_HASH_ADD(hash, _TTL >> 24); OF_HASH_ADD(hash, _TTL >> 16); OF_HASH_ADD(hash, _TTL >> 8); OF_HASH_ADD(hash, _TTL); @@ -105,47 +142,17 @@ return hash; } - (OFString *)description { - id data = _data; - - if (_dataClass == 1 && _type == 1) - data = [self IPAddress]; - return [OFString stringWithFormat: @"", - _name, _type, _dataClass, data, _TTL]; -} - -- (OFString *)IPAddress -{ - const unsigned char *dataItems; - - if (_dataClass != 1) - @throw [OFInvalidFormatException exception]; - - if ([_data itemSize] != 1) - @throw [OFInvalidFormatException exception]; - - dataItems = [_data items]; - - switch (_type) { - case 1: - if ([_data count] != 4) - @throw [OFInvalidFormatException exception]; - - return [OFString stringWithFormat: @"%u.%u.%u.%u", - dataItems[0], dataItems[1], dataItems[2], dataItems[3]]; - case 28: - /* TODO: Implement */ - default: - @throw [OFInvalidFormatException exception]; - } + _name, of_dns_resource_record_class_to_string(_recordClass), + of_dns_resource_record_type_to_string(_recordType), _data, _TTL]; } @end