Browse Source

Merge branch 'trac1278'

Jelte Jansen 13 years ago
parent
commit
2ae72d76c7

+ 6 - 6
src/lib/datasrc/database.cc

@@ -843,7 +843,7 @@ public:
         committed_(false), accessor_(accessor), zone_id_(zone_id),
         db_name_(accessor->getDBName()), zone_name_(zone_name.toText()),
         zone_class_(zone_class), journaling_(journaling),
-        diff_phase_(NOT_STARTED),
+        diff_phase_(NOT_STARTED), serial_(0),
         finder_(new DatabaseClient::Finder(accessor_, zone_id_, zone_name))
     {
         logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED)
@@ -896,7 +896,7 @@ private:
         ADD
     };
     DiffPhase diff_phase_;
-    uint32_t serial_;
+    Serial serial_;
     boost::scoped_ptr<DatabaseClient::Finder> finder_;
 
     // This is a set of validation checks commonly used for addRRset() and
@@ -985,8 +985,8 @@ DatabaseUpdater::addRRset(const RRset& rrset) {
         columns[Accessor::ADD_RDATA] = it->getCurrent().toText();
         if (journaling_) {
             journal[Accessor::DIFF_RDATA] = columns[Accessor::ADD_RDATA];
-            accessor_->addRecordDiff(zone_id_, serial_, Accessor::DIFF_ADD,
-                                     journal);
+            accessor_->addRecordDiff(zone_id_, serial_.getValue(),
+                                     Accessor::DIFF_ADD, journal);
         }
         accessor_->addRecordToZone(columns);
     }
@@ -1023,8 +1023,8 @@ DatabaseUpdater::deleteRRset(const RRset& rrset) {
         params[Accessor::DEL_RDATA] = it->getCurrent().toText();
         if (journaling_) {
             journal[Accessor::DIFF_RDATA] = params[Accessor::DEL_RDATA];
-            accessor_->addRecordDiff(zone_id_, serial_, Accessor::DIFF_DELETE,
-                                     journal);
+            accessor_->addRecordDiff(zone_id_, serial_.getValue(),
+                                     Accessor::DIFF_DELETE, journal);
         }
         accessor_->deleteRecordInZone(params);
     }

+ 1 - 0
src/lib/dns/Makefile.am

@@ -102,6 +102,7 @@ libdns___la_SOURCES += rrsetlist.h rrsetlist.cc
 libdns___la_SOURCES += rrttl.h rrttl.cc
 libdns___la_SOURCES += rrtype.cc
 libdns___la_SOURCES += question.h question.cc
+libdns___la_SOURCES += serial.h serial.cc
 libdns___la_SOURCES += tsig.h tsig.cc
 libdns___la_SOURCES += tsigerror.h tsigerror.cc
 libdns___la_SOURCES += tsigkey.h tsigkey.cc

+ 1 - 0
src/lib/dns/python/Makefile.am

@@ -12,6 +12,7 @@ libpydnspp_la_SOURCES += rrclass_python.cc rrclass_python.h
 libpydnspp_la_SOURCES += rrtype_python.cc rrtype_python.h
 libpydnspp_la_SOURCES += rrttl_python.cc rrttl_python.h
 libpydnspp_la_SOURCES += rdata_python.cc rdata_python.h
+libpydnspp_la_SOURCES += serial_python.cc serial_python.h
 libpydnspp_la_SOURCES += messagerenderer_python.cc messagerenderer_python.h
 libpydnspp_la_SOURCES += rcode_python.cc rcode_python.h
 libpydnspp_la_SOURCES += opcode_python.cc opcode_python.h

+ 17 - 0
src/lib/dns/python/pydnspp.cc

@@ -49,6 +49,7 @@
 #include "rrset_python.h"
 #include "rrttl_python.h"
 #include "rrtype_python.h"
+#include "serial_python.h"
 #include "tsigerror_python.h"
 #include "tsigkey_python.h"
 #include "tsig_python.h"
@@ -492,6 +493,18 @@ initModulePart_RRType(PyObject* mod) {
 }
 
 bool
