tor

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

unitparse.c (6998B)


      1 /* Copyright (c) 2001 Matej Pfajfar.
      2 * Copyright (c) 2001-2004, Roger Dingledine.
      3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      5 /* See LICENSE for licensing information */
      6 
      7 /**
      8 * @file unitparse.c
      9 * @brief Functions for parsing values with units from a configuration file.
     10 **/
     11 
     12 #include "orconfig.h"
     13 #include "lib/confmgt/unitparse.h"
     14 #include "lib/log/log.h"
     15 #include "lib/log/util_bug.h"
     16 #include "lib/malloc/malloc.h"
     17 #include "lib/string/parse_int.h"
     18 #include "lib/string/printf.h"
     19 #include "lib/string/util_string.h"
     20 #include "lib/intmath/muldiv.h"
     21 
     22 #include <string.h>
     23 
     24 /** Table to map the names of memory units to the number of bytes they
     25 * contain. */
     26 // clang-format off
     27 const struct unit_table_t memory_units[] = {
     28  { "",          1 },
     29  { "b",         1<< 0 },
     30  { "byte",      1<< 0 },
     31  { "bytes",     1<< 0 },
     32  { "kb",        1<<10 },
     33  { "kbyte",     1<<10 },
     34  { "kbytes",    1<<10 },
     35  { "kilobyte",  1<<10 },
     36  { "kilobytes", 1<<10 },
     37  { "kilobits",  1<<7  },
     38  { "kilobit",   1<<7  },
     39  { "kbits",     1<<7  },
     40  { "kbit",      1<<7  },
     41  { "m",         1<<20 },
     42  { "mb",        1<<20 },
     43  { "mbyte",     1<<20 },
     44  { "mbytes",    1<<20 },
     45  { "megabyte",  1<<20 },
     46  { "megabytes", 1<<20 },
     47  { "megabits",  1<<17 },
     48  { "megabit",   1<<17 },
     49  { "mbits",     1<<17 },
     50  { "mbit",      1<<17 },
     51  { "gb",        1<<30 },
     52  { "gbyte",     1<<30 },
     53  { "gbytes",    1<<30 },
     54  { "gigabyte",  1<<30 },
     55  { "gigabytes", 1<<30 },
     56  { "gigabits",  1<<27 },
     57  { "gigabit",   1<<27 },
     58  { "gbits",     1<<27 },
     59  { "gbit",      1<<27 },
     60  { "tb",        UINT64_C(1)<<40 },
     61  { "tbyte",     UINT64_C(1)<<40 },
     62  { "tbytes",    UINT64_C(1)<<40 },
     63  { "terabyte",  UINT64_C(1)<<40 },
     64  { "terabytes", UINT64_C(1)<<40 },
     65  { "terabits",  UINT64_C(1)<<37 },
     66  { "terabit",   UINT64_C(1)<<37 },
     67  { "tbits",     UINT64_C(1)<<37 },
     68  { "tbit",      UINT64_C(1)<<37 },
     69  { NULL, 0 },
     70 };
     71 // clang-format on
     72 
     73 /** Table to map the names of time units to the number of seconds they
     74 * contain. */
     75 // clang-format off
     76 const struct unit_table_t time_units[] = {
     77  { "",         1 },
     78  { "second",   1 },
     79  { "seconds",  1 },
     80  { "minute",   60 },
     81  { "minutes",  60 },
     82  { "hour",     60*60 },
     83  { "hours",    60*60 },
     84  { "day",      24*60*60 },
     85  { "days",     24*60*60 },
     86  { "week",     7*24*60*60 },
     87  { "weeks",    7*24*60*60 },
     88  { "month",    2629728, }, /* about 30.437 days */
     89  { "months",   2629728, },
     90  { NULL, 0 },
     91 };
     92 // clang-format on
     93 
     94 /** Table to map the names of time units to the number of milliseconds
     95 * they contain. */
     96 // clang-format off
     97 const struct unit_table_t time_msec_units[] = {
     98  { "",         1 },
     99  { "msec",     1 },
    100  { "millisecond", 1 },
    101  { "milliseconds", 1 },
    102  { "second",   1000 },
    103  { "seconds",  1000 },
    104  { "minute",   60*1000 },
    105  { "minutes",  60*1000 },
    106  { "hour",     60*60*1000 },
    107  { "hours",    60*60*1000 },
    108  { "day",      24*60*60*1000 },
    109  { "days",     24*60*60*1000 },
    110  { "week",     7*24*60*60*1000 },
    111  { "weeks",    7*24*60*60*1000 },
    112  { NULL, 0 },
    113 };
    114 // clang-format on
    115 
    116 /** Parse a string <b>val</b> containing a number, zero or more
    117 * spaces, and an optional unit string.  If the unit appears in the
    118 * table <b>u</b>, then multiply the number by the unit multiplier.
    119 * On success, set *<b>ok</b> to 1 and return this product.
    120 * Otherwise, set *<b>ok</b> to 0.
    121 *
    122 * If an error (like overflow or a negative value is detected), put an error
    123 * message in *<b>errmsg_out</b> if that pointer is non-NULL, and otherwise
    124 * log a warning.
    125 */
    126 uint64_t
    127 config_parse_units(const char *val, const unit_table_t *u, int *ok,
    128                   char **errmsg_out)
    129 {
    130  uint64_t v = 0;
    131  double d = 0;
    132  int use_float = 0;
    133  char *cp;
    134  char *errmsg = NULL;
    135 
    136  tor_assert(ok);
    137 
    138  v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
    139  if (!*ok || (cp && *cp == '.')) {
    140    d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp);
    141    if (!*ok) {
    142      tor_asprintf(&errmsg, "Unable to parse %s as a number", val);
    143      goto done;
    144    }
    145    use_float = 1;
    146  }
    147 
    148  if (BUG(!cp)) {
    149    // cp should always be non-NULL if the parse operation succeeds.
    150 
    151    // LCOV_EXCL_START
    152    *ok = 1;
    153    v = use_float ? ((uint64_t)d) :  v;
    154    goto done;
    155    // LCOV_EXCL_STOP
    156  }
    157 
    158  cp = (char*) eat_whitespace(cp);
    159 
    160  for ( ;u->unit;++u) {
    161    if (!strcasecmp(u->unit, cp)) {
    162      if (use_float) {
    163        d = u->multiplier * d;
    164 
    165        if (d < 0) {
    166          tor_asprintf(&errmsg, "Got a negative value while parsing %s %s",
    167                       val, u->unit);
    168          *ok = 0;
    169          goto done;
    170        }
    171 
    172        // Some compilers may warn about casting a double to an unsigned type
    173        // because they don't know if d is >= 0
    174        if (d >= 0 && (d > (double)INT64_MAX || (uint64_t)d > INT64_MAX)) {
    175          tor_asprintf(&errmsg, "Overflow while parsing %s %s",
    176                       val, u->unit);
    177          *ok = 0;
    178          goto done;
    179        }
    180 
    181        v = (uint64_t) d;
    182      } else {
    183        v = tor_mul_u64_nowrap(v, u->multiplier);
    184 
    185        if (v > INT64_MAX) {
    186          tor_asprintf(&errmsg, "Overflow while parsing %s %s",
    187                       val, u->unit);
    188          *ok = 0;
    189          goto done;
    190        }
    191      }
    192 
    193      *ok = 1;
    194      goto done;
    195    }
    196  }
    197  tor_asprintf(&errmsg, "Unknown unit in %s", val);
    198  *ok = 0;
    199 done:
    200 
    201  if (errmsg) {
    202    tor_assert_nonfatal(!*ok);
    203    if (errmsg_out) {
    204      *errmsg_out = errmsg;
    205    } else {
    206      log_warn(LD_CONFIG, "%s", errmsg);
    207      tor_free(errmsg);
    208    }
    209  }
    210 
    211  if (*ok)
    212    return v;
    213  else
    214    return 0;
    215 }
    216 
    217 /** Parse a string in the format "number unit", where unit is a unit of
    218 * information (byte, KB, M, etc).  On success, set *<b>ok</b> to true
    219 * and return the number of bytes specified.  Otherwise, set
    220 * *<b>ok</b> to false and return 0. */
    221 uint64_t
    222 config_parse_memunit(const char *s, int *ok)
    223 {
    224  uint64_t u = config_parse_units(s, memory_units, ok, NULL);
    225  return u;
    226 }
    227 
    228 /** Parse a string in the format "number unit", where unit is a unit of
    229 * time in milliseconds.  On success, set *<b>ok</b> to true and return
    230 * the number of milliseconds in the provided interval.  Otherwise, set
    231 * *<b>ok</b> to 0 and return -1. */
    232 int
    233 config_parse_msec_interval(const char *s, int *ok)
    234 {
    235  uint64_t r;
    236  r = config_parse_units(s, time_msec_units, ok, NULL);
    237  if (r > INT_MAX) {
    238    log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
    239    *ok = 0;
    240    return -1;
    241  }
    242  return (int)r;
    243 }
    244 
    245 /** Parse a string in the format "number unit", where unit is a unit of time.
    246 * On success, set *<b>ok</b> to true and return the number of seconds in
    247 * the provided interval.  Otherwise, set *<b>ok</b> to 0 and return -1.
    248 */
    249 int
    250 config_parse_interval(const char *s, int *ok)
    251 {
    252  uint64_t r;
    253  r = config_parse_units(s, time_units, ok, NULL);
    254  if (r > INT_MAX) {
    255    log_warn(LD_CONFIG, "Interval '%s' is too long", s);
    256    *ok = 0;
    257    return -1;
    258  }
    259  return (int)r;
    260 }