tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

time_to_tm.c (5635B)


      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 correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
     37           struct tm *r, char **err_out)
     38 {
     39  const char *outcome;
     40 
     41  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    if (r->tm_year > 8099) {
     45      r->tm_year = 8099;
     46      r->tm_mon = 11;
     47      r->tm_mday = 31;
     48      r->tm_yday = 364;
     49      r->tm_wday = 6;
     50      r->tm_hour = 23;
     51      r->tm_min = 59;
     52      r->tm_sec = 59;
     53    } else if (r->tm_year < (1-1900)) {
     54      r->tm_year = (1-1900);
     55      r->tm_mon = 0;
     56      r->tm_mday = 1;
     57      r->tm_yday = 0;
     58      r->tm_wday = 0;
     59      r->tm_hour = 0;
     60      r->tm_min = 0;
     61      r->tm_sec = 0;
     62    }
     63    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  if (timep) {
     70    if (*timep < 0) {
     71      r = resultbuf;
     72      r->tm_year = 70; /* 1970 CE */
     73      r->tm_mon = 0;
     74      r->tm_mday = 1;
     75      r->tm_yday = 0;
     76      r->tm_wday = 0;
     77      r->tm_hour = 0;
     78      r->tm_min = 0 ;
     79      r->tm_sec = 0;
     80      outcome = "Rounding up to 1970";
     81      goto done;
     82    } 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      r = resultbuf;
     86      r->tm_year = 137; /* 2037 CE */
     87      r->tm_mon = 11;
     88      r->tm_mday = 31;
     89      r->tm_yday = 364;
     90      r->tm_wday = 6;
     91      r->tm_hour = 23;
     92      r->tm_min = 59;
     93      r->tm_sec = 59;
     94      outcome = "Rounding down to 2037";
     95      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 done:
    107  if (err_out) {
    108    tor_asprintf(err_out, "%s(%"PRId64") failed with error %s: %s",
    109                 islocal?"localtime":"gmtime",
    110                 timep?((int64_t)*timep):0,
    111                 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 tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
    126 {
    127  struct tm *r;
    128  r = localtime_r(timep, result);
    129  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 tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
    169 {
    170  struct tm *r;
    171  r = gmtime_r(timep, result);
    172  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 /**@}*/