+initModulePart_Serial(PyObject* mod) {
+    if (PyType_Ready(&serial_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&serial_type);
+    PyModule_AddObject(mod, "Serial",
+                       reinterpret_cast<PyObject*>(&serial_type));
+
+    return (true);
+}
+
+bool
 initModulePart_TSIGError(PyObject* mod) {
     if (PyType_Ready(&tsigerror_type) < 0) {
         return (false);
@@ -804,6 +817,10 @@ PyInit_pydnspp(void) {
         return (NULL);
     }
 
+    if (!initModulePart_Serial(mod)) {
+        return (NULL);
+    }
+
     if (!initModulePart_TSIGKey(mod)) {
         return (NULL);
     }

+ 281 - 0
src/lib/dns/python/serial_python.cc

@@ -0,0 +1,281 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <Python.h>
+
+#include <dns/serial.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "serial_python.h"
+#include "pydnspp_common.h"
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::python;
+using namespace isc::util;
+using namespace isc::util::python;
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_Serial : public PyObject {
+public:
+    s_Serial() : cppobj(NULL) {};
+    isc::dns::Serial* cppobj;
+};
+
+typedef CPPPyObjectContainer<s_Serial, Serial> SerialContainer;
+
+PyObject* Serial_str(PyObject* self);
+PyObject* Serial_getValue(s_Serial* self);
+PyObject* Serial_richcmp(s_Serial* self, s_Serial* other, int op);
+PyObject* Serial_add(PyObject *right, PyObject *left);
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef Serial_methods[] = {
+    { "get_value", reinterpret_cast<PyCFunction>(Serial_getValue), METH_NOARGS,
+      "Returns the Serial value as an integer" },
+    { NULL, NULL, 0, NULL }
+};
+
+// For overriding the + operator. We do not define any other operators for
+// this type.
+PyNumberMethods Serial_NumberMethods = {
+    Serial_add, //nb_add;
+    NULL, //nb_subtract;
+    NULL, //nb_multiply;
+    NULL, //nb_remainder;
+    NULL, //nb_divmod;
+    NULL, //nb_power;
+    NULL, //nb_negative;
+    NULL, //nb_positive;
+    NULL, //nb_absolute;
+    NULL, //nb_bool;
+    NULL, //nb_invert;
+    NULL, //nb_lshift;
+    NULL, //nb_rshift;
+    NULL, //nb_and;
+    NULL, //nb_xor;
+    NULL, //nb_or;
+    NULL, //nb_int;
+    NULL, //nb_reserved;
+    NULL, //nb_float;
+
+    NULL, //nb_inplace_add;
+    NULL, //nb_inplace_subtract;
+    NULL, //nb_inplace_multiply;
+    NULL, //nb_inplace_remainder;
+    NULL, //nb_inplace_power;
+    NULL, //nb_inplace_lshift;
+    NULL, //nb_inplace_rshift;
+    NULL, //nb_inplace_and;
+    NULL, //nb_inplace_xor;
+    NULL, //nb_inplace_or;
+
+    NULL, //nb_floor_divide;
+    NULL, //nb_true_divide;
+    NULL, //nb_inplace_floor_divide;
+    NULL, //nb_inplace_true_divide;
+
+    NULL, //nb_index;
+};
+
+int
+Serial_init(s_Serial* self, PyObject* args) {
+    long long i;
+    if (PyArg_ParseTuple(args, "L", &i)) {
+        PyErr_Clear();
+        if (i < 0 || i > 0xffffffff) {
+            PyErr_SetString(PyExc_ValueError, "Serial number out of range");
+            return (-1);
+        }
+        self->cppobj = new Serial(i);
+        return (0);
+    } else {
+        return (-1);
+    }
+}
+
+void
+Serial_destroy(s_Serial* self) {
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+Serial_getValue(s_Serial* self) {
+    return (Py_BuildValue("I", self->cppobj->getValue()));
+}
+
+PyObject*
+Serial_str(PyObject* po_self) {
+    const s_Serial* const self = static_cast<s_Serial*>(po_self);
+    return (PyUnicode_FromFormat("%u", self->cppobj->getValue()));
+}
+
+PyObject*
+Serial_richcmp(s_Serial* self, s_Serial* other, int op) {
+    bool c = false;
+
+    // Check for null and if the types match. If different type,
+    // simply return False
+    if (!other || (self->ob_type != other->ob_type)) {
+        Py_RETURN_FALSE;
+    }
+
+    switch (op) {
+    case Py_LT:
+        c = *self->cppobj < *other->cppobj;
+        break;
+    case Py_LE:
+        c = *self->cppobj <= *other->cppobj;
+        break;
+    case Py_EQ:
+        c = *self->cppobj == *other->cppobj;
+        break;
+    case Py_NE:
+        c = *self->cppobj != *other->cppobj;
+        break;
+    case Py_GT:
+        c = *self->cppobj > *other->cppobj;
+        break;
+    case Py_GE:
+        c = *self->cppobj >= *other->cppobj;
+        break;
+    }
+    if (c) {
+        Py_RETURN_TRUE;
+    } else {
+        Py_RETURN_FALSE;
+    }
+}
+
+PyObject *
+Serial_add(PyObject *left, PyObject *right) {
+    // Either can be either a serial or a long, as long as one of them is a
+    // serial
+    if (PySerial_Check(left) && PySerial_Check(right)) {
+        return (createSerialObject(PySerial_ToSerial(left) +
+                                   PySerial_ToSerial(right)));
+    } else if (PySerial_Check(left) && PyLong_Check(right)) {
+        return (createSerialObject(PySerial_ToSerial(left) +
+                                   PyLong_AsLong(right)));
+    } else if (PyLong_Check(left) && PySerial_Check(right)) {
+        return (createSerialObject(PySerial_ToSerial(right) +
+                                   PyLong_AsLong(left)));
+    } else {
+        Py_INCREF(Py_NotImplemented);
+        return Py_NotImplemented;
+    }
+}
+
+} // end anonymous namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_Serial
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject serial_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.Serial",
+    sizeof(s_Serial),                   // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)Serial_destroy,         // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    &Serial_NumberMethods,              // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    Serial_str,                         // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The Serial class encapsulates Serials used in DNS SOA records.\n\n"
+    "This is a straightforward class; an Serial object simply maintains a "
+    "32-bit unsigned integer corresponding to the SOA SERIAL value.  The "
+    "main purpose of this class is to provide serial number arithmetic, as "
+    "described in RFC 1892. Objects of this type can be compared and added "
+    "to each other, as described in RFC 1892. Apart from str(), get_value(), "
+    "comparison operators, and the + operator, no other operations are "
+    "defined for this type.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    (richcmpfunc)Serial_richcmp,        // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    Serial_methods,                     // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)Serial_init,              // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createSerialObject(const Serial& source) {
+    SerialContainer container(PyObject_New(s_Serial, &serial_type));
+    container.set(new Serial(source));
+    return (container.release());
+}
+
+bool
+PySerial_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in Serial typecheck");
+    }
+    return (PyObject_TypeCheck(obj, &serial_type));
+}
+
+const Serial&
+PySerial_ToSerial(const PyObject* serial_obj) {
+    if (serial_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in Serial PyObject conversion");
+    }
+    const s_Serial* serial = static_cast<const s_Serial*>(serial_obj);
+    return (*serial->cppobj);
+}
+
+} // namespace python
+} // namespace dns
+} // namespace isc

