1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
|
#!/bin/sh
# Copyright 2019, The Tor Project, Inc.
# See LICENSE for licensing information
# Integration test script for verifying that Tor configurations are parsed as
# we expect.
#
# Valid configurations are tested with --dump-config, which parses and
# validates the configuration before writing it out. We then make sure that
# the result is what we expect, before parsing and dumping it again to make
# sure that there is no change. Optionally, we can also test the log messages
# with --verify-config.
#
# Invalid configurations are tested with --verify-config, which parses
# and validates the configuration. We capture its output and make sure that
# it contains the error message we expect.
#
# When tor is compiled with different libraries or modules, some
# configurations may have different results. We can specify these result
# variants using additional result files.
# This script looks for its test cases as individual directories in
# src/test/conf_examples/. Each test may have these files:
#
# Configuration Files
#
# torrc -- Usually needed. This file is passed to Tor on the command line
# with the "-f" flag. (If you omit it, you'll test Tor's behavior when
# it receives a nonexistent configuration file.)
#
# torrc.defaults -- Optional. If present, it is passed to Tor on the command
# line with the --defaults-torrc option. If this file is absent, an empty
# file is passed instead to prevent Tor from reading the system defaults.
#
# cmdline -- Optional. If present, it contains command-line arguments that
# will be passed to Tor.
#
# (included torrc files or directories) -- Optional. Additional files can be
# included in configuration, using the "%include" directive. Files or
# directories can be included in any of the config files listed above.
# Include paths should be specified relative to the test case directory.
#
# Result Files
#
# expected -- If this file is present, then it should be the expected result
# of "--dump-config short" for this test case. Exactly one of
# "expected" or "error" must be present, or the test will fail.
#
# expected_log -- Optional. If this file is present, then it contains a regex
# that must be matched by some line in the output of "--verify-config",
# which must succeed. Only used if "expected" is also present.
#
# error -- If this file is present, then it contains a regex that must be
# matched by some line in the output of "--verify-config", which must
# fail. Exactly one of "expected" or "error" must be present, or the
# test will fail.
#
# {expected,expected_log,error}_${TOR_LIBS_ENABLED}* -- If this file is
# present, then the outcome is different when some optional libraries are
# enabled. If there is no result file matching the exact list of enabled
# libraries, the script searches for result files with one or more of
# those libraries disabled. The search terminates at the standard result
# file. If expected* is present, the script also searches for
# expected_log*.
#
# For example:
# A test that succeeds, regardless of any enabled libraries:
# - expected
# A test that has a different result if the nss library is enabled
# (but the same result if any other library is enabled). We also check
# the log output in this test:
# - expected
# - expected_log
# - expected_nss
# - expected_log_nss
# A test that fails if the lzma and zstd modules are *not* enabled:
# - error
# - expected_lzma_zstd
#
# {expected,expected_log,error}*_no_${TOR_MODULES_DISABLED} -- If this file is
# present, then the outcome is different when some modules are disabled.
# If there is no result file matching the exact list of disabled modules,
# the standard result file is used. If expected* is present, the script
# also searches for expected_log*.
#
# For example:
# A test that succeeds, regardless of any disabled modules:
# - expected
# A test that has a different result if the relay module is disabled
# (but the same result if just the dirauth module is disabled):
# - expected
# - expected_no_relay_dirauth
# A test that fails if the dirauth module is disabled:
# - expected
# - error_no_dirauth
# - error_no_relay_dirauth
# (Disabling the relay module also disables dirauth module. But we don't
# want to encode that knowledge in this test script, so we supply a
# separate result file for every combination of disabled modules that
# has a different result.)
umask 077
set -e
# emulate realpath(), in case coreutils or equivalent is not installed.
abspath() {
f="$*"
if [ -d "$f" ]; then
dir="$f"
base=""
else
dir="$(dirname "$f")"
base="/$(basename "$f")"
fi
dir="$(cd "$dir" && pwd)"
echo "$dir$base"
}
# find the tor binary
if [ $# -ge 1 ]; then
TOR_BINARY="${1}"
shift
else
TOR_BINARY="${TESTING_TOR_BINARY:-./src/app/tor}"
fi
TOR_BINARY="$(abspath "$TOR_BINARY")"
echo "TOR BINARY IS ${TOR_BINARY}"
# make a safe space for temporary files
DATA_DIR=$(mktemp -d -t tor_parseconf_tests.XXXXXX)
trap 'rm -rf "$DATA_DIR"' 0
# This is where we look for examples
EXAMPLEDIR="$(dirname "$0")"/conf_examples
case "$(uname -s)" in
CYGWIN*) WINDOWS=1;;
MINGW*) WINDOWS=1;;
MSYS*) WINDOWS=1;;
*) WINDOWS=0;;
esac
####
# BUG WORKAROUND FOR 31757:
# On Appveyor, it seems that Tor sometimes randomly fails to produce
# output with --dump-config. Whil we are figuring this out, do not treat
# windows errors as hard failures.
####
if test "$WINDOWS" = 1; then
EXITCODE=0
else
EXITCODE=1
fi
FINAL_EXIT=0
die() { echo "$1" >&2 ; FINAL_EXIT=$EXITCODE; }
if test "$WINDOWS" = 1; then
FILTER="dos2unix"
else
FILTER="cat"
fi
EMPTY="${DATA_DIR}/EMPTY"
touch "$EMPTY" || die "Couldn't create empty file."
STANDARD_LIBS="libevent\\|openssl\\|zlib"
# Lib names are restricted to [a-z0-9]* at the moment
# We don't actually want to support foreign accents here
# shellcheck disable=SC2018,SC2019
TOR_LIBS_ENABLED="$("$TOR_BINARY" --verify-config \
-f "$EMPTY" --defaults-torrc "$EMPTY" \
| sed -n 's/.* Tor .* running on .* with\(.*\)\./\1/p' \
| tr 'A-Z' 'a-z' | tr ',' '\n' \
| grep -v "$STANDARD_LIBS" | grep -v "n/a" \
| sed 's/\( and\)* \(lib\)*\([a-z0-9]*\) .*/\3/' \
| sort | tr '\n' '_')"
# Remove the last underscore, if there is one
TOR_LIBS_ENABLED=${TOR_LIBS_ENABLED%_}
# If we ever have more than 3 optional libraries, we'll need more code here
TOR_LIBS_ENABLED_COUNT="$(echo "$TOR_LIBS_ENABLED_SEARCH" \
| tr ' ' '\n' | wc -l)"
if [ "$TOR_LIBS_ENABLED_COUNT" -gt 3 ]; then
echo "$0 can not handle more than 3 optional libraries"
exit 1
fi
# Brute-force the combinations of libraries
TOR_LIBS_ENABLED_SEARCH_3="$(echo "$TOR_LIBS_ENABLED" \
| sed -n \
's/^\([^_]*\)_\([^_]*\)_\([^_]*\)$/_\1_\2 _\1_\3 _\2_\3 _\1 _\2 _\3/p')"
TOR_LIBS_ENABLED_SEARCH_2="$(echo "$TOR_LIBS_ENABLED" \
| sed -n 's/^\([^_]*\)_\([^_]*\)$/_\1 _\2/p')"
TOR_LIBS_ENABLED_SEARCH="_$TOR_LIBS_ENABLED \
$TOR_LIBS_ENABLED_SEARCH_3 \
$TOR_LIBS_ENABLED_SEARCH_2"
TOR_LIBS_ENABLED_SEARCH="$(echo "$TOR_LIBS_ENABLED_SEARCH" | tr ' ' '\n' \
| grep -v '^_*$' | tr '\n' ' ')"
TOR_MODULES_DISABLED="$("$TOR_BINARY" --list-modules | grep ': no' \
| cut -d ':' -f1 | sort | tr '\n' '_')"
# Remove the last underscore, if there is one
TOR_MODULES_DISABLED=${TOR_MODULES_DISABLED%_}
echo "Tor is configured with:"
echo "Optional Libraries: ${TOR_LIBS_ENABLED:-(None)}"
if [ -n "${TOR_LIBS_ENABLED}" ]; then
echo "Optional Library Search List: $TOR_LIBS_ENABLED_SEARCH"
fi
echo "Disabled Modules: ${TOR_MODULES_DISABLED:-(None)}"
for dir in "${EXAMPLEDIR}"/*; do
if ! test -d "${dir}"; then
# Only count directories.
continue
fi
testname="$(basename "${dir}")"
# We use printf since "echo -n" is not standard
printf "%s: " "$testname"
PREV_DIR="$(pwd)"
cd "${dir}"
if test -f "./torrc.defaults"; then
DEFAULTS="./torrc.defaults"
else
DEFAULTS="${DATA_DIR}/EMPTY"
fi
if test -f "./cmdline"; then
CMDLINE="$(cat ./cmdline)"
else
CMDLINE=""
fi
EXPECTED=
EXPECTED_LOG=
ERROR=
# Search for a custom result file for any combination of enabled optional
# libraries
# The libs in the list are [A-Za-z0-9_]* and space-separated.
# shellcheck disable=SC2086
for lib_suffix in $TOR_LIBS_ENABLED_SEARCH ""; do
# Search for a custom result file for any disabled modules
for mod_suffix in "_no_${TOR_MODULES_DISABLED}" ""; do
suffix="${lib_suffix}${mod_suffix}"
if test -f "./expected${suffix}"; then
# Check for broken configs
if test -f "./error${suffix}"; then
echo "FAIL: Found both ${dir}/expected${suffix}" >&2
echo "and ${dir}/error${suffix}." >&2
echo "(Only one of these files should exist.)" >&2
FINAL_EXIT=$EXITCODE
fi
EXPECTED="./expected${suffix}"
if test -f "./expected_log${suffix}"; then
EXPECTED_LOG="./expected_log${suffix}"
fi
break
elif test -f "./error${suffix}"; then
ERROR="./error${suffix}"
break
fi
done
# Exit as soon as the inner loop finds a file
if test -f "$EXPECTED" || test -f "$ERROR"; then
break
fi
done
if test -f "$EXPECTED"; then
# This case should succeed: run dump-config and see if it does.
FAILED_LOG=
FAILED_CONFIG=
if test -f "$EXPECTED_LOG"; then
if ! test -s "$EXPECTED_LOG"; then
echo "FAIL: expected log file '$EXPECTED_LOG' is empty." >&2
echo "Empty expected log files match any output." >&2
FINAL_EXIT=$EXITCODE
fi
fi
"${TOR_BINARY}" -f "./torrc" \
--defaults-torrc "${DEFAULTS}" \
--dump-config short \
${CMDLINE} \
| "${FILTER}" > "${DATA_DIR}/output.${testname}" \
|| die "FAIL: $EXPECTED: Tor reported an error."
if cmp "$EXPECTED" "${DATA_DIR}/output.${testname}">/dev/null ; then
# Check round-trip.
"${TOR_BINARY}" -f "${DATA_DIR}/output.${testname}" \
--defaults-torrc "${DATA_DIR}/empty" \
--dump-config short \
| "${FILTER}" \
> "${DATA_DIR}/output_2.${testname}" \
|| die \
"FAIL: $EXPECTED: Tor reported an error on round-trip."
if ! cmp "${DATA_DIR}/output.${testname}" \
"${DATA_DIR}/output_2.${testname}"; then
echo "FAIL: $EXPECTED did not match on round-trip:" >&2
diff -u "${DATA_DIR}/output.${testname}" \
"${DATA_DIR}/output_2.${testname}" >&2 \
|| true
FINAL_EXIT=$EXITCODE
fi
else
FAILED_CONFIG="yes"
if test "$(wc -c < "${DATA_DIR}/output.${testname}")" = 0; then
echo "FAIL: $EXPECTED: Tor said:" >&2
# There was no output -- probably we failed.
"${TOR_BINARY}" -f "./torrc" \
--defaults-torrc "${DEFAULTS}" \
--verify-config \
${CMDLINE} || true
fi
echo "FAIL: $EXPECTED did not match:" >&2
diff -u "$EXPECTED" "${DATA_DIR}/output.${testname}" >&2 \
|| true
FINAL_EXIT=$EXITCODE
fi
if test -f "$EXPECTED_LOG"; then
# This case should succeed: run verify-config and see if it does.
"${TOR_BINARY}" --verify-config \
-f ./torrc \
--defaults-torrc "${DEFAULTS}" \
${CMDLINE} \
> "${DATA_DIR}/output_log.${testname}" \
|| die "FAIL: $EXPECTED_LOG: Tor reported an error."
expect_log="$(cat "$EXPECTED_LOG")"
if grep "${expect_log}" "${DATA_DIR}/output_log.${testname}" \
>/dev/null; then
:
else
FAILED_LOG="yes"
echo "FAIL: Expected $EXPECTED_LOG:" >&2
echo "${expect_log}" >&2
echo "Tor said:" >&2
cat "${DATA_DIR}/output_log.${testname}" >&2
FINAL_EXIT=$EXITCODE
fi
fi
if test -z "${FAILED_LOG}${FAILED_CONFIG}"; then
echo "OK"
fi
elif test -f "$ERROR"; then
# This case should fail: run verify-config and see if it does.
if ! test -s "$ERROR"; then
echo "FAIL: error file '$ERROR' is empty." >&2
echo "Empty error files match any output." >&2
FINAL_EXIT=$EXITCODE
fi
"${TOR_BINARY}" --verify-config \
-f ./torrc \
--defaults-torrc "${DEFAULTS}" \
${CMDLINE} \
> "${DATA_DIR}/output.${testname}" \
&& die "FAIL: $ERROR: Tor did not report an error."
expect_err="$(cat "$ERROR")"
if grep "${expect_err}" "${DATA_DIR}/output.${testname}" >/dev/null; then
echo "OK"
else
echo "FAIL: Expected $ERROR: " >&2
echo "${expect_err}" >&2
echo "Tor said:" >&2
cat "${DATA_DIR}/output.${testname}" >&2
FINAL_EXIT=$EXITCODE
fi
else
# This case is not actually configured with a success or a failure.
# call that an error.
echo "FAIL: Did not find ${dir}/*expected or ${dir}/*error." >&2
FINAL_EXIT=$EXITCODE
fi
cd "${PREV_DIR}"
done
exit $FINAL_EXIT
|