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