+ 64 - 0
src/lib/dns/python/serial_python.h

@@ -0,0 +1,64 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_SERIAL_H
+#define __PYTHON_SERIAL_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class Serial;
+
+namespace python {
+
+extern PyTypeObject serial_type;
+
+/// This is a simple shortcut to create a python Serial object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createSerialObject(const Serial& source);
+
+/// \brief Checks if the given python object is a Serial object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type Serial, false otherwise
+bool PySerial_Check(PyObject* obj);
+
+/// \brief Returns a reference to the Serial object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type Serial; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PySerial_Check()
+///
+/// \note This is not a copy; if the Serial is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param Serial_obj The Serial object to convert
+const Serial& PySerial_ToSerial(const PyObject* Serial_obj);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_SERIAL_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 1 - 0
src/lib/dns/python/tests/Makefile.am

@@ -11,6 +11,7 @@ PYTESTS += rrclass_python_test.py
 PYTESTS += rrset_python_test.py
 PYTESTS += rrttl_python_test.py
 PYTESTS += rrtype_python_test.py
+PYTESTS += serial_python_test.py
 PYTESTS += tsig_python_test.py
 PYTESTS += tsig_rdata_python_test.py
 PYTESTS += tsigerror_python_test.py

+ 109 - 0
src/lib/dns/python/tests/serial_python_test.py

@@ -0,0 +1,109 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# Tests for the rrttl part of the pydnspp module
+#
+
+import unittest
+import os
+from pydnspp import *
+
+class SerialTest(unittest.TestCase):
+    def setUp(self):
+        self.one = Serial(1)
+        self.one_2 = Serial(1)
+        self.two = Serial(2)
+        self.date_zero = Serial(1980120100)
+        self.date_one = Serial(1980120101)
+        self.zero = Serial(0)
+        self.highest = Serial(4294967295)
+        self.number_low = Serial(12345)
+        self.number_medium = Serial(2000000000)
+        self.number_high = Serial(4000000000)
+
+    def test_init(self):
+        self.assertRaises(ValueError, Serial, -1)
+        self.assertRaises(ValueError, Serial, 4294967296)
+        self.assertRaises(ValueError, Serial, 4294967297)
+        self.assertRaises(ValueError, Serial, 100000000000)
+
+    def test_get_value(self):
+        self.assertEqual(1, self.one.get_value())
+        self.assertNotEqual(2, self.one_2.get_value())
+        self.assertEqual(2, self.two.get_value())
+        self.assertEqual(1980120100, self.date_zero.get_value())
+        self.assertEqual(1980120101, self.date_one.get_value())
+        self.assertEqual(0, self.zero.get_value())
+        self.assertEqual(4294967295, self.highest.get_value())
+        self.assertEqual(12345, self.number_low.get_value())
+        self.assertEqual(2000000000, self.number_medium.get_value())
+        self.assertEqual(4000000000, self.number_high.get_value())
+
+    def test_str(self):
+        self.assertEqual('1', str(self.one))
+        self.assertNotEqual('2', str(self.one_2))
+        self.assertEqual('2', str(self.two))
+        self.assertEqual('1980120100', str(self.date_zero))
+        self.assertEqual('1980120101', str(self.date_one))
+        self.assertEqual('0', str(self.zero))
+        self.assertEqual('4294967295', str(self.highest))
+        self.assertEqual('12345', str(self.number_low))
+        self.assertEqual('2000000000', str(self.number_medium))
+        self.assertEqual('4000000000', str(self.number_high))
+
+    def test_equals(self):
+        self.assertEqual(self.one, self.one)
+        self.assertEqual(self.one, self.one_2)
+        self.assertNotEqual(self.one, self.two)
+        self.assertNotEqual(self.two, self.one)
+        self.assertEqual(Serial(12345), self.number_low)
+        self.assertNotEqual(Serial(12346), self.number_low)
+
+    def test_compare(self):
+        # These should be true/false even without serial arithmetic
+        self.assertLessEqual(self.one, self.one)
+        self.assertLessEqual(self.one, self.one_2)
+        self.assertLess(self.one, self.two)
+        self.assertLessEqual(self.one, self.two)
+        self.assertGreater(self.two, self.one)
+        self.assertGreaterEqual(self.two, self.two)
+        self.assertLess(self.one, self.number_low)
+        self.assertLess(self.number_low, self.number_medium)
+        self.assertLess(self.number_medium, self.number_high)
+
+        # These should 'wrap'
+        self.assertGreater(self.zero, self.highest)
+        self.assertLess(self.highest, self.one)
+        self.assertLess(self.number_high, self.number_low)
+
+    def test_addition(self):
+        self.assertEqual(self.two, self.one + self.one)
+        self.assertEqual(self.two, self.one + self.one_2)
+        self.assertEqual(self.highest, self.highest + self.zero)
+        self.assertEqual(self.zero, self.highest + self.one)
+        self.assertEqual(self.one, self.highest + self.two)
+        self.assertEqual(self.one, self.highest + self.one + self.one)
+        self.assertEqual(self.one + 100, self.highest + 102)
+        self.assertEqual(100 + self.one, self.highest + 102)
+        self.assertEqual(self.zero + 2147483645, self.highest + 2147483646)
+
+        # using lambda so the error doesn't get thrown on initial evaluation
+        self.assertRaises(TypeError, lambda: self.zero + "bad")
+        self.assertRaises(TypeError, lambda: self.zero + None)
+        self.assertRaises(TypeError, lambda: "bad" + self.zero)
+
+if __name__ == '__main__':
+    unittest.main()

+ 2 - 2
src/lib/dns/rdata/generic/soa_6.cc

@@ -106,10 +106,10 @@ SOA::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeData(numdata_, sizeof(numdata_));
 }
 
