/* [internal] Given a time stamp for a leap insertion (the exact begin * of the new leap era), create new leap frame and put it into the * table. This is the work horse for reading a leap file and getting a * leap second update via authenticated network packet. */ int/*BOOL*/ leapsec_raw( leap_table_t * pt, const vint64 * ttime, int taiof, int dynls) { vint64 stime; struct calendar fts; leap_info_t li; /* Check that we only extend the table. Paranoia rulez! */ if (pt->head.size && ucmpv64(ttime, &pt->info[0].ttime) <= 0) { errno = ERANGE; return FALSE; } ntpcal_ntp64_to_date(&fts, ttime); /* If this does not match the exact month start, bail out. */ if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) { errno = EINVAL; return FALSE; } fts.month--; /* was in range 1..12, no overflow here! */ stime = ntpcal_date_to_ntp64(&fts); li.ttime = *ttime; li.stime = ttime->D_s.lo - stime.D_s.lo; li.taiof = (int16_t)taiof; li.dynls = (dynls != 0); return add_range(pt, &li); }
/* [internal] Given a time stamp for a leap insertion (the exact begin * of the new leap era), create new leap frame and put it into the * table. This is the work horse for reading a leap file and getting a * leap second update via authenticated network packet. */ bool leapsec_raw( leap_table_t * pt, const time64_t ttime, int taiof, int dynls) { time64_t starttime; struct calendar fts; leap_info_t li; /* Check that we only extend the table. Paranoia rulez! */ if (pt->head.size && (ttime <= pt->info[0].ttime)) { errno = ERANGE; return false; } ntpcal_ntp64_to_date(&fts, ttime); /* If this does not match the exact month start, bail out. */ if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) { errno = EINVAL; return false; } fts.month--; /* was in range 1..12, no overflow here! */ starttime = ntpcal_date_to_ntp64(&fts); li.ttime = ttime; li.stime = time64lo(ttime) - time64lo(starttime); li.taiof = (int16_t)taiof; li.dynls = (dynls != 0); return add_range(pt, &li); }
/* [internal] Take a time stamp and create a leap second frame for * it. This will schedule a leap second for the beginning of the next * month, midnight UTC. The 'insert' argument tells if a leap second is * added (!=0) or removed (==0). We do not handle multiple inserts * (yet?) * * Returns 1 if the insert worked, 0 otherwise. (It's not possible to * insert a leap second into the current history -- only appending * towards the future is allowed!) */ static int/*BOOL*/ leapsec_add( leap_table_t* pt , const vint64 * now64 , int insert) { vint64 ttime, stime; struct calendar fts; leap_info_t li; /* Check against the table expiration and the lates available * leap entry. Do not permit inserts, only appends, and only if * the extend the table beyond the expiration! */ if ( ucmpv64(now64, &pt->head.expire) < 0 || (pt->head.size && ucmpv64(now64, &pt->info[0].ttime) <= 0)) { errno = ERANGE; return FALSE; } ntpcal_ntp64_to_date(&fts, now64); /* To guard against dangling leap flags: do not accept leap * second request on the 1st hour of the 1st day of the month. */ if (fts.monthday == 1 && fts.hour == 0) { errno = EINVAL; return FALSE; } /* Ok, do the remaining calculations */ fts.monthday = 1; fts.hour = 0; fts.minute = 0; fts.second = 0; stime = ntpcal_date_to_ntp64(&fts); fts.month++; ttime = ntpcal_date_to_ntp64(&fts); li.ttime = ttime; li.stime = ttime.D_s.lo - stime.D_s.lo; li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai) + (insert ? 1 : -1); li.dynls = 1; return add_range(pt, &li); }
/* [internal] Given a time stamp for a leap insertion (the exact begin * of the new leap era), create new leap frame and put it into the * table. This is the work horse for reading a leap file and getting a * leap second update via authenticated network packet. */ int/*BOOL*/ leapsec_raw( leap_table_t * pt, const vint64 * ttime, int taiof, int dynls) { vint64 starttime; struct calendar fts; leap_info_t li; /* Check that we either extend the table or get a duplicate of * the latest entry. The latter is a benevolent overwrite with * identical data and could happen if we get an autokey message * that extends the lifetime of the current leapsecond table. * Otherwise paranoia rulez! */ if (pt->head.size) { int cmp = ucmpv64(ttime, &pt->info[0].ttime); if (cmp == 0) cmp -= (taiof != pt->info[0].taiof); if (cmp < 0) { errno = ERANGE; return FALSE; } if (cmp == 0) return TRUE; } ntpcal_ntp64_to_date(&fts, ttime); /* If this does not match the exact month start, bail out. */ if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) { errno = EINVAL; return FALSE; } fts.month--; /* was in range 1..12, no overflow here! */ starttime = ntpcal_date_to_ntp64(&fts); li.ttime = *ttime; li.stime = ttime->D_s.lo - starttime.D_s.lo; li.taiof = (int16_t)taiof; li.dynls = (dynls != 0); return add_range(pt, &li); }
/* --------------------------------------------------------------------- * Load a leap second file and check expiration on the go */ int/*BOOL*/ leapsec_load( leap_table_t * pt , leapsec_reader func, void * farg, int use_build_limit) { char *cp, *ep, linebuf[50]; vint64 ttime, limit; long taiof; struct calendar build; leapsec_clear(pt); if (use_build_limit && ntpcal_get_build_date(&build)) limit = ntpcal_date_to_ntp64(&build); else memset(&limit, 0, sizeof(limit)); while (get_line(func, farg, linebuf, sizeof(linebuf))) { cp = linebuf; if (*cp == '#') { cp++; if (*cp == '@') { cp = skipws(cp+1); pt->head.expire = strtouv64(cp, &ep, 10); if (parsefail(cp, ep)) goto fail_read; pt->lsig.etime = pt->head.expire.D_s.lo; } else if (*cp == '$') { cp = skipws(cp+1); pt->head.update = strtouv64(cp, &ep, 10); if (parsefail(cp, ep)) goto fail_read; } } else if (isdigit((u_char)*cp)) { ttime = strtouv64(cp, &ep, 10); if (parsefail(cp, ep)) goto fail_read; cp = skipws(ep); taiof = strtol(cp, &ep, 10); if ( parsefail(cp, ep) || taiof > SHRT_MAX || taiof < SHRT_MIN) goto fail_read; if (ucmpv64(&ttime, &limit) >= 0) { if (!leapsec_raw(pt, &ttime, taiof, FALSE)) goto fail_insn; } else { pt->head.base_tai = (int16_t)taiof; } pt->lsig.ttime = ttime.D_s.lo; pt->lsig.taiof = (int16_t)taiof; } } return TRUE; fail_read: errno = EILSEQ; fail_insn: leapsec_clear(pt); return FALSE; }
/* --------------------------------------------------------------------- * Load a leap second file and check expiration on the go */ bool leapsec_load( leap_table_t * pt , leapsec_reader func, void * farg, int use_build_limit) { char *cp, *ep, linebuf[50]; time64_t ttime, limit; long taiof; struct calendar build; leapsec_clear(pt); if (use_build_limit && ntpcal_get_build_date(&build)) limit = ntpcal_date_to_ntp64(&build); else memset(&limit, 0, sizeof(limit)); while (get_line(func, farg, linebuf, sizeof(linebuf))) { cp = linebuf; if (*cp == '#') { cp++; if (*cp == '@') { cp = skipws(cp+1); settime64u(pt->head.expire, strtoull(cp, &ep, 10)); if (parsefail(cp, ep)) goto fail_read; pt->lsig.etime = time64lo(pt->head.expire); } else if (*cp == '$') { cp = skipws(cp+1); settime64u(pt->head.update, strtoull(cp, &ep, 10)); if (parsefail(cp, ep)) goto fail_read; } } else if (isdigit((uint8_t)*cp)) { settime64u(ttime, strtoull(cp, &ep, 10)); if (parsefail(cp, ep)) goto fail_read; cp = skipws(ep); taiof = strtol(cp, &ep, 10); if ( parsefail(cp, ep) || taiof > SHRT_MAX || taiof < SHRT_MIN) goto fail_read; if (ttime >= limit) { if (!leapsec_raw(pt, ttime, taiof, false)) goto fail_insn; } else { pt->head.base_tai = (int16_t)taiof; } pt->lsig.ttime = time64lo(ttime); pt->lsig.taiof = (int16_t)taiof; } } return true; fail_read: errno = EILSEQ; fail_insn: leapsec_clear(pt); return false; }