From: Kirill Isakov Date: Wed, 23 Mar 2022 06:49:09 +0000 (+0600) Subject: Move integration tests into a subdirectory X-Git-Url: https://www.tinc-vpn.org/git/?a=commitdiff_plain;h=94520716cb6ca59fc83c0c39021a085b42cda54b;p=tinc Move integration tests into a subdirectory --- diff --git a/test/algorithms.test b/test/algorithms.test deleted file mode 100755 index 9dd722c9..00000000 --- a/test/algorithms.test +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh - -# shellcheck disable=SC1090 -. "$TESTLIB_PATH" - -echo [STEP] Initialize two nodes - -tinc foo <&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" <>"$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 index 800dbfcf..00000000 --- a/test/commandline.test +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh - -# shellcheck disable=SC1090 -. "$TESTLIB_PATH" - -echo [STEP] Initialize one node - -tinc foo <&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 index 390d2c71..00000000 --- a/test/executables.test +++ /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 index 6fe303fd..00000000 --- a/test/import-export.test +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh - -# shellcheck disable=SC1090 -. "$TESTLIB_PATH" - -echo [STEP] Initialize three nodes - -tinc foo <&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" <>"$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 index 00000000..800dbfcf --- /dev/null +++ b/test/integration/commandline.test @@ -0,0 +1,65 @@ +#!/bin/sh + +# shellcheck disable=SC1090 +. "$TESTLIB_PATH" + +echo [STEP] Initialize one node + +tinc foo <&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 index 00000000..390d2c71 --- /dev/null +++ b/test/integration/executables.test @@ -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 index 00000000..6fe303fd --- /dev/null +++ b/test/integration/import-export.test @@ -0,0 +1,67 @@ +#!/bin/sh + +# shellcheck disable=SC1090 +. "$TESTLIB_PATH" + +echo [STEP] Initialize three nodes + +tinc foo <"$INVITATION_FILE" <>'$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 <>"$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" </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 < + + 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 index 00000000..0eb285b1 --- /dev/null +++ b/test/integration/sptps-basic.test @@ -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 index 00000000..3d0f990b --- /dev/null +++ b/test/integration/testlib.sh @@ -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" <"$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" <>"$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?}" </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 index 00000000..fffbd8ce --- /dev/null +++ b/test/integration/variables.test @@ -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 index be2af2be..00000000 --- a/test/invite-join.test +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh - -# shellcheck disable=SC1090 -. "$TESTLIB_PATH" - -echo [STEP] Initialize one node - -tinc foo <"$INVITATION_FILE" <>'$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 <>"$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" </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 < - - 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 index 0eb285b1..00000000 --- a/test/sptps-basic.test +++ /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 index 3d0f990b..00000000 --- a/test/testlib.sh +++ /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" <"$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" <>"$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?}" </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 index fffbd8ce..00000000 --- a/test/variables.test +++ /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)"