+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initialize two nodes
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Port 30070
-set Address localhost
-set ExperimentalProtocol no
-EOF
-
-tinc bar <<EOF
-init bar
-set DeviceType dummy
-set Port 0
-set ExperimentalProtocol no
-EOF
-
-create_script foo hosts/bar-up
-create_script bar hosts/foo-up
-
-echo [STEP] Exchange configuration
-
-tinc foo export | tinc bar exchange | tinc foo import
-tinc bar add ConnectTo foo
-start_tinc foo
-
-echo [STEP] Test various ciphers and digests
-
-# The full suite results in a large test matrix and it takes a lot of time to run.
-# The list was reduced to the strongest available algorithms (and "none").
-digests="none sha256 sha512"
-ciphers="none aes-256-cbc"
-
-for digest in $digests; do
- for cipher in $ciphers; do
- echo "Testing $cipher $digest"
-
- tinc bar <<EOF
-set Digest $digest
-set Cipher $cipher
-EOF
-
- start_tinc bar
- wait_script foo hosts/bar-up
- wait_script bar hosts/foo-up
-
- tinc foo info bar | grep reachable
-
- tinc bar stop
- done
-done
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initialize and test one node
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Port 0
-EOF
-
-echo [STEP] Test running in the foreground
-
-create_script foo tinc-up '
- tinc foo stop &
-'
-start_tinc foo -D
-
-echo [STEP] Test running tinc in the background
-
-create_script foo tinc-up '
- tinc foo stop &
-'
-start_tinc foo
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-foo_dir=$(peer_directory foo)
-foo_host=$foo_dir/hosts/foo
-foo_conf=$foo_dir/tinc.conf
-foo_rsa_priv=$foo_dir/rsa_key.priv
-foo_ec_priv=$foo_dir/ed25519_key.priv
-foo_tinc_up=$foo_dir/tinc-up
-foo_host_up=$foo_dir/host-up
-
-if is_windows; then
- foo_tinc_up=$foo_tinc_up.cmd
- foo_host_up=$foo_host_up.cmd
-fi
-
-# Sample RSA key pair (old format). Uses e = 0xFFFF.
-rsa_n=BB82C3A9B906E98ABF2D99FF9B320B229F5C1E58EC784762DA1F4D3509FFF78ECA7FFF19BA170736CDE458EC8E732DDE2C02009632DF731B4A6BD6C504E50B7B875484506AC1E49FD0DF624F6612F564C562BD20F870592A49195023D744963229C35081C8AE48BE2EBB5CC9A0D64924022DC0EB782A3A8F3EABCA04AA42B24B2A6BD2353A6893A73AE01FA54891DD24BF36CA032F19F7E78C01273334BAA2ECF36B6998754CB012BC985C975503D945E4D925F6F719ACC8FBA7B18C810FF850C3CCACD60565D4FCFE02A98FE793E2D45D481A34D1F90584D096561FF3184C462C606535F3F9BB260541DF0D1FEB16938FFDEC2FF96ACCC6BD5BFBC19471F6AB
-rsa_d=8CEC9A4316FE45E07900197D8FBB52D3AF01A51C4F8BD08A1E21A662E3CFCF7792AD7680673817B70AC1888A08B49E8C5835357016D9BF56A0EBDE8B5DF214EC422809BC8D88177F273419116EF2EC7951453F129768DE9BC31D963515CC7481559E4C0E65C549169F2B94AE68DB944171189DD654DC6970F2F5843FB7C8E9D057E2B5716752F1F5686811AC075ED3D3CBD06B5D35AE33D01260D9E0560AF545D0C9D89A31D5EAF96D5422F6567FE8A90E23906B840545805644DFD656E526A686D3B978DD271578CA3DA0F7D23FC1252A702A5D597CAE9D4A5BBF6398A75AF72582C7538A7937FB71A2610DCBC39625B77103FA3B7D0A55177FD98C39CD4A27
-
-# Extracts the PEM key from a config file, leaving the file unchanged.
-# usage: extract_pem_key_from_config path_to_file
-extract_pem_key_from_config() {
- sed -n '/-----BEGIN /,/-----END /p' "$1"
-}
-
-# Removes the PEM key from a config file.
-# usage: rm_pem_key_from_config path_to_file
-rm_pem_key_from_config() {
- sed_cmd '/-----BEGIN /,/-----END /d' "$1"
-}
-
-reinit_configs() {
- if [ -d "$foo_dir" ]; then
- chmod -f 755 "$foo_dir"
- rm -rf "$foo_dir"
- fi
-
- tinc foo <<EOF
-init foo
-set DeviceType dummy
-EOF
-}
-
-fsck_test() {
- echo >&2 "[STEP] $*"
- reinit_configs
-}
-
-run_access_checks() {
- ! is_root && ! is_windows
-}
-
-test_private_keys() {
- keyfile=$1
-
- fsck_test "Must fail on broken $keyfile"
- printf '' >"$foo_dir/$keyfile"
- if with_legacy; then
- expect_msg 'no private key is known' tinc foo fsck
- else
- must_fail_with_msg 'no Ed25519 private key found' tinc foo fsck
- fi
-
- if run_access_checks; then
- fsck_test "Must fail on inaccessible $keyfile"
- chmod 000 "$foo_dir/$keyfile"
- if with_legacy; then
- expect_msg 'error reading' tinc foo fsck
- else
- must_fail_with_msg 'error reading' tinc foo fsck
- fi
- fi
-
- if ! is_windows; then
- fsck_test "Must warn about unsafe permissions on $keyfile"
- chmod 666 "$foo_dir/$keyfile"
- expect_msg 'unsafe file permissions' tinc foo fsck
- fi
-
- if with_legacy; then
- fsck_test "Must pass on missing $keyfile when the other key is present"
- rm -f "$foo_dir/$keyfile"
- tinc foo fsck
- fi
-}
-
-test_private_key_var() {
- var=$1
- keyfile=$2
-
- fsck_test "Must find private key at $var"
- mv "$foo_dir/$keyfile" "$foo_dir/renamed_private_key"
- echo "$var = $(normalize_path "$foo_dir/renamed_private_key")" >>"$foo_conf"
- fail_on_msg 'key was found but no private key' tinc foo fsck
-}
-
-test_ec_public_key_file_var() {
- conf=$1
- fsck_test "EC public key in Ed25519PublicKeyFile in $conf must work"
- cat >"$foo_dir/ec_pubkey" <<EOF
------BEGIN ED25519 PUBLIC KEY-----
-$(awk '/^Ed25519PublicKey/ { printf $NF }' "$foo_host")
------END ED25519 PUBLIC KEY-----
-EOF
- sed_cmd '/Ed25519PublicKey/d' "$foo_host"
- echo "Ed25519PublicKeyFile = $(normalize_path "$foo_dir/ec_pubkey")" >>"$foo_dir/$conf"
- fail_on_msg 'no (usable) public Ed25519' tinc foo fsck
-}
-
-test_rsa_public_key_file_var() {
- conf=$1
- fsck_test "RSA public key in PublicKeyFile in $conf must work"
- extract_pem_key_from_config "$foo_host" >"$foo_dir/rsa_pubkey"
- rm_pem_key_from_config "$foo_host"
- echo "PublicKeyFile = $(normalize_path "$foo_dir/rsa_pubkey")" >>"$foo_dir/$conf"
- fail_on_msg 'error reading RSA public key' tinc foo fsck
-}
-
-fsck_test 'Newly created configuration should pass'
-tinc foo fsck
-
-fsck_test 'Must fail on missing tinc.conf'
-rm -f "$foo_conf"
-must_fail_with_msg 'no tinc configuration found' tinc foo fsck
-
-if run_access_checks; then
- fsck_test 'Must fail on inaccessible tinc.conf'
- chmod 000 "$foo_dir"
- must_fail_with_msg 'not running tinc as root' tinc foo fsck
-fi
-
-if ! is_windows; then
- fsck_test 'Non-executable tinc-up MUST be fixed by tinc --force'
- chmod a-x "$foo_tinc_up"
- expect_msg 'cannot read and execute' tinc foo --force fsck
- test -x "$foo_tinc_up"
-
- fsck_test 'Non-executable tinc-up MUST NOT be fixed by tinc without --force'
- chmod a-x "$foo_tinc_up"
- expect_msg 'cannot read and execute' tinc foo fsck
- must_fail test -x "$foo_tinc_up"
-fi
-
-fsck_test 'Unknown -up script warning'
-touch "$foo_dir/fake-up"
-expect_msg 'unknown script' tinc foo fsck
-
-fsck_test 'Unknown -down script warning'
-touch "$foo_dir/fake-down"
-expect_msg 'unknown script' tinc foo fsck
-
-if ! is_windows; then
- fsck_test 'Non-executable foo-up MUST be fixed by tinc --force'
- touch "$foo_host_up"
- chmod a-x "$foo_host_up"
- expect_msg 'cannot read and execute' tinc foo --force fsck
- test -x "$foo_tinc_up"
-
- fsck_test 'Non-executable bar-up MUST NOT be fixed by tinc'
- touch "$foo_dir/hosts/bar-up"
- chmod a-x "$foo_dir/hosts/bar-up"
- expect_msg 'cannot read and execute' tinc foo fsck
- must_fail test -x "$foo_dir/bar-up"
-fi
-
-if run_access_checks; then
- fsck_test 'Inaccessible hosts/foo must fail'
- chmod 000 "$foo_host"
- must_fail_with_msg 'cannot open config file' tinc foo fsck
-fi
-
-fsck_test 'Must fail when all private keys are missing'
-rm -f "$foo_ec_priv" "$foo_rsa_priv"
-if with_legacy; then
- must_fail_with_msg 'neither RSA or Ed25519 private key' tinc foo fsck
-else
- must_fail_with_msg 'no Ed25519 private key' tinc foo fsck
-fi
-
-if with_legacy; then
- test_private_keys rsa_key.priv
-
- if ! is_windows; then
- fsck_test 'Must warn about unsafe permissions on tinc.conf with PrivateKey'
- rm -f "$foo_rsa_priv"
- echo "PrivateKey = $rsa_d" >>"$foo_conf"
- echo "PublicKey = $rsa_n" >>"$foo_host"
- chmod 666 "$foo_conf"
- expect_msg 'unsafe file permissions' tinc foo fsck
- fi
-
- fsck_test 'Must warn about missing RSA private key if public key is present'
- rm -f "$foo_rsa_priv"
- expect_msg 'public RSA key was found but no private key' tinc foo fsck
-
- fsck_test 'Must warn about missing RSA public key'
- rm_pem_key_from_config "$foo_host"
- expect_msg 'no (usable) public RSA' tinc foo fsck
- must_fail grep -q 'BEGIN RSA PUBLIC KEY' "$foo_host"
-
- fsck_test 'Must fix missing RSA public key on --force'
- rm_pem_key_from_config "$foo_host"
- expect_msg 'wrote RSA public key' tinc foo --force fsck
- grep -q 'BEGIN RSA PUBLIC KEY' "$foo_host"
-
- test_private_key_var PrivateKeyFile rsa_key.priv
-
- test_rsa_public_key_file_var tinc.conf
- test_rsa_public_key_file_var hosts/foo
-
- fsck_test 'RSA PublicKey + PrivateKey must work'
- rm -f "$foo_rsa_priv"
- rm_pem_key_from_config "$foo_host"
- echo "PrivateKey = $rsa_d" >>"$foo_conf"
- echo "PublicKey = $rsa_n" >>"$foo_host"
- fail_on_msg 'no (usable) public RSA' tinc foo fsck
-
- fsck_test 'RSA PrivateKey without PublicKey must warn'
- rm -f "$foo_rsa_priv"
- rm_pem_key_from_config "$foo_host"
- echo "PrivateKey = $rsa_d" >>"$foo_conf"
- expect_msg 'PrivateKey used but no PublicKey found' tinc foo fsck
-
- fsck_test 'Must warn about missing EC private key if public key is present'
- rm -f "$foo_ec_priv"
- expect_msg 'public Ed25519 key was found but no private key' tinc foo fsck
-
- fsck_test 'Must fix broken RSA public key with --force'
- sed_cmd 2d "$foo_host"
- expect_msg 'old key(s) found and disabled' tinc foo --force fsck
- tinc foo fsck
-
- fsck_test 'Must fix missing RSA public key with --force'
- rm_pem_key_from_config "$foo_host"
- expect_msg 'no (usable) public RSA key found' tinc foo --force fsck
- tinc foo fsck
-fi
-
-fsck_test 'Must fix broken Ed25519 public key with --force'
-sed_cmd 's/Ed25519PublicKey.*/Ed25519PublicKey = foobar/' "$foo_host"
-expect_msg 'no (usable) public Ed25519 key' tinc foo --force fsck
-tinc foo fsck
-
-fsck_test 'Must fix missing Ed25519 public key with --force'
-sed_cmd '/Ed25519PublicKey/d' "$foo_host"
-expect_msg 'no (usable) public Ed25519 key' tinc foo --force fsck
-tinc foo fsck
-
-test_private_keys ed25519_key.priv
-test_private_key_var Ed25519PrivateKeyFile ed25519_key.priv
-
-test_ec_public_key_file_var tinc.conf
-test_ec_public_key_file_var hosts/foo
-
-fsck_test 'Must warn about missing EC public key and NOT fix without --force'
-sed_cmd '/Ed25519PublicKey/d' "$foo_host"
-expect_msg 'no (usable) public Ed25519' tinc foo fsck
-must_fail grep -q 'ED25519 PUBLIC KEY' "$foo_host"
-
-fsck_test 'Must fix missing EC public key on --force'
-sed_cmd '/Ed25519PublicKey/d' "$foo_host"
-expect_msg 'wrote Ed25519 public key' tinc foo --force fsck
-grep -q 'ED25519 PUBLIC KEY' "$foo_host"
-
-fsck_test 'Must warn about obsolete variables'
-echo 'GraphDumpFile = /dev/null' >>"$foo_host"
-expect_msg 'obsolete variable GraphDumpFile' tinc foo fsck
-
-fsck_test 'Must warn about missing values'
-echo 'Weight = ' >>"$foo_host"
-must_fail_with_msg 'no value for variable `Weight' tinc foo fsck
-
-fsck_test 'Must warn about duplicate variables'
-echo 'Weight = 0' >>"$foo_host"
-echo 'Weight = 1' >>"$foo_host"
-expect_msg 'multiple instances of variable Weight' tinc foo fsck
-
-fsck_test 'Must warn about server variables in host config'
-echo 'Interface = fake0' >>"$foo_host"
-expect_msg 'server variable Interface found' tinc foo fsck
-
-fsck_test 'Must warn about host variables in server config'
-echo 'Port = 1337' >>"$foo_conf"
-expect_msg 'host variable Port found' tinc foo fsck
-
-fsck_test 'Must warn about missing Name'
-sed_cmd '/^Name =/d' "$foo_conf"
-must_fail_with_msg 'without a valid Name' tinc foo fsck
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initialize one node
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Port 0
-EOF
-
-create_script foo tinc-up '
- tinc foo stop &
-'
-
-echo [STEP] Test tincd command line options that should work
-
-tincd foo -D
-tincd foo --no-detach
-tincd foo -D -d
-tincd foo -D -d2
-tincd foo -D -d 2
-tincd foo -D -n foo
-tincd foo -D -nfoo
-tincd foo -D --net=foo
-tincd foo -D --net foo
-
-echo [STEP] Test tincd command line options that should not work
-
-expect_code "$EXIT_FAILURE" tincd foo foo
-expect_code "$EXIT_FAILURE" tincd foo --pidfile
-expect_code "$EXIT_FAILURE" tincd foo --foo
-
-echo [STEP] Test tinc command line options that should work
-
-tinc foo get name
-tinc foo -n foo get name
-tinc foo -nfoo get name
-tinc foo --net=foo get name
-tinc foo --net foo get name
-
-echo [STEP] Test tinc command line options that should not work
-
-expect_code "$EXIT_FAILURE" tinc foo -n foo get somethingreallyunknown
-expect_code "$EXIT_FAILURE" tinc foo --net
-expect_code "$EXIT_FAILURE" tinc foo --net get name
-expect_code "$EXIT_FAILURE" tinc foo foo
-
-# Most of these should fail with ASAN. Some leaks are only detected by Valgrind.
-echo [STEP] Trigger previously known memory leaks
-
-tincd foo -c . -c . --help
-tincd foo -n net -n net --help
-tincd foo -n net -o FakeOpt=42 --help
-tincd foo --logfile=one --logfile=two --help
-tincd foo --pidfile=one --pidfile=two --help
-expect_code "$EXIT_FAILURE" tincd foo -n net -o Compression= --help
-expect_code "$EXIT_FAILURE" tincd foo -c fakedir -n 'n/e\t'
-
-tinc foo -c conf -c conf --help
-tinc foo -n net -n net --help
-tinc foo --pidfile=pid --pidfile=pid --help
-expect_code "$EXIT_FAILURE" tinc foo -c conf -n 'n/e\t'
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-require_root "$0" "$@"
-test -e /dev/net/tun || exit "$EXIT_SKIP_TEST"
-ip netns list || exit "$EXIT_SKIP_TEST"
-command -v socat || exit "$EXIT_SKIP_TEST"
-
-ip_foo=192.168.1.1
-ip_bar=192.168.1.2
-port_foo=30100
-recv_port_foo=30101
-mask=24
-
-echo '[STEP] Determining supported compression levels'
-
-features=$(tincd foo --version)
-bogus_levels="-1 13"
-levels=0
-
-add_levels() {
- algo=$1
- shift
-
- if echo "$features" | grep "comp_$algo"; then
- levels="$levels $*"
- else
- bogus_levels="$bogus_levels $*"
- fi
-}
-
-add_levels zlib 1 2 3 4 5 6 7 8 9
-add_levels lzo 10 11
-add_levels lz4 12
-
-echo "Supported compression levels: $levels"
-echo "Unsupported compression levels: $bogus_levels"
-
-echo [STEP] Create network namespaces
-
-ip netns add foo
-ip netns add bar
-tmp_file=$(mktemp)
-
-cleanup_hook() {
- ip netns del foo
- ip netns del bar
- rm -f "$tmp_file"
-}
-
-echo [STEP] Initialize two nodes
-
-tinc foo <<EOF
-init foo
-set Subnet $ip_foo
-set Interface foo
-set Port $port_foo
-set Address localhost
-EOF
-
-tinc bar <<EOF
-init bar
-set Subnet $ip_bar
-set Interface bar
-set ConnectTo foo
-EOF
-
-# shellcheck disable=SC2016
-create_script foo tinc-up "
- ip link set dev \$INTERFACE netns foo
- ip netns exec foo ip addr add $ip_foo/$mask dev \$INTERFACE
- ip netns exec foo ip link set \$INTERFACE up
-"
-
-# shellcheck disable=SC2016
-create_script bar tinc-up "
- ip link set dev \$INTERFACE netns bar
- ip netns exec bar ip addr add $ip_bar/$mask dev \$INTERFACE
- ip netns exec bar ip link set \$INTERFACE up
-"
-
-echo [STEP] Exchange configuration files
-
-tinc foo export | tinc bar exchange | tinc foo import
-
-echo [STEP] Test supported compression levels
-
-ref_file=$0
-
-create_script foo hosts/bar-up
-create_script bar hosts/foo-up
-
-for level in $levels; do
- echo "[STEP] Testing compression level $level"
-
- tinc foo set Compression "$level"
- tinc bar set Compression "$level"
-
- start_tinc foo
- wait_script foo tinc-up
-
- start_tinc bar
- wait_script bar tinc-up
-
- wait_script foo hosts/bar-up
- wait_script bar hosts/foo-up
-
- sh <<EOF
- set -eu
- ip netns exec foo socat -u TCP4-LISTEN:$recv_port_foo,reuseaddr OPEN:"$tmp_file",creat &
- ip netns exec bar socat -u OPEN:"$ref_file" TCP4:$ip_foo:$recv_port_foo,retry=30 &
- wait
-EOF
-
- diff -w "$ref_file" "$tmp_file"
-
- tinc foo stop
- tinc bar stop
-done
-
-echo '[STEP] Invalid compression levels should fail'
-
-for level in $bogus_levels; do
- echo "[STEP] Testing bogus compression level $level"
- tinc foo set Compression "$level"
-
- output=$(expect_code "$EXIT_FAILURE" start_tinc foo 2>&1)
-
- if ! echo "$output" | grep -q 'Bogus compression level'; then
- bail 'expected message about the wrong compression level'
- fi
-done
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Just test whether the executables work
-
-tinc foo --help
-
-tincd foo --help
-
-if [ -e "$SPTPS_TEST_PATH" ]; then
- "$SPTPS_TEST_PATH" --help
-fi
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initialize three nodes
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Port 30000
-set Address localhost
-EOF
-
-tinc bar <<EOF
-init bar
-set DeviceType dummy
-set Port 0
-EOF
-
-tinc baz <<EOF
-init baz
-set DeviceType dummy
-set Port 0
-EOF
-
-echo [STEP] Test import, export and exchange commands
-
-tinc foo export | tinc bar exchange | tinc foo import
-
-echo [STEP] Test export-all and exchange-all
-
-tinc foo export-all | tinc baz exchange | tinc foo import
-tinc foo exchange-all </dev/null | tinc bar import
-
-echo [STEP] Test equivalence of host config files
-
-diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
-diff -w "$DIR_FOO/hosts/foo" "$DIR_BAZ/hosts/foo"
-diff -w "$DIR_FOO/hosts/bar" "$DIR_BAR/hosts/bar"
-diff -w "$DIR_FOO/hosts/bar" "$DIR_BAZ/hosts/bar"
-diff -w "$DIR_FOO/hosts/baz" "$DIR_BAR/hosts/baz"
-diff -w "$DIR_FOO/hosts/baz" "$DIR_BAZ/hosts/baz"
-
-echo [STEP] Check whether the nodes can connect to each other
-
-create_script foo tinc-up '
- tinc bar add ConnectTo foo
- tinc baz add ConnectTo foo
-'
-
-create_script foo hosts/bar-up
-create_script foo hosts/baz-up
-
-start_tinc foo
-
-wait_script foo tinc-up
-
-start_tinc bar
-start_tinc baz
-
-wait_script foo hosts/bar-up
-wait_script foo hosts/baz-up
-
-require_nodes foo 3
-require_nodes bar 3
-require_nodes baz 3
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initialize two nodes
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Port 30070
+set Address localhost
+set ExperimentalProtocol no
+EOF
+
+tinc bar <<EOF
+init bar
+set DeviceType dummy
+set Port 0
+set ExperimentalProtocol no
+EOF
+
+create_script foo hosts/bar-up
+create_script bar hosts/foo-up
+
+echo [STEP] Exchange configuration
+
+tinc foo export | tinc bar exchange | tinc foo import
+tinc bar add ConnectTo foo
+start_tinc foo
+
+echo [STEP] Test various ciphers and digests
+
+# The full suite results in a large test matrix and it takes a lot of time to run.
+# The list was reduced to the strongest available algorithms (and "none").
+digests="none sha256 sha512"
+ciphers="none aes-256-cbc"
+
+for digest in $digests; do
+ for cipher in $ciphers; do
+ echo "Testing $cipher $digest"
+
+ tinc bar <<EOF
+set Digest $digest
+set Cipher $cipher
+EOF
+
+ start_tinc bar
+ wait_script foo hosts/bar-up
+ wait_script bar hosts/foo-up
+
+ tinc foo info bar | grep reachable
+
+ tinc bar stop
+ done
+done
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initialize and test one node
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Port 0
+EOF
+
+echo [STEP] Test running in the foreground
+
+create_script foo tinc-up '
+ tinc foo stop &
+'
+start_tinc foo -D
+
+echo [STEP] Test running tinc in the background
+
+create_script foo tinc-up '
+ tinc foo stop &
+'
+start_tinc foo
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+foo_dir=$(peer_directory foo)
+foo_host=$foo_dir/hosts/foo
+foo_conf=$foo_dir/tinc.conf
+foo_rsa_priv=$foo_dir/rsa_key.priv
+foo_ec_priv=$foo_dir/ed25519_key.priv
+foo_tinc_up=$foo_dir/tinc-up
+foo_host_up=$foo_dir/host-up
+
+if is_windows; then
+ foo_tinc_up=$foo_tinc_up.cmd
+ foo_host_up=$foo_host_up.cmd
+fi
+
+# Sample RSA key pair (old format). Uses e = 0xFFFF.
+rsa_n=BB82C3A9B906E98ABF2D99FF9B320B229F5C1E58EC784762DA1F4D3509FFF78ECA7FFF19BA170736CDE458EC8E732DDE2C02009632DF731B4A6BD6C504E50B7B875484506AC1E49FD0DF624F6612F564C562BD20F870592A49195023D744963229C35081C8AE48BE2EBB5CC9A0D64924022DC0EB782A3A8F3EABCA04AA42B24B2A6BD2353A6893A73AE01FA54891DD24BF36CA032F19F7E78C01273334BAA2ECF36B6998754CB012BC985C975503D945E4D925F6F719ACC8FBA7B18C810FF850C3CCACD60565D4FCFE02A98FE793E2D45D481A34D1F90584D096561FF3184C462C606535F3F9BB260541DF0D1FEB16938FFDEC2FF96ACCC6BD5BFBC19471F6AB
+rsa_d=8CEC9A4316FE45E07900197D8FBB52D3AF01A51C4F8BD08A1E21A662E3CFCF7792AD7680673817B70AC1888A08B49E8C5835357016D9BF56A0EBDE8B5DF214EC422809BC8D88177F273419116EF2EC7951453F129768DE9BC31D963515CC7481559E4C0E65C549169F2B94AE68DB944171189DD654DC6970F2F5843FB7C8E9D057E2B5716752F1F5686811AC075ED3D3CBD06B5D35AE33D01260D9E0560AF545D0C9D89A31D5EAF96D5422F6567FE8A90E23906B840545805644DFD656E526A686D3B978DD271578CA3DA0F7D23FC1252A702A5D597CAE9D4A5BBF6398A75AF72582C7538A7937FB71A2610DCBC39625B77103FA3B7D0A55177FD98C39CD4A27
+
+# Extracts the PEM key from a config file, leaving the file unchanged.
+# usage: extract_pem_key_from_config path_to_file
+extract_pem_key_from_config() {
+ sed -n '/-----BEGIN /,/-----END /p' "$1"
+}
+
+# Removes the PEM key from a config file.
+# usage: rm_pem_key_from_config path_to_file
+rm_pem_key_from_config() {
+ sed_cmd '/-----BEGIN /,/-----END /d' "$1"
+}
+
+reinit_configs() {
+ if [ -d "$foo_dir" ]; then
+ chmod -f 755 "$foo_dir"
+ rm -rf "$foo_dir"
+ fi
+
+ tinc foo <<EOF
+init foo
+set DeviceType dummy
+EOF
+}
+
+fsck_test() {
+ echo >&2 "[STEP] $*"
+ reinit_configs
+}
+
+run_access_checks() {
+ ! is_root && ! is_windows
+}
+
+test_private_keys() {
+ keyfile=$1
+
+ fsck_test "Must fail on broken $keyfile"
+ printf '' >"$foo_dir/$keyfile"
+ if with_legacy; then
+ expect_msg 'no private key is known' tinc foo fsck
+ else
+ must_fail_with_msg 'no Ed25519 private key found' tinc foo fsck
+ fi
+
+ if run_access_checks; then
+ fsck_test "Must fail on inaccessible $keyfile"
+ chmod 000 "$foo_dir/$keyfile"
+ if with_legacy; then
+ expect_msg 'error reading' tinc foo fsck
+ else
+ must_fail_with_msg 'error reading' tinc foo fsck
+ fi
+ fi
+
+ if ! is_windows; then
+ fsck_test "Must warn about unsafe permissions on $keyfile"
+ chmod 666 "$foo_dir/$keyfile"
+ expect_msg 'unsafe file permissions' tinc foo fsck
+ fi
+
+ if with_legacy; then
+ fsck_test "Must pass on missing $keyfile when the other key is present"
+ rm -f "$foo_dir/$keyfile"
+ tinc foo fsck
+ fi
+}
+
+test_private_key_var() {
+ var=$1
+ keyfile=$2
+
+ fsck_test "Must find private key at $var"
+ mv "$foo_dir/$keyfile" "$foo_dir/renamed_private_key"
+ echo "$var = $(normalize_path "$foo_dir/renamed_private_key")" >>"$foo_conf"
+ fail_on_msg 'key was found but no private key' tinc foo fsck
+}
+
+test_ec_public_key_file_var() {
+ conf=$1
+ fsck_test "EC public key in Ed25519PublicKeyFile in $conf must work"
+ cat >"$foo_dir/ec_pubkey" <<EOF
+-----BEGIN ED25519 PUBLIC KEY-----
+$(awk '/^Ed25519PublicKey/ { printf $NF }' "$foo_host")
+-----END ED25519 PUBLIC KEY-----
+EOF
+ sed_cmd '/Ed25519PublicKey/d' "$foo_host"
+ echo "Ed25519PublicKeyFile = $(normalize_path "$foo_dir/ec_pubkey")" >>"$foo_dir/$conf"
+ fail_on_msg 'no (usable) public Ed25519' tinc foo fsck
+}
+
+test_rsa_public_key_file_var() {
+ conf=$1
+ fsck_test "RSA public key in PublicKeyFile in $conf must work"
+ extract_pem_key_from_config "$foo_host" >"$foo_dir/rsa_pubkey"
+ rm_pem_key_from_config "$foo_host"
+ echo "PublicKeyFile = $(normalize_path "$foo_dir/rsa_pubkey")" >>"$foo_dir/$conf"
+ fail_on_msg 'error reading RSA public key' tinc foo fsck
+}
+
+fsck_test 'Newly created configuration should pass'
+tinc foo fsck
+
+fsck_test 'Must fail on missing tinc.conf'
+rm -f "$foo_conf"
+must_fail_with_msg 'no tinc configuration found' tinc foo fsck
+
+if run_access_checks; then
+ fsck_test 'Must fail on inaccessible tinc.conf'
+ chmod 000 "$foo_dir"
+ must_fail_with_msg 'not running tinc as root' tinc foo fsck
+fi
+
+if ! is_windows; then
+ fsck_test 'Non-executable tinc-up MUST be fixed by tinc --force'
+ chmod a-x "$foo_tinc_up"
+ expect_msg 'cannot read and execute' tinc foo --force fsck
+ test -x "$foo_tinc_up"
+
+ fsck_test 'Non-executable tinc-up MUST NOT be fixed by tinc without --force'
+ chmod a-x "$foo_tinc_up"
+ expect_msg 'cannot read and execute' tinc foo fsck
+ must_fail test -x "$foo_tinc_up"
+fi
+
+fsck_test 'Unknown -up script warning'
+touch "$foo_dir/fake-up"
+expect_msg 'unknown script' tinc foo fsck
+
+fsck_test 'Unknown -down script warning'
+touch "$foo_dir/fake-down"
+expect_msg 'unknown script' tinc foo fsck
+
+if ! is_windows; then
+ fsck_test 'Non-executable foo-up MUST be fixed by tinc --force'
+ touch "$foo_host_up"
+ chmod a-x "$foo_host_up"
+ expect_msg 'cannot read and execute' tinc foo --force fsck
+ test -x "$foo_tinc_up"
+
+ fsck_test 'Non-executable bar-up MUST NOT be fixed by tinc'
+ touch "$foo_dir/hosts/bar-up"
+ chmod a-x "$foo_dir/hosts/bar-up"
+ expect_msg 'cannot read and execute' tinc foo fsck
+ must_fail test -x "$foo_dir/bar-up"
+fi
+
+if run_access_checks; then
+ fsck_test 'Inaccessible hosts/foo must fail'
+ chmod 000 "$foo_host"
+ must_fail_with_msg 'cannot open config file' tinc foo fsck
+fi
+
+fsck_test 'Must fail when all private keys are missing'
+rm -f "$foo_ec_priv" "$foo_rsa_priv"
+if with_legacy; then
+ must_fail_with_msg 'neither RSA or Ed25519 private key' tinc foo fsck
+else
+ must_fail_with_msg 'no Ed25519 private key' tinc foo fsck
+fi
+
+if with_legacy; then
+ test_private_keys rsa_key.priv
+
+ if ! is_windows; then
+ fsck_test 'Must warn about unsafe permissions on tinc.conf with PrivateKey'
+ rm -f "$foo_rsa_priv"
+ echo "PrivateKey = $rsa_d" >>"$foo_conf"
+ echo "PublicKey = $rsa_n" >>"$foo_host"
+ chmod 666 "$foo_conf"
+ expect_msg 'unsafe file permissions' tinc foo fsck
+ fi
+
+ fsck_test 'Must warn about missing RSA private key if public key is present'
+ rm -f "$foo_rsa_priv"
+ expect_msg 'public RSA key was found but no private key' tinc foo fsck
+
+ fsck_test 'Must warn about missing RSA public key'
+ rm_pem_key_from_config "$foo_host"
+ expect_msg 'no (usable) public RSA' tinc foo fsck
+ must_fail grep -q 'BEGIN RSA PUBLIC KEY' "$foo_host"
+
+ fsck_test 'Must fix missing RSA public key on --force'
+ rm_pem_key_from_config "$foo_host"
+ expect_msg 'wrote RSA public key' tinc foo --force fsck
+ grep -q 'BEGIN RSA PUBLIC KEY' "$foo_host"
+
+ test_private_key_var PrivateKeyFile rsa_key.priv
+
+ test_rsa_public_key_file_var tinc.conf
+ test_rsa_public_key_file_var hosts/foo
+
+ fsck_test 'RSA PublicKey + PrivateKey must work'
+ rm -f "$foo_rsa_priv"
+ rm_pem_key_from_config "$foo_host"
+ echo "PrivateKey = $rsa_d" >>"$foo_conf"
+ echo "PublicKey = $rsa_n" >>"$foo_host"
+ fail_on_msg 'no (usable) public RSA' tinc foo fsck
+
+ fsck_test 'RSA PrivateKey without PublicKey must warn'
+ rm -f "$foo_rsa_priv"
+ rm_pem_key_from_config "$foo_host"
+ echo "PrivateKey = $rsa_d" >>"$foo_conf"
+ expect_msg 'PrivateKey used but no PublicKey found' tinc foo fsck
+
+ fsck_test 'Must warn about missing EC private key if public key is present'
+ rm -f "$foo_ec_priv"
+ expect_msg 'public Ed25519 key was found but no private key' tinc foo fsck
+
+ fsck_test 'Must fix broken RSA public key with --force'
+ sed_cmd 2d "$foo_host"
+ expect_msg 'old key(s) found and disabled' tinc foo --force fsck
+ tinc foo fsck
+
+ fsck_test 'Must fix missing RSA public key with --force'
+ rm_pem_key_from_config "$foo_host"
+ expect_msg 'no (usable) public RSA key found' tinc foo --force fsck
+ tinc foo fsck
+fi
+
+fsck_test 'Must fix broken Ed25519 public key with --force'
+sed_cmd 's/Ed25519PublicKey.*/Ed25519PublicKey = foobar/' "$foo_host"
+expect_msg 'no (usable) public Ed25519 key' tinc foo --force fsck
+tinc foo fsck
+
+fsck_test 'Must fix missing Ed25519 public key with --force'
+sed_cmd '/Ed25519PublicKey/d' "$foo_host"
+expect_msg 'no (usable) public Ed25519 key' tinc foo --force fsck
+tinc foo fsck
+
+test_private_keys ed25519_key.priv
+test_private_key_var Ed25519PrivateKeyFile ed25519_key.priv
+
+test_ec_public_key_file_var tinc.conf
+test_ec_public_key_file_var hosts/foo
+
+fsck_test 'Must warn about missing EC public key and NOT fix without --force'
+sed_cmd '/Ed25519PublicKey/d' "$foo_host"
+expect_msg 'no (usable) public Ed25519' tinc foo fsck
+must_fail grep -q 'ED25519 PUBLIC KEY' "$foo_host"
+
+fsck_test 'Must fix missing EC public key on --force'
+sed_cmd '/Ed25519PublicKey/d' "$foo_host"
+expect_msg 'wrote Ed25519 public key' tinc foo --force fsck
+grep -q 'ED25519 PUBLIC KEY' "$foo_host"
+
+fsck_test 'Must warn about obsolete variables'
+echo 'GraphDumpFile = /dev/null' >>"$foo_host"
+expect_msg 'obsolete variable GraphDumpFile' tinc foo fsck
+
+fsck_test 'Must warn about missing values'
+echo 'Weight = ' >>"$foo_host"
+must_fail_with_msg 'no value for variable `Weight' tinc foo fsck
+
+fsck_test 'Must warn about duplicate variables'
+echo 'Weight = 0' >>"$foo_host"
+echo 'Weight = 1' >>"$foo_host"
+expect_msg 'multiple instances of variable Weight' tinc foo fsck
+
+fsck_test 'Must warn about server variables in host config'
+echo 'Interface = fake0' >>"$foo_host"
+expect_msg 'server variable Interface found' tinc foo fsck
+
+fsck_test 'Must warn about host variables in server config'
+echo 'Port = 1337' >>"$foo_conf"
+expect_msg 'host variable Port found' tinc foo fsck
+
+fsck_test 'Must warn about missing Name'
+sed_cmd '/^Name =/d' "$foo_conf"
+must_fail_with_msg 'without a valid Name' tinc foo fsck
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initialize one node
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Port 0
+EOF
+
+create_script foo tinc-up '
+ tinc foo stop &
+'
+
+echo [STEP] Test tincd command line options that should work
+
+tincd foo -D
+tincd foo --no-detach
+tincd foo -D -d
+tincd foo -D -d2
+tincd foo -D -d 2
+tincd foo -D -n foo
+tincd foo -D -nfoo
+tincd foo -D --net=foo
+tincd foo -D --net foo
+
+echo [STEP] Test tincd command line options that should not work
+
+expect_code "$EXIT_FAILURE" tincd foo foo
+expect_code "$EXIT_FAILURE" tincd foo --pidfile
+expect_code "$EXIT_FAILURE" tincd foo --foo
+
+echo [STEP] Test tinc command line options that should work
+
+tinc foo get name
+tinc foo -n foo get name
+tinc foo -nfoo get name
+tinc foo --net=foo get name
+tinc foo --net foo get name
+
+echo [STEP] Test tinc command line options that should not work
+
+expect_code "$EXIT_FAILURE" tinc foo -n foo get somethingreallyunknown
+expect_code "$EXIT_FAILURE" tinc foo --net
+expect_code "$EXIT_FAILURE" tinc foo --net get name
+expect_code "$EXIT_FAILURE" tinc foo foo
+
+# Most of these should fail with ASAN. Some leaks are only detected by Valgrind.
+echo [STEP] Trigger previously known memory leaks
+
+tincd foo -c . -c . --help
+tincd foo -n net -n net --help
+tincd foo -n net -o FakeOpt=42 --help
+tincd foo --logfile=one --logfile=two --help
+tincd foo --pidfile=one --pidfile=two --help
+expect_code "$EXIT_FAILURE" tincd foo -n net -o Compression= --help
+expect_code "$EXIT_FAILURE" tincd foo -c fakedir -n 'n/e\t'
+
+tinc foo -c conf -c conf --help
+tinc foo -n net -n net --help
+tinc foo --pidfile=pid --pidfile=pid --help
+expect_code "$EXIT_FAILURE" tinc foo -c conf -n 'n/e\t'
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+require_root "$0" "$@"
+test -e /dev/net/tun || exit "$EXIT_SKIP_TEST"
+ip netns list || exit "$EXIT_SKIP_TEST"
+command -v socat || exit "$EXIT_SKIP_TEST"
+
+ip_foo=192.168.1.1
+ip_bar=192.168.1.2
+port_foo=30100
+recv_port_foo=30101
+mask=24
+
+echo '[STEP] Determining supported compression levels'
+
+features=$(tincd foo --version)
+bogus_levels="-1 13"
+levels=0
+
+add_levels() {
+ algo=$1
+ shift
+
+ if echo "$features" | grep "comp_$algo"; then
+ levels="$levels $*"
+ else
+ bogus_levels="$bogus_levels $*"
+ fi
+}
+
+add_levels zlib 1 2 3 4 5 6 7 8 9
+add_levels lzo 10 11
+add_levels lz4 12
+
+echo "Supported compression levels: $levels"
+echo "Unsupported compression levels: $bogus_levels"
+
+echo [STEP] Create network namespaces
+
+ip netns add foo
+ip netns add bar
+tmp_file=$(mktemp)
+
+cleanup_hook() {
+ ip netns del foo
+ ip netns del bar
+ rm -f "$tmp_file"
+}
+
+echo [STEP] Initialize two nodes
+
+tinc foo <<EOF
+init foo
+set Subnet $ip_foo
+set Interface foo
+set Port $port_foo
+set Address localhost
+EOF
+
+tinc bar <<EOF
+init bar
+set Subnet $ip_bar
+set Interface bar
+set ConnectTo foo
+EOF
+
+# shellcheck disable=SC2016
+create_script foo tinc-up "
+ ip link set dev \$INTERFACE netns foo
+ ip netns exec foo ip addr add $ip_foo/$mask dev \$INTERFACE
+ ip netns exec foo ip link set \$INTERFACE up
+"
+
+# shellcheck disable=SC2016
+create_script bar tinc-up "
+ ip link set dev \$INTERFACE netns bar
+ ip netns exec bar ip addr add $ip_bar/$mask dev \$INTERFACE
+ ip netns exec bar ip link set \$INTERFACE up
+"
+
+echo [STEP] Exchange configuration files
+
+tinc foo export | tinc bar exchange | tinc foo import
+
+echo [STEP] Test supported compression levels
+
+ref_file=$0
+
+create_script foo hosts/bar-up
+create_script bar hosts/foo-up
+
+for level in $levels; do
+ echo "[STEP] Testing compression level $level"
+
+ tinc foo set Compression "$level"
+ tinc bar set Compression "$level"
+
+ start_tinc foo
+ wait_script foo tinc-up
+
+ start_tinc bar
+ wait_script bar tinc-up
+
+ wait_script foo hosts/bar-up
+ wait_script bar hosts/foo-up
+
+ sh <<EOF
+ set -eu
+ ip netns exec foo socat -u TCP4-LISTEN:$recv_port_foo,reuseaddr OPEN:"$tmp_file",creat &
+ ip netns exec bar socat -u OPEN:"$ref_file" TCP4:$ip_foo:$recv_port_foo,retry=30 &
+ wait
+EOF
+
+ diff -w "$ref_file" "$tmp_file"
+
+ tinc foo stop
+ tinc bar stop
+done
+
+echo '[STEP] Invalid compression levels should fail'
+
+for level in $bogus_levels; do
+ echo "[STEP] Testing bogus compression level $level"
+ tinc foo set Compression "$level"
+
+ output=$(expect_code "$EXIT_FAILURE" start_tinc foo 2>&1)
+
+ if ! echo "$output" | grep -q 'Bogus compression level'; then
+ bail 'expected message about the wrong compression level'
+ fi
+done
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Just test whether the executables work
+
+tinc foo --help
+
+tincd foo --help
+
+if [ -e "$SPTPS_TEST_PATH" ]; then
+ "$SPTPS_TEST_PATH" --help
+fi
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initialize three nodes
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Port 30000
+set Address localhost
+EOF
+
+tinc bar <<EOF
+init bar
+set DeviceType dummy
+set Port 0
+EOF
+
+tinc baz <<EOF
+init baz
+set DeviceType dummy
+set Port 0
+EOF
+
+echo [STEP] Test import, export and exchange commands
+
+tinc foo export | tinc bar exchange | tinc foo import
+
+echo [STEP] Test export-all and exchange-all
+
+tinc foo export-all | tinc baz exchange | tinc foo import
+tinc foo exchange-all </dev/null | tinc bar import
+
+echo [STEP] Test equivalence of host config files
+
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAZ/hosts/foo"
+diff -w "$DIR_FOO/hosts/bar" "$DIR_BAR/hosts/bar"
+diff -w "$DIR_FOO/hosts/bar" "$DIR_BAZ/hosts/bar"
+diff -w "$DIR_FOO/hosts/baz" "$DIR_BAR/hosts/baz"
+diff -w "$DIR_FOO/hosts/baz" "$DIR_BAZ/hosts/baz"
+
+echo [STEP] Check whether the nodes can connect to each other
+
+create_script foo tinc-up '
+ tinc bar add ConnectTo foo
+ tinc baz add ConnectTo foo
+'
+
+create_script foo hosts/bar-up
+create_script foo hosts/baz-up
+
+start_tinc foo
+
+wait_script foo tinc-up
+
+start_tinc bar
+start_tinc baz
+
+wait_script foo hosts/bar-up
+wait_script foo hosts/baz-up
+
+require_nodes foo 3
+require_nodes bar 3
+require_nodes baz 3
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initialize one node
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Mode switch
+set Broadcast no
+set Address localhost
+set Port 30010
+EOF
+
+start_tinc foo
+
+echo [STEP] Generate an invitation and let another node join the VPN
+
+invitation=$(tinc foo invite bar)
+tinc bar join "$invitation"
+
+echo [STEP] Test equivalence of host config files
+
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
+test "$(grep ^Ed25519PublicKey "$DIR_FOO/hosts/bar")" = "$(grep ^Ed25519PublicKey "$DIR_BAR/hosts/bar")"
+
+echo [STEP] Test Mode, Broadcast and ConnectTo statements
+
+test "$(tinc bar get Mode)" = switch
+test "$(tinc bar get Broadcast)" = no
+test "$(tinc bar get ConnectTo)" = foo
+
+echo [STEP] Check whether the new node can join the VPN
+
+tinc bar <<EOF
+set DeviceType dummy
+set Port 0
+EOF
+
+create_script foo hosts/bar-up
+create_script bar hosts/foo-up
+
+start_tinc bar
+
+wait_script foo hosts/bar-up
+wait_script bar hosts/foo-up
+
+require_nodes foo 2
+require_nodes bar 2
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initialize one node
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Mode switch
+set Broadcast no
+set Address localhost
+set Port 30020
+EOF
+
+echo [STEP] Generate an invitation offline and let another node join the VPN
+
+invitation=$(tinc foo invite bar)
+
+start_tinc foo
+tinc bar join "$invitation"
+
+echo [STEP] Test equivalence of host config files
+
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
+test "$(grep ^Ed25519PublicKey "$DIR_FOO/hosts/bar")" = "$(grep ^Ed25519PublicKey "$DIR_BAR/hosts/bar")"
+
+echo [STEP] Test Mode, Broadcast and ConnectTo statements
+
+test "$(tinc bar get Mode)" = switch
+test "$(tinc bar get Broadcast)" = no
+test "$(tinc bar get ConnectTo)" = foo
+
+echo [STEP] Check whether the new node can join the VPN
+
+tinc bar <<EOF
+set DeviceType dummy
+set Port 0
+EOF
+
+create_script foo hosts/bar-up
+create_script bar hosts/foo-up
+
+start_tinc bar
+
+wait_script foo hosts/bar-up
+wait_script bar hosts/foo-up
+
+require_nodes foo 2
+require_nodes bar 2
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initialize one node
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Address localhost
+set Port 30030
+EOF
+
+create_script foo tinc-up
+start_tinc foo
+wait_script foo tinc-up
+
+echo [STEP] Generate an invitation and let another node join the VPN
+
+# shellcheck disable=SC2016
+create_script foo invitation-created '
+cat >"$INVITATION_FILE" <<INVITE
+Name = $NODE
+Ifconfig = 93.184.216.34/24
+Route = 2606:2800:220:1::/64 2606:2800:220:1:248:1893:25c8:1946
+Route = 1.2.3.4 1234::
+
+$(tinc foo export)
+INVITE
+'
+
+tinc foo invite bar | tail -1 | tinc bar --batch join
+
+echo [STEP] Test equivalence of host config files
+
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
+test "$(grep ^Ed25519PublicKey "$DIR_FOO/hosts/bar")" = "$(grep ^Ed25519PublicKey "$DIR_BAR/hosts/bar")"
+
+echo [STEP] Check if the tinc-up.invitation file is created and contains the right commands
+
+bar_tinc_up="$DIR_BAR/tinc-up.invitation"
+test -f "$bar_tinc_up"
+
+grep -F -q "93.184.216.34/24" "$bar_tinc_up"
+grep -F -q "2606:2800:220:1::/64" "$bar_tinc_up"
+grep -F -q "2606:2800:220:1:248:1893:25c8:1946" "$bar_tinc_up"
+must_fail grep -F -q "1234::" "$bar_tinc_up"
+
+echo [STEP] Check that no tinc-up is created and that tinc-up.invitation is not executable
+
+must_fail test -x "$bar_tinc_up"
+must_fail test -f "$DIR_BAR/tinc-up"
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initialize two nodes
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Port 30060
+set Address localhost
+add Subnet 10.98.98.1
+set PingTimeout 2
+EOF
+
+tinc bar <<EOF
+init bar
+set DeviceType dummy
+set Port 0
+add Subnet 10.98.98.2
+set PingTimeout 2
+set MaxTimeout 2
+EOF
+
+echo [STEP] Exchange host config files
+
+tinc foo export | tinc bar exchange | tinc foo import
+tinc bar add ConnectTo foo
+
+echo [STEP] Foo 1.1, bar 1.0
+
+tinc bar set ExperimentalProtocol no
+tinc foo del bar.Ed25519PublicKey
+tinc bar del foo.Ed25519PublicKey
+
+create_script foo hosts/bar-up
+create_script bar hosts/foo-up
+
+start_tinc foo
+start_tinc bar
+
+wait_script foo hosts/bar-up
+wait_script bar hosts/foo-up
+
+require_nodes foo 2
+require_nodes bar 2
+
+tinc bar stop
+tinc foo stop
+
+test -z "$(tinc foo get bar.Ed25519PublicKey)"
+test -z "$(tinc bar get foo.Ed25519PublicKey)"
+
+echo [STEP] Foo 1.1, bar upgrades to 1.1
+
+tinc bar del ExperimentalProtocol
+
+start_tinc foo
+start_tinc bar
+
+wait_script foo hosts/bar-up
+wait_script bar hosts/foo-up
+
+require_nodes foo 2
+require_nodes bar 2
+
+tinc bar stop
+tinc foo stop
+
+test -n "$(tinc foo get bar.Ed25519PublicKey)"
+test -n "$(tinc bar get foo.Ed25519PublicKey)"
+
+echo [STEP] Bar downgrades, must no longer be allowed to connect
+
+tinc bar set ExperimentalProtocol no
+
+create_script foo subnet-up
+start_tinc foo
+wait_script foo subnet-up
+
+create_script bar subnet-up
+start_tinc bar
+wait_script bar subnet-up
+
+# There is no reliable way to wait for 'not connecting'.
+sleep 10
+
+require_nodes foo 1
+require_nodes bar 1
--- /dev/null
+tests = [
+ 'basic.test',
+ 'commandline.test',
+ 'executables.test',
+ 'import-export.test',
+ 'invite-join.test',
+ 'invite-offline.test',
+ 'invite-tinc-up.test',
+ 'scripts.test',
+ 'security.test',
+ 'variables.test',
+]
+
+if opt_crypto != 'nolegacy'
+ tests += 'algorithms.test'
+ tests += 'legacy-protocol.test'
+endif
+
+if os_name != 'windows'
+ tests += 'sptps-basic.test'
+endif
+
+if os_name == 'linux'
+ tests += 'ns-ping.test'
+endif
+
+exe_splice = executable(
+ 'splice',
+ sources: 'splice.c',
+ dependencies: deps_common,
+ implicit_include_directories: false,
+ include_directories: inc_conf,
+ build_by_default: false,
+)
+
+env = environment()
+env.set('TINC_PATH', exe_tinc.full_path())
+env.set('TINCD_PATH', exe_tincd.full_path())
+env.set('SPTPS_TEST_PATH', exe_sptps_test.full_path())
+env.set('SPTPS_KEYPAIR_PATH', exe_sptps_keypair.full_path())
+env.set('SPLICE_PATH', exe_splice.full_path())
+env.set('TESTLIB_PATH', meson.current_source_dir() / 'testlib.sh')
+
+deps_test = [
+ exe_tinc,
+ exe_tincd,
+ exe_splice,
+ exe_sptps_test,
+ exe_sptps_keypair,
+]
+
+test_wd = meson.current_build_dir()
+
+foreach test_name : tests
+ target = find_program(test_name, native: true)
+ test(test_name,
+ target,
+ suite: 'integration',
+ timeout: 5 * 60,
+ env: env,
+ depends: deps_test,
+ workdir: test_wd)
+endforeach
+
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+require_root "$0" "$@"
+test -e /dev/net/tun || exit "$EXIT_SKIP_TEST"
+ip netns list || exit "$EXIT_SKIP_TEST"
+
+ip_foo=192.168.1.1
+ip_bar=192.168.1.2
+mask=24
+
+echo [STEP] Create network namespaces
+
+ip netns add ping.test1
+ip netns add ping.test2
+
+cleanup_hook() {
+ ip netns del ping.test1
+ ip netns del ping.test2
+}
+
+echo [STEP] Initialize two nodes
+
+tinc foo <<EOF
+init foo
+set Subnet $ip_foo
+set Interface ping.test1
+set Port 30090
+set Address localhost
+set AutoConnect no
+EOF
+
+# shellcheck disable=SC2016
+create_script foo tinc-up "
+ ip link set dev \$INTERFACE netns ping.test1
+ ip netns exec ping.test1 ip addr add $ip_foo/$mask dev \$INTERFACE
+ ip netns exec ping.test1 ip link set \$INTERFACE up
+"
+
+tinc bar <<EOF
+init bar
+set Subnet $ip_bar
+set Interface ping.test2
+set Port 30091
+set AutoConnect no
+EOF
+
+# shellcheck disable=SC2016
+create_script bar tinc-up "
+ ip link set dev \$INTERFACE netns ping.test2
+ ip netns exec ping.test2 ip addr add $ip_bar/$mask dev \$INTERFACE
+ ip netns exec ping.test2 ip link set \$INTERFACE up
+"
+
+echo [STEP] Exchange configuration files
+
+tinc foo export | tinc bar exchange | tinc foo import
+
+echo [STEP] Start tinc
+
+start_tinc foo
+start_tinc bar
+
+wait_script foo tinc-up
+wait_script bar tinc-up
+
+echo [STEP] The nodes should not be able to ping each other if there is no connection
+
+must_fail ip netns exec ping.test1 ping -W1 -c3 $ip_bar
+
+echo [STEP] After connecting they should be
+
+create_script bar hosts/foo-up
+
+tinc bar add ConnectTo foo
+wait_script bar hosts/foo-up
+
+ip netns exec ping.test1 ping -W1 -c3 $ip_bar
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initializing server node
+
+port_foo=30040
+port_bar=30041
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Port $port_foo
+set Address 127.0.0.1
+add Subnet 10.0.0.1
+add Subnet fec0::/64
+EOF
+
+echo [STEP] Setting up scripts
+
+OUT=$DIR_FOO/scripts.out
+rm -f "$OUT"
+
+for script in \
+ tinc-up tinc-down \
+ host-up host-down \
+ subnet-up subnet-down \
+ hosts/foo-up hosts/foo-down \
+ hosts/bar-up hosts/bar-down \
+ invitation-created invitation-accepted; do
+
+ commands=$(
+ cat <<EOF
+ if is_windows && [ -n "\$INVITATION_FILE" ]; then
+ INVITATION_FILE=\$(cygpath --unix -- "\$INVITATION_FILE")
+ fi
+ echo >>'$OUT' "$script" "$TINC_SCRIPT_VARS"
+EOF
+ )
+
+ create_script foo "$script" "$commands"
+done
+
+echo [STEP] Starting server node
+
+start_tinc foo -n netname
+wait_script foo subnet-up 2
+echo foo-started >>"$OUT"
+
+echo [STEP] Inviting client node
+
+url=$(tinc foo -n netname2 invite bar)
+file=$(basename "$(find "$DIR_FOO/invitations" -type f ! -name ed25519_key.priv)")
+
+if is_windows; then
+ file=$(cygpath --unix -- "$file")
+fi
+
+wait_script foo invitation-created
+echo bar-invited >>"$OUT"
+
+echo [STEP] Joining client node
+
+tinc bar -n netname3 join "$url"
+wait_script foo invitation-accepted
+echo bar-joined >>"$OUT"
+
+echo [STEP] Starting client node
+
+tinc bar <<EOF
+set DeviceType dummy
+set Port $port_bar
+add Subnet 10.0.0.2
+add Subnet fec0::/64#5
+EOF
+
+start_tinc bar
+wait_script foo subnet-up 2
+echo bar-started-1 >>"$OUT"
+
+tinc foo debug 4
+tinc bar stop
+wait_script foo subnet-down 2
+echo bar-stopped >>"$OUT"
+
+tinc foo debug 5
+start_tinc bar
+wait_script foo subnet-up 2
+echo bar-started-2 >>"$OUT"
+
+echo [STEP] Stop server node
+
+tinc foo stop
+tinc bar stop
+wait_script foo tinc-down
+
+echo [STEP] Check if the script output is what is expected
+
+cat >"$OUT.expected" <<EOF
+tinc-up netname,foo,dummy,,,,,,,,,5
+subnet-up netname,foo,dummy,,foo,,,10.0.0.1,,,,5
+subnet-up netname,foo,dummy,,foo,,,fec0::/64,,,,5
+foo-started
+invitation-created netname2,foo,,,bar,,,,,$DIR_FOO/invitations/$file,$url,
+bar-invited
+invitation-accepted netname,foo,dummy,,bar,127.0.0.1,,,,,,5
+bar-joined
+host-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,5
+subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,5
+bar-started-1
+host-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,4
+hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,4
+subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,4
+subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,4
+bar-stopped
+host-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,5
+subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,5
+bar-started-2
+host-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,5
+subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,5
+subnet-down netname,foo,dummy,,foo,,,10.0.0.1,,,,5
+subnet-down netname,foo,dummy,,foo,,,fec0::/64,,,,5
+tinc-down netname,foo,dummy,,,,,,,,,5
+EOF
+
+diff -w "$OUT" "$OUT.expected"
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Skip this test if tools are missing
+
+command -v nc >/dev/null || exit "$EXIT_SKIP_TEST"
+command -v timeout >/dev/null || exit "$EXIT_SKIP_TEST"
+
+foo_port=30050
+bar_port=30051
+
+# usage: splice protocol_version
+splice() {
+ "$SPLICE_PATH" foo localhost $foo_port bar localhost $bar_port "$1" &
+ sleep 10
+}
+
+# usage: send_with_timeout "data to send" "data expected to receive"
+send_with_timeout() {
+ data=$1
+ expected=$3
+
+ result=$(
+ (
+ sleep 6
+ printf "%s\n" "$data"
+ ) | timeout 10 nc localhost $foo_port
+ ) && exit 1
+
+ test $? = "$EXIT_TIMEOUT"
+
+ if [ -z "$expected" ]; then
+ test -z "$result"
+ else
+ echo "$result" | grep -q "^$expected"
+ fi
+}
+
+echo [STEP] Initialize two nodes
+
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Port $foo_port
+set Address localhost
+set PingTimeout 3
+set AutoConnect no
+set Subnet 10.96.96.1
+EOF
+
+tinc bar <<EOF
+init bar
+set DeviceType dummy
+set Port $bar_port
+set PingTimeout 3
+set MaxTimeout 3
+set ExperimentalProtocol no
+set AutoConnect no
+set Subnet 10.96.96.2
+EOF
+
+echo [STEP] Exchange host config files
+
+tinc foo export | tinc bar exchange | tinc foo import
+
+create_script foo subnet-up
+start_tinc foo
+wait_script foo subnet-up
+
+create_script bar subnet-up
+start_tinc bar
+wait_script bar subnet-up
+
+echo "[STEP] No ID sent by responding node if we don't send an ID first, before the timeout"
+send_with_timeout "0 bar 17.7" ""
+
+echo [STEP] ID sent if initiator sends first, but still tarpitted
+send_with_timeout "0 bar 17.7" "0 foo 17.7"
+
+echo [STEP] No invalid IDs allowed
+send_with_timeout "0 foo 17.7" ""
+send_with_timeout "0 baz 17.7" ""
+
+echo [STEP] No NULL METAKEYs allowed
+data="0 foo 17.0\n1 0 672 0 0 834188619F4D943FD0F4B1336F428BD4AC06171FEABA66BD2356BC9593F0ECD643F0E4B748C670D7750DFDE75DC9F1D8F65AB1026F5ED2A176466FBA4167CC567A2085ABD070C1545B180BDA86020E275EA9335F509C57786F4ED2378EFFF331869B856DDE1C05C461E4EECAF0E2FB97AF77B7BC2AD1B34C12992E45F5D1254BBF0C3FB224ABB3E8859594A83B6CA393ED81ECAC9221CE6BC71A727BCAD87DD80FC0834B87BADB5CB8FD3F08BEF90115A8DF1923D7CD9529729F27E1B8ABD83C4CF8818AE10257162E0057A658E265610B71F9BA4B365A20C70578FAC65B51B91100392171BA12A440A5E93C4AA62E0C9B6FC9B68F953514AAA7831B4B2C31C4\n"
+send_with_timeout "$data" "" # Not even the ID should be sent when the first packet contains illegal data
+
+echo [STEP] No splicing allowed
+
+tinc bar stop
+tinc bar del ExperimentalProtocol
+
+create_script bar subnet-up
+start_tinc bar
+wait_script bar subnet-up
+
+splice 17.7
+pid=$!
+
+require_nodes foo 1
+require_nodes bar 1
+
+kill $pid
+
+tinc bar stop
+tinc foo stop
+
+echo [STEP] Test splicing again with legacy protocol
+
+tinc foo set ExperimentalProtocol no
+tinc bar set ExperimentalProtocol no
+
+create_script foo subnet-up
+start_tinc foo
+wait_script foo subnet-up
+
+create_script bar subnet-up
+start_tinc bar
+wait_script bar subnet-up
+
+splice 17.0
+pid=$!
+
+require_nodes foo 1
+require_nodes bar 1
+
+kill $pid
--- /dev/null
+/*
+ splice.c -- Splice two outgoing tinc connections together
+ Copyright (C) 2018 Guus Sliepen <guus@tinc-vpn.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "../../src/system.h"
+
+#ifdef HAVE_MINGW
+static const char *winerror(int err) {
+ static char buf[1024], *ptr;
+
+ ptr = buf + snprintf(buf, sizeof(buf), "(%d) ", err);
+
+ if(!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ptr, sizeof(buf) - (ptr - buf), NULL)) {
+ strncpy(buf, "(unable to format errormessage)", sizeof(buf));
+ };
+
+ if((ptr = strchr(buf, '\r'))) {
+ *ptr = '\0';
+ }
+
+ return buf;
+}
+
+#define strerror(x) ((x)>0?strerror(x):winerror(GetLastError()))
+#define sockerrno WSAGetLastError()
+#define sockstrerror(x) winerror(x)
+#else
+#define sockerrno errno
+#define sockstrerror(x) strerror(x)
+#endif
+
+int main(int argc, char *argv[]) {
+ if(argc < 7) {
+ fprintf(stderr, "Usage: %s name1 host1 port1 name2 host2 port2 [protocol]\n", argv[0]);
+ return 1;
+ }
+
+ const char *protocol;
+
+ if(argc >= 8) {
+ protocol = argv[7];
+ } else {
+ protocol = "17.7";
+ }
+
+#ifdef HAVE_MINGW
+ static struct WSAData wsa_state;
+
+ if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
+ return 1;
+ }
+
+#endif
+ int sock[2];
+ char buf[1024];
+
+ const struct addrinfo hint = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_STREAM,
+ .ai_protocol = IPPROTO_TCP,
+ .ai_flags = 0,
+ };
+
+ for(int i = 0; i < 2; i++) {
+ struct addrinfo *ai;
+
+ if(getaddrinfo(argv[2 + 3 * i], argv[3 + 3 * i], &hint, &ai) || !ai) {
+ fprintf(stderr, "getaddrinfo() failed: %s\n", sockstrerror(sockerrno));
+ return 1;
+ }
+
+ sock[i] = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+
+ if(sock[i] == -1) {
+ fprintf(stderr, "Could not create socket: %s\n", sockstrerror(sockerrno));
+ freeaddrinfo(ai);
+ return 1;
+ }
+
+ if(connect(sock[i], ai->ai_addr, ai->ai_addrlen)) {
+ fprintf(stderr, "Could not connect to %s: %s\n", argv[i + 3 * i], sockstrerror(sockerrno));
+ freeaddrinfo(ai);
+ return 1;
+ }
+
+ freeaddrinfo(ai);
+
+ fprintf(stderr, "Connected to %s\n", argv[1 + 3 * i]);
+
+ /* Pretend to be the other one */
+ int len = snprintf(buf, sizeof buf, "0 %s %s\n", argv[4 - 3 * i], protocol);
+
+ if(send(sock[i], buf, len, 0) != len) {
+ fprintf(stderr, "Error sending data to %s: %s\n", argv[1 + 3 * i], sockstrerror(sockerrno));
+ return 1;
+ }
+
+ /* Ignore the response */
+ do {
+ if(recv(sock[i], buf, 1, 0) != 1) {
+ fprintf(stderr, "Error reading data from %s: %s\n", argv[1 + 3 * i], sockstrerror(sockerrno));
+ return 1;
+ }
+ } while(*buf != '\n');
+ }
+
+ fprintf(stderr, "Splicing...\n");
+
+ int nfds = (sock[0] > sock[1] ? sock[0] : sock[1]) + 1;
+
+ while(true) {
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(sock[0], &fds);
+ FD_SET(sock[1], &fds);
+
+ if(select(nfds, &fds, NULL, NULL, NULL) <= 0) {
+ return 1;
+ }
+
+ for(int i = 0; i < 2; i++) {
+ if(FD_ISSET(sock[i], &fds)) {
+ ssize_t len = recv(sock[i], buf, sizeof buf, 0);
+
+ if(len < 0) {
+ fprintf(stderr, "Error while reading from %s: %s\n", argv[1 + i * 3], sockstrerror(sockerrno));
+ return 1;
+ }
+
+ if(len == 0) {
+ fprintf(stderr, "Connection closed by %s\n", argv[1 + i * 3]);
+ return 0;
+ }
+
+ if(send(sock[i ^ 1], buf, len, 0) != len) {
+ fprintf(stderr, "Error while writing to %s: %s\n", argv[4 - i * 3], sockstrerror(sockerrno));
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Skip this test if we did not compile sptps_test
+
+test -e "$SPTPS_TEST" -a -e "$SPTPS_KEYPAIR_PATH" || exit "$EXIT_SKIP_TEST"
+
+port=30080
+
+server_priv="$DIR_FOO/server.priv"
+client_priv="$DIR_FOO/client.priv"
+server_pub="$DIR_FOO/server.pub"
+client_pub="$DIR_FOO/client.pub"
+
+echo [STEP] Generate keys
+
+mkdir -p "$DIR_FOO"
+"$SPTPS_KEYPAIR_PATH" "$server_priv" "$server_pub"
+"$SPTPS_KEYPAIR_PATH" "$client_priv" "$client_pub"
+
+echo [STEP] Test transfer of a simple file
+
+reference=testlib.sh
+
+(
+ sleep 3
+ "$SPTPS_TEST_PATH" -4 -q "$client_priv" "$server_pub" localhost $port <"$reference"
+) &
+
+"$SPTPS_TEST_PATH" -4 "$server_priv" "$client_pub" $port >"$DIR_FOO/out1"
+diff -w "$DIR_FOO/out1" "$reference"
+
+"$SPTPS_TEST_PATH" -4 -q "$server_priv" "$client_pub" $port <"$reference" &
+sleep 3
+"$SPTPS_TEST_PATH" -4 "$client_priv" "$server_pub" localhost $port >"$DIR_FOO/out2"
+diff -w "$DIR_FOO/out2" "$reference"
+
+echo [STEP] Datagram mode
+
+"$SPTPS_TEST_PATH" -4 -dq "$server_priv" "$client_pub" $port <"$reference" &
+sleep 3
+sleep 3 | "$SPTPS_TEST_PATH" -4 -dq "$client_priv" "$server_pub" localhost $port >"$DIR_FOO/out3"
+diff -w "$DIR_FOO/out3" "$reference"
--- /dev/null
+#!/bin/sh
+
+set -ex
+
+echo [STEP] Initialize test library
+
+# Paths to compiled executables
+
+# realpath on FreeBSD fails if the path does not exist.
+realdir() {
+ [ -e "$1" ] || mkdir -p "$1"
+ if type realpath >/dev/null; then
+ realpath "$1"
+ else
+ readlink -f "$1"
+ fi
+}
+
+# Exit status list
+# shellcheck disable=SC2034
+EXIT_FAILURE=1
+# shellcheck disable=SC2034
+EXIT_SKIP_TEST=77
+
+# The list of the environment variables that tinc injects into the scripts it calls.
+# shellcheck disable=SC2016
+TINC_SCRIPT_VARS='$NETNAME,$NAME,$DEVICE,$IFACE,$NODE,$REMOTEADDRESS,$REMOTEPORT,$SUBNET,$WEIGHT,$INVITATION_FILE,$INVITATION_URL,$DEBUG'
+
+# Test directories
+
+# Reuse script name if it was passed in an env var (when imported from tinc scripts).
+if [ -z "$SCRIPTNAME" ]; then
+ SCRIPTNAME=$(basename "$0")
+fi
+
+# Network names for tincd daemons.
+net1=$SCRIPTNAME.1
+net2=$SCRIPTNAME.2
+net3=$SCRIPTNAME.3
+
+# Configuration/pidfile directories for tincd daemons.
+DIR_FOO=$(realdir "$PWD/$net1")
+DIR_BAR=$(realdir "$PWD/$net2")
+DIR_BAZ=$(realdir "$PWD/$net3")
+
+# Register helper functions
+
+# Alias gtimeout to timeout if it exists.
+if type gtimeout >/dev/null; then
+ timeout() { gtimeout "$@"; }
+fi
+
+# As usual, BSD tools require special handling, as they do not support -i without a suffix.
+# Note that there must be no space after -i, or it won't work on GNU sed.
+sed_cmd() {
+ sed -i.orig "$@"
+}
+
+# Are the shell tools provided by busybox?
+is_busybox() {
+ timeout --help 2>&1 | grep -q -i busybox
+}
+
+# busybox timeout returns 128 + signal number (which is TERM by default)
+if is_busybox; then
+ # shellcheck disable=SC2034
+ EXIT_TIMEOUT=$((128 + 15))
+else
+ # shellcheck disable=SC2034
+ EXIT_TIMEOUT=124
+fi
+
+# Is this msys2?
+is_windows() {
+ test "$(uname -o)" = Msys
+}
+
+# Are we running on a CI server?
+is_ci() {
+ test "$CI"
+}
+
+# Dump error message and exit with an error.
+bail() {
+ echo >&2 "$@"
+ exit 1
+}
+
+# Remove carriage returns to normalize strings on Windows for easier comparisons.
+rm_cr() {
+ tr -d '\r'
+}
+
+if is_windows; then
+ normalize_path() { cygpath --mixed -- "$@"; }
+else
+ normalize_path() { echo "$@"; }
+fi
+
+# Executes whatever is passed to it, checking that the resulting exit code is non-zero.
+must_fail() {
+ if "$@"; then
+ bail "expected a non-zero exit code"
+ fi
+}
+
+# Executes the passed command and checks two conditions:
+# 1. it must exit successfully (with code 0)
+# 2. its output (stdout + stderr) must include the substring from the first argument (ignoring case)
+# usage: expect_msg 'expected message' command --with --args
+expect_msg() {
+ message=$1
+ shift
+
+ if ! output=$("$@" 2>&1); then
+ bail 'expected 0 exit code'
+ fi
+
+ if ! echo "$output" | grep -q -i "$message"; then
+ bail "expected message '$message'"
+ fi
+}
+
+# The reverse of expect_msg. We cannot simply wrap expect_msg with must_fail
+# because there should be a separate check for tinc exit code.
+fail_on_msg() {
+ message=$1
+ shift
+
+ if ! output=$("$@" 2>&1); then
+ bail 'expected 0 exit code'
+ fi
+
+ if echo "$output" | grep -q -i "$message"; then
+ bail "unexpected message '$message'"
+ fi
+}
+
+# Like expect_msg, but the command must fail with a non-zero exit code.
+# usage: must_fail_with_msg 'expected message' command --with --args
+must_fail_with_msg() {
+ message=$1
+ shift
+
+ if output=$("$@" 2>&1); then
+ bail "expected a non-zero exit code"
+ fi
+
+ if ! echo "$output" | grep -i -q "$message"; then
+ bail "expected message '$message'"
+ fi
+}
+
+# Is the legacy protocol enabled?
+with_legacy() {
+ tincd foo --version | grep -q legacy_protocol
+}
+
+# Are we running with EUID 0?
+is_root() {
+ test "$(id -u)" = 0
+}
+
+# Executes whatever is passed to it, checking that the resulting exit code is equal to the first argument.
+expect_code() {
+ expected=$1
+ shift
+
+ code=0
+ "$@" || code=$?
+
+ if [ $code != "$expected" ]; then
+ bail "wrong exit code $code, expected $expected"
+ fi
+}
+
+# wc -l on mac prints whitespace before the actual number.
+# This is simplest cross-platform alternative without that behavior.
+count_lines() {
+ awk 'END{ print NR }'
+}
+
+# Calls compiled tinc, passing any supplied arguments.
+# Usage: tinc { foo | bar | baz } --arg1 val1 "$args"
+tinc() {
+ peer=$1
+ shift
+
+ case "$peer" in
+ foo) "$TINC_PATH" -n "$net1" --config="$DIR_FOO" --pidfile="$DIR_FOO/pid" "$@" ;;
+ bar) "$TINC_PATH" -n "$net2" --config="$DIR_BAR" --pidfile="$DIR_BAR/pid" "$@" ;;
+ baz) "$TINC_PATH" -n "$net3" --config="$DIR_BAZ" --pidfile="$DIR_BAZ/pid" "$@" ;;
+ *) bail "invalid command [[$peer $*]]" ;;
+ esac
+}
+
+# Calls compiled tincd, passing any supplied arguments.
+# Usage: tincd { foo | bar | baz } --arg1 val1 "$args"
+tincd() {
+ peer=$1
+ shift
+
+ case "$peer" in
+ foo) "$TINCD_PATH" -n "$net1" --config="$DIR_FOO" --pidfile="$DIR_FOO/pid" --logfile="$DIR_FOO/log" -d5 "$@" ;;
+ bar) "$TINCD_PATH" -n "$net2" --config="$DIR_BAR" --pidfile="$DIR_BAR/pid" --logfile="$DIR_BAR/log" -d5 "$@" ;;
+ baz) "$TINCD_PATH" -n "$net3" --config="$DIR_BAZ" --pidfile="$DIR_BAZ/pid" --logfile="$DIR_BAZ/log" -d5 "$@" ;;
+ *) bail "invalid command [[$peer $*]]" ;;
+ esac
+}
+
+# Start the specified tinc daemon.
+# usage: start_tinc { foo | bar | baz }
+start_tinc() {
+ peer=$1
+ shift
+
+ case "$peer" in
+ foo) tinc "$peer" start --logfile="$DIR_FOO/log" -d5 "$@" ;;
+ bar) tinc "$peer" start --logfile="$DIR_BAR/log" -d5 "$@" ;;
+ baz) tinc "$peer" start --logfile="$DIR_BAZ/log" -d5 "$@" ;;
+ *) bail "invalid peer $peer" ;;
+ esac
+}
+
+# Stop all tinc clients.
+stop_all_tincs() {
+ (
+ # In case these pid files are mangled.
+ set +e
+ [ -f "$DIR_FOO/pid" ] && tinc foo stop
+ [ -f "$DIR_BAR/pid" ] && tinc bar stop
+ [ -f "$DIR_BAZ/pid" ] && tinc baz stop
+ true
+ )
+}
+
+# Checks that the number of reachable nodes matches what is expected.
+# usage: require_nodes node_name expected_number
+require_nodes() {
+ echo >&2 "Check that we're able to reach tincd"
+ test "$(tinc "$1" pid | count_lines)" = 1
+
+ echo >&2 "Check the number of reachable nodes for $1 (expecting $2)"
+ actual="$(tinc "$1" dump reachable nodes | count_lines)"
+
+ if [ "$actual" != "$2" ]; then
+ echo >&2 "tinc $1: expected $2 reachable nodes, got $actual"
+ exit 1
+ fi
+}
+
+peer_directory() {
+ peer=$1
+ case "$peer" in
+ foo) echo "$DIR_FOO" ;;
+ bar) echo "$DIR_BAR" ;;
+ baz) echo "$DIR_BAZ" ;;
+ *) bail "invalid peer $peer" ;;
+ esac
+}
+
+# This is an append-only log of all scripts executed by all peers.
+script_runs_log() {
+ echo "$(peer_directory "$1")/script-runs.log"
+}
+
+# Create tincd script. If it fails, it kills the test script with SIGTERM.
+# usage: create_script { foo | bar | baz } { tinc-up | host-down | ... } 'script content'
+create_script() {
+ peer=$1
+ script=$2
+ shift 2
+
+ # This is the line that we should start from when reading the script execution log while waiting
+ # for $script from $peer. It is a poor man's hash map to avoid polluting tinc's home directory with
+ # "last seen" files. There seem to be no good solutions to this that are compatible with all shells.
+ line_var=$(next_line_var "$peer" "$script")
+
+ # We must reassign it here in case the script is recreated.
+ # shellcheck disable=SC2229
+ read -r "$line_var" <<EOF
+1
+EOF
+
+ # Full path to the script.
+ script_path=$(peer_directory "$peer")/$script
+
+ # Full path to the script execution log (one for each peer).
+ script_log=$(script_runs_log "$peer")
+ printf '' >"$script_log"
+
+ # Script output is redirected into /dev/null. Otherwise, it ends up
+ # in tinc's output and breaks things like 'tinc invite'.
+ cat >"$script_path" <<EOF
+#!/bin/sh
+(
+ cd "$PWD" || exit 1
+ SCRIPTNAME="$SCRIPTNAME" . "$TESTLIB_PATH"
+ $@
+ echo "$script,\$$,$TINC_SCRIPT_VARS" >>"$script_log"
+) >/dev/null 2>&1 || kill -TERM $$
+EOF
+
+ chmod u+x "$script_path"
+
+ if is_windows; then
+ echo "@$MINGW_SHELL '$script_path'" >"$script_path.cmd"
+ fi
+}
+
+# Returns the name of the variable that contains the line number
+# we should read next when waiting on $script from $peer.
+# usage: next_line_var foo host-up
+next_line_var() {
+ peer=$1
+ script=$(echo "$2" | sed 's/[^a-zA-Z0-9]/_/g')
+ printf "%s" "next_line_${peer}_${script}"
+}
+
+# Waits for `peer`'s script `script` to finish `count` number of times.
+# usage: wait_script { foo | bar | baz } { tinc-up | host-up | ... } [count=1]
+wait_script() {
+ peer=$1
+ script=$2
+ count=$3
+
+ if [ -z "$count" ] || [ "$count" -lt 1 ]; then
+ count=1
+ fi
+
+ # Find out the location of the log and how many lines we should skip
+ # (because we've already seen them in previous invocations of wait_script
+ # for current $peer and $script).
+ line_var=$(next_line_var "$peer" "$script")
+
+ # eval is the only solution supported by POSIX shells.
+ # https://github.com/koalaman/shellcheck/wiki/SC3053
+ # 1. $line_var expands into 'next_line_foo_hosts_bar_up'
+ # 2. the name is substituted and the command becomes 'echo "$next_line_foo_hosts_bar_up"'
+ # 3. the command is evaluated and the line number is assigned to $line
+ line=$(eval "echo \"\$$line_var\"")
+
+ # This is the file that we monitor for script execution records.
+ script_log=$(script_runs_log "$peer")
+
+ # Starting from $line, read until $count matches are found.
+ # Print the number of the last matching line and exit.
+ # GNU tail 2.82 and newer terminates by itself when the pipe breaks.
+ # To support other tails we do an explicit `kill`.
+ # FIFO is useful here because otherwise it's difficult to determine
+ # which tail process should be killed. We could stick them in a process
+ # group by enabling job control, but this results in weird behavior when
+ # running tests in parallel on some interactive shells
+ # (e.g. when /bin/sh is symlinked to dash).
+ fifo=$(mktemp)
+ rm -f "$fifo"
+ mkfifo "$fifo"
+
+ # This weird thing is required to support old versions of ksh on NetBSD 8.2 and the like.
+ (tail -n +"$line" -f "$script_log" >"$fifo") &
+
+ new_line=$(
+ sh -c "
+ grep -n -m $count '^$script,' <'$fifo'
+ " | awk -F: 'END { print $1 }'
+ )
+
+ # Try to stop the background tail, ignoring possible failure (some tails
+ # detect EOF, some don't, so it may have already exited), but do wait on
+ # it (which is required at least by old ksh).
+ kill $! || true
+ wait || true
+ rm -f "$fifo"
+
+ # Remember the next line number for future reference. We'll use it if
+ # wait_script is called again with same $peer and $script.
+ read -r "${line_var?}" <<EOF
+$((line + new_line))
+EOF
+}
+
+# Cleanup after running each script.
+cleanup() {
+ (
+ set +ex
+
+ if command -v cleanup_hook 2>/dev/null; then
+ echo >&2 "Cleanup hook found, calling..."
+ cleanup_hook
+ fi
+
+ stop_all_tincs
+ ) || true
+}
+
+# If we're on a CI server, the test requires superuser privileges to run, and we're not
+# currently a superuser, try running the test as one and fail if it doesn't work (the
+# system must be configured to provide passwordless sudo for our user).
+require_root() {
+ if is_root; then
+ return
+ fi
+ if is_ci; then
+ echo "root is required for test $SCRIPTNAME, but we're a regular user; elevating privileges..."
+ if ! command -v sudo 2>/dev/null; then
+ bail "please install sudo and configure passwordless auth for user $USER"
+ fi
+ if ! sudo --preserve-env --non-interactive true; then
+ bail "sudo is not allowed or requires a password for user $USER"
+ fi
+ exec sudo --preserve-env "$@"
+ else
+ # Avoid these kinds of surprises outside CI. Just skip the test.
+ echo "root is required for test $SCRIPTNAME, but we're a regular user; skipping"
+ exit "$EXIT_SKIP_TEST"
+ fi
+}
+
+# Generate path to current shell which can be used from Windows applications.
+if is_windows; then
+ MINGW_SHELL=$(normalize_path "$SHELL")
+fi
+
+# This was called from a tincd script. Skip executing commands with side effects.
+[ -n "$NAME" ] && return
+
+echo [STEP] Check for leftover tinc daemons and test directories
+
+# Cleanup leftovers from previous runs.
+stop_all_tincs
+
+rm -rf "$DIR_FOO" "$DIR_BAR" "$DIR_BAZ"
+
+# Register cleanup function so we don't have to call it everywhere
+# (and failed scripts do not leave stray tincd running).
+trap cleanup EXIT INT TERM
--- /dev/null
+#!/bin/sh
+
+# shellcheck disable=SC1090
+. "$TESTLIB_PATH"
+
+echo [STEP] Initialize one node
+
+tinc foo init foo
+test "$(tinc foo get Name)" = "foo"
+
+echo [STEP] Test case sensitivity
+
+tinc foo set Mode switch
+test "$(tinc foo get Mode)" = "switch"
+test "$(tinc foo get mode)" = "switch"
+
+tinc foo set mode router
+test "$(tinc foo get Mode)" = "router"
+test "$(tinc foo get mode)" = "router"
+
+tinc foo set Mode Switch
+test "$(tinc foo get Mode)" = "Switch"
+
+echo [STEP] Test deletion
+
+expect_code "$EXIT_FAILURE" tinc foo del Mode hub
+tinc foo del Mode switch
+test -z "$(tinc foo get Mode)"
+
+echo [STEP] There can only be one Mode variable
+
+tinc foo add Mode switch
+tinc foo add Mode hub
+test "$(tinc foo get Mode)" = "hub"
+
+echo [STEP] Test addition/deletion of multivalued variables
+
+tinc foo add Subnet 1.1.1.1
+tinc foo add Subnet 2.2.2.2
+tinc foo add Subnet 2.2.2.2
+tinc foo add Subnet 3.3.3.3
+test "$(tinc foo get Subnet | rm_cr)" = "1.1.1.1
+2.2.2.2
+3.3.3.3"
+
+tinc foo del Subnet 2.2.2.2
+test "$(tinc foo get Subnet | rm_cr)" = "1.1.1.1
+3.3.3.3"
+
+tinc foo del Subnet
+test -z "$(tinc foo get Subnet)"
+
+echo [STEP] We should not be able to get/set server variables using node.variable syntax
+
+test -z "$(tinc foo get foo.Name)"
+expect_code "$EXIT_FAILURE" tinc foo set foo.Name bar
+
+echo [STEP] Test getting/setting host variables for other nodes
+
+touch "$DIR_FOO/hosts/bar"
+
+tinc foo add bar.PMTU 1
+tinc foo add bar.PMTU 2
+test "$(tinc foo get bar.PMTU)" = "2"
+
+tinc foo add bar.Subnet 1.1.1.1
+tinc foo add bar.Subnet 2.2.2.2
+tinc foo add bar.Subnet 2.2.2.2
+tinc foo add bar.Subnet 3.3.3.3
+test "$(tinc foo get bar.Subnet | rm_cr)" = "1.1.1.1
+2.2.2.2
+3.3.3.3"
+
+tinc foo del bar.Subnet 2.2.2.2
+test "$(tinc foo get bar.Subnet | rm_cr)" = "1.1.1.1
+3.3.3.3"
+
+tinc foo del bar.Subnet
+test -z "$(tinc foo get bar.Subnet)"
+
+echo [STEP] We should not be able to get/set for nodes with invalid names
+
+touch "$DIR_FOO/hosts/qu-ux"
+expect_code "$EXIT_FAILURE" tinc foo set qu-ux.Subnet 1.1.1.1
+
+echo [STEP] We should not be able to set obsolete variables unless forced
+
+expect_code "$EXIT_FAILURE" tinc foo set PrivateKey 12345
+tinc foo --force set PrivateKey 12345
+test "$(tinc foo get PrivateKey)" = "12345"
+
+tinc foo del PrivateKey
+test -z "$(tinc foo get PrivateKey)"
+
+echo [STEP] We should not be able to set/add malformed Subnets
+
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1.1.1
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:::6
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:6:7:8:9
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 256.256.256.256
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:6:7:8.123
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:6:7:1.2.3.4
+expect_code "$EXIT_FAILURE" tinc foo add Subnet a:b:c:d:e:f:g:h
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1.1.1.1/0
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1.1.1.1/-1
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1.1.1.1/33
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1::/0
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1::/-1
+expect_code "$EXIT_FAILURE" tinc foo add Subnet 1::/129
+expect_code "$EXIT_FAILURE" tinc foo add Subnet ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+test -z "$(tinc foo get Subnet)"
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initialize one node
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Mode switch
-set Broadcast no
-set Address localhost
-set Port 30010
-EOF
-
-start_tinc foo
-
-echo [STEP] Generate an invitation and let another node join the VPN
-
-invitation=$(tinc foo invite bar)
-tinc bar join "$invitation"
-
-echo [STEP] Test equivalence of host config files
-
-diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
-test "$(grep ^Ed25519PublicKey "$DIR_FOO/hosts/bar")" = "$(grep ^Ed25519PublicKey "$DIR_BAR/hosts/bar")"
-
-echo [STEP] Test Mode, Broadcast and ConnectTo statements
-
-test "$(tinc bar get Mode)" = switch
-test "$(tinc bar get Broadcast)" = no
-test "$(tinc bar get ConnectTo)" = foo
-
-echo [STEP] Check whether the new node can join the VPN
-
-tinc bar <<EOF
-set DeviceType dummy
-set Port 0
-EOF
-
-create_script foo hosts/bar-up
-create_script bar hosts/foo-up
-
-start_tinc bar
-
-wait_script foo hosts/bar-up
-wait_script bar hosts/foo-up
-
-require_nodes foo 2
-require_nodes bar 2
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initialize one node
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Mode switch
-set Broadcast no
-set Address localhost
-set Port 30020
-EOF
-
-echo [STEP] Generate an invitation offline and let another node join the VPN
-
-invitation=$(tinc foo invite bar)
-
-start_tinc foo
-tinc bar join "$invitation"
-
-echo [STEP] Test equivalence of host config files
-
-diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
-test "$(grep ^Ed25519PublicKey "$DIR_FOO/hosts/bar")" = "$(grep ^Ed25519PublicKey "$DIR_BAR/hosts/bar")"
-
-echo [STEP] Test Mode, Broadcast and ConnectTo statements
-
-test "$(tinc bar get Mode)" = switch
-test "$(tinc bar get Broadcast)" = no
-test "$(tinc bar get ConnectTo)" = foo
-
-echo [STEP] Check whether the new node can join the VPN
-
-tinc bar <<EOF
-set DeviceType dummy
-set Port 0
-EOF
-
-create_script foo hosts/bar-up
-create_script bar hosts/foo-up
-
-start_tinc bar
-
-wait_script foo hosts/bar-up
-wait_script bar hosts/foo-up
-
-require_nodes foo 2
-require_nodes bar 2
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initialize one node
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Address localhost
-set Port 30030
-EOF
-
-create_script foo tinc-up
-start_tinc foo
-wait_script foo tinc-up
-
-echo [STEP] Generate an invitation and let another node join the VPN
-
-# shellcheck disable=SC2016
-create_script foo invitation-created '
-cat >"$INVITATION_FILE" <<INVITE
-Name = $NODE
-Ifconfig = 93.184.216.34/24
-Route = 2606:2800:220:1::/64 2606:2800:220:1:248:1893:25c8:1946
-Route = 1.2.3.4 1234::
-
-$(tinc foo export)
-INVITE
-'
-
-tinc foo invite bar | tail -1 | tinc bar --batch join
-
-echo [STEP] Test equivalence of host config files
-
-diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
-test "$(grep ^Ed25519PublicKey "$DIR_FOO/hosts/bar")" = "$(grep ^Ed25519PublicKey "$DIR_BAR/hosts/bar")"
-
-echo [STEP] Check if the tinc-up.invitation file is created and contains the right commands
-
-bar_tinc_up="$DIR_BAR/tinc-up.invitation"
-test -f "$bar_tinc_up"
-
-grep -F -q "93.184.216.34/24" "$bar_tinc_up"
-grep -F -q "2606:2800:220:1::/64" "$bar_tinc_up"
-grep -F -q "2606:2800:220:1:248:1893:25c8:1946" "$bar_tinc_up"
-must_fail grep -F -q "1234::" "$bar_tinc_up"
-
-echo [STEP] Check that no tinc-up is created and that tinc-up.invitation is not executable
-
-must_fail test -x "$bar_tinc_up"
-must_fail test -f "$DIR_BAR/tinc-up"
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initialize two nodes
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Port 30060
-set Address localhost
-add Subnet 10.98.98.1
-set PingTimeout 2
-EOF
-
-tinc bar <<EOF
-init bar
-set DeviceType dummy
-set Port 0
-add Subnet 10.98.98.2
-set PingTimeout 2
-set MaxTimeout 2
-EOF
-
-echo [STEP] Exchange host config files
-
-tinc foo export | tinc bar exchange | tinc foo import
-tinc bar add ConnectTo foo
-
-echo [STEP] Foo 1.1, bar 1.0
-
-tinc bar set ExperimentalProtocol no
-tinc foo del bar.Ed25519PublicKey
-tinc bar del foo.Ed25519PublicKey
-
-create_script foo hosts/bar-up
-create_script bar hosts/foo-up
-
-start_tinc foo
-start_tinc bar
-
-wait_script foo hosts/bar-up
-wait_script bar hosts/foo-up
-
-require_nodes foo 2
-require_nodes bar 2
-
-tinc bar stop
-tinc foo stop
-
-test -z "$(tinc foo get bar.Ed25519PublicKey)"
-test -z "$(tinc bar get foo.Ed25519PublicKey)"
-
-echo [STEP] Foo 1.1, bar upgrades to 1.1
-
-tinc bar del ExperimentalProtocol
-
-start_tinc foo
-start_tinc bar
-
-wait_script foo hosts/bar-up
-wait_script bar hosts/foo-up
-
-require_nodes foo 2
-require_nodes bar 2
-
-tinc bar stop
-tinc foo stop
-
-test -n "$(tinc foo get bar.Ed25519PublicKey)"
-test -n "$(tinc bar get foo.Ed25519PublicKey)"
-
-echo [STEP] Bar downgrades, must no longer be allowed to connect
-
-tinc bar set ExperimentalProtocol no
-
-create_script foo subnet-up
-start_tinc foo
-wait_script foo subnet-up
-
-create_script bar subnet-up
-start_tinc bar
-wait_script bar subnet-up
-
-# There is no reliable way to wait for 'not connecting'.
-sleep 10
-
-require_nodes foo 1
-require_nodes bar 1
-tests = [
- 'basic.test',
- 'commandline.test',
- 'executables.test',
- 'import-export.test',
- 'invite-join.test',
- 'invite-offline.test',
- 'invite-tinc-up.test',
- 'scripts.test',
- 'security.test',
- 'variables.test',
-]
-
-if opt_crypto != 'nolegacy'
- tests += 'algorithms.test'
- tests += 'legacy-protocol.test'
-endif
-
-if os_name != 'windows'
- tests += 'sptps-basic.test'
-endif
-
-if os_name == 'linux'
- tests += 'ns-ping.test'
-endif
-
-exe_splice = executable(
- 'splice',
- sources: 'splice.c',
- dependencies: deps_common,
- implicit_include_directories: false,
- include_directories: inc_conf,
- build_by_default: false,
-)
-
-env = environment()
-env.set('TINC_PATH', exe_tinc.full_path())
-env.set('TINCD_PATH', exe_tincd.full_path())
-env.set('SPTPS_TEST_PATH', exe_sptps_test.full_path())
-env.set('SPTPS_KEYPAIR_PATH', exe_sptps_keypair.full_path())
-env.set('SPLICE_PATH', exe_splice.full_path())
-env.set('TESTLIB_PATH', src_root / 'test' / 'testlib.sh')
-
-deps_test = [
- exe_tinc,
- exe_tincd,
- exe_splice,
- exe_sptps_test,
- exe_sptps_keypair,
-]
-
-test_wd = meson.current_build_dir()
-
-foreach test_name : tests
- target = find_program(test_name, native: true)
- test(test_name,
- target,
- timeout: 5 * 60,
- env: env,
- depends: deps_test,
- workdir: test_wd)
-endforeach
+subdir('integration')
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-require_root "$0" "$@"
-test -e /dev/net/tun || exit "$EXIT_SKIP_TEST"
-ip netns list || exit "$EXIT_SKIP_TEST"
-
-ip_foo=192.168.1.1
-ip_bar=192.168.1.2
-mask=24
-
-echo [STEP] Create network namespaces
-
-ip netns add ping.test1
-ip netns add ping.test2
-
-cleanup_hook() {
- ip netns del ping.test1
- ip netns del ping.test2
-}
-
-echo [STEP] Initialize two nodes
-
-tinc foo <<EOF
-init foo
-set Subnet $ip_foo
-set Interface ping.test1
-set Port 30090
-set Address localhost
-set AutoConnect no
-EOF
-
-# shellcheck disable=SC2016
-create_script foo tinc-up "
- ip link set dev \$INTERFACE netns ping.test1
- ip netns exec ping.test1 ip addr add $ip_foo/$mask dev \$INTERFACE
- ip netns exec ping.test1 ip link set \$INTERFACE up
-"
-
-tinc bar <<EOF
-init bar
-set Subnet $ip_bar
-set Interface ping.test2
-set Port 30091
-set AutoConnect no
-EOF
-
-# shellcheck disable=SC2016
-create_script bar tinc-up "
- ip link set dev \$INTERFACE netns ping.test2
- ip netns exec ping.test2 ip addr add $ip_bar/$mask dev \$INTERFACE
- ip netns exec ping.test2 ip link set \$INTERFACE up
-"
-
-echo [STEP] Exchange configuration files
-
-tinc foo export | tinc bar exchange | tinc foo import
-
-echo [STEP] Start tinc
-
-start_tinc foo
-start_tinc bar
-
-wait_script foo tinc-up
-wait_script bar tinc-up
-
-echo [STEP] The nodes should not be able to ping each other if there is no connection
-
-must_fail ip netns exec ping.test1 ping -W1 -c3 $ip_bar
-
-echo [STEP] After connecting they should be
-
-create_script bar hosts/foo-up
-
-tinc bar add ConnectTo foo
-wait_script bar hosts/foo-up
-
-ip netns exec ping.test1 ping -W1 -c3 $ip_bar
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initializing server node
-
-port_foo=30040
-port_bar=30041
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Port $port_foo
-set Address 127.0.0.1
-add Subnet 10.0.0.1
-add Subnet fec0::/64
-EOF
-
-echo [STEP] Setting up scripts
-
-OUT=$DIR_FOO/scripts.out
-rm -f "$OUT"
-
-for script in \
- tinc-up tinc-down \
- host-up host-down \
- subnet-up subnet-down \
- hosts/foo-up hosts/foo-down \
- hosts/bar-up hosts/bar-down \
- invitation-created invitation-accepted; do
-
- commands=$(
- cat <<EOF
- if is_windows && [ -n "\$INVITATION_FILE" ]; then
- INVITATION_FILE=\$(cygpath --unix -- "\$INVITATION_FILE")
- fi
- echo >>'$OUT' "$script" "$TINC_SCRIPT_VARS"
-EOF
- )
-
- create_script foo "$script" "$commands"
-done
-
-echo [STEP] Starting server node
-
-start_tinc foo -n netname
-wait_script foo subnet-up 2
-echo foo-started >>"$OUT"
-
-echo [STEP] Inviting client node
-
-url=$(tinc foo -n netname2 invite bar)
-file=$(basename "$(find "$DIR_FOO/invitations" -type f ! -name ed25519_key.priv)")
-
-if is_windows; then
- file=$(cygpath --unix -- "$file")
-fi
-
-wait_script foo invitation-created
-echo bar-invited >>"$OUT"
-
-echo [STEP] Joining client node
-
-tinc bar -n netname3 join "$url"
-wait_script foo invitation-accepted
-echo bar-joined >>"$OUT"
-
-echo [STEP] Starting client node
-
-tinc bar <<EOF
-set DeviceType dummy
-set Port $port_bar
-add Subnet 10.0.0.2
-add Subnet fec0::/64#5
-EOF
-
-start_tinc bar
-wait_script foo subnet-up 2
-echo bar-started-1 >>"$OUT"
-
-tinc foo debug 4
-tinc bar stop
-wait_script foo subnet-down 2
-echo bar-stopped >>"$OUT"
-
-tinc foo debug 5
-start_tinc bar
-wait_script foo subnet-up 2
-echo bar-started-2 >>"$OUT"
-
-echo [STEP] Stop server node
-
-tinc foo stop
-tinc bar stop
-wait_script foo tinc-down
-
-echo [STEP] Check if the script output is what is expected
-
-cat >"$OUT.expected" <<EOF
-tinc-up netname,foo,dummy,,,,,,,,,5
-subnet-up netname,foo,dummy,,foo,,,10.0.0.1,,,,5
-subnet-up netname,foo,dummy,,foo,,,fec0::/64,,,,5
-foo-started
-invitation-created netname2,foo,,,bar,,,,,$DIR_FOO/invitations/$file,$url,
-bar-invited
-invitation-accepted netname,foo,dummy,,bar,127.0.0.1,,,,,,5
-bar-joined
-host-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
-hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
-subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,5
-subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,5
-bar-started-1
-host-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,4
-hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,4
-subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,4
-subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,4
-bar-stopped
-host-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
-hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
-subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,5
-subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,5
-bar-started-2
-host-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
-hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
-subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,5
-subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,5
-subnet-down netname,foo,dummy,,foo,,,10.0.0.1,,,,5
-subnet-down netname,foo,dummy,,foo,,,fec0::/64,,,,5
-tinc-down netname,foo,dummy,,,,,,,,,5
-EOF
-
-diff -w "$OUT" "$OUT.expected"
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Skip this test if tools are missing
-
-command -v nc >/dev/null || exit "$EXIT_SKIP_TEST"
-command -v timeout >/dev/null || exit "$EXIT_SKIP_TEST"
-
-foo_port=30050
-bar_port=30051
-
-# usage: splice protocol_version
-splice() {
- "$SPLICE_PATH" foo localhost $foo_port bar localhost $bar_port "$1" &
- sleep 10
-}
-
-# usage: send_with_timeout "data to send" "data expected to receive"
-send_with_timeout() {
- data=$1
- expected=$3
-
- result=$(
- (
- sleep 6
- printf "%s\n" "$data"
- ) | timeout 10 nc localhost $foo_port
- ) && exit 1
-
- test $? = "$EXIT_TIMEOUT"
-
- if [ -z "$expected" ]; then
- test -z "$result"
- else
- echo "$result" | grep -q "^$expected"
- fi
-}
-
-echo [STEP] Initialize two nodes
-
-tinc foo <<EOF
-init foo
-set DeviceType dummy
-set Port $foo_port
-set Address localhost
-set PingTimeout 3
-set AutoConnect no
-set Subnet 10.96.96.1
-EOF
-
-tinc bar <<EOF
-init bar
-set DeviceType dummy
-set Port $bar_port
-set PingTimeout 3
-set MaxTimeout 3
-set ExperimentalProtocol no
-set AutoConnect no
-set Subnet 10.96.96.2
-EOF
-
-echo [STEP] Exchange host config files
-
-tinc foo export | tinc bar exchange | tinc foo import
-
-create_script foo subnet-up
-start_tinc foo
-wait_script foo subnet-up
-
-create_script bar subnet-up
-start_tinc bar
-wait_script bar subnet-up
-
-echo "[STEP] No ID sent by responding node if we don't send an ID first, before the timeout"
-send_with_timeout "0 bar 17.7" ""
-
-echo [STEP] ID sent if initiator sends first, but still tarpitted
-send_with_timeout "0 bar 17.7" "0 foo 17.7"
-
-echo [STEP] No invalid IDs allowed
-send_with_timeout "0 foo 17.7" ""
-send_with_timeout "0 baz 17.7" ""
-
-echo [STEP] No NULL METAKEYs allowed
-data="0 foo 17.0\n1 0 672 0 0 834188619F4D943FD0F4B1336F428BD4AC06171FEABA66BD2356BC9593F0ECD643F0E4B748C670D7750DFDE75DC9F1D8F65AB1026F5ED2A176466FBA4167CC567A2085ABD070C1545B180BDA86020E275EA9335F509C57786F4ED2378EFFF331869B856DDE1C05C461E4EECAF0E2FB97AF77B7BC2AD1B34C12992E45F5D1254BBF0C3FB224ABB3E8859594A83B6CA393ED81ECAC9221CE6BC71A727BCAD87DD80FC0834B87BADB5CB8FD3F08BEF90115A8DF1923D7CD9529729F27E1B8ABD83C4CF8818AE10257162E0057A658E265610B71F9BA4B365A20C70578FAC65B51B91100392171BA12A440A5E93C4AA62E0C9B6FC9B68F953514AAA7831B4B2C31C4\n"
-send_with_timeout "$data" "" # Not even the ID should be sent when the first packet contains illegal data
-
-echo [STEP] No splicing allowed
-
-tinc bar stop
-tinc bar del ExperimentalProtocol
-
-create_script bar subnet-up
-start_tinc bar
-wait_script bar subnet-up
-
-splice 17.7
-pid=$!
-
-require_nodes foo 1
-require_nodes bar 1
-
-kill $pid
-
-tinc bar stop
-tinc foo stop
-
-echo [STEP] Test splicing again with legacy protocol
-
-tinc foo set ExperimentalProtocol no
-tinc bar set ExperimentalProtocol no
-
-create_script foo subnet-up
-start_tinc foo
-wait_script foo subnet-up
-
-create_script bar subnet-up
-start_tinc bar
-wait_script bar subnet-up
-
-splice 17.0
-pid=$!
-
-require_nodes foo 1
-require_nodes bar 1
-
-kill $pid
+++ /dev/null
-/*
- splice.c -- Splice two outgoing tinc connections together
- Copyright (C) 2018 Guus Sliepen <guus@tinc-vpn.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "../src/system.h"
-
-#ifdef HAVE_MINGW
-static const char *winerror(int err) {
- static char buf[1024], *ptr;
-
- ptr = buf + snprintf(buf, sizeof(buf), "(%d) ", err);
-
- if(!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ptr, sizeof(buf) - (ptr - buf), NULL)) {
- strncpy(buf, "(unable to format errormessage)", sizeof(buf));
- };
-
- if((ptr = strchr(buf, '\r'))) {
- *ptr = '\0';
- }
-
- return buf;
-}
-
-#define strerror(x) ((x)>0?strerror(x):winerror(GetLastError()))
-#define sockerrno WSAGetLastError()
-#define sockstrerror(x) winerror(x)
-#else
-#define sockerrno errno
-#define sockstrerror(x) strerror(x)
-#endif
-
-int main(int argc, char *argv[]) {
- if(argc < 7) {
- fprintf(stderr, "Usage: %s name1 host1 port1 name2 host2 port2 [protocol]\n", argv[0]);
- return 1;
- }
-
- const char *protocol;
-
- if(argc >= 8) {
- protocol = argv[7];
- } else {
- protocol = "17.7";
- }
-
-#ifdef HAVE_MINGW
- static struct WSAData wsa_state;
-
- if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
- return 1;
- }
-
-#endif
- int sock[2];
- char buf[1024];
-
- const struct addrinfo hint = {
- .ai_family = AF_UNSPEC,
- .ai_socktype = SOCK_STREAM,
- .ai_protocol = IPPROTO_TCP,
- .ai_flags = 0,
- };
-
- for(int i = 0; i < 2; i++) {
- struct addrinfo *ai;
-
- if(getaddrinfo(argv[2 + 3 * i], argv[3 + 3 * i], &hint, &ai) || !ai) {
- fprintf(stderr, "getaddrinfo() failed: %s\n", sockstrerror(sockerrno));
- return 1;
- }
-
- sock[i] = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-
- if(sock[i] == -1) {
- fprintf(stderr, "Could not create socket: %s\n", sockstrerror(sockerrno));
- freeaddrinfo(ai);
- return 1;
- }
-
- if(connect(sock[i], ai->ai_addr, ai->ai_addrlen)) {
- fprintf(stderr, "Could not connect to %s: %s\n", argv[i + 3 * i], sockstrerror(sockerrno));
- freeaddrinfo(ai);
- return 1;
- }
-
- freeaddrinfo(ai);
-
- fprintf(stderr, "Connected to %s\n", argv[1 + 3 * i]);
-
- /* Pretend to be the other one */
- int len = snprintf(buf, sizeof buf, "0 %s %s\n", argv[4 - 3 * i], protocol);
-
- if(send(sock[i], buf, len, 0) != len) {
- fprintf(stderr, "Error sending data to %s: %s\n", argv[1 + 3 * i], sockstrerror(sockerrno));
- return 1;
- }
-
- /* Ignore the response */
- do {
- if(recv(sock[i], buf, 1, 0) != 1) {
- fprintf(stderr, "Error reading data from %s: %s\n", argv[1 + 3 * i], sockstrerror(sockerrno));
- return 1;
- }
- } while(*buf != '\n');
- }
-
- fprintf(stderr, "Splicing...\n");
-
- int nfds = (sock[0] > sock[1] ? sock[0] : sock[1]) + 1;
-
- while(true) {
- fd_set fds;
- FD_ZERO(&fds);
- FD_SET(sock[0], &fds);
- FD_SET(sock[1], &fds);
-
- if(select(nfds, &fds, NULL, NULL, NULL) <= 0) {
- return 1;
- }
-
- for(int i = 0; i < 2; i++) {
- if(FD_ISSET(sock[i], &fds)) {
- ssize_t len = recv(sock[i], buf, sizeof buf, 0);
-
- if(len < 0) {
- fprintf(stderr, "Error while reading from %s: %s\n", argv[1 + i * 3], sockstrerror(sockerrno));
- return 1;
- }
-
- if(len == 0) {
- fprintf(stderr, "Connection closed by %s\n", argv[1 + i * 3]);
- return 0;
- }
-
- if(send(sock[i ^ 1], buf, len, 0) != len) {
- fprintf(stderr, "Error while writing to %s: %s\n", argv[4 - i * 3], sockstrerror(sockerrno));
- return 1;
- }
- }
- }
- }
-
- return 0;
-}
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Skip this test if we did not compile sptps_test
-
-test -e "$SPTPS_TEST" -a -e "$SPTPS_KEYPAIR_PATH" || exit "$EXIT_SKIP_TEST"
-
-port=30080
-
-server_priv="$DIR_FOO/server.priv"
-client_priv="$DIR_FOO/client.priv"
-server_pub="$DIR_FOO/server.pub"
-client_pub="$DIR_FOO/client.pub"
-
-echo [STEP] Generate keys
-
-mkdir -p "$DIR_FOO"
-"$SPTPS_KEYPAIR_PATH" "$server_priv" "$server_pub"
-"$SPTPS_KEYPAIR_PATH" "$client_priv" "$client_pub"
-
-echo [STEP] Test transfer of a simple file
-
-reference=testlib.sh
-
-(
- sleep 3
- "$SPTPS_TEST_PATH" -4 -q "$client_priv" "$server_pub" localhost $port <"$reference"
-) &
-
-"$SPTPS_TEST_PATH" -4 "$server_priv" "$client_pub" $port >"$DIR_FOO/out1"
-diff -w "$DIR_FOO/out1" "$reference"
-
-"$SPTPS_TEST_PATH" -4 -q "$server_priv" "$client_pub" $port <"$reference" &
-sleep 3
-"$SPTPS_TEST_PATH" -4 "$client_priv" "$server_pub" localhost $port >"$DIR_FOO/out2"
-diff -w "$DIR_FOO/out2" "$reference"
-
-echo [STEP] Datagram mode
-
-"$SPTPS_TEST_PATH" -4 -dq "$server_priv" "$client_pub" $port <"$reference" &
-sleep 3
-sleep 3 | "$SPTPS_TEST_PATH" -4 -dq "$client_priv" "$server_pub" localhost $port >"$DIR_FOO/out3"
-diff -w "$DIR_FOO/out3" "$reference"
+++ /dev/null
-#!/bin/sh
-
-set -ex
-
-echo [STEP] Initialize test library
-
-# Paths to compiled executables
-
-# realpath on FreeBSD fails if the path does not exist.
-realdir() {
- [ -e "$1" ] || mkdir -p "$1"
- if type realpath >/dev/null; then
- realpath "$1"
- else
- readlink -f "$1"
- fi
-}
-
-# Exit status list
-# shellcheck disable=SC2034
-EXIT_FAILURE=1
-# shellcheck disable=SC2034
-EXIT_SKIP_TEST=77
-
-# The list of the environment variables that tinc injects into the scripts it calls.
-# shellcheck disable=SC2016
-TINC_SCRIPT_VARS='$NETNAME,$NAME,$DEVICE,$IFACE,$NODE,$REMOTEADDRESS,$REMOTEPORT,$SUBNET,$WEIGHT,$INVITATION_FILE,$INVITATION_URL,$DEBUG'
-
-# Test directories
-
-# Reuse script name if it was passed in an env var (when imported from tinc scripts).
-if [ -z "$SCRIPTNAME" ]; then
- SCRIPTNAME=$(basename "$0")
-fi
-
-# Network names for tincd daemons.
-net1=$SCRIPTNAME.1
-net2=$SCRIPTNAME.2
-net3=$SCRIPTNAME.3
-
-# Configuration/pidfile directories for tincd daemons.
-DIR_FOO=$(realdir "$PWD/$net1")
-DIR_BAR=$(realdir "$PWD/$net2")
-DIR_BAZ=$(realdir "$PWD/$net3")
-
-# Register helper functions
-
-# Alias gtimeout to timeout if it exists.
-if type gtimeout >/dev/null; then
- timeout() { gtimeout "$@"; }
-fi
-
-# As usual, BSD tools require special handling, as they do not support -i without a suffix.
-# Note that there must be no space after -i, or it won't work on GNU sed.
-sed_cmd() {
- sed -i.orig "$@"
-}
-
-# Are the shell tools provided by busybox?
-is_busybox() {
- timeout --help 2>&1 | grep -q -i busybox
-}
-
-# busybox timeout returns 128 + signal number (which is TERM by default)
-if is_busybox; then
- # shellcheck disable=SC2034
- EXIT_TIMEOUT=$((128 + 15))
-else
- # shellcheck disable=SC2034
- EXIT_TIMEOUT=124
-fi
-
-# Is this msys2?
-is_windows() {
- test "$(uname -o)" = Msys
-}
-
-# Are we running on a CI server?
-is_ci() {
- test "$CI"
-}
-
-# Dump error message and exit with an error.
-bail() {
- echo >&2 "$@"
- exit 1
-}
-
-# Remove carriage returns to normalize strings on Windows for easier comparisons.
-rm_cr() {
- tr -d '\r'
-}
-
-if is_windows; then
- normalize_path() { cygpath --mixed -- "$@"; }
-else
- normalize_path() { echo "$@"; }
-fi
-
-# Executes whatever is passed to it, checking that the resulting exit code is non-zero.
-must_fail() {
- if "$@"; then
- bail "expected a non-zero exit code"
- fi
-}
-
-# Executes the passed command and checks two conditions:
-# 1. it must exit successfully (with code 0)
-# 2. its output (stdout + stderr) must include the substring from the first argument (ignoring case)
-# usage: expect_msg 'expected message' command --with --args
-expect_msg() {
- message=$1
- shift
-
- if ! output=$("$@" 2>&1); then
- bail 'expected 0 exit code'
- fi
-
- if ! echo "$output" | grep -q -i "$message"; then
- bail "expected message '$message'"
- fi
-}
-
-# The reverse of expect_msg. We cannot simply wrap expect_msg with must_fail
-# because there should be a separate check for tinc exit code.
-fail_on_msg() {
- message=$1
- shift
-
- if ! output=$("$@" 2>&1); then
- bail 'expected 0 exit code'
- fi
-
- if echo "$output" | grep -q -i "$message"; then
- bail "unexpected message '$message'"
- fi
-}
-
-# Like expect_msg, but the command must fail with a non-zero exit code.
-# usage: must_fail_with_msg 'expected message' command --with --args
-must_fail_with_msg() {
- message=$1
- shift
-
- if output=$("$@" 2>&1); then
- bail "expected a non-zero exit code"
- fi
-
- if ! echo "$output" | grep -i -q "$message"; then
- bail "expected message '$message'"
- fi
-}
-
-# Is the legacy protocol enabled?
-with_legacy() {
- tincd foo --version | grep -q legacy_protocol
-}
-
-# Are we running with EUID 0?
-is_root() {
- test "$(id -u)" = 0
-}
-
-# Executes whatever is passed to it, checking that the resulting exit code is equal to the first argument.
-expect_code() {
- expected=$1
- shift
-
- code=0
- "$@" || code=$?
-
- if [ $code != "$expected" ]; then
- bail "wrong exit code $code, expected $expected"
- fi
-}
-
-# wc -l on mac prints whitespace before the actual number.
-# This is simplest cross-platform alternative without that behavior.
-count_lines() {
- awk 'END{ print NR }'
-}
-
-# Calls compiled tinc, passing any supplied arguments.
-# Usage: tinc { foo | bar | baz } --arg1 val1 "$args"
-tinc() {
- peer=$1
- shift
-
- case "$peer" in
- foo) "$TINC_PATH" -n "$net1" --config="$DIR_FOO" --pidfile="$DIR_FOO/pid" "$@" ;;
- bar) "$TINC_PATH" -n "$net2" --config="$DIR_BAR" --pidfile="$DIR_BAR/pid" "$@" ;;
- baz) "$TINC_PATH" -n "$net3" --config="$DIR_BAZ" --pidfile="$DIR_BAZ/pid" "$@" ;;
- *) bail "invalid command [[$peer $*]]" ;;
- esac
-}
-
-# Calls compiled tincd, passing any supplied arguments.
-# Usage: tincd { foo | bar | baz } --arg1 val1 "$args"
-tincd() {
- peer=$1
- shift
-
- case "$peer" in
- foo) "$TINCD_PATH" -n "$net1" --config="$DIR_FOO" --pidfile="$DIR_FOO/pid" --logfile="$DIR_FOO/log" -d5 "$@" ;;
- bar) "$TINCD_PATH" -n "$net2" --config="$DIR_BAR" --pidfile="$DIR_BAR/pid" --logfile="$DIR_BAR/log" -d5 "$@" ;;
- baz) "$TINCD_PATH" -n "$net3" --config="$DIR_BAZ" --pidfile="$DIR_BAZ/pid" --logfile="$DIR_BAZ/log" -d5 "$@" ;;
- *) bail "invalid command [[$peer $*]]" ;;
- esac
-}
-
-# Start the specified tinc daemon.
-# usage: start_tinc { foo | bar | baz }
-start_tinc() {
- peer=$1
- shift
-
- case "$peer" in
- foo) tinc "$peer" start --logfile="$DIR_FOO/log" -d5 "$@" ;;
- bar) tinc "$peer" start --logfile="$DIR_BAR/log" -d5 "$@" ;;
- baz) tinc "$peer" start --logfile="$DIR_BAZ/log" -d5 "$@" ;;
- *) bail "invalid peer $peer" ;;
- esac
-}
-
-# Stop all tinc clients.
-stop_all_tincs() {
- (
- # In case these pid files are mangled.
- set +e
- [ -f "$DIR_FOO/pid" ] && tinc foo stop
- [ -f "$DIR_BAR/pid" ] && tinc bar stop
- [ -f "$DIR_BAZ/pid" ] && tinc baz stop
- true
- )
-}
-
-# Checks that the number of reachable nodes matches what is expected.
-# usage: require_nodes node_name expected_number
-require_nodes() {
- echo >&2 "Check that we're able to reach tincd"
- test "$(tinc "$1" pid | count_lines)" = 1
-
- echo >&2 "Check the number of reachable nodes for $1 (expecting $2)"
- actual="$(tinc "$1" dump reachable nodes | count_lines)"
-
- if [ "$actual" != "$2" ]; then
- echo >&2 "tinc $1: expected $2 reachable nodes, got $actual"
- exit 1
- fi
-}
-
-peer_directory() {
- peer=$1
- case "$peer" in
- foo) echo "$DIR_FOO" ;;
- bar) echo "$DIR_BAR" ;;
- baz) echo "$DIR_BAZ" ;;
- *) bail "invalid peer $peer" ;;
- esac
-}
-
-# This is an append-only log of all scripts executed by all peers.
-script_runs_log() {
- echo "$(peer_directory "$1")/script-runs.log"
-}
-
-# Create tincd script. If it fails, it kills the test script with SIGTERM.
-# usage: create_script { foo | bar | baz } { tinc-up | host-down | ... } 'script content'
-create_script() {
- peer=$1
- script=$2
- shift 2
-
- # This is the line that we should start from when reading the script execution log while waiting
- # for $script from $peer. It is a poor man's hash map to avoid polluting tinc's home directory with
- # "last seen" files. There seem to be no good solutions to this that are compatible with all shells.
- line_var=$(next_line_var "$peer" "$script")
-
- # We must reassign it here in case the script is recreated.
- # shellcheck disable=SC2229
- read -r "$line_var" <<EOF
-1
-EOF
-
- # Full path to the script.
- script_path=$(peer_directory "$peer")/$script
-
- # Full path to the script execution log (one for each peer).
- script_log=$(script_runs_log "$peer")
- printf '' >"$script_log"
-
- # Script output is redirected into /dev/null. Otherwise, it ends up
- # in tinc's output and breaks things like 'tinc invite'.
- cat >"$script_path" <<EOF
-#!/bin/sh
-(
- cd "$PWD" || exit 1
- SCRIPTNAME="$SCRIPTNAME" . "$TESTLIB_PATH"
- $@
- echo "$script,\$$,$TINC_SCRIPT_VARS" >>"$script_log"
-) >/dev/null 2>&1 || kill -TERM $$
-EOF
-
- chmod u+x "$script_path"
-
- if is_windows; then
- echo "@$MINGW_SHELL '$script_path'" >"$script_path.cmd"
- fi
-}
-
-# Returns the name of the variable that contains the line number
-# we should read next when waiting on $script from $peer.
-# usage: next_line_var foo host-up
-next_line_var() {
- peer=$1
- script=$(echo "$2" | sed 's/[^a-zA-Z0-9]/_/g')
- printf "%s" "next_line_${peer}_${script}"
-}
-
-# Waits for `peer`'s script `script` to finish `count` number of times.
-# usage: wait_script { foo | bar | baz } { tinc-up | host-up | ... } [count=1]
-wait_script() {
- peer=$1
- script=$2
- count=$3
-
- if [ -z "$count" ] || [ "$count" -lt 1 ]; then
- count=1
- fi
-
- # Find out the location of the log and how many lines we should skip
- # (because we've already seen them in previous invocations of wait_script
- # for current $peer and $script).
- line_var=$(next_line_var "$peer" "$script")
-
- # eval is the only solution supported by POSIX shells.
- # https://github.com/koalaman/shellcheck/wiki/SC3053
- # 1. $line_var expands into 'next_line_foo_hosts_bar_up'
- # 2. the name is substituted and the command becomes 'echo "$next_line_foo_hosts_bar_up"'
- # 3. the command is evaluated and the line number is assigned to $line
- line=$(eval "echo \"\$$line_var\"")
-
- # This is the file that we monitor for script execution records.
- script_log=$(script_runs_log "$peer")
-
- # Starting from $line, read until $count matches are found.
- # Print the number of the last matching line and exit.
- # GNU tail 2.82 and newer terminates by itself when the pipe breaks.
- # To support other tails we do an explicit `kill`.
- # FIFO is useful here because otherwise it's difficult to determine
- # which tail process should be killed. We could stick them in a process
- # group by enabling job control, but this results in weird behavior when
- # running tests in parallel on some interactive shells
- # (e.g. when /bin/sh is symlinked to dash).
- fifo=$(mktemp)
- rm -f "$fifo"
- mkfifo "$fifo"
-
- # This weird thing is required to support old versions of ksh on NetBSD 8.2 and the like.
- (tail -n +"$line" -f "$script_log" >"$fifo") &
-
- new_line=$(
- sh -c "
- grep -n -m $count '^$script,' <'$fifo'
- " | awk -F: 'END { print $1 }'
- )
-
- # Try to stop the background tail, ignoring possible failure (some tails
- # detect EOF, some don't, so it may have already exited), but do wait on
- # it (which is required at least by old ksh).
- kill $! || true
- wait || true
- rm -f "$fifo"
-
- # Remember the next line number for future reference. We'll use it if
- # wait_script is called again with same $peer and $script.
- read -r "${line_var?}" <<EOF
-$((line + new_line))
-EOF
-}
-
-# Cleanup after running each script.
-cleanup() {
- (
- set +ex
-
- if command -v cleanup_hook 2>/dev/null; then
- echo >&2 "Cleanup hook found, calling..."
- cleanup_hook
- fi
-
- stop_all_tincs
- ) || true
-}
-
-# If we're on a CI server, the test requires superuser privileges to run, and we're not
-# currently a superuser, try running the test as one and fail if it doesn't work (the
-# system must be configured to provide passwordless sudo for our user).
-require_root() {
- if is_root; then
- return
- fi
- if is_ci; then
- echo "root is required for test $SCRIPTNAME, but we're a regular user; elevating privileges..."
- if ! command -v sudo 2>/dev/null; then
- bail "please install sudo and configure passwordless auth for user $USER"
- fi
- if ! sudo --preserve-env --non-interactive true; then
- bail "sudo is not allowed or requires a password for user $USER"
- fi
- exec sudo --preserve-env "$@"
- else
- # Avoid these kinds of surprises outside CI. Just skip the test.
- echo "root is required for test $SCRIPTNAME, but we're a regular user; skipping"
- exit "$EXIT_SKIP_TEST"
- fi
-}
-
-# Generate path to current shell which can be used from Windows applications.
-if is_windows; then
- MINGW_SHELL=$(normalize_path "$SHELL")
-fi
-
-# This was called from a tincd script. Skip executing commands with side effects.
-[ -n "$NAME" ] && return
-
-echo [STEP] Check for leftover tinc daemons and test directories
-
-# Cleanup leftovers from previous runs.
-stop_all_tincs
-
-rm -rf "$DIR_FOO" "$DIR_BAR" "$DIR_BAZ"
-
-# Register cleanup function so we don't have to call it everywhere
-# (and failed scripts do not leave stray tincd running).
-trap cleanup EXIT INT TERM
+++ /dev/null
-#!/bin/sh
-
-# shellcheck disable=SC1090
-. "$TESTLIB_PATH"
-
-echo [STEP] Initialize one node
-
-tinc foo init foo
-test "$(tinc foo get Name)" = "foo"
-
-echo [STEP] Test case sensitivity
-
-tinc foo set Mode switch
-test "$(tinc foo get Mode)" = "switch"
-test "$(tinc foo get mode)" = "switch"
-
-tinc foo set mode router
-test "$(tinc foo get Mode)" = "router"
-test "$(tinc foo get mode)" = "router"
-
-tinc foo set Mode Switch
-test "$(tinc foo get Mode)" = "Switch"
-
-echo [STEP] Test deletion
-
-expect_code "$EXIT_FAILURE" tinc foo del Mode hub
-tinc foo del Mode switch
-test -z "$(tinc foo get Mode)"
-
-echo [STEP] There can only be one Mode variable
-
-tinc foo add Mode switch
-tinc foo add Mode hub
-test "$(tinc foo get Mode)" = "hub"
-
-echo [STEP] Test addition/deletion of multivalued variables
-
-tinc foo add Subnet 1.1.1.1
-tinc foo add Subnet 2.2.2.2
-tinc foo add Subnet 2.2.2.2
-tinc foo add Subnet 3.3.3.3
-test "$(tinc foo get Subnet | rm_cr)" = "1.1.1.1
-2.2.2.2
-3.3.3.3"
-
-tinc foo del Subnet 2.2.2.2
-test "$(tinc foo get Subnet | rm_cr)" = "1.1.1.1
-3.3.3.3"
-
-tinc foo del Subnet
-test -z "$(tinc foo get Subnet)"
-
-echo [STEP] We should not be able to get/set server variables using node.variable syntax
-
-test -z "$(tinc foo get foo.Name)"
-expect_code "$EXIT_FAILURE" tinc foo set foo.Name bar
-
-echo [STEP] Test getting/setting host variables for other nodes
-
-touch "$DIR_FOO/hosts/bar"
-
-tinc foo add bar.PMTU 1
-tinc foo add bar.PMTU 2
-test "$(tinc foo get bar.PMTU)" = "2"
-
-tinc foo add bar.Subnet 1.1.1.1
-tinc foo add bar.Subnet 2.2.2.2
-tinc foo add bar.Subnet 2.2.2.2
-tinc foo add bar.Subnet 3.3.3.3
-test "$(tinc foo get bar.Subnet | rm_cr)" = "1.1.1.1
-2.2.2.2
-3.3.3.3"
-
-tinc foo del bar.Subnet 2.2.2.2
-test "$(tinc foo get bar.Subnet | rm_cr)" = "1.1.1.1
-3.3.3.3"
-
-tinc foo del bar.Subnet
-test -z "$(tinc foo get bar.Subnet)"
-
-echo [STEP] We should not be able to get/set for nodes with invalid names
-
-touch "$DIR_FOO/hosts/qu-ux"
-expect_code "$EXIT_FAILURE" tinc foo set qu-ux.Subnet 1.1.1.1
-
-echo [STEP] We should not be able to set obsolete variables unless forced
-
-expect_code "$EXIT_FAILURE" tinc foo set PrivateKey 12345
-tinc foo --force set PrivateKey 12345
-test "$(tinc foo get PrivateKey)" = "12345"
-
-tinc foo del PrivateKey
-test -z "$(tinc foo get PrivateKey)"
-
-echo [STEP] We should not be able to set/add malformed Subnets
-
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1.1.1
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:::6
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:6:7:8:9
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 256.256.256.256
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:6:7:8.123
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1:2:3:4:5:6:7:1.2.3.4
-expect_code "$EXIT_FAILURE" tinc foo add Subnet a:b:c:d:e:f:g:h
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1.1.1.1/0
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1.1.1.1/-1
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1.1.1.1/33
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1::/0
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1::/-1
-expect_code "$EXIT_FAILURE" tinc foo add Subnet 1::/129
-expect_code "$EXIT_FAILURE" tinc foo add Subnet ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-test -z "$(tinc foo get Subnet)"