12b15cb3dSCy Schubert /*
22b15cb3dSCy Schubert * ntp_leapsec.c - leap second processing for NTPD
32b15cb3dSCy Schubert *
42b15cb3dSCy Schubert * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
52b15cb3dSCy Schubert * The contents of 'html/copyright.html' apply.
62b15cb3dSCy Schubert * ----------------------------------------------------------------------
72b15cb3dSCy Schubert * This is an attempt to get the leap second handling into a dedicated
82b15cb3dSCy Schubert * module to make the somewhat convoluted logic testable.
92b15cb3dSCy Schubert */
102b15cb3dSCy Schubert
112b15cb3dSCy Schubert #include <config.h>
122b15cb3dSCy Schubert #include <sys/types.h>
132b15cb3dSCy Schubert #include <sys/stat.h>
142b15cb3dSCy Schubert #include <ctype.h>
152b15cb3dSCy Schubert
16*f5f40dd6SCy Schubert #include "ntp.h"
172b15cb3dSCy Schubert #include "ntp_stdlib.h"
182b15cb3dSCy Schubert #include "ntp_calendar.h"
192b15cb3dSCy Schubert #include "ntp_leapsec.h"
202b15cb3dSCy Schubert #include "vint64ops.h"
212b15cb3dSCy Schubert
222b15cb3dSCy Schubert #include "isc/sha1.h"
232b15cb3dSCy Schubert
242b15cb3dSCy Schubert static const char * const logPrefix = "leapsecond file";
252b15cb3dSCy Schubert
262b15cb3dSCy Schubert /* ---------------------------------------------------------------------
272b15cb3dSCy Schubert * Our internal data structure
282b15cb3dSCy Schubert */
292b15cb3dSCy Schubert #define MAX_HIST 10 /* history of leap seconds */
302b15cb3dSCy Schubert
312b15cb3dSCy Schubert struct leap_info {
322b15cb3dSCy Schubert vint64 ttime; /* transition time (after the step, ntp scale) */
332b15cb3dSCy Schubert uint32_t stime; /* schedule limit (a month before transition) */
342b15cb3dSCy Schubert int16_t taiof; /* TAI offset on and after the transition */
352b15cb3dSCy Schubert uint8_t dynls; /* dynamic: inserted on peer/clock request */
362b15cb3dSCy Schubert };
372b15cb3dSCy Schubert typedef struct leap_info leap_info_t;
382b15cb3dSCy Schubert
392b15cb3dSCy Schubert struct leap_head {
402b15cb3dSCy Schubert vint64 update; /* time of information update */
412b15cb3dSCy Schubert vint64 expire; /* table expiration time */
422b15cb3dSCy Schubert uint16_t size; /* number of infos in table */
432b15cb3dSCy Schubert int16_t base_tai; /* total leaps before first entry */
442b15cb3dSCy Schubert int16_t this_tai; /* current TAI offset */
452b15cb3dSCy Schubert int16_t next_tai; /* TAI offset after 'when' */
462b15cb3dSCy Schubert vint64 dtime; /* due time (current era end) */
472b15cb3dSCy Schubert vint64 ttime; /* nominal transition time (next era start) */
482b15cb3dSCy Schubert vint64 stime; /* schedule time (when we take notice) */
492b15cb3dSCy Schubert vint64 ebase; /* base time of this leap era */
502b15cb3dSCy Schubert uint8_t dynls; /* next leap is dynamic (by peer request) */
512b15cb3dSCy Schubert };
522b15cb3dSCy Schubert typedef struct leap_head leap_head_t;
532b15cb3dSCy Schubert
542b15cb3dSCy Schubert struct leap_table {
552b15cb3dSCy Schubert leap_signature_t lsig;
562b15cb3dSCy Schubert leap_head_t head;
572b15cb3dSCy Schubert leap_info_t info[MAX_HIST];
582b15cb3dSCy Schubert };
592b15cb3dSCy Schubert
602b15cb3dSCy Schubert /* Where we store our tables */
612b15cb3dSCy Schubert static leap_table_t _ltab[2], *_lptr;
622b15cb3dSCy Schubert static int/*BOOL*/ _electric;
632b15cb3dSCy Schubert
642b15cb3dSCy Schubert /* Forward decls of local helpers */
652b15cb3dSCy Schubert static int add_range (leap_table_t *, const leap_info_t *);
66*f5f40dd6SCy Schubert static char * get_line (leapsec_reader, void *, char *,
67*f5f40dd6SCy Schubert size_t);
68*f5f40dd6SCy Schubert static inline char * skipws (char *ptr);
692b15cb3dSCy Schubert static int parsefail (const char *cp, const char *ep);
702b15cb3dSCy Schubert static void reload_limits (leap_table_t *, const vint64 *);
71276da39aSCy Schubert static void fetch_leap_era (leap_era_t *, const leap_table_t *,
72276da39aSCy Schubert const vint64 *);
73*f5f40dd6SCy Schubert static int betweenu32 (u_int32, u_int32, u_int32);
742b15cb3dSCy Schubert static void reset_times (leap_table_t *);
752b15cb3dSCy Schubert static int leapsec_add (leap_table_t *, const vint64 *, int);
76*f5f40dd6SCy Schubert static int leapsec_raw (leap_table_t *, const vint64 *, int,
77*f5f40dd6SCy Schubert int);
78276da39aSCy Schubert static const char * lstostr (const vint64 *ts);
792b15cb3dSCy Schubert
802b15cb3dSCy Schubert /* =====================================================================
812b15cb3dSCy Schubert * Get & Set the current leap table
822b15cb3dSCy Schubert */
832b15cb3dSCy Schubert
842b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
852b15cb3dSCy Schubert leap_table_t *
leapsec_get_table(int alternate)862b15cb3dSCy Schubert leapsec_get_table(
872b15cb3dSCy Schubert int alternate)
882b15cb3dSCy Schubert {
892b15cb3dSCy Schubert leap_table_t *p1, *p2;
902b15cb3dSCy Schubert
912b15cb3dSCy Schubert p1 = _lptr;
92276da39aSCy Schubert if (p1 == &_ltab[0]) {
93276da39aSCy Schubert p2 = &_ltab[1];
94276da39aSCy Schubert } else if (p1 == &_ltab[1]) {
95276da39aSCy Schubert p2 = &_ltab[0];
96276da39aSCy Schubert } else {
97276da39aSCy Schubert p1 = &_ltab[0];
98276da39aSCy Schubert p2 = &_ltab[1];
99276da39aSCy Schubert reset_times(p1);
100276da39aSCy Schubert reset_times(p2);
101276da39aSCy Schubert _lptr = p1;
102276da39aSCy Schubert }
1032b15cb3dSCy Schubert if (alternate) {
1042b15cb3dSCy Schubert memcpy(p2, p1, sizeof(leap_table_t));
1052b15cb3dSCy Schubert p1 = p2;
1062b15cb3dSCy Schubert }
1072b15cb3dSCy Schubert
1082b15cb3dSCy Schubert return p1;
1092b15cb3dSCy Schubert }
1102b15cb3dSCy Schubert
1112b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
1122b15cb3dSCy Schubert int/*BOOL*/
leapsec_set_table(leap_table_t * pt)1132b15cb3dSCy Schubert leapsec_set_table(
1142b15cb3dSCy Schubert leap_table_t * pt)
1152b15cb3dSCy Schubert {
1162b15cb3dSCy Schubert if (pt == &_ltab[0] || pt == &_ltab[1])
1172b15cb3dSCy Schubert _lptr = pt;
1182b15cb3dSCy Schubert return _lptr == pt;
1192b15cb3dSCy Schubert }
1202b15cb3dSCy Schubert
1212b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
1222b15cb3dSCy Schubert int/*BOOL*/
leapsec_electric(int on)1232b15cb3dSCy Schubert leapsec_electric(
1242b15cb3dSCy Schubert int/*BOOL*/ on)
1252b15cb3dSCy Schubert {
1262b15cb3dSCy Schubert int res = _electric;
1272b15cb3dSCy Schubert if (on < 0)
1282b15cb3dSCy Schubert return res;
1292b15cb3dSCy Schubert
1302b15cb3dSCy Schubert _electric = (on != 0);
1312b15cb3dSCy Schubert if (_electric == res)
1322b15cb3dSCy Schubert return res;
1332b15cb3dSCy Schubert
1342b15cb3dSCy Schubert if (_lptr == &_ltab[0] || _lptr == &_ltab[1])
1352b15cb3dSCy Schubert reset_times(_lptr);
1362b15cb3dSCy Schubert
1372b15cb3dSCy Schubert return res;
1382b15cb3dSCy Schubert }
1392b15cb3dSCy Schubert
1402b15cb3dSCy Schubert /* =====================================================================
1412b15cb3dSCy Schubert * API functions that operate on tables
1422b15cb3dSCy Schubert */
1432b15cb3dSCy Schubert
1442b15cb3dSCy Schubert /* ---------------------------------------------------------------------
1452b15cb3dSCy Schubert * Clear all leap second data. Use it for init & cleanup
1462b15cb3dSCy Schubert */
1472b15cb3dSCy Schubert void
leapsec_clear(leap_table_t * pt)1482b15cb3dSCy Schubert leapsec_clear(
1492b15cb3dSCy Schubert leap_table_t * pt)
1502b15cb3dSCy Schubert {
1512b15cb3dSCy Schubert memset(&pt->lsig, 0, sizeof(pt->lsig));
1522b15cb3dSCy Schubert memset(&pt->head, 0, sizeof(pt->head));
1532b15cb3dSCy Schubert reset_times(pt);
1542b15cb3dSCy Schubert }
1552b15cb3dSCy Schubert
1562b15cb3dSCy Schubert /* ---------------------------------------------------------------------
1572b15cb3dSCy Schubert * Load a leap second file and check expiration on the go
1582b15cb3dSCy Schubert */
1592b15cb3dSCy Schubert int/*BOOL*/
leapsec_load(leap_table_t * pt,leapsec_reader func,void * farg,int use_build_limit)1602b15cb3dSCy Schubert leapsec_load(
1612b15cb3dSCy Schubert leap_table_t * pt,
1622b15cb3dSCy Schubert leapsec_reader func,
1632b15cb3dSCy Schubert void * farg,
164*f5f40dd6SCy Schubert int use_build_limit
165*f5f40dd6SCy Schubert )
1662b15cb3dSCy Schubert {
167*f5f40dd6SCy Schubert char *cp, *ep, *endp, linebuf[50];
1682b15cb3dSCy Schubert vint64 ttime, limit;
1692b15cb3dSCy Schubert long taiof;
1702b15cb3dSCy Schubert struct calendar build;
1712b15cb3dSCy Schubert
1722b15cb3dSCy Schubert leapsec_clear(pt);
173276da39aSCy Schubert if (use_build_limit && ntpcal_get_build_date(&build)) {
174276da39aSCy Schubert /* don't prune everything -- permit the last 10yrs
175276da39aSCy Schubert * before build.
176276da39aSCy Schubert */
177276da39aSCy Schubert build.year -= 10;
1782b15cb3dSCy Schubert limit = ntpcal_date_to_ntp64(&build);
179276da39aSCy Schubert } else {
1802b15cb3dSCy Schubert memset(&limit, 0, sizeof(limit));
181276da39aSCy Schubert }
1822b15cb3dSCy Schubert
1832b15cb3dSCy Schubert while (get_line(func, farg, linebuf, sizeof(linebuf))) {
1842b15cb3dSCy Schubert cp = linebuf;
1852b15cb3dSCy Schubert if (*cp == '#') {
1862b15cb3dSCy Schubert cp++;
1872b15cb3dSCy Schubert if (*cp == '@') {
1882b15cb3dSCy Schubert cp = skipws(cp+1);
1892b15cb3dSCy Schubert pt->head.expire = strtouv64(cp, &ep, 10);
1902b15cb3dSCy Schubert if (parsefail(cp, ep))
1912b15cb3dSCy Schubert goto fail_read;
1922b15cb3dSCy Schubert pt->lsig.etime = pt->head.expire.D_s.lo;
1932b15cb3dSCy Schubert } else if (*cp == '$') {
1942b15cb3dSCy Schubert cp = skipws(cp+1);
1952b15cb3dSCy Schubert pt->head.update = strtouv64(cp, &ep, 10);
1962b15cb3dSCy Schubert if (parsefail(cp, ep))
1972b15cb3dSCy Schubert goto fail_read;
1982b15cb3dSCy Schubert }
1992b15cb3dSCy Schubert } else if (isdigit((u_char)*cp)) {
2002b15cb3dSCy Schubert ttime = strtouv64(cp, &ep, 10);
2012b15cb3dSCy Schubert if (parsefail(cp, ep))
2022b15cb3dSCy Schubert goto fail_read;
2032b15cb3dSCy Schubert cp = skipws(ep);
204a466cc55SCy Schubert taiof = strtol(cp, &endp, 10);
205a466cc55SCy Schubert if ( parsefail(cp, endp)
206a466cc55SCy Schubert || taiof > INT16_MAX || taiof < INT16_MIN)
2072b15cb3dSCy Schubert goto fail_read;
2082b15cb3dSCy Schubert if (ucmpv64(&ttime, &limit) >= 0) {
2092b15cb3dSCy Schubert if (!leapsec_raw(pt, &ttime,
2102b15cb3dSCy Schubert taiof, FALSE))
2112b15cb3dSCy Schubert goto fail_insn;
2122b15cb3dSCy Schubert } else {
2132b15cb3dSCy Schubert pt->head.base_tai = (int16_t)taiof;
2142b15cb3dSCy Schubert }
2152b15cb3dSCy Schubert pt->lsig.ttime = ttime.D_s.lo;
2162b15cb3dSCy Schubert pt->lsig.taiof = (int16_t)taiof;
2172b15cb3dSCy Schubert }
2182b15cb3dSCy Schubert }
2192b15cb3dSCy Schubert return TRUE;
2202b15cb3dSCy Schubert
2212b15cb3dSCy Schubert fail_read:
2222b15cb3dSCy Schubert errno = EILSEQ;
2232b15cb3dSCy Schubert fail_insn:
2242b15cb3dSCy Schubert leapsec_clear(pt);
2252b15cb3dSCy Schubert return FALSE;
2262b15cb3dSCy Schubert }
2272b15cb3dSCy Schubert
2282b15cb3dSCy Schubert /* ---------------------------------------------------------------------
2292b15cb3dSCy Schubert * Dump a table in human-readable format. Use 'fprintf' and a FILE
2302b15cb3dSCy Schubert * pointer if you want to get it printed into a stream.
2312b15cb3dSCy Schubert */
2322b15cb3dSCy Schubert void
leapsec_dump(const leap_table_t * pt,leapsec_dumper func,void * farg)2332b15cb3dSCy Schubert leapsec_dump(
2342b15cb3dSCy Schubert const leap_table_t * pt ,
2352b15cb3dSCy Schubert leapsec_dumper func,
2362b15cb3dSCy Schubert void * farg)
2372b15cb3dSCy Schubert {
2382b15cb3dSCy Schubert int idx;
2392b15cb3dSCy Schubert vint64 ts;
2402b15cb3dSCy Schubert struct calendar atb, ttb;
2412b15cb3dSCy Schubert
2422b15cb3dSCy Schubert ntpcal_ntp64_to_date(&ttb, &pt->head.expire);
2432b15cb3dSCy Schubert (*func)(farg, "leap table (%u entries) expires at %04u-%02u-%02u:\n",
2442b15cb3dSCy Schubert pt->head.size,
2452b15cb3dSCy Schubert ttb.year, ttb.month, ttb.monthday);
2462b15cb3dSCy Schubert idx = pt->head.size;
2472b15cb3dSCy Schubert while (idx-- != 0) {
2482b15cb3dSCy Schubert ts = pt->info[idx].ttime;
2492b15cb3dSCy Schubert ntpcal_ntp64_to_date(&ttb, &ts);
2502b15cb3dSCy Schubert ts = subv64u32(&ts, pt->info[idx].stime);
2512b15cb3dSCy Schubert ntpcal_ntp64_to_date(&atb, &ts);
2522b15cb3dSCy Schubert
2532b15cb3dSCy Schubert (*func)(farg, "%04u-%02u-%02u [%c] (%04u-%02u-%02u) - %d\n",
2542b15cb3dSCy Schubert ttb.year, ttb.month, ttb.monthday,
2552b15cb3dSCy Schubert "-*"[pt->info[idx].dynls != 0],
2562b15cb3dSCy Schubert atb.year, atb.month, atb.monthday,
2572b15cb3dSCy Schubert pt->info[idx].taiof);
2582b15cb3dSCy Schubert }
2592b15cb3dSCy Schubert }
2602b15cb3dSCy Schubert
2612b15cb3dSCy Schubert /* =====================================================================
2622b15cb3dSCy Schubert * usecase driven API functions
2632b15cb3dSCy Schubert */
2642b15cb3dSCy Schubert
2652b15cb3dSCy Schubert int/*BOOL*/
leapsec_query(leap_result_t * qr,uint32_t ts32,const time_t * pivot)2662b15cb3dSCy Schubert leapsec_query(
2672b15cb3dSCy Schubert leap_result_t * qr ,
2682b15cb3dSCy Schubert uint32_t ts32 ,
2692b15cb3dSCy Schubert const time_t * pivot)
2702b15cb3dSCy Schubert {
2712b15cb3dSCy Schubert leap_table_t * pt;
2722b15cb3dSCy Schubert vint64 ts64, last, next;
2732b15cb3dSCy Schubert uint32_t due32;
2742b15cb3dSCy Schubert int fired;
2752b15cb3dSCy Schubert
2762b15cb3dSCy Schubert /* preset things we use later on... */
2772b15cb3dSCy Schubert fired = FALSE;
2782b15cb3dSCy Schubert ts64 = ntpcal_ntp_to_ntp(ts32, pivot);
2792b15cb3dSCy Schubert pt = leapsec_get_table(FALSE);
2802b15cb3dSCy Schubert memset(qr, 0, sizeof(leap_result_t));
2812b15cb3dSCy Schubert
2822b15cb3dSCy Schubert if (ucmpv64(&ts64, &pt->head.ebase) < 0) {
2832b15cb3dSCy Schubert /* Most likely after leap frame reset. Could also be a
2842b15cb3dSCy Schubert * backstep of the system clock. Anyway, get the new
2852b15cb3dSCy Schubert * leap era frame.
2862b15cb3dSCy Schubert */
2872b15cb3dSCy Schubert reload_limits(pt, &ts64);
2882b15cb3dSCy Schubert } else if (ucmpv64(&ts64, &pt->head.dtime) >= 0) {
2892b15cb3dSCy Schubert /* Boundary crossed in forward direction. This might
2902b15cb3dSCy Schubert * indicate a leap transition, so we prepare for that
2912b15cb3dSCy Schubert * case.
2922b15cb3dSCy Schubert *
2932b15cb3dSCy Schubert * Some operations below are actually NOPs in electric
2942b15cb3dSCy Schubert * mode, but having only one code path that works for
2952b15cb3dSCy Schubert * both modes is easier to maintain.
296276da39aSCy Schubert *
297276da39aSCy Schubert * There's another quirk we must keep looking out for:
298276da39aSCy Schubert * If we just stepped the clock, the step might have
299276da39aSCy Schubert * crossed a leap boundary. As with backward steps, we
300276da39aSCy Schubert * do not want to raise the 'fired' event in that case.
301276da39aSCy Schubert * So we raise the 'fired' event only if we're close to
302276da39aSCy Schubert * the transition and just reload the limits otherwise.
3032b15cb3dSCy Schubert */
304276da39aSCy Schubert last = addv64i32(&pt->head.dtime, 3); /* get boundary */
305276da39aSCy Schubert if (ucmpv64(&ts64, &last) >= 0) {
306276da39aSCy Schubert /* that was likely a query after a step */
307276da39aSCy Schubert reload_limits(pt, &ts64);
308276da39aSCy Schubert } else {
309276da39aSCy Schubert /* close enough for deeper examination */
3102b15cb3dSCy Schubert last = pt->head.ttime;
3112b15cb3dSCy Schubert qr->warped = (int16_t)(last.D_s.lo -
3122b15cb3dSCy Schubert pt->head.dtime.D_s.lo);
3132b15cb3dSCy Schubert next = addv64i32(&ts64, qr->warped);
3142b15cb3dSCy Schubert reload_limits(pt, &next);
3152b15cb3dSCy Schubert fired = ucmpv64(&pt->head.ebase, &last) == 0;
3162b15cb3dSCy Schubert if (fired) {
3172b15cb3dSCy Schubert ts64 = next;
3182b15cb3dSCy Schubert ts32 = next.D_s.lo;
3192b15cb3dSCy Schubert } else {
3202b15cb3dSCy Schubert qr->warped = 0;
3212b15cb3dSCy Schubert }
3222b15cb3dSCy Schubert }
323276da39aSCy Schubert }
3242b15cb3dSCy Schubert
3252b15cb3dSCy Schubert qr->tai_offs = pt->head.this_tai;
326276da39aSCy Schubert qr->ebase = pt->head.ebase;
327276da39aSCy Schubert qr->ttime = pt->head.ttime;
3282b15cb3dSCy Schubert
3292b15cb3dSCy Schubert /* If before the next scheduling alert, we're done. */
3302b15cb3dSCy Schubert if (ucmpv64(&ts64, &pt->head.stime) < 0)
3312b15cb3dSCy Schubert return fired;
3322b15cb3dSCy Schubert
333276da39aSCy Schubert /* now start to collect the remaining data */
3342b15cb3dSCy Schubert due32 = pt->head.dtime.D_s.lo;
3352b15cb3dSCy Schubert
3362b15cb3dSCy Schubert qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
3372b15cb3dSCy Schubert qr->ddist = due32 - ts32;
3382b15cb3dSCy Schubert qr->dynamic = pt->head.dynls;
3392b15cb3dSCy Schubert qr->proximity = LSPROX_SCHEDULE;
3402b15cb3dSCy Schubert
3412b15cb3dSCy Schubert /* if not in the last day before transition, we're done. */
3422b15cb3dSCy Schubert if (!betweenu32(due32 - SECSPERDAY, ts32, due32))
3432b15cb3dSCy Schubert return fired;
3442b15cb3dSCy Schubert
3452b15cb3dSCy Schubert qr->proximity = LSPROX_ANNOUNCE;
3462b15cb3dSCy Schubert if (!betweenu32(due32 - 10, ts32, due32))
3472b15cb3dSCy Schubert return fired;
3482b15cb3dSCy Schubert
3492b15cb3dSCy Schubert /* The last 10s before the transition. Prepare for action! */
3502b15cb3dSCy Schubert qr->proximity = LSPROX_ALERT;
3512b15cb3dSCy Schubert return fired;
3522b15cb3dSCy Schubert }
3532b15cb3dSCy Schubert
3542b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
3552b15cb3dSCy Schubert int/*BOOL*/
leapsec_query_era(leap_era_t * qr,uint32_t ntpts,const time_t * pivot)356276da39aSCy Schubert leapsec_query_era(
357276da39aSCy Schubert leap_era_t * qr ,
358276da39aSCy Schubert uint32_t ntpts,
359276da39aSCy Schubert const time_t * pivot)
360276da39aSCy Schubert {
361276da39aSCy Schubert const leap_table_t * pt;
362276da39aSCy Schubert vint64 ts64;
363276da39aSCy Schubert
364276da39aSCy Schubert pt = leapsec_get_table(FALSE);
365276da39aSCy Schubert ts64 = ntpcal_ntp_to_ntp(ntpts, pivot);
366276da39aSCy Schubert fetch_leap_era(qr, pt, &ts64);
367276da39aSCy Schubert return TRUE;
368276da39aSCy Schubert }
369276da39aSCy Schubert
370276da39aSCy Schubert /* ------------------------------------------------------------------ */
371276da39aSCy Schubert int/*BOOL*/
leapsec_frame(leap_result_t * qr)3722b15cb3dSCy Schubert leapsec_frame(
3732b15cb3dSCy Schubert leap_result_t *qr)
3742b15cb3dSCy Schubert {
3752b15cb3dSCy Schubert const leap_table_t * pt;
3762b15cb3dSCy Schubert
3772b15cb3dSCy Schubert memset(qr, 0, sizeof(leap_result_t));
3782b15cb3dSCy Schubert pt = leapsec_get_table(FALSE);
3792b15cb3dSCy Schubert
3802b15cb3dSCy Schubert qr->tai_offs = pt->head.this_tai;
3812b15cb3dSCy Schubert qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
382276da39aSCy Schubert qr->ebase = pt->head.ebase;
3832b15cb3dSCy Schubert qr->ttime = pt->head.ttime;
3842b15cb3dSCy Schubert qr->dynamic = pt->head.dynls;
3852b15cb3dSCy Schubert
386276da39aSCy Schubert return ucmpv64(&pt->head.ttime, &pt->head.stime) >= 0;
3872b15cb3dSCy Schubert }
3882b15cb3dSCy Schubert
3892b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
3902b15cb3dSCy Schubert /* Reset the current leap frame */
3912b15cb3dSCy Schubert void
leapsec_reset_frame(void)3922b15cb3dSCy Schubert leapsec_reset_frame(void)
3932b15cb3dSCy Schubert {
3942b15cb3dSCy Schubert reset_times(leapsec_get_table(FALSE));
3952b15cb3dSCy Schubert }
3962b15cb3dSCy Schubert
3972b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
398*f5f40dd6SCy Schubert /* load a file from a FILE pointer. Note: If vhash is true, load
3992b15cb3dSCy Schubert * only after successful signature check. The stream must be seekable
4002b15cb3dSCy Schubert * or this will fail.
4012b15cb3dSCy Schubert */
4022b15cb3dSCy Schubert int/*BOOL*/
leapsec_load_stream(FILE * ifp,const char * fname,int logall,int vhash)4032b15cb3dSCy Schubert leapsec_load_stream(
4042b15cb3dSCy Schubert FILE * ifp ,
4052b15cb3dSCy Schubert const char * fname,
4062d4e511cSCy Schubert int/*BOOL*/ logall,
4072d4e511cSCy Schubert int/*BOOL*/ vhash)
4082b15cb3dSCy Schubert {
4092b15cb3dSCy Schubert leap_table_t *pt;
4102b15cb3dSCy Schubert int rcheck;
4112b15cb3dSCy Schubert
4122b15cb3dSCy Schubert if (NULL == fname)
4132b15cb3dSCy Schubert fname = "<unknown>";
4142b15cb3dSCy Schubert
4152d4e511cSCy Schubert if (vhash) {
416*f5f40dd6SCy Schubert rcheck = leapsec_validate((leapsec_reader)&getc, ifp);
4172b15cb3dSCy Schubert if (logall)
4182b15cb3dSCy Schubert switch (rcheck)
4192b15cb3dSCy Schubert {
4202b15cb3dSCy Schubert case LSVALID_GOODHASH:
4212b15cb3dSCy Schubert msyslog(LOG_NOTICE, "%s ('%s'): good hash signature",
4222b15cb3dSCy Schubert logPrefix, fname);
4232b15cb3dSCy Schubert break;
4242b15cb3dSCy Schubert
4252b15cb3dSCy Schubert case LSVALID_NOHASH:
4262b15cb3dSCy Schubert msyslog(LOG_ERR, "%s ('%s'): no hash signature",
4272b15cb3dSCy Schubert logPrefix, fname);
4282b15cb3dSCy Schubert break;
4292b15cb3dSCy Schubert case LSVALID_BADHASH:
4302b15cb3dSCy Schubert msyslog(LOG_ERR, "%s ('%s'): signature mismatch",
4312b15cb3dSCy Schubert logPrefix, fname);
4322b15cb3dSCy Schubert break;
4332b15cb3dSCy Schubert case LSVALID_BADFORMAT:
4342b15cb3dSCy Schubert msyslog(LOG_ERR, "%s ('%s'): malformed hash signature",
4352b15cb3dSCy Schubert logPrefix, fname);
4362b15cb3dSCy Schubert break;
4372b15cb3dSCy Schubert default:
4382b15cb3dSCy Schubert msyslog(LOG_ERR, "%s ('%s'): unknown error code %d",
4392b15cb3dSCy Schubert logPrefix, fname, rcheck);
4402b15cb3dSCy Schubert break;
4412b15cb3dSCy Schubert }
4422b15cb3dSCy Schubert if (rcheck < 0)
4432b15cb3dSCy Schubert return FALSE;
4442b15cb3dSCy Schubert rewind(ifp);
4452d4e511cSCy Schubert }
4462b15cb3dSCy Schubert pt = leapsec_get_table(TRUE);
4472b15cb3dSCy Schubert if (!leapsec_load(pt, (leapsec_reader)getc, ifp, TRUE)) {
4482b15cb3dSCy Schubert switch (errno) {
4492b15cb3dSCy Schubert case EINVAL:
4502b15cb3dSCy Schubert msyslog(LOG_ERR, "%s ('%s'): bad transition time",
4512b15cb3dSCy Schubert logPrefix, fname);
4522b15cb3dSCy Schubert break;
4532b15cb3dSCy Schubert case ERANGE:
4542b15cb3dSCy Schubert msyslog(LOG_ERR, "%s ('%s'): times not ascending",
4552b15cb3dSCy Schubert logPrefix, fname);
4562b15cb3dSCy Schubert break;
4572b15cb3dSCy Schubert default:
4582b15cb3dSCy Schubert msyslog(LOG_ERR, "%s ('%s'): parsing error",
4592b15cb3dSCy Schubert logPrefix, fname);
4602b15cb3dSCy Schubert break;
4612b15cb3dSCy Schubert }
4622b15cb3dSCy Schubert return FALSE;
4632b15cb3dSCy Schubert }
4642b15cb3dSCy Schubert
4652b15cb3dSCy Schubert if (pt->head.size)
4662b15cb3dSCy Schubert msyslog(LOG_NOTICE, "%s ('%s'): loaded, expire=%s last=%s ofs=%d",
4672b15cb3dSCy Schubert logPrefix, fname, lstostr(&pt->head.expire),
4682b15cb3dSCy Schubert lstostr(&pt->info[0].ttime), pt->info[0].taiof);
4692b15cb3dSCy Schubert else
4702b15cb3dSCy Schubert msyslog(LOG_NOTICE,
4712b15cb3dSCy Schubert "%s ('%s'): loaded, expire=%s ofs=%d (no entries after build date)",
4722b15cb3dSCy Schubert logPrefix, fname, lstostr(&pt->head.expire),
4732b15cb3dSCy Schubert pt->head.base_tai);
4742b15cb3dSCy Schubert
4752b15cb3dSCy Schubert return leapsec_set_table(pt);
4762b15cb3dSCy Schubert }
4772b15cb3dSCy Schubert
4782b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
4792b15cb3dSCy Schubert int/*BOOL*/
leapsec_load_file(const char * fname,struct stat * sb_old,int force,int logall,int vhash)4802b15cb3dSCy Schubert leapsec_load_file(
4812b15cb3dSCy Schubert const char * fname,
4822b15cb3dSCy Schubert struct stat * sb_old,
4832b15cb3dSCy Schubert int/*BOOL*/ force,
4842d4e511cSCy Schubert int/*BOOL*/ logall,
4852d4e511cSCy Schubert int/*BOOL*/ vhash)
4862b15cb3dSCy Schubert {
4872b15cb3dSCy Schubert FILE * fp;
4882b15cb3dSCy Schubert struct stat sb_new;
4892b15cb3dSCy Schubert int rc;
4902b15cb3dSCy Schubert
4912b15cb3dSCy Schubert /* just do nothing if there is no leap file */
4922b15cb3dSCy Schubert if ( !(fname && *fname) )
4932b15cb3dSCy Schubert return FALSE;
4942b15cb3dSCy Schubert
4952b15cb3dSCy Schubert /* try to stat the leapfile */
4962b15cb3dSCy Schubert if (0 != stat(fname, &sb_new)) {
4972b15cb3dSCy Schubert if (logall)
4982b15cb3dSCy Schubert msyslog(LOG_ERR, "%s ('%s'): stat failed: %m",
4992b15cb3dSCy Schubert logPrefix, fname);
5002b15cb3dSCy Schubert return FALSE;
5012b15cb3dSCy Schubert }
5022b15cb3dSCy Schubert
5032b15cb3dSCy Schubert /* silently skip to postcheck if no new file found */
5042b15cb3dSCy Schubert if (NULL != sb_old) {
5052b15cb3dSCy Schubert if (!force
5062b15cb3dSCy Schubert && sb_old->st_mtime == sb_new.st_mtime
5072b15cb3dSCy Schubert && sb_old->st_ctime == sb_new.st_ctime
5082b15cb3dSCy Schubert )
5092b15cb3dSCy Schubert return FALSE;
5102b15cb3dSCy Schubert *sb_old = sb_new;
5112b15cb3dSCy Schubert }
5122b15cb3dSCy Schubert
5132b15cb3dSCy Schubert /* try to open the leap file, complain if that fails
5142b15cb3dSCy Schubert *
5152b15cb3dSCy Schubert * [perlinger@ntp.org]
5162b15cb3dSCy Schubert * coverity raises a TOCTOU (time-of-check/time-of-use) issue
5172b15cb3dSCy Schubert * here, which is not entirely helpful: While there is indeed a
5182b15cb3dSCy Schubert * possible race condition between the 'stat()' call above and
5192b15cb3dSCy Schubert * the 'fopen)' call below, I intentionally want to omit the
5202b15cb3dSCy Schubert * overhead of opening the file and calling 'fstat()', because
5212b15cb3dSCy Schubert * in most cases the file would have be to closed anyway without
5222b15cb3dSCy Schubert * reading the contents. I chose to disable the coverity
5232b15cb3dSCy Schubert * warning instead.
5242b15cb3dSCy Schubert *
5252b15cb3dSCy Schubert * So unless someone comes up with a reasonable argument why
5262b15cb3dSCy Schubert * this could be a real issue, I'll just try to silence coverity
5272b15cb3dSCy Schubert * on that topic.
5282b15cb3dSCy Schubert */
5292b15cb3dSCy Schubert /* coverity[toctou] */
5302b15cb3dSCy Schubert if ((fp = fopen(fname, "r")) == NULL) {
5312b15cb3dSCy Schubert if (logall)
5322b15cb3dSCy Schubert msyslog(LOG_ERR,
5332b15cb3dSCy Schubert "%s ('%s'): open failed: %m",
5342b15cb3dSCy Schubert logPrefix, fname);
5352b15cb3dSCy Schubert return FALSE;
5362b15cb3dSCy Schubert }
5372b15cb3dSCy Schubert
5382d4e511cSCy Schubert rc = leapsec_load_stream(fp, fname, logall, vhash);
5392b15cb3dSCy Schubert fclose(fp);
5402b15cb3dSCy Schubert return rc;
5412b15cb3dSCy Schubert }
5422b15cb3dSCy Schubert
5432b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
5442b15cb3dSCy Schubert void
leapsec_getsig(leap_signature_t * psig)5452b15cb3dSCy Schubert leapsec_getsig(
5462b15cb3dSCy Schubert leap_signature_t * psig)
5472b15cb3dSCy Schubert {
5482b15cb3dSCy Schubert const leap_table_t * pt;
5492b15cb3dSCy Schubert
5502b15cb3dSCy Schubert pt = leapsec_get_table(FALSE);
5512b15cb3dSCy Schubert memcpy(psig, &pt->lsig, sizeof(leap_signature_t));
5522b15cb3dSCy Schubert }
5532b15cb3dSCy Schubert
5542b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
5552b15cb3dSCy Schubert int/*BOOL*/
leapsec_expired(uint32_t when,const time_t * tpiv)5562b15cb3dSCy Schubert leapsec_expired(
5572b15cb3dSCy Schubert uint32_t when,
5582b15cb3dSCy Schubert const time_t * tpiv)
5592b15cb3dSCy Schubert {
5602b15cb3dSCy Schubert const leap_table_t * pt;
5612b15cb3dSCy Schubert vint64 limit;
5622b15cb3dSCy Schubert
5632b15cb3dSCy Schubert pt = leapsec_get_table(FALSE);
5642b15cb3dSCy Schubert limit = ntpcal_ntp_to_ntp(when, tpiv);
5652b15cb3dSCy Schubert return ucmpv64(&limit, &pt->head.expire) >= 0;
5662b15cb3dSCy Schubert }
5672b15cb3dSCy Schubert
5682b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
5692b15cb3dSCy Schubert int32_t
leapsec_daystolive(uint32_t when,const time_t * tpiv)5702b15cb3dSCy Schubert leapsec_daystolive(
5712b15cb3dSCy Schubert uint32_t when,
5722b15cb3dSCy Schubert const time_t * tpiv)
5732b15cb3dSCy Schubert {
5742b15cb3dSCy Schubert const leap_table_t * pt;
5752b15cb3dSCy Schubert vint64 limit;
5762b15cb3dSCy Schubert
5772b15cb3dSCy Schubert pt = leapsec_get_table(FALSE);
5782b15cb3dSCy Schubert limit = ntpcal_ntp_to_ntp(when, tpiv);
5792b15cb3dSCy Schubert limit = subv64(&pt->head.expire, &limit);
5802b15cb3dSCy Schubert return ntpcal_daysplit(&limit).hi;
5812b15cb3dSCy Schubert }
5822b15cb3dSCy Schubert
5832b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
584276da39aSCy Schubert #if 0 /* currently unused -- possibly revived later */
5852b15cb3dSCy Schubert int/*BOOL*/
5862b15cb3dSCy Schubert leapsec_add_fix(
5872b15cb3dSCy Schubert int total,
5882b15cb3dSCy Schubert uint32_t ttime,
5892b15cb3dSCy Schubert uint32_t etime,
5902b15cb3dSCy Schubert const time_t * pivot)
5912b15cb3dSCy Schubert {
5922b15cb3dSCy Schubert time_t tpiv;
5932b15cb3dSCy Schubert leap_table_t * pt;
5942b15cb3dSCy Schubert vint64 tt64, et64;
5952b15cb3dSCy Schubert
5962b15cb3dSCy Schubert if (pivot == NULL) {
5972b15cb3dSCy Schubert time(&tpiv);
5982b15cb3dSCy Schubert pivot = &tpiv;
5992b15cb3dSCy Schubert }
6002b15cb3dSCy Schubert
6012b15cb3dSCy Schubert et64 = ntpcal_ntp_to_ntp(etime, pivot);
6022b15cb3dSCy Schubert tt64 = ntpcal_ntp_to_ntp(ttime, pivot);
6032b15cb3dSCy Schubert pt = leapsec_get_table(TRUE);
6042b15cb3dSCy Schubert
6052b15cb3dSCy Schubert if ( ucmpv64(&et64, &pt->head.expire) <= 0
6062b15cb3dSCy Schubert || !leapsec_raw(pt, &tt64, total, FALSE) )
6072b15cb3dSCy Schubert return FALSE;
6082b15cb3dSCy Schubert
6092b15cb3dSCy Schubert pt->lsig.etime = etime;
6102b15cb3dSCy Schubert pt->lsig.ttime = ttime;
6112b15cb3dSCy Schubert pt->lsig.taiof = (int16_t)total;
6122b15cb3dSCy Schubert
6132b15cb3dSCy Schubert pt->head.expire = et64;
6142b15cb3dSCy Schubert
6152b15cb3dSCy Schubert return leapsec_set_table(pt);
6162b15cb3dSCy Schubert }
617276da39aSCy Schubert #endif
6182b15cb3dSCy Schubert
6192b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
6202b15cb3dSCy Schubert int/*BOOL*/
leapsec_add_dyn(int insert,uint32_t ntpnow,const time_t * pivot)6212b15cb3dSCy Schubert leapsec_add_dyn(
6222b15cb3dSCy Schubert int insert,
6232b15cb3dSCy Schubert uint32_t ntpnow,
6242b15cb3dSCy Schubert const time_t * pivot )
6252b15cb3dSCy Schubert {
6262b15cb3dSCy Schubert leap_table_t * pt;
6272b15cb3dSCy Schubert vint64 now64;
6282b15cb3dSCy Schubert
6292b15cb3dSCy Schubert pt = leapsec_get_table(TRUE);
6302b15cb3dSCy Schubert now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
6312b15cb3dSCy Schubert return ( leapsec_add(pt, &now64, (insert != 0))
6322b15cb3dSCy Schubert && leapsec_set_table(pt));
6332b15cb3dSCy Schubert }
6342b15cb3dSCy Schubert
635276da39aSCy Schubert /* ------------------------------------------------------------------ */
636276da39aSCy Schubert int/*BOOL*/
leapsec_autokey_tai(int tai_offset,uint32_t ntpnow,const time_t * pivot)637276da39aSCy Schubert leapsec_autokey_tai(
638276da39aSCy Schubert int tai_offset,
639276da39aSCy Schubert uint32_t ntpnow ,
640276da39aSCy Schubert const time_t * pivot )
641276da39aSCy Schubert {
642276da39aSCy Schubert leap_table_t * pt;
643276da39aSCy Schubert leap_era_t era;
644276da39aSCy Schubert vint64 now64;
645276da39aSCy Schubert int idx;
646276da39aSCy Schubert
647276da39aSCy Schubert (void)tai_offset;
648276da39aSCy Schubert pt = leapsec_get_table(FALSE);
649276da39aSCy Schubert
650276da39aSCy Schubert /* Bail out if the basic offset is not zero and the putative
651276da39aSCy Schubert * offset is bigger than 10s. That was in 1972 -- we don't want
652276da39aSCy Schubert * to go back that far!
653276da39aSCy Schubert */
654276da39aSCy Schubert if (pt->head.base_tai != 0 || tai_offset < 10)
655276da39aSCy Schubert return FALSE;
656276da39aSCy Schubert
657276da39aSCy Schubert /* If there's already data in the table, check if an update is
658276da39aSCy Schubert * possible. Update is impossible if there are static entries
659276da39aSCy Schubert * (since this indicates a valid leapsecond file) or if we're
660276da39aSCy Schubert * too close to a leapsecond transition: We do not know on what
661276da39aSCy Schubert * side the transition the sender might have been, so we use a
662276da39aSCy Schubert * dead zone around the transition.
663276da39aSCy Schubert */
664276da39aSCy Schubert
665276da39aSCy Schubert /* Check for static entries */
666276da39aSCy Schubert for (idx = 0; idx != pt->head.size; idx++)
667276da39aSCy Schubert if ( ! pt->info[idx].dynls)
668276da39aSCy Schubert return FALSE;
669276da39aSCy Schubert
670276da39aSCy Schubert /* get the fulll time stamp and leap era for it */
671276da39aSCy Schubert now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
672276da39aSCy Schubert fetch_leap_era(&era, pt, &now64);
673276da39aSCy Schubert
674276da39aSCy Schubert /* check the limits with 20s dead band */
675276da39aSCy Schubert era.ebase = addv64i32(&era.ebase, 20);
676276da39aSCy Schubert if (ucmpv64(&now64, &era.ebase) < 0)
677276da39aSCy Schubert return FALSE;
678276da39aSCy Schubert
679276da39aSCy Schubert era.ttime = addv64i32(&era.ttime, -20);
680276da39aSCy Schubert if (ucmpv64(&now64, &era.ttime) > 0)
681276da39aSCy Schubert return FALSE;
682276da39aSCy Schubert
683276da39aSCy Schubert /* Here we can proceed. Calculate the delta update. */
684276da39aSCy Schubert tai_offset -= era.taiof;
685276da39aSCy Schubert
686276da39aSCy Schubert /* Shift the header info offsets. */
687276da39aSCy Schubert pt->head.base_tai += tai_offset;
688276da39aSCy Schubert pt->head.this_tai += tai_offset;
689276da39aSCy Schubert pt->head.next_tai += tai_offset;
690276da39aSCy Schubert
691276da39aSCy Schubert /* Shift table entry offsets (if any) */
692276da39aSCy Schubert for (idx = 0; idx != pt->head.size; idx++)
693276da39aSCy Schubert pt->info[idx].taiof += tai_offset;
694276da39aSCy Schubert
695276da39aSCy Schubert /* claim success... */
696276da39aSCy Schubert return TRUE;
697276da39aSCy Schubert }
698276da39aSCy Schubert
699276da39aSCy Schubert
7002b15cb3dSCy Schubert /* =====================================================================
7012b15cb3dSCy Schubert * internal helpers
7022b15cb3dSCy Schubert */
7032b15cb3dSCy Schubert
7042b15cb3dSCy Schubert /* [internal] Reset / init the time window in the leap processor to
7052b15cb3dSCy Schubert * force reload on next query. Since a leap transition cannot take place
7062b15cb3dSCy Schubert * at an odd second, the value chosen avoids spurious leap transition
7072b15cb3dSCy Schubert * triggers. Making all three times equal forces a reload. Using the
7082b15cb3dSCy Schubert * maximum value for unsigned 64 bits makes finding the next leap frame
7092b15cb3dSCy Schubert * a bit easier.
7102b15cb3dSCy Schubert */
7112b15cb3dSCy Schubert static void
reset_times(leap_table_t * pt)7122b15cb3dSCy Schubert reset_times(
7132b15cb3dSCy Schubert leap_table_t * pt)
7142b15cb3dSCy Schubert {
7152b15cb3dSCy Schubert memset(&pt->head.ebase, 0xFF, sizeof(vint64));
7162b15cb3dSCy Schubert pt->head.stime = pt->head.ebase;
7172b15cb3dSCy Schubert pt->head.ttime = pt->head.ebase;
7182b15cb3dSCy Schubert pt->head.dtime = pt->head.ebase;
7192b15cb3dSCy Schubert }
7202b15cb3dSCy Schubert
7212b15cb3dSCy Schubert /* [internal] Add raw data to the table, removing old entries on the
7222b15cb3dSCy Schubert * fly. This cannot fail currently.
7232b15cb3dSCy Schubert */
7242b15cb3dSCy Schubert static int/*BOOL*/
add_range(leap_table_t * pt,const leap_info_t * pi)7252b15cb3dSCy Schubert add_range(
7262b15cb3dSCy Schubert leap_table_t * pt,
7272b15cb3dSCy Schubert const leap_info_t * pi)
7282b15cb3dSCy Schubert {
7292b15cb3dSCy Schubert /* If the table is full, make room by throwing out the oldest
73009100258SXin LI * entry. But remember the accumulated leap seconds!
73109100258SXin LI *
73209100258SXin LI * Setting the first entry is a bit tricky, too: Simply assuming
73309100258SXin LI * it is an insertion is wrong if the first entry is a dynamic
73409100258SXin LI * leap second removal. So we decide on the sign -- if the first
73509100258SXin LI * entry has a negative offset, we assume that it is a leap
73609100258SXin LI * second removal. In both cases the table base offset is set
73709100258SXin LI * accordingly to reflect the decision.
73809100258SXin LI *
73909100258SXin LI * In practice starting with a removal can only happen if the
74009100258SXin LI * first entry is a dynamic request without having a leap file
74109100258SXin LI * for the history proper.
7422b15cb3dSCy Schubert */
743276da39aSCy Schubert if (pt->head.size == 0) {
74409100258SXin LI if (pi->taiof >= 0)
745276da39aSCy Schubert pt->head.base_tai = pi->taiof - 1;
74609100258SXin LI else
74709100258SXin LI pt->head.base_tai = pi->taiof + 1;
748276da39aSCy Schubert } else if (pt->head.size >= MAX_HIST) {
7492b15cb3dSCy Schubert pt->head.size = MAX_HIST - 1;
7502b15cb3dSCy Schubert pt->head.base_tai = pt->info[pt->head.size].taiof;
7512b15cb3dSCy Schubert }
7522b15cb3dSCy Schubert
7532b15cb3dSCy Schubert /* make room in lower end and insert item */
7542b15cb3dSCy Schubert memmove(pt->info+1, pt->info, pt->head.size*sizeof(*pt->info));
7552b15cb3dSCy Schubert pt->info[0] = *pi;
7562b15cb3dSCy Schubert pt->head.size++;
7572b15cb3dSCy Schubert
7582b15cb3dSCy Schubert /* invalidate the cached limit data -- we might have news ;-)
7592b15cb3dSCy Schubert *
7602b15cb3dSCy Schubert * This blocks a spurious transition detection. OTOH, if you add
7612b15cb3dSCy Schubert * a value after the last query before a leap transition was
7622b15cb3dSCy Schubert * expected to occur, this transition trigger is lost. But we
7632b15cb3dSCy Schubert * can probably live with that.
7642b15cb3dSCy Schubert */
7652b15cb3dSCy Schubert reset_times(pt);
7662b15cb3dSCy Schubert return TRUE;
7672b15cb3dSCy Schubert }
7682b15cb3dSCy Schubert
7692b15cb3dSCy Schubert /* [internal] given a reader function, read characters into a buffer
7702b15cb3dSCy Schubert * until either EOL or EOF is reached. Makes sure that the buffer is
7712b15cb3dSCy Schubert * always NUL terminated, but silently truncates excessive data. The
7722b15cb3dSCy Schubert * EOL-marker ('\n') is *not* stored in the buffer.
7732b15cb3dSCy Schubert *
7742b15cb3dSCy Schubert * Returns the pointer to the buffer, unless EOF was reached when trying
7752b15cb3dSCy Schubert * to read the first character of a line.
7762b15cb3dSCy Schubert */
7772b15cb3dSCy Schubert static char *
get_line(leapsec_reader func,void * farg,char * buff,size_t size)7782b15cb3dSCy Schubert get_line(
7792b15cb3dSCy Schubert leapsec_reader func,
7802b15cb3dSCy Schubert void * farg,
7812b15cb3dSCy Schubert char * buff,
7822b15cb3dSCy Schubert size_t size)
7832b15cb3dSCy Schubert {
7842b15cb3dSCy Schubert int ch;
7852b15cb3dSCy Schubert char *ptr;
7862b15cb3dSCy Schubert
7872b15cb3dSCy Schubert /* if we cannot even store the delimiter, declare failure */
7882b15cb3dSCy Schubert if (buff == NULL || size == 0)
7892b15cb3dSCy Schubert return NULL;
7902b15cb3dSCy Schubert
7912b15cb3dSCy Schubert ptr = buff;
7922b15cb3dSCy Schubert while (EOF != (ch = (*func)(farg)) && '\n' != ch)
7932b15cb3dSCy Schubert if (size > 1) {
7942b15cb3dSCy Schubert size--;
7952b15cb3dSCy Schubert *ptr++ = (char)ch;
7962b15cb3dSCy Schubert }
7972b15cb3dSCy Schubert /* discard trailing whitespace */
7982b15cb3dSCy Schubert while (ptr != buff && isspace((u_char)ptr[-1]))
7992b15cb3dSCy Schubert ptr--;
8002b15cb3dSCy Schubert *ptr = '\0';
8012b15cb3dSCy Schubert return (ptr == buff && ch == EOF) ? NULL : buff;
8022b15cb3dSCy Schubert }
8032b15cb3dSCy Schubert
8042b15cb3dSCy Schubert /* [internal] skips whitespace characters from a character buffer. */
805*f5f40dd6SCy Schubert static inline char *
skipws(char * ptr)8062b15cb3dSCy Schubert skipws(
807*f5f40dd6SCy Schubert char * ptr
808*f5f40dd6SCy Schubert )
8092b15cb3dSCy Schubert {
810*f5f40dd6SCy Schubert while (isspace((u_char)*ptr)) {
8112b15cb3dSCy Schubert ptr++;
812*f5f40dd6SCy Schubert }
813*f5f40dd6SCy Schubert return ptr;
8142b15cb3dSCy Schubert }
8152b15cb3dSCy Schubert
816276da39aSCy Schubert /* [internal] check if a strtoXYZ ended at EOL or whitespace and
8172b15cb3dSCy Schubert * converted something at all. Return TRUE if something went wrong.
8182b15cb3dSCy Schubert */
8192b15cb3dSCy Schubert static int/*BOOL*/
parsefail(const char * cp,const char * ep)8202b15cb3dSCy Schubert parsefail(
8212b15cb3dSCy Schubert const char * cp,
8222b15cb3dSCy Schubert const char * ep)
8232b15cb3dSCy Schubert {
8242b15cb3dSCy Schubert return (cp == ep)
8252b15cb3dSCy Schubert || (*ep && *ep != '#' && !isspace((u_char)*ep));
8262b15cb3dSCy Schubert }
8272b15cb3dSCy Schubert
8282b15cb3dSCy Schubert /* [internal] reload the table limits around the given time stamp. This
8292b15cb3dSCy Schubert * is where the real work is done when it comes to table lookup and
8302b15cb3dSCy Schubert * evaluation. Some care has been taken to have correct code for dealing
8312b15cb3dSCy Schubert * with boundary conditions and empty tables.
8322b15cb3dSCy Schubert *
8332b15cb3dSCy Schubert * In electric mode, transition and trip time are the same. In dumb
8342b15cb3dSCy Schubert * mode, the difference of the TAI offsets must be taken into account
8352b15cb3dSCy Schubert * and trip time and transition time become different. The difference
8362b15cb3dSCy Schubert * becomes the warping distance when the trip time is reached.
8372b15cb3dSCy Schubert */
8382b15cb3dSCy Schubert static void
reload_limits(leap_table_t * pt,const vint64 * ts)8392b15cb3dSCy Schubert reload_limits(
8402b15cb3dSCy Schubert leap_table_t * pt,
8412b15cb3dSCy Schubert const vint64 * ts)
8422b15cb3dSCy Schubert {
8432b15cb3dSCy Schubert int idx;
8442b15cb3dSCy Schubert
8452b15cb3dSCy Schubert /* Get full time and search the true lower bound. Use a
8462b15cb3dSCy Schubert * simple loop here, since the number of entries does
8472b15cb3dSCy Schubert * not warrant a binary search. This also works for an empty
8482b15cb3dSCy Schubert * table, so there is no shortcut for that case.
8492b15cb3dSCy Schubert */
8502b15cb3dSCy Schubert for (idx = 0; idx != pt->head.size; idx++)
8512b15cb3dSCy Schubert if (ucmpv64(ts, &pt->info[idx].ttime) >= 0)
8522b15cb3dSCy Schubert break;
8532b15cb3dSCy Schubert
8542b15cb3dSCy Schubert /* get time limits with proper bound conditions. Note that the
8552b15cb3dSCy Schubert * bounds of the table will be observed even if the table is
8562b15cb3dSCy Schubert * empty -- no undefined condition must arise from this code.
8572b15cb3dSCy Schubert */
8582b15cb3dSCy Schubert if (idx >= pt->head.size) {
8592b15cb3dSCy Schubert memset(&pt->head.ebase, 0x00, sizeof(vint64));
8602b15cb3dSCy Schubert pt->head.this_tai = pt->head.base_tai;
8612b15cb3dSCy Schubert } else {
8622b15cb3dSCy Schubert pt->head.ebase = pt->info[idx].ttime;
8632b15cb3dSCy Schubert pt->head.this_tai = pt->info[idx].taiof;
8642b15cb3dSCy Schubert }
8652b15cb3dSCy Schubert if (--idx >= 0) {
8662b15cb3dSCy Schubert pt->head.next_tai = pt->info[idx].taiof;
8672b15cb3dSCy Schubert pt->head.dynls = pt->info[idx].dynls;
8682b15cb3dSCy Schubert pt->head.ttime = pt->info[idx].ttime;
8692b15cb3dSCy Schubert
8702b15cb3dSCy Schubert if (_electric)
8712b15cb3dSCy Schubert pt->head.dtime = pt->head.ttime;
8722b15cb3dSCy Schubert else
8732b15cb3dSCy Schubert pt->head.dtime = addv64i32(
8742b15cb3dSCy Schubert &pt->head.ttime,
8752b15cb3dSCy Schubert pt->head.next_tai - pt->head.this_tai);
8762b15cb3dSCy Schubert
8772b15cb3dSCy Schubert pt->head.stime = subv64u32(
8782b15cb3dSCy Schubert &pt->head.ttime, pt->info[idx].stime);
8792b15cb3dSCy Schubert
8802b15cb3dSCy Schubert } else {
8812b15cb3dSCy Schubert memset(&pt->head.ttime, 0xFF, sizeof(vint64));
8822b15cb3dSCy Schubert pt->head.stime = pt->head.ttime;
8832b15cb3dSCy Schubert pt->head.dtime = pt->head.ttime;
8842b15cb3dSCy Schubert pt->head.next_tai = pt->head.this_tai;
8852b15cb3dSCy Schubert pt->head.dynls = 0;
8862b15cb3dSCy Schubert }
8872b15cb3dSCy Schubert }
8882b15cb3dSCy Schubert
889276da39aSCy Schubert /* [internal] fetch the leap era for a given time stamp.
890276da39aSCy Schubert * This is a cut-down version the algorithm used to reload the table
891276da39aSCy Schubert * limits, but it does not update any global state and provides just the
892276da39aSCy Schubert * era information for a given time stamp.
893276da39aSCy Schubert */
894276da39aSCy Schubert static void
fetch_leap_era(leap_era_t * into,const leap_table_t * pt,const vint64 * ts)895276da39aSCy Schubert fetch_leap_era(
896276da39aSCy Schubert leap_era_t * into,
897276da39aSCy Schubert const leap_table_t * pt ,
898276da39aSCy Schubert const vint64 * ts )
899276da39aSCy Schubert {
900276da39aSCy Schubert int idx;
901276da39aSCy Schubert
902276da39aSCy Schubert /* Simple search loop, also works with empty table. */
903276da39aSCy Schubert for (idx = 0; idx != pt->head.size; idx++)
904276da39aSCy Schubert if (ucmpv64(ts, &pt->info[idx].ttime) >= 0)
905276da39aSCy Schubert break;
906276da39aSCy Schubert /* fetch era data, keeping an eye on boundary conditions */
907276da39aSCy Schubert if (idx >= pt->head.size) {
908276da39aSCy Schubert memset(&into->ebase, 0x00, sizeof(vint64));
909276da39aSCy Schubert into->taiof = pt->head.base_tai;
910276da39aSCy Schubert } else {
911276da39aSCy Schubert into->ebase = pt->info[idx].ttime;
912276da39aSCy Schubert into->taiof = pt->info[idx].taiof;
913276da39aSCy Schubert }
914276da39aSCy Schubert if (--idx >= 0)
915276da39aSCy Schubert into->ttime = pt->info[idx].ttime;
916276da39aSCy Schubert else
917276da39aSCy Schubert memset(&into->ttime, 0xFF, sizeof(vint64));
918276da39aSCy Schubert }
919276da39aSCy Schubert
9202b15cb3dSCy Schubert /* [internal] Take a time stamp and create a leap second frame for
9212b15cb3dSCy Schubert * it. This will schedule a leap second for the beginning of the next
9222b15cb3dSCy Schubert * month, midnight UTC. The 'insert' argument tells if a leap second is
9232b15cb3dSCy Schubert * added (!=0) or removed (==0). We do not handle multiple inserts
9242b15cb3dSCy Schubert * (yet?)
9252b15cb3dSCy Schubert *
9262b15cb3dSCy Schubert * Returns 1 if the insert worked, 0 otherwise. (It's not possible to
9272b15cb3dSCy Schubert * insert a leap second into the current history -- only appending
9282b15cb3dSCy Schubert * towards the future is allowed!)
9292b15cb3dSCy Schubert */
9302b15cb3dSCy Schubert static int/*BOOL*/
leapsec_add(leap_table_t * pt,const vint64 * now64,int insert)9312b15cb3dSCy Schubert leapsec_add(
9322b15cb3dSCy Schubert leap_table_t* pt ,
9332b15cb3dSCy Schubert const vint64 * now64 ,
9342b15cb3dSCy Schubert int insert)
9352b15cb3dSCy Schubert {
936a25439b6SCy Schubert vint64 ttime, starttime;
9372b15cb3dSCy Schubert struct calendar fts;
9382b15cb3dSCy Schubert leap_info_t li;
9392b15cb3dSCy Schubert
940276da39aSCy Schubert /* Check against the table expiration and the latest available
9412b15cb3dSCy Schubert * leap entry. Do not permit inserts, only appends, and only if
9422b15cb3dSCy Schubert * the extend the table beyond the expiration!
9432b15cb3dSCy Schubert */
9442b15cb3dSCy Schubert if ( ucmpv64(now64, &pt->head.expire) < 0
9452b15cb3dSCy Schubert || (pt->head.size && ucmpv64(now64, &pt->info[0].ttime) <= 0)) {
9462b15cb3dSCy Schubert errno = ERANGE;
9472b15cb3dSCy Schubert return FALSE;
9482b15cb3dSCy Schubert }
9492b15cb3dSCy Schubert
9502b15cb3dSCy Schubert ntpcal_ntp64_to_date(&fts, now64);
9512b15cb3dSCy Schubert /* To guard against dangling leap flags: do not accept leap
9522b15cb3dSCy Schubert * second request on the 1st hour of the 1st day of the month.
9532b15cb3dSCy Schubert */
9542b15cb3dSCy Schubert if (fts.monthday == 1 && fts.hour == 0) {
9552b15cb3dSCy Schubert errno = EINVAL;
9562b15cb3dSCy Schubert return FALSE;
9572b15cb3dSCy Schubert }
9582b15cb3dSCy Schubert
9592b15cb3dSCy Schubert /* Ok, do the remaining calculations */
9602b15cb3dSCy Schubert fts.monthday = 1;
9612b15cb3dSCy Schubert fts.hour = 0;
9622b15cb3dSCy Schubert fts.minute = 0;
9632b15cb3dSCy Schubert fts.second = 0;
964a25439b6SCy Schubert starttime = ntpcal_date_to_ntp64(&fts);
9652b15cb3dSCy Schubert fts.month++;
9662b15cb3dSCy Schubert ttime = ntpcal_date_to_ntp64(&fts);
9672b15cb3dSCy Schubert
9682b15cb3dSCy Schubert li.ttime = ttime;
969a25439b6SCy Schubert li.stime = ttime.D_s.lo - starttime.D_s.lo;
9702b15cb3dSCy Schubert li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai)
9712b15cb3dSCy Schubert + (insert ? 1 : -1);
9722b15cb3dSCy Schubert li.dynls = 1;
9732b15cb3dSCy Schubert return add_range(pt, &li);
9742b15cb3dSCy Schubert }
9752b15cb3dSCy Schubert
9762b15cb3dSCy Schubert /* [internal] Given a time stamp for a leap insertion (the exact begin
9772b15cb3dSCy Schubert * of the new leap era), create new leap frame and put it into the
9782b15cb3dSCy Schubert * table. This is the work horse for reading a leap file and getting a
9792b15cb3dSCy Schubert * leap second update via authenticated network packet.
9802b15cb3dSCy Schubert */
9812b15cb3dSCy Schubert int/*BOOL*/
leapsec_raw(leap_table_t * pt,const vint64 * ttime,int taiof,int dynls)9822b15cb3dSCy Schubert leapsec_raw(
9832b15cb3dSCy Schubert leap_table_t * pt,
9842b15cb3dSCy Schubert const vint64 * ttime,
9852b15cb3dSCy Schubert int taiof,
9862b15cb3dSCy Schubert int dynls)
9872b15cb3dSCy Schubert {
988a25439b6SCy Schubert vint64 starttime;
9892b15cb3dSCy Schubert struct calendar fts;
9902b15cb3dSCy Schubert leap_info_t li;
9912b15cb3dSCy Schubert
992276da39aSCy Schubert /* Check that we either extend the table or get a duplicate of
993276da39aSCy Schubert * the latest entry. The latter is a benevolent overwrite with
994276da39aSCy Schubert * identical data and could happen if we get an autokey message
995276da39aSCy Schubert * that extends the lifetime of the current leapsecond table.
996276da39aSCy Schubert * Otherwise paranoia rulez!
997276da39aSCy Schubert */
998276da39aSCy Schubert if (pt->head.size) {
999276da39aSCy Schubert int cmp = ucmpv64(ttime, &pt->info[0].ttime);
1000276da39aSCy Schubert if (cmp == 0)
1001276da39aSCy Schubert cmp -= (taiof != pt->info[0].taiof);
1002276da39aSCy Schubert if (cmp < 0) {
10032b15cb3dSCy Schubert errno = ERANGE;
10042b15cb3dSCy Schubert return FALSE;
10052b15cb3dSCy Schubert }
1006276da39aSCy Schubert if (cmp == 0)
1007276da39aSCy Schubert return TRUE;
1008276da39aSCy Schubert }
10092b15cb3dSCy Schubert
10102b15cb3dSCy Schubert ntpcal_ntp64_to_date(&fts, ttime);
10112b15cb3dSCy Schubert /* If this does not match the exact month start, bail out. */
10122b15cb3dSCy Schubert if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) {
10132b15cb3dSCy Schubert errno = EINVAL;
10142b15cb3dSCy Schubert return FALSE;
10152b15cb3dSCy Schubert }
10162b15cb3dSCy Schubert fts.month--; /* was in range 1..12, no overflow here! */
1017a25439b6SCy Schubert starttime = ntpcal_date_to_ntp64(&fts);
10182b15cb3dSCy Schubert li.ttime = *ttime;
1019a25439b6SCy Schubert li.stime = ttime->D_s.lo - starttime.D_s.lo;
10202b15cb3dSCy Schubert li.taiof = (int16_t)taiof;
10212b15cb3dSCy Schubert li.dynls = (dynls != 0);
10222b15cb3dSCy Schubert return add_range(pt, &li);
10232b15cb3dSCy Schubert }
10242b15cb3dSCy Schubert
10252b15cb3dSCy Schubert /* [internal] Do a wrap-around save range inclusion check.
10262b15cb3dSCy Schubert * Returns TRUE if x in [lo,hi[ (intervall open on right side) with full
10272b15cb3dSCy Schubert * handling of an overflow / wrap-around.
10282b15cb3dSCy Schubert */
10292b15cb3dSCy Schubert static int/*BOOL*/
betweenu32(uint32_t lo,uint32_t x,uint32_t hi)10302b15cb3dSCy Schubert betweenu32(
10312b15cb3dSCy Schubert uint32_t lo,
10322b15cb3dSCy Schubert uint32_t x,
10332b15cb3dSCy Schubert uint32_t hi)
10342b15cb3dSCy Schubert {
10352b15cb3dSCy Schubert int rc;
10362b15cb3dSCy Schubert
10372b15cb3dSCy Schubert if (lo <= hi)
10382b15cb3dSCy Schubert rc = (lo <= x) && (x < hi);
10392b15cb3dSCy Schubert else
10402b15cb3dSCy Schubert rc = (lo <= x) || (x < hi);
10412b15cb3dSCy Schubert return rc;
10422b15cb3dSCy Schubert }
10432b15cb3dSCy Schubert
10442b15cb3dSCy Schubert /* =====================================================================
10452b15cb3dSCy Schubert * validation stuff
10462b15cb3dSCy Schubert */
10472b15cb3dSCy Schubert
10482b15cb3dSCy Schubert typedef struct {
10492b15cb3dSCy Schubert unsigned char hv[ISC_SHA1_DIGESTLENGTH];
10502b15cb3dSCy Schubert } sha1_digest;
10512b15cb3dSCy Schubert
10522b15cb3dSCy Schubert /* [internal] parse a digest line to get the hash signature
10532b15cb3dSCy Schubert * The NIST code creating the hash writes them out as 5 hex integers
10542b15cb3dSCy Schubert * without leading zeros. This makes reading them back as hex-encoded
10552b15cb3dSCy Schubert * BLOB impossible, because there might be less than 40 hex digits.
10562b15cb3dSCy Schubert *
10572b15cb3dSCy Schubert * The solution is to read the values back as integers, and then do the
10582b15cb3dSCy Schubert * byte twiddle necessary to get it into an array of 20 chars. The
10592b15cb3dSCy Schubert * drawback is that it permits any acceptable number syntax provided by
10602b15cb3dSCy Schubert * 'scanf()' and 'strtoul()', including optional signs and '0x'
10612b15cb3dSCy Schubert * prefixes.
10622b15cb3dSCy Schubert */
10632b15cb3dSCy Schubert static int/*BOOL*/
do_leap_hash(sha1_digest * mac,char const * cp)10642b15cb3dSCy Schubert do_leap_hash(
10652b15cb3dSCy Schubert sha1_digest * mac,
10662b15cb3dSCy Schubert char const * cp )
10672b15cb3dSCy Schubert {
10682b15cb3dSCy Schubert int wi, di, num, len;
10692b15cb3dSCy Schubert unsigned long tmp[5];
10702b15cb3dSCy Schubert
10712b15cb3dSCy Schubert memset(mac, 0, sizeof(*mac));
10722b15cb3dSCy Schubert num = sscanf(cp, " %lx %lx %lx %lx %lx%n",
10732b15cb3dSCy Schubert &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4],
10742b15cb3dSCy Schubert &len);
10752b15cb3dSCy Schubert if (num != 5 || cp[len] > ' ')
10762b15cb3dSCy Schubert return FALSE;
10772b15cb3dSCy Schubert
10782b15cb3dSCy Schubert /* now do the byte twiddle */
10792b15cb3dSCy Schubert for (wi=0; wi < 5; ++wi)
10802b15cb3dSCy Schubert for (di=3; di >= 0; --di) {
10812b15cb3dSCy Schubert mac->hv[wi*4 + di] =
10822b15cb3dSCy Schubert (unsigned char)(tmp[wi] & 0x0FF);
10832b15cb3dSCy Schubert tmp[wi] >>= 8;
10842b15cb3dSCy Schubert }
10852b15cb3dSCy Schubert return TRUE;
10862b15cb3dSCy Schubert }
10872b15cb3dSCy Schubert
10882b15cb3dSCy Schubert /* [internal] add the digits of a data line to the hash, stopping at the
10892b15cb3dSCy Schubert * next hash ('#') character.
10902b15cb3dSCy Schubert */
10912b15cb3dSCy Schubert static void
do_hash_data(isc_sha1_t * mdctx,char const * cp)10922b15cb3dSCy Schubert do_hash_data(
10932b15cb3dSCy Schubert isc_sha1_t * mdctx,
10942b15cb3dSCy Schubert char const * cp )
10952b15cb3dSCy Schubert {
10962b15cb3dSCy Schubert unsigned char text[32]; // must be power of two!
10972b15cb3dSCy Schubert unsigned int tlen = 0;
10982b15cb3dSCy Schubert unsigned char ch;
10992b15cb3dSCy Schubert
11002b15cb3dSCy Schubert while ('\0' != (ch = *cp++) && '#' != ch)
11012b15cb3dSCy Schubert if (isdigit(ch)) {
11022b15cb3dSCy Schubert text[tlen++] = ch;
11032b15cb3dSCy Schubert tlen &= (sizeof(text)-1);
11042b15cb3dSCy Schubert if (0 == tlen)
11052b15cb3dSCy Schubert isc_sha1_update(
11062b15cb3dSCy Schubert mdctx, text, sizeof(text));
11072b15cb3dSCy Schubert }
11082b15cb3dSCy Schubert
11092b15cb3dSCy Schubert if (0 < tlen)
11102b15cb3dSCy Schubert isc_sha1_update(mdctx, text, tlen);
11112b15cb3dSCy Schubert }
11122b15cb3dSCy Schubert
11132b15cb3dSCy Schubert /* given a reader and a reader arg, calculate and validate the the hash
11142b15cb3dSCy Schubert * signature of a NIST leap second file.
11152b15cb3dSCy Schubert */
11162b15cb3dSCy Schubert int
leapsec_validate(leapsec_reader func,void * farg)11172b15cb3dSCy Schubert leapsec_validate(
11182b15cb3dSCy Schubert leapsec_reader func,
11192b15cb3dSCy Schubert void * farg)
11202b15cb3dSCy Schubert {
11212b15cb3dSCy Schubert isc_sha1_t mdctx;
11222b15cb3dSCy Schubert sha1_digest rdig, ldig; /* remote / local digests */
11232b15cb3dSCy Schubert char line[50];
11242b15cb3dSCy Schubert int hlseen = -1;
11252b15cb3dSCy Schubert
11262b15cb3dSCy Schubert isc_sha1_init(&mdctx);
11272b15cb3dSCy Schubert while (get_line(func, farg, line, sizeof(line))) {
11282b15cb3dSCy Schubert if (!strncmp(line, "#h", 2))
11292b15cb3dSCy Schubert hlseen = do_leap_hash(&rdig, line+2);
11302b15cb3dSCy Schubert else if (!strncmp(line, "#@", 2))
11312b15cb3dSCy Schubert do_hash_data(&mdctx, line+2);
11322b15cb3dSCy Schubert else if (!strncmp(line, "#$", 2))
11332b15cb3dSCy Schubert do_hash_data(&mdctx, line+2);
11342b15cb3dSCy Schubert else if (isdigit((unsigned char)line[0]))
11352b15cb3dSCy Schubert do_hash_data(&mdctx, line);
11362b15cb3dSCy Schubert }
11372b15cb3dSCy Schubert isc_sha1_final(&mdctx, ldig.hv);
11382b15cb3dSCy Schubert isc_sha1_invalidate(&mdctx);
11392b15cb3dSCy Schubert
11402b15cb3dSCy Schubert if (0 > hlseen)
11412b15cb3dSCy Schubert return LSVALID_NOHASH;
11422b15cb3dSCy Schubert if (0 == hlseen)
11432b15cb3dSCy Schubert return LSVALID_BADFORMAT;
11442b15cb3dSCy Schubert if (0 != memcmp(&rdig, &ldig, sizeof(sha1_digest)))
11452b15cb3dSCy Schubert return LSVALID_BADHASH;
11462b15cb3dSCy Schubert return LSVALID_GOODHASH;
11472b15cb3dSCy Schubert }
11482b15cb3dSCy Schubert
11492b15cb3dSCy Schubert /*
11502b15cb3dSCy Schubert * lstostr - prettyprint NTP seconds
11512b15cb3dSCy Schubert */
1152276da39aSCy Schubert static const char *
lstostr(const vint64 * ts)1153276da39aSCy Schubert lstostr(
11542b15cb3dSCy Schubert const vint64 * ts)
11552b15cb3dSCy Schubert {
11562b15cb3dSCy Schubert char * buf;
11572b15cb3dSCy Schubert struct calendar tm;
11582b15cb3dSCy Schubert
11592b15cb3dSCy Schubert LIB_GETBUF(buf);
1160276da39aSCy Schubert
1161276da39aSCy Schubert if ( ! (ts->d_s.hi >= 0 && ntpcal_ntp64_to_date(&tm, ts) >= 0))
1162276da39aSCy Schubert snprintf(buf, LIB_BUFLENGTH, "%s", "9999-12-31T23:59:59Z");
1163276da39aSCy Schubert else
1164276da39aSCy Schubert snprintf(buf, LIB_BUFLENGTH, "%04d-%02d-%02dT%02d:%02d:%02dZ",
11652b15cb3dSCy Schubert tm.year, tm.month, tm.monthday,
1166276da39aSCy Schubert tm.hour, tm.minute, tm.second);
1167276da39aSCy Schubert
11682b15cb3dSCy Schubert return buf;
11692b15cb3dSCy Schubert }
11702b15cb3dSCy Schubert
1171276da39aSCy Schubert /* reset the global state for unit tests */
1172276da39aSCy Schubert void
leapsec_ut_pristine(void)1173276da39aSCy Schubert leapsec_ut_pristine(void)
1174276da39aSCy Schubert {
1175276da39aSCy Schubert memset(_ltab, 0, sizeof(_ltab));
1176276da39aSCy Schubert _lptr = NULL;
1177276da39aSCy Schubert _electric = 0;
1178276da39aSCy Schubert }
1179276da39aSCy Schubert
11802b15cb3dSCy Schubert
11812b15cb3dSCy Schubert
11822b15cb3dSCy Schubert /* -*- that's all folks! -*- */
1183