Index: src/OFDNSResolver.m ================================================================== --- src/OFDNSResolver.m +++ src/OFDNSResolver.m @@ -246,11 +246,11 @@ } static OF_KINDOF(OFDNSResourceRecord *) createResourceRecord(OFString *name, of_dns_resource_record_class_t recordClass, of_dns_resource_record_type_t recordType, uint32_t TTL, - const unsigned char *buffer, size_t length, size_t i, size_t dataLength) + const unsigned char *buffer, size_t length, size_t i, uint16_t dataLength) { if (recordType == OF_DNS_RESOURCE_RECORD_TYPE_A && recordClass == OF_DNS_RESOURCE_RECORD_CLASS_IN) { OFString *address; @@ -278,20 +278,61 @@ authoritativeHost: authoritativeHost TTL: TTL] autorelease]; } else if (recordType == OF_DNS_RESOURCE_RECORD_TYPE_CNAME) { size_t j = i; OFString *alias = parseName(buffer, length, &j, - ALLOWED_POINTER_LEVELS); + ALLOWED_POINTER_LEVELS); if (j != i + dataLength) @throw [OFInvalidServerReplyException exception]; return [[[OFCNAMEDNSResourceRecord alloc] initWithName: name recordClass: recordClass alias: alias TTL: TTL] autorelease]; + } else if (recordType == OF_DNS_RESOURCE_RECORD_TYPE_SOA) { + size_t j = i; + OFString *primaryNameServer = parseName(buffer, length, &j, + ALLOWED_POINTER_LEVELS); + OFString *responsiblePerson; + uint32_t serialNumber, refreshInterval, retryInterval; + uint32_t expirationInterval, minTTL; + + if (j > i + dataLength) + @throw [OFInvalidServerReplyException exception]; + + responsiblePerson = parseName(buffer, length, &j, + ALLOWED_POINTER_LEVELS); + + if (dataLength - (j - i) != 20) + @throw [OFInvalidServerReplyException exception]; + + serialNumber = (buffer[j] << 24) | (buffer[j + 1] << 16) | + (buffer[j + 2] << 8) | buffer[j + 3]; + refreshInterval = (buffer[j + 4] << 24) | + (buffer[j + 5] << 16) | (buffer[j + 6] << 8) | + buffer[j + 7]; + retryInterval = (buffer[j + 8] << 24) | (buffer[j + 9] << 16) | + (buffer[j + 10] << 8) | buffer[j + 11]; + expirationInterval = (buffer[j + 12] << 24) | + (buffer[j + 13] << 16) | (buffer[j + 14] << 8) | + buffer[j + 15]; + minTTL = (buffer[j + 16] << 24) | (buffer[j + 17] << 16) | + (buffer[j + 18] << 8) | buffer[j + 19]; + + return [[[OFSOADNSResourceRecord alloc] + initWithName: name + recordClass: recordClass + primaryNameServer: primaryNameServer + responsiblePerson: responsiblePerson + serialNumber: serialNumber + refreshInterval: refreshInterval + retryInterval: retryInterval + expirationInterval: expirationInterval + minTTL: minTTL + TTL: TTL] autorelease]; } else if (recordType == OF_DNS_RESOURCE_RECORD_TYPE_PTR) { size_t j = i; OFString *domainName = parseName(buffer, length, &j, ALLOWED_POINTER_LEVELS); Index: src/OFDNSResourceRecord.h ================================================================== --- src/OFDNSResourceRecord.h +++ src/OFDNSResourceRecord.h @@ -326,10 +326,89 @@ recordClass: (of_dns_resource_record_class_t)recordClass domainName: (OFString *)domainName TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER; @end +/*! + * @class OFSOADNSResourceRecord \ + * OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h + * + * @brief A class representing an SOA DNS resource record. + */ +@interface OFSOADNSResourceRecord: OFDNSResourceRecord +{ + OFString *_primaryNameServer, *_responsiblePerson; + uint32_t _serialNumber, _refreshInterval, _retryInterval; + uint32_t _expirationInterval, _minTTL; +} + +/*! + * The the primary name server for the zone. + */ +@property (readonly, nonatomic) OFString *primaryNameServer; + +/*! + * The mailbox of the person responsible for the zone. + */ +@property (readonly, nonatomic) OFString *responsiblePerson; + +/*! + * The serial number of the original copy of the zone. + */ +@property (readonly, nonatomic) uint32_t serialNumber; + +/*! + * The refresh interval of the zone. + */ +@property (readonly, nonatomic) uint32_t refreshInterval; + +/*! + * The retry interval of the zone. + */ +@property (readonly, nonatomic) uint32_t retryInterval; + +/*! + * The expiration interval of the zone. + */ +@property (readonly, nonatomic) uint32_t expirationInterval; +/*! + * The minimum TTL of the zone. + */ +@property (readonly, nonatomic) uint32_t minTTL; + +- (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 OFTXTDNSResourceRecord with the + * specified name, class, text data and time to live. + * + * @param name The name for the resource record + * @param recordClass The class code for the resource record + * @param primaryNameServer The the primary name server for the zone + * @param responsiblePerson The mailbox of the person responsible for the zone + * @param serialNumber The serial number of the original copy of the zone + * @param refreshInterval The refresh interval of the zone + * @param retryInterval The retry interval of the zone + * @param expirationInterval The expiration interval of the zone + * @param minTTL The minimum TTL of the zone + * @param TTL The time to live for the resource record + */ +- (instancetype)initWithName: (OFString *)name + recordClass: (of_dns_resource_record_class_t)recordClass + primaryNameServer: (OFString *)primaryNameServer + responsiblePerson: (OFString *)responsiblePerson + serialNumber: (uint32_t)serialNumber + refreshInterval: (uint32_t)refreshInterval + retryInterval: (uint32_t)retryInterval + expirationInterval: (uint32_t)expirationInterval + minTTL: (uint32_t)minTTL + TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER; +@end + /*! * @class OFTXTDNSResourceRecord \ * OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h * * @brief A class representing a TXT DNS resource record. Index: src/OFDNSResourceRecord.m ================================================================== --- src/OFDNSResourceRecord.m +++ src/OFDNSResourceRecord.m @@ -683,10 +683,171 @@ [self className], _name, of_dns_resource_record_class_to_string(_recordClass), _domainName, _TTL]; } @end + +@implementation OFSOADNSResourceRecord +@synthesize primaryNameServer = _primaryNameServer; +@synthesize responsiblePerson = _responsiblePerson; +@synthesize serialNumber = _serialNumber, refreshInterval = _refreshInterval; +@synthesize retryInterval = _retryInterval; +@synthesize expirationInterval = _expirationInterval, minTTL = _minTTL; + +- (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 + primaryNameServer: (OFString *)primaryNameServer + responsiblePerson: (OFString *)responsiblePerson + serialNumber: (uint32_t)serialNumber + refreshInterval: (uint32_t)refreshInterval + retryInterval: (uint32_t)retryInterval + expirationInterval: (uint32_t)expirationInterval + minTTL: (uint32_t)minTTL + TTL: (uint32_t)TTL +{ + self = [super initWithName: name + recordClass: recordClass + recordType: OF_DNS_RESOURCE_RECORD_TYPE_SOA + TTL: TTL]; + + @try { + _primaryNameServer = [primaryNameServer copy]; + _responsiblePerson = [responsiblePerson copy]; + _serialNumber = serialNumber; + _refreshInterval = refreshInterval; + _retryInterval = retryInterval; + _expirationInterval = expirationInterval; + _minTTL = minTTL; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_primaryNameServer release]; + [_responsiblePerson release]; + + [super dealloc]; +} + +- (bool)isEqual: (id)otherObject +{ + OFSOADNSResourceRecord *otherRecord; + + if (![otherObject isKindOfClass: [OFSOADNSResourceRecord 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->_primaryNameServer != _primaryNameServer && + ![otherRecord->_primaryNameServer isEqual: _primaryNameServer]) + return false; + + if (otherRecord->_responsiblePerson != _responsiblePerson && + ![otherRecord->_responsiblePerson isEqual: _responsiblePerson]) + return false; + + if (otherRecord->_serialNumber != _serialNumber) + return false; + + if (otherRecord->_refreshInterval != _refreshInterval) + return false; + + if (otherRecord->_retryInterval != _retryInterval) + return false; + + if (otherRecord->_expirationInterval != _expirationInterval) + return false; + + if (otherRecord->_minTTL != _minTTL) + 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, [_primaryNameServer hash]); + OF_HASH_ADD_HASH(hash, [_responsiblePerson hash]); + OF_HASH_ADD(hash, _serialNumber >> 24); + OF_HASH_ADD(hash, _serialNumber >> 16); + OF_HASH_ADD(hash, _serialNumber >> 8); + OF_HASH_ADD(hash, _serialNumber); + OF_HASH_ADD(hash, _refreshInterval >> 24); + OF_HASH_ADD(hash, _refreshInterval >> 16); + OF_HASH_ADD(hash, _refreshInterval >> 8); + OF_HASH_ADD(hash, _refreshInterval); + OF_HASH_ADD(hash, _retryInterval >> 24); + OF_HASH_ADD(hash, _retryInterval >> 16); + OF_HASH_ADD(hash, _retryInterval >> 8); + OF_HASH_ADD(hash, _retryInterval); + OF_HASH_ADD(hash, _expirationInterval >> 24); + OF_HASH_ADD(hash, _expirationInterval >> 16); + OF_HASH_ADD(hash, _expirationInterval >> 8); + OF_HASH_ADD(hash, _expirationInterval); + OF_HASH_ADD(hash, _minTTL >> 24); + OF_HASH_ADD(hash, _minTTL >> 16); + OF_HASH_ADD(hash, _minTTL >> 8); + OF_HASH_ADD(hash, _minTTL); + + OF_HASH_FINALIZE(hash); + + return hash; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"<%@:\n" + @"\tName = %@\n" + @"\tClass = %@\n" + @"\tPrimary Name Server = %@\n" + @"\tResponsible Person = %@\n" + @"\tSerial Number = %" PRIu32 "\n" + @"\tRefresh Interval = %" PRIu32 "\n" + @"\tRetry Interval = %" PRIu32 "\n" + @"\tExpiration Interval = %" PRIu32 "\n" + @"\tMinimum TTL = %" PRIu32 "\n" + @"\tTTL = %" PRIu32 "\n" + @">", + [self className], _name, + of_dns_resource_record_class_to_string(_recordClass), + _primaryNameServer, _responsiblePerson, _serialNumber, + _refreshInterval, _retryInterval, _expirationInterval, _minTTL, + _TTL]; +} +@end @implementation OFTXTDNSResourceRecord @synthesize textData = _textData; - (instancetype)initWithName: (OFString *)name