1275970Scy/* 2275970Scy * ntp_leapsec.c - leap second processing for NTPD 3275970Scy * 4275970Scy * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 5275970Scy * The contents of 'html/copyright.html' apply. 6275970Scy * ---------------------------------------------------------------------- 7275970Scy * This is an attempt to get the leap second handling into a dedicated 8275970Scy * module to make the somewhat convoluted logic testable. 9275970Scy */ 10275970Scy 11275970Scy#include <config.h> 12275970Scy#include <sys/types.h> 13275970Scy#include <sys/stat.h> 14275970Scy#include <ctype.h> 15275970Scy 16275970Scy#include "ntp_types.h" 17275970Scy#include "ntp_fp.h" 18275970Scy#include "ntp_stdlib.h" 19275970Scy#include "ntp_calendar.h" 20275970Scy#include "ntp_leapsec.h" 21275970Scy#include "ntp.h" 22275970Scy#include "vint64ops.h" 23275970Scy#include "lib_strbuf.h" 24275970Scy 25275970Scy#include "isc/sha1.h" 26275970Scy 27275970Scystatic const char * const logPrefix = "leapsecond file"; 28275970Scy 29275970Scy/* --------------------------------------------------------------------- 30275970Scy * GCC is rather sticky with its 'const' attribute. We have to do it more 31275970Scy * explicit than with a cast if we want to get rid of a CONST qualifier. 32275970Scy * Greetings from the PASCAL world, where casting was only possible via 33275970Scy * untagged unions... 34275970Scy */ 35275970Scystatic inline void* 36275970Scynoconst( 37275970Scy const void* ptr 38275970Scy ) 39275970Scy{ 40275970Scy union { 41275970Scy const void * cp; 42275970Scy void * vp; 43275970Scy } tmp; 44275970Scy tmp.cp = ptr; 45275970Scy return tmp.vp; 46275970Scy} 47275970Scy 48275970Scy/* --------------------------------------------------------------------- 49275970Scy * Our internal data structure 50275970Scy */ 51275970Scy#define MAX_HIST 10 /* history of leap seconds */ 52275970Scy 53275970Scystruct leap_info { 54275970Scy vint64 ttime; /* transition time (after the step, ntp scale) */ 55275970Scy uint32_t stime; /* schedule limit (a month before transition) */ 56275970Scy int16_t taiof; /* TAI offset on and after the transition */ 57275970Scy uint8_t dynls; /* dynamic: inserted on peer/clock request */ 58275970Scy}; 59275970Scytypedef struct leap_info leap_info_t; 60275970Scy 61275970Scystruct leap_head { 62275970Scy vint64 update; /* time of information update */ 63275970Scy vint64 expire; /* table expiration time */ 64275970Scy uint16_t size; /* number of infos in table */ 65275970Scy int16_t base_tai; /* total leaps before first entry */ 66275970Scy int16_t this_tai; /* current TAI offset */ 67275970Scy int16_t next_tai; /* TAI offset after 'when' */ 68275970Scy vint64 dtime; /* due time (current era end) */ 69275970Scy vint64 ttime; /* nominal transition time (next era start) */ 70275970Scy vint64 stime; /* schedule time (when we take notice) */ 71275970Scy vint64 ebase; /* base time of this leap era */ 72275970Scy uint8_t dynls; /* next leap is dynamic (by peer request) */ 73275970Scy}; 74275970Scytypedef struct leap_head leap_head_t; 75275970Scy 76275970Scystruct leap_table { 77275970Scy leap_signature_t lsig; 78275970Scy leap_head_t head; 79275970Scy leap_info_t info[MAX_HIST]; 80275970Scy}; 81275970Scy 82275970Scy/* Where we store our tables */ 83275970Scystatic leap_table_t _ltab[2], *_lptr; 84275970Scystatic int/*BOOL*/ _electric; 85275970Scy 86275970Scy/* Forward decls of local helpers */ 87275970Scystatic int add_range(leap_table_t*, const leap_info_t*); 88275970Scystatic char * get_line(leapsec_reader, void*, char*, size_t); 89275970Scystatic char * skipws(const char*); 90275970Scystatic int parsefail(const char * cp, const char * ep); 91275970Scystatic void reload_limits(leap_table_t*, const vint64*); 92290000Sglebiusstatic void fetch_leap_era(leap_era_t*, const leap_table_t*, 93290000Sglebius const vint64*); 94275970Scystatic int betweenu32(uint32_t, uint32_t, uint32_t); 95275970Scystatic void reset_times(leap_table_t*); 96275970Scystatic int leapsec_add(leap_table_t*, const vint64*, int); 97275970Scystatic int leapsec_raw(leap_table_t*, const vint64 *, int, int); 98290000Sglebiusstatic const char * lstostr(const vint64 * ts); 99275970Scy 100275970Scy/* ===================================================================== 101275970Scy * Get & Set the current leap table 102275970Scy */ 103275970Scy 104275970Scy/* ------------------------------------------------------------------ */ 105275970Scyleap_table_t * 106275970Scyleapsec_get_table( 107275970Scy int alternate) 108275970Scy{ 109275970Scy leap_table_t *p1, *p2; 110275970Scy 111275970Scy p1 = _lptr; 112290000Sglebius if (p1 == &_ltab[0]) { 113290000Sglebius p2 = &_ltab[1]; 114290000Sglebius } else if (p1 == &_ltab[1]) { 115290000Sglebius p2 = &_ltab[0]; 116290000Sglebius } else { 117290000Sglebius p1 = &_ltab[0]; 118290000Sglebius p2 = &_ltab[1]; 119290000Sglebius reset_times(p1); 120290000Sglebius reset_times(p2); 121290000Sglebius _lptr = p1; 122290000Sglebius } 123275970Scy if (alternate) { 124275970Scy memcpy(p2, p1, sizeof(leap_table_t)); 125275970Scy p1 = p2; 126275970Scy } 127275970Scy 128275970Scy return p1; 129275970Scy} 130275970Scy 131275970Scy/* ------------------------------------------------------------------ */ 132275970Scyint/*BOOL*/ 133275970Scyleapsec_set_table( 134275970Scy leap_table_t * pt) 135275970Scy{ 136275970Scy if (pt == &_ltab[0] || pt == &_ltab[1]) 137275970Scy _lptr = pt; 138275970Scy return _lptr == pt; 139275970Scy} 140275970Scy 141275970Scy/* ------------------------------------------------------------------ */ 142275970Scyint/*BOOL*/ 143275970Scyleapsec_electric( 144275970Scy int/*BOOL*/ on) 145275970Scy{ 146275970Scy int res = _electric; 147275970Scy if (on < 0) 148275970Scy return res; 149275970Scy 150275970Scy _electric = (on != 0); 151275970Scy if (_electric == res) 152275970Scy return res; 153275970Scy 154275970Scy if (_lptr == &_ltab[0] || _lptr == &_ltab[1]) 155275970Scy reset_times(_lptr); 156275970Scy 157275970Scy return res; 158275970Scy} 159275970Scy 160275970Scy/* ===================================================================== 161275970Scy * API functions that operate on tables 162275970Scy */ 163275970Scy 164275970Scy/* --------------------------------------------------------------------- 165275970Scy * Clear all leap second data. Use it for init & cleanup 166275970Scy */ 167275970Scyvoid 168275970Scyleapsec_clear( 169275970Scy leap_table_t * pt) 170275970Scy{ 171275970Scy memset(&pt->lsig, 0, sizeof(pt->lsig)); 172275970Scy memset(&pt->head, 0, sizeof(pt->head)); 173275970Scy reset_times(pt); 174275970Scy} 175275970Scy 176275970Scy/* --------------------------------------------------------------------- 177275970Scy * Load a leap second file and check expiration on the go 178275970Scy */ 179275970Scyint/*BOOL*/ 180275970Scyleapsec_load( 181275970Scy leap_table_t * pt , 182275970Scy leapsec_reader func, 183275970Scy void * farg, 184275970Scy int use_build_limit) 185275970Scy{ 186275970Scy char *cp, *ep, linebuf[50]; 187275970Scy vint64 ttime, limit; 188275970Scy long taiof; 189275970Scy struct calendar build; 190275970Scy 191275970Scy leapsec_clear(pt); 192290000Sglebius if (use_build_limit && ntpcal_get_build_date(&build)) { 193290000Sglebius /* don't prune everything -- permit the last 10yrs 194290000Sglebius * before build. 195290000Sglebius */ 196290000Sglebius build.year -= 10; 197275970Scy limit = ntpcal_date_to_ntp64(&build); 198290000Sglebius } else { 199275970Scy memset(&limit, 0, sizeof(limit)); 200290000Sglebius } 201275970Scy 202275970Scy while (get_line(func, farg, linebuf, sizeof(linebuf))) { 203275970Scy cp = linebuf; 204275970Scy if (*cp == '#') { 205275970Scy cp++; 206275970Scy if (*cp == '@') { 207275970Scy cp = skipws(cp+1); 208275970Scy pt->head.expire = strtouv64(cp, &ep, 10); 209275970Scy if (parsefail(cp, ep)) 210275970Scy goto fail_read; 211275970Scy pt->lsig.etime = pt->head.expire.D_s.lo; 212275970Scy } else if (*cp == '$') { 213275970Scy cp = skipws(cp+1); 214275970Scy pt->head.update = strtouv64(cp, &ep, 10); 215275970Scy if (parsefail(cp, ep)) 216275970Scy goto fail_read; 217290000Sglebius } 218275970Scy } else if (isdigit((u_char)*cp)) { 219275970Scy ttime = strtouv64(cp, &ep, 10); 220275970Scy if (parsefail(cp, ep)) 221275970Scy goto fail_read; 222275970Scy cp = skipws(ep); 223275970Scy taiof = strtol(cp, &ep, 10); 224275970Scy if ( parsefail(cp, ep) 225275970Scy || taiof > SHRT_MAX || taiof < SHRT_MIN) 226275970Scy goto fail_read; 227275970Scy if (ucmpv64(&ttime, &limit) >= 0) { 228275970Scy if (!leapsec_raw(pt, &ttime, 229275970Scy taiof, FALSE)) 230275970Scy goto fail_insn; 231275970Scy } else { 232275970Scy pt->head.base_tai = (int16_t)taiof; 233275970Scy } 234275970Scy pt->lsig.ttime = ttime.D_s.lo; 235275970Scy pt->lsig.taiof = (int16_t)taiof; 236275970Scy } 237275970Scy } 238275970Scy return TRUE; 239275970Scy 240275970Scyfail_read: 241275970Scy errno = EILSEQ; 242275970Scyfail_insn: 243275970Scy leapsec_clear(pt); 244275970Scy return FALSE; 245275970Scy} 246275970Scy 247275970Scy/* --------------------------------------------------------------------- 248275970Scy * Dump a table in human-readable format. Use 'fprintf' and a FILE 249275970Scy * pointer if you want to get it printed into a stream. 250275970Scy */ 251275970Scyvoid 252275970Scyleapsec_dump( 253275970Scy const leap_table_t * pt , 254275970Scy leapsec_dumper func, 255275970Scy void * farg) 256275970Scy{ 257275970Scy int idx; 258275970Scy vint64 ts; 259275970Scy struct calendar atb, ttb; 260275970Scy 261275970Scy ntpcal_ntp64_to_date(&ttb, &pt->head.expire); 262275970Scy (*func)(farg, "leap table (%u entries) expires at %04u-%02u-%02u:\n", 263275970Scy pt->head.size, 264275970Scy ttb.year, ttb.month, ttb.monthday); 265275970Scy idx = pt->head.size; 266275970Scy while (idx-- != 0) { 267275970Scy ts = pt->info[idx].ttime; 268275970Scy ntpcal_ntp64_to_date(&ttb, &ts); 269275970Scy ts = subv64u32(&ts, pt->info[idx].stime); 270275970Scy ntpcal_ntp64_to_date(&atb, &ts); 271275970Scy 272275970Scy (*func)(farg, "%04u-%02u-%02u [%c] (%04u-%02u-%02u) - %d\n", 273275970Scy ttb.year, ttb.month, ttb.monthday, 274275970Scy "-*"[pt->info[idx].dynls != 0], 275275970Scy atb.year, atb.month, atb.monthday, 276275970Scy pt->info[idx].taiof); 277275970Scy } 278275970Scy} 279275970Scy 280275970Scy/* ===================================================================== 281275970Scy * usecase driven API functions 282275970Scy */ 283275970Scy 284275970Scyint/*BOOL*/ 285275970Scyleapsec_query( 286275970Scy leap_result_t * qr , 287275970Scy uint32_t ts32 , 288275970Scy const time_t * pivot) 289275970Scy{ 290275970Scy leap_table_t * pt; 291275970Scy vint64 ts64, last, next; 292275970Scy uint32_t due32; 293275970Scy int fired; 294275970Scy 295275970Scy /* preset things we use later on... */ 296275970Scy fired = FALSE; 297275970Scy ts64 = ntpcal_ntp_to_ntp(ts32, pivot); 298275970Scy pt = leapsec_get_table(FALSE); 299275970Scy memset(qr, 0, sizeof(leap_result_t)); 300275970Scy 301275970Scy if (ucmpv64(&ts64, &pt->head.ebase) < 0) { 302275970Scy /* Most likely after leap frame reset. Could also be a 303275970Scy * backstep of the system clock. Anyway, get the new 304275970Scy * leap era frame. 305275970Scy */ 306275970Scy reload_limits(pt, &ts64); 307290000Sglebius } else if (ucmpv64(&ts64, &pt->head.dtime) >= 0) { 308275970Scy /* Boundary crossed in forward direction. This might 309275970Scy * indicate a leap transition, so we prepare for that 310275970Scy * case. 311275970Scy * 312275970Scy * Some operations below are actually NOPs in electric 313275970Scy * mode, but having only one code path that works for 314275970Scy * both modes is easier to maintain. 315290000Sglebius * 316290000Sglebius * There's another quirk we must keep looking out for: 317290000Sglebius * If we just stepped the clock, the step might have 318290000Sglebius * crossed a leap boundary. As with backward steps, we 319290000Sglebius * do not want to raise the 'fired' event in that case. 320290000Sglebius * So we raise the 'fired' event only if we're close to 321290000Sglebius * the transition and just reload the limits otherwise. 322275970Scy */ 323290000Sglebius last = addv64i32(&pt->head.dtime, 3); /* get boundary */ 324290000Sglebius if (ucmpv64(&ts64, &last) >= 0) { 325290000Sglebius /* that was likely a query after a step */ 326290000Sglebius reload_limits(pt, &ts64); 327275970Scy } else { 328290000Sglebius /* close enough for deeper examination */ 329290000Sglebius last = pt->head.ttime; 330290000Sglebius qr->warped = (int16_t)(last.D_s.lo - 331290000Sglebius pt->head.dtime.D_s.lo); 332290000Sglebius next = addv64i32(&ts64, qr->warped); 333290000Sglebius reload_limits(pt, &next); 334290000Sglebius fired = ucmpv64(&pt->head.ebase, &last) == 0; 335290000Sglebius if (fired) { 336290000Sglebius ts64 = next; 337290000Sglebius ts32 = next.D_s.lo; 338290000Sglebius } else { 339290000Sglebius qr->warped = 0; 340290000Sglebius } 341275970Scy } 342275970Scy } 343275970Scy 344275970Scy qr->tai_offs = pt->head.this_tai; 345290000Sglebius qr->ebase = pt->head.ebase; 346290000Sglebius qr->ttime = pt->head.ttime; 347275970Scy 348275970Scy /* If before the next scheduling alert, we're done. */ 349275970Scy if (ucmpv64(&ts64, &pt->head.stime) < 0) 350275970Scy return fired; 351275970Scy 352290000Sglebius /* now start to collect the remaining data */ 353275970Scy due32 = pt->head.dtime.D_s.lo; 354275970Scy 355275970Scy qr->tai_diff = pt->head.next_tai - pt->head.this_tai; 356275970Scy qr->ddist = due32 - ts32; 357275970Scy qr->dynamic = pt->head.dynls; 358275970Scy qr->proximity = LSPROX_SCHEDULE; 359275970Scy 360275970Scy /* if not in the last day before transition, we're done. */ 361275970Scy if (!betweenu32(due32 - SECSPERDAY, ts32, due32)) 362275970Scy return fired; 363290000Sglebius 364275970Scy qr->proximity = LSPROX_ANNOUNCE; 365275970Scy if (!betweenu32(due32 - 10, ts32, due32)) 366275970Scy return fired; 367275970Scy 368275970Scy /* The last 10s before the transition. Prepare for action! */ 369275970Scy qr->proximity = LSPROX_ALERT; 370275970Scy return fired; 371275970Scy} 372275970Scy 373275970Scy/* ------------------------------------------------------------------ */ 374275970Scyint/*BOOL*/ 375290000Sglebiusleapsec_query_era( 376290000Sglebius leap_era_t * qr , 377290000Sglebius uint32_t ntpts, 378290000Sglebius const time_t * pivot) 379290000Sglebius{ 380290000Sglebius const leap_table_t * pt; 381290000Sglebius vint64 ts64; 382290000Sglebius 383290000Sglebius pt = leapsec_get_table(FALSE); 384290000Sglebius ts64 = ntpcal_ntp_to_ntp(ntpts, pivot); 385290000Sglebius fetch_leap_era(qr, pt, &ts64); 386290000Sglebius return TRUE; 387290000Sglebius} 388290000Sglebius 389290000Sglebius/* ------------------------------------------------------------------ */ 390290000Sglebiusint/*BOOL*/ 391275970Scyleapsec_frame( 392275970Scy leap_result_t *qr) 393275970Scy{ 394275970Scy const leap_table_t * pt; 395275970Scy 396275970Scy memset(qr, 0, sizeof(leap_result_t)); 397275970Scy pt = leapsec_get_table(FALSE); 398275970Scy 399275970Scy qr->tai_offs = pt->head.this_tai; 400275970Scy qr->tai_diff = pt->head.next_tai - pt->head.this_tai; 401290000Sglebius qr->ebase = pt->head.ebase; 402275970Scy qr->ttime = pt->head.ttime; 403275970Scy qr->dynamic = pt->head.dynls; 404275970Scy 405290000Sglebius return ucmpv64(&pt->head.ttime, &pt->head.stime) >= 0; 406275970Scy} 407275970Scy 408275970Scy/* ------------------------------------------------------------------ */ 409275970Scy/* Reset the current leap frame */ 410275970Scyvoid 411275970Scyleapsec_reset_frame(void) 412275970Scy{ 413275970Scy reset_times(leapsec_get_table(FALSE)); 414275970Scy} 415275970Scy 416275970Scy/* ------------------------------------------------------------------ */ 417275970Scy/* load a file from a FILE pointer. Note: If hcheck is true, load 418275970Scy * only after successful signature check. The stream must be seekable 419275970Scy * or this will fail. 420275970Scy */ 421275970Scyint/*BOOL*/ 422275970Scyleapsec_load_stream( 423275970Scy FILE * ifp , 424275970Scy const char * fname, 425275970Scy int/*BOOL*/ logall) 426275970Scy{ 427275970Scy leap_table_t *pt; 428275970Scy int rcheck; 429275970Scy 430275970Scy if (NULL == fname) 431275970Scy fname = "<unknown>"; 432275970Scy 433275970Scy rcheck = leapsec_validate((leapsec_reader)getc, ifp); 434275970Scy if (logall) 435275970Scy switch (rcheck) 436275970Scy { 437275970Scy case LSVALID_GOODHASH: 438275970Scy msyslog(LOG_NOTICE, "%s ('%s'): good hash signature", 439275970Scy logPrefix, fname); 440275970Scy break; 441290000Sglebius 442275970Scy case LSVALID_NOHASH: 443275970Scy msyslog(LOG_ERR, "%s ('%s'): no hash signature", 444275970Scy logPrefix, fname); 445275970Scy break; 446275970Scy case LSVALID_BADHASH: 447275970Scy msyslog(LOG_ERR, "%s ('%s'): signature mismatch", 448275970Scy logPrefix, fname); 449275970Scy break; 450275970Scy case LSVALID_BADFORMAT: 451275970Scy msyslog(LOG_ERR, "%s ('%s'): malformed hash signature", 452275970Scy logPrefix, fname); 453275970Scy break; 454275970Scy default: 455275970Scy msyslog(LOG_ERR, "%s ('%s'): unknown error code %d", 456275970Scy logPrefix, fname, rcheck); 457275970Scy break; 458275970Scy } 459275970Scy if (rcheck < 0) 460275970Scy return FALSE; 461275970Scy 462275970Scy rewind(ifp); 463275970Scy pt = leapsec_get_table(TRUE); 464275970Scy if (!leapsec_load(pt, (leapsec_reader)getc, ifp, TRUE)) { 465275970Scy switch (errno) { 466275970Scy case EINVAL: 467275970Scy msyslog(LOG_ERR, "%s ('%s'): bad transition time", 468275970Scy logPrefix, fname); 469275970Scy break; 470275970Scy case ERANGE: 471275970Scy msyslog(LOG_ERR, "%s ('%s'): times not ascending", 472275970Scy logPrefix, fname); 473275970Scy break; 474275970Scy default: 475275970Scy msyslog(LOG_ERR, "%s ('%s'): parsing error", 476275970Scy logPrefix, fname); 477275970Scy break; 478275970Scy } 479275970Scy return FALSE; 480275970Scy } 481275970Scy 482275970Scy if (pt->head.size) 483275970Scy msyslog(LOG_NOTICE, "%s ('%s'): loaded, expire=%s last=%s ofs=%d", 484275970Scy logPrefix, fname, lstostr(&pt->head.expire), 485275970Scy lstostr(&pt->info[0].ttime), pt->info[0].taiof); 486275970Scy else 487275970Scy msyslog(LOG_NOTICE, 488275970Scy "%s ('%s'): loaded, expire=%s ofs=%d (no entries after build date)", 489275970Scy logPrefix, fname, lstostr(&pt->head.expire), 490275970Scy pt->head.base_tai); 491290000Sglebius 492275970Scy return leapsec_set_table(pt); 493275970Scy} 494275970Scy 495275970Scy/* ------------------------------------------------------------------ */ 496275970Scyint/*BOOL*/ 497275970Scyleapsec_load_file( 498275970Scy const char * fname, 499275970Scy struct stat * sb_old, 500275970Scy int/*BOOL*/ force, 501275970Scy int/*BOOL*/ logall) 502275970Scy{ 503275970Scy FILE * fp; 504275970Scy struct stat sb_new; 505275970Scy int rc; 506275970Scy 507275970Scy /* just do nothing if there is no leap file */ 508275970Scy if ( !(fname && *fname) ) 509275970Scy return FALSE; 510290000Sglebius 511275970Scy /* try to stat the leapfile */ 512275970Scy if (0 != stat(fname, &sb_new)) { 513275970Scy if (logall) 514275970Scy msyslog(LOG_ERR, "%s ('%s'): stat failed: %m", 515275970Scy logPrefix, fname); 516275970Scy return FALSE; 517275970Scy } 518275970Scy 519275970Scy /* silently skip to postcheck if no new file found */ 520275970Scy if (NULL != sb_old) { 521275970Scy if (!force 522275970Scy && sb_old->st_mtime == sb_new.st_mtime 523275970Scy && sb_old->st_ctime == sb_new.st_ctime 524275970Scy ) 525275970Scy return FALSE; 526275970Scy *sb_old = sb_new; 527275970Scy } 528275970Scy 529275970Scy /* try to open the leap file, complain if that fails 530275970Scy * 531275970Scy * [perlinger@ntp.org] 532275970Scy * coverity raises a TOCTOU (time-of-check/time-of-use) issue 533275970Scy * here, which is not entirely helpful: While there is indeed a 534275970Scy * possible race condition between the 'stat()' call above and 535275970Scy * the 'fopen)' call below, I intentionally want to omit the 536275970Scy * overhead of opening the file and calling 'fstat()', because 537275970Scy * in most cases the file would have be to closed anyway without 538275970Scy * reading the contents. I chose to disable the coverity 539275970Scy * warning instead. 540275970Scy * 541275970Scy * So unless someone comes up with a reasonable argument why 542275970Scy * this could be a real issue, I'll just try to silence coverity 543275970Scy * on that topic. 544275970Scy */ 545275970Scy /* coverity[toctou] */ 546275970Scy if ((fp = fopen(fname, "r")) == NULL) { 547275970Scy if (logall) 548275970Scy msyslog(LOG_ERR, 549275970Scy "%s ('%s'): open failed: %m", 550275970Scy logPrefix, fname); 551275970Scy return FALSE; 552275970Scy } 553275970Scy 554275970Scy rc = leapsec_load_stream(fp, fname, logall); 555275970Scy fclose(fp); 556275970Scy return rc; 557275970Scy} 558275970Scy 559275970Scy/* ------------------------------------------------------------------ */ 560275970Scyvoid 561275970Scyleapsec_getsig( 562275970Scy leap_signature_t * psig) 563275970Scy{ 564275970Scy const leap_table_t * pt; 565275970Scy 566275970Scy pt = leapsec_get_table(FALSE); 567275970Scy memcpy(psig, &pt->lsig, sizeof(leap_signature_t)); 568275970Scy} 569275970Scy 570275970Scy/* ------------------------------------------------------------------ */ 571275970Scyint/*BOOL*/ 572275970Scyleapsec_expired( 573275970Scy uint32_t when, 574275970Scy const time_t * tpiv) 575275970Scy{ 576275970Scy const leap_table_t * pt; 577275970Scy vint64 limit; 578275970Scy 579275970Scy pt = leapsec_get_table(FALSE); 580275970Scy limit = ntpcal_ntp_to_ntp(when, tpiv); 581275970Scy return ucmpv64(&limit, &pt->head.expire) >= 0; 582275970Scy} 583275970Scy 584275970Scy/* ------------------------------------------------------------------ */ 585275970Scyint32_t 586275970Scyleapsec_daystolive( 587275970Scy uint32_t when, 588275970Scy const time_t * tpiv) 589275970Scy{ 590275970Scy const leap_table_t * pt; 591275970Scy vint64 limit; 592275970Scy 593275970Scy pt = leapsec_get_table(FALSE); 594275970Scy limit = ntpcal_ntp_to_ntp(when, tpiv); 595275970Scy limit = subv64(&pt->head.expire, &limit); 596275970Scy return ntpcal_daysplit(&limit).hi; 597275970Scy} 598275970Scy 599275970Scy/* ------------------------------------------------------------------ */ 600290000Sglebius#if 0 /* currently unused -- possibly revived later */ 601275970Scyint/*BOOL*/ 602275970Scyleapsec_add_fix( 603275970Scy int total, 604275970Scy uint32_t ttime, 605275970Scy uint32_t etime, 606275970Scy const time_t * pivot) 607275970Scy{ 608275970Scy time_t tpiv; 609275970Scy leap_table_t * pt; 610275970Scy vint64 tt64, et64; 611275970Scy 612275970Scy if (pivot == NULL) { 613275970Scy time(&tpiv); 614275970Scy pivot = &tpiv; 615275970Scy } 616290000Sglebius 617275970Scy et64 = ntpcal_ntp_to_ntp(etime, pivot); 618275970Scy tt64 = ntpcal_ntp_to_ntp(ttime, pivot); 619275970Scy pt = leapsec_get_table(TRUE); 620275970Scy 621275970Scy if ( ucmpv64(&et64, &pt->head.expire) <= 0 622275970Scy || !leapsec_raw(pt, &tt64, total, FALSE) ) 623275970Scy return FALSE; 624275970Scy 625275970Scy pt->lsig.etime = etime; 626275970Scy pt->lsig.ttime = ttime; 627275970Scy pt->lsig.taiof = (int16_t)total; 628275970Scy 629275970Scy pt->head.expire = et64; 630275970Scy 631275970Scy return leapsec_set_table(pt); 632275970Scy} 633290000Sglebius#endif 634275970Scy 635275970Scy/* ------------------------------------------------------------------ */ 636275970Scyint/*BOOL*/ 637275970Scyleapsec_add_dyn( 638275970Scy int insert, 639275970Scy uint32_t ntpnow, 640275970Scy const time_t * pivot ) 641275970Scy{ 642275970Scy leap_table_t * pt; 643275970Scy vint64 now64; 644275970Scy 645275970Scy pt = leapsec_get_table(TRUE); 646275970Scy now64 = ntpcal_ntp_to_ntp(ntpnow, pivot); 647275970Scy return ( leapsec_add(pt, &now64, (insert != 0)) 648275970Scy && leapsec_set_table(pt)); 649275970Scy} 650275970Scy 651290000Sglebius/* ------------------------------------------------------------------ */ 652290000Sglebiusint/*BOOL*/ 653290000Sglebiusleapsec_autokey_tai( 654290000Sglebius int tai_offset, 655290000Sglebius uint32_t ntpnow , 656290000Sglebius const time_t * pivot ) 657290000Sglebius{ 658290000Sglebius leap_table_t * pt; 659290000Sglebius leap_era_t era; 660290000Sglebius vint64 now64; 661290000Sglebius int idx; 662290000Sglebius 663290000Sglebius (void)tai_offset; 664290000Sglebius pt = leapsec_get_table(FALSE); 665290000Sglebius 666290000Sglebius /* Bail out if the basic offset is not zero and the putative 667290000Sglebius * offset is bigger than 10s. That was in 1972 -- we don't want 668290000Sglebius * to go back that far! 669290000Sglebius */ 670290000Sglebius if (pt->head.base_tai != 0 || tai_offset < 10) 671290000Sglebius return FALSE; 672290000Sglebius 673290000Sglebius /* If there's already data in the table, check if an update is 674290000Sglebius * possible. Update is impossible if there are static entries 675290000Sglebius * (since this indicates a valid leapsecond file) or if we're 676290000Sglebius * too close to a leapsecond transition: We do not know on what 677290000Sglebius * side the transition the sender might have been, so we use a 678290000Sglebius * dead zone around the transition. 679290000Sglebius */ 680290000Sglebius 681290000Sglebius /* Check for static entries */ 682290000Sglebius for (idx = 0; idx != pt->head.size; idx++) 683290000Sglebius if ( ! pt->info[idx].dynls) 684290000Sglebius return FALSE; 685290000Sglebius 686290000Sglebius /* get the fulll time stamp and leap era for it */ 687290000Sglebius now64 = ntpcal_ntp_to_ntp(ntpnow, pivot); 688290000Sglebius fetch_leap_era(&era, pt, &now64); 689290000Sglebius 690290000Sglebius /* check the limits with 20s dead band */ 691290000Sglebius era.ebase = addv64i32(&era.ebase, 20); 692290000Sglebius if (ucmpv64(&now64, &era.ebase) < 0) 693290000Sglebius return FALSE; 694290000Sglebius 695290000Sglebius era.ttime = addv64i32(&era.ttime, -20); 696290000Sglebius if (ucmpv64(&now64, &era.ttime) > 0) 697290000Sglebius return FALSE; 698290000Sglebius 699290000Sglebius /* Here we can proceed. Calculate the delta update. */ 700290000Sglebius tai_offset -= era.taiof; 701290000Sglebius 702290000Sglebius /* Shift the header info offsets. */ 703290000Sglebius pt->head.base_tai += tai_offset; 704290000Sglebius pt->head.this_tai += tai_offset; 705290000Sglebius pt->head.next_tai += tai_offset; 706290000Sglebius 707290000Sglebius /* Shift table entry offsets (if any) */ 708290000Sglebius for (idx = 0; idx != pt->head.size; idx++) 709290000Sglebius pt->info[idx].taiof += tai_offset; 710290000Sglebius 711290000Sglebius /* claim success... */ 712290000Sglebius return TRUE; 713290000Sglebius} 714290000Sglebius 715290000Sglebius 716275970Scy/* ===================================================================== 717275970Scy * internal helpers 718275970Scy */ 719275970Scy 720275970Scy/* [internal] Reset / init the time window in the leap processor to 721275970Scy * force reload on next query. Since a leap transition cannot take place 722275970Scy * at an odd second, the value chosen avoids spurious leap transition 723275970Scy * triggers. Making all three times equal forces a reload. Using the 724275970Scy * maximum value for unsigned 64 bits makes finding the next leap frame 725275970Scy * a bit easier. 726275970Scy */ 727275970Scystatic void 728275970Scyreset_times( 729275970Scy leap_table_t * pt) 730275970Scy{ 731275970Scy memset(&pt->head.ebase, 0xFF, sizeof(vint64)); 732275970Scy pt->head.stime = pt->head.ebase; 733275970Scy pt->head.ttime = pt->head.ebase; 734275970Scy pt->head.dtime = pt->head.ebase; 735275970Scy} 736275970Scy 737275970Scy/* [internal] Add raw data to the table, removing old entries on the 738275970Scy * fly. This cannot fail currently. 739275970Scy */ 740275970Scystatic int/*BOOL*/ 741275970Scyadd_range( 742275970Scy leap_table_t * pt, 743275970Scy const leap_info_t * pi) 744275970Scy{ 745275970Scy /* If the table is full, make room by throwing out the oldest 746290000Sglebius * entry. But remember the accumulated leap seconds! Likewise, 747290000Sglebius * assume a positive leap insertion if this is the first entry 748290000Sglebius * in the table. This is not necessarily the best of all ideas, 749290000Sglebius * but it helps a great deal if a system does not have a leap 750290000Sglebius * table and gets updated from an upstream server. 751275970Scy */ 752290000Sglebius if (pt->head.size == 0) { 753290000Sglebius pt->head.base_tai = pi->taiof - 1; 754290000Sglebius } else if (pt->head.size >= MAX_HIST) { 755275970Scy pt->head.size = MAX_HIST - 1; 756275970Scy pt->head.base_tai = pt->info[pt->head.size].taiof; 757275970Scy } 758275970Scy 759275970Scy /* make room in lower end and insert item */ 760290000Sglebius memmove(pt->info+1, pt->info, pt->head.size*sizeof(*pt->info)); 761275970Scy pt->info[0] = *pi; 762275970Scy pt->head.size++; 763275970Scy 764275970Scy /* invalidate the cached limit data -- we might have news ;-) 765275970Scy * 766275970Scy * This blocks a spurious transition detection. OTOH, if you add 767275970Scy * a value after the last query before a leap transition was 768275970Scy * expected to occur, this transition trigger is lost. But we 769275970Scy * can probably live with that. 770275970Scy */ 771275970Scy reset_times(pt); 772275970Scy return TRUE; 773275970Scy} 774275970Scy 775275970Scy/* [internal] given a reader function, read characters into a buffer 776275970Scy * until either EOL or EOF is reached. Makes sure that the buffer is 777275970Scy * always NUL terminated, but silently truncates excessive data. The 778275970Scy * EOL-marker ('\n') is *not* stored in the buffer. 779275970Scy * 780275970Scy * Returns the pointer to the buffer, unless EOF was reached when trying 781275970Scy * to read the first character of a line. 782275970Scy */ 783275970Scystatic char * 784275970Scyget_line( 785275970Scy leapsec_reader func, 786275970Scy void * farg, 787275970Scy char * buff, 788275970Scy size_t size) 789275970Scy{ 790275970Scy int ch; 791275970Scy char *ptr; 792290000Sglebius 793275970Scy /* if we cannot even store the delimiter, declare failure */ 794275970Scy if (buff == NULL || size == 0) 795275970Scy return NULL; 796275970Scy 797275970Scy ptr = buff; 798275970Scy while (EOF != (ch = (*func)(farg)) && '\n' != ch) 799275970Scy if (size > 1) { 800275970Scy size--; 801275970Scy *ptr++ = (char)ch; 802275970Scy } 803275970Scy /* discard trailing whitespace */ 804275970Scy while (ptr != buff && isspace((u_char)ptr[-1])) 805275970Scy ptr--; 806275970Scy *ptr = '\0'; 807275970Scy return (ptr == buff && ch == EOF) ? NULL : buff; 808275970Scy} 809275970Scy 810275970Scy/* [internal] skips whitespace characters from a character buffer. */ 811275970Scystatic char * 812275970Scyskipws( 813275970Scy const char *ptr) 814275970Scy{ 815275970Scy while (isspace((u_char)*ptr)) 816275970Scy ptr++; 817275970Scy return (char*)noconst(ptr); 818275970Scy} 819275970Scy 820290000Sglebius/* [internal] check if a strtoXYZ ended at EOL or whitespace and 821275970Scy * converted something at all. Return TRUE if something went wrong. 822275970Scy */ 823275970Scystatic int/*BOOL*/ 824275970Scyparsefail( 825275970Scy const char * cp, 826275970Scy const char * ep) 827275970Scy{ 828275970Scy return (cp == ep) 829275970Scy || (*ep && *ep != '#' && !isspace((u_char)*ep)); 830275970Scy} 831275970Scy 832275970Scy/* [internal] reload the table limits around the given time stamp. This 833275970Scy * is where the real work is done when it comes to table lookup and 834275970Scy * evaluation. Some care has been taken to have correct code for dealing 835275970Scy * with boundary conditions and empty tables. 836275970Scy * 837275970Scy * In electric mode, transition and trip time are the same. In dumb 838275970Scy * mode, the difference of the TAI offsets must be taken into account 839275970Scy * and trip time and transition time become different. The difference 840275970Scy * becomes the warping distance when the trip time is reached. 841275970Scy */ 842275970Scystatic void 843275970Scyreload_limits( 844275970Scy leap_table_t * pt, 845275970Scy const vint64 * ts) 846275970Scy{ 847275970Scy int idx; 848275970Scy 849275970Scy /* Get full time and search the true lower bound. Use a 850275970Scy * simple loop here, since the number of entries does 851275970Scy * not warrant a binary search. This also works for an empty 852275970Scy * table, so there is no shortcut for that case. 853275970Scy */ 854275970Scy for (idx = 0; idx != pt->head.size; idx++) 855275970Scy if (ucmpv64(ts, &pt->info[idx].ttime) >= 0) 856275970Scy break; 857275970Scy 858275970Scy /* get time limits with proper bound conditions. Note that the 859275970Scy * bounds of the table will be observed even if the table is 860275970Scy * empty -- no undefined condition must arise from this code. 861275970Scy */ 862275970Scy if (idx >= pt->head.size) { 863275970Scy memset(&pt->head.ebase, 0x00, sizeof(vint64)); 864275970Scy pt->head.this_tai = pt->head.base_tai; 865275970Scy } else { 866275970Scy pt->head.ebase = pt->info[idx].ttime; 867275970Scy pt->head.this_tai = pt->info[idx].taiof; 868275970Scy } 869275970Scy if (--idx >= 0) { 870275970Scy pt->head.next_tai = pt->info[idx].taiof; 871275970Scy pt->head.dynls = pt->info[idx].dynls; 872275970Scy pt->head.ttime = pt->info[idx].ttime; 873275970Scy 874275970Scy if (_electric) 875275970Scy pt->head.dtime = pt->head.ttime; 876275970Scy else 877275970Scy pt->head.dtime = addv64i32( 878275970Scy &pt->head.ttime, 879275970Scy pt->head.next_tai - pt->head.this_tai); 880290000Sglebius 881275970Scy pt->head.stime = subv64u32( 882275970Scy &pt->head.ttime, pt->info[idx].stime); 883275970Scy 884275970Scy } else { 885275970Scy memset(&pt->head.ttime, 0xFF, sizeof(vint64)); 886275970Scy pt->head.stime = pt->head.ttime; 887275970Scy pt->head.dtime = pt->head.ttime; 888275970Scy pt->head.next_tai = pt->head.this_tai; 889275970Scy pt->head.dynls = 0; 890275970Scy } 891275970Scy} 892275970Scy 893290000Sglebius/* [internal] fetch the leap era for a given time stamp. 894290000Sglebius * This is a cut-down version the algorithm used to reload the table 895290000Sglebius * limits, but it does not update any global state and provides just the 896290000Sglebius * era information for a given time stamp. 897290000Sglebius */ 898290000Sglebiusstatic void 899290000Sglebiusfetch_leap_era( 900290000Sglebius leap_era_t * into, 901290000Sglebius const leap_table_t * pt , 902290000Sglebius const vint64 * ts ) 903290000Sglebius{ 904290000Sglebius int idx; 905290000Sglebius 906290000Sglebius /* Simple search loop, also works with empty table. */ 907290000Sglebius for (idx = 0; idx != pt->head.size; idx++) 908290000Sglebius if (ucmpv64(ts, &pt->info[idx].ttime) >= 0) 909290000Sglebius break; 910290000Sglebius /* fetch era data, keeping an eye on boundary conditions */ 911290000Sglebius if (idx >= pt->head.size) { 912290000Sglebius memset(&into->ebase, 0x00, sizeof(vint64)); 913290000Sglebius into->taiof = pt->head.base_tai; 914290000Sglebius } else { 915290000Sglebius into->ebase = pt->info[idx].ttime; 916290000Sglebius into->taiof = pt->info[idx].taiof; 917290000Sglebius } 918290000Sglebius if (--idx >= 0) 919290000Sglebius into->ttime = pt->info[idx].ttime; 920290000Sglebius else 921290000Sglebius memset(&into->ttime, 0xFF, sizeof(vint64)); 922290000Sglebius} 923290000Sglebius 924275970Scy/* [internal] Take a time stamp and create a leap second frame for 925275970Scy * it. This will schedule a leap second for the beginning of the next 926275970Scy * month, midnight UTC. The 'insert' argument tells if a leap second is 927275970Scy * added (!=0) or removed (==0). We do not handle multiple inserts 928275970Scy * (yet?) 929275970Scy * 930275970Scy * Returns 1 if the insert worked, 0 otherwise. (It's not possible to 931275970Scy * insert a leap second into the current history -- only appending 932275970Scy * towards the future is allowed!) 933275970Scy */ 934275970Scystatic int/*BOOL*/ 935275970Scyleapsec_add( 936275970Scy leap_table_t* pt , 937275970Scy const vint64 * now64 , 938275970Scy int insert) 939275970Scy{ 940290000Sglebius vint64 ttime, starttime; 941275970Scy struct calendar fts; 942275970Scy leap_info_t li; 943275970Scy 944290000Sglebius /* Check against the table expiration and the latest available 945275970Scy * leap entry. Do not permit inserts, only appends, and only if 946275970Scy * the extend the table beyond the expiration! 947275970Scy */ 948275970Scy if ( ucmpv64(now64, &pt->head.expire) < 0 949275970Scy || (pt->head.size && ucmpv64(now64, &pt->info[0].ttime) <= 0)) { 950275970Scy errno = ERANGE; 951275970Scy return FALSE; 952275970Scy } 953275970Scy 954275970Scy ntpcal_ntp64_to_date(&fts, now64); 955275970Scy /* To guard against dangling leap flags: do not accept leap 956275970Scy * second request on the 1st hour of the 1st day of the month. 957275970Scy */ 958275970Scy if (fts.monthday == 1 && fts.hour == 0) { 959275970Scy errno = EINVAL; 960275970Scy return FALSE; 961275970Scy } 962275970Scy 963275970Scy /* Ok, do the remaining calculations */ 964275970Scy fts.monthday = 1; 965275970Scy fts.hour = 0; 966275970Scy fts.minute = 0; 967275970Scy fts.second = 0; 968290000Sglebius starttime = ntpcal_date_to_ntp64(&fts); 969275970Scy fts.month++; 970275970Scy ttime = ntpcal_date_to_ntp64(&fts); 971275970Scy 972275970Scy li.ttime = ttime; 973290000Sglebius li.stime = ttime.D_s.lo - starttime.D_s.lo; 974275970Scy li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai) 975275970Scy + (insert ? 1 : -1); 976275970Scy li.dynls = 1; 977275970Scy return add_range(pt, &li); 978275970Scy} 979275970Scy 980275970Scy/* [internal] Given a time stamp for a leap insertion (the exact begin 981275970Scy * of the new leap era), create new leap frame and put it into the 982275970Scy * table. This is the work horse for reading a leap file and getting a 983275970Scy * leap second update via authenticated network packet. 984275970Scy */ 985275970Scyint/*BOOL*/ 986275970Scyleapsec_raw( 987275970Scy leap_table_t * pt, 988275970Scy const vint64 * ttime, 989275970Scy int taiof, 990275970Scy int dynls) 991275970Scy{ 992290000Sglebius vint64 starttime; 993275970Scy struct calendar fts; 994275970Scy leap_info_t li; 995275970Scy 996290000Sglebius /* Check that we either extend the table or get a duplicate of 997290000Sglebius * the latest entry. The latter is a benevolent overwrite with 998290000Sglebius * identical data and could happen if we get an autokey message 999290000Sglebius * that extends the lifetime of the current leapsecond table. 1000290000Sglebius * Otherwise paranoia rulez! 1001290000Sglebius */ 1002290000Sglebius if (pt->head.size) { 1003290000Sglebius int cmp = ucmpv64(ttime, &pt->info[0].ttime); 1004290000Sglebius if (cmp == 0) 1005290000Sglebius cmp -= (taiof != pt->info[0].taiof); 1006290000Sglebius if (cmp < 0) { 1007290000Sglebius errno = ERANGE; 1008290000Sglebius return FALSE; 1009290000Sglebius } 1010290000Sglebius if (cmp == 0) 1011290000Sglebius return TRUE; 1012275970Scy } 1013275970Scy 1014275970Scy ntpcal_ntp64_to_date(&fts, ttime); 1015275970Scy /* If this does not match the exact month start, bail out. */ 1016275970Scy if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) { 1017275970Scy errno = EINVAL; 1018275970Scy return FALSE; 1019275970Scy } 1020275970Scy fts.month--; /* was in range 1..12, no overflow here! */ 1021290000Sglebius starttime = ntpcal_date_to_ntp64(&fts); 1022275970Scy li.ttime = *ttime; 1023290000Sglebius li.stime = ttime->D_s.lo - starttime.D_s.lo; 1024275970Scy li.taiof = (int16_t)taiof; 1025275970Scy li.dynls = (dynls != 0); 1026275970Scy return add_range(pt, &li); 1027275970Scy} 1028275970Scy 1029275970Scy/* [internal] Do a wrap-around save range inclusion check. 1030275970Scy * Returns TRUE if x in [lo,hi[ (intervall open on right side) with full 1031275970Scy * handling of an overflow / wrap-around. 1032275970Scy */ 1033275970Scystatic int/*BOOL*/ 1034275970Scybetweenu32( 1035275970Scy uint32_t lo, 1036275970Scy uint32_t x, 1037275970Scy uint32_t hi) 1038275970Scy{ 1039275970Scy int rc; 1040275970Scy 1041275970Scy if (lo <= hi) 1042275970Scy rc = (lo <= x) && (x < hi); 1043275970Scy else 1044275970Scy rc = (lo <= x) || (x < hi); 1045275970Scy return rc; 1046275970Scy} 1047275970Scy 1048275970Scy/* ===================================================================== 1049275970Scy * validation stuff 1050275970Scy */ 1051275970Scy 1052275970Scytypedef struct { 1053275970Scy unsigned char hv[ISC_SHA1_DIGESTLENGTH]; 1054275970Scy} sha1_digest; 1055275970Scy 1056275970Scy/* [internal] parse a digest line to get the hash signature 1057275970Scy * The NIST code creating the hash writes them out as 5 hex integers 1058275970Scy * without leading zeros. This makes reading them back as hex-encoded 1059275970Scy * BLOB impossible, because there might be less than 40 hex digits. 1060275970Scy * 1061275970Scy * The solution is to read the values back as integers, and then do the 1062275970Scy * byte twiddle necessary to get it into an array of 20 chars. The 1063275970Scy * drawback is that it permits any acceptable number syntax provided by 1064275970Scy * 'scanf()' and 'strtoul()', including optional signs and '0x' 1065275970Scy * prefixes. 1066275970Scy */ 1067275970Scystatic int/*BOOL*/ 1068275970Scydo_leap_hash( 1069275970Scy sha1_digest * mac, 1070275970Scy char const * cp ) 1071275970Scy{ 1072275970Scy int wi, di, num, len; 1073275970Scy unsigned long tmp[5]; 1074275970Scy 1075275970Scy memset(mac, 0, sizeof(*mac)); 1076275970Scy num = sscanf(cp, " %lx %lx %lx %lx %lx%n", 1077275970Scy &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], 1078275970Scy &len); 1079275970Scy if (num != 5 || cp[len] > ' ') 1080275970Scy return FALSE; 1081275970Scy 1082275970Scy /* now do the byte twiddle */ 1083275970Scy for (wi=0; wi < 5; ++wi) 1084275970Scy for (di=3; di >= 0; --di) { 1085280849Scy mac->hv[wi*4 + di] = 1086280849Scy (unsigned char)(tmp[wi] & 0x0FF); 1087275970Scy tmp[wi] >>= 8; 1088275970Scy } 1089275970Scy return TRUE; 1090275970Scy} 1091275970Scy 1092275970Scy/* [internal] add the digits of a data line to the hash, stopping at the 1093275970Scy * next hash ('#') character. 1094275970Scy */ 1095275970Scystatic void 1096275970Scydo_hash_data( 1097275970Scy isc_sha1_t * mdctx, 1098275970Scy char const * cp ) 1099275970Scy{ 1100275970Scy unsigned char text[32]; // must be power of two! 1101275970Scy unsigned int tlen = 0; 1102275970Scy unsigned char ch; 1103275970Scy 1104275970Scy while ('\0' != (ch = *cp++) && '#' != ch) 1105275970Scy if (isdigit(ch)) { 1106275970Scy text[tlen++] = ch; 1107275970Scy tlen &= (sizeof(text)-1); 1108275970Scy if (0 == tlen) 1109275970Scy isc_sha1_update( 1110275970Scy mdctx, text, sizeof(text)); 1111275970Scy } 1112290000Sglebius 1113275970Scy if (0 < tlen) 1114275970Scy isc_sha1_update(mdctx, text, tlen); 1115275970Scy} 1116275970Scy 1117275970Scy/* given a reader and a reader arg, calculate and validate the the hash 1118275970Scy * signature of a NIST leap second file. 1119275970Scy */ 1120275970Scyint 1121275970Scyleapsec_validate( 1122275970Scy leapsec_reader func, 1123275970Scy void * farg) 1124275970Scy{ 1125275970Scy isc_sha1_t mdctx; 1126275970Scy sha1_digest rdig, ldig; /* remote / local digests */ 1127275970Scy char line[50]; 1128275970Scy int hlseen = -1; 1129275970Scy 1130275970Scy isc_sha1_init(&mdctx); 1131275970Scy while (get_line(func, farg, line, sizeof(line))) { 1132275970Scy if (!strncmp(line, "#h", 2)) 1133275970Scy hlseen = do_leap_hash(&rdig, line+2); 1134275970Scy else if (!strncmp(line, "#@", 2)) 1135275970Scy do_hash_data(&mdctx, line+2); 1136275970Scy else if (!strncmp(line, "#$", 2)) 1137275970Scy do_hash_data(&mdctx, line+2); 1138280849Scy else if (isdigit((unsigned char)line[0])) 1139275970Scy do_hash_data(&mdctx, line); 1140275970Scy } 1141275970Scy isc_sha1_final(&mdctx, ldig.hv); 1142275970Scy isc_sha1_invalidate(&mdctx); 1143275970Scy 1144275970Scy if (0 > hlseen) 1145275970Scy return LSVALID_NOHASH; 1146275970Scy if (0 == hlseen) 1147275970Scy return LSVALID_BADFORMAT; 1148275970Scy if (0 != memcmp(&rdig, &ldig, sizeof(sha1_digest))) 1149275970Scy return LSVALID_BADHASH; 1150275970Scy return LSVALID_GOODHASH; 1151275970Scy} 1152275970Scy 1153275970Scy/* 1154275970Scy * lstostr - prettyprint NTP seconds 1155275970Scy */ 1156290000Sglebiusstatic const char * 1157290000Sglebiuslstostr( 1158275970Scy const vint64 * ts) 1159275970Scy{ 1160275970Scy char * buf; 1161275970Scy struct calendar tm; 1162275970Scy 1163275970Scy LIB_GETBUF(buf); 1164290000Sglebius 1165290000Sglebius if ( ! (ts->d_s.hi >= 0 && ntpcal_ntp64_to_date(&tm, ts) >= 0)) 1166290000Sglebius snprintf(buf, LIB_BUFLENGTH, "%s", "9999-12-31T23:59:59Z"); 1167290000Sglebius else 1168290000Sglebius snprintf(buf, LIB_BUFLENGTH, "%04d-%02d-%02dT%02d:%02d:%02dZ", 1169290000Sglebius tm.year, tm.month, tm.monthday, 1170290000Sglebius tm.hour, tm.minute, tm.second); 1171290000Sglebius 1172275970Scy return buf; 1173275970Scy} 1174275970Scy 1175290000Sglebius/* reset the global state for unit tests */ 1176290000Sglebiusvoid 1177290000Sglebiusleapsec_ut_pristine(void) 1178290000Sglebius{ 1179290000Sglebius memset(_ltab, 0, sizeof(_ltab)); 1180290000Sglebius _lptr = NULL; 1181290000Sglebius _electric = 0; 1182290000Sglebius} 1183275970Scy 1184275970Scy 1185290000Sglebius 1186275970Scy/* -*- that's all folks! -*- */ 1187