]> tinc-vpn.org Git - tinc/commitdiff
Move integration tests into a subdirectory
authorKirill Isakov <bootctl@gmail.com>
Wed, 23 Mar 2022 06:49:09 +0000 (12:49 +0600)
committerKirill Isakov <bootctl@gmail.com>
Wed, 23 Mar 2022 11:32:18 +0000 (17:32 +0600)
38 files changed:
test/algorithms.test [deleted file]
test/basic.test [deleted file]
test/command-fsck.test [deleted file]
test/commandline.test [deleted file]
test/compression.test [deleted file]
test/executables.test [deleted file]
test/import-export.test [deleted file]
test/integration/algorithms.test [new file with mode: 0755]
test/integration/basic.test [new file with mode: 0755]
test/integration/command-fsck.test [new file with mode: 0755]
test/integration/commandline.test [new file with mode: 0755]
test/integration/compression.test [new file with mode: 0755]
test/integration/executables.test [new file with mode: 0755]
test/integration/import-export.test [new file with mode: 0755]
test/integration/invite-join.test [new file with mode: 0755]
test/integration/invite-offline.test [new file with mode: 0755]
test/integration/invite-tinc-up.test [new file with mode: 0755]
test/integration/legacy-protocol.test [new file with mode: 0755]
test/integration/meson.build [new file with mode: 0644]
test/integration/ns-ping.test [new file with mode: 0755]
test/integration/scripts.test [new file with mode: 0755]
test/integration/security.test [new file with mode: 0755]
test/integration/splice.c [new file with mode: 0644]
test/integration/sptps-basic.test [new file with mode: 0755]
test/integration/testlib.sh [new file with mode: 0644]
test/integration/variables.test [new file with mode: 0755]
test/invite-join.test [deleted file]
test/invite-offline.test [deleted file]
test/invite-tinc-up.test [deleted file]
test/legacy-protocol.test [deleted file]
test/meson.build
test/ns-ping.test [deleted file]
test/scripts.test [deleted file]
test/security.test [deleted file]
test/splice.c [deleted file]
test/sptps-basic.test [deleted file]
test/testlib.sh [deleted file]
test/variables.test [deleted file]

