From afa21334b484e946b11ea4415783a93d6e3be22b Mon Sep 17 00:00:00 2001 From: Rui Li Date: Tue, 10 Mar 2026 17:49:10 -0700 Subject: [PATCH] tests: Add tests for originalhost/host separation and Match support Signed-off-by: Rui Li Reviewed-by: Jakub Jelen --- tests/unittests/torture_config.c | 260 ++++++++++++++++++++++++++---- tests/unittests/torture_options.c | 158 +++++++++++++++++- 2 files changed, 386 insertions(+), 32 deletions(-) diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c index 89687872..56b1b814 100644 --- a/tests/unittests/torture_config.c +++ b/tests/unittests/torture_config.c @@ -130,23 +130,23 @@ extern LIBSSH_THREAD int ssh_log_level; "ProxyJump = many-spaces.com\n" /* valid */ /* Match keyword */ -#define LIBSSH_TESTCONFIG_STRING10 \ - "Match host example\n" \ - "\tHostName example.com\n" \ - "Match host example1,example2\n" \ - "\tHostName exampleN\n" \ - "Match user guest\n" \ - "\tHostName guest.com\n" \ - "Match user tester host testhost\n" \ - "\tHostName testhost.com\n" \ +#define LIBSSH_TESTCONFIG_STRING10 \ + "Match host example\n" \ + "\tHostName example.com\n" \ + "Match host example1,example2\n" \ + "\tHostName exampleN\n" \ + "Match user guest\n" \ + "\tHostName guest.com\n" \ + "Match user tester host testhost\n" \ + "\tHostName testhost.com\n" \ "Match !user tester host testhost\n" \ - "\tHostName nonuser-testhost.com\n" \ - "Match all\n" \ - "\tHostName all-matched.com\n" \ - /* Unsupported options */ \ - "Match originalhost example\n" \ - "\tHostName original-example.com\n" \ - "Match localuser guest\n" \ + "\tHostName nonuser-testhost.com\n" \ + "Match all\n" \ + "\tHostName all-matched.com\n" \ + "Match originalhost example\n" \ + "\tHostName original-example.com\n" \ + "\tUser originaluser\n" \ + "Match localuser guest\n" \ "\tHostName local-guest.com\n" /* ProxyJump */ @@ -851,26 +851,40 @@ static void torture_config_match(void **state, ssh_options_set(session, SSH_OPTIONS_HOST, "unmatched"); _parse_config(session, file, string, SSH_OK); assert_string_equal(session->opts.host, "all-matched.com"); + assert_string_equal(session->opts.originalhost, "unmatched"); /* Hostname example does simple hostname matching */ torture_reset_config(session); ssh_options_set(session, SSH_OPTIONS_HOST, "example"); _parse_config(session, file, string, SSH_OK); assert_string_equal(session->opts.host, "example.com"); + assert_string_equal(session->opts.originalhost, "example"); /* We can match also both hosts from a comma separated list */ torture_reset_config(session); ssh_options_set(session, SSH_OPTIONS_HOST, "example1"); _parse_config(session, file, string, SSH_OK); assert_string_equal(session->opts.host, "exampleN"); + assert_string_equal(session->opts.originalhost, "example1"); torture_reset_config(session); ssh_options_set(session, SSH_OPTIONS_HOST, "example2"); _parse_config(session, file, string, SSH_OK); assert_string_equal(session->opts.host, "exampleN"); + assert_string_equal(session->opts.originalhost, "example2"); - /* We can match by user */ + /* We can match by originalhost */ torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "example"); + _parse_config(session, file, string, SSH_OK); + assert_string_equal(session->opts.host, "example.com"); + assert_string_equal(session->opts.originalhost, "example"); + /* Match originalhost sets User */ + assert_string_equal(session->opts.username, "originaluser"); + + /* We can match by user - clear originalhost to isolate user match */ + torture_reset_config(session); + SAFE_FREE(session->opts.originalhost); ssh_options_set(session, SSH_OPTIONS_USER, "guest"); _parse_config(session, file, string, SSH_OK); assert_string_equal(session->opts.host, "guest.com"); @@ -881,6 +895,7 @@ static void torture_config_match(void **state, ssh_options_set(session, SSH_OPTIONS_HOST, "testhost"); _parse_config(session, file, string, SSH_OK); assert_string_equal(session->opts.host, "testhost.com"); + assert_string_equal(session->opts.originalhost, "testhost"); /* We can also negate conditions */ torture_reset_config(session); @@ -888,9 +903,43 @@ static void torture_config_match(void **state, ssh_options_set(session, SSH_OPTIONS_HOST, "testhost"); _parse_config(session, file, string, SSH_OK); assert_string_equal(session->opts.host, "nonuser-testhost.com"); + assert_string_equal(session->opts.originalhost, "testhost"); /* In this part, we try various other config files and strings. */ + /* Match host compares against resolved hostname */ + config = "Host ssh-host\n" + "\tHostname 10.1.1.1\n" + "Match host 10.1.1.*\n" + "\tPort 2222\n"; + if (file != NULL) { + torture_write_file(file, config); + } else { + string = config; + } + torture_reset_config(session); + session->opts.port = 0; + ssh_options_set(session, SSH_OPTIONS_HOST, "ssh-host"); + _parse_config(session, file, string, SSH_OK); + assert_string_equal(session->opts.host, "10.1.1.1"); + assert_string_equal(session->opts.originalhost, "ssh-host"); + assert_int_equal(session->opts.port, 2222); + + /* Match host falls back to originalhost when host is NULL */ + config = "Match host my_alias\n" + "\tHostName alias-matched.com\n"; + if (file != NULL) { + torture_write_file(file, config); + } else { + string = config; + } + torture_reset_config(session); + SAFE_FREE(session->opts.username); + ssh_options_set(session, SSH_OPTIONS_HOST, "my_alias"); + assert_null(session->opts.host); + _parse_config(session, file, string, SSH_OK); + assert_string_equal(session->opts.host, "alias-matched.com"); + /* Match final is not completely supported, but should do quite much the * same as "match all". The trailing "all" is not mandatory. */ config = "Match final all\n" @@ -1018,7 +1067,7 @@ static void torture_config_match(void **state, _parse_config(session, file, string, SSH_OK); assert_string_equal(session->opts.host, "unmatched"); - /* Missing argument to unsupported option originalhost */ + /* Missing argument to option originalhost */ config = "Match originalhost\n" "\tHost originalhost.com\n"; if (file != NULL) { @@ -1289,7 +1338,6 @@ static void torture_config_proxyjump(void **state, assert_string_equal(session->opts.ProxyCommand, "ssh -W '[%h]:%p' 2620:52:0::fed"); - /* Multiple @ is allowed in second jump */ config = "Host allowed-hostname\n" "\tProxyJump localhost,user@principal.com@jumpbox:22\n"; @@ -1351,7 +1399,73 @@ static void torture_config_proxyjump(void **state, "jumpbox", "user@principal.com", "22"); + /* Non-RFC-1035 alias (underscore) — accepted with non-strict parse */ + config = "Host alias-jump\n" + "\tProxyJump my_alias\n"; + if (file != NULL) { + torture_write_file(file, config); + } else { + string = config; + } torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "alias-jump"); + _parse_config(session, file, string, SSH_OK); + helper_proxy_jump_check(session->opts.proxy_jumps->root, + "my_alias", + NULL, + NULL); + + /* Non-RFC-1035 alias in multi-hop second jump */ + config = "Host alias-multi\n" + "\tProxyJump localhost,my_alias:2222\n"; + if (file != NULL) { + torture_write_file(file, config); + } else { + string = config; + } + torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "alias-multi"); + _parse_config(session, file, string, SSH_OK); + helper_proxy_jump_check(session->opts.proxy_jumps->root, + "my_alias", + NULL, + "2222"); + helper_proxy_jump_check(session->opts.proxy_jumps->root->next, + "localhost", + NULL, + NULL); + + /* Non-RFC-1035 alias — proxycommand based */ + torture_setenv("OPENSSH_PROXYJUMP", "1"); + + config = "Host alias-jump\n" + "\tProxyJump my_alias\n"; + if (file != NULL) { + torture_write_file(file, config); + } else { + string = config; + } + torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "alias-jump"); + _parse_config(session, file, string, SSH_OK); + assert_string_equal(session->opts.ProxyCommand, + "ssh -W '[%h]:%p' my_alias"); + + /* Non-RFC-1035 alias in multi-hop — proxycommand based */ + config = "Host alias-multi\n" + "\tProxyJump localhost,my_alias:2222\n"; + if (file != NULL) { + torture_write_file(file, config); + } else { + string = config; + } + torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "alias-multi"); + _parse_config(session, file, string, SSH_OK); + assert_string_equal(session->opts.ProxyCommand, + "ssh -J my_alias:2222 -W '[%h]:%p' localhost"); + + torture_unsetenv("OPENSSH_PROXYJUMP"); /* In this part, we try various other config files and strings. */ torture_setenv("OPENSSH_PROXYJUMP", "1"); @@ -2762,21 +2876,36 @@ static void torture_config_parse_uri(void **state) (void)state; /* unused */ - rc = ssh_config_parse_uri("localhost", &username, &hostname, &port, false, true); + rc = ssh_config_parse_uri("localhost", + &username, + &hostname, + &port, + false, + true); assert_return_code(rc, errno); assert_null(username); assert_string_equal(hostname, "localhost"); SAFE_FREE(hostname); assert_null(port); - rc = ssh_config_parse_uri("1.2.3.4", &username, &hostname, &port, false, true); + rc = ssh_config_parse_uri("1.2.3.4", + &username, + &hostname, + &port, + false, + true); assert_return_code(rc, errno); assert_null(username); assert_string_equal(hostname, "1.2.3.4"); SAFE_FREE(hostname); assert_null(port); - rc = ssh_config_parse_uri("1.2.3.4:2222", &username, &hostname, &port, false, true); + rc = ssh_config_parse_uri("1.2.3.4:2222", + &username, + &hostname, + &port, + false, + true); assert_return_code(rc, errno); assert_null(username); assert_string_equal(hostname, "1.2.3.4"); @@ -2784,7 +2913,12 @@ static void torture_config_parse_uri(void **state) assert_string_equal(port, "2222"); SAFE_FREE(port); - rc = ssh_config_parse_uri("[1:2:3::4]:2222", &username, &hostname, &port, false, true); + rc = ssh_config_parse_uri("[1:2:3::4]:2222", + &username, + &hostname, + &port, + false, + true); assert_return_code(rc, errno); assert_null(username); assert_string_equal(hostname, "1:2:3::4"); @@ -2793,7 +2927,12 @@ static void torture_config_parse_uri(void **state) SAFE_FREE(port); /* do not want port */ - rc = ssh_config_parse_uri("1:2:3::4", &username, &hostname, NULL, true, true); + rc = ssh_config_parse_uri("1:2:3::4", + &username, + &hostname, + NULL, + true, + true); assert_return_code(rc, errno); assert_null(username); assert_string_equal(hostname, "1:2:3::4"); @@ -2803,13 +2942,23 @@ static void torture_config_parse_uri(void **state) assert_int_equal(rc, SSH_ERROR); /* Non-strict accepts non-RFC1035 chars (e.g. _, %) */ - rc = ssh_config_parse_uri("customer_1", &username, &hostname, NULL, true, false); + rc = ssh_config_parse_uri("customer_1", + &username, + &hostname, + NULL, + true, + false); assert_return_code(rc, errno); assert_null(username); assert_string_equal(hostname, "customer_1"); SAFE_FREE(hostname); - rc = ssh_config_parse_uri("admin@%prod", &username, &hostname, NULL, true, false); + rc = ssh_config_parse_uri("admin@%prod", + &username, + &hostname, + NULL, + true, + false); assert_return_code(rc, errno); assert_string_equal(username, "admin"); assert_string_equal(hostname, "%prod"); @@ -2817,11 +2966,21 @@ static void torture_config_parse_uri(void **state) SAFE_FREE(hostname); /* Strict rejects what non-strict accepts */ - rc = ssh_config_parse_uri("customer_1", &username, &hostname, NULL, true, true); + rc = ssh_config_parse_uri("customer_1", + &username, + &hostname, + NULL, + true, + true); assert_int_equal(rc, SSH_ERROR); /* Non-strict rejects shell metacharacters */ - rc = ssh_config_parse_uri("host;cmd", &username, &hostname, NULL, true, false); + rc = ssh_config_parse_uri("host;cmd", + &username, + &hostname, + NULL, + true, + false); assert_int_equal(rc, SSH_ERROR); /* Non-strict rejects leading dash */ @@ -2959,6 +3118,48 @@ static void torture_config_jump(void **state) printf("%s: EOF\n", __func__); } +/* Verify Hostname directive resolves host without overwriting originalhost + */ +static void torture_config_hostname(void **state) +{ + ssh_session session = *state; + char *expanded = NULL; + + /* Hostname directive sets host, originalhost is unchanged */ + torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "my_alias"); + assert_null(session->opts.host); + assert_string_equal(session->opts.originalhost, "my_alias"); + _parse_config(session, + NULL, + "Host my_alias\n\tHostname 192.168.1.1\n", + SSH_OK); + assert_string_equal(session->opts.host, "192.168.1.1"); + assert_string_equal(session->opts.originalhost, "my_alias"); + + /* Host keyword compares against originalhost, not the resolved IP */ + torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "ssh-host"); + _parse_config(session, + NULL, + "Host ssh-host\n\tHostname 10.1.1.1\n" + "Host 10.1.1.*\n\tProxyJump ssh-host\n", + SSH_OK); + assert_string_equal(session->opts.host, "10.1.1.1"); + assert_string_equal(session->opts.originalhost, "ssh-host"); + assert_int_equal(ssh_list_count(session->opts.proxy_jumps), 0); + assert_null(session->opts.ProxyCommand); + + /* %h falls back to originalhost when host is not yet resolved */ + torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "my_alias"); + assert_null(session->opts.host); + expanded = ssh_path_expand_escape(session, "%h"); + assert_non_null(expanded); + assert_string_equal(expanded, "my_alias"); + free(expanded); +} + /* Invalid configuration files */ static void torture_config_invalid(void **state) @@ -3127,7 +3328,8 @@ int torture_run_tests(void) cmocka_unit_test_setup_teardown(torture_config_loglevel_missing_value, setup, teardown), - cmocka_unit_test_setup_teardown(torture_config_jump, + cmocka_unit_test_setup_teardown(torture_config_jump, setup, teardown), + cmocka_unit_test_setup_teardown(torture_config_hostname, setup, teardown), cmocka_unit_test_setup_teardown(torture_config_invalid, diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c index 796a125d..da2d4630 100644 --- a/tests/unittests/torture_options.c +++ b/tests/unittests/torture_options.c @@ -17,6 +17,13 @@ #include #include #include + +#ifdef _WIN32 +#include +#else +#include +#endif + #ifdef WITH_SERVER #include #define LIBSSH_CUSTOM_BIND_CONFIG_FILE "my_bind_config" @@ -59,12 +66,16 @@ static void torture_options_set_host(void **state) { assert_true(rc == 0); assert_non_null(session->opts.host); assert_string_equal(session->opts.host, "localhost"); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "localhost"); /* IPv4 address */ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "127.1.1.1"); assert_true(rc == 0); assert_non_null(session->opts.host); assert_string_equal(session->opts.host, "127.1.1.1"); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "127.1.1.1"); assert_null(session->opts.username); /* IPv6 address */ @@ -72,12 +83,16 @@ static void torture_options_set_host(void **state) { assert_true(rc == 0); assert_non_null(session->opts.host); assert_string_equal(session->opts.host, "::1"); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "::1"); assert_null(session->opts.username); rc = ssh_options_set(session, SSH_OPTIONS_HOST, "guru@meditation"); assert_true(rc == 0); assert_non_null(session->opts.host); assert_string_equal(session->opts.host, "meditation"); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "meditation"); assert_non_null(session->opts.username); assert_string_equal(session->opts.username, "guru"); @@ -86,6 +101,8 @@ static void torture_options_set_host(void **state) { assert_true(rc == 0); assert_non_null(session->opts.host); assert_string_equal(session->opts.host, "hostname"); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "hostname"); assert_non_null(session->opts.username); assert_string_equal(session->opts.username, "at@login"); @@ -104,6 +121,9 @@ static void torture_options_set_host(void **state) { assert_non_null(session->opts.host); assert_string_equal(session->opts.host, "fd4d:5449:7400:111:626d:3cff:fedf:4d39"); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, + "fd4d:5449:7400:111:626d:3cff:fedf:4d39"); assert_null(session->opts.username); /* IPv6 hostnames should work also with square braces */ @@ -114,20 +134,103 @@ static void torture_options_set_host(void **state) { assert_non_null(session->opts.host); assert_string_equal(session->opts.host, "fd4d:5449:7400:111:626d:3cff:fedf:4d39"); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, + "fd4d:5449:7400:111:626d:3cff:fedf:4d39"); assert_null(session->opts.username); + /* user@IPv6%interface + * Use dynamic interface name for cross-platform portability */ + { + char interf[IF_NAMESIZE] = {0}; + char ipv6_zone[128] = {0}; + char expected_host[128] = {0}; + + if_indextoname(1, interf); + assert_non_null(interf); + snprintf(ipv6_zone, sizeof(ipv6_zone), "user@fe80::1%%%s", interf); + snprintf(expected_host, sizeof(expected_host), "fe80::1%%%s", interf); + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, ipv6_zone); + assert_return_code(rc, errno); + assert_non_null(session->opts.host); + assert_string_equal(session->opts.host, expected_host); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, expected_host); + assert_string_equal(session->opts.username, "user"); + } + /* IDN need to be in punycode format */ + SAFE_FREE(session->opts.username); rc = ssh_options_set(session, SSH_OPTIONS_HOST, "xn--bcher-kva.tld"); assert_return_code(rc, errno); assert_non_null(session->opts.host); assert_string_equal(session->opts.host, "xn--bcher-kva.tld"); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "xn--bcher-kva.tld"); assert_null(session->opts.username); - /* IDN in UTF8 won't work */ + /* IDN in UTF-8 is accepted but not as a valid hostname, + * only originalhost is set */ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "bücher.tld"); - assert_string_equal(ssh_get_error(session), - "Invalid argument in ssh_options_set"); + assert_return_code(rc, errno); + assert_null(session->opts.host); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "bücher.tld"); + + /* Config alias '%' rejected by RFC1035, only originalhost is set */ + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "%customer1"); + assert_return_code(rc, errno); + assert_null(session->opts.host); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "%customer1"); + + /* user@alias '_' rejected by RFC1035, alias stored in originalhost */ + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "admin@customer_1"); + assert_return_code(rc, errno); + assert_null(session->opts.host); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "customer_1"); + assert_string_equal(session->opts.username, "admin"); + + /* Shell metacharacters and leading dash rejected. + * Verify failure cases do not update session options. */ + SAFE_FREE(session->opts.username); + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "host;rm -rf /"); assert_ssh_return_code_equal(session, rc, SSH_ERROR); + assert_null(session->opts.host); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "customer_1"); + assert_null(session->opts.username); + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "-leading-dash"); + assert_ssh_return_code_equal(session, rc, SSH_ERROR); + assert_null(session->opts.host); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "customer_1"); + assert_null(session->opts.username); + + /* Empty user or host rejected */ + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "@hostname"); + assert_ssh_return_code_equal(session, rc, SSH_ERROR); + assert_null(session->opts.host); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "customer_1"); + assert_null(session->opts.username); + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "user@"); + assert_ssh_return_code_equal(session, rc, SSH_ERROR); + assert_null(session->opts.host); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "customer_1"); + assert_null(session->opts.username); + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, ""); + assert_ssh_return_code_equal(session, rc, SSH_ERROR); + assert_null(session->opts.host); + assert_non_null(session->opts.originalhost); + assert_string_equal(session->opts.originalhost, "customer_1"); + assert_null(session->opts.username); } static void torture_options_set_ciphers(void **state) @@ -714,6 +817,26 @@ static void torture_options_get_host(void **state) assert_string_equal(host, "localhost"); ssh_string_free_char(host); + + /* When host is not yet resolved, falls back to originalhost */ + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "my_alias"); + assert_true(rc == 0); + assert_null(session->opts.host); + assert_non_null(session->opts.originalhost); + + assert_false(ssh_options_get(session, SSH_OPTIONS_HOST, &host)); + assert_string_equal(host, "my_alias"); + ssh_string_free_char(host); + + /* After config resolution, get returns resolved host, not originalhost */ + session->opts.host = strdup("192.168.1.1"); + assert_non_null(session->opts.host); + + assert_false(ssh_options_get(session, SSH_OPTIONS_HOST, &host)); + assert_string_equal(host, "192.168.1.1"); + ssh_string_free_char(host); + /* originalhost is unchanged */ + assert_string_equal(session->opts.originalhost, "my_alias"); } static void torture_options_set_port(void **state) @@ -1074,6 +1197,7 @@ static void torture_options_config_host(void **state) { ssh_session session = *state; FILE *config = NULL; + int rv; /* create a new config file */ config = fopen("test_config", "w"); @@ -1113,6 +1237,33 @@ static void torture_options_config_host(void **state) ssh_options_parse_config(session, "test_config"); assert_int_equal(session->opts.port, 44); + /* ssh_options_parse_config rejects when originalhost is NULL */ + SAFE_FREE(session->opts.host); + SAFE_FREE(session->opts.originalhost); + rv = ssh_options_parse_config(session, "test_config"); + assert_int_equal(rv, -1); + + /* Config Hostname with invalid hostname: verify stale host not leaked */ + torture_write_file("test_config", "Host 192.168.1.1\nHostname my_alias\n"); + + torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "192.168.1.1"); + assert_string_equal(session->opts.host, "192.168.1.1"); + assert_string_equal(session->opts.originalhost, "192.168.1.1"); + rv = ssh_options_parse_config(session, "test_config"); + assert_int_equal(rv, 0); + assert_null(session->opts.host); + assert_string_equal(session->opts.originalhost, "192.168.1.1"); + + /* Calling ssh_options_set(HOST) twice: verify stale host not leaked */ + ssh_options_set(session, SSH_OPTIONS_HOST, "real.server.com"); + assert_string_equal(session->opts.host, "real.server.com"); + assert_string_equal(session->opts.originalhost, "real.server.com"); + + ssh_options_set(session, SSH_OPTIONS_HOST, "my_alias"); + assert_null(session->opts.host); + assert_string_equal(session->opts.originalhost, "my_alias"); + unlink("test_config"); } @@ -1437,6 +1588,7 @@ static void torture_options_copy(void **state) assert_string_equal(session->opts.username, new->opts.username); assert_string_equal(session->opts.host, new->opts.host); + assert_string_equal(session->opts.originalhost, new->opts.originalhost); assert_string_equal(session->opts.bindaddr, new->opts.bindaddr); assert_string_equal(session->opts.sshdir, new->opts.sshdir); assert_string_equal(session->opts.knownhosts, new->opts.knownhosts);