tm_cvt.c 5.4 KB

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