Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
09ece8cd
Commit
09ece8cd
authored
Feb 19, 2011
by
JINMEI Tatuya
Browse files
Merge branch 'trac61'
parents
296d6b7b
bd8392ff
Changes
4
Hide whitespace changes
Inline
Side-by-side
src/lib/dns/dnssectime.cc
View file @
09ece8cd
...
...
@@ -12,6 +12,10 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include
<stdint.h>
#include
<sys/time.h>
#include
<string>
#include
<iomanip>
#include
<iostream>
...
...
@@ -26,30 +30,121 @@
using
namespace
std
;
namespace
{
int
days
[
12
]
=
{
31
,
28
,
31
,
30
,
31
,
30
,
31
,
31
,
30
,
31
,
30
,
31
};
inline
bool
isLeap
(
const
int
y
)
{
return
((((
y
)
%
4
)
==
0
&&
((
y
)
%
100
)
!=
0
)
||
((
y
)
%
400
)
==
0
);
}
unsigned
int
yearSecs
(
const
int
year
)
{
return
((
isLeap
(
year
)
?
366
:
365
)
*
86400
);
}
unsigned
int
monthSecs
(
const
int
month
,
const
int
year
)
{
return
((
days
[
month
]
+
((
month
==
1
&&
isLeap
(
year
))
?
1
:
0
))
*
86400
);
}
}
namespace
isc
{
namespace
dns
{
string
timeToText
(
const
time_t
timeval
)
{
struct
tm
*
const
t
=
gmtime
(
&
timeval
);
// gmtime() will keep most values within range, but it can
// produce a five-digit year; check for this.
if
((
t
->
tm_year
+
1900
)
>
9999
)
{
isc_throw
(
InvalidTime
,
"Time value out of range: year > 9999"
);
timeToText64
(
uint64_t
value
)
{
struct
tm
tm
;
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
))
<=
value
)
{
value
-=
secs
;
++
tm
.
tm_year
;
if
(
tm
.
tm_year
+
1900
>
9999
)
{
isc_throw
(
InvalidTime
,
"Time value out of range (year > 9999): "
<<
tm
.
tm_year
+
1900
);
}
}
tm
.
tm_mon
=
0
;
while
((
secs
=
monthSecs
(
tm
.
tm_mon
,
tm
.
tm_year
+
1900
))
<=
value
)
{
value
-=
secs
;
tm
.
tm_mon
++
;
}
tm
.
tm_mday
=
1
;
while
(
86400
<=
value
)
{
value
-=
86400
;
++
tm
.
tm_mday
;
}
tm
.
tm_hour
=
0
;
while
(
3600
<=
value
)
{
value
-=
3600
;
++
tm
.
tm_hour
;
}
tm
.
tm_min
=
0
;
while
(
60
<=
value
)
{
value
-=
60
;
++
tm
.
tm_min
;
}
tm
.
tm_sec
=
value
;
// now t < 60, so this substitution is safe.
ostringstream
oss
;
oss
<<
setfill
(
'0'
)
<<
setw
(
4
)
<<
t
->
tm_year
+
1900
<<
setw
(
2
)
<<
t
->
tm_mon
+
1
<<
setw
(
2
)
<<
t
->
tm_mday
<<
setw
(
2
)
<<
t
->
tm_hour
<<
setw
(
2
)
<<
t
->
tm_min
<<
setw
(
2
)
<<
t
->
tm_sec
;
<<
setw
(
4
)
<<
t
m
.
tm_year
+
1900
<<
setw
(
2
)
<<
t
m
.
tm_mon
+
1
<<
setw
(
2
)
<<
t
m
.
tm_mday
<<
setw
(
2
)
<<
t
m
.
tm_hour
<<
setw
(
2
)
<<
t
m
.
tm_min
<<
setw
(
2
)
<<
t
m
.
tm_sec
;
return
(
oss
.
str
());
}
// timeToText32() below uses the current system time. To test it with
// unusual current time values we introduce the following function pointer;
// when it's non NULL, we call it to get the (normally faked) current time.
// Otherwise we use the standard gettimeofday(2). This hook is specifically
// intended for testing purposes, so, even if it's visible outside of this
// library, it's not even declared in a header file.
namespace
dnssectime
{
namespace
detail
{
int64_t
(
*
gettimeFunction
)()
=
NULL
;
}
}
namespace
{
int64_t
gettimeofdayWrapper
()
{
using
namespace
dnssectime
::
detail
;
if
(
gettimeFunction
!=
NULL
)
{
return
(
gettimeFunction
());
}
struct
timeval
now
;
gettimeofday
(
&
now
,
NULL
);
return
(
static_cast
<
int64_t
>
(
now
.
tv_sec
));
}
}
string
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
+
value
))
<
start
)
{
base
+=
0x100000000LL
;
}
// Then convert it to text.
return
(
timeToText64
(
t
));
}
namespace
{
const
size_t
DATE_LEN
=
14
;
// YYYYMMDDHHmmSS
...
...
@@ -62,27 +157,20 @@ checkRange(const int min, const int max, const int value,
}
isc_throw
(
InvalidTime
,
"Invalid "
<<
valname
<<
"value: "
<<
value
);
}
int
days
[
12
]
=
{
31
,
28
,
31
,
30
,
31
,
30
,
31
,
31
,
30
,
31
,
30
,
31
};
inline
bool
isLeap
(
const
int
y
)
{
return
((((
y
)
%
4
)
==
0
&&
((
y
)
%
100
)
!=
0
)
||
((
y
)
%
400
)
==
0
);
}
}
time_t
timeFromText
(
const
string
&
time_txt
)
{
// first try reading YYYYMMDDHHmmSS format
int
year
,
month
,
day
,
hour
,
minute
,
second
;
uint64_t
timeFromText64
(
const
string
&
time_txt
)
{
// Confirm the source only consists digits. sscanf() allows some
// minor exceptions.
for
(
int
i
=
0
;
i
<
time_txt
.
length
();
++
i
)
{
if
(
!
isdigit
(
time_txt
.
at
(
i
)))
{
isc_throw
(
InvalidTime
,
"Couldn't convert non-numeric time value: "
<<
time_txt
);
isc_throw
(
InvalidTime
,
"Couldn't convert non-numeric time value: "
<<
time_txt
);
}
}
int
year
,
month
,
day
,
hour
,
minute
,
second
;
if
(
time_txt
.
length
()
!=
DATE_LEN
||
sscanf
(
time_txt
.
c_str
(),
"%4d%2d%2d%2d%2d%2d"
,
&
year
,
&
month
,
&
day
,
&
hour
,
&
minute
,
&
second
)
!=
6
)
...
...
@@ -100,7 +188,7 @@ timeFromText(const string& time_txt) {
time_t
timeval
=
second
+
(
60
*
minute
)
+
(
3600
*
hour
)
+
((
day
-
1
)
*
86400
);
for
(
int
m
=
0
;
m
<
(
month
-
1
);
m
++
)
{
for
(
int
m
=
0
;
m
<
(
month
-
1
);
++
m
)
{
timeval
+=
days
[
m
]
*
86400
;
}
if
(
isLeap
(
year
)
&&
month
>
2
)
{
...
...
@@ -112,5 +200,12 @@ timeFromText(const string& time_txt) {
return
(
timeval
);
}
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
));
}
}
}
src/lib/dns/dnssectime.h
View file @
09ece8cd
...
...
@@ -17,7 +17,6 @@
#include
<sys/types.h>
#include
<stdint.h>
#include
<time.h>
#include
<exceptions/exceptions.h>
...
...
@@ -40,11 +39,102 @@ public:
isc
::
Exception
(
file
,
line
,
what
)
{}
};
time_t
timeFromText
(
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
timeToText
(
const
time_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
timeToText32
(
uint32_t
value
);
//@}
}
}
...
...
src/lib/dns/rdata/generic/rrsig_46.cc
View file @
09ece8cd
...
...
@@ -93,8 +93,8 @@ RRSIG::RRSIG(const string& rrsig_str) :
isc_throw
(
InvalidRdataText
,
"RRSIG labels out of range"
);
}
uint32_t
timeexpire
=
timeFromText
(
expire_txt
);
uint32_t
timeinception
=
timeFromText
(
inception_txt
);
const
uint32_t
timeexpire
=
timeFromText
32
(
expire_txt
);
const
uint32_t
timeinception
=
timeFromText
32
(
inception_txt
);
vector
<
uint8_t
>
signature
;
decodeBase64
(
signaturebuf
.
str
(),
signature
);
...
...
@@ -157,15 +157,12 @@ RRSIG::~RRSIG() {
string
RRSIG
::
toText
()
const
{
string
expire
=
timeToText
(
impl_
->
timeexpire_
);
string
inception
=
timeToText
(
impl_
->
timeinception_
);
return
(
impl_
->
covered_
.
toText
()
+
" "
+
boost
::
lexical_cast
<
string
>
(
static_cast
<
int
>
(
impl_
->
algorithm_
))
+
" "
+
boost
::
lexical_cast
<
string
>
(
static_cast
<
int
>
(
impl_
->
labels_
))
+
" "
+
boost
::
lexical_cast
<
string
>
(
impl_
->
originalttl_
)
+
" "
+
expire
+
" "
+
inception
+
" "
+
timeToText32
(
impl_
->
time
expire
_
)
+
" "
+
timeToText32
(
impl_
->
time
inception
_
)
+
" "
+
boost
::
lexical_cast
<
string
>
(
impl_
->
tag_
)
+
" "
+
impl_
->
signer_
.
toText
()
+
" "
+
encodeBase64
(
impl_
->
signature_
));
...
...
src/lib/dns/tests/dnssectime_unittest.cc
View file @
09ece8cd
...
...
@@ -23,48 +23,141 @@
using
namespace
std
;
using
namespace
isc
::
dns
;
// See dnssectime.cc
namespace
isc
{
namespace
dns
{
namespace
dnssectime
{
namespace
detail
{
extern
int64_t
(
*
gettimeFunction
)();
}
}
}
}
namespace
{
TEST
(
DNSSECTimeTest
,
fromText
)
{
class
DNSSECTimeTest
:
public
::
testing
::
Test
{
protected:
~
DNSSECTimeTest
()
{
dnssectime
::
detail
::
gettimeFunction
=
NULL
;
}
};
TEST_F
(
DNSSECTimeTest
,
fromText
)
{
// In most cases (in practice) the 32-bit and 64-bit versions should
// behave identically, so we'll mainly test the 32-bit version, which
// will be more commonly used in actual code (because many of the wire
// format time field are 32-bit). The subtle cases where these two
// return different values will be tested at the end of this test case.
// These are bogus and should be rejected
EXPECT_THROW
(
timeFromText
(
"2011 101120000"
),
InvalidTime
);
EXPECT_THROW
(
timeFromText
(
"201101011200-0"
),
InvalidTime
);
EXPECT_THROW
(
timeFromText
32
(
"2011 101120000"
),
InvalidTime
);
EXPECT_THROW
(
timeFromText
32
(
"201101011200-0"
),
InvalidTime
);
// Short length
EXPECT_THROW
(
timeFromText
(
"20100223"
),
InvalidTime
);
// 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
EXPECT_THROW
(
timeFromText
(
"20110229120000"
),
InvalidTime
);
EXPECT_THROW
(
timeFromText
(
"21000229120000"
),
InvalidTime
);
EXPECT_NO_THROW
(
timeFromText
(
"20000229120000"
));
EXPECT_NO_THROW
(
timeFromText
(
"20120229120000"
));
EXPECT_THROW
(
timeFromText
32
(
"20110229120000"
),
InvalidTime
);
EXPECT_THROW
(
timeFromText
32
(
"21000229120000"
),
InvalidTime
);
EXPECT_NO_THROW
(
timeFromText
32
(
"20000229120000"
));
EXPECT_NO_THROW
(
timeFromText
32
(
"20120229120000"
));
// unusual case: this implementation allows SS=60 for "leap seconds"
EXPECT_NO_THROW
(
timeFromText
(
"20110101120060"
));
EXPECT_NO_THROW
(
timeFromText
32
(
"20110101120060"
));
// Out of range parameters
EXPECT_THROW
(
timeFromText
(
"19100223214617"
),
InvalidTime
);
// YY<1970
EXPECT_THROW
(
timeFromText
(
"20110001120000"
),
InvalidTime
);
// MM=00
EXPECT_THROW
(
timeFromText
(
"20111301120000"
),
InvalidTime
);
// MM=13
EXPECT_THROW
(
timeFromText
(
"20110100120000"
),
InvalidTime
);
// DD=00
EXPECT_THROW
(
timeFromText
(
"20110132120000"
),
InvalidTime
);
// DD=32
EXPECT_THROW
(
timeFromText
(
"20110431120000"
),
InvalidTime
);
// 'Apr31'
EXPECT_THROW
(
timeFromText
(
"20110101250000"
),
InvalidTime
);
// HH=25
EXPECT_THROW
(
timeFromText
(
"20110101126000"
),
InvalidTime
);
// mm=60
EXPECT_THROW
(
timeFromText
(
"20110101120061"
),
InvalidTime
);
// SS=61
EXPECT_THROW
(
timeFromText32
(
"19100223214617"
),
InvalidTime
);
// YY<1970
EXPECT_THROW
(
timeFromText32
(
"20110001120000"
),
InvalidTime
);
// MM=00
EXPECT_THROW
(
timeFromText32
(
"20111301120000"
),
InvalidTime
);
// MM=13
EXPECT_THROW
(
timeFromText32
(
"20110100120000"
),
InvalidTime
);
// DD=00
EXPECT_THROW
(
timeFromText32
(
"20110132120000"
),
InvalidTime
);
// DD=32
EXPECT_THROW
(
timeFromText32
(
"20110431120000"
),
InvalidTime
);
// 'Apr31'
EXPECT_THROW
(
timeFromText32
(
"20110101250000"
),
InvalidTime
);
// HH=25
EXPECT_THROW
(
timeFromText32
(
"20110101126000"
),
InvalidTime
);
// mm=60
EXPECT_THROW
(
timeFromText32
(
"20110101120061"
),
InvalidTime
);
// SS=61
// Feb 7, 06:28:15 UTC 2106 is the possible maximum time that can be
// represented as an unsigned 32bit integer without overflow.
EXPECT_EQ
(
4294967295L
,
timeFromText32
(
"21060207062815"
));
// After that, timeFromText32() should start returning the second count
// modulo 2^32.
EXPECT_EQ
(
0
,
timeFromText32
(
"21060207062816"
));
EXPECT_EQ
(
10
,
timeFromText32
(
"21060207062826"
));
// On the other hand, the 64-bit version should return monotonically
// increasing counters.
EXPECT_EQ
(
4294967296LL
,
timeFromText64
(
"21060207062816"
));
EXPECT_EQ
(
4294967306LL
,
timeFromText64
(
"21060207062826"
));
}
TEST
(
DNSSECTimeTest
,
toText
)
{
EXPECT_EQ
(
"19700101000000"
,
timeToText
(
0
));
EXPECT_EQ
(
"20100311233000"
,
timeToText
(
1268350200
));
// This helper templated function tells timeToText32 a faked current time.
// The template parameter is that faked time in the form of int64_t seconds
// since epoch.
template
<
int64_t
NOW
>
int64_t
testGetTime
()
{
return
(
NOW
);
}
TEST
(
DNSSECTimeTest
,
overflow
)
{
// Seconds since epoch for the year 10K eve. Commonly used in some tests
// below.
const
uint64_t
YEAR10K_EVE
=
253402300799LL
;
TEST_F
(
DNSSECTimeTest
,
toText
)
{
// Check a basic case with the default (normal) gettimeFunction
// based on the "real current time".
// Note: this will fail after year 2078, but at that point we won't use
// this program anyway:-)
EXPECT_EQ
(
"20100311233000"
,
timeToText32
(
1268350200
));
// Set the current time to: Feb 18 09:04:14 UTC 2012 (an arbitrary choice
// in the range of the first half of uint32 since epoch).
dnssectime
::
detail
::
gettimeFunction
=
testGetTime
<
1329555854LL
>
;
// Test the "year 2038" problem.
// Check the result of toText() for "INT_MIN" in int32_t. It's in the
// 68-year range from the faked current time, so the result should be
// in year 2038, instead of 1901.
EXPECT_EQ
(
"20380119031408"
,
timeToText64
(
0x80000000L
));
EXPECT_EQ
(
"20380119031408"
,
timeToText32
(
0x80000000L
));
// A controversial case: what should we do with "-1"? It's out of range
// in future, but according to RFC time before epoch doesn't seem to be
// considered "in-range" either. Our toText() implementation handles
// this range as a special case and always treats them as future time
// until year 2038. This won't be a real issue in practice, though,
// since such too large values won't be used in actual deployment by then.
EXPECT_EQ
(
"21060207062815"
,
timeToText32
(
0xffffffffL
));
// After the singular point of year 2038, the first half of uint32 can
// point to a future time.
// Set the current time to: Apr 1 00:00:00 UTC 2038:
dnssectime
::
detail
::
gettimeFunction
=
testGetTime
<
2153692800LL
>
;
// then time "10" is Feb 7 06:28:26 UTC 2106
EXPECT_EQ
(
"21060207062826"
,
timeToText32
(
10
));
// in 64-bit, it's 2^32 + 10
EXPECT_EQ
(
"21060207062826"
,
timeToText64
(
0x10000000aLL
));
// After year 2106, the upper half of uint32 can point to past time
// (as it should).
dnssectime
::
detail
::
gettimeFunction
=
testGetTime
<
0x10000000aLL
>
;
EXPECT_EQ
(
"21060207062815"
,
timeToText32
(
0xffffffffL
));
// Try very large time value. Actually it's the possible farthest time
// that can be represented in the form of YYYYMMDDHHmmSS.
EXPECT_EQ
(
"99991231235959"
,
timeToText64
(
YEAR10K_EVE
));
dnssectime
::
detail
::
gettimeFunction
=
testGetTime
<
YEAR10K_EVE
-
10
>
;
EXPECT_EQ
(
"99991231235959"
,
timeToText32
(
4294197631L
));
}
TEST_F
(
DNSSECTimeTest
,
overflow
)
{
// Jan 1, Year 10,000.
if
(
sizeof
(
time_t
)
>
4
)
{
EXPECT_THROW
(
timeToText
(
static_cast
<
time_t
>
(
253402300800LL
)),
InvalidTime
);
}
EXPECT_THROW
(
timeToText64
(
253402300800LL
),
InvalidTime
);
dnssectime
::
detail
::
gettimeFunction
=
testGetTime
<
YEAR10K_EVE
-
10
>
;
EXPECT_THROW
(
timeToText32
(
4294197632L
),
InvalidTime
);
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment