Line data Source code
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 75739 : config_parse_units(const char *val, const unit_table_t *u, int *ok,
128 : char **errmsg_out)
129 : {
130 75739 : uint64_t v = 0;
131 75739 : double d = 0;
132 75739 : int use_float = 0;
133 75739 : char *cp;
134 75739 : char *errmsg = NULL;
135 :
136 75739 : tor_assert(ok);
137 :
138 75739 : v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
139 75739 : if (!*ok || (cp && *cp == '.')) {
140 9 : d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp);
141 9 : if (!*ok) {
142 2 : tor_asprintf(&errmsg, "Unable to parse %s as a number", val);
143 2 : goto done;
144 : }
145 : use_float = 1;
146 : }
147 :
148 75737 : 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 75737 : cp = (char*) eat_whitespace(cp);
159 :
160 313810 : for ( ;u->unit;++u) {
161 313803 : if (!strcasecmp(u->unit, cp)) {
162 75730 : if (use_float) {
163 7 : d = u->multiplier * d;
164 :
165 7 : if (d < 0) {
166 0 : tor_asprintf(&errmsg, "Got a negative value while parsing %s %s",
167 : val, u->unit);
168 0 : *ok = 0;
169 0 : 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 7 : if (d >= 0 && (d > (double)INT64_MAX || (uint64_t)d > INT64_MAX)) {
175 2 : tor_asprintf(&errmsg, "Overflow while parsing %s %s",
176 : val, u->unit);
177 2 : *ok = 0;
178 2 : goto done;
179 : }
180 :
181 5 : v = (uint64_t) d;
182 : } else {
183 75723 : v = tor_mul_u64_nowrap(v, u->multiplier);
184 :
185 75723 : if (v > INT64_MAX) {
186 1 : tor_asprintf(&errmsg, "Overflow while parsing %s %s",
187 1 : val, u->unit);
188 1 : *ok = 0;
189 1 : goto done;
190 : }
191 : }
192 :
193 75727 : *ok = 1;
194 75727 : goto done;
195 : }
196 : }
197 7 : tor_asprintf(&errmsg, "Unknown unit in %s", val);
198 7 : *ok = 0;
199 75739 : done:
200 :
201 75739 : if (errmsg) {
202 12 : tor_assert_nonfatal(!*ok);
203 12 : if (errmsg_out) {
204 5 : *errmsg_out = errmsg;
205 : } else {
206 7 : log_warn(LD_CONFIG, "%s", errmsg);
207 7 : tor_free(errmsg);
208 : }
209 : }
210 :
211 75739 : if (*ok)
212 : return v;
213 : else
214 12 : 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 8 : config_parse_memunit(const char *s, int *ok)
223 : {
224 8 : uint64_t u = config_parse_units(s, memory_units, ok, NULL);
225 8 : 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 4 : config_parse_msec_interval(const char *s, int *ok)
234 : {
235 4 : uint64_t r;
236 4 : r = config_parse_units(s, time_msec_units, ok, NULL);
237 4 : if (r > INT_MAX) {
238 1 : log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
239 1 : *ok = 0;
240 1 : return -1;
241 : }
242 3 : 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 7 : config_parse_interval(const char *s, int *ok)
251 : {
252 7 : uint64_t r;
253 7 : r = config_parse_units(s, time_units, ok, NULL);
254 7 : if (r > INT_MAX) {
255 1 : log_warn(LD_CONFIG, "Interval '%s' is too long", s);
256 1 : *ok = 0;
257 1 : return -1;
258 : }
259 6 : return (int)r;
260 : }
|