diff --git a/CHANGES b/CHANGES index f57ba09d54..07207f6285 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,15 @@ + 189. [func] isc_time_secondsastimet(), a new function, will ensure + that the number of seconds in an isc_time_t does not + exceed the range of a time_t, or return ISC_R_RANGE. + Similarly, isc_time_now(), isc_time_nowplusinterval(), + isc_time_add() and isc_time_subtract() now check the + range for overflow/underflow. In the case of + isc_time_subtract, this changed a calling requirement + (ie, something that could generate an assertion) + into merely a condition that returns an error result. + isc_time_add() and isc_time_subtract() were void- + valued before but now return isc_result_t. + 188. [func] Log a warning message when an incoming zone transfer contains out-of-zone data. diff --git a/lib/isc/unix/include/isc/time.h b/lib/isc/unix/include/isc/time.h index 90fcaee4de..372bf6703b 100644 --- a/lib/isc/unix/include/isc/time.h +++ b/lib/isc/unix/include/isc/time.h @@ -61,8 +61,7 @@ isc_interval_iszero(isc_interval_t *i); * * Requires: * - * 't' is a valid pointer. - * + * 'i' is a valid pointer. */ /*** @@ -77,7 +76,7 @@ isc_interval_iszero(isc_interval_t *i); */ struct isc_time { - time_t seconds; + unsigned int seconds; unsigned int nanoseconds; }; @@ -111,7 +110,6 @@ isc_time_settoepoch(isc_time_t *t); * Requires: * * 't' is a valid pointer. - * */ isc_boolean_t @@ -122,7 +120,6 @@ isc_time_isepoch(isc_time_t *t); * Requires: * * 't' is a valid pointer. - * */ isc_result_t @@ -138,6 +135,10 @@ isc_time_now(isc_time_t *t); * * Success * Unexpected error + * Getting the time from the system failed. + * Out of range + * The time from the system is too large to be represented + * in the current definition of isc_time_t. */ isc_result_t @@ -146,8 +147,6 @@ isc_time_nowplusinterval(isc_time_t *t, isc_interval_t *i); * Set *t to the current absolute time + i. * * Note: - * Overflow is not checked. - * * This call is equivalent to: * * isc_time_now(t); @@ -161,6 +160,10 @@ isc_time_nowplusinterval(isc_time_t *t, isc_interval_t *i); * * Success * Unexpected error + * Getting the time from the system failed. + * Out of range + * The interval added to the time from the system is too large to + * be represented in the current definition of isc_time_t. */ int @@ -179,20 +182,23 @@ isc_time_compare(isc_time_t *t1, isc_time_t *t2); * 1 t1 > t2 */ -void +isc_result_t isc_time_add(isc_time_t *t, isc_interval_t *i, isc_time_t *result); /* * Add 'i' to 't', storing the result in 'result'. * - * Notes: - * Overflow is not checked. - * * Requires: * * 't', 'i', and 'result' are valid pointers. + * + * Returns: + * Success + * Out of range + * The interval added to the time is too large to + * be represented in the current definition of isc_time_t. */ -void +isc_result_t isc_time_subtract(isc_time_t *t, isc_interval_t *i, isc_time_t *result); /* * Subtract 'i' from 't', storing the result in 'result'. @@ -201,7 +207,10 @@ isc_time_subtract(isc_time_t *t, isc_interval_t *i, isc_time_t *result); * * 't', 'i', and 'result' are valid pointers. * - * t >= epoch + i (comparing times, not pointers) + * Returns: + * Success + * Out of range + * The interval is larger than the time since the epoch. */ isc_uint64_t @@ -211,6 +220,7 @@ isc_time_microdiff(isc_time_t *t1, isc_time_t *t2); * t2 is the subtrahend of t1; ie, difference = t1 - t2. * * Requires: + * * 't1' and 't2' are valid pointers. */ @@ -220,9 +230,33 @@ isc_time_seconds(isc_time_t *t); * Return the number of seconds since the epoch stored in a time structure. * * Requires: + * * 't' is a valid pointer. */ +isc_result_t +isc_time_secondsastimet(isc_time_t *t, time_t *secondsp); +/* + * Ensure the number of seconds in an isc_time_t is representable by a time_t. + * + * Notes: + * The number of seconds stored in an isc_time_t might be larger + * than the number of seconds a time_t is able to handle. Since + * time_t is mostly opaque according to the ANSI/ISO standard + * (essentially, all you can be sure of is that it is an arithmetic type, + * not even necessarily integral), it can be tricky to ensure that + * the isc_time_t is in the range a time_t can handle. Use this + * function in place of isc_time_seconds() any time you need to set a + * time_t from an isc_time_t. + * + * Requires: + * 't' is a valid pointer. + * + * Returns: + * Success + * Out of range + */ + isc_uint32_t isc_time_nanoseconds(isc_time_t *t); /* diff --git a/lib/isc/unix/time.c b/lib/isc/unix/time.c index 7fe33b4199..6469dd4fdf 100644 --- a/lib/isc/unix/time.c +++ b/lib/isc/unix/time.c @@ -18,6 +18,7 @@ #include #include +#include #include #include /* Required for struct timeval on some platforms. */ @@ -26,6 +27,17 @@ #include #include +#define NS_PER_S 1000000000 /* Nanoseconds per second. */ +#define NS_PER_US 1000 /* Nanoseconds per microsecond. */ +#define US_PER_S 1000000 /* Microseconds per second. */ + +/* + * All of the INSIST()s checks of nanoseconds < NS_PER_S are for + * consistency checking of the type. In lieu of magic numbers, it + * is the best we've got. The check is only performed on functions which + * need an initialized type. + */ + /*** *** Intervals ***/ @@ -35,7 +47,8 @@ isc_interval_t *isc_interval_zero = &zero_interval; void isc_interval_set(isc_interval_t *i, - unsigned int seconds, unsigned int nanoseconds) { + unsigned int seconds, unsigned int nanoseconds) +{ /* * Set 'i' to a value representing an interval of 'seconds' seconds @@ -44,7 +57,7 @@ isc_interval_set(isc_interval_t *i, */ REQUIRE(i != NULL); - REQUIRE(nanoseconds < 1000000000); + REQUIRE(nanoseconds < NS_PER_S); i->seconds = seconds; i->nanoseconds = nanoseconds; @@ -58,6 +71,7 @@ isc_interval_iszero(isc_interval_t *i) { */ REQUIRE(i != NULL); + INSIST(i->nanoseconds < NS_PER_S); if (i->seconds == 0 && i->nanoseconds == 0) return (ISC_TRUE); @@ -80,6 +94,7 @@ isc_time_set(isc_time_t *t, unsigned int seconds, unsigned int nanoseconds) { * epoch. */ REQUIRE(t != NULL); + REQUIRE(t->nanoseconds < NS_PER_S); t->seconds = seconds; t->nanoseconds = nanoseconds; @@ -105,6 +120,7 @@ isc_time_isepoch(isc_time_t *t) { */ REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); if (t->seconds == 0 && t->nanoseconds == 0) return (ISC_TRUE); @@ -127,8 +143,25 @@ isc_time_now(isc_time_t *t) { return (ISC_R_UNEXPECTED); } + /* + * Does POSIX guarantee the signedness of tv_sec and tv_usec? If not, + * then this test will generate warnings for platforms on which it is + * unsigned. In any event, the chances of any of these problems + * happening are pretty much zero, but since the libisc library ensures + * certain things to be true ... + */ + if (tv.tv_sec < 0 || tv.tv_usec < 0 || tv.tv_usec >= US_PER_S) + return (ISC_R_UNEXPECTED); + + /* + * Ensure the tv_sec value fits in t->seconds. + */ + if (sizeof(tv.tv_sec) > sizeof(t->seconds) && + ((tv.tv_sec | (unsigned int)-1) ^ (unsigned int)-1) != 0) + return (ISC_R_RANGE); + t->seconds = tv.tv_sec; - t->nanoseconds = tv.tv_usec * 1000; + t->nanoseconds = tv.tv_usec * NS_PER_US; return (ISC_R_SUCCESS); } @@ -143,17 +176,38 @@ isc_time_nowplusinterval(isc_time_t *t, isc_interval_t *i) { REQUIRE(t != NULL); REQUIRE(i != NULL); - + INSIST(i->nanoseconds < NS_PER_S); + if (gettimeofday(&tv, NULL) == -1) { UNEXPECTED_ERROR(__FILE__, __LINE__, strerror(errno)); return (ISC_R_UNEXPECTED); } + /* + * Does POSIX guarantee the signedness of tv_sec and tv_usec? If not, + * then this test will generate warnings for platforms on which it is + * unsigned. In any event, the chances of any of these problems + * happening are pretty much zero, but since the libisc library ensures + * certain things to be true ... + */ + if (tv.tv_sec < 0 || tv.tv_usec < 0 || tv.tv_usec >= US_PER_S) + return (ISC_R_UNEXPECTED); + + /* + * Ensure the resulting seconds value fits in the size of an + * unsigned int. (It is written this way as a slight optimization; + * note that even if both values == INT_MAX, then when added + * and getting another 1 added below the result is UINT_MAX.) + */ + if ((tv.tv_sec > INT_MAX || i->seconds > INT_MAX) && + ((long long)tv.tv_sec + i->seconds > UINT_MAX)) + return (ISC_R_RANGE); + t->seconds = tv.tv_sec + i->seconds; - t->nanoseconds = tv.tv_usec * 1000 + i->nanoseconds; - if (t->nanoseconds > 1000000000) { + t->nanoseconds = tv.tv_usec * NS_PER_US + i->nanoseconds; + if (t->nanoseconds > NS_PER_S) { t->seconds++; - t->nanoseconds -= 1000000000; + t->nanoseconds -= NS_PER_S; } return (ISC_R_SUCCESS); @@ -167,6 +221,7 @@ isc_time_compare(isc_time_t *t1, isc_time_t *t2) { */ REQUIRE(t1 != NULL && t2 != NULL); + INSIST(t1->nanoseconds < NS_PER_S && t2->nanoseconds < NS_PER_S); if (t1->seconds < t2->seconds) return (-1); @@ -179,42 +234,59 @@ isc_time_compare(isc_time_t *t1, isc_time_t *t2) { return (0); } -void -isc_time_add(isc_time_t *t, isc_interval_t *i, isc_time_t *result) -{ +isc_result_t +isc_time_add(isc_time_t *t, isc_interval_t *i, isc_time_t *result) { /* * Add 't' to 'i', storing the result in 'result'. */ REQUIRE(t != NULL && i != NULL && result != NULL); + INSIST(t->nanoseconds < NS_PER_S && i->nanoseconds < NS_PER_S); + + /* + * Ensure the resulting seconds value fits in the size of an + * unsigned int. (It is written this way as a slight optimization; + * note that even if both values == INT_MAX, then when added + * and getting another 1 added below the result is UINT_MAX.) + */ + if ((t->seconds > INT_MAX || i->seconds > INT_MAX) && + ((long long)t->seconds + i->seconds > UINT_MAX)) + return (ISC_R_RANGE); result->seconds = t->seconds + i->seconds; result->nanoseconds = t->nanoseconds + i->nanoseconds; - if (result->nanoseconds > 1000000000) { + if (result->nanoseconds > NS_PER_S) { result->seconds++; - result->nanoseconds -= 1000000000; + result->nanoseconds -= NS_PER_S; } + + return (ISC_R_SUCCESS); } -void +isc_result_t isc_time_subtract(isc_time_t *t, isc_interval_t *i, isc_time_t *result) { /* * Subtract 'i' from 't', storing the result in 'result'. */ REQUIRE(t != NULL && i != NULL && result != NULL); - REQUIRE((unsigned int)t->seconds > i->seconds || - ((unsigned int)t->seconds == i->seconds && - t->nanoseconds >= i->nanoseconds)); - + INSIST(t->nanoseconds < NS_PER_S && i->nanoseconds < NS_PER_S); + + if ((unsigned int)t->seconds < i->seconds || + ((unsigned int)t->seconds == i->seconds && + t->nanoseconds < i->nanoseconds)) + return (ISC_R_RANGE); + result->seconds = t->seconds - i->seconds; if (t->nanoseconds >= i->nanoseconds) result->nanoseconds = t->nanoseconds - i->nanoseconds; else { - result->nanoseconds = 1000000000 - i->nanoseconds + + result->nanoseconds = NS_PER_S - i->nanoseconds + t->nanoseconds; result->seconds--; } + + return (ISC_R_SUCCESS); } isc_uint64_t @@ -222,9 +294,10 @@ isc_time_microdiff(isc_time_t *t1, isc_time_t *t2) { isc_uint64_t i1, i2, i3; REQUIRE(t1 != NULL && t2 != NULL); + INSIST(t1->nanoseconds < NS_PER_S && t2->nanoseconds < NS_PER_S); - i1 = t1->seconds * 1000000000 + t1->nanoseconds; - i2 = t2->seconds * 1000000000 + t2->nanoseconds; + i1 = (isc_uint64_t)t1->seconds * NS_PER_S + t1->nanoseconds; + i2 = (isc_uint64_t)t2->seconds * NS_PER_S + t2->nanoseconds; if (i1 <= i2) return (0); @@ -234,7 +307,7 @@ isc_time_microdiff(isc_time_t *t1, isc_time_t *t2) { /* * Convert to microseconds. */ - i3 = (i1 - i2) / 1000; + i3 = (i1 - i2) / NS_PER_US; return (i3); } @@ -242,15 +315,57 @@ isc_time_microdiff(isc_time_t *t1, isc_time_t *t2) { isc_uint32_t isc_time_seconds(isc_time_t *t) { REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); return ((isc_uint32_t)t->seconds); } +isc_result_t +isc_time_secondsastimet(isc_time_t *t, time_t *secondsp) { + isc_uint64_t i; + time_t seconds; + + REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); + + /* + * Ensure that the number of seconds represented by t->seconds + * can be represented by a time_t. Since t->seconds is an unsigned + * int and since time_t is mostly opaque, this is trickier than + * it seems. (This standardized opaqueness of time_t is *very* + * frustrating; time_t is not even limited to being an integral + * type.) + * + * The mission, then, is to avoid generating any kind of warning + * about "signed versus unsigned" while trying to determine if the + * the unsigned int t->seconds is out range for tv_sec, which is + * pretty much only true if time_t is a signed integer of the same + * size as the return value of isc_time_seconds. + * + * The use of a 64 bit integer takes advantage of C's conversion rules + * to either zero fill or sign extend the widened type. + */ + seconds = (time_t)t->seconds; + + INSIST(sizeof(unsigned int) == sizeof(isc_uint32_t)); + INSIST(sizeof(time_t) >= sizeof(isc_uint32_t)); + + if (sizeof(time_t) == sizeof(isc_uint32_t) && /* Same size. */ + (time_t)0.5 != 0.5 && /* Not a floating point type. */ + (i = (time_t)-1) != 4294967295u && /* Is signed. */ + (seconds & (1 << (sizeof(time_t) * 8 - 1))) != 0) /* Negative. */ + return (ISC_R_RANGE); + + *secondsp = seconds; + + return (ISC_R_SUCCESS); +} + isc_uint32_t isc_time_nanoseconds(isc_time_t *t) { REQUIRE(t != NULL); - ENSURE(t->nanoseconds < 1000000000); + ENSURE(t->nanoseconds < NS_PER_S); return ((isc_uint32_t)t->nanoseconds); } diff --git a/lib/isc/win32/include/isc/time.h b/lib/isc/win32/include/isc/time.h index 33cc4929ff..0d44439b16 100644 --- a/lib/isc/win32/include/isc/time.h +++ b/lib/isc/win32/include/isc/time.h @@ -62,8 +62,7 @@ isc_interval_iszero(isc_interval_t *i); * * Requires: * - * 't' is a valid pointer. - * + * 'i' is a valid pointer. */ /*** @@ -111,7 +110,6 @@ isc_time_settoepoch(isc_time_t *t); * Requires: * * 't' is a valid pointer. - * */ isc_boolean_t @@ -122,7 +120,6 @@ isc_time_isepoch(isc_time_t *t); * Requires: * * 't' is a valid pointer. - * */ isc_result_t @@ -138,6 +135,10 @@ isc_time_now(isc_time_t *t); * * Success * Unexpected error + * Getting the time from the system failed. + * Out of range + * The time from the system is too large to be represented + * in the current definition of isc_time_t. */ isc_result_t @@ -146,8 +147,6 @@ isc_time_nowplusinterval(isc_time_t *t, isc_interval_t *i); * Set *t to the current absolute time + i. * * Note: - * Overflow is not checked. - * * This call is equivalent to: * * isc_time_now(t); @@ -161,6 +160,10 @@ isc_time_nowplusinterval(isc_time_t *t, isc_interval_t *i); * * Success * Unexpected error + * Getting the time from the system failed. + * Out of range + * The interval added to the time from the system is too large to + * be represented in the current definition of isc_time_t. */ int @@ -179,20 +182,23 @@ isc_time_compare(isc_time_t *t1, isc_time_t *t2); * 1 t1 > t2 */ -void +isc_result_t isc_time_add(isc_time_t *t, isc_interval_t *i, isc_time_t *result); /* * Add 'i' to 't', storing the result in 'result'. * - * Notes: - * Overflow is not checked. - * * Requires: * * 't', 'i', and 'result' are valid pointers. + * + * Returns: + * Success + * Out of range + * The interval added to the time is too large to + * be represented in the current definition of isc_time_t. */ -void +isc_result_t isc_time_subtract(isc_time_t *t, isc_interval_t *i, isc_time_t *result); /* * Subtract 'i' from 't', storing the result in 'result'. @@ -201,7 +207,10 @@ isc_time_subtract(isc_time_t *t, isc_interval_t *i, isc_time_t *result); * * 't', 'i', and 'result' are valid pointers. * - * t >= epoch + i (comparing times, not pointers) + * Returns: + * Success + * Out of range + * The interval is larger than the time since the epoch. */ isc_uint64_t @@ -211,6 +220,7 @@ isc_time_microdiff(isc_time_t *t1, isc_time_t *t2); * t2 is the subtrahend of t1; ie, difference = t1 - t2. * * Requires: + * * 't1' and 't2' are valid pointers. */ @@ -220,9 +230,33 @@ isc_time_seconds(isc_time_t *t); * Return the number of seconds since the epoch stored in a time structure. * * Requires: + * * 't' is a valid pointer. */ +isc_result_t +isc_time_secondsastimet(isc_time_t *t, time_t *secondsp); +/* + * Ensure the number of seconds in an isc_time_t is representable by a time_t. + * + * Notes: + * The number of seconds stored in an isc_time_t might be larger + * than the number of seconds a time_t is able to handle. Since + * time_t is mostly opaque according to the ANSI/ISO standard + * (essentially, all you can be sure of is that it is an arithmetic type, + * not even necessarily integral), it can be tricky to ensure that + * the isc_time_t is in the range a time_t can handle. Use this + * function in place of isc_time_seconds() any time you need to set a + * time_t from an isc_time_t. + * + * Requires: + * 't' is a valid pointer. + * + * Returns: + * Success + * Out of range + */ + isc_uint32_t isc_time_nanoseconds(isc_time_t *t); /* diff --git a/lib/isc/win32/time.c b/lib/isc/win32/time.c index 3a15cbaabc..4a6bb7115a 100644 --- a/lib/isc/win32/time.c +++ b/lib/isc/win32/time.c @@ -17,11 +17,12 @@ #include +#include +#include #include #include #include #include -#include #include @@ -29,7 +30,7 @@ #include /* - * struct FILETIME uses "100-nanoseconds intevals". + * struct FILETIME uses "100-nanoseconds intervals". * NS / S = 1000000000 (10^9). * While it is reasonably obvious that this makes the needed * conversion factor 10^7, it is coded this way for additional clarity. @@ -37,6 +38,7 @@ #define NS_PER_S 1000000000 #define NS_INTERVAL 100 #define INTERVALS_PER_S (NS_PER_S / NS_INTERVAL) +#define UINT64_MAX 0xffffffffffffffffui64 /*** *** Intervals @@ -159,6 +161,9 @@ isc_time_nowplusinterval(isc_time_t *t, isc_interval_t *i) { i1.LowPart = t->absolute.dwLowDateTime; i1.HighPart = t->absolute.dwHighDateTime; + if (UINT64_MAX - i1.QuadPart < i->interval) + return (ISC_R_RANGE); + i1.QuadPart += i->interval; t->absolute.dwLowDateTime = i1.LowPart; @@ -178,7 +183,7 @@ isc_time_compare(isc_time_t *t1, isc_time_t *t2) { return ((int)CompareFileTime(&t1->absolute, &t2->absolute)); } -void +isc_result_t isc_time_add(isc_time_t *t, isc_interval_t *i, isc_time_t *result) { ULARGE_INTEGER i1; @@ -191,13 +196,18 @@ isc_time_add(isc_time_t *t, isc_interval_t *i, isc_time_t *result) { i1.LowPart = t->absolute.dwLowDateTime; i1.HighPart = t->absolute.dwHighDateTime; + if (UINT64_MAX - i1.QuadPart < i->interval) + return (ISC_R_RANGE); + i1.QuadPart += i->interval; result->absolute.dwLowDateTime = i1.LowPart; result->absolute.dwHighDateTime = i1.HighPart; + + return (ISC_R_SUCCESS); } -void +isc_result_t isc_time_subtract(isc_time_t *t, isc_interval_t *i, isc_time_t *result) { ULARGE_INTEGER i1; @@ -210,7 +220,8 @@ isc_time_subtract(isc_time_t *t, isc_interval_t *i, isc_time_t *result) { i1.LowPart = t->absolute.dwLowDateTime; i1.HighPart = t->absolute.dwHighDateTime; - REQUIRE(i1.QuadPart >= i->interval); + if (i.QuadPart < i->interval) + return (ISC_R_RANGE); i1.QuadPart -= i->interval; @@ -255,6 +266,99 @@ isc_time_seconds(isc_time_t *t) { return ((isc_uint32_t)(i.QuadPart / INTERVALS_PER_S)); } +isc_result_t +isc_time_secondsastimet(isc_time_t *t, time_t *secondsp) { + ULARGE_INTEGER i1, i2; + time_t seconds; + + REQUIRE(t != NULL); + + i1.LowPart = t->absolute.dwLowDateTime; + i1.HighPart = t->absolute.dwHighDateTime; + + i1.QuadPart /= INTERVALS_PER_S; + + /* + * Ensure that the number of seconds can be represented by a time_t. + * Since the number seconds is an unsigned int and since time_t is + * mostly opaque, this is trickier than it seems. (This standardized + * opaqueness of time_t is *very* * frustrating; time_t is not even + * limited to being an integral type.) Thought it is known at the + * time of this writing that time_t is a signed long on the Win32 + * platform, the full treatment is given to figuring out if things + * fit to allow for future Windows platforms where time_t is *not* + * a signed long, or where perhaps a signed long is longer than + * it currently is. + */ + seconds = (time_t)i1.QuadPart; + + /* + * First, only do the range tests if the type of size_t is integral. + * Float/double easily include the maximum possible values. + */ + if ((time_t)0.5 != 0.5) { + /* + * Did all the bits make it in? + */ + if ((seconds & i1.QuadPart) != i1.QuadPart) + return (ISC_R_RANGE); + + /* + * Is time_t signed with the high bit set? + * + * The first test (the sizeof comparison) determines + * whether we can even deduce the signedness of time_t + * by using ANSI's rule about integer conversion to + * wider integers. + * + * The second test uses that ANSI rule to see whether + * the value of time_t was sign extended into QuadPart. + * If the test is true, then time_t is signed. + * + * The final test ensures the high bit is not set, or + * the value is negative and hence there is a range error. + */ + if (sizeof(time_t) < sizeof(i2.QuadPart) && + ((i2.QuadPart = (time_t)-1) ^ (time_t)-1) != 0 && + (seconds & (1 << (sizeof(time_t) * 8 - 1))) != 0) + return (ISC_R_RANGE); + + /* + * Last test ... the size of time_t is >= that of i2.QuadPart, + * so we can't determine its signedness. Unconditionally + * declare anything with the high bit set as out of range. + * Since even the maxed signed value is ludicrously far from + * when this is being written, this rule shall not impact + * anything for all intents and purposes. + * + * How far? Well ... if FILETIME is in 100 ns intervals since + * 1600, and a QuadPart can store 9223372036854775808 such + * intervals when interpreted as signed (ie, if sizeof(time_t) + * == sizeof(QuadPart) but time_t is signed), that means + * 9223372036854775808 / INTERVALS_PER_S = 922,337,203,685 + * seconds. That number divided by 60 * 60 * 24 * 365 seconds + * per year means a signed time_t can store at least 29,247 + * years, with only 400 of those years used up since 1600 as I + * write this in May, 2000. + * + * (Real date calculations are of course incredibly more + * complex; I'm only describing the approximate scale of + * the numbers involved here.) + * + * If the Galactic Federation is still running libisc's time + * libray on a Windows platform in the year 27647 A.D., then + * feel free to hunt down my greatgreatgreatgreatgreat(etc) + * grandchildren and whine at them about what I did. + */ + if ((seconds & (1 << (sizeof(time_t) * 8 - 1))) != 0) + return (ISC_R_RANGE); + } + + *secondsp = seconds; + + return (ISC_R_SUCCESS); +} + isc_uint32_t isc_time_nanoseconds(isc_time_t *t) { ULARGE_INTEGER i;