diff options
-rw-r--r-- | erts/doc/src/erl_driver.xml | 4 | ||||
-rw-r--r-- | erts/emulator/beam/beam_bif_load.c | 4 | ||||
-rw-r--r-- | erts/emulator/beam/beam_emu.c | 2 | ||||
-rw-r--r-- | erts/emulator/beam/beam_load.h | 3 | ||||
-rw-r--r-- | erts/emulator/beam/erl_fun.c | 1 | ||||
-rw-r--r-- | erts/emulator/hipe/hipe_bif0.c | 4 | ||||
-rw-r--r-- | erts/emulator/hipe/hipe_mode_switch.h | 6 | ||||
-rw-r--r-- | lib/compiler/src/sys_core_fold.erl | 8 | ||||
-rw-r--r-- | lib/compiler/test/guard_SUITE.erl | 13 | ||||
-rw-r--r-- | lib/eldap/doc/src/eldap.xml | 22 | ||||
-rw-r--r-- | lib/kernel/doc/src/file.xml | 8 | ||||
-rw-r--r-- | lib/mnesia/test/mnesia_test_lib.erl | 1 | ||||
-rw-r--r-- | lib/ssh/src/ssh_channel.erl | 2 | ||||
-rw-r--r-- | lib/ssh/src/ssh_dbg.erl | 7 | ||||
-rw-r--r-- | lib/ssh/test/ssh_basic_SUITE.erl | 33 | ||||
-rw-r--r-- | lib/ssh/test/ssh_options_SUITE.erl | 16 | ||||
-rw-r--r-- | lib/ssh/test/ssh_renegotiate_SUITE.erl | 40 | ||||
-rw-r--r-- | lib/ssh/test/ssh_test_lib.erl | 42 | ||||
-rw-r--r-- | lib/ssh/test/ssh_to_openssh_SUITE.erl | 96 |
19 files changed, 220 insertions, 92 deletions
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 5191742bc9..d8bf45c523 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -2039,7 +2039,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> <fsummary>Set and get limits for busy port message queue.</fsummary> <desc> <marker id="erl_drv_busy_msgq_limits"></marker> - <p>Sets and gets limits that will be used for controling the + <p>Sets and gets limits that will be used for controlling the busy state of the port message queue.</p> <p>The port message queue is set into a busy state when the amount of command data queued on the @@ -2112,7 +2112,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> It is used to identify the condition variable in planned future debug functionality.</p> <p>Returns <c>NULL</c> on failure. The driver - creating the condition variable is responsibile for + creating the condition variable is responsible for destroying it before the driver is unloaded.</p> <p>This function is thread-safe.</p> </desc> diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 5969197168..8af7703f51 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1623,10 +1623,10 @@ erts_purge_state_add_fun(ErlFunEntry *fe) } Export * -erts_suspend_process_on_pending_purge_lambda(Process *c_p) +erts_suspend_process_on_pending_purge_lambda(Process *c_p, ErlFunEntry* fe) { erts_smp_mtx_lock(&purge_state.mtx); - if (is_value(purge_state.module)) { + if (purge_state.module == fe->module) { /* * The process c_p is about to call a fun in the code * that we are trying to purge. Suspend it and call diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index ef4cdf9d5a..3be5c0d24c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6555,7 +6555,7 @@ call_fun(Process* p, /* Current process. */ * and let it try again when the purge operation is * done (may succeed or not). */ - ep = erts_suspend_process_on_pending_purge_lambda(p); + ep = erts_suspend_process_on_pending_purge_lambda(p, fe); ASSERT(ep); } else { diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index 1200bb9c6f..9be5e14e40 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -124,7 +124,8 @@ int erts_is_module_native(BeamCodeHeader* code); void erts_beam_bif_load_init(void); struct erl_fun_entry; void erts_purge_state_add_fun(struct erl_fun_entry *fe); -Export *erts_suspend_process_on_pending_purge_lambda(Process *c_p); +Export *erts_suspend_process_on_pending_purge_lambda(Process *c_p, + struct erl_fun_entry*); /* * Layout of the line table. diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c index c639ba623f..5258d83a18 100644 --- a/erts/emulator/beam/erl_fun.c +++ b/erts/emulator/beam/erl_fun.c @@ -236,7 +236,6 @@ erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no) ErlFunEntry *fe = funs[ix]; if (fe->address == unloaded_fun) fe->address = fe->pend_purge_address; - fe->pend_purge_address = NULL; } } diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 3336fded7a..dcb6c35bfa 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1476,6 +1476,10 @@ int hipe_find_mfa_from_ra(const void *ra, Eterm *m, Eterm *f, unsigned int *a) struct hipe_mfa_info **bucket; unsigned int i, nrbuckets; + if (hipe_is_ra_mode_switch(ra)) { + return 0; + } + /* Note about locking: the table is only updated from the loader, which runs with the rest of the system suspended. */ /* XXX: alas not true; see comment at hipe_mfa_info_table.lock */ diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index c40077d558..e54b81cf78 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -64,6 +64,7 @@ Eterm hipe_build_stacktrace(Process *p, struct StackTrace *s); ERTS_GLB_INLINE void hipe_reserve_beam_trap_frame(Process*, Eterm reg[], unsigned arity); ERTS_GLB_INLINE void hipe_unreserve_beam_trap_frame(Process*); +ERTS_GLB_INLINE int hipe_is_ra_mode_switch(const void* ra); extern Uint hipe_beam_pc_return[]; extern Uint hipe_beam_pc_throw[]; @@ -112,6 +113,11 @@ ERTS_GLB_INLINE void hipe_unreserve_beam_trap_frame(Process *p) p->stop += 2; } +ERTS_GLB_INLINE int hipe_is_ra_mode_switch(const void* ra) +{ + return ra == nbif_return; +} + #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ #endif /* ASM */ diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 5e1602cb5b..4922953407 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -1130,7 +1130,13 @@ clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) -> %% %% case A of NewVar when true -> ... %% - sub_set_var(Var, Cexpr, Sub2); + case cerl:is_c_fname(Cexpr) of + false -> + sub_set_var(Var, Cexpr, Sub2); + true -> + %% We must not copy funs, and especially not into guards. + Sub2 + end; _ -> Sub2 end, diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index 6302f82f29..429d6b79e0 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -87,6 +87,7 @@ misc(Config) when is_list(Config) -> {ok,buf,<<>>} = get_data({o,true,0}, 42, buf), {ok,buf,<<>>} = get_data({o,false,0}, 0, buf), error = get_data({o,false,0}, 42, buf), + ok. @@ -343,6 +344,11 @@ complex_semicolon(Config) when is_list(Config) -> ok = csemi7(#{a=>1}, 3, 3), ok = csemi7(#{a=>1, b=>3}, 0, 0), + %% 8: Make sure that funs cannot be copied into guards. + ok = csemi8(true), + error = csemi8(false), + error = csemi8(42), + ok. csemi1(Type, Val) when is_list(Val), Type == float; @@ -457,6 +463,13 @@ csemi6(_, _) -> error. csemi7(A, B, C) when A#{a:=B} > #{a=>1}; abs(C) > 2 -> ok; csemi7(_, _, _) -> error. +csemi8(Together) -> + case fun csemi8/1 of + Typically when Together; Typically, Together -> ok; + _ -> error + end. + + comma(Config) when is_list(Config) -> %% ',' combinations of literal true/false. diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml index 43873e44e2..f2c7889e58 100644 --- a/lib/eldap/doc/src/eldap.xml +++ b/lib/eldap/doc/src/eldap.xml @@ -197,7 +197,7 @@ </type> <desc> <p> Add an entry. The entry must not exist.</p> - <pre> + <code> add(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com", [{"objectclass", ["person"]}, @@ -205,7 +205,7 @@ {"sn", ["Valentine"]}, {"telephoneNumber", ["545 555 00"]}] ) - </pre> + </code> </desc> </func> <func> @@ -216,9 +216,9 @@ </type> <desc> <p> Delete an entry.</p> - <pre> + <code> delete(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com") - </pre> + </code> </desc> </func> @@ -259,11 +259,11 @@ </type> <desc> <p> Modify an entry.</p> - <pre> + <code> modify(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com", [eldap:mod_replace("telephoneNumber", ["555 555 00"]), eldap:mod_add("description", ["LDAP Hacker"]) ]) - </pre> + </code> </desc> </func> <func> @@ -320,10 +320,10 @@ whether the current RDN should be removed from the attribute list after the after operation. <c>NewSupDN</c> is the new parent that the RDN shall be moved to. If the old parent should remain as parent, <c>NewSupDN</c> shall be "".</p> - <pre> + <code> modify_dn(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com ", "cn=Bill Jr Valentine", true, "") - </pre> + </code> </desc> </func> <func> @@ -342,10 +342,10 @@ Default values: scope is <c>wholeSubtree()</c>, deref is <c>derefAlways()</c>, types_only is <c>false</c> and timeout is <c>0</c> (meaning infinity). </p> - <pre> + <code> Filter = eldap:substrings("cn", [{any,"V"}]), search(Handle, [{base, "dc=example, dc=com"}, {filter, Filter}, {attributes, ["cn"]}]), - </pre> + </code> <p>The <c>timeout</c> option in the <c>SearchOptions</c> is for the ldap server, while the timeout in <seealso marker="#open/2">eldap:open/2</seealso> is used for each individual request in the search operation. @@ -454,7 +454,7 @@ </type> <desc> <p>Creates an extensible match filter. For example, </p> <code> - eldap:extensibleMatch("Bar", [{type,"sn"}, {matchingRule,"caseExactMatch"}])) + eldap:extensibleMatch("Bar", [{type,"sn"}, {matchingRule,"caseExactMatch"}])) </code> <p>creates a filter which performs a <c>caseExactMatch</c> on the attribute <c>sn</c> and matches with the value <c>"Bar"</c>. The default value of <c>dnAttributes</c> is <c>false</c>.</p> </desc> </func> diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 09497482cf..b674b3ca93 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -1477,8 +1477,8 @@ f.txt: {person, "kalle", 25}. <tag><c>16#400</c></tag> <item><p>set group id on execution</p></item> </taglist> - <p>On Unix platforms, the following bits - can also be set:</p> + <p>On Unix platforms, other bits than those listed above + may be set.</p> </item> <tag><c>links = integer() >= 0</c></tag> <item> @@ -2042,8 +2042,8 @@ f.txt: {person, "kalle", 25}. <tag><c>16#400</c></tag> <item><p>Set group id on execution</p></item> </taglist> - <p>On Unix platforms, the following bits - can also be set.</p> + <p>On Unix platforms, other bits than those listed above + may be set.</p> </item> <tag><c>uid = integer() >= 0</c></tag> <item> diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl index 6e84a27ec9..0fabdc7929 100644 --- a/lib/mnesia/test/mnesia_test_lib.erl +++ b/lib/mnesia/test/mnesia_test_lib.erl @@ -263,6 +263,7 @@ slave_start_link(Host, Name, Retries) -> Path = code:get_path(), ok = rpc:call(NewNode, file, set_cwd, [Cwd]), true = rpc:call(NewNode, code, set_path, [Path]), + ok = rpc:call(NewNode, error_logger, tty, [false]), spawn_link(NewNode, ?MODULE, slave_sup, []), rpc:multicall([node() | nodes()], global, sync, []), {ok, NewNode}; diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl index 426e2f5125..85b31f3669 100644 --- a/lib/ssh/src/ssh_channel.erl +++ b/lib/ssh/src/ssh_channel.erl @@ -261,7 +261,7 @@ handle_info({ssh_cm, _, _} = Msg, #state{cm = ConnectionManager, adjust_window(Msg), {noreply, State#state{channel_state = ChannelState}, Timeout}; {stop, ChannelId, ChannelState} -> - ssh_connection:close(ConnectionManager, ChannelId), + catch ssh_connection:close(ConnectionManager, ChannelId), {stop, normal, State#state{close_sent = true, channel_state = ChannelState}} end; diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl index bd6bc0335b..ce5596e0f9 100644 --- a/lib/ssh/src/ssh_dbg.erl +++ b/lib/ssh/src/ssh_dbg.erl @@ -113,7 +113,12 @@ setup_tracer(Write, MangleArg) -> ok. %%%---------------------------------------------------------------- -shrink_bin(B) when is_binary(B), size(B)>100 -> {'*** SHRINKED BIN',size(B),element(1,split_binary(B,20)),'***'}; +shrink_bin(B) when is_binary(B), size(B)>100 -> {'*** SHRINKED BIN', + size(B), + element(1,split_binary(B,20)), + '...', + element(2,split_binary(B,size(B)-20)) + }; shrink_bin(L) when is_list(L) -> lists:map(fun shrink_bin/1, L); shrink_bin(T) when is_tuple(T) -> list_to_tuple(shrink_bin(tuple_to_list(T))); shrink_bin(X) -> X. diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 51e0d5196b..0a0ab5cdf7 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -315,9 +315,9 @@ init_per_testcase(TC, Config) when TC==shell_no_unicode ; {user_passwords, [{"foo", "bar"}]}]), ct:sleep(500), IO = ssh_test_lib:start_io_server(), - Shell = ssh_test_lib:start_shell(Port, IO, UserDir, - [{silently_accept_hosts, true}, - {user,"foo"},{password,"bar"}]), + Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}, + {silently_accept_hosts, true}, + {user,"foo"},{password,"bar"}]), ct:log("IO=~p, Shell=~p, self()=~p",[IO,Shell,self()]), ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p", [file:native_name_encoding(),io:getopts()]), @@ -343,14 +343,15 @@ end_per_testcase(TC, Config) when TC==shell_no_unicode ; TC==shell_unicode_string -> case proplists:get_value(sftpd, Config) of {Pid, _, _} -> - ssh:stop_daemon(Pid), - ssh:stop(); + catch ssh:stop_daemon(Pid); _ -> - ssh:stop() - end; + ok + end, + end_per_testcase(Config); end_per_testcase(_TestCase, Config) -> end_per_testcase(Config). -end_per_testcase(_Config) -> + +end_per_testcase(_Config) -> ssh:stop(), ok. @@ -524,7 +525,7 @@ shell(Config) when is_list(Config) -> ct:sleep(500), IO = ssh_test_lib:start_io_server(), - Shell = ssh_test_lib:start_shell(Port, IO, UserDir), + Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}]), receive {'EXIT', _, _} -> ct:fail(no_ssh_connection); @@ -562,10 +563,10 @@ exec_key_differs(Config, UserPKAlgs) -> ct:sleep(500), IO = ssh_test_lib:start_io_server(), - Shell = ssh_test_lib:start_shell(Port, IO, UserDir, - [{preferred_algorithms,[{public_key,['ssh-rsa']}]}, - {pref_public_key_algs,UserPKAlgs} - ]), + Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}, + {preferred_algorithms,[{public_key,['ssh-rsa']}]}, + {pref_public_key_algs,UserPKAlgs} + ]), receive @@ -596,9 +597,9 @@ exec_key_differs_fail(Config) when is_list(Config) -> ct:sleep(500), IO = ssh_test_lib:start_io_server(), - ssh_test_lib:start_shell(Port, IO, UserDir, - [{preferred_algorithms,[{public_key,['ssh-rsa']}]}, - {pref_public_key_algs,['ssh-dss']}]), + ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}, + {preferred_algorithms,[{public_key,['ssh-rsa']}]}, + {pref_public_key_algs,['ssh-dss']}]), receive {'EXIT', _, _} -> ok; diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl index 61883c0647..60eae5a850 100644 --- a/lib/ssh/test/ssh_options_SUITE.erl +++ b/lib/ssh/test/ssh_options_SUITE.erl @@ -540,10 +540,18 @@ connectfun_disconnectfun_server(Config) -> {disconnect,Ref,R} -> ct:log("Disconnect result: ~p",[R]), ssh:stop_daemon(Pid) - after 2000 -> + after 5000 -> + receive + X -> ct:log("received ~p",[X]) + after 0 -> ok + end, {fail, "No disconnectfun action"} end - after 2000 -> + after 5000 -> + receive + X -> ct:log("received ~p",[X]) + after 0 -> ok + end, {fail, "No connectfun action"} end. @@ -649,7 +657,7 @@ disconnectfun_option_server(Config) -> ct:log("Server detected disconnect: ~p",[Reason]), ssh:stop_daemon(Pid), ok - after 3000 -> + after 5000 -> receive X -> ct:log("received ~p",[X]) after 0 -> ok @@ -1003,7 +1011,7 @@ ssh_connect_nonegtimeout_connected(Config, Parallel) -> ct:sleep(500), IO = ssh_test_lib:start_io_server(), - Shell = ssh_test_lib:start_shell(Port, IO, UserDir), + Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}]), receive Error = {'EXIT', _, _} -> ct:log("~p",[Error]), diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl index b10ec3707f..74bbc291b2 100644 --- a/lib/ssh/test/ssh_renegotiate_SUITE.erl +++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl @@ -92,11 +92,11 @@ rekey(Config) -> ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, [{rekey_limit, 0}]), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), receive after ?REKEY_DATA_TMO -> %%By this time rekeying would have been done - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), ssh:close(ConnectionRef), ssh:stop_daemon(Pid) @@ -120,31 +120,31 @@ rekey_limit(Config) -> {max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), timer:sleep(?REKEY_DATA_TMO), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), Data = lists:duplicate(159000,1), ok = ssh_sftp:write_file(SftpPid, DataFile, Data), timer:sleep(?REKEY_DATA_TMO), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), timer:sleep(?REKEY_DATA_TMO), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), ok = ssh_sftp:write_file(SftpPid, DataFile, "hi\n"), timer:sleep(?REKEY_DATA_TMO), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), timer:sleep(?REKEY_DATA_TMO), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), ssh_sftp:stop_channel(SftpPid), ssh:close(ConnectionRef), @@ -169,7 +169,7 @@ renegotiate1(Config) -> ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]), @@ -181,7 +181,7 @@ renegotiate1(Config) -> timer:sleep(2000), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), @@ -208,7 +208,7 @@ renegotiate2(Config) -> ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]), @@ -223,7 +223,7 @@ renegotiate2(Config) -> timer:sleep(2000), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), @@ -235,19 +235,3 @@ renegotiate2(Config) -> %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- -%% get_kex_init - helper function to get key_exchange_init_msg -get_kex_init(Conn) -> - %% First, validate the key exchange is complete (StateName == connected) - {{connected,_},S} = sys:get_state(Conn), - %% Next, walk through the elements of the #state record looking - %% for the #ssh_msg_kexinit record. This method is robust against - %% changes to either record. The KEXINIT message contains a cookie - %% unique to each invocation of the key exchange procedure (RFC4253) - SL = tuple_to_list(S), - case lists:keyfind(ssh_msg_kexinit, 1, SL) of - false -> - throw(not_found); - KexInit -> - KexInit - end. - diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl index 6fd401d182..7cd364a6dc 100644 --- a/lib/ssh/test/ssh_test_lib.erl +++ b/lib/ssh/test/ssh_test_lib.erl @@ -127,24 +127,19 @@ std_simple_exec(Host, Port, Config, Opts) -> ssh:close(ConnectionRef). -start_shell(Port, IOServer, UserDir) -> - start_shell(Port, IOServer, UserDir, []). - -start_shell(Port, IOServer, UserDir, Options) -> - spawn_link(?MODULE, init_shell, [Port, IOServer, [{user_dir, UserDir}|Options]]). - start_shell(Port, IOServer) -> - spawn_link(?MODULE, init_shell, [Port, IOServer, []]). + start_shell(Port, IOServer, []). -init_shell(Port, IOServer, UserDir) -> - Host = hostname(), - Options = [{user_interaction, false}, {silently_accept_hosts, - true}] ++ UserDir, - group_leader(IOServer, self()), - loop_shell(Host, Port, Options). +start_shell(Port, IOServer, ExtraOptions) -> + spawn_link( + fun() -> + Host = hostname(), + Options = [{user_interaction, false}, + {silently_accept_hosts,true} | ExtraOptions], + group_leader(IOServer, self()), + ssh:shell(Host, Port, Options) + end). -loop_shell(Host, Port, Options) -> - ssh:shell(Host, Port, Options). start_io_server() -> spawn_link(?MODULE, init_io_server, [self()]). @@ -802,3 +797,20 @@ busy_wait(Nus, T0) -> end. %%%---------------------------------------------------------------- +%% get_kex_init - helper function to get key_exchange_init_msg + +get_kex_init(Conn) -> + %% First, validate the key exchange is complete (StateName == connected) + {{connected,_},S} = sys:get_state(Conn), + %% Next, walk through the elements of the #state record looking + %% for the #ssh_msg_kexinit record. This method is robust against + %% changes to either record. The KEXINIT message contains a cookie + %% unique to each invocation of the key exchange procedure (RFC4253) + SL = tuple_to_list(S), + case lists:keyfind(ssh_msg_kexinit, 1, SL) of + false -> + throw(not_found); + KexInit -> + KexInit + end. + diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index f481e9c1ce..f378188b8b 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -29,6 +29,7 @@ -define(TIMEOUT, 50000). -define(SSH_DEFAULT_PORT, 22). +-define(REKEY_DATA_TMO, 65000). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- @@ -55,7 +56,8 @@ groups() -> erlang_client_openssh_server_publickey_rsa, erlang_client_openssh_server_password, erlang_client_openssh_server_kexs, - erlang_client_openssh_server_nonexistent_subsystem + erlang_client_openssh_server_nonexistent_subsystem, + erlang_client_openssh_server_renegotiate ]}, {erlang_server, [], [erlang_server_openssh_client_public_key_dsa, erlang_server_openssh_client_public_key_rsa, @@ -105,6 +107,11 @@ init_per_testcase(erlang_server_openssh_client_public_key_rsa, Config) -> chk_key(sshc, 'ssh-rsa', ".ssh/id_rsa", Config); init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) -> chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config); +init_per_testcase(erlang_server_openssh_client_renegotiate, Config) -> + case os:type() of + {unix,_} -> ssh:start(), Config; + Type -> ct:fail("Unsupported test on ~p",[Type]) + end; init_per_testcase(_TestCase, Config) -> ssh:start(), Config. @@ -393,24 +400,34 @@ erlang_server_openssh_client_renegotiate(Config) -> SystemDir = proplists:get_value(data_dir, Config), PrivDir = proplists:get_value(priv_dir, Config), KnownHosts = filename:join(PrivDir, "known_hosts"), + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, {public_key_alg, PubKeyAlg}, {failfun, fun ssh_test_lib:failfun/2}]), +%% catch ssh_dbg:messages(fun(String,_D) -> ct:log(String) end), ct:sleep(500), + RenegLimitK = 3, DataFile = filename:join(PrivDir, "renegotiate_openssh_client.data"), - Data = lists:duplicate(32000, $a), + Data = lists:duplicate(trunc(1.1*RenegLimitK*1024), $a), ok = file:write_file(DataFile, Data), Cmd = "ssh -p " ++ integer_to_list(Port) ++ " -o UserKnownHostsFile=" ++ KnownHosts ++ - " -o RekeyLimit=20K" ++ + " -o RekeyLimit=" ++ integer_to_list(RenegLimitK) ++"K" ++ " " ++ Host ++ " < " ++ DataFile, OpenSsh = ssh_test_lib:open_port({spawn, Cmd}), Expect = fun({data,R}) -> - try lists:prefix(binary_to_list(R), Data) + try + NonAlphaChars = [C || C<-lists:seq(1,255), + not lists:member(C,lists:seq($a,$z)), + not lists:member(C,lists:seq($A,$Z)) + ], + Lines = string:tokens(binary_to_list(R), NonAlphaChars), + lists:any(fun(L) -> length(L)>1 andalso lists:prefix(L, Data) end, + Lines) catch _:_ -> false end; @@ -419,9 +436,61 @@ erlang_server_openssh_client_renegotiate(Config) -> end, ssh_test_lib:rcv_expected(Expect, OpenSsh, ?TIMEOUT), + %% Unfortunatly we can't check that there has been a renegotiation, just trust OpenSSH. ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +erlang_client_openssh_server_renegotiate(_Config) -> + process_flag(trap_exit, true), + + IO = ssh_test_lib:start_io_server(), + Ref = make_ref(), + Parent = self(), + +%% catch ssh_dbg:messages(fun(X,_) -> ct:log(X) end), + Shell = + spawn_link( + fun() -> + Host = ssh_test_lib:hostname(), + Options = [{user_interaction, false}, + {silently_accept_hosts,true}], + group_leader(IO, self()), + {ok, ConnRef} = ssh:connect(Host, ?SSH_DEFAULT_PORT, Options), + case ssh_connection:session_channel(ConnRef, infinity) of + {ok,ChannelId} -> + success = ssh_connection:ptty_alloc(ConnRef, ChannelId, []), + Args = [{channel_cb, ssh_shell}, + {init_args,[ConnRef, ChannelId]}, + {cm, ConnRef}, {channel_id, ChannelId}], + {ok, State} = ssh_channel:init([Args]), + Parent ! {ok, Ref, ConnRef}, + ssh_channel:enter_loop(State); + Error -> + Parent ! {error, Ref, Error} + end, + receive + nothing -> ok + end + end), + + receive + {error, Ref, Error} -> + ct:fail("Error=~p",[Error]); + {ok, Ref, ConnectionRef} -> + IO ! {input, self(), "echo Hej\n"}, + receive_hej(), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), + ssh_connection_handler:renegotiate(ConnectionRef), + IO ! {input, self(), "echo Hej\n"}, + receive_hej(), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), + IO ! {input, self(), "exit\n"}, + receive_logout(), + receive_normal_exit(Shell), + true = (Kex1 =/= Kex2) + end. + +%%-------------------------------------------------------------------- erlang_client_openssh_server_password() -> [{doc, "Test client password option"}]. erlang_client_openssh_server_password(Config) when is_list(Config) -> @@ -498,6 +567,25 @@ receive_hej() -> 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. +receive_data(Data) -> + receive + Info when is_binary(Info) -> + Lines = string:tokens(binary_to_list(Info), "\r\n "), + case lists:member(Data, Lines) of + true -> + ct:log("Expected result found in lines: ~p~n", [Lines]), + ok; + false -> + ct:log("Extra info: ~p~n", [Info]), + receive_data(Data) + end; + Other -> + ct:log("Unexpected: ~p",[Other]), + receive_data(Data) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) + end. + receive_logout() -> receive <<"logout">> -> |