freebsd-nq/libntp/caljulian.c
Ollivier Robert 2b45e011ca Virgin import of ntpd 4.2.6p5.
When the series of commits is complete, things like
https://cert.litnet.lt/en/docs/ntp-distributed-reflection-dos-attacks
should be fixed.

PR:		bin/148836 (except that we import a newer version)
Asked by:	Too many
MFC after:	2 weeks
2013-12-04 21:33:17 +00:00

198 lines
6.0 KiB
C

/*
* caljulian - determine the Julian date from an NTP time.
*/
#include <sys/types.h>
#include "ntp_types.h"
#include "ntp_calendar.h"
#include "ntp_stdlib.h"
#include "ntp_fp.h"
#include "ntp_unixtime.h"
#if !(defined(ISC_CHECK_ALL) || defined(ISC_CHECK_NONE) || \
defined(ISC_CHECK_ENSURE) || defined(ISC_CHECK_INSIST) || \
defined(ISC_CHECK_INVARIANT))
# define ISC_CHECK_ALL
#endif
#include "ntp_assert.h"
#if 1
/* Updated 2008-11-10 Juergen Perlinger <juergen.perlinger@t-online.de>
*
* Make the conversion 2038-proof with proper NTP epoch unfolding and extended
* precision calculations. Though we should really get a 'time_t' with more
* than 32 bits at least until 2037, because the unfolding cannot work after
* the wrap of the 32-bit 'time_t'.
*/
void
caljulian(
u_long ntptime,
register struct calendar *jt
)
{
u_long saved_time = ntptime;
u_long ntp_day; /* days (since christian era or in year) */
u_long n400; /* # of Gregorian cycles */
u_long n100; /* # of normal centuries */
u_long n4; /* # of 4-year cycles */
u_long n1; /* # of years into a leap year cycle */
u_long sclday; /* scaled days for month conversion */
int leaps; /* # of leaps days in year */
time_t now; /* current system time */
u_int32 tmplo; /* double precision tmp value / lo part */
int32 tmphi; /* double precision tmp value / hi part */
NTP_INSIST(NULL != jt);
/*
* First we have to unfold the ntp time stamp around the current time
* to make sure we are in the right epoch. Also we we do *NOT* fold
* before the begin of the first NTP epoch, so we WILL have a
* non-negative time stamp afterwards. Though at the time of this
* writing (2008 A.D.) it would be really strange to have systems
* running with clock set to he 1960's or before...
*
* But's important to use a 32 bit max signed value -- LONG_MAX is 64
* bit on a 64-bit system, and it will give wrong results.
*/
now = time(NULL);
tmplo = (u_int32)now;
#if ( SIZEOF_TIME_T > 4 )
tmphi = (int32)(now >> 16 >> 16);
#else
/*
* Get the correct sign extension in the high part.
* (now >> 32) may not work correctly on every 32 bit
* system, e.g. it yields garbage under Win32/VC6.
*/
tmphi = (int32)(now >> 31);
#endif
M_ADD(tmphi, tmplo, 0, ((1UL << 31)-1)); /* 32-bit max signed */
M_ADD(tmphi, tmplo, 0, JAN_1970);
if ((ntptime > tmplo) && (tmphi > 0))
--tmphi;
tmplo = ntptime;
/*
* Now split into days and seconds-of-day, using the fact that
* SECSPERDAY (86400) == 675 * 128; we can get roughly 17000 years of
* time scale, using only 32-bit calculations. Some magic numbers here,
* sorry for that. (This could be streamlined for 64 bit machines, but
* is worth the trouble?)
*/
ntptime = tmplo & 127; /* save remainder bits */
tmplo = (tmplo >> 7) | (tmphi << 25);
ntp_day = (u_int32)tmplo / 675;
ntptime += ((u_int32)tmplo % 675) << 7;
/* some checks for the algorithm
* There's some 64-bit trouble out there: the original NTP time stamp
* had only 32 bits, so our calculation invariant only holds in 32 bits!
*/
NTP_ENSURE(ntptime < SECSPERDAY);
NTP_INVARIANT((u_int32)(ntptime + ntp_day * SECSPERDAY) == (u_int32)saved_time);
/*
* Do the easy stuff first: take care of hh:mm:ss, ignoring leap
* seconds
*/
jt->second = (u_char)(ntptime % SECSPERMIN);
ntptime /= SECSPERMIN;
jt->minute = (u_char)(ntptime % MINSPERHR);
ntptime /= MINSPERHR;
jt->hour = (u_char)(ntptime);
/* check time invariants */
NTP_ENSURE(jt->second < SECSPERMIN);
NTP_ENSURE(jt->minute < MINSPERHR);
NTP_ENSURE(jt->hour < HRSPERDAY);
/*
* Find the day past 1900/01/01 00:00 UTC
*/
ntp_day += DAY_NTP_STARTS - 1; /* convert to days in CE */
n400 = ntp_day / GREGORIAN_CYCLE_DAYS; /* split off cycles */
ntp_day %= GREGORIAN_CYCLE_DAYS;
n100 = ntp_day / GREGORIAN_NORMAL_CENTURY_DAYS;
ntp_day %= GREGORIAN_NORMAL_CENTURY_DAYS;
n4 = ntp_day / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
ntp_day %= GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
n1 = ntp_day / DAYSPERYEAR;
ntp_day %= DAYSPERYEAR; /* now zero-based day-of-year */
NTP_ENSURE(ntp_day < 366);
/*
* Calculate the year and day-of-year
*/
jt->year = (u_short)(400*n400 + 100*n100 + 4*n4 + n1);
if ((n100 | n1) > 3) {
/*
* If the cycle year ever comes out to 4, it must be December
* 31st of a leap year.
*/
jt->month = 12;
jt->monthday = 31;
jt->yearday = 366;
} else {
/*
* The following code is according to the excellent book
* 'Calendrical Calculations' by Nachum Dershowitz and Edward
* Reingold. It converts the day-of-year into month and
* day-of-month, using a linear transformation with integer
* truncation. Magic numbers again, but they will not be used
* anywhere else.
*/
sclday = ntp_day * 7 + 217;
leaps = ((n1 == 3) && ((n4 != 24) || (n100 == 3))) ? 1 : 0;
if (ntp_day >= (u_long)(JAN + FEB + leaps))
sclday += (2 - leaps) * 7;
++jt->year;
jt->month = (u_char)(sclday / 214);
jt->monthday = (u_char)((sclday % 214) / 7 + 1);
jt->yearday = (u_short)(1 + ntp_day);
}
/* check date invariants */
NTP_ENSURE(1 <= jt->month && jt->month <= 12);
NTP_ENSURE(1 <= jt->monthday && jt->monthday <= 31);
NTP_ENSURE(1 <= jt->yearday && jt->yearday <= 366);
}
#else
/* Updated 2003-12-30 TMa
Uses common code with the *prettydate functions to convert an ntp
seconds count into a calendar date.
Will handle ntp epoch wraparound as long as the underlying os/library
does so for the unix epoch, i.e. works after 2038.
*/
void
caljulian(
u_long ntptime,
register struct calendar *jt
)
{
struct tm *tm;
NTP_REQUIRE(jt != NULL);
tm = ntp2unix_tm(ntptime, 0);
NTP_INSIST(tm != NULL);
jt->hour = (u_char) tm->tm_hour;
jt->minute = (u_char) tm->tm_min;
jt->month = (u_char) (tm->tm_mon + 1);
jt->monthday = (u_char) tm->tm_mday;
jt->second = (u_char) tm->tm_sec;
jt->year = (u_short) (tm->tm_year + 1900);
jt->yearday = (u_short) (tm->tm_yday + 1); /* Assumes tm_yday starts with day 0! */
}
#endif