1 /*	$Id$ */
2 /*
3  * Copyright (c) 2016, 2017, 2020 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include "config.h"
18 
19 #include <assert.h> /* debug */
20 #include <inttypes.h>
21 #include <limits.h>
22 #include <stdarg.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 
29 #include "kcgi.h"
30 #include "extern.h"
31 
32 /*
33  * We'll never have khttp_epoch2str or khttp_epoch2ustr larger than
34  * this, even with 64-bit time.
35  */
36 #define MAX_TIME_STRING	64
37 
38 struct tm64 {
39 	int64_t	tm_sec;		/* seconds after the minute [0-60] */
40 	int64_t	tm_min;		/* minutes after the hour [0-59] */
41 	int64_t	tm_hour;	/* hours since midnight [0-23] */
42 	int64_t	tm_mday;	/* day of the month [1-31] */
43 	int64_t	tm_mon;		/* months since January [0-11] */
44 	int64_t	tm_year;	/* years since 1900 */
45 	int64_t	tm_wday;	/* days since Sunday [0-6] */
46 	int64_t	tm_yday;	/* days since January 1 [0-365] */
47 };
48 
49 /*
50  * The following code is modified from newlib, the relevant parts being
51  * licensed as follows:
52  *
53  * Copyright (c) 1994-2009  Red Hat, Inc. All rights reserved.
54  *
55  * This copyrighted material is made available to anyone wishing to use,
56  * modify, copy, or redistribute it subject to the terms and conditions
57  * of the BSD License.   This program is distributed in the hope that
58  * it will be useful, but WITHOUT ANY WARRANTY expressed or implied,
59  * including the implied warranties of MERCHANTABILITY or FITNESS FOR
60  * A PARTICULAR PURPOSE.  A copy of this license is available at
61  * http://www.opensource.org/licenses. Any Red Hat trademarks that are
62  * incorporated in the source code or documentation are not subject to
63  * the BSD License and may only be used or replicated with the express
64  * permission of Red Hat, Inc.
65  */
66 #define _SEC_IN_MINUTE 60L
67 #define _SEC_IN_HOUR 3600L
68 #define _SEC_IN_DAY 86400L
69 
70 static const int DAYS_IN_MONTH[12] =
71 	{31, 28, 31, 30, 31, 30,
72 	 31, 31, 30, 31, 30, 31};
73 
74 #define _DAYS_IN_MONTH(x) \
75 	((x == 1) ? days_in_feb : DAYS_IN_MONTH[x])
76 
77 static const int _DAYS_BEFORE_MONTH[12] =
78 	{0, 31, 59, 90, 120, 151,
79 	 181, 212, 243, 273, 304, 334};
80 
81 #define _ISLEAP(y) \
82 	(((y) % 4) == 0 && \
83 	 (((y) % 100) != 0 || (((y)+1900) % 400) == 0))
84 #define _DAYS_IN_YEAR(year) \
85 	(_ISLEAP(year) ? 366 : 365)
86 
87 /*
88  * There are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 *
89  * 366).
90  */
91 #define DAYS_PER_ERA		146097L
92 
93 /*
94  * Make sure that all values are sane.
95  * Return zero on failure, non-zero on success.
96  */
97 static int
khttp_validate_time(const struct tm64 * tim_p)98 khttp_validate_time(const struct tm64 *tim_p)
99 {
100 	int64_t	 days_in_feb = 28;
101 
102 	if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59)
103 		return 0;
104 	if (tim_p->tm_min < 0 || tim_p->tm_min > 59)
105 		return 0;
106 	if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23)
107 		return 0;
108 	if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11)
109 		return 0;
110 
111 	/*
112 	 * This magic number is (more or less) the maximum number of
113 	 * years that we can consider in a 64-bit year.
114 	 * Outside of this we'll get overflow or underflow.
115 	 */
116 
117 	if (tim_p->tm_year > 292277026596 ||
118 	    tim_p->tm_year < -292277024557)
119 		return 0;
120 
121 	if (_DAYS_IN_YEAR(tim_p->tm_year) == 366)
122 		days_in_feb = 29;
123 	if (tim_p->tm_mday <= 0 ||
124 	    tim_p->tm_mday > _DAYS_IN_MONTH(tim_p->tm_mon))
125 		return 0;
126 
127 	return 1;
128 }
129 
130 /*
131  * Convert broken-down time to the UNIX epoch.
132  * Returns zero if the broken-dwon values are not sane, non-zero
133  * otherwise.
134  * See khttp_validate_time().
135  */
136 static int
khttp_mktime(int64_t * res,struct tm64 * tim_p)137 khttp_mktime(int64_t *res, struct tm64 *tim_p)
138 {
139 	int64_t	tim = 0, days = 0, year, maxyear, era;
140 
141 	/* Validate structure. */
142 
143 	if (!khttp_validate_time(tim_p))
144 		return 0;
145 
146 	/* Compute hours, minutes, seconds. */
147 
148 	tim += tim_p->tm_sec +
149 		(tim_p->tm_min * _SEC_IN_MINUTE) +
150 		(tim_p->tm_hour * _SEC_IN_HOUR);
151 
152 	/* Compute days in year. */
153 
154 	days += tim_p->tm_mday - 1;
155 	days += _DAYS_BEFORE_MONTH[tim_p->tm_mon];
156 
157 	if (tim_p->tm_mon > 1 && _DAYS_IN_YEAR(tim_p->tm_year) == 366)
158 		days++;
159 
160 	/* Compute day of the year. */
161 
162 	tim_p->tm_yday = days;
163 
164 	/* Compute days in other years. */
165 	/* WARNING: THIS IS VERY SLOW. */
166 
167 	if ((year = tim_p->tm_year) > 70) {
168 		maxyear = tim_p->tm_year > 400 ? 400 : tim_p->tm_year;
169 		for (year = 70; year < maxyear; year++)
170 			days += _DAYS_IN_YEAR(year);
171 		era = (tim_p->tm_year - year) / 400;
172 		days += era * DAYS_PER_ERA;
173 		year += era * 400;
174 		for ( ; year < tim_p->tm_year; year++)
175 			days += _DAYS_IN_YEAR(year);
176 	} else if (year < 70) {
177 		maxyear = tim_p->tm_year < -400 ? -400 : tim_p->tm_year;
178 		for (year = 69; year > maxyear; year--)
179 			days -= _DAYS_IN_YEAR(year);
180 		era = (tim_p->tm_year - year) / 400;
181 		assert(era <= 0);
182 		days += era * DAYS_PER_ERA;
183 		year += era * 400;
184 		for ( ; year > tim_p->tm_year; year--)
185 			days -= _DAYS_IN_YEAR(year);
186 		days -= _DAYS_IN_YEAR(year);
187 	}
188 
189 	/* Compute total seconds. */
190 
191 	tim += days * _SEC_IN_DAY;
192 
193 	/* Compute day of the week. */
194 
195 	if ((tim_p->tm_wday = (days + 4) % 7) < 0)
196 		tim_p->tm_wday += 7;
197 
198 	*res = tim;
199 	return 1;
200 }
201 
202 /*
203  * Move epoch from 01.01.1970 to 01.03.0000 (yes, Year 0) - this is the
204  * first day of a 400-year long "era", right after additional day of
205  * leap year.  This adjustment is required only for date calculation, so
206  * instead of modifying time_t value (which would require 64-bit
207  * operations to work correctly) it's enough to adjust the calculated
208  * number of days since epoch.
209  */
210 #define EPOCH_ADJUSTMENT_DAYS	719468L
211 
212 /* Year to which the adjustment was made. */
213 #define ADJUSTED_EPOCH_YEAR	0
214 
215 /* 1st March of year 0 is Wednesday. */
216 #define ADJUSTED_EPOCH_WDAY	3
217 
218 /*
219  * There are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 *
220  * 366).
221  */
222 #define DAYS_PER_CENTURY	36524L
223 
224 /* There is one leap year every 4 years. */
225 #define DAYS_PER_4_YEARS	(3 * 365 + 366)
226 
227 /* Number of days in a non-leap year. */
228 #define DAYS_PER_YEAR		365
229 
230 /* Number of days in January. */
231 #define DAYS_IN_JANUARY		31
232 
233 /* Number of days in non-leap February. */
234 #define DAYS_IN_FEBRUARY	28
235 
236 /* Number of years per era. */
237 #define YEARS_PER_ERA		400
238 
239 /* Various constants. */
240 #define SECSPERMIN	60L
241 #define MINSPERHOUR	60L
242 #define HOURSPERDAY	24L
243 #define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
244 #define SECSPERDAY	(SECSPERHOUR * HOURSPERDAY)
245 #define YEAR_BASE	1900
246 #define DAYSPERWEEK	7
247 #define MONSPERYEAR	12
248 
249 /*
250  * This form of _ISLEAP doesn't adjust the year.
251  */
252 #define _ISLEAP2(y) \
253 	(((y) % 4) == 0 && \
254 	 (((y) % 100) != 0 || ((y) % 400) == 0))
255 
256 /*
257  * Convert UNIX epoch to values in "res".
258  */
259 static void
khttp_gmtime_r(int64_t lcltime,struct tm64 * res)260 khttp_gmtime_r(int64_t lcltime, struct tm64 *res)
261 {
262 	int64_t		days, rem, era, weekday, year;
263 	uint64_t	erayear, yearday, month, day, eraday;
264 
265 	memset(res, 0, sizeof(struct tm64));
266 
267 	days = lcltime / SECSPERDAY + EPOCH_ADJUSTMENT_DAYS;
268 	rem = lcltime % SECSPERDAY;
269 
270 	if (rem < 0) {
271 		rem += SECSPERDAY;
272 		--days;
273 	}
274 
275 	/* Compute hour, min, and sec. */
276 
277 	res->tm_hour = (int64_t)(rem / SECSPERHOUR);
278 	rem %= SECSPERHOUR;
279 	res->tm_min = (int64_t)(rem / SECSPERMIN);
280 	res->tm_sec = (int64_t)(rem % SECSPERMIN);
281 
282 	/* Compute day of week. */
283 
284 	if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
285 		weekday += DAYSPERWEEK;
286 
287 	res->tm_wday = weekday;
288 
289 	/*
290 	 * Compute year, month, day & day of year.
291 	 * For description of this algorithm see
292 	 * http://howardhinnant.github.io/date_algorithms.html#civil_from_days
293 	 */
294 
295 	era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
296 
297 	/* [0, 146096] */
298 	eraday = days - era * DAYS_PER_ERA;
299 
300 	/* [0, 399] */
301 	erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) +
302 			eraday / DAYS_PER_CENTURY - eraday /
303 			(DAYS_PER_ERA - 1)) / 365;
304 
305 	/* [0, 365] */
306 	yearday = eraday - (DAYS_PER_YEAR * erayear +
307 			erayear / 4 - erayear / 100);
308 
309 	/* [0, 11] */
310 	month = (5 * yearday + 2) / 153;
311 
312 	/* [1, 31] */
313 	day = yearday - (153 * month + 2) / 5 + 1;
314 
315 	month += month < 10 ? 2 : -10;
316 	year = ADJUSTED_EPOCH_YEAR + erayear +
317 		era * YEARS_PER_ERA + (month <= 1);
318 
319 	res->tm_yday =
320 		yearday >= DAYS_PER_YEAR -
321 			DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ?
322 		yearday - (DAYS_PER_YEAR -
323 			DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) :
324 		yearday + DAYS_IN_JANUARY +
325 			DAYS_IN_FEBRUARY + _ISLEAP2(erayear);
326 	res->tm_year = year - YEAR_BASE;
327 	res->tm_mon = month;
328 	res->tm_mday = day;
329 }
330 
331 char *
khttp_epoch2str(int64_t tt,char * buf,size_t sz)332 khttp_epoch2str(int64_t tt, char *buf, size_t sz)
333 {
334 	struct tm64	 tm;
335 	char		 rbuf[MAX_TIME_STRING];
336 	const char	*days[7] = {
337 		"Sun",
338 		"Mon",
339 		"Tue",
340 		"Wed",
341 		"Thu",
342 		"Fri",
343 		"Sat"
344 	};
345 	const char	*months[12] = {
346 		"Jan",
347 		"Feb",
348 		"Mar",
349 		"Apr",
350 		"May",
351 		"Jun",
352 		"Jul",
353 		"Aug",
354 		"Sep",
355 		"Oct",
356 		"Nov",
357 		"Dec"
358 	};
359 
360 	if (buf == NULL || sz == 0)
361 		return NULL;
362 
363 	khttp_gmtime_r(tt, &tm);
364 
365 	if (snprintf(rbuf, sizeof(rbuf),
366 	    "%s, %.2" PRId64 " %s %.4" PRId64 " "
367 	    "%.2" PRId64 ":%.2" PRId64 ":%.2" PRId64 " GMT",
368 	    days[tm.tm_wday], tm.tm_mday,
369 	    months[tm.tm_mon], tm.tm_year + 1900,
370 	    tm.tm_hour, tm.tm_min, tm.tm_sec) == -1) {
371 		kutil_warn(NULL, NULL, "snprintf");
372 		return NULL;
373 	}
374 
375 	strlcpy(buf, rbuf, sz);
376 	return buf;
377 }
378 
379 /*
380  * Deprecated interface simply zeroes the time structure if it's
381  * negative.  The original implementation did not do this (it set the
382  * "struct tm" to zero), but the documentation still said otherwise.
383  * This uses the original documented behaviour.
384  */
385 char *
kutil_epoch2str(int64_t tt,char * buf,size_t sz)386 kutil_epoch2str(int64_t tt, char *buf, size_t sz)
387 {
388 
389 	return khttp_epoch2str(tt < 0 ? 0 : tt, buf, sz);
390 }
391 
392 char *
khttp_epoch2ustr(int64_t tt,char * buf,size_t sz)393 khttp_epoch2ustr(int64_t tt, char *buf, size_t sz)
394 {
395 	char		 rbuf[MAX_TIME_STRING];
396 	struct tm64	 tm;
397 
398 	if (buf == NULL || sz == 0)
399 		return NULL;
400 
401 	khttp_gmtime_r(tt, &tm);
402 
403 	if (snprintf(rbuf, sizeof(rbuf),
404 	    "%.4" PRId64 "-%.2" PRId64 "-%.2" PRId64
405 	    "T%.2" PRId64 ":%.2" PRId64 ":%.2" PRId64 "Z",
406 	    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
407 	    tm.tm_hour, tm.tm_min, tm.tm_sec) == -1) {
408 		kutil_warn(NULL, NULL, "snprintf");
409 		return NULL;
410 	}
411 
412 	strlcpy(buf, rbuf, sz);
413 	return buf;
414 }
415 
416 /*
417  * Deprecated.
418  * See kutil_epoch2str() for behaviour notes.
419  */
420 char *
kutil_epoch2utcstr(int64_t tt,char * buf,size_t sz)421 kutil_epoch2utcstr(int64_t tt, char *buf, size_t sz)
422 {
423 
424 	return khttp_epoch2ustr(tt < 0 ? 0 : tt, buf, sz);
425 }
426 
427 void
khttp_epoch2datetime(int64_t tt,int64_t * tm_sec,int64_t * tm_min,int64_t * tm_hour,int64_t * tm_mday,int64_t * tm_mon,int64_t * tm_year,int64_t * tm_wday,int64_t * tm_yday)428 khttp_epoch2datetime(int64_t tt, int64_t *tm_sec, int64_t *tm_min,
429 	int64_t *tm_hour, int64_t *tm_mday, int64_t *tm_mon,
430 	int64_t *tm_year, int64_t *tm_wday, int64_t *tm_yday)
431 {
432 	struct tm64	tm;
433 
434 	khttp_gmtime_r(tt, &tm);
435 
436 	if (tm_sec != NULL)
437 		*tm_sec = tm.tm_sec;
438 	if (tm_min != NULL)
439 		*tm_min = tm.tm_min;
440 	if (tm_hour != NULL)
441 		*tm_hour = tm.tm_hour;
442 	if (tm_mday != NULL)
443 		*tm_mday = tm.tm_mday;
444 	if (tm_mon != NULL)
445 		*tm_mon = tm.tm_mon + 1;
446 	if (tm_year != NULL)
447 		*tm_year = tm.tm_year + 1900;
448 	if (tm_wday != NULL)
449 		*tm_wday = tm.tm_wday;
450 	if (tm_yday != NULL)
451 		*tm_yday = tm.tm_yday;
452 }
453 
454 int
khttp_epoch2tms(int64_t tt,int * tm_sec,int * tm_min,int * tm_hour,int * tm_mday,int * tm_mon,int * tm_year,int * tm_wday,int * tm_yday)455 khttp_epoch2tms(int64_t tt, int *tm_sec, int *tm_min,
456 	int *tm_hour, int *tm_mday, int *tm_mon,
457 	int *tm_year, int *tm_wday, int *tm_yday)
458 {
459 	struct tm64	tm;
460 
461 	khttp_gmtime_r(tt, &tm);
462 
463 	if (tm.tm_year > INT_MAX || tm.tm_year < -INT_MAX)
464 		return 0;
465 	if (tm_sec != NULL)
466 		*tm_sec = tm.tm_sec;
467 	if (tm_min != NULL)
468 		*tm_min = tm.tm_min;
469 	if (tm_hour != NULL)
470 		*tm_hour = tm.tm_hour;
471 	if (tm_mday != NULL)
472 		*tm_mday = tm.tm_mday;
473 	if (tm_mon != NULL)
474 		*tm_mon = tm.tm_mon;
475 	if (tm_year != NULL)
476 		*tm_year = tm.tm_year;
477 	if (tm_wday != NULL)
478 		*tm_wday = tm.tm_wday;
479 	if (tm_yday != NULL)
480 		*tm_yday = tm.tm_yday;
481 
482 	return 1;
483 }
484 
485 /*
486  * Deprecated.
487  * This has a bad corner case where gmtime() returns NULL and we just
488  * assume the epoch---this wasn't present in the earlier version of this
489  * because it was using a hand-rolled gmtime() that didn't fail.  This
490  * is suitably unlikely that it's ok, as it's deprecated.
491  */
492 void
kutil_epoch2tmvals(int64_t tt,int * tm_sec,int * tm_min,int * tm_hour,int * tm_mday,int * tm_mon,int * tm_year,int * tm_wday,int * tm_yday)493 kutil_epoch2tmvals(int64_t tt, int *tm_sec, int *tm_min,
494 	int *tm_hour, int *tm_mday, int *tm_mon,
495 	int *tm_year, int *tm_wday, int *tm_yday)
496 {
497 
498 	khttp_epoch2tms(tt < 0 ? 0 : tt, tm_sec, tm_min,
499 		tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday);
500 }
501 
502 int
khttp_datetime2epoch(int64_t * res,int64_t day,int64_t mon,int64_t year,int64_t hour,int64_t min,int64_t sec)503 khttp_datetime2epoch(int64_t *res, int64_t day, int64_t mon,
504 	int64_t year, int64_t hour, int64_t min, int64_t sec)
505 {
506 	struct tm64	 tm;
507 	int64_t		 val;
508 
509 	if (res == NULL)
510 		res = &val;
511 
512 	memset(&tm, 0, sizeof(struct tm64));
513 	tm.tm_sec = sec;
514 	tm.tm_min = min;
515 	tm.tm_hour = hour;
516 	tm.tm_mday = day;
517 	tm.tm_mon = mon - 1;
518 	tm.tm_year = year - 1900;
519 	return khttp_mktime(res, &tm);
520 }
521 
522 int
khttp_date2epoch(int64_t * res,int64_t day,int64_t mon,int64_t year)523 khttp_date2epoch(int64_t *res, int64_t day, int64_t mon, int64_t year)
524 {
525 
526 	return khttp_datetime2epoch(res, day, mon, year, 0, 0, 0);
527 }
528 
529 /*
530  * Deprecated interface.
531  */
532 int64_t
kutil_date2epoch(int64_t day,int64_t mon,int64_t year)533 kutil_date2epoch(int64_t day, int64_t mon, int64_t year)
534 {
535 	int64_t	 res;
536 
537 	if (!khttp_date2epoch(&res, day, mon, year))
538 		return -1;
539 
540 	return res;
541 }
542 
543 /*
544  * Deprecated interface.
545  */
546 int
kutil_datetime_check(int64_t day,int64_t mon,int64_t year,int64_t hour,int64_t min,int64_t sec)547 kutil_datetime_check(int64_t day, int64_t mon, int64_t year,
548 	int64_t hour, int64_t min, int64_t sec)
549 {
550 
551 	return khttp_datetime2epoch
552 		(NULL, day, mon, year, hour, min, sec);
553 }
554 
555 /*
556  * Deprecated interface.
557  */
558 int
kutil_date_check(int64_t day,int64_t mon,int64_t year)559 kutil_date_check(int64_t day, int64_t mon, int64_t year)
560 {
561 
562 	return khttp_date2epoch(NULL, day, mon, year);
563 }
564 
565 /*
566  * Deprecated interface.
567  */
568 int64_t
kutil_datetime2epoch(int64_t day,int64_t mon,int64_t year,int64_t hour,int64_t min,int64_t sec)569 kutil_datetime2epoch(int64_t day, int64_t mon, int64_t year,
570 	int64_t hour, int64_t min, int64_t sec)
571 {
572 	int64_t	 res;
573 
574 	if (!khttp_datetime2epoch
575 	    (&res, day, mon, year, hour, min, sec))
576 		return -1;
577 
578 	return res;
579 }
580