diff --git a/test/algorithms.test b/test/algorithms.test
deleted file mode 100755 (executable)
index 9dd722c..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/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
diff --git a/test/basic.test b/test/basic.test
deleted file mode 100755 (executable)
index 31f2755..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/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
diff --git a/test/command-fsck.test b/test/command-fsck.test
deleted file mode 100755 (executable)
index daf9291..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-#!/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
diff --git a/test/commandline.test b/test/commandline.test
deleted file mode 100755 (executable)
index 800dbfc..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/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'
diff --git a/test/compression.test b/test/compression.test
deleted file mode 100755 (executable)
index ac92541..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/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
diff --git a/test/executables.test b/test/executables.test
deleted file mode 100755 (executable)
index 390d2c7..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/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
diff --git a/test/import-export.test b/test/import-export.test
deleted file mode 100755 (executable)
index 6fe303f..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/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
diff --git a/test/integration/algorithms.test b/test/integration/algorithms.test
new file mode 100755 (executable)
index 0000000..9dd722c
--- /dev/null
@@ -0,0 +1,56 @@
+#!/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
diff --git a/test/integration/basic.test b/test/integration/basic.test
new file mode 100755 (executable)
index 0000000..31f2755
--- /dev/null
@@ -0,0 +1,26 @@
+#!/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
diff --git a/test/integration/command-fsck.test b/test/integration/command-fsck.test
new file mode 100755 (executable)
index 0000000..daf9291
--- /dev/null
@@ -0,0 +1,291 @@
+#!/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
diff --git a/test/integration/commandline.test b/test/integration/commandline.test
new file mode 100755 (executable)
index 0000000..800dbfc
--- /dev/null
@@ -0,0 +1,65 @@
+#!/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'
diff --git a/test/integration/compression.test b/test/integration/compression.test
new file mode 100755 (executable)
index 0000000..ac92541
--- /dev/null
@@ -0,0 +1,134 @@
+#!/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
diff --git a/test/integration/executables.test b/test/integration/executables.test
new file mode 100755 (executable)
index 0000000..390d2c7
--- /dev/null
@@ -0,0 +1,14 @@
+#!/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
diff --git a/test/integration/import-export.test b/test/integration/import-export.test
new file mode 100755 (executable)
index 0000000..6fe303f
--- /dev/null
@@ -0,0 +1,67 @@
+#!/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
diff --git a/test/integration/invite-join.test b/test/integration/invite-join.test
new file mode 100755 (executable)
index 0000000..be2af2b
--- /dev/null
@@ -0,0 +1,51 @@
+#!/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
diff --git a/test/integration/invite-offline.test b/test/integration/invite-offline.test
new file mode 100755 (executable)
index 0000000..8e53513
--- /dev/null
@@ -0,0 +1,51 @@
+#!/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
diff --git a/test/integration/invite-tinc-up.test b/test/integration/invite-tinc-up.test
new file mode 100755 (executable)
index 0000000..14d5a60
--- /dev/null
@@ -0,0 +1,53 @@
+#!/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"
diff --git a/test/integration/legacy-protocol.test b/test/integration/legacy-protocol.test
new file mode 100755 (executable)
index 0000000..9c44f79
--- /dev/null
@@ -0,0 +1,90 @@
+#!/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
diff --git a/test/integration/meson.build b/test/integration/meson.build
new file mode 100644 (file)
index 0000000..1c3b9ea
--- /dev/null
@@ -0,0 +1,64 @@
+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
+
diff --git a/test/integration/ns-ping.test b/test/integration/ns-ping.test
new file mode 100755 (executable)
index 0000000..33e7270
--- /dev/null
@@ -0,0 +1,80 @@
+#!/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
diff --git a/test/integration/scripts.test b/test/integration/scripts.test
new file mode 100755 (executable)
index 0000000..ff0e565
--- /dev/null
@@ -0,0 +1,133 @@
+#!/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"
diff --git a/test/integration/security.test b/test/integration/security.test
new file mode 100755 (executable)
index 0000000..fb783b0
--- /dev/null
@@ -0,0 +1,129 @@
+#!/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
diff --git a/test/integration/splice.c b/test/integration/splice.c
new file mode 100644 (file)
index 0000000..07c3503
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+    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;
+}
diff --git a/test/integration/sptps-basic.test b/test/integration/sptps-basic.test
new file mode 100755 (executable)
index 0000000..0eb285b
--- /dev/null
@@ -0,0 +1,45 @@
+#!/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"
diff --git a/test/integration/testlib.sh b/test/integration/testlib.sh
new file mode 100644 (file)
index 0000000..3d0f990
--- /dev/null
@@ -0,0 +1,436 @@
+#!/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
diff --git a/test/integration/variables.test b/test/integration/variables.test
new file mode 100755 (executable)
index 0000000..fffbd8c
--- /dev/null
@@ -0,0 +1,112 @@
+#!/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)"
diff --git a/test/invite-join.test b/test/invite-join.test
deleted file mode 100755 (executable)
index be2af2b..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/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
diff --git a/test/invite-offline.test b/test/invite-offline.test
deleted file mode 100755 (executable)
index 8e53513..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/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
diff --git a/test/invite-tinc-up.test b/test/invite-tinc-up.test
deleted file mode 100755 (executable)
index 14d5a60..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/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"
diff --git a/test/legacy-protocol.test b/test/legacy-protocol.test
deleted file mode 100755 (executable)
index 9c44f79..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/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
index 4b12f2e9b308d5980b6e2235249585298d07c6eb..db719a2f1c737d486c45fd2aea0d945b8ddbcfb0 100644 (file)
@@ -1,63 +1,2 @@
-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')
 
diff --git a/test/ns-ping.test b/test/ns-ping.test
deleted file mode 100755 (executable)
index 33e7270..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/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
diff --git a/test/scripts.test b/test/scripts.test
deleted file mode 100755 (executable)
index ff0e565..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/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"
diff --git a/test/security.test b/test/security.test
deleted file mode 100755 (executable)
index fb783b0..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/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
diff --git a/test/splice.c b/test/splice.c
deleted file mode 100644 (file)
index 118d011..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
-    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;
-}
diff --git a/test/sptps-basic.test b/test/sptps-basic.test
deleted file mode 100755 (executable)
index 0eb285b..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/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"
diff --git a/test/testlib.sh b/test/testlib.sh
deleted file mode 100644 (file)
index 3d0f990..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
-#!/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
diff --git a/test/variables.test b/test/variables.test
deleted file mode 100755 (executable)
index fffbd8c..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/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)"