Line data Source code
1 : /* crypto/o_time.c -*- mode:C; c-file-style: "eay" -*- */
2 : /*
3 : * Written by Richard Levitte (richard@levitte.org) for the OpenSSL project
4 : * 2001.
5 : */
6 : /*
7 : * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project
8 : * 2008.
9 : */
10 : /* ====================================================================
11 : * Copyright (c) 2001 The OpenSSL Project. All rights reserved.
12 : *
13 : * Redistribution and use in source and binary forms, with or without
14 : * modification, are permitted provided that the following conditions
15 : * are met:
16 : *
17 : * 1. Redistributions of source code must retain the above copyright
18 : * notice, this list of conditions and the following disclaimer.
19 : *
20 : * 2. Redistributions in binary form must reproduce the above copyright
21 : * notice, this list of conditions and the following disclaimer in
22 : * the documentation and/or other materials provided with the
23 : * distribution.
24 : *
25 : * 3. All advertising materials mentioning features or use of this
26 : * software must display the following acknowledgment:
27 : * "This product includes software developed by the OpenSSL Project
28 : * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
29 : *
30 : * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
31 : * endorse or promote products derived from this software without
32 : * prior written permission. For written permission, please contact
33 : * licensing@OpenSSL.org.
34 : *
35 : * 5. Products derived from this software may not be called "OpenSSL"
36 : * nor may "OpenSSL" appear in their names without prior written
37 : * permission of the OpenSSL Project.
38 : *
39 : * 6. Redistributions of any form whatsoever must retain the following
40 : * acknowledgment:
41 : * "This product includes software developed by the OpenSSL Project
42 : * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
43 : *
44 : * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
45 : * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
47 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
48 : * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
49 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
50 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
51 : * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
53 : * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
55 : * OF THE POSSIBILITY OF SUCH DAMAGE.
56 : * ====================================================================
57 : *
58 : * This product includes cryptographic software written by Eric Young
59 : * (eay@cryptsoft.com). This product includes software written by Tim
60 : * Hudson (tjh@cryptsoft.com).
61 : *
62 : */
63 :
64 : #include <openssl/e_os2.h>
65 : #include <string.h>
66 : #include "o_time.h"
67 :
68 : #ifdef OPENSSL_SYS_VMS
69 : # if __CRTL_VER >= 70000000 && \
70 : (defined _POSIX_C_SOURCE || !defined _ANSI_C_SOURCE)
71 : # define VMS_GMTIME_OK
72 : # endif
73 : # ifndef VMS_GMTIME_OK
74 : # include <libdtdef.h>
75 : # include <lib$routines.h>
76 : # include <lnmdef.h>
77 : # include <starlet.h>
78 : # include <descrip.h>
79 : # include <stdlib.h>
80 : # endif /* ndef VMS_GMTIME_OK */
81 : #endif
82 :
83 1480 : struct tm *OPENSSL_gmtime(const time_t *timer, struct tm *result)
84 : {
85 : struct tm *ts = NULL;
86 :
87 : #if defined(OPENSSL_THREADS) && !defined(OPENSSL_SYS_WIN32) && !defined(OPENSSL_SYS_OS2) && (!defined(OPENSSL_SYS_VMS) || defined(gmtime_r)) && !defined(OPENSSL_SYS_MACOSX) && !defined(OPENSSL_SYS_SUNOS)
88 : /*
89 : * should return &data, but doesn't on some systems, so we don't even
90 : * look at the return value
91 : */
92 1480 : gmtime_r(timer, result);
93 : ts = result;
94 : #elif !defined(OPENSSL_SYS_VMS) || defined(VMS_GMTIME_OK)
95 : ts = gmtime(timer);
96 : if (ts == NULL)
97 : return NULL;
98 :
99 : memcpy(result, ts, sizeof(struct tm));
100 : ts = result;
101 : #endif
102 : #if defined( OPENSSL_SYS_VMS) && !defined( VMS_GMTIME_OK)
103 : if (ts == NULL) {
104 : static $DESCRIPTOR(tabnam, "LNM$DCL_LOGICAL");
105 : static $DESCRIPTOR(lognam, "SYS$TIMEZONE_DIFFERENTIAL");
106 : char logvalue[256];
107 : unsigned int reslen = 0;
108 : struct {
109 : short buflen;
110 : short code;
111 : void *bufaddr;
112 : unsigned int *reslen;
113 : } itemlist[] = {
114 : {
115 : 0, LNM$_STRING, 0, 0
116 : },
117 : {
118 : 0, 0, 0, 0
119 : },
120 : };
121 : int status;
122 : time_t t;
123 :
124 : /* Get the value for SYS$TIMEZONE_DIFFERENTIAL */
125 : itemlist[0].buflen = sizeof(logvalue);
126 : itemlist[0].bufaddr = logvalue;
127 : itemlist[0].reslen = &reslen;
128 : status = sys$trnlnm(0, &tabnam, &lognam, 0, itemlist);
129 : if (!(status & 1))
130 : return NULL;
131 : logvalue[reslen] = '\0';
132 :
133 : t = *timer;
134 :
135 : /* The following is extracted from the DEC C header time.h */
136 : /*
137 : ** Beginning in OpenVMS Version 7.0 mktime, time, ctime, strftime
138 : ** have two implementations. One implementation is provided
139 : ** for compatibility and deals with time in terms of local time,
140 : ** the other __utc_* deals with time in terms of UTC.
141 : */
142 : /*
143 : * We use the same conditions as in said time.h to check if we should
144 : * assume that t contains local time (and should therefore be
145 : * adjusted) or UTC (and should therefore be left untouched).
146 : */
147 : # if __CRTL_VER < 70000000 || defined _VMS_V6_SOURCE
148 : /* Get the numerical value of the equivalence string */
149 : status = atoi(logvalue);
150 :
151 : /* and use it to move time to GMT */
152 : t -= status;
153 : # endif
154 :
155 : /* then convert the result to the time structure */
156 :
157 : /*
158 : * Since there was no gmtime_r() to do this stuff for us, we have to
159 : * do it the hard way.
160 : */
161 : {
162 : /*-
163 : * The VMS epoch is the astronomical Smithsonian date,
164 : if I remember correctly, which is November 17, 1858.
165 : Furthermore, time is measure in thenths of microseconds
166 : and stored in quadwords (64 bit integers). unix_epoch
167 : below is January 1st 1970 expressed as a VMS time. The
168 : following code was used to get this number:
169 :
170 : #include <stdio.h>
171 : #include <stdlib.h>
172 : #include <lib$routines.h>
173 : #include <starlet.h>
174 :
175 : main()
176 : {
177 : unsigned long systime[2];
178 : unsigned short epoch_values[7] =
179 : { 1970, 1, 1, 0, 0, 0, 0 };
180 :
181 : lib$cvt_vectim(epoch_values, systime);
182 :
183 : printf("%u %u", systime[0], systime[1]);
184 : }
185 : */
186 : unsigned long unix_epoch[2] = { 1273708544, 8164711 };
187 : unsigned long deltatime[2];
188 : unsigned long systime[2];
189 : struct vms_vectime {
190 : short year, month, day, hour, minute, second, centi_second;
191 : } time_values;
192 : long operation;
193 :
194 : /*
195 : * Turn the number of seconds since January 1st 1970 to an
196 : * internal delta time. Note that lib$cvt_to_internal_time() will
197 : * assume that t is signed, and will therefore break on 32-bit
198 : * systems some time in 2038.
199 : */
200 : operation = LIB$K_DELTA_SECONDS;
201 : status = lib$cvt_to_internal_time(&operation, &t, deltatime);
202 :
203 : /*
204 : * Add the delta time with the Unix epoch and we have the current
205 : * UTC time in internal format
206 : */
207 : status = lib$add_times(unix_epoch, deltatime, systime);
208 :
209 : /* Turn the internal time into a time vector */
210 : status = sys$numtim(&time_values, systime);
211 :
212 : /* Fill in the struct tm with the result */
213 : result->tm_sec = time_values.second;
214 : result->tm_min = time_values.minute;
215 : result->tm_hour = time_values.hour;
216 : result->tm_mday = time_values.day;
217 : result->tm_mon = time_values.month - 1;
218 : result->tm_year = time_values.year - 1900;
219 :
220 : operation = LIB$K_DAY_OF_WEEK;
221 : status = lib$cvt_from_internal_time(&operation,
222 : &result->tm_wday, systime);
223 : result->tm_wday %= 7;
224 :
225 : operation = LIB$K_DAY_OF_YEAR;
226 : status = lib$cvt_from_internal_time(&operation,
227 : &result->tm_yday, systime);
228 : result->tm_yday--;
229 :
230 : result->tm_isdst = 0; /* There's no way to know... */
231 :
232 : ts = result;
233 : }
234 : }
235 : #endif
236 1480 : return ts;
237 : }
238 :
239 : /*
240 : * Take a tm structure and add an offset to it. This avoids any OS issues
241 : * with restricted date types and overflows which cause the year 2038
242 : * problem.
243 : */
244 :
245 : #define SECS_PER_DAY (24 * 60 * 60)
246 :
247 : static long date_to_julian(int y, int m, int d);
248 : static void julian_to_date(long jd, int *y, int *m, int *d);
249 : static int julian_adj(const struct tm *tm, int off_day, long offset_sec,
250 : long *pday, int *psec);
251 :
252 0 : int OPENSSL_gmtime_adj(struct tm *tm, int off_day, long offset_sec)
253 : {
254 : int time_sec, time_year, time_month, time_day;
255 : long time_jd;
256 :
257 : /* Convert time and offset into julian day and seconds */
258 0 : if (!julian_adj(tm, off_day, offset_sec, &time_jd, &time_sec))
259 : return 0;
260 :
261 : /* Convert Julian day back to date */
262 :
263 0 : julian_to_date(time_jd, &time_year, &time_month, &time_day);
264 :
265 0 : if (time_year < 1900 || time_year > 9999)
266 : return 0;
267 :
268 : /* Update tm structure */
269 :
270 0 : tm->tm_year = time_year - 1900;
271 0 : tm->tm_mon = time_month - 1;
272 0 : tm->tm_mday = time_day;
273 :
274 0 : tm->tm_hour = time_sec / 3600;
275 0 : tm->tm_min = (time_sec / 60) % 60;
276 0 : tm->tm_sec = time_sec % 60;
277 :
278 0 : return 1;
279 :
280 : }
281 :
282 0 : int OPENSSL_gmtime_diff(int *pday, int *psec,
283 : const struct tm *from, const struct tm *to)
284 : {
285 : int from_sec, to_sec, diff_sec;
286 : long from_jd, to_jd, diff_day;
287 0 : if (!julian_adj(from, 0, 0, &from_jd, &from_sec))
288 : return 0;
289 0 : if (!julian_adj(to, 0, 0, &to_jd, &to_sec))
290 : return 0;
291 0 : diff_day = to_jd - from_jd;
292 0 : diff_sec = to_sec - from_sec;
293 : /* Adjust differences so both positive or both negative */
294 0 : if (diff_day > 0 && diff_sec < 0) {
295 0 : diff_day--;
296 0 : diff_sec += SECS_PER_DAY;
297 : }
298 0 : if (diff_day < 0 && diff_sec > 0) {
299 0 : diff_day++;
300 0 : diff_sec -= SECS_PER_DAY;
301 : }
302 :
303 0 : if (pday)
304 0 : *pday = (int)diff_day;
305 0 : if (psec)
306 0 : *psec = diff_sec;
307 :
308 : return 1;
309 :
310 : }
311 :
312 : /* Convert tm structure and offset into julian day and seconds */
313 0 : static int julian_adj(const struct tm *tm, int off_day, long offset_sec,
314 : long *pday, int *psec)
315 : {
316 : int offset_hms, offset_day;
317 : long time_jd;
318 : int time_year, time_month, time_day;
319 : /* split offset into days and day seconds */
320 0 : offset_day = offset_sec / SECS_PER_DAY;
321 : /* Avoid sign issues with % operator */
322 0 : offset_hms = offset_sec - (offset_day * SECS_PER_DAY);
323 0 : offset_day += off_day;
324 : /* Add current time seconds to offset */
325 0 : offset_hms += tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
326 : /* Adjust day seconds if overflow */
327 0 : if (offset_hms >= SECS_PER_DAY) {
328 0 : offset_day++;
329 0 : offset_hms -= SECS_PER_DAY;
330 0 : } else if (offset_hms < 0) {
331 0 : offset_day--;
332 0 : offset_hms += SECS_PER_DAY;
333 : }
334 :
335 : /*
336 : * Convert date of time structure into a Julian day number.
337 : */
338 :
339 0 : time_year = tm->tm_year + 1900;
340 0 : time_month = tm->tm_mon + 1;
341 0 : time_day = tm->tm_mday;
342 :
343 0 : time_jd = date_to_julian(time_year, time_month, time_day);
344 :
345 : /* Work out Julian day of new date */
346 0 : time_jd += offset_day;
347 :
348 0 : if (time_jd < 0)
349 : return 0;
350 :
351 0 : *pday = time_jd;
352 0 : *psec = offset_hms;
353 0 : return 1;
354 : }
355 :
356 : /*
357 : * Convert date to and from julian day Uses Fliegel & Van Flandern algorithm
358 : */
359 0 : static long date_to_julian(int y, int m, int d)
360 : {
361 0 : return (1461 * (y + 4800 + (m - 14) / 12)) / 4 +
362 0 : (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 -
363 0 : (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + d - 32075;
364 : }
365 :
366 0 : static void julian_to_date(long jd, int *y, int *m, int *d)
367 : {
368 0 : long L = jd + 68569;
369 0 : long n = (4 * L) / 146097;
370 : long i, j;
371 :
372 0 : L = L - (146097 * n + 3) / 4;
373 0 : i = (4000 * (L + 1)) / 1461001;
374 0 : L = L - (1461 * i) / 4 + 31;
375 0 : j = (80 * L) / 2447;
376 0 : *d = L - (2447 * j) / 80;
377 0 : L = j / 11;
378 0 : *m = j + 2 - (12 * L);
379 0 : *y = 100 * (n - 49) + i + L;
380 0 : }
381 :
382 : #ifdef OPENSSL_TIME_TEST
383 :
384 : # include <stdio.h>
385 :
386 : /*
387 : * Time checking test code. Check times are identical for a wide range of
388 : * offsets. This should be run on a machine with 64 bit time_t or it will
389 : * trigger the very errors the routines fix.
390 : */
391 :
392 : int main(int argc, char **argv)
393 : {
394 : long offset;
395 : for (offset = 0; offset < 1000000; offset++) {
396 : check_time(offset);
397 : check_time(-offset);
398 : check_time(offset * 1000);
399 : check_time(-offset * 1000);
400 : }
401 : }
402 :
403 : int check_time(long offset)
404 : {
405 : struct tm tm1, tm2, o1;
406 : int off_day, off_sec;
407 : long toffset;
408 : time_t t1, t2;
409 : time(&t1);
410 : t2 = t1 + offset;
411 : OPENSSL_gmtime(&t2, &tm2);
412 : OPENSSL_gmtime(&t1, &tm1);
413 : o1 = tm1;
414 : OPENSSL_gmtime_adj(&tm1, 0, offset);
415 : if ((tm1.tm_year != tm2.tm_year) ||
416 : (tm1.tm_mon != tm2.tm_mon) ||
417 : (tm1.tm_mday != tm2.tm_mday) ||
418 : (tm1.tm_hour != tm2.tm_hour) ||
419 : (tm1.tm_min != tm2.tm_min) || (tm1.tm_sec != tm2.tm_sec)) {
420 : fprintf(stderr, "TIME ERROR!!\n");
421 : fprintf(stderr, "Time1: %d/%d/%d, %d:%02d:%02d\n",
422 : tm2.tm_mday, tm2.tm_mon + 1, tm2.tm_year + 1900,
423 : tm2.tm_hour, tm2.tm_min, tm2.tm_sec);
424 : fprintf(stderr, "Time2: %d/%d/%d, %d:%02d:%02d\n",
425 : tm1.tm_mday, tm1.tm_mon + 1, tm1.tm_year + 1900,
426 : tm1.tm_hour, tm1.tm_min, tm1.tm_sec);
427 : return 0;
428 : }
429 : OPENSSL_gmtime_diff(&o1, &tm1, &off_day, &off_sec);
430 : toffset = (long)off_day *SECS_PER_DAY + off_sec;
431 : if (offset != toffset) {
432 : fprintf(stderr, "TIME OFFSET ERROR!!\n");
433 : fprintf(stderr, "Expected %ld, Got %ld (%d:%d)\n",
434 : offset, toffset, off_day, off_sec);
435 : return 0;
436 : }
437 : return 1;
438 : }
439 :
440 : #endif
|