Line data Source code
1 : /* Copyright (c) 2003-2004, Roger Dingledine
2 : * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 : * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 : /* See LICENSE for licensing information */
5 :
6 : /**
7 : * \file time_to_tm.c
8 : * \brief Convert to struct tm, portably.
9 : **/
10 :
11 : #include "orconfig.h"
12 : #include "lib/cc/torint.h"
13 : #include "lib/cc/compat_compiler.h"
14 : #include "lib/wallclock/time_to_tm.h"
15 : #include "lib/string/printf.h"
16 : #include "lib/err/torerr.h"
17 :
18 : #include <errno.h>
19 : #include <time.h>
20 : #include <string.h>
21 : #include <stdlib.h>
22 :
23 : #if !defined(_WIN32)
24 : /** Defined iff we need to add locks when defining fake versions of reentrant
25 : * versions of time-related functions. */
26 : #define TIME_FNS_NEED_LOCKS
27 : #endif
28 :
29 : /** Helper: Deal with confused or out-of-bounds values from localtime_r and
30 : * friends. (On some platforms, they can give out-of-bounds values or can
31 : * return NULL.) If <b>islocal</b>, this is a localtime result; otherwise
32 : * it's from gmtime. The function returns <b>r</b>, when given <b>timep</b>
33 : * as its input. If we need to store new results, store them in
34 : * <b>resultbuf</b>. */
35 : static struct tm *
36 11874 : correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
37 : struct tm *r, char **err_out)
38 : {
39 11874 : const char *outcome;
40 :
41 11874 : if (PREDICT_LIKELY(r)) {
42 : /* We can't strftime dates after 9999 CE, and we want to avoid dates
43 : * before 1 CE (avoiding the year 0 issue and negative years). */
44 11870 : if (r->tm_year > 8099) {
45 2 : r->tm_year = 8099;
46 2 : r->tm_mon = 11;
47 2 : r->tm_mday = 31;
48 2 : r->tm_yday = 364;
49 2 : r->tm_wday = 6;
50 2 : r->tm_hour = 23;
51 2 : r->tm_min = 59;
52 2 : r->tm_sec = 59;
53 11868 : } else if (r->tm_year < (1-1900)) {
54 2 : r->tm_year = (1-1900);
55 2 : r->tm_mon = 0;
56 2 : r->tm_mday = 1;
57 2 : r->tm_yday = 0;
58 2 : r->tm_wday = 0;
59 2 : r->tm_hour = 0;
60 2 : r->tm_min = 0;
61 2 : r->tm_sec = 0;
62 : }
63 11870 : return r;
64 : }
65 :
66 : /* If we get here, gmtime or localtime returned NULL. It might have done
67 : * this because of overrun or underrun, or it might have done it because of
68 : * some other weird issue. */
69 4 : if (timep) {
70 4 : if (*timep < 0) {
71 2 : r = resultbuf;
72 2 : r->tm_year = 70; /* 1970 CE */
73 2 : r->tm_mon = 0;
74 2 : r->tm_mday = 1;
75 2 : r->tm_yday = 0;
76 2 : r->tm_wday = 0;
77 2 : r->tm_hour = 0;
78 2 : r->tm_min = 0 ;
79 2 : r->tm_sec = 0;
80 2 : outcome = "Rounding up to 1970";
81 2 : goto done;
82 2 : } else if (*timep >= INT32_MAX) {
83 : /* Rounding down to INT32_MAX isn't so great, but keep in mind that we
84 : * only do it if gmtime/localtime tells us NULL. */
85 2 : r = resultbuf;
86 2 : r->tm_year = 137; /* 2037 CE */
87 2 : r->tm_mon = 11;
88 2 : r->tm_mday = 31;
89 2 : r->tm_yday = 364;
90 2 : r->tm_wday = 6;
91 2 : r->tm_hour = 23;
92 2 : r->tm_min = 59;
93 2 : r->tm_sec = 59;
94 2 : outcome = "Rounding down to 2037";
95 2 : goto done;
96 : }
97 : }
98 :
99 : /* If we get here, then gmtime/localtime failed without getting an extreme
100 : * value for *timep */
101 : /* LCOV_EXCL_START */
102 : r = resultbuf;
103 : memset(resultbuf, 0, sizeof(struct tm));
104 : outcome="can't recover";
105 : /* LCOV_EXCL_STOP */
106 4 : done:
107 4 : if (err_out) {
108 6 : tor_asprintf(err_out, "%s(%"PRId64") failed with error %s: %s",
109 : islocal?"localtime":"gmtime",
110 : timep?((int64_t)*timep):0,
111 4 : strerror(errno),
112 : outcome);
113 : }
114 : return r;
115 : }
116 :
117 : /** @{ */
118 : /** As localtime_r, but defined for platforms that don't have it:
119 : *
120 : * Convert *<b>timep</b> to a struct tm in local time, and store the value in
121 : * *<b>result</b>. Return the result on success, or NULL on failure.
122 : */
123 : #ifdef HAVE_LOCALTIME_R
124 : struct tm *
125 10135 : tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
126 : {
127 10135 : struct tm *r;
128 10135 : r = localtime_r(timep, result);
129 10135 : return correct_tm(1, timep, result, r, err_out);
130 : }
131 : #elif defined(TIME_FNS_NEED_LOCKS)
132 : struct tm *
133 : tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
134 : {
135 : struct tm *r;
136 : static tor_mutex_t *m=NULL;
137 : if (!m) { m=tor_mutex_new(); }
138 : raw_assert(result);
139 : tor_mutex_acquire(m);
140 : r = localtime(timep);
141 : if (r)
142 : memcpy(result, r, sizeof(struct tm));
143 : tor_mutex_release(m);
144 : return correct_tm(1, timep, result, r, err_out);
145 : }
146 : #else
147 : struct tm *
148 : tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
149 : {
150 : struct tm *r;
151 : raw_assert(result);
152 : r = localtime(timep);
153 : if (r)
154 : memcpy(result, r, sizeof(struct tm));
155 : return correct_tm(1, timep, result, r, err_out);
156 : }
157 : #endif /* defined(HAVE_LOCALTIME_R) || ... */
158 : /** @} */
159 :
160 : /** @{ */
161 : /** As gmtime_r, but defined for platforms that don't have it:
162 : *
163 : * Convert *<b>timep</b> to a struct tm in UTC, and store the value in
164 : * *<b>result</b>. Return the result on success, or NULL on failure.
165 : */
166 : #ifdef HAVE_GMTIME_R
167 : struct tm *
168 1739 : tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
169 : {
170 1739 : struct tm *r;
171 1739 : r = gmtime_r(timep, result);
172 1739 : return correct_tm(0, timep, result, r, err_out);
173 : }
174 : #elif defined(TIME_FNS_NEED_LOCKS)
175 : struct tm *
176 : tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
177 : {
178 : struct tm *r;
179 : static tor_mutex_t *m=NULL;
180 : if (!m) { m=tor_mutex_new(); }
181 : raw_assert(result);
182 : tor_mutex_acquire(m);
183 : r = gmtime(timep);
184 : if (r)
185 : memcpy(result, r, sizeof(struct tm));
186 : tor_mutex_release(m);
187 : return correct_tm(0, timep, result, r, err_out);
188 : }
189 : #else
190 : struct tm *
191 : tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
192 : {
193 : struct tm *r;
194 : raw_assert(result);
195 : r = gmtime(timep);
196 : if (r)
197 : memcpy(result, r, sizeof(struct tm));
198 : return correct_tm(0, timep, result, r, err_out);
199 : }
200 : #endif /* defined(HAVE_GMTIME_R) || ... */
201 : /**@}*/
|