Index: src/OFDNSResolver.m ================================================================== --- src/OFDNSResolver.m +++ src/OFDNSResolver.m @@ -121,10 +121,34 @@ return nil; return [OFString stringWithCString: domain + 1 encoding: [OFLocale encoding]]; } + +static OFString * +parseString(const unsigned char *buffer, size_t length, size_t *idx) +{ + size_t i = *idx; + uint8_t stringLength; + OFString *string; + + if (i >= length) + @throw [OFTruncatedDataException exception]; + + stringLength = buffer[i++]; + + if (i + stringLength > length) + @throw [OFTruncatedDataException exception]; + + string = [OFString stringWithUTF8String: (char *)&buffer[i] + length: stringLength]; + i += stringLength; + + *idx = i; + + return string; +} static OFString * parseName(const unsigned char *buffer, size_t length, size_t *idx, uint_fast8_t pointerLevel) { @@ -341,10 +365,29 @@ return [[[OFPTRDNSResourceRecord alloc] initWithName: name recordClass: recordClass domainName: domainName + TTL: TTL] autorelease]; + } else if (recordType == OF_DNS_RESOURCE_RECORD_TYPE_HINFO) { + size_t j = i; + OFString *CPU = parseString(buffer, length, &j); + OFString *OS; + + if (j > i + dataLength) + @throw [OFInvalidServerReplyException exception]; + + OS = parseString(buffer, length, &j); + + if (j != i + dataLength) + @throw [OFInvalidServerReplyException exception]; + + return [[[OFHINFODNSResourceRecord alloc] + initWithName: name + recordClass: recordClass + CPU: CPU + OS: OS TTL: TTL] autorelease]; } else if (recordType == OF_DNS_RESOURCE_RECORD_TYPE_MX) { uint16_t preference; size_t j; OFString *mailExchange; Index: src/OFDNSResourceRecord.h ================================================================== --- src/OFDNSResourceRecord.h +++ src/OFDNSResourceRecord.h @@ -210,10 +210,53 @@ recordClass: (of_dns_resource_record_class_t)recordClass alias: (OFString *)alias TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER; @end +/*! + * @class OFHINFODNSResourceRecord \ + * OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h + * + * @brief A class representing an HINFO DNS resource record. + */ +@interface OFHINFODNSResourceRecord: OFDNSResourceRecord +{ + OFString *_CPU, *_OS; +} + +/*! + * The CPU of the host info of the resource record. + */ +@property (readonly, nonatomic) OFString *CPU; + +/*! + * The OS of the host info of the resource record. + */ +@property (readonly, nonatomic) OFString *OS; + +- (instancetype)initWithName: (OFString *)name + recordClass: (of_dns_resource_record_class_t)recordClass + recordType: (of_dns_resource_record_type_t)recordType + TTL: (uint32_t)TTL OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated OFPTRDNSResourceRecord with the + * specified name, class, domain name and time to live. + * + * @param name The name for the resource record + * @param recordClass The class code for the resource record + * @param CPU The CPU of the host info for the resource record + * @param OS The OS of the host info for the resource record + * @param TTL The time to live for the resource record + */ +- (instancetype)initWithName: (OFString *)name + recordClass: (of_dns_resource_record_class_t)recordClass + CPU: (OFString *)CPU + OS: (OFString *)OS + TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER; +@end + /*! * @class OFMXDNSResourceRecord \ * OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h * * @brief A class representing an MX DNS resource record. Index: src/OFDNSResourceRecord.m ================================================================== --- src/OFDNSResourceRecord.m +++ src/OFDNSResourceRecord.m @@ -440,10 +440,113 @@ @">", [self className], _name, of_dns_resource_record_class_to_string(_recordClass), _alias, _TTL]; } @end + +@implementation OFHINFODNSResourceRecord +@synthesize CPU = _CPU, OS = _OS; + +- (instancetype)initWithName: (OFString *)name + recordClass: (of_dns_resource_record_class_t)recordClass + recordType: (of_dns_resource_record_type_t)recordType + TTL: (uint32_t)TTL +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithName: (OFString *)name + recordClass: (of_dns_resource_record_class_t)recordClass + CPU: (OFString *)CPU + OS: (OFString *)OS + TTL: (uint32_t)TTL +{ + self = [super initWithName: name + recordClass: recordClass + recordType: OF_DNS_RESOURCE_RECORD_TYPE_HINFO + TTL: TTL]; + + @try { + _CPU = [CPU copy]; + _OS = [OS copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_CPU release]; + [_OS release]; + + [super dealloc]; +} + +- (bool)isEqual: (id)otherObject +{ + OFHINFODNSResourceRecord *otherRecord; + + if (![otherObject isKindOfClass: [OFHINFODNSResourceRecord class]]) + return false; + + otherRecord = otherObject; + + if (otherRecord->_name != _name && ![otherRecord->_name isEqual: _name]) + return false; + + if (otherRecord->_recordClass != _recordClass) + return false; + + if (otherRecord->_recordType != _recordType) + return false; + + if (otherRecord->_CPU != _CPU && ![otherRecord->_CPU isEqual: _CPU]) + return false; + + if (otherRecord->_OS != _OS && ![otherRecord->_OS isEqual: _OS]) + return false; + + return true; +} + +- (uint32_t)hash +{ + uint32_t hash; + + OF_HASH_INIT(hash); + + OF_HASH_ADD_HASH(hash, [_name hash]); + 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, [_CPU hash]); + OF_HASH_ADD_HASH(hash, [_OS hash]); + + OF_HASH_FINALIZE(hash); + + return hash; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"<%@:\n" + @"\tName = %@\n" + @"\tClass = %@\n" + @"\tCPU = %@\n" + @"\tOS = %@\n" + @"\tTTL = %" PRIu32 "\n" + @">", + [self className], _name, + of_dns_resource_record_class_to_string(_recordClass), _CPU, _OS, + _TTL]; +} +@end @implementation OFMXDNSResourceRecord @synthesize preference = _preference, mailExchange = _mailExchange; - (instancetype)initWithName: (OFString *)name