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