/src/tor/src/app/config/statefile.c
Line | Count | Source (jump to first uncovered line) |
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 statefile.c |
9 | | * |
10 | | * \brief Handles parsing and encoding the persistent 'state' file that carries |
11 | | * miscellaneous persistent state between Tor invocations. |
12 | | * |
13 | | * This 'state' file is a typed key-value store that allows multiple |
14 | | * entries for the same key. It follows the same metaformat as described |
15 | | * in confmgt.c, and uses the same code to read and write itself. |
16 | | * |
17 | | * The state file is most suitable for small values that don't change too |
18 | | * frequently. For values that become very large, we typically use a separate |
19 | | * file -- for example, see how we handle microdescriptors, by storing them in |
20 | | * a separate file with a journal. |
21 | | * |
22 | | * The current state is accessed via get_or_state(), which returns a singleton |
23 | | * or_state_t object. Functions that change it should call |
24 | | * or_state_mark_dirty() to ensure that it will get written to disk. |
25 | | * |
26 | | * The or_state_save() function additionally calls various functioens |
27 | | * throughout Tor that might want to flush more state to the the disk, |
28 | | * including some in rephist.c, entrynodes.c, circuitstats.c, hibernate.c. |
29 | | */ |
30 | | |
31 | | #define STATEFILE_PRIVATE |
32 | | #include "core/or/or.h" |
33 | | #include "core/or/circuitstats.h" |
34 | | #include "app/config/config.h" |
35 | | #include "feature/relay/transport_config.h" |
36 | | #include "lib/confmgt/confmgt.h" |
37 | | #include "core/mainloop/mainloop.h" |
38 | | #include "core/mainloop/netstatus.h" |
39 | | #include "core/mainloop/connection.h" |
40 | | #include "feature/control/control_events.h" |
41 | | #include "feature/client/entrynodes.h" |
42 | | #include "feature/hibernate/hibernate.h" |
43 | | #include "feature/stats/bwhist.h" |
44 | | #include "feature/relay/router.h" |
45 | | #include "feature/relay/routermode.h" |
46 | | #include "lib/sandbox/sandbox.h" |
47 | | #include "app/config/statefile.h" |
48 | | #include "app/main/subsysmgr.h" |
49 | | #include "lib/encoding/confline.h" |
50 | | #include "lib/net/resolve.h" |
51 | | #include "lib/version/torversion.h" |
52 | | |
53 | | #include "app/config/or_state_st.h" |
54 | | |
55 | | #ifdef HAVE_UNISTD_H |
56 | | #include <unistd.h> |
57 | | #endif |
58 | | |
59 | | /** A list of state-file "abbreviations," for compatibility. */ |
60 | | static config_abbrev_t state_abbrevs_[] = { |
61 | | { NULL, NULL, 0, 0}, |
62 | | }; |
63 | | |
64 | | /** A list of obsolete keys that we do not and should not preserve. |
65 | | * |
66 | | * We could just let these live in ExtraLines indefinitely, but they're |
67 | | * never going to be used again, and every version that used them |
68 | | * has been obsolete for a long time. |
69 | | * */ |
70 | | static const char *obsolete_state_keys[] = { |
71 | | /* These were renamed in 0.1.1.11-alpha */ |
72 | | "AccountingBytesReadInterval", |
73 | | "HelperNode", |
74 | | "HelperNodeDownSince", |
75 | | "HelperNodeUnlistedSince", |
76 | | "EntryNode", |
77 | | "HelperNodeDownSince", |
78 | | "EntryNodeUnlistedSince", |
79 | | /* These were replaced by "Guard" in 0.3.0.1-alpha. */ |
80 | | "EntryGuard", |
81 | | "EntryGuardDownSince", |
82 | | "EntryGuardUnlistedSince", |
83 | | "EntryGuardAddedBy", |
84 | | "EntryGuardPathBias", |
85 | | "EntryGuardPathUseBias", |
86 | | /* This was replaced by OPE-based revision numbers in 0.3.5.1-alpha, |
87 | | * and was never actually used in a released version. */ |
88 | | "HidServRevCounter", |
89 | | |
90 | | NULL, |
91 | | }; |
92 | | |
93 | | /** dummy instance of or_state_t, used for type-checking its |
94 | | * members with CONF_CHECK_VAR_TYPE. */ |
95 | | DUMMY_TYPECHECK_INSTANCE(or_state_t); |
96 | | |
97 | | #define VAR(varname,conftype,member,initvalue) \ |
98 | | CONFIG_VAR_ETYPE(or_state_t, varname, conftype, member, 0, initvalue) |
99 | | #define V(member,conftype,initvalue) \ |
100 | | VAR(#member, conftype, member, initvalue) |
101 | | |
102 | | /** Array of "state" variables saved to the ~/.tor/state file. */ |
103 | | // clang-format off |
104 | | static const config_var_t state_vars_[] = { |
105 | | /* Remember to document these in state-contents.txt ! */ |
106 | | |
107 | | V(AccountingBytesReadInInterval, MEMUNIT, NULL), |
108 | | V(AccountingBytesWrittenInInterval, MEMUNIT, NULL), |
109 | | V(AccountingExpectedUsage, MEMUNIT, NULL), |
110 | | V(AccountingIntervalStart, ISOTIME, NULL), |
111 | | V(AccountingSecondsActive, INTERVAL, NULL), |
112 | | V(AccountingSecondsToReachSoftLimit,INTERVAL, NULL), |
113 | | V(AccountingSoftLimitHitAt, ISOTIME, NULL), |
114 | | V(AccountingBytesAtSoftLimit, MEMUNIT, NULL), |
115 | | |
116 | | VAR("TransportProxy", LINELIST_S, TransportProxies, NULL), |
117 | | V(TransportProxies, LINELIST_V, NULL), |
118 | | |
119 | | V(BWHistoryReadEnds, ISOTIME, NULL), |
120 | | V(BWHistoryReadInterval, POSINT, "900"), |
121 | | V(BWHistoryReadValues, CSV, ""), |
122 | | V(BWHistoryReadMaxima, CSV, ""), |
123 | | V(BWHistoryWriteEnds, ISOTIME, NULL), |
124 | | V(BWHistoryWriteInterval, POSINT, "900"), |
125 | | V(BWHistoryWriteValues, CSV, ""), |
126 | | V(BWHistoryWriteMaxima, CSV, ""), |
127 | | V(BWHistoryIPv6ReadEnds, ISOTIME, NULL), |
128 | | V(BWHistoryIPv6ReadInterval, POSINT, "900"), |
129 | | V(BWHistoryIPv6ReadValues, CSV, ""), |
130 | | V(BWHistoryIPv6ReadMaxima, CSV, ""), |
131 | | V(BWHistoryIPv6WriteEnds, ISOTIME, NULL), |
132 | | V(BWHistoryIPv6WriteInterval, POSINT, "900"), |
133 | | V(BWHistoryIPv6WriteValues, CSV, ""), |
134 | | V(BWHistoryIPv6WriteMaxima, CSV, ""), |
135 | | V(BWHistoryDirReadEnds, ISOTIME, NULL), |
136 | | V(BWHistoryDirReadInterval, POSINT, "900"), |
137 | | V(BWHistoryDirReadValues, CSV, ""), |
138 | | V(BWHistoryDirReadMaxima, CSV, ""), |
139 | | V(BWHistoryDirWriteEnds, ISOTIME, NULL), |
140 | | V(BWHistoryDirWriteInterval, POSINT, "900"), |
141 | | V(BWHistoryDirWriteValues, CSV, ""), |
142 | | V(BWHistoryDirWriteMaxima, CSV, ""), |
143 | | |
144 | | V(Guard, LINELIST, NULL), |
145 | | |
146 | | V(TorVersion, STRING, NULL), |
147 | | |
148 | | V(LastRotatedOnionKey, ISOTIME, NULL), |
149 | | V(LastWritten, ISOTIME, NULL), |
150 | | |
151 | | V(TotalBuildTimes, POSINT, NULL), |
152 | | V(CircuitBuildAbandonedCount, POSINT, "0"), |
153 | | VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL), |
154 | | VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL), |
155 | | |
156 | | END_OF_CONFIG_VARS |
157 | | }; |
158 | | // clang-format on |
159 | | |
160 | | #undef VAR |
161 | | #undef V |
162 | | |
163 | | static int or_state_validate(or_state_t *state, char **msg); |
164 | | |
165 | | static int or_state_validate_cb(const void *old_options, |
166 | | void *options, char **msg); |
167 | | |
168 | | /** Magic value for or_state_t. */ |
169 | | #define OR_STATE_MAGIC 0x57A73f57 |
170 | | |
171 | | /** "Extra" variable in the state that receives lines we can't parse. This |
172 | | * lets us preserve options from versions of Tor newer than us. */ |
173 | | static struct_member_t state_extra_var = { |
174 | | .name = "__extra", |
175 | | .type = CONFIG_TYPE_LINELIST, |
176 | | .offset = offsetof(or_state_t, ExtraLines), |
177 | | }; |
178 | | |
179 | | /** Configuration format for or_state_t. */ |
180 | | static const config_format_t state_format = { |
181 | | .size = sizeof(or_state_t), |
182 | | .magic = { |
183 | | "or_state_t", |
184 | | OR_STATE_MAGIC, |
185 | | offsetof(or_state_t, magic_), |
186 | | }, |
187 | | .abbrevs = state_abbrevs_, |
188 | | .vars = state_vars_, |
189 | | .legacy_validate_fn = or_state_validate_cb, |
190 | | .extra = &state_extra_var, |
191 | | .has_config_suite = true, |
192 | | .config_suite_offset = offsetof(or_state_t, substates_), |
193 | | }; |
194 | | |
195 | | /* A global configuration manager for state-file objects */ |
196 | | static config_mgr_t *state_mgr = NULL; |
197 | | |
198 | | /** Return the configuration manager for state-file objects. */ |
199 | | STATIC const config_mgr_t * |
200 | | get_state_mgr(void) |
201 | 0 | { |
202 | 0 | if (PREDICT_UNLIKELY(state_mgr == NULL)) { |
203 | 0 | state_mgr = config_mgr_new(&state_format); |
204 | 0 | int rv = subsystems_register_state_formats(state_mgr); |
205 | 0 | tor_assert(rv == 0); |
206 | 0 | config_mgr_freeze(state_mgr); |
207 | 0 | } |
208 | 0 | return state_mgr; |
209 | 0 | } |
210 | | |
211 | 0 | #define CHECK_STATE_MAGIC(s) STMT_BEGIN \ |
212 | 0 | config_check_toplevel_magic(get_state_mgr(), (s)); \ |
213 | 0 | STMT_END |
214 | | |
215 | | /** Persistent serialized state. */ |
216 | | static or_state_t *global_state = NULL; |
217 | | |
218 | | /** Return the persistent state struct for this Tor. */ |
219 | | MOCK_IMPL(or_state_t *, |
220 | | get_or_state, (void)) |
221 | 0 | { |
222 | 0 | tor_assert(global_state); |
223 | 0 | return global_state; |
224 | 0 | } |
225 | | |
226 | | /** Return true iff we have loaded the global state for this Tor */ |
227 | | int |
228 | | or_state_loaded(void) |
229 | 0 | { |
230 | 0 | return global_state != NULL; |
231 | 0 | } |
232 | | |
233 | | /** Return true if <b>line</b> is a valid state TransportProxy line. |
234 | | * Return false otherwise. */ |
235 | | static int |
236 | | state_transport_line_is_valid(const char *line) |
237 | 0 | { |
238 | 0 | smartlist_t *items = NULL; |
239 | 0 | char *addrport=NULL; |
240 | 0 | tor_addr_t addr; |
241 | 0 | uint16_t port = 0; |
242 | 0 | int r; |
243 | |
|
244 | 0 | items = smartlist_new(); |
245 | 0 | smartlist_split_string(items, line, NULL, |
246 | 0 | SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); |
247 | |
|
248 | 0 | if (smartlist_len(items) != 2) { |
249 | 0 | log_warn(LD_CONFIG, "state: Not enough arguments in TransportProxy line."); |
250 | 0 | goto err; |
251 | 0 | } |
252 | | |
253 | 0 | addrport = smartlist_get(items, 1); |
254 | 0 | if (tor_addr_port_lookup(addrport, &addr, &port) < 0) { |
255 | 0 | log_warn(LD_CONFIG, "state: Could not parse addrport."); |
256 | 0 | goto err; |
257 | 0 | } |
258 | | |
259 | 0 | if (!port) { |
260 | 0 | log_warn(LD_CONFIG, "state: Transport line did not contain port."); |
261 | 0 | goto err; |
262 | 0 | } |
263 | | |
264 | 0 | r = 1; |
265 | 0 | goto done; |
266 | | |
267 | 0 | err: |
268 | 0 | r = 0; |
269 | |
|
270 | 0 | done: |
271 | 0 | SMARTLIST_FOREACH(items, char*, s, tor_free(s)); |
272 | 0 | smartlist_free(items); |
273 | 0 | return r; |
274 | 0 | } |
275 | | |
276 | | /** Return 0 if all TransportProxy lines in <b>state</b> are well |
277 | | * formed. Otherwise, return -1. */ |
278 | | static int |
279 | | validate_transports_in_state(or_state_t *state) |
280 | 0 | { |
281 | 0 | int broken = 0; |
282 | 0 | config_line_t *line; |
283 | |
|
284 | 0 | for (line = state->TransportProxies ; line ; line = line->next) { |
285 | 0 | tor_assert(!strcmp(line->key, "TransportProxy")); |
286 | 0 | if (!state_transport_line_is_valid(line->value)) |
287 | 0 | broken = 1; |
288 | 0 | } |
289 | | |
290 | 0 | if (broken) |
291 | 0 | log_warn(LD_CONFIG, "state: State file seems to be broken."); |
292 | |
|
293 | 0 | return 0; |
294 | 0 | } |
295 | | |
296 | | /** Return 0 if every setting in <b>state</b> is reasonable, and a |
297 | | * permissible transition from <b>old_state</b>. Else warn and return -1. |
298 | | * Should have no side effects, except for normalizing the contents of |
299 | | * <b>state</b>. |
300 | | */ |
301 | | static int |
302 | | or_state_validate(or_state_t *state, char **msg) |
303 | 0 | { |
304 | 0 | return config_validate(get_state_mgr(), NULL, state, msg); |
305 | 0 | } |
306 | | |
307 | | /** |
308 | | * Legacy validation/normalization callback for or_state_t. See |
309 | | * legacy_validate_fn_t for more information. |
310 | | */ |
311 | | static int |
312 | | or_state_validate_cb(const void *old_state, void *state_, char **msg) |
313 | 0 | { |
314 | | /* There is not a meaningful concept of a state-to-state transition, |
315 | | * since we do not reload the state after we start. */ |
316 | 0 | (void) old_state; |
317 | 0 | CHECK_STATE_MAGIC(state_); |
318 | |
|
319 | 0 | or_state_t *state = state_; |
320 | |
|
321 | 0 | if (entry_guards_parse_state(state, 0, msg)<0) |
322 | 0 | return -1; |
323 | | |
324 | 0 | if (validate_transports_in_state(state)<0) |
325 | 0 | return -1; |
326 | | |
327 | 0 | return 0; |
328 | 0 | } |
329 | | |
330 | | /** Replace the current persistent state with <b>new_state</b> */ |
331 | | static int |
332 | | or_state_set(or_state_t *new_state) |
333 | 0 | { |
334 | 0 | char *err = NULL; |
335 | 0 | int ret = 0; |
336 | 0 | tor_assert(new_state); |
337 | 0 | config_free(get_state_mgr(), global_state); |
338 | 0 | global_state = new_state; |
339 | 0 | if (subsystems_set_state(get_state_mgr(), global_state) < 0) { |
340 | 0 | ret = -1; |
341 | 0 | } |
342 | 0 | if (entry_guards_parse_state(global_state, 1, &err)<0) { |
343 | 0 | log_warn(LD_GENERAL,"%s",err); |
344 | 0 | tor_free(err); |
345 | 0 | ret = -1; |
346 | 0 | } |
347 | 0 | if (bwhist_load_state(global_state, &err)<0) { |
348 | 0 | log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err); |
349 | 0 | tor_free(err); |
350 | 0 | ret = -1; |
351 | 0 | } |
352 | 0 | if (circuit_build_times_parse_state( |
353 | 0 | get_circuit_build_times_mutable(),global_state) < 0) { |
354 | 0 | ret = -1; |
355 | 0 | } |
356 | |
|
357 | 0 | return ret; |
358 | 0 | } |
359 | | |
360 | | /** |
361 | | * Save a broken state file to a backup location. |
362 | | */ |
363 | | static void |
364 | | or_state_save_broken(char *fname) |
365 | 0 | { |
366 | 0 | int i, res; |
367 | 0 | file_status_t status; |
368 | 0 | char *fname2 = NULL; |
369 | 0 | for (i = 0; i < 100; ++i) { |
370 | 0 | tor_asprintf(&fname2, "%s.%d", fname, i); |
371 | 0 | status = file_status(fname2); |
372 | 0 | if (status == FN_NOENT) |
373 | 0 | break; |
374 | 0 | tor_free(fname2); |
375 | 0 | } |
376 | 0 | if (i == 100) { |
377 | 0 | log_warn(LD_BUG, "Unable to parse state in \"%s\"; too many saved bad " |
378 | 0 | "state files to move aside. Discarding the old state file.", |
379 | 0 | fname); |
380 | 0 | res = unlink(fname); |
381 | 0 | if (res != 0) { |
382 | 0 | log_warn(LD_FS, |
383 | 0 | "Also couldn't discard old state file \"%s\" because " |
384 | 0 | "unlink() failed: %s", |
385 | 0 | fname, strerror(errno)); |
386 | 0 | } |
387 | 0 | } else { |
388 | 0 | log_warn(LD_BUG, "Unable to parse state in \"%s\". Moving it aside " |
389 | 0 | "to \"%s\". This could be a bug in Tor; please tell " |
390 | 0 | "the developers.", fname, fname2); |
391 | 0 | if (tor_rename(fname, fname2) < 0) {//XXXX sandbox prohibits |
392 | 0 | log_warn(LD_BUG, "Weirdly, I couldn't even move the state aside. The " |
393 | 0 | "OS gave an error of %s", strerror(errno)); |
394 | 0 | } |
395 | 0 | } |
396 | 0 | tor_free(fname2); |
397 | 0 | } |
398 | | |
399 | | STATIC or_state_t * |
400 | | or_state_new(void) |
401 | 0 | { |
402 | 0 | or_state_t *new_state = config_new(get_state_mgr()); |
403 | 0 | config_init(get_state_mgr(), new_state); |
404 | |
|
405 | 0 | return new_state; |
406 | 0 | } |
407 | | |
408 | | /** Reload the persistent state from disk, generating a new state as needed. |
409 | | * Return 0 on success, less than 0 on failure. |
410 | | */ |
411 | | int |
412 | | or_state_load(void) |
413 | 0 | { |
414 | 0 | or_state_t *new_state = NULL; |
415 | 0 | char *contents = NULL, *fname; |
416 | 0 | char *errmsg = NULL; |
417 | 0 | int r = -1, badstate = 0; |
418 | |
|
419 | 0 | fname = get_datadir_fname("state"); |
420 | 0 | switch (file_status(fname)) { |
421 | 0 | case FN_FILE: |
422 | 0 | if (!(contents = read_file_to_str(fname, 0, NULL))) { |
423 | 0 | log_warn(LD_FS, "Unable to read state file \"%s\"", fname); |
424 | 0 | goto done; |
425 | 0 | } |
426 | 0 | break; |
427 | | /* treat empty state files as if the file doesn't exist, and generate |
428 | | * a new state file, overwriting the empty file in or_state_save() */ |
429 | 0 | case FN_NOENT: |
430 | 0 | case FN_EMPTY: |
431 | 0 | break; |
432 | 0 | case FN_ERROR: |
433 | 0 | case FN_DIR: |
434 | 0 | default: |
435 | 0 | log_warn(LD_GENERAL,"State file \"%s\" is not a file? Failing.", fname); |
436 | 0 | goto done; |
437 | 0 | } |
438 | 0 | new_state = or_state_new(); |
439 | 0 | if (contents) { |
440 | 0 | config_line_t *lines=NULL; |
441 | 0 | int assign_retval; |
442 | 0 | if (config_get_lines(contents, &lines, 0)<0) |
443 | 0 | goto done; |
444 | 0 | assign_retval = config_assign(get_state_mgr(), new_state, |
445 | 0 | lines, 0, &errmsg); |
446 | 0 | config_free_lines(lines); |
447 | 0 | if (assign_retval<0) |
448 | 0 | badstate = 1; |
449 | 0 | if (errmsg) { |
450 | 0 | log_warn(LD_GENERAL, "%s", errmsg); |
451 | 0 | tor_free(errmsg); |
452 | 0 | } |
453 | 0 | } |
454 | | |
455 | 0 | if (!badstate && or_state_validate(new_state, &errmsg) < 0) |
456 | 0 | badstate = 1; |
457 | |
|
458 | 0 | if (errmsg) { |
459 | 0 | log_warn(LD_GENERAL, "%s", errmsg); |
460 | 0 | tor_free(errmsg); |
461 | 0 | } |
462 | |
|
463 | 0 | if (badstate && !contents) { |
464 | 0 | log_warn(LD_BUG, "Uh oh. We couldn't even validate our own default state." |
465 | 0 | " This is a bug in Tor."); |
466 | 0 | goto done; |
467 | 0 | } else if (badstate && contents) { |
468 | 0 | or_state_save_broken(fname); |
469 | |
|
470 | 0 | tor_free(contents); |
471 | 0 | config_free(get_state_mgr(), new_state); |
472 | |
|
473 | 0 | new_state = or_state_new(); |
474 | 0 | } else if (contents) { |
475 | 0 | log_info(LD_GENERAL, "Loaded state from \"%s\"", fname); |
476 | | /* Warn the user if their clock has been set backwards, |
477 | | * they could be tricked into using old consensuses */ |
478 | 0 | time_t apparent_skew = time(NULL) - new_state->LastWritten; |
479 | 0 | if (apparent_skew < 0) { |
480 | | /* Initialize bootstrap event reporting because we might call |
481 | | * clock_skew_warning() before the bootstrap state is |
482 | | * initialized, causing an assertion failure. */ |
483 | 0 | control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0); |
484 | 0 | clock_skew_warning(NULL, (long)apparent_skew, 1, LD_GENERAL, |
485 | 0 | "local state file", fname); |
486 | 0 | } |
487 | 0 | } else { |
488 | 0 | log_info(LD_GENERAL, "Initialized state"); |
489 | 0 | } |
490 | 0 | or_state_remove_obsolete_lines(&new_state->ExtraLines); |
491 | 0 | if (or_state_set(new_state) == -1) { |
492 | 0 | or_state_save_broken(fname); |
493 | 0 | } |
494 | 0 | new_state = NULL; |
495 | 0 | if (!contents) { |
496 | 0 | global_state->next_write = 0; |
497 | 0 | or_state_save(time(NULL)); |
498 | 0 | } |
499 | 0 | r = 0; |
500 | |
|
501 | 0 | done: |
502 | 0 | tor_free(fname); |
503 | 0 | tor_free(contents); |
504 | 0 | if (new_state) |
505 | 0 | config_free(get_state_mgr(), new_state); |
506 | |
|
507 | 0 | return r; |
508 | 0 | } |
509 | | |
510 | | /** Remove from `extra_lines` every element whose key appears in |
511 | | * `obsolete_state_keys`. */ |
512 | | STATIC void |
513 | | or_state_remove_obsolete_lines(config_line_t **extra_lines) |
514 | 0 | { |
515 | | /* make a strmap for the obsolete state names, so we can have O(1) |
516 | | lookup. */ |
517 | 0 | strmap_t *bad_keys = strmap_new(); |
518 | 0 | for (unsigned i = 0; obsolete_state_keys[i] != NULL; ++i) { |
519 | 0 | strmap_set_lc(bad_keys, obsolete_state_keys[i], (void*)"rmv"); |
520 | 0 | } |
521 | |
|
522 | 0 | config_line_t **line = extra_lines; |
523 | 0 | while (*line) { |
524 | 0 | if (strmap_get_lc(bad_keys, (*line)->key) != NULL) { |
525 | | /* This key is obsolete; remove it. */ |
526 | 0 | config_line_t *victim = *line; |
527 | 0 | *line = (*line)->next; |
528 | |
|
529 | 0 | victim->next = NULL; // prevent double-free. |
530 | 0 | config_free_lines(victim); |
531 | 0 | } else { |
532 | | /* This is just an unrecognized key; keep it. */ |
533 | 0 | line = &(*line)->next; |
534 | 0 | } |
535 | 0 | } |
536 | |
|
537 | 0 | strmap_free(bad_keys, NULL); |
538 | 0 | } |
539 | | |
540 | | /** Did the last time we tried to write the state file fail? If so, we |
541 | | * should consider disabling such features as preemptive circuit generation |
542 | | * to compute circuit-build-time. */ |
543 | | static int last_state_file_write_failed = 0; |
544 | | |
545 | | /** Return whether the state file failed to write last time we tried. */ |
546 | | int |
547 | | did_last_state_file_write_fail(void) |
548 | 0 | { |
549 | 0 | return last_state_file_write_failed; |
550 | 0 | } |
551 | | |
552 | | /** If writing the state to disk fails, try again after this many seconds. */ |
553 | 0 | #define STATE_WRITE_RETRY_INTERVAL 3600 |
554 | | |
555 | | /** If we're a relay, how often should we checkpoint our state file even |
556 | | * if nothing else dirties it? This will checkpoint ongoing stats like |
557 | | * bandwidth used, per-country user stats, etc. */ |
558 | 0 | #define STATE_RELAY_CHECKPOINT_INTERVAL (12*60*60) |
559 | | |
560 | | /** Write the persistent state to disk. Return 0 for success, <0 on failure. */ |
561 | | int |
562 | | or_state_save(time_t now) |
563 | 0 | { |
564 | 0 | char *state, *contents; |
565 | 0 | char tbuf[ISO_TIME_LEN+1]; |
566 | 0 | char *fname; |
567 | |
|
568 | 0 | tor_assert(global_state); |
569 | | |
570 | 0 | if (global_state->next_write > now) |
571 | 0 | return 0; |
572 | | |
573 | | /* Call everything else that might dirty the state even more, in order |
574 | | * to avoid redundant writes. */ |
575 | 0 | (void) subsystems_flush_state(get_state_mgr(), global_state); |
576 | 0 | entry_guards_update_state(global_state); |
577 | 0 | bwhist_update_state(global_state); |
578 | 0 | circuit_build_times_update_state(get_circuit_build_times(), global_state); |
579 | |
|
580 | 0 | if (accounting_is_enabled(get_options())) |
581 | 0 | accounting_run_housekeeping(now); |
582 | |
|
583 | 0 | global_state->LastWritten = now; |
584 | |
|
585 | 0 | tor_free(global_state->TorVersion); |
586 | 0 | tor_asprintf(&global_state->TorVersion, "Tor %s", get_version()); |
587 | |
|
588 | 0 | state = config_dump(get_state_mgr(), NULL, global_state, 1, 0); |
589 | 0 | format_local_iso_time(tbuf, now); |
590 | 0 | tor_asprintf(&contents, |
591 | 0 | "# Tor state file last generated on %s local time\n" |
592 | 0 | "# Other times below are in UTC\n" |
593 | 0 | "# You *do not* need to edit this file.\n\n%s", |
594 | 0 | tbuf, state); |
595 | 0 | tor_free(state); |
596 | 0 | fname = get_datadir_fname("state"); |
597 | 0 | if (write_str_to_file(fname, contents, 0)<0) { |
598 | 0 | log_warn(LD_FS, "Unable to write state to file \"%s\"; " |
599 | 0 | "will try again later", fname); |
600 | 0 | last_state_file_write_failed = 1; |
601 | 0 | tor_free(fname); |
602 | 0 | tor_free(contents); |
603 | | /* Try again after STATE_WRITE_RETRY_INTERVAL (or sooner, if the state |
604 | | * changes sooner). */ |
605 | 0 | global_state->next_write = now + STATE_WRITE_RETRY_INTERVAL; |
606 | 0 | return -1; |
607 | 0 | } |
608 | | |
609 | 0 | last_state_file_write_failed = 0; |
610 | 0 | log_info(LD_GENERAL, "Saved state to \"%s\"", fname); |
611 | 0 | tor_free(fname); |
612 | 0 | tor_free(contents); |
613 | |
|
614 | 0 | if (server_mode(get_options())) |
615 | 0 | global_state->next_write = now + STATE_RELAY_CHECKPOINT_INTERVAL; |
616 | 0 | else |
617 | 0 | global_state->next_write = TIME_MAX; |
618 | |
|
619 | 0 | return 0; |
620 | 0 | } |
621 | | |
622 | | /** Return the config line for transport <b>transport</b> in the current state. |
623 | | * Return NULL if there is no config line for <b>transport</b>. */ |
624 | | STATIC config_line_t * |
625 | | get_transport_in_state_by_name(const char *transport) |
626 | 0 | { |
627 | 0 | or_state_t *or_state = get_or_state(); |
628 | 0 | config_line_t *line; |
629 | 0 | config_line_t *ret = NULL; |
630 | 0 | smartlist_t *items = NULL; |
631 | |
|
632 | 0 | for (line = or_state->TransportProxies ; line ; line = line->next) { |
633 | 0 | tor_assert(!strcmp(line->key, "TransportProxy")); |
634 | | |
635 | 0 | items = smartlist_new(); |
636 | 0 | smartlist_split_string(items, line->value, NULL, |
637 | 0 | SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); |
638 | 0 | if (smartlist_len(items) != 2) /* broken state */ |
639 | 0 | goto done; |
640 | | |
641 | 0 | if (!strcmp(smartlist_get(items, 0), transport)) { |
642 | 0 | ret = line; |
643 | 0 | goto done; |
644 | 0 | } |
645 | | |
646 | 0 | SMARTLIST_FOREACH(items, char*, s, tor_free(s)); |
647 | 0 | smartlist_free(items); |
648 | 0 | items = NULL; |
649 | 0 | } |
650 | | |
651 | 0 | done: |
652 | 0 | if (items) { |
653 | 0 | SMARTLIST_FOREACH(items, char*, s, tor_free(s)); |
654 | 0 | smartlist_free(items); |
655 | 0 | } |
656 | 0 | return ret; |
657 | 0 | } |
658 | | |
659 | | /** Return string containing the address:port part of the |
660 | | * TransportProxy <b>line</b> for transport <b>transport</b>. |
661 | | * If the line is corrupted, return NULL. */ |
662 | | static const char * |
663 | | get_transport_bindaddr(const char *line, const char *transport) |
664 | 0 | { |
665 | 0 | char *line_tmp = NULL; |
666 | |
|
667 | 0 | if (strlen(line) < strlen(transport) + 2) { |
668 | 0 | goto broken_state; |
669 | 0 | } else { |
670 | | /* line should start with the name of the transport and a space. |
671 | | (for example, "obfs2 127.0.0.1:47245") */ |
672 | 0 | tor_asprintf(&line_tmp, "%s ", transport); |
673 | 0 | if (strcmpstart(line, line_tmp)) |
674 | 0 | goto broken_state; |
675 | | |
676 | 0 | tor_free(line_tmp); |
677 | 0 | return (line+strlen(transport)+1); |
678 | 0 | } |
679 | | |
680 | 0 | broken_state: |
681 | 0 | tor_free(line_tmp); |
682 | 0 | return NULL; |
683 | 0 | } |
684 | | |
685 | | /** Return a string containing the address:port that a proxy transport |
686 | | * should bind on. The string is stored on the heap and must be freed |
687 | | * by the caller of this function. */ |
688 | | char * |
689 | | get_stored_bindaddr_for_server_transport(const char *transport) |
690 | 0 | { |
691 | 0 | char *default_addrport = NULL; |
692 | 0 | const char *stored_bindaddr = NULL; |
693 | 0 | config_line_t *line = NULL; |
694 | |
|
695 | 0 | { |
696 | | /* See if the user explicitly asked for a specific listening |
697 | | address for this transport. */ |
698 | 0 | char *conf_bindaddr = pt_get_bindaddr_from_config(transport); |
699 | 0 | if (conf_bindaddr) |
700 | 0 | return conf_bindaddr; |
701 | 0 | } |
702 | | |
703 | 0 | line = get_transport_in_state_by_name(transport); |
704 | 0 | if (!line) /* Found no references in state for this transport. */ |
705 | 0 | goto no_bindaddr_found; |
706 | | |
707 | 0 | stored_bindaddr = get_transport_bindaddr(line->value, transport); |
708 | 0 | if (stored_bindaddr) /* found stored bindaddr in state file. */ |
709 | 0 | return tor_strdup(stored_bindaddr); |
710 | | |
711 | 0 | no_bindaddr_found: |
712 | | /** If we didn't find references for this pluggable transport in the |
713 | | state file, we should instruct the pluggable transport proxy to |
714 | | listen on INADDR_ANY on a random ephemeral port. */ |
715 | 0 | tor_asprintf(&default_addrport, "%s:%s", fmt_addr32(INADDR_ANY), "0"); |
716 | 0 | return default_addrport; |
717 | 0 | } |
718 | | |
719 | | /** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to |
720 | | state */ |
721 | | void |
722 | | save_transport_to_state(const char *transport, |
723 | | const tor_addr_t *addr, uint16_t port) |
724 | 0 | { |
725 | 0 | or_state_t *state = get_or_state(); |
726 | |
|
727 | 0 | char *transport_addrport=NULL; |
728 | | |
729 | | /** find where to write on the state */ |
730 | 0 | config_line_t **next, *line; |
731 | | |
732 | | /* see if this transport is already stored in state */ |
733 | 0 | config_line_t *transport_line = |
734 | 0 | get_transport_in_state_by_name(transport); |
735 | |
|
736 | 0 | if (transport_line) { /* if transport already exists in state... */ |
737 | 0 | const char *prev_bindaddr = /* get its addrport... */ |
738 | 0 | get_transport_bindaddr(transport_line->value, transport); |
739 | 0 | transport_addrport = tor_strdup(fmt_addrport(addr, port)); |
740 | | |
741 | | /* if transport in state has the same address as this one, life is good */ |
742 | 0 | if (!strcmp(prev_bindaddr, transport_addrport)) { |
743 | 0 | log_info(LD_CONFIG, "Transport seems to have spawned on its usual " |
744 | 0 | "address:port."); |
745 | 0 | goto done; |
746 | 0 | } else { /* if addrport in state is different than the one we got */ |
747 | 0 | log_info(LD_CONFIG, "Transport seems to have spawned on different " |
748 | 0 | "address:port. Let's update the state file with the new " |
749 | 0 | "address:port"); |
750 | 0 | tor_free(transport_line->value); /* free the old line */ |
751 | | /* replace old addrport line with new line */ |
752 | 0 | tor_asprintf(&transport_line->value, "%s %s", transport, |
753 | 0 | fmt_addrport(addr, port)); |
754 | 0 | } |
755 | 0 | } else { /* never seen this one before; save it in state for next time */ |
756 | 0 | log_info(LD_CONFIG, "It's the first time we see this transport. " |
757 | 0 | "Let's save its address:port"); |
758 | 0 | next = &state->TransportProxies; |
759 | | /* find the last TransportProxy line in the state and point 'next' |
760 | | right after it */ |
761 | 0 | line = state->TransportProxies; |
762 | 0 | while (line) { |
763 | 0 | next = &(line->next); |
764 | 0 | line = line->next; |
765 | 0 | } |
766 | | |
767 | | /* allocate space for the new line and fill it in */ |
768 | 0 | *next = line = tor_malloc_zero(sizeof(config_line_t)); |
769 | 0 | line->key = tor_strdup("TransportProxy"); |
770 | 0 | tor_asprintf(&line->value, "%s %s", transport, fmt_addrport(addr, port)); |
771 | 0 | } |
772 | | |
773 | 0 | if (!get_options()->AvoidDiskWrites) |
774 | 0 | or_state_mark_dirty(state, 0); |
775 | |
|
776 | 0 | done: |
777 | 0 | tor_free(transport_addrport); |
778 | 0 | } |
779 | | |
780 | | /** Change the next_write time of <b>state</b> to <b>when</b>, unless the |
781 | | * state is already scheduled to be written to disk earlier than <b>when</b>. |
782 | | */ |
783 | | void |
784 | | or_state_mark_dirty(or_state_t *state, time_t when) |
785 | 0 | { |
786 | 0 | if (state->next_write > when) { |
787 | 0 | state->next_write = when; |
788 | 0 | reschedule_or_state_save(); |
789 | 0 | } |
790 | 0 | } |
791 | | |
792 | | STATIC void |
793 | | or_state_free_(or_state_t *state) |
794 | 0 | { |
795 | 0 | if (!state) |
796 | 0 | return; |
797 | | |
798 | 0 | config_free(get_state_mgr(), state); |
799 | 0 | } |
800 | | |
801 | | void |
802 | | or_state_free_all(void) |
803 | 0 | { |
804 | 0 | or_state_free(global_state); |
805 | 0 | global_state = NULL; |
806 | 0 | config_mgr_free(state_mgr); |
807 | 0 | } |