-uint32_t
+Serial
 SOA::getSerial() const {
     InputBuffer b(numdata_, sizeof(numdata_));
-    return (b.readUint32());
+    return (Serial(b.readUint32()));
 }
 
 string

+ 2 - 1
src/lib/dns/rdata/generic/soa_6.h

@@ -18,6 +18,7 @@
 
 #include <dns/name.h>
 #include <dns/rdata.h>
+#include <dns/serial.h>
 
 // BEGIN_ISC_NAMESPACE
 
@@ -35,7 +36,7 @@ public:
         uint32_t refresh, uint32_t retry, uint32_t expire,
         uint32_t minimum);
     /// \brief Returns the serial stored in the SOA.
-    uint32_t getSerial() const;
+    Serial getSerial() const;
 private:
     /// Note: this is a prototype version; we may reconsider
     /// this representation later.

+ 76 - 0
src/lib/dns/serial.cc

@@ -0,0 +1,76 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/serial.h>
+
+namespace isc {
+namespace dns {
+
+bool
+Serial::operator==(const Serial& other) const {
+    return (value_ == other.getValue());
+}
+
+bool
+Serial::operator!=(const Serial& other) const {
+    return (value_ != other.getValue());
+}
+
+bool
+Serial::operator<(const Serial& other) const {
+    uint32_t other_val = other.getValue();
+    bool result = false;
+    if (value_ < other_val) {
+        result = ((other_val - value_) <= MAX_SERIAL_INCREMENT);
+    } else if (other_val < value_) {
+        result = ((value_ - other_val) > MAX_SERIAL_INCREMENT);
+    }
+    return (result);
+}
+
+bool
+Serial::operator<=(const Serial& other) const {
+    return (operator==(other) || operator<(other));
+}
+
+bool
+Serial::operator>(const Serial& other) const {
+    return (!operator==(other) && !operator<(other));
+}
+
+bool
+Serial::operator>=(const Serial& other) const {
+    return (operator==(other) || !operator>(other));
+}
+
+Serial
+Serial::operator+(uint32_t other_val) const {
+    uint64_t new_val = static_cast<uint64_t>(value_) +
+                       static_cast<uint64_t>(other_val);
+    return Serial(static_cast<uint32_t>(new_val % MAX_SERIAL_VALUE));
+}
+
+Serial
+Serial::operator+(const Serial& other) const {
+    return (operator+(other.getValue()));
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Serial& serial) {
+    return (os << serial.getValue());
+}
+
+} // end namespace dns
+} // end namespace isc
+

