1 /* check_y2k.c -- test ntp code constructs for Y2K correctness Y2KFixes [*/ 2 3 /* 4 Code invoked by `make check`. Not part of ntpd and not to be 5 installed. 6 7 On any code I even wonder about, I've cut and pasted the code 8 here and ran it as a test case just to be sure. 9 10 For code not in "ntpd" proper, we have tried to call most 11 repaired functions from herein to properly test them 12 (something never done before!). This has found several bugs, 13 not normal Y2K bugs, that will strike in Y2K so repair them 14 we did. 15 16 Program exits with 0 on success, 1 on Y2K failure (stdout messages). 17 Exit of 2 indicates internal logic bug detected OR failure of 18 what should be our correct formulas. 19 20 While "make check" should only check logic for source within that 21 specific directory, this check goes outside the scope of the local 22 directory. It's not a perfect world (besides, there is a lot of 23 interdependence here, and it really needs to be tested in 24 a controled order). 25 */ 26 27 /* { definitions lifted from ntpd.c to allow us to complie with 28 "#include ntp.h". I have not taken the time to reduce the clutter. */ 29 30 #ifdef HAVE_CONFIG_H 31 # include <config.h> 32 #endif 33 34 #include <sys/types.h> 35 #ifdef HAVE_UNISTD_H 36 # include <unistd.h> 37 #endif 38 #ifdef HAVE_SYS_STAT_H 39 # include <sys/stat.h> 40 #endif 41 #include <stdio.h> 42 #include <errno.h> 43 #ifndef SYS_WINNT 44 # if !defined(VMS) /*wjm*/ 45 # include <sys/param.h> 46 # endif /* VMS */ 47 # include <sys/signal.h> 48 # ifdef HAVE_SYS_IOCTL_H 49 # include <sys/ioctl.h> 50 # endif /* HAVE_SYS_IOCTL_H */ 51 # include <sys/time.h> 52 # if !defined(VMS) /*wjm*/ 53 # include <sys/resource.h> 54 # endif /* VMS */ 55 #else 56 # include <signal.h> 57 # include <process.h> 58 # include <io.h> 59 # include "../libntp/log.h" 60 #endif /* SYS_WINNT */ 61 #if defined(HAVE_RTPRIO) 62 # ifdef HAVE_SYS_RESOURCE_H 63 # include <sys/resource.h> 64 # endif 65 # ifdef HAVE_SYS_LOCK_H 66 # include <sys/lock.h> 67 # endif 68 # include <sys/rtprio.h> 69 #else 70 # ifdef HAVE_PLOCK 71 # ifdef HAVE_SYS_LOCK_H 72 # include <sys/lock.h> 73 # endif 74 # endif 75 #endif 76 #if defined(HAVE_SCHED_SETSCHEDULER) 77 # ifdef HAVE_SCHED_H 78 # include <sched.h> 79 # else 80 # ifdef HAVE_SYS_SCHED_H 81 # include <sys/sched.h> 82 # endif 83 # endif 84 #endif 85 #if defined(HAVE_SYS_MMAN_H) 86 # include <sys/mman.h> 87 #endif 88 89 #ifdef HAVE_TERMIOS_H 90 # include <termios.h> 91 #endif 92 93 #ifdef SYS_DOMAINOS 94 # include <apollo/base.h> 95 #endif /* SYS_DOMAINOS */ 96 97 #include "ntpd.h" 98 99 /* } end definitions lifted from ntpd.c */ 100 101 #include "ntp_calendar.h" 102 #include "parse.h" 103 104 #define GoodLeap(Year) (((Year)%4 || (!((Year)%100) && (Year)%400)) ? 0 : 13 ) 105 106 int debug = 0; /* debugging requests for parse stuff */ 107 char const *progname = "check_y2k"; 108 109 long Days ( int Year ) /* return number of days since year "0" */ 110 { 111 long Return; 112 /* this is a known to be good algorithm */ 113 Return = Year * 365; /* first aproximation to the value */ 114 if ( Year >= 1 ) 115 { /* see notes in libparse/parse.c if you want a PROPER 116 * **generic algorithm. */ 117 Return += (Year+3) / 4; /* add in (too many) leap days */ 118 Return -= (Year-1) / 100; /* reduce by (too many) centurys */ 119 Return += (Year-1) / 400; /* get final answer */ 120 } 121 122 return Return; 123 } 124 125 static int year0 = 1900; /* sarting year for NTP time */ 126 static int yearend; /* ending year we test for NTP time. 127 * 32-bit systems: through 2036, the 128 **year in which NTP time overflows. 129 * 64-bit systems: a reasonable upper 130 **limit (well, maybe somewhat beyond 131 **reasonable, but well before the 132 **max time, by which time the earth 133 **will be dead.) */ 134 static time_t Time; 135 static struct tm LocalTime; 136 137 #define Error(year) if ( (year)>=2036 && LocalTime.tm_year < 110 ) \ 138 Warnings++; else Fatals++ 139 140 int main( void ) 141 { 142 int Fatals; 143 int Warnings; 144 int year; 145 146 Time = time( (time_t *)NULL ) 147 #ifdef TESTTIMEOFFSET 148 + test_time_offset 149 #endif 150 ; 151 LocalTime = *localtime( &Time ); 152 153 year = ( sizeof( u_long ) > 4 ) /* save max span using year as temp */ 154 ? ( 400 * 3 ) /* three greater gregorian cycles */ 155 : ((int)(0x7FFFFFFF / 365.242 / 24/60/60)* 2 ); /*32-bit limit*/ 156 /* NOTE: will automacially expand test years on 157 * 64 bit machines.... this may cause some of the 158 * existing ntp logic to fail for years beyond 159 * 2036 (the current 32-bit limit). If all checks 160 * fail ONLY beyond year 2036 you may ignore such 161 * errors, at least for a decade or so. */ 162 yearend = year0 + year; 163 164 puts( " internal self check" ); 165 { /* verify our own logic used to verify repairs */ 166 unsigned long days; 167 168 if ( year0 >= yearend ) 169 { 170 fprintf( stdout, "year0=%d NOT LESS THAN yearend=%d (span=%d)\n", 171 (int)year0, (int)yearend, (int)year ); 172 exit(2); 173 } 174 175 { 176 int save_year; 177 178 save_year = LocalTime.tm_year; /* save current year */ 179 180 year = 1980; 181 LocalTime.tm_year = year - 1900; 182 Fatals = Warnings = 0; 183 Error(year); /* should increment Fatals */ 184 if ( Fatals == 0 ) 185 { 186 fprintf( stdout, 187 "%4d: %s(%d): FATAL DID NOT INCREMENT (Fatals=%d Warnings=%d)\n", 188 (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings ); 189 exit(2); 190 } 191 192 year = 2100; /* test year > limit but CURRENT year < limit */ 193 Fatals = Warnings = 0; 194 Error(year); /* should increment Fatals */ 195 if ( Warnings == 0 ) 196 { 197 fprintf( stdout, 198 "%4d: %s(%d): WARNING DID NOT INCREMENT (Fatals=%d Warnings=%d)\n", 199 (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings ); 200 exit(2); 201 } 202 Fatals = Warnings = 0; 203 LocalTime.tm_year = year - 1900; /* everything > limit */ 204 Error(1980); /* should increment Fatals */ 205 if ( Fatals == 0 ) 206 { 207 fprintf( stdout, 208 "%4d: %s(%d): FATALS DID NOT INCREMENT (Fatals=%d Warnings=%d)\n", 209 (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings ); 210 exit(2); 211 } 212 213 LocalTime.tm_year = save_year; 214 } 215 216 days = 365+1; /* days in year 0 + 1 more day */ 217 for ( year = 1; year <= 2500; year++ ) 218 { 219 long Test; 220 Test = Days( year ); 221 if ( days != Test ) 222 { 223 fprintf( stdout, "%04d: Days() DAY COUNT ERROR: s/b=%ld was=%ld\n", 224 year, (long)days, (long)Test ); 225 exit(2); /* would throw off many other tests */ 226 } 227 228 Test = julian0(year); /* compare with julian0() macro */ 229 if ( days != Test ) 230 { 231 fprintf( stdout, "%04d: julian0() DAY COUNT ERROR: s/b=%ld was=%ld\n", 232 year, (long)days, (long)Test ); 233 exit(2); /* would throw off many other tests */ 234 } 235 236 days += 365; 237 if ( isleap_4(year) ) days++; 238 } 239 240 if ( isleap_4(1999) ) 241 { 242 fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" ); 243 exit(2); 244 } 245 if ( !isleap_4(2000) ) 246 { 247 fprintf( stdout, "isleap_4(2000) REPORTED FALSE\n" ); 248 exit(2); 249 } 250 if ( isleap_4(2001) ) 251 { 252 fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" ); 253 exit(2); 254 } 255 256 if ( !isleap_tm(2000-1900) ) 257 { 258 fprintf( stdout, "isleap_tm(100) REPORTED FALSE\n" ); 259 exit(2); 260 } 261 } 262 263 Fatals = Warnings = 0; 264 265 puts( " include/ntp.h" ); 266 { /* test our new isleap_*() #define "functions" */ 267 268 for ( year = 1400; year <= 2200; year++ ) 269 { 270 int LeapSw; 271 int IsLeapSw; 272 273 LeapSw = GoodLeap(year); 274 IsLeapSw = isleap_4(year); 275 276 if ( !!LeapSw != !!IsLeapSw ) 277 { 278 Error(year); 279 fprintf( stdout, 280 " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw ); 281 break; 282 } 283 284 IsLeapSw = isleap_tm(year-1900); 285 286 if ( !!LeapSw != !!IsLeapSw ) 287 { 288 Error(year); 289 fprintf( stdout, 290 " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw ); 291 break; 292 } 293 } 294 } 295 296 puts( " include/ntp_calendar.h" ); 297 { /* I belive this is good, but just to be sure... */ 298 299 /* we are testing this #define */ 300 #define is_leapyear(y) (y%4 == 0 && !(y%100 == 0 && !(y%400 == 0))) 301 302 for ( year = 1400; year <= 2200; year++ ) 303 { 304 int LeapSw; 305 306 LeapSw = GoodLeap(year); 307 308 if ( !(!LeapSw) != !(!is_leapyear(year)) ) 309 { 310 Error(year); 311 fprintf( stdout, 312 " %4d %2d *** ERROR\n", year, LeapSw ); 313 break; 314 } 315 } 316 } 317 318 319 puts( " libparse/parse.c" ); 320 { 321 long Days1970; /* days from 1900 to 1970 */ 322 323 struct ParseTime /* womp up a test structure to all cut/paste code */ 324 { 325 int year; 326 } Clock_Time, *clock_time; 327 328 clock_time = &Clock_Time; 329 330 /* first test this #define */ 331 #define days_per_year(x) ((x) % 4 ? 365 : ((x % 400) ? ((x % 100) ? 366 : 365) : 366)) 332 333 for ( year = 1400; year <= 2200; year++ ) 334 { 335 int LeapSw; 336 int DayCnt; 337 338 LeapSw = GoodLeap(year); 339 DayCnt = (int)days_per_year(year); 340 341 if ( ( LeapSw ? 366 : 365 ) != DayCnt ) 342 { 343 Error(year); 344 fprintf( stdout, 345 " days_per_year() %4d %2d %3d *** ERROR\n", 346 year, LeapSw, DayCnt ); 347 break; 348 } 349 } 350 351 /* test (what is now julian0) calculations */ 352 353 Days1970 = Days( 1970 ); /* get days since 1970 using a known good */ 354 355 for ( year = 1970; year < yearend; year++ ) 356 { 357 unsigned long t; 358 long DaysYear ; 359 360 clock_time->year = year; 361 362 /* here is the code we are testing, cut and pasted out of the source */ 363 #if 0 /* old BUGGY code that has Y2K (and many other) failures */ 364 /* ghealton: this logic FAILED with great frequency when run 365 * over a period of time, including for year 2000. True, it 366 * had more successes than failures, but that's not really good 367 * enough for critical time distribution software. 368 * It is so awful I wonder if it has had a history of failure 369 * and fixes? */ 370 t = (clock_time->year - 1970) * 365; 371 t += (clock_time->year >> 2) - (1970 >> 2); 372 t -= clock_time->year / 100 - 1970 / 100; 373 t += clock_time->year / 400 - 1970 / 400; 374 375 /* (immediate feare of rounding errors on integer 376 * **divisions proved well founded) */ 377 378 #else 379 /* my replacement, based on Days() above */ 380 t = julian0(year) - julian0(1970); 381 #endif 382 383 /* compare result in t against trusted calculations */ 384 DaysYear = Days( year ); /* get days to this year */ 385 if ( t != DaysYear - Days1970 ) 386 { 387 Error(year); 388 fprintf( stdout, 389 " %4d 1970=%-8ld %4d=%-8ld %-3ld t=%-8ld *** ERROR ***\n", 390 year, (long)Days1970, 391 year, 392 (long)DaysYear, 393 (long)(DaysYear - Days1970), 394 (long)t ); 395 } 396 } 397 398 #if 1 /* { */ 399 { 400 debug = 1; /* enable debugging */ 401 for ( year = 1970; year < yearend; year++ ) 402 { /* (limited by theory unix 2038 related bug lives by, but 403 * ends in yearend) */ 404 clocktime_t ct; 405 time_t Observed; 406 time_t Expected; 407 u_long Flag; 408 unsigned long t; 409 410 ct.day = 1; 411 ct.month = 1; 412 ct.year = year; 413 ct.hour = ct.minute = ct.second = ct.usecond = 0; 414 ct.utcoffset = 0; 415 ct.utctime = 0; 416 ct.flags = 0; 417 418 Flag = 0; 419 Observed = parse_to_unixtime( &ct, &Flag ); 420 if ( ct.year != year ) 421 { 422 fprintf( stdout, 423 "%04d: parse_to_unixtime(,%d) CORRUPTED ct.year: was %d\n", 424 (int)year, (int)Flag, (int)ct.year ); 425 Error(year); 426 break; 427 } 428 t = julian0(year) - julian0(1970); /* Julian day from 1970 */ 429 Expected = t * 24 * 60 * 60; 430 if ( Observed != Expected || Flag ) 431 { /* time difference */ 432 fprintf( stdout, 433 "%04d: parse_to_unixtime(,%d) FAILURE: was=%lu s/b=%lu (%ld)\n", 434 year, (int)Flag, 435 (unsigned long)Observed, (unsigned long)Expected, 436 ((long)Observed - (long)Expected) ); 437 Error(year); 438 break; 439 } 440 441 if ( year >= YEAR_PIVOT+1900 ) 442 { 443 /* check year % 100 code we put into parse_to_unixtime() */ 444 ct.utctime = 0; 445 ct.year = year % 100; 446 Flag = 0; 447 448 Observed = parse_to_unixtime( &ct, &Flag ); 449 450 if ( Observed != Expected || Flag ) 451 { /* time difference */ 452 fprintf( stdout, 453 "%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n", 454 year, (int)ct.year, (int)Flag, 455 (unsigned long)Observed, (unsigned long)Expected, 456 ((long)Observed - (long)Expected) ); 457 Error(year); 458 break; 459 } 460 461 /* check year - 1900 code we put into parse_to_unixtime() */ 462 ct.utctime = 0; 463 ct.year = year - 1900; 464 Flag = 0; 465 466 Observed = parse_to_unixtime( &ct, &Flag ); 467 468 if ( Observed != Expected || Flag ) 469 { /* time difference */ 470 fprintf( stdout, 471 "%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n", 472 year, (int)ct.year, (int)Flag, 473 (unsigned long)Observed, (unsigned long)Expected, 474 ((long)Observed - (long)Expected) ); 475 Error(year); 476 break; 477 } 478 479 480 } 481 } 482 #endif /* } */ 483 } 484 } 485 486 puts( " libntp/caljulian.c" ); 487 { /* test caljulian() */ 488 struct calendar ot; 489 u_long ntp_time; /* NTP time */ 490 491 year = year0; /* calculate the basic year */ 492 printf( " starting year %04d\n", (int)year0 ); 493 printf( " ending year %04d\n", (int)yearend ); 494 495 496 ntp_time = julian0( year0 ); /* NTP starts in 1900-01-01 */ 497 #if DAY_NTP_STARTS == 693596 498 ntp_time -= 365; /* BIAS required for successful test */ 499 #endif 500 if ( DAY_NTP_STARTS != ntp_time ) 501 { 502 Error(year); 503 fprintf( stdout, 504 "%04d: DAY_NTP_STARTS (%ld) NOT TRUE VALUE OF %ld (%ld)\n", 505 (int)year0, 506 (long)DAY_NTP_STARTS, (long)ntp_time, 507 (long)DAY_NTP_STARTS - (long)ntp_time ); 508 } 509 510 for ( ; year < yearend; year++ ) 511 { 512 513 /* 01-01 for the current year */ 514 ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */ 515 ntp_time *= 24 * 60 * 60; /* convert into seconds */ 516 caljulian( ntp_time, &ot ); /* convert January 1 */ 517 if ( ot.year != year 518 || ot.month != 1 519 || ot.monthday != 1 ) 520 { 521 Error(year); 522 fprintf( stdout, "%lu: EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n", 523 (unsigned long)ntp_time, 524 year, 525 (int)ot.year, (int)ot.month, (int)ot.monthday ); 526 break; 527 } 528 529 ntp_time += (31 + 28-1) * ( 24 * 60 * 60 ); /* advance to 02-28 */ 530 caljulian( ntp_time, &ot ); /* convert Feb 28 */ 531 if ( ot.year != year 532 || ot.month != 2 533 || ot.monthday != 28 ) 534 { 535 Error(year); 536 fprintf( stdout, "%lu: EXPECTED %04d-02-28: FOUND %04d-%02d-%02d\n", 537 (unsigned long)ntp_time, 538 year, 539 (int)ot.year, (int)ot.month, (int)ot.monthday ); 540 break; 541 } 542 543 { 544 int m; /* expected month */ 545 int d; /* expected day */ 546 547 m = isleap_4(year) ? 2 : 3; 548 d = isleap_4(year) ? 29 : 1; 549 550 ntp_time += ( 24 * 60 * 60 ); /* advance to the next day */ 551 caljulian( ntp_time, &ot ); /* convert this day */ 552 if ( ot.year != year 553 || ot.month != m 554 || ot.monthday != d ) 555 { 556 Error(year); 557 fprintf( stdout, "%lu: EXPECTED %04d-%02d-%02d: FOUND %04d-%02d-%02d\n", 558 (unsigned long)ntp_time, 559 year, m, d, 560 (int)ot.year, (int)ot.month, (int)ot.monthday ); 561 break; 562 } 563 564 } 565 } 566 } 567 568 puts( " libntp/caltontp.c" ); 569 { /* test caltontp() */ 570 struct calendar ot; 571 u_long ntp_time; /* NTP time */ 572 573 year = year0; /* calculate the basic year */ 574 printf( " starting year %04d\n", (int)year0 ); 575 printf( " ending year %04d\n", (int)yearend ); 576 577 578 for ( ; year < yearend; year++ ) 579 { 580 u_long ObservedNtp; 581 582 /* 01-01 for the current year */ 583 ot.year = year; 584 ot.month = ot.monthday = 1; /* unused, but set anyway JIC */ 585 ot.yearday = 1; /* this is the magic value used by caltontp() */ 586 ot.hour = ot.minute = ot.second = 0; 587 588 ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */ 589 ntp_time *= 24 * 60 * 60; /* convert into seconds */ 590 ObservedNtp = caltontp( &ot ); 591 if ( ntp_time != ObservedNtp ) 592 { 593 Error(year); 594 fprintf( stdout, "%d: EXPECTED %lu: FOUND %lu (%ld)\n", 595 (int)year, 596 (unsigned long)ntp_time, (unsigned long)ObservedNtp , 597 (long)ntp_time - (long)ObservedNtp ); 598 599 break; 600 } 601 602 /* now call caljulian as a type of failsafe supercheck */ 603 caljulian( ObservedNtp, &ot ); /* convert January 1 */ 604 if ( ot.year != year 605 || ot.month != 1 606 || ot.monthday != 1 ) 607 { 608 Error(year); 609 fprintf( stdout, "%lu: caljulian FAILSAFE EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n", 610 (unsigned long)ObservedNtp, 611 year, 612 (int)ot.year, (int)ot.month, (int)ot.monthday ); 613 break; 614 } 615 } 616 } 617 618 if ( Warnings > 0 ) 619 fprintf( stdout, "%d WARNINGS\n", Warnings ); 620 if ( Fatals > 0 ) 621 fprintf( stdout, "%d FATAL ERRORS\n", Fatals ); 622 return Fatals ? 1 : 0; 623 } 624 /* Y2KFixes ] */ 625