Browse Source

[trac61] documented the functions. not the main subject this ticket,
but it was missing so far and should be provided.

JINMEI Tatuya 14 years ago
parent
commit
301cadd12c
3 changed files with 111 additions and 23 deletions
  1. 19 17
      src/lib/dns/dnssectime.cc
  2. 90 5
      src/lib/dns/dnssectime.h
  3. 2 1
      src/lib/dns/tests/dnssectime_unittest.cc

+ 19 - 17
src/lib/dns/dnssectime.cc

@@ -38,12 +38,12 @@ isLeap(const int y) {
     return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
 }
 
-int
+unsigned int
 yearSecs(const int year) {
     return ((isLeap(year) ? 366 : 365 ) * 86400);
 }
 
-int
+unsigned int
 monthSecs(const int month, const int year) {
     return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
 }
@@ -53,15 +53,15 @@ namespace isc {
 namespace dns {
 
 string
-timeToText64(uint64_t t) {
+timeToText64(uint64_t value) {
     struct tm tm;
-    int secs;
+    unsigned int secs;
 
     // We cannot rely on gmtime() because time_t may not be of 64 bit
     // integer.  The following conversion logic is borrowed from BIND 9.
     tm.tm_year = 70;
-    while ((secs = yearSecs(tm.tm_year + 1900)) <= t) {
-        t -= secs;
+    while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
+        value -= secs;
         ++tm.tm_year;
         if (tm.tm_year + 1900 > 9999) {
             isc_throw(InvalidTime,
@@ -70,26 +70,26 @@ timeToText64(uint64_t t) {
         }
     }
     tm.tm_mon = 0;
-    while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= t) {
-        t -= secs;
+    while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
+        value -= secs;
         tm.tm_mon++;
     }
     tm.tm_mday = 1;
-    while (86400 <= t) {
-        t -= 86400;
+    while (86400 <= value) {
+        value -= 86400;
         ++tm.tm_mday;
     }
     tm.tm_hour = 0;
-    while (3600 <= t) {
-        t -= 3600;
+    while (3600 <= value) {
+        value -= 3600;
         ++tm.tm_hour;
     }
     tm.tm_min = 0;
-    while (60 <= t) {
-        t -= 60;
+    while (60 <= value) {
+        value -= 60;
         ++tm.tm_min;
     }
-    tm.tm_sec = t;              // now t < 60, so this substitution is safe.
+    tm.tm_sec = value;    // now t < 60, so this substitution is safe.
 
     ostringstream oss;
     oss << setfill('0')
@@ -130,14 +130,14 @@ gettimeofdayWrapper() {
 }
 
 string
-timeToText32(const uint32_t timeval) {
+timeToText32(const uint32_t value) {
     // We first adjust the time to the closest epoch based on the current time.
     // Note that the following variables must be signed in order to handle
     // time until year 2038 correctly.
     const int64_t start = gettimeofdayWrapper() - 0x7fffffff;
     int64_t base = 0;
     int64_t t;
-    while ((t = (base + timeval)) < start) {
+    while ((t = (base + value)) < start) {
         base += 0x100000000LL;
     }
 
@@ -203,6 +203,8 @@ timeFromText64(const string& time_txt) {
 
 uint32_t
 timeFromText32(const string& time_txt) {
+    // The implicit conversion from uint64_t to uint32_t should just work here,
+    // because we only need to drop higher 32 bits.
     return (timeFromText64(time_txt));
 }
 }

+ 90 - 5
src/lib/dns/dnssectime.h

@@ -39,17 +39,102 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-uint32_t
-timeFromText32(const std::string& time_txt);
-
+///
+/// \name DNSSEC time conversion functions.
+///
+/// These functions convert between times represented in seconds (in integer)
+/// since epoch and those in the textual form used in the RRSIG records.
+/// For integers we provide both 32-bit and 64-bit versions.
+/// The RRSIG expiration and inception fields are both 32-bit unsigned
+/// integers, so 32-bit versions would be more useful for protocol operations.
+/// However, with 32-bit integers we need to take into account wrap-around
+/// points and compare values using the serial number arithmetic as specified
+/// in RFC4034, which would be more error prone.  We therefore provide 64-bit
+/// versions, too.
+///
+/// The timezone is always UTC for these functions.
+//@{
+/// Convert textual DNSSEC time to integer, 64-bit version.
+///
+/// The textual form must only consist of digits and be in the form of
+/// YYYYMMDDHHmmSS, where:
+/// - YYYY must be between 1970 and 9999
+/// - MM must be between 01 and 12
+/// - DD must be between 01 and 31 and must be a valid day for the month
+///   represented in 'MM'.  For example, if MM is 04, DD cannot be 31.
+///   DD can be 29 when MM is 02 only when YYYY is a leap year.
+/// - HH must be between 00 and 23
+/// - mm must be between 00 and 59
+/// - SS must be between 00 and 60
+///
+/// For all fields the range includes the begin and end values.  Note that
+/// 60 is allowed for 'SS', intending a leap second, although in real operation
+/// it's unlikely to be specified.
+///
+/// If the given text is valid, this function converts it to an unsigned
+/// 64-bit number of seconds since epoch (1 January 1970 00:00:00) and returns
+/// the converted value.  64 bits are sufficient to represent all possible
+/// values for the valid format uniquely, so there is no overflow.
+///
+/// \note RFC4034 also defines the textual form of an unsigned decimal integer
+/// for the corresponding time in seconds.  This function doesn't support
+/// this form, and if given it throws an exception of class \c InvalidTime.
+///
+/// \exception InvalidTime The given textual representation is invalid.
+///
+/// \param time_txt Textual time in the form of YYYYMMDDHHmmSS
+/// \return Seconds since epoch corresponding to \c time_txt
 uint64_t
 timeFromText64(const std::string& time_txt);
 
+/// Convert textual DNSSEC time to integer, 32-bit version.
+///
+/// This version is the same as \c timeFromText64() except that the return
+/// value is wrapped around to an unsigned 32-bit integer, simply dropping
+/// the upper 32 bits.
+uint32_t
+timeFromText32(const std::string& time_txt);
+
+/// Convert integral DNSSEC time to textual form, 64-bit version.
+///
+/// This function takes an integer that would be seconds since epoch and
+/// converts it in the form of YYYYMMDDHHmmSS.  For example, if \c value is
+/// 0, it returns "19700101000000".  If the value corresponds to a point
+/// of time on and after year 10,000, which cannot be represented in the
+/// YYYY... form, an exception of class \c InvalidTime will be thrown.
+///
+/// \exception InvalidTime The given time specifies on or after year 10,000.
+/// \exception Other A standard exception, if resource allocation for the
+/// returned text fails.
+///
+/// \param value Seconds since epoch to be converted.
+/// \return Textual representation of \c value in the form of YYYYMMDDHHmmSS.
 std::string
-timeToText32(const uint32_t timeval);
+timeToText64(uint64_t value);
 
+/// Convert integral DNSSEC time to textual form, 32-bit version.
+///
+/// This version is the same as \c timeToText64(), but the time value
+/// is expected to be the lower 32 bits of the full 64-bit value.
+/// These two will be different on and after a certain point of time
+/// in year 2106, so this function internally resolves the ambiguity
+/// using the current system time at the time of function call;
+/// it first identifies the range of [N*2^32 - 2^31, N*2^32 + 2^31)
+/// that contains the current time, and interprets \c value in the context
+/// of that range.  It then applies the same process as \c timeToText64().
+///
+/// There is one important exception in this processing, however.
+/// Until 19 Jan 2038 03:14:08 (2^31 seconds since epoch), this range
+/// would contain time before epoch.  In order to ensure the returned
+/// value is also a valid input to \c timeFromText, this function uses
+/// a special range [0, 2^32) until that time.  As a result, all upper
+/// half of the 32-bit values are treated as a future time.  For example,
+/// 2^32-1 (the highest value in 32-bit unsigned integers) will be converted
+/// to "21060207062815", instead of "19691231235959".
 std::string
-timeToText64(uint64_t t);
+timeToText32(uint32_t value);
+
+//@}
 }
 }
 

+ 2 - 1
src/lib/dns/tests/dnssectime_unittest.cc

@@ -54,7 +54,8 @@ TEST_F(DNSSECTimeTest, fromText) {
     EXPECT_THROW(timeFromText32("2011 101120000"), InvalidTime);
     EXPECT_THROW(timeFromText32("201101011200-0"), InvalidTime);
 
-    // Short length
+    // Short length (or "decimal integer" version of representation;
+    // it's valid per RFC4034, but is not supported in this implementation)
     EXPECT_THROW(timeFromText32("20100223"), InvalidTime);
 
     // Leap year checks