+ 155 - 0
src/lib/dns/serial.h

@@ -0,0 +1,155 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __SERIAL_H
+#define __SERIAL_H 1
+
+#include <stdint.h>
+#include <iostream>
+
+namespace isc {
+namespace dns {
+
+/// The maximum difference between two serial numbers. If the (plain uint32_t)
+/// difference between two serials is greater than this number, the smaller one
+/// is considered greater.
+const uint32_t MAX_SERIAL_INCREMENT = 2147483647;
+
+/// Maximum value a serial can have, used in + operator.
+const uint64_t MAX_SERIAL_VALUE = 4294967296;
+
+/// \brief This class defines DNS serial numbers and serial arithmetic.
+///
+/// DNS Serial number are in essence unsigned 32-bits numbers, with one
+/// catch; they should be compared using sequence space arithmetic.
+/// So given that they are 32-bits; as soon as the difference between two
+/// serial numbers is greater than 2147483647 (2^31 - 1), the lower number
+/// (in plain comparison) is considered the higher one.
+///
+/// In order to do this as transparently as possible, these numbers are
+/// stored in the Serial class, which overrides the basic comparison operators.
+///
+/// In this specific context, these operations are called 'serial number
+/// arithmetic', and they are defined in RFC 1982.
+///
+/// \note RFC 1982 defines everything based on the value SERIAL_BITS. Since
+/// the serial number has a fixed length of 32 bits, the values we use are
+/// hard-coded, and not computed based on variable bit lengths.
+class Serial {
+public:
+    /// \brief Constructor with value
+    ///
+    /// \param value The uint32_t value of the serial
+    explicit Serial(uint32_t value) : value_(value) {}
+
+    /// \brief Copy constructor
+    Serial(const Serial& other) : value_(other.getValue()) {}
+
+    /// \brief Direct assignment from other Serial
+    ///
+    /// \param other The Serial to assign the value from
+    void operator=(const Serial& other) { value_ = other.getValue(); }
+
+    /// \brief Direct assignment from value
+    ///
+    /// \param value the uint32_t value to assing
+    void operator=(uint32_t value) { value_ = value; }
+
+    /// \brief Returns the uint32_t representation of this serial value
+    ///
+    /// \return The uint32_t value of this Serial
+    uint32_t getValue() const { return (value_); }
+
+    /// \brief Returns true if the serial values are equal
+    ///
+    /// \return True if the values are equal
+    bool operator==(const Serial& other) const;
+
+    /// \brief Returns true if the serial values are not equal
+    ///
+    /// \return True if the values are not equal
+    bool operator!=(const Serial& other) const;
+
+    /// \brief Returns true if the serial value of this serial is smaller than
+    /// the other, according to serial arithmetic as described in RFC 1982
+    ///
+    /// \param other The Serial to compare to
+    ///
+    /// \return True if this is smaller than the given value
+    bool operator<(const Serial& other) const;
+
+    /// \brief Returns true if the serial value of this serial is equal to or
+    /// smaller than the other, according to serial arithmetic as described
+    /// in RFC 1982
+    ///
+    /// \param other The Serial to compare to
+    ///
+    /// \return True if this is smaller than or equal to the given value
+    bool operator<=(const Serial& other) const;
+
+    /// \brief Returns true if the serial value of this serial is greater than
+    /// the other, according to serial arithmetic as described in RFC 1982
+    ///
+    /// \param other The Serial to compare to
+    ///
+    /// \return True if this is greater than the given value
+    bool operator>(const Serial& other) const;
+
+    /// \brief Returns true if the serial value of this serial is equal to or
+    /// greater than the other, according to serial arithmetic as described in
+    /// RFC 1982
+    ///
+    /// \param other The Serial to compare to
+    ///
+    /// \return True if this is greater than or equal to the given value
+    bool operator>=(const Serial& other) const;
+
+    /// \brief Adds the given value to the serial number. If this would make
+    /// the number greater than 2^32-1, it is 'wrapped'.
+    /// \note According to the specification, an addition greater than
+    /// MAX_SERIAL_INCREMENT is undefined. We do NOT catch this error (so as not
+    /// to raise exceptions), but this behaviour remains undefined.
+    ///
+    /// \param other The Serial to add
+    ///
+    /// \return The result of the addition
+    Serial operator+(const Serial& other) const;
+
+    /// \brief Adds the given value to the serial number. If this would make
+    /// the number greater than 2^32-1, it is 'wrapped'.
+    ///
+    /// \note According to the specification, an addition greater than
+    /// MAX_SERIAL_INCREMENT is undefined. We do NOT catch this error (so as not
+    /// to raise exceptions), but this behaviour remains undefined.
+    ///
+    /// \param other_val The uint32_t value to add
+    ///
+    /// \return The result of the addition
+    Serial operator+(uint32_t other_val) const;
+
+private:
+    uint32_t value_;
+};
+
+/// \brief Helper operator for output streams, writes the value to the stream
+///
+/// \param os The ostream to write to
+/// \param serial The Serial to write
+/// \return the output stream
+std::ostream& operator<<(std::ostream& os, const Serial& serial);
+
+} // end namespace dns
+} // end namespace isc
+
+#endif // __SERIAL_H

