From: JINMEI Tatuya Date: Thu, 23 Aug 2012 06:51:02 +0000 (-0700) Subject: [2107] overall documentation update, and adjusted some interfaces X-Git-Tag: trac2351_base~109^2~1^2~20 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0b8a3ffb1876c789a1694b627eaf370bb66c19d5;p=thirdparty%2Fkea.git [2107] overall documentation update, and adjusted some interfaces --- diff --git a/src/lib/datasrc/memory/tests/zone_data_unittest.cc b/src/lib/datasrc/memory/tests/zone_data_unittest.cc index 5f09e3c925..c81801cf10 100644 --- a/src/lib/datasrc/memory/tests/zone_data_unittest.cc +++ b/src/lib/datasrc/memory/tests/zone_data_unittest.cc @@ -64,7 +64,7 @@ protected: NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN()); } if (zone_data_ != NULL) { - ZoneData::destroy(mem_sgmt_, RRClass::IN(), zone_data_); + ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN()); } // detect any memory leak in the test memory segment EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); @@ -89,7 +89,7 @@ checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) { NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata); // Internal tree should be created and empty. - EXPECT_EQ(0, nsec3_data->getNSEC3Tree()->getNodeCount()); + EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount()); EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg); EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags); @@ -105,11 +105,11 @@ checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) { } void -checkFindRdataSet(const ZoneTree* tree, const Name& name, RRType type, +checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type, const RdataSet* expected_set) { ZoneNode* node = NULL; - tree->find(name, &node); + tree.find(name, &node); ASSERT_NE(static_cast(NULL), node); EXPECT_EQ(expected_set, findRdataSetOfType(node->getData(), type)); } diff --git a/src/lib/datasrc/memory/zone_data.cc b/src/lib/datasrc/memory/zone_data.cc index 004cfc7efd..03eab04d44 100644 --- a/src/lib/datasrc/memory/zone_data.cc +++ b/src/lib/datasrc/memory/zone_data.cc @@ -139,8 +139,8 @@ ZoneData::create(util::MemorySegment& mem_sgmt, const Name& zone_origin) { } void -ZoneData::destroy(util::MemorySegment& mem_sgmt, RRClass zone_class, - ZoneData* zone_data) +ZoneData::destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data, + RRClass zone_class) { ZoneTree::destroy(mem_sgmt, zone_data->zone_tree_.get(), boost::bind(rdataSetDeleter, zone_class, &mem_sgmt, diff --git a/src/lib/datasrc/memory/zone_data.h b/src/lib/datasrc/memory/zone_data.h index 896ad405a7..30cfea4ac3 100644 --- a/src/lib/datasrc/memory/zone_data.h +++ b/src/lib/datasrc/memory/zone_data.h @@ -44,28 +44,146 @@ namespace memory { typedef DomainTree ZoneTree; typedef DomainTreeNode ZoneNode; +/// \brief NSEC3 data for a DNS zone. +/// +/// This class encapsulates a set of NSEC3 related data for a zone +/// that is signed with NSEC3 RRs. Specifically, it contains hash +/// parameters as given in an NSEC3PARAM RDATA and all NSEC3 RRs of the zone. +/// +/// The main concept of the class is generally the same as that of +/// \c ZoneData (see its description for details), but the related data +//// are encapsulated in a more straightforward way in this class. +/// +/// The NSEC3 RRs (which should normally have RRSIGs) are stored in a +/// \c DomainTree object whose data type is (a list of) \c RdataSet. +/// This tree is expected to store NSEC3 RRs only, so the RR type of +/// \c RdataSet should be NSEC3. But this class itself doesn't guarantee +/// this condition. It's caller's responsibility. +/// +/// Read-only access to the tree is possible via the \c getNSEC3Tree() method. +/// Modifying the tree must be done by specific method; the application +/// cannot directly change the content of the tree in an arbitrary way. +/// This class does not have a strong reason to be that strict, but is +/// defined this way mainly to be consistent with \c ZoneData class. +/// +/// Most of the hash parameters are maintained in the form of straightforward +/// member variables, which can be directly referenced by the application. +/// The exception is the salt, which is encapsulated as opaque data +/// immediately following the main class object, and should be accessible +/// via the \c getSaltLen() and \c getSaltData() method. +/// +/// \note The fact that the this class couples one set of hash parameters +/// and the set of NSEC3 RRs implicitly means a zone is assumed to have +/// only one set of NSEC3 parameters. When we support multiple sets of +/// parameters the design should be revised accordingly. class NSEC3Data : boost::noncopyable { public: + /// \brief Allocate and construct \c NSEC3Data from NSEC3PARAM Rdata. + /// + /// The NSEC3 parameters are extracted and stored within the created + /// \c NSEC3Data object. + /// + /// \throw std::bad_alloc Memory allocation fails. + /// + /// \param mem_sgmt A \c MemorySegment from which memory for the new + /// \c NSEC3Data is allocated. + /// \param rdata An NSEC3PARAM RDATA that specifies the NSEC3 parameters + /// to be stored. static NSEC3Data* create(util::MemorySegment& mem_sgmt, const dns::rdata::generic::NSEC3PARAM& rdata); + + /// \brief Allocate and construct \c NSEC3Data from NSEC3 Rdata. + /// + /// The NSEC3 hash parameters are extracted and stored within the created + /// \c NSEC3Data object. + /// + /// \throw std::bad_alloc Memory allocation fails. + /// + /// \param mem_sgmt A \c MemorySegment from which memory for the new + /// \c NSEC3Data is allocated. + /// \param rdata An NSEC3 RDATA that specifies the NSEC3 parameters + /// to be stored. static NSEC3Data* create(util::MemorySegment& mem_sgmt, const dns::rdata::generic::NSEC3& rdata); + + /// \brief Destruct and deallocate \c NSEC3Data. + /// + /// It releases all resource allocated for the internal NSEC3 name space + /// including NSEC3 RdataSet. It assumes \c RdataSets objects stored + /// in the space were allocated using the same memory segment as + /// \c mem_sgmt. The caller must ensure this assumption. + /// + /// Note that an \c RRClass object must be passed to this method. + /// It's necessary to destroy the stored \c RdataSet objects + /// (see its class description). This class doesn't hold this information; + /// it's caller's responsibility to associate an \c NSEC3Data class object + /// with its expected RR class, and pass it to \c destroy(). (In practice, + /// it will be passed via \c ZoneData::destroy()) + /// + /// \throw none + /// + /// \param mem_sgmt The \c MemorySegment that allocated memory for + /// \c data. + /// \param data A non NULL pointer to a valid NSEC3Data object + /// that was originally created by the \c create() method (the behavior + /// is undefined if this condition isn't met). + /// \param nsec3_class The RR class of the \c RdataSet stored in the NSEC3 + /// name space to be destroyed. static void destroy(util::MemorySegment& mem_sgmt, NSEC3Data* data, dns::RRClass nsec3_class); +private: + // Domain tree for the Internal NSEC3 name space. Access to it is + // limited only via public methods. const boost::interprocess::offset_ptr nsec3_tree_; public: - const uint8_t hashalg; - const uint8_t flags; - const uint16_t iterations; + const uint8_t hashalg; ///< Hash algorithm + const uint8_t flags; ///< NSEC3 parameter flags + const uint16_t iterations; ///< Hash iterations // For 64-bit machines there'll be a padding space here, but since // only at most one (or a few in very rare cases) instance will be // created per zone, the overhead should be acceptable. - const ZoneTree* getNSEC3Tree() const { return (nsec3_tree_.get()); } + /// \brief Return \c ZoneTree for the NSEC3 name space. + /// + /// \throw none + const ZoneTree& getNSEC3Tree() const { return (*nsec3_tree_); } + + /// \brief Return the size of NSEC3 salt. + /// + /// \throw none + /// + /// The return value must be in the range between 0 and 255 (inclusive). size_t getSaltLen() const { return (*getSaltBuf()); } + + /// \brief Return a pointer to the salt data. + /// + /// \throw none + /// + /// The valid range is up to the \c getSaltLen() bytes from the + /// returned value. If \c getSaltLen() returns 0, the return value + /// of this method is invalid and must not be used. const uint8_t* getSaltData() const { return (getSaltBuf() + 1); } + /// \brief Insert a name to the NSEC3 name space. + /// + /// It allocates resource for the given name in the internal NSEC3 name + /// space, and returns an access point to it in the form of \c ZoneNode + /// pointer via the given \c node variable. If the name already exists + /// in the name space, it returns a pointer to the existing node. + /// + /// This method does not perform any semantics check on the given name + /// (e.g., whether the first label is a valid encoded string for an NSEC3 + /// owner name). + /// + /// \throw std::bad_alloc Memory allocation fails + /// + /// \param mem_sgmt Memory segment in which resource for the new memory + /// is to be allocated. + /// \param name The name to be inserted. + /// \param node A pointer to \c ZoneNode pointer in which the created or + /// found node for the name is stored. Must not be NULL (the method does + /// not check that condition). void insertName(util::MemorySegment& mem_sgmt, const dns::Name& name, ZoneNode** node); @@ -75,6 +193,12 @@ private: uint8_t flags, uint16_t iterations, const std::vector& salt); + /// \brief The constructor. + /// + /// An object of this class is always expected to be created by the + /// allocator (\c create()), so the constructor is hidden as private. + /// + /// It never throws an exception. NSEC3Data(ZoneTree* nsec3_tree_param, uint8_t hashalg_param, uint8_t flags_param, uint16_t iterations_param) : nsec3_tree_(nsec3_tree_param), hashalg(hashalg_param), @@ -89,8 +213,132 @@ private: } }; +/// \brief DNS zone data. +/// +/// This class encapsulates the content of a DNS zone (which is essentially a +/// set of RRs) in a memory efficient way and provides accessor interfaces +/// to it. +/// +/// The primary goal of this class is to provide a packed structure of the +/// data for memory efficiency. Basically, this class should be considered +/// a private part of some other classes within this module and should not +/// be used directly from normal applications. So it's not intended to hide +/// much details of the underlying implementation details; rather, it tries +/// to keep the representation is simple. +/// +/// The RRs are stored in a \c DomainTree object whose data type is +/// (a list of) \c RdataSet. The tree nodes correspond to owner names, +/// and the \c RdataSet objects (forming a linked list) set in the node +/// represent the rest of the RR parameters except the RR class: type, +/// TTL, and RDATA. This class does not have any knowledge of the RR class +/// of the zone; since it's quite likely that the application maintains +/// a set of zones of the same RR class, and the number of such zones can be +/// huge, it makes more sense to have the application maintain the class value +/// in a unified way to minimize memory footprint. +/// +/// The \c DomainTree object in this class is not expected to hold NSEC3 +/// RRs when the zone is signed with NSEC3; they should be maintained +/// in an associated \c NSEC3Data object. But this class does not prevent +/// the unexpected usage of adding an NSEC3 RdataSet directly in the tree. +/// It's caller's responsibility to ensure this assumption. +/// +/// This class maintains some other meta data and additional zone related +/// content. First, it automatically creates a \c DomainTree node for the +/// zone's origin name on initialization and keeps a reference to it +/// throughout its lifetime. This is the case even if the zone doesn't have +/// any RRs (such as in the case before initial loading). Any valid zone +/// to be served should have an RR at the origin node (at least SOA, for +/// example), so this assumption should be reasonable. But the application +/// must ensure that any \c ZoneData object in actual use should have an +/// RR at the origin; otherwise the inconsistency between the internal state +/// and the actual zone content could lead to unexpected disruption. +/// In particular, it must be careful when it supports dynamic updates +/// to an existing zone so an update attempt doesn't result in deleting +/// the origin node. +/// +/// To ensure the integrity regarding the reference to the origin, write +/// access to the tree node can be done only by public methods; the member +/// variable for the tree is hidden as private. On the other hand, read-only +/// access to the tree is allowed via the const version of \c getZoneTree() +/// method for the convenience of the application. So, it's intentional +/// that there's no non const version of this method. Do not add one +/// when this class is to be extended. +/// +/// Another type of meta data is parameters and records of NSEC3 RRs +/// when the zone is signed with NSEC3. It's represented in the form of +/// \c NSEC3Data object, and a \c ZoneData object may be associated with +/// 0 or 1 \c NSEC3Data object using the \c setNSEC3Data() method, which +/// can be retrieved by the \c getNSEC3Data() method. If the \c ZoneData +/// object is not associated with an \c NSEC3Data, it's considered not +/// signed with NSEC3 RRs; otherwise it's considered to be signed with +/// NSEC3 RRs and with the parameters stored in the \c NSEC3Data object. +/// +/// \note This interpretation may change in future as we support migration +/// from NSEC to NSEC3 or vice versa, support incremental signing, or support +/// multiple sets of NSEC3 parameters. +/// +/// One last type of meta data is the status of the zone in terms of DNSSEC +/// signing. This class supports the following concepts: +/// - Whether the zone is signed or not, either with NSEC records or NSEC3 +/// records. +/// - Whether the zone has a complete set of NSEC3 records. +/// +/// The former status can be accessed via the \c isSigned() and \c setSigned() +/// methods; the latter can be retrieved via the \c isNSEC3Signed() method. +/// +/// This class does not actually relate the status of signed or not to +/// any of its other attributes; it's up to the application how to set or +/// use this status and maintain it in a reasonable way. One possible +/// definition is to set this status if and only if the zone has a +/// DNSKEY RR at the zone origin (which is BIND 9's definition of signed +/// zone). When the application adopts this definition, it's the +/// application's responsibility to keep the status consistent with the +/// actual existence or non-existence of a DNSKEY RR. +/// +/// In the current implementation, a zone is considered to have a complete +/// set of NSEC3 records if and only if it's associated with an \c NSEC3Data +/// object (as noted above, these concepts may be separated in future). +/// For this reason there is no "set" method for the latter; setting +/// an \c NSEC3Data effectively enables the latter status. \c isNSEC3Signed() +/// method is still provided (even though it's a kind of trivial wrapper to +/// \c getNSEC3Data()) partly for a more intuitive shortcut, and partly +/// because we won't have to change the application code when we implement +/// the future separation. +/// +/// The intended usage of these two status concepts is to implement the +/// \c ZoneFinder::Context::isNSECSigned() and +/// \c ZoneFinder::Context::isNSEC3Signed() methods. A possible implementation +/// is as follows: +/// - \c ZoneFinder::Context::isNSECSigned() returns true iff \c isSigned() +/// is true and \c isNSEC3Signed() is false. +/// - \c ZoneFinder::Context::isNSEC3Signed() returns true iff \c isSigned() +/// is true and \c isNSEC3Signed() is true. +/// +/// Note that even though \c isNSEC3Signed() being true should indicate +/// \c isSgned() is true too in practice, the interfaces do not +/// automatically ensure that, so we'd need to check both conditions +/// explicitly. And, in fact, if we adopt the above definition of +/// \c isSigned(), it's possible that a zone has a complete set of NSEC3 +/// RRs but no DNSKEY (although it's effectively a broken zone unless we +/// support incremental signing). +/// +/// This class is designed so an instance can be stored in a shared +/// memory region. So the pointer member variables (the initial +/// implementation only contain pointer member variables) are defined +/// as offset pointers. When this class is extended these properties must +/// be preserved, and also meet other requirements so it can be stored +/// in a shared memory region (see, for example, \c RdataSet description). +/// Future extensions must also be conscious of placing the member variables +/// so that they will not accidentally cause padding and increase memory +/// footprint. class ZoneData : boost::noncopyable { private: + /// \brief The constructor. + /// + /// An object of this class is always expected to be created by the + /// allocator (\c create()), so the constructor is hidden as private. + /// + /// It never throws an exception. ZoneData(ZoneTree* zone_tree, ZoneNode* origin_node) : zone_tree_(zone_tree), origin_node_(origin_node) {} @@ -108,34 +356,178 @@ public: static const ZoneNode::Flags WILD_NODE = ZoneNode::FLAG_USER2; public: + /// \brief Allocate and construct \c ZoneData. + /// + /// \throw std::bad_alloc Memory allocation fails. + /// + /// \param mem_sgmt A \c MemorySegment from which memory for the new + /// \c ZoneData is allocated. + /// \param name The zone name. static ZoneData* create(util::MemorySegment& mem_sgmt, const dns::Name& zone_name); - static void destroy(util::MemorySegment& mem_sgmt, dns::RRClass zone_class, - ZoneData* zone_data); - void insertName(util::MemorySegment& mem_sgmt, const dns::Name& name, - ZoneNode** node); + /// \brief Destruct and deallocate \c ZoneData. + /// + /// It releases all resource allocated in the internal storage NSEC3 for + /// zone names and RdataSet objects, and if associated, the \c NSEC3Data. + /// It assumes \c RdataSets objects stored in the space and the + /// associated \c NSEC3Data object were allocated using the same memory + /// segment as \c mem_sgmt. The caller must ensure this assumption. + /// + /// Note that an \c RRClass object must be passed to this method. + /// It's necessary to destroy the stored \c RdataSet objects + /// (see its class description). This class doesn't hold this information; + /// it's caller's responsibility to associate an \c ZoneData class object + /// with its expected RR class, and pass it to \c destroy(). + /// + /// \throw none + /// + /// \param mem_sgmt The \c MemorySegment that allocated memory for + /// \c zone_data. + /// \param zone_data A non NULL pointer to a valid ZoneData object + /// that was originally created by the \c create() method (the behavior + /// is undefined if this condition isn't met). + /// \param zone_class The RR class of the \c RdataSet stored in the + /// internal tree. + static void destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data, + dns::RRClass zone_class); - // This is a shortcut. + /// \brief Return zone's origin node. + /// + /// This is a convenience and efficient short cut to get access to the + /// zone origin in the form of \c ZoneNode object. + /// + /// The class encapsulation ensures that the origin node always exists at + /// the same address, so this method always returns a non NULL valid + /// valid pointer. + /// + /// \throw none const ZoneNode* getOriginNode() const { return (origin_node_.get()); } + /// \brief Return the zone's name space in the form of \c ZoneTree + /// + /// \note It's intentional that non const version of this method + /// isn't provided. See the class description. + /// + /// \throw none + const ZoneTree& getZoneTree() const { return (*zone_tree_); } + + /// \brief Return whether or not the zone is signed in terms of DNSSEC. + /// + /// Note that this class does not care about what "signed" means. + /// This method simply returns the last value set by \c setSigned() + /// (or the default, which is \c false). The caller is expected to + /// use this method and \c setSigned() in a reasonable, consistent way. + /// + /// \throw none bool isSigned() const { return (origin_node_->getFlag(DNSSEC_SIGNED)); } - void setSigned(bool on) { - origin_node_->setFlag(DNSSEC_SIGNED, on); - } + /// \brief Return whether or not the zone is signed with NSEC3 RRs. + /// /// In the current implementation, the zone is considered signed with /// NSEC3 if and only if it has non NULL NSEC3 data. /// /// This also means it's not considered NSEC3 signed by default. + /// + /// \throw none bool isNSEC3Signed() const { return (nsec3_data_); } - const ZoneTree* getZoneTree() const { return (zone_tree_.get()); } - + /// \brief Return NSEC3Data of the zone. + /// + /// This method returns non NULL valid pointer to \c NSEC3Data object + /// associated to the \c ZoneData if it was set by \c setNSEC3Data(); + /// otherwise it returns NULL. + /// + /// \throw none const NSEC3Data* getNSEC3Data() const { return (nsec3_data_.get()); } + + /// \brief Insert a name to the zone. + /// + /// It allocates resource for the given name in the internal storage + /// for zone data, and returns an access point to it in the form of + /// \c ZoneNode pointer via the given \c node variable. If the name + /// already exists in the name space, it returns a pointer to the existing + /// node. + /// + /// The name to be inserted by this method is expected to belong to + /// zone's "normal" (i.e., non NSEÇ3) name space. If it's a name for + /// an NSEC3 RR, it must be set in the corresponding \c NSEC3Data for + /// this zone data (if it doesn't exist it must be created and set + /// by \c setNSEC3Data()). + /// + /// The name is also expected to be a subdomain of or euqal to the + /// zone's origin name (specified on creation in \c create()), but + /// this method does not check that condition. The caller is responsible + /// for ensuring this assumption. + /// + /// Since this method doesn't check any semantics check, it always succeeds + /// (except for the rare case where memory allocation fails) and \c node + /// will be set to a valid pointer. + /// + /// \note We may want to differentiate between the case where the name is + /// newly created and the case where it already existed. Right now it's + /// unclear, so it doesn't return this information. If we see the need + /// for it, this method can be extended that way. + /// + /// \throw std::bad_alloc Memory allocation fails + /// + /// \param mem_sgmt Memory segment in which resource for the new memory + /// is to be allocated. + /// \param name The name to be inserted. + /// \param node A pointer to \c ZoneNode pointer in which the created or + /// found node for the name is stored. Must not be NULL (the method does + /// not check that condition). + void insertName(util::MemorySegment& mem_sgmt, const dns::Name& name, + ZoneNode** node); + + /// \brief Specify whether or not the zone is signed in terms of DNSSEC. + /// + /// The zone will be considered "signed" (in that subsequent calls to + /// \c isSigned() will return \c true) iff the parameter \c on is \c true. + /// + /// This class does not care what "signed" actually means; it does not + /// check any zone RRs to check if the given state makes sense (e.g. + /// whether the zone has a DNSKEY RR at the origin). The caller is + /// expected to use this method and \c isSigned() in a reasonable, + /// consistent way. + /// + /// \throw none + void setSigned(bool on) { + origin_node_->setFlag(DNSSEC_SIGNED, on); + } + + /// \brief Return NSEC3Data of the zone, non const version. + /// + /// This is similar to the const version, but return a non const pointer + /// so the caller can modify the content. + /// + /// \throw none NSEC3Data* getNSEC3Data() { return (nsec3_data_.get()); } + + /// \brief Associate \c NSEC3Data to the zone. + /// + /// This method associates the given \c NSEC3Data object with the zone + /// data. If there was already associated \c NSEC3Data object, it will + /// be returned. If no \c NSEC3Data object was associated before, + /// a NULL pointer will be returned. \c nsec3_data can be NULL, in which + /// case the zone will be disassociated with a \c NSEC3Data. + /// + /// In general, if a non NULL pointer is passed, it's assumed that + /// the \c NSEC3Data object was allocated in the same \c MemorySegment + /// as that for the zone data, so the \c destroy() method can destroy + /// both with the same memory segment. If this condition is not met, + /// the caller must extract the associated \c NSEC3Data by calling + /// this method with NULL and release any resource for it by itself + /// before destroying this zone data. + /// + /// \throw none + /// + /// \param nsec3_data A pointer to \c NSEC3Data object to be associated + /// with the zone. Can be NULL. + /// \return Previously associated \c NSEC3Data object in the zone. This + /// can be NULL. NSEC3Data* setNSEC3Data(NSEC3Data* nsec3_data) { NSEC3Data* old = nsec3_data_.get(); nsec3_data_ = nsec3_data;