+ 1 - 0
src/lib/dns/tests/Makefile.am

@@ -54,6 +54,7 @@ run_unittests_SOURCES += question_unittest.cc
 run_unittests_SOURCES += rrparamregistry_unittest.cc
 run_unittests_SOURCES += masterload_unittest.cc
 run_unittests_SOURCES += message_unittest.cc
+run_unittests_SOURCES += serial_unittest.cc
 run_unittests_SOURCES += tsig_unittest.cc
 run_unittests_SOURCES += tsigerror_unittest.cc
 run_unittests_SOURCES += tsigkey_unittest.cc

+ 1 - 1
src/lib/dns/tests/rdata_soa_unittest.cc

@@ -76,7 +76,7 @@ TEST_F(Rdata_SOA_Test, toText) {
 }
 
 TEST_F(Rdata_SOA_Test, getSerial) {
-    EXPECT_EQ(2010012601, rdata_soa.getSerial());
+    EXPECT_EQ(2010012601, rdata_soa.getSerial().getValue());
 }
 
 }

+ 179 - 0
src/lib/dns/tests/serial_unittest.cc

@@ -0,0 +1,179 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+
+#include <dns/serial.h>
+
+using namespace isc::dns;
+
+class SerialTest : public ::testing::Test {
+public:
+    SerialTest() : one(1), one_2(1), two(2),
+                   date_zero(1980120100), date_one(1980120101),
+                   min(0), max(4294967295u),
+                   number_low(12345),
+                   number_medium(2000000000),
+                   number_high(4000000000u)
+    {}
+    Serial one, one_2, two, date_zero, date_one, min, max, number_low, number_medium, number_high;
+};
+
+//
+// Basic tests
+//
+
+TEST_F(SerialTest, get_value) {
+    EXPECT_EQ(1, one.getValue());
+    EXPECT_NE(2, one.getValue());
+    EXPECT_EQ(2, two.getValue());
+    EXPECT_EQ(1980120100, date_zero.getValue());
+    EXPECT_EQ(1980120101, date_one.getValue());
+    EXPECT_EQ(0, min.getValue());
+    EXPECT_EQ(4294967295u, max.getValue());
+    EXPECT_EQ(12345, number_low.getValue());
+    EXPECT_EQ(2000000000, number_medium.getValue());
+    EXPECT_EQ(4000000000u, number_high.getValue());
+}
+
+TEST_F(SerialTest, equals) {
+    EXPECT_EQ(one, one);
+    EXPECT_EQ(one, one);
+    EXPECT_EQ(one, one_2);
+    EXPECT_NE(one, two);
+    EXPECT_NE(two, one);
+    EXPECT_EQ(Serial(12345), number_low);
+    EXPECT_NE(Serial(12346), number_low);
+}
+
+TEST_F(SerialTest, comparison) {
+    // These should be true/false even without serial arithmetic
+    EXPECT_LE(one, one);
+    EXPECT_LE(one, one_2);
+    EXPECT_LT(one, two);
+    EXPECT_LE(one, two);
+    EXPECT_GE(two, two);
+    EXPECT_GT(two, one);
+    EXPECT_LT(one, number_low);
+    EXPECT_LT(number_low, number_medium);
+    EXPECT_LT(number_medium, number_high);
+
+    // now let's try some that 'wrap', as it were
+    EXPECT_GT(min, max);
+    EXPECT_LT(max, min);
+    EXPECT_LT(number_high, number_low);
+}
+
+//
+// RFC 1982 Section 3.1
+//
+TEST_F(SerialTest, addition) {
+    EXPECT_EQ(two, one + one);
+    EXPECT_EQ(two, one + one_2);
+    EXPECT_EQ(max, max + min);
+    EXPECT_EQ(min, max + one);
+    EXPECT_EQ(one, max + two);
+    EXPECT_EQ(one, max + one + one);
+
+    EXPECT_EQ(one + 100, max + 102);
+    EXPECT_EQ(min + 2147483645, max + 2147483646);
+    EXPECT_EQ(min + 2147483646, max + MAX_SERIAL_INCREMENT);
+}
+
+//
+// RFC 1982 Section 3.2 has been checked by the basic tests above
+//
+
+//
+// RFC 1982 Section 4.1
+//
+
+// Helper function for addition_always_larger test, add some numbers
+// and check that the result is always larger than the original
+void do_addition_larger_test(const Serial& number) {
+    EXPECT_GE(number + 0, number);
+    EXPECT_EQ(number + 0, number);
+    EXPECT_GT(number + 1, number);
+    EXPECT_GT(number + 2, number);
+    EXPECT_GT(number + 100, number);
+    EXPECT_GT(number + 1111111, number);
+    EXPECT_GT(number + 2147483646, number);
+    EXPECT_GT(number + MAX_SERIAL_INCREMENT, number);
+    // Try MAX_SERIAL_INCREMENT as a hardcoded number as well
+    EXPECT_GT(number + 2147483647, number);
+}
+
+TEST_F(SerialTest, addition_always_larger) {
+    do_addition_larger_test(one);
+    do_addition_larger_test(two);
+    do_addition_larger_test(date_zero);
+    do_addition_larger_test(date_one);
+    do_addition_larger_test(min);
+    do_addition_larger_test(max);
+    do_addition_larger_test(number_low);
+    do_addition_larger_test(number_medium);
+    do_addition_larger_test(number_high);
+}
+
+//
+// RFC 1982 Section 4.2
+//
+
+// Helper function to do the second addition
+void
+do_two_additions_test_second(const Serial &original,
+                             const Serial &number)
+{
+    EXPECT_NE(original, number);
+    EXPECT_NE(original, number + 0);
+    EXPECT_NE(original, number + 1);
+    EXPECT_NE(original, number + 2);
+    EXPECT_NE(original, number + 100);
+    EXPECT_NE(original, number + 1111111);
+    EXPECT_NE(original, number + 2147483646);
+    EXPECT_NE(original, number + MAX_SERIAL_INCREMENT);
+    EXPECT_NE(original, number + 2147483647);
+}
+
+void do_two_additions_test_first(const Serial &number) {
+    do_two_additions_test_second(number, number + 1);
+    do_two_additions_test_second(number, number + 2);
+    do_two_additions_test_second(number, number + 100);
+    do_two_additions_test_second(number, number + 1111111);
+    do_two_additions_test_second(number, number + 2147483646);
+    do_two_additions_test_second(number, number + MAX_SERIAL_INCREMENT);
+    do_two_additions_test_second(number, number + 2147483647);
+}
+
+TEST_F(SerialTest, two_additions_never_equal) {
+    do_two_additions_test_first(one);
+    do_two_additions_test_first(two);
+    do_two_additions_test_first(date_zero);
+    do_two_additions_test_first(date_one);
+    do_two_additions_test_first(min);
+    do_two_additions_test_first(max);
+    do_two_additions_test_first(number_low);
+    do_two_additions_test_first(number_medium);
+    do_two_additions_test_first(number_high);
+}
+
+//
+// RFC 1982 Section 4.3 and 4.4 have nothing to test
+//
+
+//
+// Tests from RFC 1982 examples
+//
+TEST(SerialTextRFCExamples, rfc_example_tests) {
+}