diff options
147 files changed, 5021 insertions, 1250 deletions
diff --git a/OTP_VERSION b/OTP_VERSION index 01c3bca9e3..8b74066182 100644 --- a/OTP_VERSION +++ b/OTP_VERSION @@ -1 +1 @@ -22.0.1 +22.0.3 diff --git a/bootstrap/bin/no_dot_erlang.boot b/bootstrap/bin/no_dot_erlang.boot Binary files differindex 9a2412e1f0..1f7f088a77 100644 --- a/bootstrap/bin/no_dot_erlang.boot +++ b/bootstrap/bin/no_dot_erlang.boot diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot Binary files differindex 9a2412e1f0..1f7f088a77 100644 --- a/bootstrap/bin/start.boot +++ b/bootstrap/bin/start.boot diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot Binary files differindex 9a2412e1f0..1f7f088a77 100644 --- a/bootstrap/bin/start_clean.boot +++ b/bootstrap/bin/start_clean.boot diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam Binary files differindex f6e7f0d68e..07acbb1da7 100644 --- a/bootstrap/lib/compiler/ebin/beam_asm.beam +++ b/bootstrap/lib/compiler/ebin/beam_asm.beam diff --git a/bootstrap/lib/compiler/ebin/beam_except.beam b/bootstrap/lib/compiler/ebin/beam_except.beam Binary files differindex 5daead0cab..1894483f71 100644 --- a/bootstrap/lib/compiler/ebin/beam_except.beam +++ b/bootstrap/lib/compiler/ebin/beam_except.beam diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam Binary files differindex afba2693b4..e59340de4f 100644 --- a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam +++ b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam Binary files differindex f3dbfc42ff..f6bd1b7a69 100644 --- a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam +++ b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam Binary files differindex f7c9c34c9a..3de363dbb6 100644 --- a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam +++ b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam Binary files differindex 029de3d4a5..c13b25bac3 100644 --- a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam +++ b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam Binary files differindex 79d64b800b..9d0c34a94a 100644 --- a/bootstrap/lib/compiler/ebin/beam_validator.beam +++ b/bootstrap/lib/compiler/ebin/beam_validator.beam diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam Binary files differindex 023d0bda6d..7ff507242b 100644 --- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam +++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam diff --git a/bootstrap/lib/kernel/ebin/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam Binary files differindex e53e183312..20d795e75f 100644 --- a/bootstrap/lib/kernel/ebin/inet_db.beam +++ b/bootstrap/lib/kernel/ebin/inet_db.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam Binary files differindex c5ea3ee70f..62838ed771 100644 --- a/bootstrap/lib/stdlib/ebin/erl_lint.beam +++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam Binary files differindex c52efe1b44..45692978e4 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib_format.beam b/bootstrap/lib/stdlib/ebin/io_lib_format.beam Binary files differindex 5556dc733b..66047b5070 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib_format.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib_format.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam Binary files differindex ba644430da..57c700cb31 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam diff --git a/bootstrap/lib/stdlib/ebin/queue.beam b/bootstrap/lib/stdlib/ebin/queue.beam Binary files differindex 24b8582188..94728e30cc 100644 --- a/bootstrap/lib/stdlib/ebin/queue.beam +++ b/bootstrap/lib/stdlib/ebin/queue.beam diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam Binary files differindex e6d68ea84e..5d1d66892b 100644 --- a/bootstrap/lib/stdlib/ebin/string.beam +++ b/bootstrap/lib/stdlib/ebin/string.beam diff --git a/erts/configure.in b/erts/configure.in index 10a006d812..3a043c940d 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -676,7 +676,7 @@ elif test "X$PROFILE_INSTR_GENERATE" = "Xtrue" -a "X$PROFILE_INSTR_USE" = "Xtrue PROFILE_COMPILER=clang AC_MSG_RESULT([yes, using -fprofile-instr-generate]) else - if $enable_pgo = yes; then + if test $enable_pgo = yes; then AC_MSG_ERROR(cannot use PGO with this compiler) else AC_MSG_RESULT([no]) diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index 33d0903622..876834307a 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -289,7 +289,7 @@ Slogan: <reason></pre> <marker id="memory"></marker> <title>Memory Information</title> <p>Under the tag <em>=memory</em> is shown information similar - to what can be obtainted on a living node with + to what can be obtained on a living node with <seealso marker="erts:erlang#memory/0"> <c>erlang:memory()</c></seealso>.</p> </section> diff --git a/erts/doc/src/driver.xml b/erts/doc/src/driver.xml index 8f31df4cad..a873bf9931 100644 --- a/erts/doc/src/driver.xml +++ b/erts/doc/src/driver.xml @@ -391,7 +391,7 @@ void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn) <p>Before a driver can be called from Erlang, it must be loaded and opened. Loading is done using the <c><![CDATA[erl_ddll]]></c> module (the <c><![CDATA[erl_ddll]]></c> driver that loads dynamic - driver is actually a driver itself). If loading is successfull, + driver is actually a driver itself). If loading is successful, the port can be opened with <c><![CDATA[open_port/2]]></c>. The port name must match the name of the shared library and the name in the driver entry structure.</p> diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index 3730f0e8ac..2ba5994557 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -128,7 +128,7 @@ are deprecated and are only kept for backward compatibility when decoding terms encoded by older nodes.</p> <p>Support for UTF-8 encoded atoms in the external format has been - available since ERTS 5.10 (OTP R16). This abillity allows such old nodes + available since ERTS 5.10 (OTP R16). This ability allows such old nodes to decode, store and encode any Unicode atoms received from a new OTP 20 node.</p> <p>The maximum number of allowed characters in an atom is 255. In the @@ -913,7 +913,7 @@ <tcaption>SMALL_BIG_EXT</tcaption></table> <p> Bignums are stored in unary form with a <c>Sign</c> byte, - that is, 0 if the binum is positive and 1 if it is negative. The + that is, 0 if the bignum is positive and 1 if it is negative. The digits are stored with the least significant byte stored first. To calculate the integer, the following formula can be used: </p> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 51c1ecc8b1..df77ce181e 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,56 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 10.4.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed <c>process_info(Pid,reductions)</c> to not + categorically increase reduction count of the measured + process <c>Pid</c>. Repeated reduction measure of an idle + process will most often (but not guaranteed) return the + same value, like it behaved before OTP 21.3.8.</p> + <p> + Own Id: OTP-15865 Aux Id: ERL-964 </p> + </item> + <item> + <p>Fixed an incorrect load-time optimization that could + cause a crash when extracting deeply nested tuple + elements.</p> + <p> + Own Id: OTP-15871 Aux Id: ERIERL-374 </p> + </item> + <item> + <p> + Fix bug causing VM crash when pressing P for "proc info" + in Erlang shell break menu. Bug exists since OTP 22.0.</p> + <p> + Own Id: OTP-15873 Aux Id: ERL-965 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.4.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>In nested use of <c>try</c>/<c>catch</c>, rethrowing + an exception using <c>erlang:raise/3</c> with a different + class would not always be able to change the class of the + exception.</p> + <p> + Own Id: OTP-15834 Aux Id: ERIERL-367 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.4</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -514,6 +564,41 @@ </section> +<section><title>Erts 10.3.5.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>In nested use of <c>try</c>/<c>catch</c>, rethrowing + an exception using <c>erlang:raise/3</c> with a different + class would not always be able to change the class of the + exception.</p> + <p> + Own Id: OTP-15834 Aux Id: ERIERL-367 </p> + </item> + <item> + <p> + Fixed bug in <c>seq_trace:set_token(label,Term)</c> which + could cause VM crash if <c>Term</c> was heap allocated + (not an atom, small integer, local pid or port). Bug + exists since OTP 21.0 when terms other than small + integers were first allowed as labels.</p> + <p> + Own Id: OTP-15849 Aux Id: ERL-700 </p> + </item> + <item> + <p> + Fix <c>seq_trace:print/2</c> not to raise <c>badarg</c> + exception if label is not a small integer. Bug exists + since OTP 21.0.</p> + <p> + Own Id: OTP-15859 Aux Id: ERL-700 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.3.5.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml index 343b61d4aa..9b34bf1df1 100644 --- a/erts/doc/src/socket.xml +++ b/erts/doc/src/socket.xml @@ -373,19 +373,24 @@ <name name="open" arity="4" since="OTP 22.0"/> <fsummary>Create an endpoint for communication.</fsummary> <desc> - <p>Creates an endpoint (socket) for communication.</p> - <p>For some <c>types</c> there is a default protocol, which will - be used if no protocol is specified: </p> + <p>Creates an endpoint (socket) for communication.</p> - <list> - <item><p><c>stream</c>: <c>tcp</c></p></item> - <item><p><c>dgram</c>: <c>udp</c></p></item> - <item><p><c>seqpacket</c>: <c>sctp</c></p></item> - </list> + <p>For some <c>types</c> there is a default protocol, + indicated by <c>default</c>, which it <em>may</em> be + possible to specify. + And for <c>Domain = local</c>, if a protocol <em>is</em> pecified, + it <em>must</em> be <c>default</c>. </p> <p>The <c>Extra</c> argument is intended for "obscure" options. Currently the only supported option is <c>netns</c>, which is only supported on the linux platform.</p> + + <note> + <p>It may not be possible to specify the default protocol (except + when <c>Domain = local</c>). We need to be able to retreive + the resulting protocol, which is <em>not</em> possble on all + platforms. </p> + </note> </desc> </func> diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml index e0f006e618..4b3872d7e3 100644 --- a/erts/doc/src/socket_usage.xml +++ b/erts/doc/src/socket_usage.xml @@ -205,7 +205,7 @@ <cell>protocol()</cell> <cell>no</cell> <cell>yes</cell> - <cell>none</cell> + <cell><em>Not</em> on (some) Darwin (for instance)</cell> </row> <row> <cell>rcvbuf</cell> diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 38229456c9..8a580c8d7b 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -411,7 +411,7 @@ list_to_binary([B1,B2])</pre> <func> <name name="deflateParams" arity="3" since=""/> - <fsummary>Dynamicly update deflate parameters.</fsummary> + <fsummary>Dynamically update deflate parameters.</fsummary> <desc> <p>Dynamically updates the compression level and compression strategy. The interpretation of <c><anno>Level</anno></c> and diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 80e871aaf6..6379e4e04d 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -84,8 +84,9 @@ process_info(fmtfn_t to, void *to_arg) * they are most likely just created and has invalid data */ if (p->heap != NULL) { - ErtsProcLocks locks = (p == esdp->current_process || - p == esdp->free_process) ? ERTS_PROC_LOCK_MAIN : 0; + ErtsProcLocks locks = ((esdp && (p == esdp->current_process || + p == esdp->free_process)) + ? ERTS_PROC_LOCK_MAIN : 0); print_process_info(to, to_arg, p, locks); } } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 2704b99aa4..0339589b79 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -768,7 +768,7 @@ static ErtsProcessInfoArgs pi_args[] = { {am_memory, 0, ERTS_PI_FLAG_NEED_MSGQ_LEN|ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, {am_garbage_collection, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + ERTS_MAX_HEAP_SIZE_MAP_SZ, 0, ERTS_PROC_LOCK_MAIN}, {am_group_leader, 0, 0, ERTS_PROC_LOCK_MAIN}, - {am_reductions, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, + {am_reductions, 0, 0, ERTS_PROC_LOCK_MAIN}, {am_priority, 0, 0, 0}, {am_trace, 0, 0, ERTS_PROC_LOCK_MAIN}, {am_binary, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 711e62c795..b31d5b86cb 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -74,7 +74,7 @@ static void smp_bp_finisher(void* arg); static BIF_RETTYPE system_monitor(Process *p, Eterm monitor_pid, Eterm list); -static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/ +static void new_seq_trace_token(Process* p, int); /* help func for seq_trace_2*/ static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_on_load(Process* p, Eterm key); @@ -1874,7 +1874,7 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, if (current_flag && ( (arg2 == am_true) || (arg2 == am_false)) ) { /* Flags */ - new_seq_trace_token(p); + new_seq_trace_token(p, 0); flags = unsigned_val(SEQ_TRACE_TOKEN_FLAGS(p)); if (build_result) { old_value = flags & current_flag ? am_true : am_false; @@ -1889,11 +1889,11 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, return old_value; } else if (arg1 == am_label) { - new_seq_trace_token(p); + new_seq_trace_token(p, is_not_immed(arg2)); if (build_result) { old_value = SEQ_TRACE_TOKEN_LABEL(p); } - SEQ_TRACE_TOKEN_LABEL(p) = arg2; + SEQ_TRACE_TOKEN_LABEL(p) = arg2; return old_value; } else if (arg1 == am_serial) { @@ -1905,7 +1905,7 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, if ((*tp != make_arityval(2)) || is_not_small(*(tp+1)) || is_not_small(*(tp+2))) { return THE_NON_VALUE; } - new_seq_trace_token(p); + new_seq_trace_token(p, 0); if (build_result) { hp = HAlloc(p,3); old_value = TUPLE2(hp, SEQ_TRACE_TOKEN_LASTCNT(p), @@ -1940,8 +1940,8 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, } } -void -new_seq_trace_token(Process* p) +static void +new_seq_trace_token(Process* p, int ensure_new_heap) { Eterm* hp; @@ -1953,6 +1953,16 @@ new_seq_trace_token(Process* p) p->common.id, /* Internal pid */ /* From */ make_small(p->seq_trace_lastcnt)); } + else if (ensure_new_heap) { + Eterm* tpl = tuple_val(SEQ_TRACE_TOKEN(p)); + ASSERT(arityval(tpl[0]) == 5); + if (ErtsInArea(tpl, OLD_HEAP(p), + (OLD_HEND(p) - OLD_HEAP(p))*sizeof(Eterm))) { + hp = HAlloc(p, 6); + sys_memcpy(hp, tpl, 6*sizeof(Eterm)); + SEQ_TRACE_TOKEN(p) = make_tuple(hp); + } + } } BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item) @@ -2050,10 +2060,7 @@ BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2) if (have_no_seqtrace(SEQ_TRACE_TOKEN(BIF_P))) { BIF_RET(am_false); } - if (!(is_atom(BIF_ARG_1) || is_small(BIF_ARG_1))) { - BIF_ERROR(BIF_P, BADARG); - } - if (SEQ_TRACE_TOKEN_LABEL(BIF_P) != BIF_ARG_1) + if (!EQ(BIF_ARG_1, SEQ_TRACE_TOKEN_LABEL(BIF_P))) BIF_RET(am_false); seq_trace_update_send(BIF_P); seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_2, diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 6118c671ee..bbf50b4189 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1230,9 +1230,10 @@ void erts_check_for_holes(Process* p); /* The sequential tracing token is a tuple of size 5: * - * {Flags, Label, Serial, Sender} + * {Flags, Label, Serial, Sender, LastCnt} + * + * WARNING: The top 5-tuple is *MUTABLE* and thus INTERNAL ONLY. */ - #define SEQ_TRACE_TOKEN_ARITY(p) (arityval(*(tuple_val(SEQ_TRACE_TOKEN(p))))) #define SEQ_TRACE_TOKEN_FLAGS(p) (*(tuple_val(SEQ_TRACE_TOKEN(p)) + 1)) #define SEQ_TRACE_TOKEN_LABEL(p) (*(tuple_val(SEQ_TRACE_TOKEN(p)) + 2)) diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h index 74cc966cbe..6f715ae80d 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h @@ -49,7 +49,7 @@ do { \ #endif #ifdef DEBUG -extern Uint erts_no_schedulers; +extern Uint ERTS_WRITE_UNLIKELY(erts_no_schedulers); #endif #define ERTS_SSPA_FORCE_THR_CHECK_PROGRESS 10 diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 10ca74cd60..e9107933f9 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2018. All Rights Reserved. +# Copyright Ericsson AB 1997-2019. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -699,13 +699,18 @@ is_tuple NotTupleFail Tuple=x | is_tagged_tuple WrongRecordFail Tuple Arity Atom is_tagged_tuple_ff f? f? rx A a -get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ +get_tuple_element Reg=x P1 D1=x | \ + get_tuple_element Reg=x P2 D2=x | \ get_tuple_element Reg=x P3 D3=x | \ - succ(P1, P2) | succ(P2, P3) | \ - succ(D1, D2) | succ(D2, D3) => i_get_tuple_element3 Reg P1 D1 - -get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ - succ(P1, P2) | succ(D1, D2) => i_get_tuple_element2 Reg P1 D1 + succ(P1, P2) | succ(P2, P3) | succ(D1, D2) | succ(D2, D3) | \ + distinct(D1, Reg) | distinct(D2, Reg) | distinct(D3, Reg) => \ + i_get_tuple_element3 Reg P1 D1 + +get_tuple_element Reg=x P1 D1=x | \ + get_tuple_element Reg=x P2 D2=x | \ + succ(P1, P2) | succ(D1, D2) | \ + distinct(D1, Reg) => \ + i_get_tuple_element2 Reg P1 D1 get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ succ(P1, P2) | distinct(D1, Reg) => i_get_tuple_element2_dst Reg P1 D1 D2 diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index c93966d24f..0d9b4ff59f 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2018. All Rights Reserved. + * Copyright Ericsson AB 1997-2019. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -9846,10 +9846,8 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, copy_desc->send_timeout = desc->send_timeout; copy_desc->send_timeout_close = desc->send_timeout_close; - if (desc->tcp_add_flags & TCP_ADDF_SHOW_ECONNRESET) - copy_desc->tcp_add_flags |= TCP_ADDF_SHOW_ECONNRESET; - else - copy_desc->tcp_add_flags &= ~TCP_ADDF_SHOW_ECONNRESET; + copy_desc->tcp_add_flags = desc->tcp_add_flags + & (TCP_ADDF_SHOW_ECONNRESET | TCP_ADDF_LINGER_ZERO); /* The new port will be linked and connected to the original caller */ port = driver_create_port(port, owner, "tcp_inet", (ErlDrvData) copy_desc); diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c index b61939724c..c5e2af0b5e 100644 --- a/erts/emulator/hipe/hipe_arm.c +++ b/erts/emulator/hipe/hipe_arm.c @@ -30,24 +30,39 @@ #include "hipe_native_bif.h" /* nbif_callemu() */ #include "hipe_bif0.h" +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + /* Flush dcache and invalidate icache for a range of addresses. */ void hipe_flush_icache_range(void *address, unsigned int nbytes) { -#if defined(__ARM_EABI__) + void* end = (char*)address + nbytes; + +#if ERTS_AT_LEAST_GCC_VSN__(4, 3, 0) || __has_builtin(__builtin___clear_cache) + __builtin___clear_cache(address, end); +#elif defined(__clang__) + void __clear_cache(void *start, void *end); + __clear_cache(address, end); +#elif defined(__linux__) +# if defined(__ARM_EABI__) register unsigned long beg __asm__("r0") = (unsigned long)address; - register unsigned long end __asm__("r1") = (unsigned long)address + nbytes; + register unsigned long end __asm__("r1") = (unsigned long)end; register unsigned long flg __asm__("r2") = 0; register unsigned long scno __asm__("r7") = 0xf0002; __asm__ __volatile__("swi 0" /* sys_cacheflush() */ : "=r"(beg) : "0"(beg), "r"(end), "r"(flg), "r"(scno)); -#else +# else register unsigned long beg __asm__("r0") = (unsigned long)address; - register unsigned long end __asm__("r1") = (unsigned long)address + nbytes; + register unsigned long end __asm__("r1") = (unsigned long)end; register unsigned long flg __asm__("r2") = 0; __asm__ __volatile__("swi 0x9f0002" /* sys_cacheflush() */ : "=r"(beg) : "0"(beg), "r"(end), "r"(flg)); +# endif +#else +# error "Don't know how to flush instruction cache" #endif } diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 38c28a6de5..d6977be5aa 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -139,6 +139,7 @@ typedef unsigned int BOOLEAN_T; GLOBAL_ATOM_DEF(ctrunc); \ GLOBAL_ATOM_DEF(data); \ GLOBAL_ATOM_DEF(debug); \ + GLOBAL_ATOM_DEF(default); \ GLOBAL_ATOM_DEF(default_send_params); \ GLOBAL_ATOM_DEF(delayed_ack_time); \ GLOBAL_ATOM_DEF(dgram); \ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index e71c786d3e..25bc712949 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -528,6 +528,7 @@ typedef union { #define SOCKET_TYPE_SEQPACKET 5 /* protocol */ +#define SOCKET_PROTOCOL_DEFAULT 0 #define SOCKET_PROTOCOL_IP 1 #define SOCKET_PROTOCOL_TCP 2 #define SOCKET_PROTOCOL_UDP 3 @@ -658,6 +659,10 @@ typedef union { #define SOCKET_SUPPORTS_OPTIONS 0x0001 #define SOCKET_SUPPORTS_SCTP 0x0002 #define SOCKET_SUPPORTS_IPV6 0x0003 +#define SOCKET_SUPPORTS_LOCAL 0x0004 + +#define ESOCK_WHICH_PROTO_ERROR -1 +#define ESOCK_WHICH_PROTO_UNSUP -2 @@ -994,12 +999,15 @@ static ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env); static ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env); static ERL_NIF_TERM nsupports_sctp(ErlNifEnv* env); static ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_local(ErlNifEnv* env); static ERL_NIF_TERM nopen(ErlNifEnv* env, int domain, int type, int protocol, char* netns); +static BOOLEAN_T nopen_which_protocol(SOCKET sock, int* proto); + static ERL_NIF_TERM nbind(ErlNifEnv* env, ESockDescriptor* descP, ESockAddress* sockAddrP, @@ -2644,6 +2652,7 @@ static char str_exsend[] = "exsend"; // failed send GLOBAL_ATOM_DECL(ctrunc); \ GLOBAL_ATOM_DECL(data); \ GLOBAL_ATOM_DECL(debug); \ + GLOBAL_ATOM_DECL(default); \ GLOBAL_ATOM_DECL(default_send_params); \ GLOBAL_ATOM_DECL(delayed_ack_time); \ GLOBAL_ATOM_DECL(dgram); \ @@ -3046,6 +3055,9 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, * {tcp, [{Opt, boolean()}]}, * {udp, [{Opt, boolean()}]}, * {sctp, [{Opt, boolean()}]}] + * sctp boolean() + * ipv6 boolean() + * local boolean() */ static @@ -3073,13 +3085,10 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env, -/* nopen - create an endpoint for communication - * - * Assumes the input has been validated. +/* nsupports - what features do we support * - * Normally we want debugging on (individual) sockets to be controlled - * by the sockets own debug flag. But since we don't even have a socket - * yet, we must use the global debug flag. + * This is to prove information about what features actually + * work on the current platform. */ #if !defined(__WIN32__) static @@ -3102,6 +3111,10 @@ ERL_NIF_TERM nsupports(ErlNifEnv* env, int key) result = nsupports_ipv6(env); break; + case SOCKET_SUPPORTS_LOCAL: + result = nsupports_local(env); + break; + default: result = esock_atom_false; break; @@ -4324,6 +4337,24 @@ ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env) +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsupports_local(ErlNifEnv* env) +{ + ERL_NIF_TERM supports; + +#if defined(AF_LOCAL) + supports = esock_atom_true; +#else + supports = esock_atom_false; +#endif + + return supports; +} +#endif + + + /* ---------------------------------------------------------------------- * nif_open * @@ -4421,6 +4452,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, * yet, we must use the global debug flag. */ #if !defined(__WIN32__) + static ERL_NIF_TERM nopen(ErlNifEnv* env, int domain, int type, int protocol, @@ -4428,7 +4460,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, { ESockDescriptor* descP; ERL_NIF_TERM res; - int save_errno = 0; + int proto = protocol, save_errno = 0; SOCKET sock; HANDLE event; #ifdef HAVE_SETNS @@ -4448,11 +4480,35 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, return esock_make_error_errno(env, save_errno); #endif - if ((sock = sock_open(domain, type, protocol)) == INVALID_SOCKET) + if ((sock = sock_open(domain, type, proto)) == INVALID_SOCKET) return esock_make_error_errno(env, sock_errno()); SGDBG( ("SOCKET", "nopen -> open success: %d\r\n", sock) ); + + /* NOTE that if the protocol = 0 (default) and the domain is not + * local (AF_LOCAL) we need to explicitly get the protocol here! + */ + + if ((proto == 0) +#if defined(AF_LOCAL) + && (domain != AF_LOCAL) +#endif + ) + if (!nopen_which_protocol(sock, &proto)) { + if (proto == ESOCK_WHICH_PROTO_ERROR) { + save_errno = sock_errno(); + while ((sock_close(sock) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + return esock_make_error_errno(env, save_errno); + } else { + while ((sock_close(sock) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + return esock_make_error(env, esock_atom_eafnosupport); + } + } + + #ifdef HAVE_SETNS if ((netns != NULL) && !restore_network_namespace(current_ns, sock, &save_errno)) @@ -4484,7 +4540,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, descP->state = SOCKET_STATE_OPEN; descP->domain = domain; descP->type = type; - descP->protocol = protocol; + descP->protocol = proto; /* Does this apply to other types? Such as RAW? * Also, is this really correct? Should we not wait for bind? @@ -4521,6 +4577,32 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, return esock_make_ok2(env, res); } + + +static +BOOLEAN_T nopen_which_protocol(SOCKET sock, int* proto) +{ +#if defined(SO_PROTOCOL) + int val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + res = sock_getopt(sock, SOL_SOCKET, SO_PROTOCOL, &val, &valSz); + + if (res != 0) { + *proto = ESOCK_WHICH_PROTO_ERROR; + return FALSE; + } else { + *proto = val; + return TRUE; + } +#else + *proto = ESOCK_WHICH_PROTO_UNSUP; + return FALSE; +#endif +} + + #endif // if !defined(__WIN32__) @@ -10895,7 +10977,15 @@ ERL_NIF_TERM ngetopt_otp_protocol(ErlNifEnv* env, switch (val) { case IPPROTO_IP: +#if defined(AF_LOCAL) + if (descP->domain == AF_LOCAL) { + result = esock_make_ok2(env, esock_atom_default); + } else { + result = esock_make_ok2(env, esock_atom_ip); + } +#else result = esock_make_ok2(env, esock_atom_ip); +#endif break; case IPPROTO_TCP: @@ -11467,7 +11557,14 @@ ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, } else { switch (val) { case IPPROTO_IP: +#if defined(AF_LOCAL) + if (descP->domain == AF_LOCAL) + result = esock_make_ok2(env, esock_atom_default); + else + result = esock_make_ok2(env, esock_atom_ip); +#else result = esock_make_ok2(env, esock_atom_ip); +#endif break; case IPPROTO_TCP: @@ -15135,8 +15232,8 @@ char* encode_msghdr(ErlNifEnv* env, "\r\n read: %d" "\r\n", read) ); - /* The address is not used if we are connected, - * so check (length = 0) before we try to encodel + /* The address is not used if we are connected (unless, maybe, + * family is 'local'), so check (length = 0) before we try to encodel */ if (msgHdrP->msg_namelen != 0) { if ((xres = esock_encode_sockaddr(env, @@ -16258,8 +16355,6 @@ char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, size_t dataLen, ERL_NIF_TERM* eCMsgHdrData) { - char* xres; - switch (type) { #if defined(IPV6_PKTINFO) case IPV6_PKTINFO: @@ -16267,6 +16362,7 @@ char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, struct in6_pktinfo* pktInfoP = (struct in6_pktinfo*) dataP; ERL_NIF_TERM ifIndex = MKI(env, pktInfoP->ipi6_ifindex); ERL_NIF_TERM addr; + char* xres; if ((xres = esock_encode_ip6_address(env, &pktInfoP->ipi6_addr, @@ -17039,6 +17135,10 @@ BOOLEAN_T eproto2proto(ErlNifEnv* env, } switch (ep) { + case SOCKET_PROTOCOL_DEFAULT: + *proto = 0; // default - note that _IP also has the value 0... + break; + case SOCKET_PROTOCOL_IP: *proto = IPPROTO_IP; break; diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index a61cfc75ef..2740cb51ef 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -986,9 +986,27 @@ char* esock_decode_timeval(ErlNifEnv* env, if (!GET_LONG(env, eSec, &timeP->tv_sec)) return ESOCK_STR_EINVAL; +#if (SIZEOF_INT == 4) + { + int usec; + if (!GET_INT(env, eUSec, &usec)) + return ESOCK_STR_EINVAL; + timeP->tv_usec = (typeof(timeP->tv_usec)) usec; + } +#elif (SIZEOF_LONG == 4) + { + long usec; + if (!GET_LONG(env, eUSec, &usec)) + return ESOCK_STR_EINVAL; + timeP->tv_usec = (typeof(timeP->tv_usec)) usec; + } +#else + /* Ok, we give up... */ if (!GET_LONG(env, eUSec, &timeP->tv_usec)) return ESOCK_STR_EINVAL; +#endif + return NULL; } @@ -1656,7 +1674,7 @@ char* make_sockaddr_un(ErlNifEnv* env, ERL_NIF_TERM* sa) { ERL_NIF_TERM keys[] = {esock_atom_family, esock_atom_path}; - ERL_NIF_TERM vals[] = {esock_atom_inet, path}; + ERL_NIF_TERM vals[] = {esock_atom_local, path}; unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); diff --git a/erts/emulator/test/esock_ttest/esock-ttest b/erts/emulator/test/esock_ttest/esock-ttest index f0d363ab30..cf1d9cd9ab 100755 --- a/erts/emulator/test/esock_ttest/esock-ttest +++ b/erts/emulator/test/esock_ttest/esock-ttest @@ -50,43 +50,47 @@ usage() -> "~n units (server or client)." "~n" "~n options: " - "~n --help Display this info and exit. " - "~n --server [server-options] Start a server. " - "~n There are no mandatory server options." - "~n --client client-options Start a client" - "~n Some client options are mandatory and" - "~n others optional." - "~n --active <active> boolean() | once." - "~n Valid for both client and server." - "~n Defaults to: false" - "~n --transport <transport> Which transport to use: gen|sock[:plain|msg]" - "~n gen: gen_tcp" - "~n sock: socket" - "~n plain: recv/send (default)" - "~n msg: recvmsg/sendmsg" - "~n Defaults to: sock:plain" - "~n --scon <addr>:<port> Address and port of the server." - "~n The address part is in the standard form:" - "~n \"a.b.c.d\"." - "~n Only valid for client." - "~n Mandatory." - "~n --msg-id <1|2|3> Choose which message to use during the test." - "~n Basically: " - "~n 1: small" - "~n 2: medium" - "~n 3: large" - "~n Defaults to: 1" - "~n --max-outstanding <Num> How many messages to send before waiting for" - "~n a reply." - "~n Valid only for client." - "~n Defaults to: " - "~n MsgID 1: 100" - "~n MsgID 2: 10" - "~n MsgID 3: 1" - "~n --runtime <Time> Time of the test in seconds." - "~n Only valid for client." - "~n Mandatory." - "~n Defaults to: 60 (seconds)" + "~n --help Display this info and exit. " + "~n --server [server-options] Start a server. " + "~n There are no mandatory server options." + "~n --client client-options Start a client" + "~n Some client options are mandatory and" + "~n others optional." + "~n --domain <domain> local | inet | inet6" + "~n Which domain to use." + "~n Only valid for server." + "~n Defaults to: inet" + "~n --active <active> boolean() | once." + "~n Valid for both client and server." + "~n Defaults to: false" + "~n --transport <transport> Which transport to use: gen|sock[:plain|msg]" + "~n gen: gen_tcp" + "~n sock: socket" + "~n plain: recv/send (default)" + "~n msg: recvmsg/sendmsg" + "~n Defaults to: sock:plain" + "~n --scon <addr>:<port>|<path> Server info." + "~n The address part is in the standard form:" + "~n \"a.b.c.d\"." + "~n <path> is used for Unix Domain sockets (local)." + "~n Only valid, and mandatory, for client." + "~n --msg-id <1|2|3> Choose which message to use during the test." + "~n Basically: " + "~n 1: small" + "~n 2: medium" + "~n 3: large" + "~n Defaults to: 1" + "~n --max-outstanding <Num> How many messages to send before waiting for" + "~n a reply." + "~n Valid only for client." + "~n Defaults to: " + "~n MsgID 1: 100" + "~n MsgID 2: 10" + "~n MsgID 3: 1" + "~n --runtime <Time> Time of the test in seconds." + "~n Only valid for client." + "~n Mandatory." + "~n Defaults to: 60 (seconds)" "~n" "~n" "~n", @@ -106,6 +110,7 @@ process_args(Args) -> process_server_args(Args) -> Defaults = #{role => server, + domain => inet, active => false, transport => {sock, plain}}, process_server_args(Args, Defaults). @@ -113,6 +118,12 @@ process_server_args(Args) -> process_server_args([], State) -> State; +process_server_args(["--domain", Domain|Args], State) + when ((Domain =:= "local") orelse + (Domain =:= "inet") orelse + (Domain =:= "inet6")) -> + process_server_args(Args, State#{domain => list_to_atom(Domain)}); + process_server_args(["--active", Active|Args], State) when ((Active =:= "false") orelse (Active =:= "once") orelse @@ -137,7 +148,7 @@ process_client_args(Args) -> active => false, transport => {sock, plain}, %% Will cause error if not provided - %% Should be "addr:port" + %% Should be "addr:port or string() server => undefined, msg_id => 1, %% Will be filled in based on msg_id if not provided @@ -199,6 +210,8 @@ process_client_args(["--scon", Server|Args], State) -> usage(f("Invalid Server Port: ~s", [PortStr])) end, process_client_args(Args, State#{server => {Addr, Port}}); + [Path] -> + process_client_args(Args, State#{server => Path}); _ -> usage(f("Invalid Server: ~s", [Server])) end; @@ -249,9 +262,11 @@ process_client_args_ensure_max_outstanding( %% ========================================================================== exec(#{role := server, + domain := Domain, active := Active, - transport := gen}) -> - case socket_test_ttest_tcp_server_gen:start(Active) of + transport := gen}) + when (Domain =:= inet) orelse (Domain =:= inet6) -> + case socket_test_ttest_tcp_server_gen:start(Domain, Active) of {ok, {Pid, _}} -> MRef = erlang:monitor(process, Pid), receive @@ -264,9 +279,10 @@ exec(#{role := server, error end; exec(#{role := server, + domain := Domain, active := Active, transport := {sock, Method}}) -> - case socket_test_ttest_tcp_server_socket:start(Method, Active) of + case socket_test_ttest_tcp_server_socket:start(Method, Domain, Active) of {ok, {Pid, _}} -> MRef = erlang:monitor(process, Pid), receive @@ -283,15 +299,15 @@ exec(#{role := client, server := undefined}) -> usage("Mandatory option 'server' not provided"); exec(#{role := client, - server := {Addr, Port}, + server := {_Addr, _Port} = ServerInfo, active := Active, transport := gen, msg_id := MsgID, max_outstanding := MaxOutstanding, runtime := RunTime}) -> case socket_test_ttest_tcp_client_gen:start(true, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime) of {ok, Pid} -> @@ -306,7 +322,7 @@ exec(#{role := client, error end; exec(#{role := client, - server := {Addr, Port}, + server := ServerInfo, active := Active, transport := {sock, Method}, msg_id := MsgID, @@ -314,8 +330,8 @@ exec(#{role := client, runtime := RunTime}) -> case socket_test_ttest_tcp_client_socket:start(true, Method, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime) of {ok, Pid} -> diff --git a/erts/emulator/test/esock_ttest/esock-ttest-client b/erts/emulator/test/esock_ttest/esock-ttest-client index 1ab56f2d44..7c90ae6391 100755 --- a/erts/emulator/test/esock_ttest/esock-ttest-client +++ b/erts/emulator/test/esock_ttest/esock-ttest-client @@ -26,33 +26,46 @@ ESOCK_TTEST=$EMU_TEST/esock_ttest RUNTIME=30 -MSGID=$1 -SERVER_ADDR=$2 -SERVER_PORT=$3 +if [ $# = 3 ]; then + MSGID=$1 + SERVER_INFO=$2:$3 + + ITERATIONS="\ + gen false $MSGID + gen true $MSGID + gen once $MSGID + sock false $MSGID + sock true $MSGID + sock once $MSGID" + +else + if [ $# = 2 ]; then + MSGID=$1 + SERVER_INFO=$2 + + ITERATIONS="\ + sock false $MSGID + sock true $MSGID + sock once $MSGID" + + else + echo "Invalid number of args" + exit 1; + fi +fi + # --------------------------------------------------------------------------- -ITERATIONS="\ - gen false $MSGID - gen true $MSGID - gen once $MSGID - sock false $MSGID - sock true $MSGID - sock once $MSGID" - -# gen false 2 -# gen true 2 -# gen once 2 -# sock false 2 -# sock true 2 -# sock once 2 -# gen false 3 -# gen true 3 -# gen once 3 -# sock false 3 -# sock true 3 -# sock once 3 -# +# For when we have figured out how to configure local for gen_tcp... + +#ITERATIONS="\ +# gen false $MSGID +# gen true $MSGID +# gen once $MSGID +# sock false $MSGID +# sock true $MSGID +# sock once $MSGID" # --------------------------------------------------------------------------- @@ -64,7 +77,7 @@ echo "$ITERATIONS" | # The /dev/null at the end is necessary because erlang "does things" with stdin # and this case would cause the 'while read' to "fail" so that we only would # loop one time - $ESOCK_TTEST/esock-ttest --client --transport $TRANSPORT --active $ACTIVE --msg-id $MSG_ID --scon $SERVER_ADDR:$SERVER_PORT --runtime $RUNTIME </dev/null + $ESOCK_TTEST/esock-ttest --client --transport $TRANSPORT --active $ACTIVE --msg-id $MSG_ID --scon $SERVER_INFO --runtime $RUNTIME </dev/null echo "" done diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index b530ced566..3684cde8d4 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -1098,42 +1098,86 @@ process_info_status_handled_signal(Config) when is_list(Config) -> %% OTP-15709 %% Provoke a bug where process_info(reductions) returned wrong result %% because REDS_IN (def_arg_reg[5]) is read when the process in not running. +%% +%% And a bug where process_info(reductions) on a process which was releasing its +%% main lock during execution could result in negative reduction diffs. process_info_reductions(Config) when is_list(Config) -> - pi_reductions_tester(spawn_link(fun() -> pi_reductions_spinnloop() end)), - pi_reductions_tester(spawn_link(fun() -> pi_reductions_recvloop() end)), + {S1, S2} = case erlang:system_info(schedulers) of + 1 -> {1,1}; + _ -> {1,2} + end, + io:format("Run on schedulers ~p and ~p\n", [S1,S2]), + Boss = self(), + Doer = spawn_opt(fun () -> + pi_reductions_tester(true, 10, fun pi_reductions_spinnloop/0, S2), + pi_reductions_tester(true, 10, fun pi_reductions_recvloop/0, S2), + pi_reductions_tester(false, 100, fun pi_reductions_main_unlocker/0, S2), + Boss ! {self(), done} + end, + [link, {scheduler, S1}]), + + {Doer, done} = receive M -> M end, ok. -pi_reductions_tester(Pid) -> - {_, DiffList} = - lists:foldl(fun(_, {Prev, Acc}) -> - %% Add another item that force sending the request - %% as a signal, like 'current_function'. - PI = process_info(Pid, [reductions, current_function]), - [{reductions,Reds}, {current_function,_}] = PI, - Diff = Reds - Prev, - {Diff, true} = {Diff, (Diff >= 0)}, - {Diff, true} = {Diff, (Diff =< 1000*1000)}, - {Reds, [Diff | Acc]} - end, - {0, []}, - lists:seq(1,10)), +pi_reductions_tester(ForceSignal, MaxCalls, Fun, S2) -> + Pid = spawn_opt(Fun, [link, {scheduler,S2}]), + Extra = case ForceSignal of + true -> + %% Add another item that force sending the request + %% as a signal, like 'current_function'. + [current_function]; + false -> + [] + end, + LoopFun = fun Me(Calls, Prev, Acc0) -> + PI = process_info(Pid, [reductions | Extra]), + [{reductions,Reds} | _] = PI, + Diff = Reds - Prev, + %% Verify we get sane non-negative reduction diffs + {Diff, true} = {Diff, (Diff >= 0)}, + {Diff, true} = {Diff, (Diff =< 1000*1000)}, + Acc1 = [Diff | Acc0], + case Calls >= MaxCalls of + true -> Acc1; + false -> Me(Calls+1, Reds, Acc1) + end + end, + DiffList = LoopFun(0, 0, []), unlink(Pid), exit(Pid,kill), - io:format("Reduction diffs: ~p\n", [DiffList]), + io:format("Reduction diffs: ~p\n", [lists:reverse(DiffList)]), ok. pi_reductions_spinnloop() -> %% 6 args to make use of def_arg_reg[5] which is also used as REDS_IN - pi_reductions_spinnloop(1, atom, "hej", self(), make_ref(), 3.14). + pi_reductions_spinnloop(999*1000, atom, "hej", self(), make_ref(), 3.14). -pi_reductions_spinnloop(A,B,C,D,E,F) -> - pi_reductions_spinnloop(B,C,D,E,F,A). +pi_reductions_spinnloop(N,A,B,C,D,E) when N > 0 -> + pi_reductions_spinnloop(N-1,B,C,D,E,A); +pi_reductions_spinnloop(0,_,_,_,_,_) -> + %% Stop to limit max number of reductions consumed + pi_reductions_recvloop(). pi_reductions_recvloop() -> receive "a free lunch" -> false end. +pi_reductions_main_unlocker() -> + Other = spawn_link(fun() -> receive die -> ok end end), + pi_reductions_main_unlocker_loop(Other). + +pi_reductions_main_unlocker_loop(Other) -> + %% Assumption: register(OtherPid, Name) will unlock main lock of calling + %% process during execution. + register(pi_reductions_main_unlocker, Other), + unregister(pi_reductions_main_unlocker), + + %% Yield in order to increase probability of process_info sometimes probing + %% this process when it's not RUNNING. + erlang:yield(), + pi_reductions_main_unlocker_loop(Other). + %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl index e3545ccbf9..49b0fcccc2 100644 --- a/erts/emulator/test/socket_SUITE.erl +++ b/erts/emulator/test/socket_SUITE.erl @@ -70,10 +70,16 @@ %% *** API Basic *** api_b_open_and_close_udp4/1, api_b_open_and_close_tcp4/1, + api_b_open_and_close_udpL/1, + api_b_open_and_close_tcpL/1, api_b_sendto_and_recvfrom_udp4/1, + api_b_sendto_and_recvfrom_udpL/1, api_b_sendmsg_and_recvmsg_udp4/1, + api_b_sendmsg_and_recvmsg_udpL/1, api_b_send_and_recv_tcp4/1, + api_b_send_and_recv_tcpL/1, api_b_sendmsg_and_recvmsg_tcp4/1, + api_b_sendmsg_and_recvmsg_tcpL/1, %% *** API Options *** api_opt_simple_otp_options/1, @@ -107,57 +113,79 @@ %% *** Socket Closure *** sc_cpe_socket_cleanup_tcp4/1, sc_cpe_socket_cleanup_tcp6/1, + sc_cpe_socket_cleanup_tcpL/1, sc_cpe_socket_cleanup_udp4/1, sc_cpe_socket_cleanup_udp6/1, + sc_cpe_socket_cleanup_udpL/1, sc_lc_recv_response_tcp4/1, sc_lc_recv_response_tcp6/1, + sc_lc_recv_response_tcpL/1, sc_lc_recvfrom_response_udp4/1, sc_lc_recvfrom_response_udp6/1, + sc_lc_recvfrom_response_udpL/1, sc_lc_recvmsg_response_tcp4/1, sc_lc_recvmsg_response_tcp6/1, + sc_lc_recvmsg_response_tcpL/1, sc_lc_recvmsg_response_udp4/1, sc_lc_recvmsg_response_udp6/1, + sc_lc_recvmsg_response_udpL/1, sc_lc_acceptor_response_tcp4/1, sc_lc_acceptor_response_tcp6/1, + sc_lc_acceptor_response_tcpL/1, sc_rc_recv_response_tcp4/1, sc_rc_recv_response_tcp6/1, + sc_rc_recv_response_tcpL/1, sc_rc_recvmsg_response_tcp4/1, sc_rc_recvmsg_response_tcp6/1, + sc_rc_recvmsg_response_tcpL/1, sc_rs_recv_send_shutdown_receive_tcp4/1, sc_rs_recv_send_shutdown_receive_tcp6/1, + sc_rs_recv_send_shutdown_receive_tcpL/1, sc_rs_recvmsg_send_shutdown_receive_tcp4/1, sc_rs_recvmsg_send_shutdown_receive_tcp6/1, + sc_rs_recvmsg_send_shutdown_receive_tcpL/1, %% *** Traffic *** traffic_send_and_recv_chunks_tcp4/1, traffic_send_and_recv_chunks_tcp6/1, + traffic_send_and_recv_chunks_tcpL/1, traffic_ping_pong_small_send_and_recv_tcp4/1, traffic_ping_pong_small_send_and_recv_tcp6/1, + traffic_ping_pong_small_send_and_recv_tcpL/1, traffic_ping_pong_medium_send_and_recv_tcp4/1, traffic_ping_pong_medium_send_and_recv_tcp6/1, + traffic_ping_pong_medium_send_and_recv_tcpL/1, traffic_ping_pong_large_send_and_recv_tcp4/1, traffic_ping_pong_large_send_and_recv_tcp6/1, + traffic_ping_pong_large_send_and_recv_tcpL/1, traffic_ping_pong_small_sendto_and_recvfrom_udp4/1, traffic_ping_pong_small_sendto_and_recvfrom_udp6/1, + traffic_ping_pong_small_sendto_and_recvfrom_udpL/1, traffic_ping_pong_medium_sendto_and_recvfrom_udp4/1, traffic_ping_pong_medium_sendto_and_recvfrom_udp6/1, + traffic_ping_pong_medium_sendto_and_recvfrom_udpL/1, traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4/1, traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6/1, + traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6/1, + traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL/1, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4/1, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6/1, + traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL/1, traffic_ping_pong_small_sendmsg_and_recvmsg_udp4/1, traffic_ping_pong_small_sendmsg_and_recvmsg_udp6/1, + traffic_ping_pong_small_sendmsg_and_recvmsg_udpL/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6/1, + traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL/1, %% *** Time Test *** %% Server: transport = gen_tcp, active = false @@ -325,24 +353,33 @@ %% Client: transport = socket(tcp) ttest_ssockf_csockf_small_tcp4/1, ttest_ssockf_csockf_small_tcp6/1, + ttest_ssockf_csockf_small_tcpL/1, ttest_ssockf_csockf_medium_tcp4/1, ttest_ssockf_csockf_medium_tcp6/1, + ttest_ssockf_csockf_medium_tcpL/1, ttest_ssockf_csockf_large_tcp4/1, ttest_ssockf_csockf_large_tcp6/1, + ttest_ssockf_csockf_large_tcpL/1, ttest_ssockf_csocko_small_tcp4/1, ttest_ssockf_csocko_small_tcp6/1, + ttest_ssockf_csocko_small_tcpL/1, ttest_ssockf_csocko_medium_tcp4/1, ttest_ssockf_csocko_medium_tcp6/1, + ttest_ssockf_csocko_medium_tcpL/1, ttest_ssockf_csocko_large_tcp4/1, ttest_ssockf_csocko_large_tcp6/1, + ttest_ssockf_csocko_large_tcpL/1, ttest_ssockf_csockt_small_tcp4/1, ttest_ssockf_csockt_small_tcp6/1, + ttest_ssockf_csockt_small_tcpL/1, ttest_ssockf_csockt_medium_tcp4/1, ttest_ssockf_csockt_medium_tcp6/1, + ttest_ssockf_csockt_medium_tcpL/1, ttest_ssockf_csockt_large_tcp4/1, ttest_ssockf_csockt_large_tcp6/1, + ttest_ssockf_csockt_large_tcpL/1, %% Server: transport = socket(tcp), active = once %% Client: transport = gen_tcp @@ -371,24 +408,33 @@ %% Client: transport = socket(tcp) ttest_ssocko_csockf_small_tcp4/1, ttest_ssocko_csockf_small_tcp6/1, + ttest_ssocko_csockf_small_tcpL/1, ttest_ssocko_csockf_medium_tcp4/1, + ttest_ssocko_csockf_medium_tcpL/1, ttest_ssocko_csockf_medium_tcp6/1, ttest_ssocko_csockf_large_tcp4/1, ttest_ssocko_csockf_large_tcp6/1, + ttest_ssocko_csockf_large_tcpL/1, ttest_ssocko_csocko_small_tcp4/1, ttest_ssocko_csocko_small_tcp6/1, + ttest_ssocko_csocko_small_tcpL/1, ttest_ssocko_csocko_medium_tcp4/1, ttest_ssocko_csocko_medium_tcp6/1, + ttest_ssocko_csocko_medium_tcpL/1, ttest_ssocko_csocko_large_tcp4/1, ttest_ssocko_csocko_large_tcp6/1, + ttest_ssocko_csocko_large_tcpL/1, ttest_ssocko_csockt_small_tcp4/1, ttest_ssocko_csockt_small_tcp6/1, + ttest_ssocko_csockt_small_tcpL/1, ttest_ssocko_csockt_medium_tcp4/1, ttest_ssocko_csockt_medium_tcp6/1, + ttest_ssocko_csockt_medium_tcpL/1, ttest_ssocko_csockt_large_tcp4/1, ttest_ssocko_csockt_large_tcp6/1, + ttest_ssocko_csockt_large_tcpL/1, %% Server: transport = socket(tcp), active = true %% Client: transport = gen_tcp @@ -417,24 +463,33 @@ %% Client: transport = socket(tcp) ttest_ssockt_csockf_small_tcp4/1, ttest_ssockt_csockf_small_tcp6/1, + ttest_ssockt_csockf_small_tcpL/1, ttest_ssockt_csockf_medium_tcp4/1, ttest_ssockt_csockf_medium_tcp6/1, + ttest_ssockt_csockf_medium_tcpL/1, ttest_ssockt_csockf_large_tcp4/1, ttest_ssockt_csockf_large_tcp6/1, + ttest_ssockt_csockf_large_tcpL/1, ttest_ssockt_csocko_small_tcp4/1, ttest_ssockt_csocko_small_tcp6/1, + ttest_ssockt_csocko_small_tcpL/1, ttest_ssockt_csocko_medium_tcp4/1, ttest_ssockt_csocko_medium_tcp6/1, + ttest_ssockt_csocko_medium_tcpL/1, ttest_ssockt_csocko_large_tcp4/1, ttest_ssockt_csocko_large_tcp6/1, + ttest_ssockt_csocko_large_tcpL/1, ttest_ssockt_csockt_small_tcp4/1, ttest_ssockt_csockt_small_tcp6/1, + ttest_ssockt_csockt_small_tcpL/1, ttest_ssockt_csockt_medium_tcp4/1, ttest_ssockt_csockt_medium_tcp6/1, + ttest_ssockt_csockt_medium_tcpL/1, ttest_ssockt_csockt_large_tcp4/1, - ttest_ssockt_csockt_large_tcp6/1 + ttest_ssockt_csockt_large_tcp6/1, + ttest_ssockt_csockt_large_tcpL/1 %% Tickets ]). @@ -508,71 +563,75 @@ use_group(Group, Env, Default) -> groups() -> - [{api, [], api_cases()}, - {api_basic, [], api_basic_cases()}, - {api_options, [], api_options_cases()}, - {api_op_with_timeout, [], api_op_with_timeout_cases()}, - {socket_closure, [], socket_closure_cases()}, - {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, - {sc_local_close, [], sc_lc_cases()}, - {sc_remote_close, [], sc_rc_cases()}, - {sc_remote_shutdown, [], sc_rs_cases()}, - {traffic, [], traffic_cases()}, - {ttest, [], ttest_cases()}, - {ttest_sgenf, [], ttest_sgenf_cases()}, - {ttest_sgenf_cgen, [], ttest_sgenf_cgen_cases()}, - {ttest_sgenf_cgenf, [], ttest_sgenf_cgenf_cases()}, - {ttest_sgenf_cgeno, [], ttest_sgenf_cgeno_cases()}, - {ttest_sgenf_cgent, [], ttest_sgenf_cgent_cases()}, - {ttest_sgenf_csock, [], ttest_sgenf_csock_cases()}, - {ttest_sgenf_csockf, [], ttest_sgenf_csockf_cases()}, - {ttest_sgenf_csocko, [], ttest_sgenf_csocko_cases()}, - {ttest_sgenf_csockt, [], ttest_sgenf_csockt_cases()}, - {ttest_sgeno, [], ttest_sgeno_cases()}, - {ttest_sgeno_cgen, [], ttest_sgeno_cgen_cases()}, - {ttest_sgeno_cgenf, [], ttest_sgeno_cgenf_cases()}, - {ttest_sgeno_cgeno, [], ttest_sgeno_cgeno_cases()}, - {ttest_sgeno_cgent, [], ttest_sgeno_cgent_cases()}, - {ttest_sgeno_csock, [], ttest_sgeno_csock_cases()}, - {ttest_sgeno_csockf, [], ttest_sgeno_csockf_cases()}, - {ttest_sgeno_csocko, [], ttest_sgeno_csocko_cases()}, - {ttest_sgeno_csockt, [], ttest_sgeno_csockt_cases()}, - {ttest_sgent, [], ttest_sgent_cases()}, - {ttest_sgent_cgen, [], ttest_sgent_cgen_cases()}, - {ttest_sgent_cgenf, [], ttest_sgent_cgenf_cases()}, - {ttest_sgent_cgeno, [], ttest_sgent_cgeno_cases()}, - {ttest_sgent_cgent, [], ttest_sgent_cgent_cases()}, - {ttest_sgent_csock, [], ttest_sgent_csock_cases()}, - {ttest_sgent_csockf, [], ttest_sgent_csockf_cases()}, - {ttest_sgent_csocko, [], ttest_sgent_csocko_cases()}, - {ttest_sgent_csockt, [], ttest_sgent_csockt_cases()}, - {ttest_ssockf, [], ttest_ssockf_cases()}, - {ttest_ssockf_cgen, [], ttest_ssockf_cgen_cases()}, - {ttest_ssockf_cgenf, [], ttest_ssockf_cgenf_cases()}, - {ttest_ssockf_cgeno, [], ttest_ssockf_cgeno_cases()}, - {ttest_ssockf_cgent, [], ttest_ssockf_cgent_cases()}, - {ttest_ssockf_csock, [], ttest_ssockf_csock_cases()}, - {ttest_ssockf_csockf, [], ttest_ssockf_csockf_cases()}, - {ttest_ssockf_csocko, [], ttest_ssockf_csocko_cases()}, - {ttest_ssockf_csockt, [], ttest_ssockf_csockt_cases()}, - {ttest_ssocko, [], ttest_ssocko_cases()}, - {ttest_ssocko_cgen, [], ttest_ssocko_cgen_cases()}, - {ttest_ssocko_cgenf, [], ttest_ssocko_cgenf_cases()}, - {ttest_ssocko_cgeno, [], ttest_ssocko_cgeno_cases()}, - {ttest_ssocko_cgent, [], ttest_ssocko_cgent_cases()}, - {ttest_ssocko_csock, [], ttest_ssocko_csock_cases()}, - {ttest_ssocko_csockf, [], ttest_ssocko_csockf_cases()}, - {ttest_ssocko_csocko, [], ttest_ssocko_csocko_cases()}, - {ttest_ssocko_csockt, [], ttest_ssocko_csockt_cases()}, - {ttest_ssockt, [], ttest_ssockt_cases()}, - {ttest_ssockt_cgen, [], ttest_ssockt_cgen_cases()}, - {ttest_ssockt_cgenf, [], ttest_ssockt_cgenf_cases()}, - {ttest_ssockt_cgeno, [], ttest_ssockt_cgeno_cases()}, - {ttest_ssockt_cgent, [], ttest_ssockt_cgent_cases()}, - {ttest_ssockt_csock, [], ttest_ssockt_csock_cases()}, - {ttest_ssockt_csockf, [], ttest_ssockt_csockf_cases()}, - {ttest_ssockt_csocko, [], ttest_ssockt_csocko_cases()}, - {ttest_ssockt_csockt, [], ttest_ssockt_csockt_cases()} + [{api, [], api_cases()}, + {api_basic, [], api_basic_cases()}, + {api_options, [], api_options_cases()}, + {api_op_with_timeout, [], api_op_with_timeout_cases()}, + {socket_closure, [], socket_closure_cases()}, + {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, + {sc_local_close, [], sc_lc_cases()}, + {sc_remote_close, [], sc_rc_cases()}, + {sc_remote_shutdown, [], sc_rs_cases()}, + {traffic, [], traffic_cases()}, + {traffic_chunks, [], traffic_chunks_cases()}, + {traffic_pp_send_recv, [], traffic_pp_send_recv_cases()}, + {traffic_pp_sendto_recvfrom, [], traffic_pp_sendto_recvfrom_cases()}, + {traffic_pp_sendmsg_recvmsg, [], traffic_pp_sendmsg_recvmsg_cases()}, + {ttest, [], ttest_cases()}, + {ttest_sgenf, [], ttest_sgenf_cases()}, + {ttest_sgenf_cgen, [], ttest_sgenf_cgen_cases()}, + {ttest_sgenf_cgenf, [], ttest_sgenf_cgenf_cases()}, + {ttest_sgenf_cgeno, [], ttest_sgenf_cgeno_cases()}, + {ttest_sgenf_cgent, [], ttest_sgenf_cgent_cases()}, + {ttest_sgenf_csock, [], ttest_sgenf_csock_cases()}, + {ttest_sgenf_csockf, [], ttest_sgenf_csockf_cases()}, + {ttest_sgenf_csocko, [], ttest_sgenf_csocko_cases()}, + {ttest_sgenf_csockt, [], ttest_sgenf_csockt_cases()}, + {ttest_sgeno, [], ttest_sgeno_cases()}, + {ttest_sgeno_cgen, [], ttest_sgeno_cgen_cases()}, + {ttest_sgeno_cgenf, [], ttest_sgeno_cgenf_cases()}, + {ttest_sgeno_cgeno, [], ttest_sgeno_cgeno_cases()}, + {ttest_sgeno_cgent, [], ttest_sgeno_cgent_cases()}, + {ttest_sgeno_csock, [], ttest_sgeno_csock_cases()}, + {ttest_sgeno_csockf, [], ttest_sgeno_csockf_cases()}, + {ttest_sgeno_csocko, [], ttest_sgeno_csocko_cases()}, + {ttest_sgeno_csockt, [], ttest_sgeno_csockt_cases()}, + {ttest_sgent, [], ttest_sgent_cases()}, + {ttest_sgent_cgen, [], ttest_sgent_cgen_cases()}, + {ttest_sgent_cgenf, [], ttest_sgent_cgenf_cases()}, + {ttest_sgent_cgeno, [], ttest_sgent_cgeno_cases()}, + {ttest_sgent_cgent, [], ttest_sgent_cgent_cases()}, + {ttest_sgent_csock, [], ttest_sgent_csock_cases()}, + {ttest_sgent_csockf, [], ttest_sgent_csockf_cases()}, + {ttest_sgent_csocko, [], ttest_sgent_csocko_cases()}, + {ttest_sgent_csockt, [], ttest_sgent_csockt_cases()}, + {ttest_ssockf, [], ttest_ssockf_cases()}, + {ttest_ssockf_cgen, [], ttest_ssockf_cgen_cases()}, + {ttest_ssockf_cgenf, [], ttest_ssockf_cgenf_cases()}, + {ttest_ssockf_cgeno, [], ttest_ssockf_cgeno_cases()}, + {ttest_ssockf_cgent, [], ttest_ssockf_cgent_cases()}, + {ttest_ssockf_csock, [], ttest_ssockf_csock_cases()}, + {ttest_ssockf_csockf, [], ttest_ssockf_csockf_cases()}, + {ttest_ssockf_csocko, [], ttest_ssockf_csocko_cases()}, + {ttest_ssockf_csockt, [], ttest_ssockf_csockt_cases()}, + {ttest_ssocko, [], ttest_ssocko_cases()}, + {ttest_ssocko_cgen, [], ttest_ssocko_cgen_cases()}, + {ttest_ssocko_cgenf, [], ttest_ssocko_cgenf_cases()}, + {ttest_ssocko_cgeno, [], ttest_ssocko_cgeno_cases()}, + {ttest_ssocko_cgent, [], ttest_ssocko_cgent_cases()}, + {ttest_ssocko_csock, [], ttest_ssocko_csock_cases()}, + {ttest_ssocko_csockf, [], ttest_ssocko_csockf_cases()}, + {ttest_ssocko_csocko, [], ttest_ssocko_csocko_cases()}, + {ttest_ssocko_csockt, [], ttest_ssocko_csockt_cases()}, + {ttest_ssockt, [], ttest_ssockt_cases()}, + {ttest_ssockt_cgen, [], ttest_ssockt_cgen_cases()}, + {ttest_ssockt_cgenf, [], ttest_ssockt_cgenf_cases()}, + {ttest_ssockt_cgeno, [], ttest_ssockt_cgeno_cases()}, + {ttest_ssockt_cgent, [], ttest_ssockt_cgent_cases()}, + {ttest_ssockt_csock, [], ttest_ssockt_csock_cases()}, + {ttest_ssockt_csockf, [], ttest_ssockt_csockf_cases()}, + {ttest_ssockt_csocko, [], ttest_ssockt_csocko_cases()}, + {ttest_ssockt_csockt, [], ttest_ssockt_csockt_cases()} %% {tickets, [], ticket_cases()} ]. @@ -588,10 +647,16 @@ api_basic_cases() -> [ api_b_open_and_close_udp4, api_b_open_and_close_tcp4, + api_b_open_and_close_udpL, + api_b_open_and_close_tcpL, api_b_sendto_and_recvfrom_udp4, + api_b_sendto_and_recvfrom_udpL, api_b_sendmsg_and_recvmsg_udp4, + api_b_sendmsg_and_recvmsg_udpL, api_b_send_and_recv_tcp4, - api_b_sendmsg_and_recvmsg_tcp4 + api_b_send_and_recv_tcpL, + api_b_sendmsg_and_recvmsg_tcp4, + api_b_sendmsg_and_recvmsg_tcpL ]. api_options_cases() -> @@ -638,13 +703,15 @@ socket_closure_cases() -> ]. %% These cases are all about socket cleanup after the controlling process -%% exits *without* calling socket:close/1. +%% exits *without* explicitly calling socket:close/1. sc_cp_exit_cases() -> [ sc_cpe_socket_cleanup_tcp4, sc_cpe_socket_cleanup_tcp6, + sc_cpe_socket_cleanup_tcpL, sc_cpe_socket_cleanup_udp4, - sc_cpe_socket_cleanup_udp6 + sc_cpe_socket_cleanup_udp6, + sc_cpe_socket_cleanup_udpL ]. %% These cases tests what happens when the socket is closed locally. @@ -652,17 +719,22 @@ sc_lc_cases() -> [ sc_lc_recv_response_tcp4, sc_lc_recv_response_tcp6, + sc_lc_recv_response_tcpL, sc_lc_recvfrom_response_udp4, sc_lc_recvfrom_response_udp6, + sc_lc_recvfrom_response_udpL, sc_lc_recvmsg_response_tcp4, sc_lc_recvmsg_response_tcp6, + sc_lc_recvmsg_response_tcpL, sc_lc_recvmsg_response_udp4, sc_lc_recvmsg_response_udp6, + sc_lc_recvmsg_response_udpL, sc_lc_acceptor_response_tcp4, - sc_lc_acceptor_response_tcp6 + sc_lc_acceptor_response_tcp6, + sc_lc_acceptor_response_tcpL ]. %% These cases tests what happens when the socket is closed remotely. @@ -670,9 +742,11 @@ sc_rc_cases() -> [ sc_rc_recv_response_tcp4, sc_rc_recv_response_tcp6, + sc_rc_recv_response_tcpL, sc_rc_recvmsg_response_tcp4, - sc_rc_recvmsg_response_tcp6 + sc_rc_recvmsg_response_tcp6, + sc_rc_recvmsg_response_tcpL ]. %% These cases tests what happens when the socket is shutdown/closed remotely @@ -681,43 +755,72 @@ sc_rs_cases() -> [ sc_rs_recv_send_shutdown_receive_tcp4, sc_rs_recv_send_shutdown_receive_tcp6, + sc_rs_recv_send_shutdown_receive_tcpL, sc_rs_recvmsg_send_shutdown_receive_tcp4, - sc_rs_recvmsg_send_shutdown_receive_tcp6 + sc_rs_recvmsg_send_shutdown_receive_tcp6, + sc_rs_recvmsg_send_shutdown_receive_tcpL ]. traffic_cases() -> [ + {group, traffic_chunks}, + {group, traffic_pp_send_recv}, + {group, traffic_pp_sendto_recvfrom}, + {group, traffic_pp_sendmsg_recvmsg} + ]. + +traffic_chunks_cases() -> + [ traffic_send_and_recv_chunks_tcp4, traffic_send_and_recv_chunks_tcp6, + traffic_send_and_recv_chunks_tcpL + ]. +traffic_pp_send_recv_cases() -> + [ traffic_ping_pong_small_send_and_recv_tcp4, traffic_ping_pong_small_send_and_recv_tcp6, + traffic_ping_pong_small_send_and_recv_tcpL, traffic_ping_pong_medium_send_and_recv_tcp4, traffic_ping_pong_medium_send_and_recv_tcp6, + traffic_ping_pong_medium_send_and_recv_tcpL, traffic_ping_pong_large_send_and_recv_tcp4, traffic_ping_pong_large_send_and_recv_tcp6, + traffic_ping_pong_large_send_and_recv_tcpL + ]. +traffic_pp_sendto_recvfrom_cases() -> + [ traffic_ping_pong_small_sendto_and_recvfrom_udp4, traffic_ping_pong_small_sendto_and_recvfrom_udp6, + traffic_ping_pong_small_sendto_and_recvfrom_udpL, traffic_ping_pong_medium_sendto_and_recvfrom_udp4, traffic_ping_pong_medium_sendto_and_recvfrom_udp6, + traffic_ping_pong_medium_sendto_and_recvfrom_udpL + ]. +traffic_pp_sendmsg_recvmsg_cases() -> + [ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4, traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6, + traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6, + traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6, + traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL, traffic_ping_pong_small_sendmsg_and_recvmsg_udp4, traffic_ping_pong_small_sendmsg_and_recvmsg_udp6, + traffic_ping_pong_small_sendmsg_and_recvmsg_udpL, traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4, - traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6 + traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6, + traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL ]. - - + ttest_cases() -> [ %% Server: transport = gen_tcp, active = false @@ -1123,12 +1226,15 @@ ttest_ssockf_csockf_cases() -> [ ttest_ssockf_csockf_small_tcp4, ttest_ssockf_csockf_small_tcp6, + ttest_ssockf_csockf_small_tcpL, ttest_ssockf_csockf_medium_tcp4, ttest_ssockf_csockf_medium_tcp6, + ttest_ssockf_csockf_medium_tcpL, ttest_ssockf_csockf_large_tcp4, - ttest_ssockf_csockf_large_tcp6 + ttest_ssockf_csockf_large_tcp6, + ttest_ssockf_csockf_large_tcpL ]. %% Server: transport = socket(tcp), active = false @@ -1137,12 +1243,15 @@ ttest_ssockf_csocko_cases() -> [ ttest_ssockf_csocko_small_tcp4, ttest_ssockf_csocko_small_tcp6, + ttest_ssockf_csocko_small_tcpL, ttest_ssockf_csocko_medium_tcp4, ttest_ssockf_csocko_medium_tcp6, + ttest_ssockf_csocko_medium_tcpL, ttest_ssockf_csocko_large_tcp4, - ttest_ssockf_csocko_large_tcp6 + ttest_ssockf_csocko_large_tcp6, + ttest_ssockf_csocko_large_tcpL ]. %% Server: transport = socket(tcp), active = false @@ -1151,12 +1260,15 @@ ttest_ssockf_csockt_cases() -> [ ttest_ssockf_csockt_small_tcp4, ttest_ssockf_csockt_small_tcp6, + ttest_ssockf_csockt_small_tcpL, ttest_ssockf_csockt_medium_tcp4, ttest_ssockf_csockt_medium_tcp6, + ttest_ssockf_csockt_medium_tcpL, ttest_ssockf_csockt_large_tcp4, - ttest_ssockf_csockt_large_tcp6 + ttest_ssockf_csockt_large_tcp6, + ttest_ssockf_csockt_large_tcpL ]. %% Server: transport = socket(tcp), active = once @@ -1232,12 +1344,15 @@ ttest_ssocko_csockf_cases() -> [ ttest_ssocko_csockf_small_tcp4, ttest_ssocko_csockf_small_tcp6, + ttest_ssocko_csockf_small_tcpL, ttest_ssocko_csockf_medium_tcp4, ttest_ssocko_csockf_medium_tcp6, + ttest_ssocko_csockf_medium_tcpL, ttest_ssocko_csockf_large_tcp4, - ttest_ssocko_csockf_large_tcp6 + ttest_ssocko_csockf_large_tcp6, + ttest_ssocko_csockf_large_tcpL ]. %% Server: transport = socket(tcp), active = once @@ -1246,12 +1361,15 @@ ttest_ssocko_csocko_cases() -> [ ttest_ssocko_csocko_small_tcp4, ttest_ssocko_csocko_small_tcp6, + ttest_ssocko_csocko_small_tcpL, ttest_ssocko_csocko_medium_tcp4, ttest_ssocko_csocko_medium_tcp6, + ttest_ssocko_csocko_medium_tcpL, ttest_ssocko_csocko_large_tcp4, - ttest_ssocko_csocko_large_tcp6 + ttest_ssocko_csocko_large_tcp6, + ttest_ssocko_csocko_large_tcpL ]. %% Server: transport = socket(tcp), active = once @@ -1260,12 +1378,15 @@ ttest_ssocko_csockt_cases() -> [ ttest_ssocko_csockt_small_tcp4, ttest_ssocko_csockt_small_tcp6, + ttest_ssocko_csockt_small_tcpL, ttest_ssocko_csockt_medium_tcp4, ttest_ssocko_csockt_medium_tcp6, + ttest_ssocko_csockt_medium_tcpL, ttest_ssocko_csockt_large_tcp4, - ttest_ssocko_csockt_large_tcp6 + ttest_ssocko_csockt_large_tcp6, + ttest_ssocko_csockt_large_tcpL ]. %% Server: transport = socket(tcp), active = true @@ -1341,12 +1462,15 @@ ttest_ssockt_csockf_cases() -> [ ttest_ssockt_csockf_small_tcp4, ttest_ssockt_csockf_small_tcp6, + ttest_ssockt_csockf_small_tcpL, ttest_ssockt_csockf_medium_tcp4, ttest_ssockt_csockf_medium_tcp6, + ttest_ssockt_csockf_medium_tcpL, ttest_ssockt_csockf_large_tcp4, - ttest_ssockt_csockf_large_tcp6 + ttest_ssockt_csockf_large_tcp6, + ttest_ssockt_csockf_large_tcpL ]. %% Server: transport = socket(tcp), active = true @@ -1355,12 +1479,15 @@ ttest_ssockt_csocko_cases() -> [ ttest_ssockt_csocko_small_tcp4, ttest_ssockt_csocko_small_tcp6, + ttest_ssockt_csocko_small_tcpL, ttest_ssockt_csocko_medium_tcp4, ttest_ssockt_csocko_medium_tcp6, + ttest_ssockt_csocko_medium_tcpL, ttest_ssockt_csocko_large_tcp4, - ttest_ssockt_csocko_large_tcp6 + ttest_ssockt_csocko_large_tcp6, + ttest_ssockt_csocko_large_tcpL ]. %% Server: transport = socket(tcp), active = true @@ -1369,12 +1496,15 @@ ttest_ssockt_csockt_cases() -> [ ttest_ssockt_csockt_small_tcp4, ttest_ssockt_csockt_small_tcp6, + ttest_ssockt_csockt_small_tcpL, ttest_ssockt_csockt_medium_tcp4, ttest_ssockt_csockt_medium_tcp6, + ttest_ssockt_csockt_medium_tcpL, ttest_ssockt_csockt_large_tcp4, - ttest_ssockt_csockt_large_tcp6 + ttest_ssockt_csockt_large_tcp6, + ttest_ssockt_csockt_large_tcpL ]. %% ticket_cases() -> @@ -1510,6 +1640,46 @@ api_b_open_and_close_tcp4(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Basically open (create) and close an Unix Domain dgram (UDP) socket. +%% With some extra checks... +api_b_open_and_close_udpL(suite) -> + []; +api_b_open_and_close_udpL(doc) -> + []; +api_b_open_and_close_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_open_and_close_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => dgram, + protocol => default}, + ok = api_b_open_and_close(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically open (create) and close an Unix Domain stream (TCP) socket. +%% With some extra checks... +api_b_open_and_close_tcpL(suite) -> + []; +api_b_open_and_close_tcpL(doc) -> + []; +api_b_open_and_close_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_open_and_close_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => stream, + protocol => default}, + ok = api_b_open_and_close(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + api_b_open_and_close(InitState) -> Seq = [ @@ -1633,6 +1803,37 @@ api_b_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) -> socket:recvfrom(Sock) end, InitState = #{domain => inet, + proto => udp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket using +%% sendto and recvfrom. +api_b_sendto_and_recvfrom_udpL(suite) -> + []; +api_b_sendto_and_recvfrom_udpL(doc) -> + []; +api_b_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_sendto_and_recvfrom_udpL, + fun() -> + has_support_unix_domain_socket(), + unix_domain_socket_host_cond() + end, + fun() -> + Send = fun(Sock, Data, Dest) -> + socket:sendto(Sock, Data, Dest) + end, + Recv = fun(Sock) -> + socket:recvfrom(Sock) + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_udp(InitState) @@ -1665,17 +1866,66 @@ api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> end, Recv = fun(Sock) -> %% We have some issues on old darwing... - socket:setopt(Sock, otp, debug, true), + %% socket:setopt(Sock, otp, debug, true), case socket:recvmsg(Sock) of {ok, #{addr := Source, iov := [Data]}} -> - socket:setopt(Sock, otp, debug, false), + %% socket:setopt(Sock, otp, debug, false), {ok, {Source, Data}}; {error, _} = ERROR -> ERROR end end, InitState = #{domain => inet, + proto => udp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket +%% using sendmsg and recvmsg. +api_b_sendmsg_and_recvmsg_udpL(suite) -> + []; +api_b_sendmsg_and_recvmsg_udpL(doc) -> + []; +api_b_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_sendmsg_and_recvmsg_udpL, + fun() -> + has_support_unix_domain_socket(), + unix_domain_socket_host_cond() + end, + fun() -> + Send = fun(Sock, Data, Dest) -> + %% We need tests for this, + %% but this is not the place it. + %% CMsgHdr = #{level => ip, + %% type => tos, + %% data => reliability}, + %% CMsgHdrs = [CMsgHdr], + MsgHdr = #{addr => Dest, + %% ctrl => CMsgHdrs, + iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + %% We have some issues on old darwing... + %% socket:setopt(Sock, otp, debug, true), + case socket:recvmsg(Sock) of + {ok, #{addr := Source, + iov := [Data]}} -> + %% socket:setopt(Sock, otp, debug, false), + {ok, {Source, Data}}; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_udp(InitState) @@ -1688,42 +1938,64 @@ api_b_send_and_recv_udp(InitState) -> Seq = [ #{desc => "local address", - cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, - {ok, State#{lsa => LSA}} + cmd => fun(#{domain := local = Domain} = State) -> + LSASrc = which_local_socket_addr(Domain), + LSADst = which_local_socket_addr(Domain), + {ok, State#{lsa_src => LSASrc, + lsa_dst => LSADst}}; + (#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa_src => LSA, + lsa_dst => LSA}} end}, + #{desc => "open src socket", - cmd => fun(#{domain := Domain} = State) -> - Sock = sock_open(Domain, dgram, udp), - SASrc = sock_sockname(Sock), - {ok, State#{sock_src => Sock, sa_src => SASrc}} + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + Sock = sock_open(Domain, dgram, Proto), + {ok, State#{sock_src => Sock}} end}, #{desc => "bind src", - cmd => fun(#{sock_src := Sock, lsa := LSA}) -> - sock_bind(Sock, LSA), - ok + cmd => fun(#{sock_src := Sock, lsa_src := LSA}) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("src bound"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("src bind failed: ~p", [Reason]), + ERROR + end end}, #{desc => "sockname src socket", cmd => fun(#{sock_src := Sock} = State) -> SASrc = sock_sockname(Sock), - %% ei("src sockaddr: ~p", [SASrc]), + ?SEV_IPRINT("src sockaddr: " + "~n ~p", [SASrc]), {ok, State#{sa_src => SASrc}} end}, + #{desc => "open dst socket", - cmd => fun(#{domain := Domain} = State) -> - Sock = sock_open(Domain, dgram, udp), + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + Sock = sock_open(Domain, dgram, Proto), {ok, State#{sock_dst => Sock}} end}, #{desc => "bind dst", - cmd => fun(#{sock_dst := Sock, lsa := LSA}) -> - sock_bind(Sock, LSA), - ok + cmd => fun(#{sock_dst := Sock, lsa_dst := LSA}) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("src bound"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("src bind failed: ~p", [Reason]), + ERROR + end end}, #{desc => "sockname dst socket", cmd => fun(#{sock_dst := Sock} = State) -> SADst = sock_sockname(Sock), - %% ei("dst sockaddr: ~p", [SADst]), + ?SEV_IPRINT("dst sockaddr: " + "~n ~p", [SADst]), {ok, State#{sa_dst => SADst}} end}, #{desc => "send req (to dst)", @@ -1761,12 +2033,32 @@ api_b_send_and_recv_udp(InitState) -> end end}, #{desc => "close src socket", - cmd => fun(#{sock_src := Sock}) -> - ok = socket:close(Sock) + cmd => fun(#{domain := local, + sock_src := Sock, + lsa_src := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> maps:remove(lsa_src, State) end, + fun() -> State end), + {ok, maps:remove(sock_src, State1)}; + (#{sock_src := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock_src, State)} end}, #{desc => "close dst socket", - cmd => fun(#{sock_dst := Sock}) -> - ok = socket:close(Sock) + cmd => fun(#{domain := local, + sock_dst := Sock, + lsa_dst := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> maps:remove(lsa_dst, State) end, + fun() -> State end), + {ok, maps:remove(sock_dst, State1)}; + (#{sock_dst := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock_dst, State)} end}, %% *** We are done *** @@ -1796,6 +2088,34 @@ api_b_send_and_recv_tcp4(_Config) when is_list(_Config) -> socket:recv(Sock) end, InitState = #{domain => inet, + proto => tcp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the "common" functions (send and recv) +%% on an Unix Domain (stream) socket (TCP). +api_b_send_and_recv_tcpL(suite) -> + []; +api_b_send_and_recv_tcpL(doc) -> + []; +api_b_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_b_send_and_recv_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + Recv = fun(Sock) -> + socket:recv(Sock) + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_tcp(InitState) @@ -1828,6 +2148,56 @@ api_b_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) -> end end, InitState = #{domain => inet, + proto => tcp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the msg functions (sendmsg and recvmsg) +%% on an Unix Domain (stream) socket (TCP). +api_b_sendmsg_and_recvmsg_tcpL(suite) -> + []; +api_b_sendmsg_and_recvmsg_tcpL(doc) -> + []; +api_b_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_b_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Send = fun(Sock, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock) of + %% On some platforms, the address + %% is *not* provided (e.g. FreeBSD) + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + %% On some platforms, the address + %% *is* provided (e.g. linux) + {ok, #{addr := #{family := local}, + iov := [Data]}} -> + socket:setopt(Sock, + otp, + debug, + false), + {ok, Data}; + {error, _} = ERROR -> + socket:setopt(Sock, + otp, + debug, + false), + ERROR + end + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_tcp(InitState) @@ -1855,13 +2225,13 @@ api_b_send_and_recv_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -1869,9 +2239,19 @@ api_b_send_and_recv_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, lsa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -1882,7 +2262,12 @@ api_b_send_and_recv_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, lsa := #{path := Path}}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, lport := Port}) -> + %% This is actually not used for unix domain socket ?SEV_ANNOUNCE_READY(Tester, init, Port), ok end}, @@ -1946,12 +2331,29 @@ api_b_send_and_recv_tcp(InitState) -> end end}, #{desc => "close connection socket", - cmd => fun(#{csock := Sock}) -> - socket:close(Sock) + cmd => fun(#{csock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(csock, State)} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := Sock}) -> - socket:close(Sock) + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := LSock} = State) -> + case socket:close(LSock) of + ok -> + {ok, maps:remove(lsock, State)}; + {error, _} = ERROR -> + ERROR + end end}, %% *** We are done *** @@ -1962,7 +2364,10 @@ api_b_send_and_recv_tcp(InitState) -> [ %% *** Wait for start order *** #{desc => "await start (from tester)", - cmd => fun(State) -> + cmd => fun(#{domain := local} = State) -> + {Tester, Path} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_path => Path}}; + (State) -> {Tester, Port} = ?SEV_AWAIT_START(), {ok, State#{tester => Tester, server_port => Port}} end}, @@ -1974,16 +2379,20 @@ api_b_send_and_recv_tcp(InitState) -> %% *** The init part *** #{desc => "which server (local) address", - cmd => fun(#{domain := Domain, server_port := Port} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, - SSA = LSA#{port => Port}, + cmd => fun(#{domain := local = Domain, + server_path := Path} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = #{family => Domain, path => Path}, + {ok, State#{local_sa => LSA, server_sa => SSA}}; + (#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, {ok, State#{local_sa => LSA, server_sa => SSA}} end}, #{desc => "create socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -2054,8 +2463,20 @@ api_b_send_and_recv_tcp(InitState) -> end end}, #{desc => "close socket", - cmd => fun(#{sock := Sock}) -> - socket:close(Sock) + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} end}, %% *** We are done *** @@ -2530,8 +2951,7 @@ api_opt_simple_otp_rcvbuf_option() -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", @@ -2776,8 +3196,7 @@ api_opt_simple_otp_rcvbuf_option() -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create socket", @@ -3434,11 +3853,11 @@ api_to_connect_tcp4(suite) -> api_to_connect_tcp4(doc) -> []; api_to_connect_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), Cond = fun() -> api_to_connect_cond() end, tc_try(api_to_connect_tcp4, Cond, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet, backlog => 1, timeout => 5000, @@ -3454,7 +3873,9 @@ api_to_connect_cond() -> %% So, just to simplify, we require atleast 4.15 api_to_connect_cond({unix, linux}, {Maj, Min, _Rev}) -> if - ((Maj >= 4) andalso (Min >= 15)) -> + (Maj > 4) -> + ok; + ((Maj =:= 4) andalso (Min >= 15)) -> ok; true -> skip("TC does not work") @@ -3491,10 +3912,10 @@ api_to_connect_tcp6(suite) -> api_to_connect_tcp6(doc) -> []; api_to_connect_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_connect_tcp6, fun() -> has_support_ipv6(), api_to_connect_cond() end, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet6, backlog => 1, timeout => 5000, @@ -3531,8 +3952,7 @@ api_to_connect_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", @@ -3603,8 +4023,7 @@ api_to_connect_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create node", @@ -3754,8 +4173,7 @@ api_to_connect_tcp(InitState) -> end}, #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "order server start", @@ -3906,9 +4324,7 @@ api_toc_tcp_client_await_terminate(Parent) -> end. api_to_connect_tcp_await_timeout(To, ServerSA, Domain, ConLimit) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), NewSock = fun() -> S = case socket:open(Domain, stream, tcp) of {ok, Sock} -> @@ -3986,9 +4402,9 @@ api_to_accept_tcp4(suite) -> api_to_accept_tcp4(doc) -> []; api_to_accept_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_accept_tcp4, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet, timeout => 5000}, ok = api_to_accept_tcp(InitState) end). @@ -4003,10 +4419,10 @@ api_to_accept_tcp6(suite) -> api_to_accept_tcp6(doc) -> []; api_to_accept_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_accept_tcp4, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet6, timeout => 5000}, ok = api_to_accept_tcp(InitState) end). @@ -4020,8 +4436,7 @@ api_to_accept_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", @@ -4145,8 +4560,7 @@ api_to_maccept_tcp(InitState) -> end}, #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", @@ -4602,9 +5016,9 @@ api_to_recv_tcp4(suite) -> api_to_recv_tcp4(doc) -> []; api_to_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recv_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recv(Sock, 0, To) end, InitState = #{domain => inet, recv => Recv, @@ -4622,12 +5036,12 @@ api_to_recv_tcp6(suite) -> api_to_recv_tcp6(doc) -> []; api_to_recv_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recv_tcp6, fun() -> has_support_ipv6() end, fun() -> case socket:supports(ipv6) of true -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recv(Sock, 0, To) end, @@ -4663,8 +5077,7 @@ api_to_receive_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", @@ -4782,10 +5195,8 @@ api_to_receive_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain, server_port := Port} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, - SSA = LSA#{port => Port}, + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, {ok, State#{local_sa => LSA, server_sa => SSA}} end}, #{desc => "create socket", @@ -4961,9 +5372,9 @@ api_to_recvfrom_udp4(suite) -> api_to_recvfrom_udp4(doc) -> []; api_to_recvfrom_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvfrom_udp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end, InitState = #{domain => inet, recv => Recv, @@ -4981,10 +5392,10 @@ api_to_recvfrom_udp6(suite) -> api_to_recvfrom_udp6(doc) -> []; api_to_recvfrom_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvfrom_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end, InitState = #{domain => inet6, recv => Recv, @@ -5001,8 +5412,7 @@ api_to_receive_udp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create socket", @@ -5078,9 +5488,9 @@ api_to_recvmsg_udp4(suite) -> api_to_recvmsg_udp4(doc) -> []; api_to_recvmsg_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_udp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet, recv => Recv, @@ -5098,10 +5508,10 @@ api_to_recvmsg_udp6(suite) -> api_to_recvmsg_udp6(doc) -> []; api_to_recvmsg_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet6, recv => Recv, @@ -5119,9 +5529,9 @@ api_to_recvmsg_tcp4(suite) -> api_to_recvmsg_tcp4(doc) -> []; api_to_recvmsg_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet, recv => Recv, @@ -5139,10 +5549,10 @@ api_to_recvmsg_tcp6(suite) -> api_to_recvmsg_tcp6(doc) -> []; api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet6, recv => Recv, @@ -5170,9 +5580,9 @@ sc_cpe_socket_cleanup_tcp4(suite) -> sc_cpe_socket_cleanup_tcp4(doc) -> []; sc_cpe_socket_cleanup_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), tc_try(sc_cpe_socket_cleanup_tcp4, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet, type => stream, protocol => tcp}, @@ -5190,10 +5600,10 @@ sc_cpe_socket_cleanup_tcp6(suite) -> sc_cpe_socket_cleanup_tcp6(doc) -> []; sc_cpe_socket_cleanup_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), tc_try(sc_cpe_socket_cleanup_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet6, type => stream, protocol => tcp}, @@ -5204,6 +5614,27 @@ sc_cpe_socket_cleanup_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sockets are cleaned up %% ("removed") when the controlling process terminates (without explicitly +%% calling the close function). For a Unix Domain (stream) socket (TCP). + +sc_cpe_socket_cleanup_tcpL(suite) -> + []; +sc_cpe_socket_cleanup_tcpL(doc) -> + []; +sc_cpe_socket_cleanup_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(sc_cpe_socket_cleanup_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => stream, + protocol => default}, + ok = sc_cpe_socket_cleanup(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% ("removed") when the controlling process terminates (without explicitly %% calling the close function). For a IPv4 UDP (dgram) socket. sc_cpe_socket_cleanup_udp4(suite) -> @@ -5211,9 +5642,9 @@ sc_cpe_socket_cleanup_udp4(suite) -> sc_cpe_socket_cleanup_udp4(doc) -> []; sc_cpe_socket_cleanup_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), tc_try(sc_cpe_socket_cleanup_udp4, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet, type => dgram, protocol => udp}, @@ -5232,10 +5663,10 @@ sc_cpe_socket_cleanup_udp6(suite) -> sc_cpe_socket_cleanup_udp6(doc) -> []; sc_cpe_socket_cleanup_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), tc_try(sc_cpe_socket_cleanup_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet6, type => dgram, protocol => udp}, @@ -5244,6 +5675,28 @@ sc_cpe_socket_cleanup_udp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% ("removed") when the controlling process terminates (without explicitly +%% calling the close function). For a Unix Domain (dgram) socket (UDP). + +sc_cpe_socket_cleanup_udpL(suite) -> + []; +sc_cpe_socket_cleanup_udpL(doc) -> + []; +sc_cpe_socket_cleanup_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(sc_cpe_socket_cleanup_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => dgram, + protocol => default}, + ok = sc_cpe_socket_cleanup(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_cpe_socket_cleanup(InitState) -> OwnerSeq = @@ -5386,12 +5839,11 @@ sc_lc_recv_response_tcp4(suite) -> sc_lc_recv_response_tcp4(doc) -> []; sc_lc_recv_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_recv_response_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -5408,13 +5860,12 @@ sc_lc_recv_response_tcp6(suite) -> sc_lc_recv_response_tcp6(doc) -> []; sc_lc_recv_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_recv_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -5422,6 +5873,28 @@ sc_lc_recv_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recv function. +%% Socket is Unix Domain (stream) socket. + +sc_lc_recv_response_tcpL(suite) -> + []; +sc_lc_recv_response_tcpL(doc) -> + []; +sc_lc_recv_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_lc_recv_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_lc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_lc_receive_response_tcp(InitState) -> %% This (acceptor) is the server that accepts connections. @@ -5444,15 +5917,13 @@ sc_lc_receive_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, - {ok, State#{local_sa => LSA}} + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", cmd => fun(#{domain := Domain, - type := Type, protocol := Proto} = State) -> - case socket:open(Domain, Type, Proto) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -5460,9 +5931,22 @@ sc_lc_receive_response_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + ?SEV_IPRINT("bind to LSA: " + "~n ~p", [LSA]), + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + lsa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -5473,7 +5957,12 @@ sc_lc_receive_response_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, + lsa := #{path := Path}}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, lport := Port}) -> ?SEV_ANNOUNCE_READY(Tester, init, Port), ok end}, @@ -5494,7 +5983,8 @@ sc_lc_receive_response_tcp(InitState) -> cmd => fun(#{lsock := LSock} = State) -> case socket:accept(LSock) of {ok, Sock} -> - ?SEV_IPRINT("connection accepted"), + ?SEV_IPRINT("connection accepted: " + "~n ~p", [socket:sockname(Sock)]), {ok, State#{csock => Sock}}; {error, _} = ERROR -> ERROR @@ -5525,9 +6015,8 @@ sc_lc_receive_response_tcp(InitState) -> ?SEV_AWAIT_CONTINUE(Tester, tester, close), ok end}, - #{desc => "close the connection socket", + #{desc => "close connection socket", cmd => fun(#{csock := Sock} = State) -> - %% ok = socket:setopt(Sock, otp, debug, true), case socket:close(Sock) of ok -> {ok, maps:remove(csock, State)}; @@ -5551,8 +6040,19 @@ sc_lc_receive_response_tcp(InitState) -> ERROR end end}, - #{desc => "close socket", - cmd => fun(#{lsock := Sock} = State) -> + #{desc => "close listen socket", + cmd => fun(#{domain := local, + lsock := Sock, + lsa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() ->maps:remove(lsa, State) end, + fun() -> State end), + State2 = maps:remove(lsock, State1), + State3 = maps:remove(lport, State2), + {ok, State3}; + (#{lsock := Sock} = State) -> case socket:close(Sock) of ok -> State1 = maps:remove(lsock, State), @@ -5667,15 +6167,13 @@ sc_lc_receive_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create socket", cmd => fun(#{domain := Domain, - type := Type, protocol := Proto} = State) -> - case socket:open(Domain, Type, Proto) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -5684,6 +6182,8 @@ sc_lc_receive_response_tcp(InitState) -> end}, #{desc => "bind socket to local address", cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + ?SEV_IPRINT("bind to LSA: " + "~n ~p", [LSA]), case socket:bind(Sock, LSA) of {ok, _} -> ok; @@ -5699,7 +6199,19 @@ sc_lc_receive_response_tcp(InitState) -> %% The actual test #{desc => "await continue (connect)", - cmd => fun(#{tester := Tester, local_sa := LSA} = State) -> + cmd => fun(#{domain := local = Domain, + tester := Tester} = State) -> + case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of + {ok, ServerPath} -> + ?SEV_IPRINT("Server Path: " + "~n ~s", [ServerPath]), + ServerSA = #{family => Domain, + path => ServerPath}, + {ok, State#{server_sa => ServerSA}}; + {error, _} = ERROR -> + ERROR + end; + (#{tester := Tester, local_sa := LSA} = State) -> case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of {ok, Port} -> ServerSA = LSA#{port => Port}, @@ -5729,7 +6241,18 @@ sc_lc_receive_response_tcp(InitState) -> end end}, #{desc => "close socket", - cmd => fun(#{sock := Sock} = State) -> + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + sock_close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> sock_close(Sock), {ok, maps:remove(sock, State)} end}, @@ -5776,8 +6299,8 @@ sc_lc_receive_response_tcp(InitState) -> #{desc => "await acceptor ready (init)", cmd => fun(#{acceptor := Pid} = State) -> case ?SEV_AWAIT_READY(Pid, acceptor, init) of - {ok, Port} -> - {ok, State#{lport => Port}}; + {ok, PortOrPath} -> + {ok, State#{server_info => PortOrPath}}; {error, _} = ERROR -> ERROR end @@ -5834,8 +6357,8 @@ sc_lc_receive_response_tcp(InitState) -> end}, ?SEV_SLEEP(?SECS(1)), #{desc => "order client to continue (connect)", - cmd => fun(#{client := Pid, lport := Port} = _State) -> - ?SEV_ANNOUNCE_CONTINUE(Pid, connect, Port), + cmd => fun(#{client := Pid, server_info := Info} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, connect, Info), ok end}, #{desc => "await acceptor ready (accept)", @@ -6002,12 +6525,11 @@ sc_lc_recvfrom_response_udp4(suite) -> sc_lc_recvfrom_response_udp4(doc) -> []; sc_lc_recvfrom_response_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_lc_recvfrom_response_udp4, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, InitState = #{domain => inet, - type => dgram, protocol => udp, recv => Recv}, ok = sc_lc_receive_response_udp(InitState) @@ -6024,13 +6546,36 @@ sc_lc_recvfrom_response_udp6(suite) -> sc_lc_recvfrom_response_udp6(doc) -> []; sc_lc_recvfrom_response_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_lc_recvfrom_response_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, - InitState = #{domain => inet6, - recv => Recv}, + InitState = #{domain => inet6, + protocol => udp, + recv => Recv}, + ok = sc_lc_receive_response_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recv function. +%% Socket is Unix Domainm (dgram) socket. + +sc_lc_recvfrom_response_udpL(suite) -> + []; +sc_lc_recvfrom_response_udpL(doc) -> + []; +sc_lc_recvfrom_response_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_lc_recvfrom_response_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, ok = sc_lc_receive_response_udp(InitState) end). @@ -6055,20 +6600,25 @@ sc_lc_receive_response_udp(InitState) -> %% *** Init part *** #{desc => "local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "open socket", - cmd => fun(#{domain := Domain} = State) -> - Sock = sock_open(Domain, dgram, udp), + cmd => fun(#{domain := Domain, protocol := Proto} = State) -> + Sock = sock_open(Domain, dgram, Proto), SA = sock_sockname(Sock), {ok, State#{sock => Sock, sa => SA}} end}, #{desc => "bind socket", cmd => fun(#{sock := Sock, local_sa := LSA}) -> - sock_bind(Sock, LSA), - ok + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("src bound"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("src bind failed: ~p", [Reason]), + ERROR + end end}, #{desc => "announce ready (init)", cmd => fun(#{tester := Tester, sock := Sock}) -> @@ -6107,7 +6657,18 @@ sc_lc_receive_response_udp(InitState) -> ok = ?SEV_AWAIT_CONTINUE(Tester, tester, close) end}, #{desc => "close socket", - cmd => fun(#{sock := Sock} = State) -> + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> case socket:close(Sock) of ok -> {ok, maps:remove(sock, State)}; @@ -6400,9 +6961,9 @@ sc_lc_receive_response_udp(InitState) -> i("start 'tester' evaluator"), TesterInitState = #{prim_server => PrimServer#ev.pid, - sec_server1 => SecServer1#ev.pid, - sec_server2 => SecServer2#ev.pid, - sec_server3 => SecServer3#ev.pid}, + sec_server1 => SecServer1#ev.pid, + sec_server2 => SecServer2#ev.pid, + sec_server3 => SecServer3#ev.pid}, Tester = ?SEV_START("tester", TesterSeq, TesterInitState), i("await evaluator"), @@ -6422,12 +6983,11 @@ sc_lc_recvmsg_response_tcp4(suite) -> sc_lc_recvmsg_response_tcp4(doc) -> []; sc_lc_recvmsg_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_recvmsg_response_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -6444,13 +7004,12 @@ sc_lc_recvmsg_response_tcp6(suite) -> sc_lc_recvmsg_response_tcp6(doc) -> []; sc_lc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_recvmsg_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -6460,6 +7019,28 @@ sc_lc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test what happens when a socket is %% locally closed while the process is calling the recvmsg function. +%% Socket is Unix Domain (stream) socket. + +sc_lc_recvmsg_response_tcpL(suite) -> + []; +sc_lc_recvmsg_response_tcpL(doc) -> + []; +sc_lc_recvmsg_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_recvmsg_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_lc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recvmsg function. %% Socket is IPv4. sc_lc_recvmsg_response_udp4(suite) -> @@ -6471,8 +7052,9 @@ sc_lc_recvmsg_response_udp4(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, - InitState = #{domain => inet, - recv => Recv}, + InitState = #{domain => inet, + protocol => udp, + recv => Recv}, ok = sc_lc_receive_response_udp(InitState) end). @@ -6492,8 +7074,32 @@ sc_lc_recvmsg_response_udp6(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, - InitState = #{domain => inet6, - recv => Recv}, + InitState = #{domain => inet6, + protocol => udp, + recv => Recv}, + ok = sc_lc_receive_response_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recvmsg function. +%% Socket is Unix Domain (dgram) socket. + +sc_lc_recvmsg_response_udpL(suite) -> + []; +sc_lc_recvmsg_response_udpL(doc) -> + []; +sc_lc_recvmsg_response_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_recvmsg_response_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, ok = sc_lc_receive_response_udp(InitState) end). @@ -6511,11 +7117,10 @@ sc_lc_acceptor_response_tcp4(suite) -> sc_lc_acceptor_response_tcp4(doc) -> []; sc_lc_acceptor_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_acceptor_response_tcp4, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet, - type => stream, protocol => tcp}, ok = sc_lc_acceptor_response_tcp(InitState) end). @@ -6533,18 +7138,39 @@ sc_lc_acceptor_response_tcp6(suite) -> sc_lc_acceptor_response_tcp6(doc) -> []; sc_lc_acceptor_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_acceptor_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), - InitState = #{domain => inet, - type => stream, + InitState = #{domain => inet6, protocol => tcp}, ok = sc_lc_acceptor_response_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the accept function. +%% We test what happens with a non-controlling_process also, since we +%% git the setup anyway. +%% Socket is Unix Domain (stream) socket. + +sc_lc_acceptor_response_tcpL(suite) -> + []; +sc_lc_acceptor_response_tcpL(doc) -> + []; +sc_lc_acceptor_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_lc_acceptor_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + protocol => default}, + ok = sc_lc_acceptor_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_lc_acceptor_response_tcp(InitState) -> PrimAcceptorSeq = @@ -6564,15 +7190,13 @@ sc_lc_acceptor_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", cmd => fun(#{domain := Domain, - type := Type, protocol := Proto} = State) -> - case socket:open(Domain, Type, Proto) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -6609,8 +7233,8 @@ sc_lc_acceptor_response_tcp(InitState) -> end end}, #{desc => "await connection", - cmd => fun(#{sock := Sock, timeout := Timeout} = _State) -> - case socket:accept(Sock, Timeout) of + cmd => fun(#{sock := LSock, timeout := Timeout} = _State) -> + case socket:accept(LSock, Timeout) of {error, timeout} -> ok; {ok, Sock} -> @@ -6631,7 +7255,25 @@ sc_lc_acceptor_response_tcp(InitState) -> ok = ?SEV_AWAIT_CONTINUE(Tester, tester, close) end}, #{desc => "close socket", - cmd => fun(#{sock := Sock} = State) -> + cmd => fun(#{domain := local, + sock := Sock, + lsa := #{path := Path}} = State) -> + case socket:close(Sock) of + ok -> + State1 = + unlink_path(Path, + fun() -> + maps:remove(lsa, State) + end, + fun() -> + State + end), + {ok, maps:remove(sock, State1)}; + {error, _} = ERROR -> + unlink_path(Path), + ERROR + end; + (#{sock := Sock} = State) -> case socket:close(Sock) of ok -> {ok, maps:remove(sock, State)}; @@ -6946,12 +7588,11 @@ sc_rc_recv_response_tcp4(suite) -> sc_rc_recv_response_tcp4(doc) -> []; sc_rc_recv_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recv_response_tcp4, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -6968,13 +7609,12 @@ sc_rc_recv_response_tcp6(suite) -> sc_rc_recv_response_tcp6(doc) -> []; sc_rc_recv_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recv_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -6982,6 +7622,28 @@ sc_rc_recv_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recv function. +%% Socket is Unix Domain (stream) socket. + +sc_rc_recv_response_tcpL(suite) -> + []; +sc_rc_recv_response_tcpL(doc) -> + []; +sc_rc_recv_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_rc_recv_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_rc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_rc_receive_response_tcp(InitState) -> %% Each connection are handled by handler processes. @@ -7004,13 +7666,12 @@ sc_rc_receive_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, protocol := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -7018,7 +7679,17 @@ sc_rc_receive_response_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> {ok, State#{lport => Port}}; @@ -7031,7 +7702,15 @@ sc_rc_receive_response_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, + local_sa := LSA}) -> + %% Actually we only need to send the path, + %% but to keep it simple, we send the "same" + %% as for non-local. + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -7213,7 +7892,25 @@ sc_rc_receive_response_tcp(InitState) -> {ok, State2} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := LSock} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := #{path := Path}} = State) -> + case socket:close(LSock) of + ok -> + State1 = + unlink_path(Path, + fun() -> + maps:remove(lsa, State) + end, + fun() -> + State + end), + {ok, maps:remove(lsock, State1)}; + {error, _} = ERROR -> + unlink_path(Path), + ERROR + end; + (#{lsock := LSock} = State) -> case socket:close(LSock) of ok -> {ok, maps:remove(lsock, State)}; @@ -7270,8 +7967,10 @@ sc_rc_receive_response_tcp(InitState) -> ok end}, #{desc => "order remote client to start", - cmd => fun(#{rclient := Client, server_sa := ServerSA}) -> - ?SEV_ANNOUNCE_START(Client, ServerSA), + cmd => fun(#{rclient := Client, + server_sa := ServerSA, + protocol := Proto}) -> + ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}), ok end}, #{desc => "await remote client ready", @@ -7672,16 +8371,16 @@ sc_rc_tcp_client_start(Node) -> sc_rc_tcp_client(Parent) -> sc_rc_tcp_client_init(Parent), - ServerSA = sc_rc_tcp_client_await_start(Parent), + {ServerSA, Proto} = sc_rc_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = sc_rc_tcp_client_create(Domain), - sc_rc_tcp_client_bind(Sock, Domain), + Sock = sc_rc_tcp_client_create(Domain, Proto), + Path = sc_rc_tcp_client_bind(Sock, Domain), sc_rc_tcp_client_announce_ready(Parent, init), sc_rc_tcp_client_await_continue(Parent, connect), sc_rc_tcp_client_connect(Sock, ServerSA), sc_rc_tcp_client_announce_ready(Parent, connect), sc_rc_tcp_client_await_continue(Parent, close), - sc_rc_tcp_client_close(Sock), + sc_rc_tcp_client_close(Sock, Path), sc_rc_tcp_client_announce_ready(Parent, close), Reason = sc_rc_tcp_client_await_terminate(Parent), ?SEV_IPRINT("terminate"), @@ -7697,9 +8396,9 @@ sc_rc_tcp_client_await_start(Parent) -> i("sc_rc_tcp_client_await_start -> entry"), ?SEV_AWAIT_START(Parent). -sc_rc_tcp_client_create(Domain) -> +sc_rc_tcp_client_create(Domain, Proto) -> i("sc_rc_tcp_client_create -> entry"), - case socket:open(Domain, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> case socket:getopt(Sock, otp, fd) of {ok, FD} -> @@ -7714,12 +8413,17 @@ sc_rc_tcp_client_create(Domain) -> sc_rc_tcp_client_bind(Sock, Domain) -> i("sc_rc_tcp_client_bind -> entry"), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; {error, Reason} -> exit({bind, Reason}) end. @@ -7741,13 +8445,17 @@ sc_rc_tcp_client_connect(Sock, ServerSA) -> exit({connect, Reason}) end. -sc_rc_tcp_client_close(Sock) -> +sc_rc_tcp_client_close(Sock, Path) -> i("sc_rc_tcp_client_close -> entry"), case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. sc_rc_tcp_client_await_terminate(Parent) -> @@ -7827,12 +8535,11 @@ sc_rc_recvmsg_response_tcp4(suite) -> sc_rc_recvmsg_response_tcp4(doc) -> []; sc_rc_recvmsg_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recvmsg_response_tcp4, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -7849,13 +8556,12 @@ sc_rc_recvmsg_response_tcp6(suite) -> sc_rc_recvmsg_response_tcp6(doc) -> []; sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recvmsg_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -7863,6 +8569,28 @@ sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recvmsg function. +%% Socket is Unix Domain (stream) socket. + +sc_rc_recvmsg_response_tcpL(suite) -> + []; +sc_rc_recvmsg_response_tcpL(doc) -> + []; +sc_rc_recvmsg_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_rc_recvmsg_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_rc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test what happens when a socket is %% remotely closed while the process is calling the recv function. %% The remote client sends data, then shutdown(write) and then the @@ -7872,6 +8600,7 @@ sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> %% To minimize the chance of "weirdness", we should really have test cases %% where the two sides of the connection is on different machines. But for %% now, we will make do with different VMs on the same host. +%% This would of course not work for Unix Domain sockets. %% sc_rs_recv_send_shutdown_receive_tcp4(suite) -> @@ -7879,9 +8608,9 @@ sc_rs_recv_send_shutdown_receive_tcp4(suite) -> sc_rs_recv_send_shutdown_receive_tcp4(doc) -> []; sc_rs_recv_send_shutdown_receive_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rs_recv_send_shutdown_receive_tcp4, fun() -> - ?TT(?SECS(30)), MsgData = ?DATA, Recv = fun(Sock) -> socket:recv(Sock) @@ -7890,6 +8619,7 @@ sc_rs_recv_send_shutdown_receive_tcp4(_Config) when is_list(_Config) -> socket:send(Sock, Data) end, InitState = #{domain => inet, + proto => tcp, recv => Recv, send => Send, data => MsgData}, @@ -7921,6 +8651,39 @@ sc_rs_recv_send_shutdown_receive_tcp6(_Config) when is_list(_Config) -> socket:send(Sock, Data) end, InitState = #{domain => inet6, + proto => tcp, + recv => Recv, + send => Send, + data => MsgData}, + ok = sc_rs_send_shutdown_receive_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recv function. +%% The remote client sends data, then shutdown(write) and then the +%% reader attempts a recv. +%% Socket is Unix Domain (stream) socket. + +sc_rs_recv_send_shutdown_receive_tcpL(suite) -> + []; +sc_rs_recv_send_shutdown_receive_tcpL(doc) -> + []; +sc_rs_recv_send_shutdown_receive_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_rs_recv_send_shutdown_receive_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + MsgData = ?DATA, + Recv = fun(Sock) -> + socket:recv(Sock) + end, + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + InitState = #{domain => local, + proto => default, recv => Recv, send => Send, data => MsgData}, @@ -7952,13 +8715,12 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> i("get local address for ~p", [Domain]), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -7966,9 +8728,19 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + local_sa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -7979,7 +8751,11 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -8090,7 +8866,18 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> {ok, State2} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := LSock} = State) -> + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := LSock} = State) -> case socket:close(LSock) of ok -> {ok, maps:remove(lsock, State)}; @@ -8148,8 +8935,10 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> ok end}, #{desc => "order remote client to start", - cmd => fun(#{rclient := Client, server_sa := ServerSA}) -> - ?SEV_ANNOUNCE_START(Client, ServerSA), + cmd => fun(#{rclient := Client, + proto := Proto, + server_sa := ServerSA}) -> + ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}), ok end}, #{desc => "await remote client ready", @@ -8479,12 +9268,14 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> i("start server evaluator"), ServerInitState = #{domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), recv => maps:get(recv, InitState)}, Server = ?SEV_START("server", ServerSeq, ServerInitState), i("start client evaluator"), ClientInitState = #{host => local_host(), domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), send => maps:get(send, InitState)}, Client = ?SEV_START("client", ClientSeq, ClientInitState), @@ -8506,10 +9297,10 @@ sc_rs_tcp_client_start(Node, Send) -> sc_rs_tcp_client(Parent, Send) -> sc_rs_tcp_client_init(Parent), - ServerSA = sc_rs_tcp_client_await_start(Parent), + {ServerSA, Proto} = sc_rs_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = sc_rs_tcp_client_create(Domain), - sc_rs_tcp_client_bind(Sock, Domain), + Sock = sc_rs_tcp_client_create(Domain, Proto), + Path = sc_rs_tcp_client_bind(Sock, Domain), sc_rs_tcp_client_announce_ready(Parent, init), sc_rs_tcp_client_await_continue(Parent, connect), sc_rs_tcp_client_connect(Sock, ServerSA), @@ -8521,7 +9312,7 @@ sc_rs_tcp_client(Parent, Send) -> sc_rs_tcp_client_shutdown(Sock), sc_rs_tcp_client_announce_ready(Parent, shutdown), sc_rs_tcp_client_await_continue(Parent, close), - sc_rs_tcp_client_close(Sock), + sc_rs_tcp_client_close(Sock, Path), sc_rs_tcp_client_announce_ready(Parent, close), Reason = sc_rs_tcp_client_await_terminate(Parent), ?SEV_IPRINT("terminate"), @@ -8537,9 +9328,9 @@ sc_rs_tcp_client_await_start(Parent) -> i("sc_rs_tcp_client_await_start -> entry"), ?SEV_AWAIT_START(Parent). -sc_rs_tcp_client_create(Domain) -> +sc_rs_tcp_client_create(Domain, Proto) -> i("sc_rs_tcp_client_create -> entry"), - case socket:open(Domain, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> Sock; {error, Reason} -> @@ -8548,12 +9339,17 @@ sc_rs_tcp_client_create(Domain) -> sc_rs_tcp_client_bind(Sock, Domain) -> i("sc_rs_tcp_client_bind -> entry"), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; {error, Reason} -> exit({bind, Reason}) end. @@ -8600,13 +9396,17 @@ sc_rs_tcp_client_shutdown(Sock) -> exit({shutdown, Reason}) end. -sc_rs_tcp_client_close(Sock) -> +sc_rs_tcp_client_close(Sock, Path) -> i("sc_rs_tcp_client_close -> entry"), case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. sc_rs_tcp_client_await_terminate(Parent) -> @@ -8711,12 +9511,11 @@ sc_rs_recvmsg_send_shutdown_receive_tcp4(_Config) when is_list(_Config) -> MsgHdr = #{iov => [Data]}, socket:sendmsg(Sock, MsgHdr) end, - InitState = #{domain => inet, - type => stream, - protocol => tcp, - recv => Recv, - send => Send, - data => MsgData}, + InitState = #{domain => inet, + proto => tcp, + recv => Recv, + send => Send, + data => MsgData}, ok = sc_rs_send_shutdown_receive_tcp(InitState) end). @@ -8748,15 +9547,62 @@ sc_rs_recvmsg_send_shutdown_receive_tcp6(_Config) when is_list(_Config) -> end end, Send = fun(Sock, Data) when is_binary(Data) -> - MsgHdr = #{iov => [Data]}, - socket:sendmsg(Sock, MsgHdr) + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) end, - InitState = #{domain => inet6, - type => stream, - protocol => tcp, - recv => Recv, - send => Send, - data => MsgData}, + InitState = #{domain => inet6, + proto => tcp, + recv => Recv, + send => Send, + data => MsgData}, + ok = sc_rs_send_shutdown_receive_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recvmsg function. +%% The remote client sends data, then shutdown(write) and then the +%% reader attempts a recv. +%% Socket is UNix Domain (stream) socket. + +sc_rs_recvmsg_send_shutdown_receive_tcpL(suite) -> + []; +sc_rs_recvmsg_send_shutdown_receive_tcpL(doc) -> + []; +sc_rs_recvmsg_send_shutdown_receive_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_rs_recvmsg_send_shutdown_receive_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + {ok, CWD} = file:get_cwd(), + ?SEV_IPRINT("CWD: ~s", [CWD]), + MsgData = ?DATA, + Recv = fun(Sock) -> + case socket:recvmsg(Sock) of + %% On some platforms, the address + %% is *not* provided (e.g. FreeBSD) + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + %% On some platforms, the address + %% *is* provided (e.g. linux) + {ok, #{addr := #{family := local}, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end + end, + Send = fun(Sock, Data) when is_binary(Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + InitState = #{domain => local, + proto => default, + recv => Recv, + send => Send, + data => MsgData}, ok = sc_rs_send_shutdown_receive_tcp(InitState) end). @@ -8773,10 +9619,11 @@ traffic_send_and_recv_chunks_tcp4(suite) -> traffic_send_and_recv_chunks_tcp4(doc) -> []; traffic_send_and_recv_chunks_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(traffic_send_and_recv_chunks_tcp4, fun() -> - ?TT(?SECS(30)), - InitState = #{domain => inet}, + InitState = #{domain => inet, + proto => tcp}, ok = traffic_send_and_recv_chunks_tcp(InitState) end). @@ -8794,11 +9641,34 @@ traffic_send_and_recv_chunks_tcp6(suite) -> traffic_send_and_recv_chunks_tcp6(doc) -> []; traffic_send_and_recv_chunks_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(traffic_send_and_recv_chunks_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(30)), - InitState = #{domain => inet6}, + InitState = #{domain => inet6, + proto => tcp}, + ok = traffic_send_and_recv_chunks_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% behave as expected when sending and/or reading chunks. +%% First send data in one "big" chunk, and read it in "small" chunks. +%% Second, send in a bunch of "small" chunks, and read in one "big" chunk. +%% Socket is UNix Domain (Stream) socket. + +traffic_send_and_recv_chunks_tcpL(suite) -> + []; +traffic_send_and_recv_chunks_tcpL(doc) -> + []; +traffic_send_and_recv_chunks_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(traffic_send_and_recv_chunks_tcp6, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default}, ok = traffic_send_and_recv_chunks_tcp(InitState) end). @@ -8823,13 +9693,12 @@ traffic_send_and_recv_chunks_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -8837,7 +9706,17 @@ traffic_send_and_recv_chunks_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + local_sa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> {ok, State#{lport => Port}}; @@ -8850,7 +9729,14 @@ traffic_send_and_recv_chunks_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, + local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, + local_sa := LSA, + lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -9045,9 +9931,20 @@ traffic_send_and_recv_chunks_tcp(InitState) -> {ok, maps:remove(csock, State)} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := Sock} = State) -> + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := Sock} = State) -> (catch socket:close(Sock)), - {ok, maps:remove(lsock, State)} + {ok, maps:remove(lsock, State)} end}, %% *** We are done *** @@ -9098,8 +9995,10 @@ traffic_send_and_recv_chunks_tcp(InitState) -> ok end}, #{desc => "order remote client to start", - cmd => fun(#{rclient := Client, server_sa := ServerSA}) -> - ?SEV_ANNOUNCE_START(Client, ServerSA), + cmd => fun(#{rclient := Client, + server_sa := ServerSA, + proto := Proto}) -> + ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}), ok end}, #{desc => "await remote client ready", @@ -9654,14 +10553,14 @@ traffic_snr_tcp_client_start(Node) -> erlang:spawn(Node, Fun). traffic_snr_tcp_client(Parent) -> - {Sock, ServerSA} = traffic_snr_tcp_client_init(Parent), + {Sock, ServerSA, Path} = traffic_snr_tcp_client_init(Parent), traffic_snr_tcp_client_announce_ready(Parent, init), traffic_snr_tcp_client_await_continue(Parent, connect), traffic_snr_tcp_client_connect(Sock, ServerSA), traffic_snr_tcp_client_announce_ready(Parent, connect), traffic_snr_tcp_client_send_loop(Parent, Sock), Reason = traffic_snr_tcp_client_await_terminate(Parent), - traffic_snr_tcp_client_close(Sock), + traffic_snr_tcp_client_close(Sock, Path), exit(Reason). @@ -9687,19 +10586,19 @@ traffic_snr_tcp_client_init(Parent) -> put(sname, "rclient"), ?SEV_IPRINT("init"), _MRef = erlang:monitor(process, Parent), - ServerSA = traffic_snr_tcp_client_await_start(Parent), + {ServerSA, Proto} = traffic_snr_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = traffic_snr_tcp_client_create(Domain), - traffic_snr_tcp_client_bind(Sock, Domain), - {Sock, ServerSA}. + Sock = traffic_snr_tcp_client_create(Domain, Proto), + Path = traffic_snr_tcp_client_bind(Sock, Domain), + {Sock, ServerSA, Path}. traffic_snr_tcp_client_await_start(Parent) -> i("traffic_snr_tcp_client_await_start -> entry"), ?SEV_AWAIT_START(Parent). -traffic_snr_tcp_client_create(Domain) -> +traffic_snr_tcp_client_create(Domain, Proto) -> i("traffic_snr_tcp_client_create -> entry"), - case socket:open(Domain, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> Sock; {error, Reason} -> @@ -9708,12 +10607,17 @@ traffic_snr_tcp_client_create(Domain) -> traffic_snr_tcp_client_bind(Sock, Domain) -> i("traffic_snr_tcp_client_bind -> entry"), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; {error, Reason} -> exit({bind, Reason}) end. @@ -9734,13 +10638,17 @@ traffic_snr_tcp_client_connect(Sock, ServerSA) -> exit({connect, Reason}) end. -traffic_snr_tcp_client_close(Sock) -> +traffic_snr_tcp_client_close(Sock, Path) -> i("traffic_snr_tcp_client_close -> entry"), case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. traffic_snr_tcp_client_await_terminate(Parent) -> @@ -9768,12 +10676,13 @@ traffic_ping_pong_small_send_and_recv_tcp4(suite) -> traffic_ping_pong_small_send_and_recv_tcp4(doc) -> []; traffic_ping_pong_small_send_and_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), Msg = l2b(?TPP_SMALL), Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_send_and_recv_tcp4, fun() -> - ?TT(?SECS(15)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9795,13 +10704,42 @@ traffic_ping_pong_small_send_and_recv_tcp6(suite) -> traffic_ping_pong_small_send_and_recv_tcp6(doc) -> []; traffic_ping_pong_small_send_and_recv_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), Msg = l2b(?TPP_SMALL), Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_send_and_recv_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(15)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_small_send_and_recv_tcpL(suite) -> + []; +traffic_ping_pong_small_send_and_recv_tcpL(doc) -> + []; +traffic_ping_pong_small_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_send_and_recv_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9828,6 +10766,7 @@ traffic_ping_pong_medium_send_and_recv_tcp4(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9855,6 +10794,36 @@ traffic_ping_pong_medium_send_and_recv_tcp6(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(30)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_medium_send_and_recv_tcpL(suite) -> + []; +traffic_ping_pong_medium_send_and_recv_tcpL(doc) -> + []; +traffic_ping_pong_medium_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_send_and_recv_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9876,12 +10845,13 @@ traffic_ping_pong_large_send_and_recv_tcp4(suite) -> traffic_ping_pong_large_send_and_recv_tcp4(doc) -> []; traffic_ping_pong_large_send_and_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), Msg = l2b(?TPP_LARGE), Num = ?TPP_LARGE_NUM, tc_try(traffic_ping_pong_large_send_and_recv_tcp4, fun() -> - ?TT(?SECS(45)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9909,11 +10879,53 @@ traffic_ping_pong_large_send_and_recv_tcp6(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(45)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'large' message test case, for UNix Domain (stream) socket. + +traffic_ping_pong_large_send_and_recv_tcpL(suite) -> + []; +traffic_ping_pong_large_send_and_recv_tcpL(doc) -> + []; +traffic_ping_pong_large_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), + Msg = l2b(?TPP_LARGE), + Num = ?TPP_LARGE_NUM, + tc_try(traffic_ping_pong_large_send_and_recv_tcpL, + fun() -> + has_support_unix_domain_socket(), + traffic_ping_pong_large_host_cond() + end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) end). +%% This test case is a bit extreme and fails on some hosts +%% (e.g. OpenIndiana Hipster), so exclude them. +traffic_ping_pong_large_host_cond() -> + traffic_ping_pong_large_host_cond(os:type(), os:version()). + +traffic_ping_pong_large_host_cond({unix, sunos}, _) -> + skip("TC does not work on platform"); +traffic_ping_pong_large_host_cond(_, _) -> + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -9936,6 +10948,7 @@ traffic_ping_pong_small_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(45)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -9956,12 +10969,43 @@ traffic_ping_pong_small_sendto_and_recvfrom_udp6(suite) -> traffic_ping_pong_small_sendto_and_recvfrom_udp6(doc) -> []; traffic_ping_pong_small_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), Msg = l2b(?TPP_SMALL), Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udp6, + fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(45)), - InitState = #{domain => inet, + InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendto and recvfrom +%% functions by repeatedly sending a meassage between two entities. +%% The same basic test case is used for two different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_small_sendto_and_recvfrom_udpL(suite) -> + []; +traffic_ping_pong_small_sendto_and_recvfrom_udpL(doc) -> + []; +traffic_ping_pong_small_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -9989,6 +11033,7 @@ traffic_ping_pong_medium_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(45)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -10016,6 +11061,36 @@ traffic_ping_pong_medium_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) fun() -> ?TT(?SECS(45)), InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendto and recvfrom +%% functions by repeatedly sending a meassage between two entities. +%% The same basic test case is used for two different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_medium_sendto_and_recvfrom_udpL(suite) -> + []; +traffic_ping_pong_medium_sendto_and_recvfrom_udpL(doc) -> + []; +traffic_ping_pong_medium_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_sendto_and_recvfrom_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(45)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -10043,6 +11118,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(20)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10070,6 +11146,35 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) fun() -> ?TT(?SECS(20)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(suite) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(doc) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(20)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10096,6 +11201,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10122,7 +11228,36 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) fun() -> has_support_ipv6() end, fun() -> ?TT(?SECS(20)), - InitState = #{domain => ine6, + InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(suite) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(doc) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(20)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10146,15 +11281,27 @@ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) Msg = l2b(?TPP_LARGE), Num = ?TPP_LARGE_NUM, tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4, + fun() -> traffic_ping_pong_large_sendmsg_and_recvmsg_cond() end, fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) end). +traffic_ping_pong_large_sendmsg_and_recvmsg_cond() -> + traffic_ping_pong_large_sendmsg_and_recvmsg_cond(os:type(), os:version()). + +traffic_ping_pong_large_sendmsg_and_recvmsg_cond({unix, linux}, {M, _, _}) + when (M < 3) -> + skip("TC may not work on this version"); +traffic_ping_pong_large_sendmsg_and_recvmsg_cond(_, _) -> + ok. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sendmsg and recvmsg functions %% by repeatedly sending a meassage between two entities. @@ -10172,10 +11319,43 @@ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) Msg = l2b(?TPP_LARGE), Num = ?TPP_LARGE_NUM, tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6, - fun() -> has_support_ipv6() end, + fun() -> + has_support_ipv6(), + traffic_ping_pong_large_sendmsg_and_recvmsg_cond() + end, fun() -> ?TT(?SECS(30)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'large' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(suite) -> + []; +traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(doc) -> + []; +traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_LARGE), + Num = ?TPP_LARGE_NUM, + tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(30)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10203,6 +11383,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(60)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10229,7 +11410,36 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) fun() -> has_support_ipv6() end, fun() -> ?TT(?SECS(30)), - InitState = #{domain => inet, + InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(suite) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(doc) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(30)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10256,6 +11466,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10282,7 +11493,37 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) fun() -> has_support_ipv6() end, fun() -> ?TT(?SECS(20)), - InitState = #{domain => ine6, + InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg +%% functions by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(suite) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(doc) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(20)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10302,14 +11543,26 @@ traffic_ping_pong_send_and_recv_tcp(InitState) -> }, traffic_ping_pong_send_and_receive_tcp(InitState2). -traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) -> - Send = fun(Sock, Data) when is_binary(Data) -> - MsgHdr = #{iov => [Data]}, - socket:sendmsg(Sock, MsgHdr); - (Sock, Data) when is_list(Data) -> %% We assume iovec... - MsgHdr = #{iov => Data}, - socket:sendmsg(Sock, MsgHdr) +traffic_ping_pong_sendmsg_and_recvmsg_tcp(#{domain := local} = InitState) -> + Recv = fun(Sock, Sz) -> + case socket:recvmsg(Sock, Sz, 0) of + %% On some platforms, the address + %% is *not* provided (e.g. FreeBSD) + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + %% On some platforms, the address + %% *is* provided (e.g. linux) + {ok, #{addr := #{family := local}, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end end, + InitState2 = InitState#{recv => Recv}, % Receive function + traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState2); +traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) -> Recv = fun(Sock, Sz) -> case socket:recvmsg(Sock, Sz, 0) of {ok, #{addr := undefined, @@ -10319,9 +11572,18 @@ traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) -> ERROR end end, - InitState2 = InitState#{send => Send, % Send function - recv => Recv % Receive function - }, + InitState2 = InitState#{recv => Recv}, % Receive function + traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState2). + +traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState) -> + Send = fun(Sock, Data) when is_binary(Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr); + (Sock, Data) when is_list(Data) -> %% We assume iovec... + MsgHdr = #{iov => Data}, + socket:sendmsg(Sock, MsgHdr) + end, + InitState2 = InitState#{send => Send}, % Send function traffic_ping_pong_send_and_receive_tcp(InitState2). @@ -10381,13 +11643,12 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -10395,9 +11656,19 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -10412,7 +11683,11 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -10523,9 +11798,20 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> {ok, State1} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := Sock} = State) -> + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + (catch socket:close(Sock)), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := Sock} = State) -> (catch socket:close(Sock)), - {ok, maps:remove(lsock, State)} + {ok, maps:remove(lsock, State)} end}, %% *** We are done *** @@ -10577,12 +11863,14 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> end}, #{desc => "order remote client to start", cmd => fun(#{rclient := RClient, + proto := Proto, server_sa := ServerSA, buf_init := BufInit, send := Send, recv := Recv}) -> ?SEV_ANNOUNCE_START(RClient, - {ServerSA, BufInit, Send, Recv}), + {ServerSA, Proto, BufInit, + Send, Recv}), ok end}, #{desc => "await remote client ready", @@ -10883,6 +12171,7 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> i("start server evaluator"), ServerInitState = #{domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), recv => maps:get(recv, InitState), send => maps:get(send, InitState), buf_init => maps:get(buf_init, InitState)}, @@ -10976,14 +12265,6 @@ tpp_tcp_handler_msg_exchange_loop(Sock, Send, Recv, N, Sent, Received, Start) -> ?SEV_EPRINT("send (~w): ~p", [N, SReason]), exit({send, SReason, N}) end; - %% {error, timeout} -> - %% ?SEV_IPRINT("timeout(~w) - try again", [N]), - %% case Send(Sock, list_to_binary("ping")) of - %% ok -> - %% exit({'ping-send', ok, N}); - %% {error, Reason} -> - %% exit({'ping-send', Reason, N}) - %% end; {error, closed} -> ?SEV_IPRINT("closed - we are done: ~w, ~w, ~w", [N, Sent, Received]), Stop = ?LIB:timestamp(), @@ -11002,10 +12283,10 @@ tpp_tcp_client_create(Node) -> tpp_tcp_client(Parent) -> tpp_tcp_client_init(Parent), - {ServerSA, BufInit, Send, Recv} = tpp_tcp_client_await_start(Parent), + {ServerSA, Proto, BufInit, Send, Recv} = tpp_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = tpp_tcp_client_sock_open(Domain, BufInit), - tpp_tcp_client_sock_bind(Sock, Domain), + Sock = tpp_tcp_client_sock_open(Domain, Proto, BufInit), + Path = tpp_tcp_client_sock_bind(Sock, Domain), tpp_tcp_client_announce_ready(Parent, init), tpp_tcp_client_await_continue(Parent, connect), tpp_tcp_client_sock_connect(Sock, ServerSA), @@ -11014,7 +12295,7 @@ tpp_tcp_client(Parent) -> Result = tpp_tcp_client_msg_exchange(Sock, Send, Recv, InitMsg, Num), tpp_tcp_client_announce_ready(Parent, send, Result), Reason = tpp_tcp_client_await_terminate(Parent), - tpp_tcp_client_sock_close(Sock), + tpp_tcp_client_sock_close(Sock, Path), ?SEV_IPRINT("terminating"), exit(Reason). @@ -11054,8 +12335,10 @@ tpp_tcp_client_await_terminate(Parent) -> ?SEV_IPRINT("await terminate"), case ?SEV_AWAIT_TERMINATE(Parent, parent) of ok -> - ok; + ?SEV_IPRINT("termination received: normal"), + normal; {error, Reason} -> + ?SEV_IPRINT("termination received: ~w", [Reason]), Reason end. @@ -11099,8 +12382,8 @@ tpp_tcp_client_msg_exchange_loop(Sock, Send, Recv, Data, exit({send, SReason, N}) end. -tpp_tcp_client_sock_open(Domain, BufInit) -> - case socket:open(Domain, stream, tcp) of +tpp_tcp_client_sock_open(Domain, Proto, BufInit) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> ok = BufInit(Sock), Sock; @@ -11109,14 +12392,19 @@ tpp_tcp_client_sock_open(Domain, BufInit) -> end. tpp_tcp_client_sock_bind(Sock, Domain) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; - {error, Reason} -> - exit({bind, Reason}) + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; + {error, Reason2} -> + exit({bind, Reason2}) end. tpp_tcp_client_sock_connect(Sock, ServerSA) -> @@ -11127,14 +12415,20 @@ tpp_tcp_client_sock_connect(Sock, ServerSA) -> exit({connect, Reason}) end. -tpp_tcp_client_sock_close(Sock) -> +tpp_tcp_client_sock_close(Sock, Path) -> case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. + + -define(TPP_REQUEST, 1). -define(TPP_REPLY, 2). @@ -11300,13 +12594,12 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, dgram, udp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, dgram, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -11314,7 +12607,15 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{sock := Sock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _} -> + ok; + {error, _} = ERROR -> + ERROR + end; + (#{sock := Sock, local_sa := LSA} = State) -> case socket:bind(Sock, LSA) of {ok, Port} -> {ok, State#{port => Port}}; @@ -11357,7 +12658,11 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> end end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, port := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, port := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -11396,6 +12701,19 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> ERROR end end}, + #{desc => "(maybe) unlink socket", + cmd => fun(#{domain := local, + local_sa := #{path := Path}} = State) -> + unlink_path(Path, + fun() -> + {ok, maps:remove(local_sa, State)} + end, + fun() -> + ok + end); + (_) -> + ok + end}, #{desc => "announce ready (close)", cmd => fun(#{tester := Tester} = _State) -> ?SEV_ANNOUNCE_READY(Tester, close), @@ -11492,11 +12810,13 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> #{desc => "order remote handler to start", cmd => fun(#{handler := Handler, server_sa := ServerSA, + proto := Proto, buf_init := BufInit, send := Send, recv := Recv}) -> ?SEV_ANNOUNCE_START(Handler, - {ServerSA, BufInit, Send, Recv}), + {ServerSA, Proto, BufInit, + Send, Recv}), ok end}, #{desc => "await (remote) handler ready", @@ -11741,6 +13061,7 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> i("start server evaluator"), ServerInitState = #{domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), recv => maps:get(recv, InitState), send => maps:get(send, InitState), buf_init => maps:get(buf_init, InitState)}, @@ -11841,12 +13162,12 @@ tpp_udp_client_handler_create(Node) -> tpp_udp_client_handler(Parent) -> tpp_udp_client_handler_init(Parent), ?SEV_IPRINT("await start command"), - {ServerSA, BufInit, Send, Recv} = tpp_udp_handler_await_start(Parent), + {ServerSA, Proto, BufInit, Send, Recv} = tpp_udp_handler_await_start(Parent), ?SEV_IPRINT("start command with" "~n ServerSA: ~p", [ServerSA]), Domain = maps:get(family, ServerSA), - Sock = tpp_udp_sock_open(Domain, BufInit), - tpp_udp_sock_bind(Sock, Domain), + Sock = tpp_udp_sock_open(Domain, Proto, BufInit), + Path = tpp_udp_sock_bind(Sock, Domain), ?SEV_IPRINT("announce ready", []), tpp_udp_handler_announce_ready(Parent, init), {InitMsg, Num} = tpp_udp_handler_await_continue(Parent, send), @@ -11859,7 +13180,7 @@ tpp_udp_client_handler(Parent) -> ?SEV_IPRINT("await terminate"), Reason = tpp_udp_handler_await_terminate(Parent), ?SEV_IPRINT("terminate with ~p", [Reason]), - tpp_udp_sock_close(Sock), + tpp_udp_sock_close(Sock, Path), ?SEV_IPRINT("terminating"), exit(Reason). @@ -11997,8 +13318,8 @@ tpp_udp_handler_await_terminate(Parent) -> end. -tpp_udp_sock_open(Domain, BufInit) -> - case socket:open(Domain, dgram, udp) of +tpp_udp_sock_open(Domain, Proto, BufInit) -> + case socket:open(Domain, dgram, Proto) of {ok, Sock} -> ok = BufInit(Sock), Sock; @@ -12007,9 +13328,7 @@ tpp_udp_sock_open(Domain, BufInit) -> end. tpp_udp_sock_bind(Sock, Domain) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> ok; @@ -12017,12 +13336,16 @@ tpp_udp_sock_bind(Sock, Domain) -> exit({bind, Reason}) end. -tpp_udp_sock_close(Sock) -> +tpp_udp_sock_close(Sock, Path) -> case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("Failed closing socket: " + "~n ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. @@ -15104,6 +16427,30 @@ ttest_ssockf_csockf_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = false +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockf_csockf_small_tcpL(suite) -> + []; +ttest_ssockf_csockf_small_tcpL(doc) -> + []; +ttest_ssockf_csockf_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_small_tcpL, + Runtime, + local, + sock, false, + sock, false, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = false %% Message Size: medium (=2) %% Domain: inet %% @@ -15152,6 +16499,30 @@ ttest_ssockf_csockf_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = false +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockf_csockf_medium_tcpL(suite) -> + []; +ttest_ssockf_csockf_medium_tcpL(doc) -> + []; +ttest_ssockf_csockf_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_medium_tcpL, + Runtime, + local, + sock, false, + sock, false, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = false %% Message Size: large (=3) %% Domain: inet %% @@ -15199,6 +16570,30 @@ ttest_ssockf_csockf_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = false +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockf_csockf_large_tcpL(suite) -> + []; +ttest_ssockf_csockf_large_tcpL(doc) -> + []; +ttest_ssockf_csockf_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_large_tcpL, + Runtime, + local, + sock, false, + sock, false, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = once %% Message Size: small (=1) %% Domain: inet @@ -15248,6 +16643,30 @@ ttest_ssockf_csocko_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = once +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockf_csocko_small_tcpL(suite) -> + []; +ttest_ssockf_csocko_small_tcpL(doc) -> + []; +ttest_ssockf_csocko_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_small_tcpL, + Runtime, + local, + sock, false, + sock, once, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = once %% Message Size: medium (=2) %% Domain: inet %% @@ -15296,6 +16715,30 @@ ttest_ssockf_csocko_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = once +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockf_csocko_medium_tcpL(suite) -> + []; +ttest_ssockf_csocko_medium_tcpL(doc) -> + []; +ttest_ssockf_csocko_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_medium_tcpL, + Runtime, + local, + sock, false, + sock, once, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = once %% Message Size: large (=3) %% Domain: inet %% @@ -15343,6 +16786,30 @@ ttest_ssockf_csocko_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = once +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockf_csocko_large_tcpL(suite) -> + []; +ttest_ssockf_csocko_large_tcpL(doc) -> + []; +ttest_ssockf_csocko_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_large_tcpL, + Runtime, + local, + sock, false, + sock, once, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = true %% Message Size: small (=1) %% Domain: inet @@ -15390,6 +16857,30 @@ ttest_ssockf_csockt_small_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = true +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockf_csockt_small_tcpL(suite) -> + []; +ttest_ssockf_csockt_small_tcpL(doc) -> + []; +ttest_ssockf_csockt_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_small_tcpL, + Runtime, + local, + sock, false, + sock, true, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = true %% Message Size: medium (=2) @@ -15440,6 +16931,30 @@ ttest_ssockf_csockt_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = true +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockf_csockt_medium_tcpL(suite) -> + []; +ttest_ssockf_csockt_medium_tcpL(doc) -> + []; +ttest_ssockf_csockt_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_medium_tcpL, + Runtime, + local, + sock, false, + sock, true, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = true %% Message Size: large (=3) %% Domain: inet %% @@ -15486,6 +17001,30 @@ ttest_ssockf_csockt_large_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = true +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockf_csockt_large_tcpL(suite) -> + []; +ttest_ssockf_csockt_large_tcpL(doc) -> + []; +ttest_ssockf_csockt_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_large_tcpL, + Runtime, + local, + sock, false, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = gen_tcp, Active = false %% Message Size: small (=1) @@ -15968,6 +17507,30 @@ ttest_ssocko_csockf_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = false +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssocko_csockf_small_tcpL(suite) -> + []; +ttest_ssocko_csockf_small_tcpL(doc) -> + []; +ttest_ssocko_csockf_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_small_tcpL, + Runtime, + local, + sock, once, + sock, false, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = false %% Message Size: medium (=2) %% Domain: inet %% @@ -16016,6 +17579,30 @@ ttest_ssocko_csockf_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = false +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssocko_csockf_medium_tcpL(suite) -> + []; +ttest_ssocko_csockf_medium_tcpL(doc) -> + []; +ttest_ssocko_csockf_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_medium_tcpL, + Runtime, + local, + sock, once, + sock, false, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = false %% Message Size: large (=3) %% Domain: inet %% @@ -16063,6 +17650,30 @@ ttest_ssocko_csockf_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = false +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssocko_csockf_large_tcpL(suite) -> + []; +ttest_ssocko_csockf_large_tcpL(doc) -> + []; +ttest_ssocko_csockf_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_large_tcpL, + Runtime, + local, + sock, once, + sock, false, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = once %% Message Size: small (=1) %% Domain: inet @@ -16112,6 +17723,30 @@ ttest_ssocko_csocko_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = once +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssocko_csocko_small_tcpL(suite) -> + []; +ttest_ssocko_csocko_small_tcpL(doc) -> + []; +ttest_ssocko_csocko_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_small_tcpL, + Runtime, + local, + sock, once, + sock, once, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = once %% Message Size: medium (=2) %% Domain: inet %% @@ -16160,6 +17795,30 @@ ttest_ssocko_csocko_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = once +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssocko_csocko_medium_tcpL(suite) -> + []; +ttest_ssocko_csocko_medium_tcpL(doc) -> + []; +ttest_ssocko_csocko_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_medium_tcpL, + Runtime, + local, + sock, once, + sock, once, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = once %% Message Size: large (=3) %% Domain: inet %% @@ -16207,6 +17866,30 @@ ttest_ssocko_csocko_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = once +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssocko_csocko_large_tcpL(suite) -> + []; +ttest_ssocko_csocko_large_tcpL(doc) -> + []; +ttest_ssocko_csocko_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_large_tcpL, + Runtime, + local, + sock, once, + sock, once, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = true %% Message Size: small (=1) %% Domain: inet @@ -16254,6 +17937,30 @@ ttest_ssocko_csockt_small_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = true +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssocko_csockt_small_tcpL(suite) -> + []; +ttest_ssocko_csockt_small_tcpL(doc) -> + []; +ttest_ssocko_csockt_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_small_tcpL, + Runtime, + local, + sock, once, + sock, true, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = true %% Message Size: medium (=2) @@ -16304,6 +18011,30 @@ ttest_ssocko_csockt_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = true +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssocko_csockt_medium_tcpL(suite) -> + []; +ttest_ssocko_csockt_medium_tcpL(doc) -> + []; +ttest_ssocko_csockt_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_medium_tcpL, + Runtime, + local, + sock, once, + sock, true, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = true %% Message Size: large (=3) %% Domain: inet %% @@ -16350,6 +18081,30 @@ ttest_ssocko_csockt_large_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = true +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssocko_csockt_large_tcpL(suite) -> + []; +ttest_ssocko_csockt_large_tcpL(doc) -> + []; +ttest_ssocko_csockt_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_large_tcpL, + Runtime, + local, + sock, once, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = gen_tcp, Active = false %% Message Size: small (=1) @@ -16832,6 +18587,30 @@ ttest_ssockt_csockf_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = false +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockt_csockf_small_tcpL(suite) -> + []; +ttest_ssockt_csockf_small_tcpL(doc) -> + []; +ttest_ssockt_csockf_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_small_tcpL, + Runtime, + local, + sock, true, + sock, false, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = false %% Message Size: medium (=2) %% Domain: inet %% @@ -16880,6 +18659,30 @@ ttest_ssockt_csockf_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = false +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockt_csockf_medium_tcpL(suite) -> + []; +ttest_ssockt_csockf_medium_tcpL(doc) -> + []; +ttest_ssockt_csockf_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_medium_tcpL, + Runtime, + local, + sock, true, + sock, false, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = false %% Message Size: large (=3) %% Domain: inet %% @@ -16927,6 +18730,30 @@ ttest_ssockt_csockf_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = false +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockt_csockf_large_tcpL(suite) -> + []; +ttest_ssockt_csockf_large_tcpL(doc) -> + []; +ttest_ssockt_csockf_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_large_tcpL, + Runtime, + local, + sock, true, + sock, false, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = once %% Message Size: small (=1) %% Domain: inet @@ -16976,6 +18803,30 @@ ttest_ssockt_csocko_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = once +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockt_csocko_small_tcpL(suite) -> + []; +ttest_ssockt_csocko_small_tcpL(doc) -> + []; +ttest_ssockt_csocko_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_small_tcpL, + Runtime, + local, + sock, true, + sock, once, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = once %% Message Size: medium (=2) %% Domain: inet %% @@ -17024,6 +18875,30 @@ ttest_ssockt_csocko_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = once +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockt_csocko_medium_tcpL(suite) -> + []; +ttest_ssockt_csocko_medium_tcpL(doc) -> + []; +ttest_ssockt_csocko_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_medium_tcpL, + Runtime, + local, + sock, true, + sock, once, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = once %% Message Size: large (=3) %% Domain: inet %% @@ -17071,6 +18946,30 @@ ttest_ssockt_csocko_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = once +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockt_csocko_large_tcpL(suite) -> + []; +ttest_ssockt_csocko_large_tcpL(doc) -> + []; +ttest_ssockt_csocko_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_large_tcpL, + Runtime, + local, + sock, true, + sock, once, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = true %% Message Size: small (=1) %% Domain: inet @@ -17118,6 +19017,30 @@ ttest_ssockt_csockt_small_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = true +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockt_csockt_small_tcpL(suite) -> + []; +ttest_ssockt_csockt_small_tcpL(doc) -> + []; +ttest_ssockt_csockt_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_small_tcpL, + Runtime, + local, + sock, true, + sock, true, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = true %% Message Size: medium (=2) @@ -17168,6 +19091,30 @@ ttest_ssockt_csockt_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = true +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockt_csockt_medium_tcpL(suite) -> + []; +ttest_ssockt_csockt_medium_tcpL(doc) -> + []; +ttest_ssockt_csockt_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_medium_tcpL, + Runtime, + local, + sock, true, + sock, true, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = true %% Message Size: large (=3) %% Domain: inet %% @@ -17212,6 +19159,30 @@ ttest_ssockt_csockt_large_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = true +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockt_csockt_large_tcpL(suite) -> + []; +ttest_ssockt_csockt_large_tcpL(doc) -> + []; +ttest_ssockt_csockt_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_large_tcpL, + Runtime, + local, + sock, true, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% which_ttest_runtime(Config) when is_list(Config) -> case lists:keysearch(esock_test_ttest_runtime, 1, Config) of @@ -17272,7 +19243,8 @@ ttest_tcp(TC, tc_try(TC, fun() -> if - (Domain =/= inet) -> has_support_ipv6(); + (Domain =:= local) -> has_support_unix_domain_socket(); + (Domain =:= inet6) -> has_support_ipv6(); true -> ok end end, @@ -17326,11 +19298,25 @@ ttest_tcp(InitState) -> ok end}, #{desc => "start ttest (remote) server", - cmd => fun(#{mod := Mod, + cmd => fun(#{domain := local = Domain, + mod := Mod, active := Active, node := Node} = State) -> - case ttest_tcp_server_start(Node, Mod, Active) of - {ok, {{Pid, _MRef}, {Addr, Port}}} -> + case ttest_tcp_server_start(Node, + Domain, Mod, Active) of + {ok, {{Pid, _}, Path}} -> + {ok, State#{rserver => Pid, + path => Path}}; + {error, _} = ERROR -> + ERROR + end; + (#{domain := Domain, + mod := Mod, + active := Active, + node := Node} = State) -> + case ttest_tcp_server_start(Node, + Domain, Mod, Active) of + {ok, {{Pid, _}, {Addr, Port}}} -> {ok, State#{rserver => Pid, addr => Addr, port => Port}}; @@ -17339,7 +19325,12 @@ ttest_tcp(InitState) -> end end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, + cmd => fun(#{domain := local, + tester := Tester, + path := Path}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, addr := Addr, port := Port}) -> ?SEV_ANNOUNCE_READY(Tester, init, {Addr, Port}), @@ -17394,8 +19385,14 @@ ttest_tcp(InitState) -> [ %% *** Wait for start order part *** #{desc => "await start", - cmd => fun(State) -> - {Tester, {ServerAddr, ServerPort}} = ?SEV_AWAIT_START(), + cmd => fun(#{domain := local} = State) -> + {Tester, ServerPath} = + ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, + server_path => ServerPath}}; + (State) -> + {Tester, {ServerAddr, ServerPort}} = + ?SEV_AWAIT_START(), {ok, State#{tester => Tester, server_addr => ServerAddr, server_port => ServerPort}} @@ -17436,7 +19433,32 @@ ttest_tcp(InitState) -> ok end}, #{desc => "start ttest (remote) client", - cmd => fun(#{node := Node, + cmd => fun(#{domain := local = Domain, + node := Node, + mod := Mod, + active := Active, + msg_id := MsgID, + max_outstanding := MaxOutstanding, + runtime := RunTime, + server_path := Path} = State) -> + Self = self(), + Notify = + fun(Result) -> + ?SEV_ANNOUNCE_READY(Self, ttest, Result) + end, + case ttest_tcp_client_start(Node, Notify, + Domain, Mod, + Path, + Active, + MsgID, MaxOutstanding, + RunTime) of + {ok, {Pid, _MRef}} -> + {ok, State#{rclient => Pid}}; + {error, _} = ERROR -> + ERROR + end; + (#{domain := Domain, + node := Node, mod := Mod, active := Active, msg_id := MsgID, @@ -17450,8 +19472,9 @@ ttest_tcp(InitState) -> ?SEV_ANNOUNCE_READY(Self, ttest, Result) end, case ttest_tcp_client_start(Node, Notify, - Mod, Active, - Addr, Port, + Domain, Mod, + {Addr, Port}, + Active, MsgID, MaxOutstanding, RunTime) of {ok, {Pid, _MRef}} -> @@ -17463,8 +19486,6 @@ ttest_tcp(InitState) -> #{desc => "await ttest ready", cmd => fun(#{tester := Tester, rclient := RClient} = State) -> - %% TTestResult = ?SEV_AWAIT_READY(RClient, rclient, ttest, - %% [{tester, Tester}]), case ?SEV_AWAIT_READY(RClient, rclient, ttest, [{tester, Tester}]) of {ok, Result} -> @@ -17535,8 +19556,13 @@ ttest_tcp(InitState) -> ok end}, #{desc => "await server ready (init)", - cmd => fun(#{server := Pid} = State) -> - {ok, {Addr, Port}} = ?SEV_AWAIT_READY(Pid, server, init), + cmd => fun(#{domain := local, + server := Pid} = State) -> + {ok, Path} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_path => Path}}; + (#{server := Pid} = State) -> + {ok, {Addr, Port}} = + ?SEV_AWAIT_READY(Pid, server, init), {ok, State#{server_addr => Addr, server_port => Port}} end}, @@ -17544,7 +19570,12 @@ ttest_tcp(InitState) -> %% Start the client #{desc => "order client start", - cmd => fun(#{client := Pid, + cmd => fun(#{domain := local, + client := Pid, + server_path := Path} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Path), + ok; + (#{client := Pid, server_addr := Addr, server_port := Port} = _State) -> ?SEV_ANNOUNCE_START(Pid, {Addr, Port}), @@ -17683,7 +19714,8 @@ ttest_tcp(InitState) -> Client = ?SEV_START("client", ClientSeq, ClientInitState), i("start 'tester' evaluator"), - TesterInitState = #{server => Server#ev.pid, + TesterInitState = #{domain => maps:get(domain, InitState), + server => Server#ev.pid, client => Client#ev.pid}, Tester = ?SEV_START("tester", TesterSeq, TesterInitState), @@ -17692,12 +19724,12 @@ ttest_tcp(InitState) -> -ttest_tcp_server_start(Node, gen, Active) -> +ttest_tcp_server_start(Node, _Domain, gen, Active) -> Transport = socket_test_ttest_tcp_gen, socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active); -ttest_tcp_server_start(Node, sock, Active) -> +ttest_tcp_server_start(Node, Domain, sock, Active) -> TransportMod = socket_test_ttest_tcp_socket, - Transport = {TransportMod, #{method => plain}}, + Transport = {TransportMod, #{domain => Domain, method => plain}}, socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active). ttest_tcp_server_stop(Pid) -> @@ -17705,26 +19737,26 @@ ttest_tcp_server_stop(Pid) -> ttest_tcp_client_start(Node, Notify, - gen, - Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> + _Domain, gen, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> Transport = socket_test_ttest_tcp_gen, socket_test_ttest_tcp_client:start_monitor(Node, Notify, Transport, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime); ttest_tcp_client_start(Node, Notify, - sock, - Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> + Domain, sock, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> TransportMod = socket_test_ttest_tcp_socket, - Transport = {TransportMod, #{method => plain}}, + Transport = {TransportMod, #{domain => Domain, method => plain}}, socket_test_ttest_tcp_client:start_monitor(Node, Notify, Transport, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime). @@ -17801,19 +19833,6 @@ sock_open(Domain, Type, Proto) -> end. -sock_bind(Sock, SockAddr) -> - try socket:bind(Sock, SockAddr) of - {ok, Port} -> - Port; - {error, Reason} -> - i("sock_bind -> error: ~p", [Reason]), - ?FAIL({bind, Reason}) - catch - C:E:S -> - i("sock_bind -> failed: ~p, ~p, ~p", [C, E, S]), - ?FAIL({bind, C, E, S}) - end. - sock_connect(Sock, SockAddr) -> try socket:connect(Sock, SockAddr) of ok -> @@ -17899,13 +19918,50 @@ local_host() -> end. +%% The point of this is to "ensure" that paths from different test runs +%% don't clash. +mk_unique_path() -> + [NodeName | _] = string:tokens(atom_to_list(node()), [$@]), + Path = ?LIB:f("/tmp/esock_~s_~w", [NodeName, erlang:system_time(nanosecond)]), + ensure_unique_path(Path). + +ensure_unique_path(Path) -> + case file:read_file_info(Path) of + {ok, _} -> % Ouch, append a unique ID and try again + ensure_unique_path(Path, 1); + {error, _} -> + %% We assume this means it does not exist yet... + %% If we have several process in paralell trying to create + %% (unique) path's, then we are in trouble. To *really* be + %% on the safe side we should have a (central) path registry... + Path + end. + +ensure_unique_path(Path, ID) when (ID < 100) -> % If this is not enough... + NewPath = ?LIB:f("~s_~w", [Path, ID]), + case file:read_file_info(NewPath) of + {ok, _} -> % Ouch, this also existed, increment and try again + ensure_unique_path(Path, ID + 1); + {error, _} -> % We assume this means it does not exist yet... + NewPath + end; +ensure_unique_path(_, _) -> + skip("Could not create unique path"). + + +which_local_socket_addr(local = Domain) -> + #{family => Domain, + path => mk_unique_path()}; + %% This gets the local address (not 127.0...) %% We should really implement this using the (new) net module, %% but until that gets the necessary functionality... -which_local_addr(Domain) -> +which_local_socket_addr(Domain) -> case inet:getifaddrs() of {ok, IFL} -> - which_addr(Domain, IFL); + Addr = which_addr(Domain, IFL), + #{family => Domain, + addr => Addr}; {error, Reason} -> ?FAIL({inet, getifaddrs, Reason}) end. @@ -17935,12 +19991,55 @@ which_addr2(Domain, [_|IFO]) -> +unlink_path(Path) -> + unlink_path(Path, fun() -> ok end, fun() -> ok end). + +unlink_path(Path, Success, Failure) when is_list(Path) andalso + is_function(Success, 0) andalso + is_function(Failure, 0) -> + ?SEV_IPRINT("try unlink path: " + "~n ~s", [Path]), + case os:cmd("unlink " ++ Path) of + "" -> + ?SEV_IPRINT("path unlinked: " + "~n Path: ~s", [Path]), + Success(); + Result -> + ?SEV_EPRINT("unlink maybe failed: " + "~n Path: ~s" + "~n Res: ~s", [Path, Result]), + Failure() + end; +unlink_path(_, _, _) -> + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Here are all the *general* test vase condition functions. +unix_domain_socket_host_cond() -> + unix_domain_socket_host_cond(os:type(), os:version()). + +unix_domain_socket_host_cond({unix, linux}, {M, _, _}) when (M < 3) -> + skip("TC may not work on this version"); +unix_domain_socket_host_cond(_, _) -> + ok. + +has_support_unix_domain_socket() -> + case os:type() of + {win32, _} -> + skip("Not supported"); + _ -> + case socket:supports(local) of + true -> + ok; + false -> + skip("Not supported") + end + end. + + %% The idea is that this function shall test if the test host has %% support for IPv6. If not, there is no point in running IPv6 tests. %% Currently we just skip. diff --git a/erts/emulator/test/socket_test_ttest_tcp_client.erl b/erts/emulator/test/socket_test_ttest_tcp_client.erl index 5efa3fe491..b5c5300fd0 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_client.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_client.erl @@ -42,16 +42,16 @@ -export([ %% These are for the test suite - start_monitor/6, start_monitor/7, start_monitor/9, + start_monitor/5, start_monitor/6, start_monitor/8, %% These are for starting in a shell when run "manually" - start/4, start/5, start/7, start/8, + start/3, start/4, start/6, start/7, stop/1 ]). %% Internal exports -export([ - do_start/10 + do_start/9 ]). -include_lib("kernel/include/inet.hrl"). @@ -80,25 +80,25 @@ %% ========================================================================== -start_monitor(Node, Notify, Transport, Active, Addr, Port) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, ?MSG_ID_DEFAULT). +start_monitor(Node, Notify, Transport, ServerInfo, Active) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, ?MSG_ID_DEFAULT). -start_monitor(Node, Notify, Transport, Active, Addr, Port, 1 = MsgID) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, MsgID, +start_monitor(Node, Notify, Transport, ServerInfo, Active, 1 = MsgID) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_1, ?RUNTIME_DEFAULT); -start_monitor(Node, Notify, Transport, Active, Addr, Port, 2 = MsgID) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, MsgID, +start_monitor(Node, Notify, Transport, ServerInfo, Active, 2 = MsgID) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_2, ?RUNTIME_DEFAULT); -start_monitor(Node, Notify, Transport, Active, Addr, Port, 3 = MsgID) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, MsgID, +start_monitor(Node, Notify, Transport, ServerInfo, Active, 3 = MsgID) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_3, ?RUNTIME_DEFAULT). -start_monitor(Node, Notify, Transport, Active, Addr, Port, +start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) when (Node =/= node()) -> Args = [false, self(), Notify, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime], + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime], case rpc:call(Node, ?MODULE, do_start, Args) of {badrpc, _} = Reason -> {error, Reason}; @@ -108,11 +108,11 @@ start_monitor(Node, Notify, Transport, Active, Addr, Port, {error, _} = ERROR -> ERROR end; -start_monitor(_, Notify, Transport, Active, Addr, Port, +start_monitor(_, Notify, Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> case do_start(false, self(), Notify, - Transport, Active, Addr, Port, + Transport, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) of {ok, Pid} -> MRef = erlang:monitor(process, Pid), @@ -122,50 +122,48 @@ start_monitor(_, Notify, Transport, Active, Addr, Port, end. -start(Transport, Active, Addr, Port) -> - start(Transport, Active, Addr, Port, ?MSG_ID_DEFAULT). +start(Transport, ServerInfo, Active) -> + start(Transport, ServerInfo, Active, ?MSG_ID_DEFAULT). -start(Transport, Active, Addr, Port, 1 = MsgID) -> +start(Transport, ServerInfo, Active, 1 = MsgID) -> start(false, - Transport, Active, Addr, Port, MsgID, + Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_1, ?RUNTIME_DEFAULT); -start(Transport, Active, Addr, Port, 2 = MsgID) -> +start(Transport, ServerInfo, Active, 2 = MsgID) -> start(false, - Transport, Active, Addr, Port, MsgID, + Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_2, ?RUNTIME_DEFAULT); -start(Transport, Active, Addr, Port, 3 = MsgID) -> +start(Transport, ServerInfo, Active, 3 = MsgID) -> start(false, - Transport, Active, Addr, Port, MsgID, + Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_3, ?RUNTIME_DEFAULT). -start(Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> start(false, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime). + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -start(Quiet, Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Quiet, Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> Notify = fun(R) -> present_results(R) end, do_start(Quiet, self(), Notify, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime). + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -spec do_start(Quiet, Parent, Notify, Transport, + ServerInfo, Active, - Addr, - Port, MsgID, MaxOutstanding, RunTime) -> {ok, Pid} | {error, Reason} when - Quiet :: pid(), + Quiet :: boolean(), Parent :: pid(), Notify :: function(), Transport :: atom() | tuple(), + ServerInfo :: {inet:ip_address(), inet:port_number()} | string(), Active :: active(), - Addr :: inet:ip_address(), - Port :: inet:port_number(), MsgID :: msg_id(), MaxOutstanding :: max_outstanding(), RunTime :: runtime(), @@ -174,14 +172,13 @@ start(Quiet, Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> do_start(Quiet, Parent, Notify, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) when is_boolean(Quiet) andalso is_pid(Parent) andalso is_function(Notify) andalso (is_atom(Transport) orelse is_tuple(Transport)) andalso (is_boolean(Active) orelse (Active =:= once)) andalso - is_tuple(Addr) andalso - (is_integer(Port) andalso (Port > 0)) andalso + (is_tuple(ServerInfo) orelse is_list(ServerInfo)) andalso (is_integer(MsgID) andalso (MsgID >= 1) andalso (MsgID =< 3)) andalso (is_integer(MaxOutstanding) andalso (MaxOutstanding > 0)) andalso (is_integer(RunTime) andalso (RunTime > 0)) -> @@ -191,7 +188,7 @@ do_start(Quiet, Starter, Parent, Notify, - Transport, Active, Addr, Port, + Transport, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) end, {Pid, MRef} = spawn_monitor(Init), @@ -217,25 +214,30 @@ stop(Pid) when is_pid(Pid) -> init(Quiet, Starter, Parent, Notify, - Transport, Active, Addr, Port, + Transport, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) -> if not Quiet -> ?I("init with" "~n Transport: ~p" "~n Active: ~p" - "~n Addr: ~s" - "~n Port: ~p" + "~n ServerInfo: ~s" "~n Msg ID: ~p (=> 16 + ~w bytes)" "~n Max Outstanding: ~p" "~n (Suggested) Run Time: ~p ms", - [Transport, Active, inet:ntoa(Addr), Port, + [Transport, Active, + case ServerInfo of + {Addr, Port} -> + ?F("Addr: ~s, Port: ~w", [inet:ntoa(Addr), Port]); + Path -> + Path + end, MsgID, size(which_msg_data(MsgID)), MaxOutstanding, RunTime]); true -> ok end, {Mod, Connect} = process_transport(Transport), - case Connect(Addr, Port) of + case Connect(ServerInfo) of {ok, Sock} -> if not Quiet -> ?I("connected"); true -> ok @@ -269,9 +271,15 @@ init(Quiet, end. process_transport(Mod) when is_atom(Mod) -> - {Mod, fun(A, P) -> Mod:connect(A, P) end}; -process_transport({Mod, Opts}) -> - {Mod, fun(A, P) -> Mod:connect(A, P, Opts) end}. + %% In this case we assume it to be a plain tcp socket + {Mod, fun({A, P}) -> Mod:connect(A, P) end}; +process_transport({Mod, #{domain := Domain} = Opts}) -> + Connect = + case Domain of + local -> fun(Path) -> Mod:connect(Path, Opts) end; + _ -> fun({A, P}) -> Mod:connect(A, P, Opts) end + end, + {Mod, Connect}. which_msg_data(1) -> ?MSG_DATA1; diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl index 0ec2e908d7..65a3a94d38 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl @@ -21,28 +21,28 @@ -module(socket_test_ttest_tcp_client_gen). -export([ - start/3, start/4, start/6, start/7, + start/2, start/3, start/5, start/6, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_gen). -start(Active, Addr, Port) -> - socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, Active, Addr, Port). +start(ServerInfo, Active) -> + socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, ServerInfo, Active). -start(Active, Addr, Port, MsgID) -> - socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, Active, Addr, Port, MsgID). +start(ServerInfo, Active, MsgID) -> + socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, ServerInfo, Active, MsgID). -start(Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> socket_test_ttest_tcp_client:start(false, ?TRANSPORT_MOD, - Active, Addr, Port, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -start(Quiet, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Quiet, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> socket_test_ttest_tcp_client:start(Quiet, ?TRANSPORT_MOD, - Active, Addr, Port, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). stop(Pid) -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl index acf2556793..ccace2a560 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl @@ -21,30 +21,89 @@ -module(socket_test_ttest_tcp_client_socket). -export([ - start/4, start/5, start/7, start/8, + start/3, start/4, start/6, start/7, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_socket). --define(MOD(M), {?TRANSPORT_MOD, #{method => Method}}). +-define(MOD(D, M), {?TRANSPORT_MOD, #{domain => D, method => M}}). -start(Method, Active, Addr, Port) -> - socket_test_ttest_tcp_client:start_monitor(?MOD(Method), Active, Addr, Port). +start(Method, ServerInfo, Active) + when is_list(ServerInfo) -> + Domain = local, + socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Method), + ServerInfo, Active); +start(Method, ServerInfo = {Addr, _}, Active) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + Domain = inet, + socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Method), + ServerInfo, Active); +start(Method, ServerInfo = {Addr, _}, Active) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, + socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Method), + ServerInfo, Active). -start(Method, Active, Addr, Port, MsgID) -> - socket_test_ttest_tcp_client:start(?MOD(Method), - Active, Addr, Port, MsgID). +start(Method, ServerInfo, Active, MsgID) + when is_list(ServerInfo) -> + %% This is just a simplification + Domain = local, + socket_test_ttest_tcp_client:start(?MOD(Domain, Method), + ServerInfo, Active, MsgID); +start(Method, ServerInfo = {Addr, _}, Active, MsgID) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + %% This is just a simplification + Domain = inet, + socket_test_ttest_tcp_client:start(?MOD(Domain, Method), + ServerInfo, Active, MsgID); +start(Method, ServerInfo = {Addr, _}, Active, MsgID) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, + socket_test_ttest_tcp_client:start(?MOD(Domain, Method), + ServerInfo, Active, MsgID). -start(Method, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Method, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) + when is_list(ServerInfo) -> + Domain = local, socket_test_ttest_tcp_client:start(false, - ?MOD(Method), - Active, Addr, Port, + ?MOD(Domain, Method), + ServerInfo, Active, + MsgID, MaxOutstanding, RunTime); +start(Method, ServerInfo = {Addr, _}, Active, MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + Domain = inet, + socket_test_ttest_tcp_client:start(false, + ?MOD(Domain, Method), + ServerInfo, Active, + MsgID, MaxOutstanding, RunTime); +start(Method, ServerInfo = {Addr, _}, Active, MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, + socket_test_ttest_tcp_client:start(false, + ?MOD(Domain, Method), + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -start(Quiet, Method, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Quiet, Method, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) + when is_list(ServerInfo) -> + Domain = local, + socket_test_ttest_tcp_client:start(Quiet, + ?MOD(Domain, Method), + ServerInfo, Active, + MsgID, MaxOutstanding, RunTime); +start(Quiet, Method, ServerInfo = {Addr, _}, Active, MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + Domain = inet, + socket_test_ttest_tcp_client:start(Quiet, + ?MOD(Domain, Method), + ServerInfo, Active, + MsgID, MaxOutstanding, RunTime); +start(Quiet, Method, ServerInfo = {Addr, _}, Active, MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, socket_test_ttest_tcp_client:start(Quiet, - ?MOD(Method), - Active, Addr, Port, + ?MOD(Domain, Method), + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). stop(Pid) -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_gen.erl index 604408c489..05b250e3d9 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_gen.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_gen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -24,9 +24,9 @@ accept/1, accept/2, active/2, close/1, - connect/2, + connect/2, connect/3, controlling_process/2, - listen/0, listen/1, + listen/0, listen/1, listen/2, peername/1, port/1, recv/2, recv/3, @@ -80,6 +80,13 @@ close(Sock) -> connect(Addr, Port) -> Opts = [binary, {packet, raw}, {active, false}, {buffer, 32*1024}], + do_connect(Addr, Port, Opts). + +connect(Addr, Port, #{domain := Domain}) -> + Opts = [Domain, binary, {packet, raw}, {active, false}, {buffer, 32*1024}], + do_connect(Addr, Port, Opts). + +do_connect(Addr, Port, Opts) -> case gen_tcp:connect(Addr, Port, Opts) of {ok, Sock} -> {ok, Sock}; @@ -95,8 +102,12 @@ controlling_process(Sock, NewPid) -> listen() -> listen(0). -listen(Port) when is_integer(Port) andalso (Port >= 0) -> - Opts = [binary, {ip, {0,0,0,0}}, {packet, raw}, {active, false}, +listen(Port) -> + listen(Port, #{domain => inet}). + +listen(Port, #{domain := Domain}) when is_integer(Port) andalso (Port >= 0) -> + Opts = [Domain, + binary, {ip, {0,0,0,0}}, {packet, raw}, {active, false}, {buffer, 32*1024}], case gen_tcp:listen(Port, Opts) of {ok, Sock} -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_server.erl b/erts/emulator/test/socket_test_ttest_tcp_server.erl index e8d626e3d8..e916fcb93e 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_server.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -96,8 +96,9 @@ do_start(Parent, Transport, Active) (is_atom(Transport) orelse is_tuple(Transport)) andalso (is_boolean(Active) orelse (Active =:= once)) -> Starter = self(), - ServerInit = fun() -> put(sname, "server"), - server_init(Starter, Parent, Transport, Active) + ServerInit = fun() -> + put(sname, "server"), + server_init(Starter, Parent, Transport, Active) end, {Pid, MRef} = spawn_monitor(ServerInit), receive @@ -126,17 +127,29 @@ server_init(Starter, Parent, Transport, Active) -> case Listen(0) of {ok, LSock} -> case Mod:port(LSock) of - {ok, Port} -> - Addr = which_addr(), % This is just for convenience - ?I("listening on:" - "~n Addr: ~p (~s)" - "~n Port: ~w" - "~n", [Addr, inet:ntoa(Addr), Port]), - Starter ! {?MODULE, self(), {ok, {Addr, Port}}}, + {ok, PortOrPath} -> + Result = + if + is_integer(PortOrPath) -> + %% This is just for convenience + Addr = which_addr(), + ?I("listening on:" + "~n Addr: ~p (~s)" + "~n Port: ~w" + "~n", [Addr, inet:ntoa(Addr), PortOrPath]), + {Addr, PortOrPath}; + is_list(PortOrPath) -> + ?I("listening on:" + "~n Path: ~s" + "~n", [PortOrPath]), + PortOrPath + end, + Starter ! {?MODULE, self(), {ok, Result}}, server_loop(#{parent => Parent, mod => Mod, active => Active, lsock => LSock, + port_or_path => PortOrPath, handlers => [], stats_interval => StatsInterval, %% Accumulation @@ -208,7 +221,9 @@ format_peername({Addr, Port}) -> ?F("~s (~s:~w)", [N, inet:ntoa(Addr), Port]); {error, _} -> ?F("~p, ~p", [Addr, Port]) - end. + end; +format_peername(Path) when is_list(Path) -> + Path. maybe_start_stats_timer(#{active := Active, stats_interval := Time}, Handler) when (Active =/= false) andalso (is_integer(Time) andalso (Time > 0)) -> @@ -219,7 +234,10 @@ maybe_start_stats_timer(_, _) -> start_stats_timer(Time, ProcStr, Pid) -> erlang:start_timer(Time, self(), {stats, Time, ProcStr, Pid}). -server_handle_message(#{parent := Parent, handlers := H} = State) -> +server_handle_message(#{mod := Mod, + lsock := LSock, + parent := Parent, + handlers := H} = State) -> receive {timeout, _TRef, {stats, Interval, ProcStr, Pid}} -> case server_handle_stats(ProcStr, Pid) of @@ -233,6 +251,7 @@ server_handle_message(#{parent := Parent, handlers := H} = State) -> {?MODULE, Ref, Parent, stop} -> reply(Parent, Ref, ok), lists:foreach(fun(P) -> handler_stop(P) end, H), + (catch Mod:close(LSock)), exit(normal); {'DOWN', _MRef, process, Pid, Reason} -> @@ -272,28 +291,26 @@ server_handle_handler_down(Pid, AccMCnt2 = AccMCnt + MCnt, AccBCnt2 = AccBCnt + BCnt, AccHCnt2 = AccHCnt + 1, - ?I("handler ~p (~w) done => accumulated results: " - "~n Run Time: ~s ms" + MsgCount2Str = + fun(RT, ART, MC, AMC) when (RT > 0) -> + ?F("~w => ~w (~w) msgs / ms", [MC, MC div RT, AMC div ART]); + (_, _, MC, AMC) -> + ?F("~w (~w)", [MC, AMC]) + end, + ByteCount2Str = + fun(RT, ART, BC, ABC) when (RT > 0) -> + ?F("~w => ~w (~w) bytes / ms", [BC, BC div RT, ABC div ART]); + (_, _, BC, ABC) -> + ?F("~w", [BC, ABC]) + end, + ?I("handler ~p (~w) done: " + "~n Run Time: ~s" "~n Message Count: ~s" "~n Byte Count: ~s", [Pid, AccHCnt2, - ?FORMAT_TIME(AccRunTime2), - if (AccRunTime2 > 0) -> - ?F("~w => ~w (~w) msgs / ms", - [AccMCnt2, - AccMCnt2 div AccRunTime2, - (AccMCnt2 div AccHCnt2) div AccRunTime2]); - true -> - ?F("~w", [AccMCnt2]) - end, - if (AccRunTime2 > 0) -> - ?F("~w => ~w (~w) bytes / ms", - [AccBCnt2, - AccBCnt2 div AccRunTime2, - (AccBCnt2 div AccHCnt2) div AccRunTime2]); - true -> - ?F("~w", [AccBCnt2]) - end]), + ?FORMAT_TIME(RunTime), + MsgCount2Str(RunTime, AccRunTime2, MCnt, AccMCnt2), + ByteCount2Str(RunTime, AccRunTime2, BCnt, AccBCnt2)]), State#{runtime => AccRunTime2, mcnt => AccMCnt2, bcnt => AccBCnt2, diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl index b1b31f5158..fdf40f1369 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,20 +21,18 @@ -module(socket_test_ttest_tcp_server_gen). -export([ - start/1, + start/1, start/2, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_gen). +-define(MOD(D), {?TRANSPORT_MOD, #{domain => D}}). start(Active) -> - socket_test_ttest_tcp_server:start(?TRANSPORT_MOD, Active). - %% {ok, {Pid, AddrPort}} -> - %% MRef = erlang:monitor(process, Pid), - %% {ok, {Pid, MRef, AddrPort}}; - %% {error, _} = ERROR -> - %% ERROR - %% end. + start(inet, Active). + +start(Domain, Active) -> + socket_test_ttest_tcp_server:start(?MOD(Domain), Active). stop(Pid) -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl index b7ea1e8e93..d1de230637 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,23 +21,18 @@ -module(socket_test_ttest_tcp_server_socket). -export([ - start/2, + start/3, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_socket). -%% -define(MOD(M), {?TRANSPORT_MOD, #{method => M, +%% -define(MOD(D, M), {?TRANSPORT_MOD, #{domain => D, +%% method => M, %% stats_interval => 10000}}). --define(MOD(M), {?TRANSPORT_MOD, #{method => M}}). +-define(MOD(D, M), {?TRANSPORT_MOD, #{domain => D, method => M}}). -start(Method, Active) -> - socket_test_ttest_tcp_server:start(?MOD(Method), Active). - %% {ok, {Pid, AddrPort}} -> - %% MRef = erlang:monitor(process, Pid), - %% {ok, {Pid, MRef, AddrPort}}; - %% {error, _} = ERROR -> - %% ERROR - %% end. +start(Method, Domain, Active) -> + socket_test_ttest_tcp_server:start(?MOD(Domain, Method), Active). stop(Pid) -> socket_test_ttest_tcp_server:stop(Pid). diff --git a/erts/emulator/test/socket_test_ttest_tcp_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_socket.erl index 0ae2412e4c..cf68bfe591 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_socket.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ accept/1, accept/2, active/2, close/1, - connect/2, connect/3, + connect/1, connect/2, connect/3, controlling_process/2, listen/0, listen/1, listen/2, port/1, @@ -36,6 +36,8 @@ ]). +-define(LIB, socket_test_lib). + -define(READER_RECV_TIMEOUT, 1000). -define(DATA_MSG(Sock, Method, Data), @@ -97,37 +99,85 @@ active(#{reader := Pid}, NewActive) close(#{sock := Sock, reader := Pid}) -> Pid ! {?MODULE, stop}, - socket:close(Sock). + Unlink = case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + fun() -> os:cmd("unlink " ++ Path), ok end; + _ -> + fun() -> ok end + end, + Res = socket:close(Sock), + Unlink(), + Res. %% Create a socket and connect it to a peer -connect(Addr, Port) -> - connect(Addr, Port, #{method => plain}). - -connect(Addr, Port, #{method := Method} = Opts) -> +connect(ServerPath) when is_list(ServerPath) -> + Domain = local, + ClientPath = mk_unique_path(), + LocalSA = #{family => Domain, + path => ClientPath}, + ServerSA = #{family => Domain, path => ServerPath}, + Opts = #{domain => Domain, + proto => default, + method => plain}, + Cleanup = fun() -> os:cmd("unlink " ++ ClientPath), ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts). + +connect(Addr, Port) when is_tuple(Addr) andalso is_integer(Port) -> + Domain = inet, + LocalSA = any, + ServerSA = #{family => Domain, + addr => Addr, + port => Port}, + Opts = #{domain => Domain, + proto => tcp, + method => plain}, + Cleanup = fun() -> ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts); +connect(ServerPath, + #{domain := local = Domain} = Opts) + when is_list(ServerPath) -> + ClientPath = mk_unique_path(), + LocalSA = #{family => Domain, + path => ClientPath}, + ServerSA = #{family => Domain, + path => ServerPath}, + Cleanup = fun() -> os:cmd("unlink " ++ ClientPath), ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts#{proto => default}). + +connect(Addr, Port, #{domain := Domain} = Opts) -> + LocalSA = any, + ServerSA = #{family => Domain, + addr => Addr, + port => Port}, + Cleanup = fun() -> ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts#{proto => tcp}). + +do_connect(LocalSA, ServerSA, Cleanup, #{domain := Domain, + proto := Proto, + method := Method} = Opts) -> try begin Sock = - case socket:open(inet, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, S} -> S; {error, OReason} -> throw({error, {open, OReason}}) end, - case socket:bind(Sock, any) of + case socket:bind(Sock, LocalSA) of {ok, _} -> ok; {error, BReason} -> (catch socket:close(Sock)), + Cleanup(), throw({error, {bind, BReason}}) end, - SA = #{family => inet, - addr => Addr, - port => Port}, - case socket:connect(Sock, SA) of + case socket:connect(Sock, ServerSA) of ok -> ok; {error, CReason} -> (catch socket:close(Sock)), + Cleanup(), throw({error, {connect, CReason}}) end, Self = self(), @@ -140,6 +190,9 @@ connect(Addr, Port, #{method := Method} = Opts) -> ERROR end. +mk_unique_path() -> + [NodeName | _] = string:tokens(atom_to_list(node()), [$@]), + ?LIB:f("/tmp/esock_~s_~w", [NodeName, erlang:system_time(nanosecond)]). maybe_start_stats_timer(#{stats_to := Pid, stats_interval := T}, @@ -163,37 +216,57 @@ controlling_process(#{sock := Sock, reader := Pid}, NewPid) -> %% Create a listen socket listen() -> - listen(0, #{method => plain}). - -listen(Port) -> - listen(Port, #{method => plain}). -listen(Port, #{method := Method} = Opts) - when (is_integer(Port) andalso (Port >= 0)) andalso - ((Method =:= plain) orelse (Method =:= msg)) -> + listen(0). + +listen(Port) when is_integer(Port) -> + listen(Port, #{domain => inet, method => plain}); +listen(Path) when is_list(Path) -> + listen(Path, #{domain => local, method => plain}). + +listen(0, #{domain := local} = Opts) -> + listen(mk_unique_path(), Opts); +listen(Path, #{domain := local = Domain} = Opts) + when is_list(Path) andalso (Path =/= []) -> + SA = #{family => Domain, + path => Path}, + Cleanup = fun() -> os:cmd("unlink " ++ Path), ok end, + do_listen(SA, Cleanup, Opts#{proto => default}); +listen(Port, #{domain := Domain} = Opts) + when is_integer(Port) andalso (Port >= 0) -> + %% Bind fills in the rest + SA = #{family => Domain, + port => Port}, + Cleanup = fun() -> ok end, + do_listen(SA, Cleanup, Opts#{proto => tcp}). + +do_listen(SA, + Cleanup, + #{domain := Domain, proto := Proto, method := Method} = Opts) + when (Method =:= plain) orelse (Method =:= msg) -> try begin - Sock = case socket:open(inet, stream, tcp) of + Sock = case socket:open(Domain, stream, Proto) of {ok, S} -> S; {error, OReason} -> throw({error, {open, OReason}}) end, - SA = #{family => inet, - port => Port}, case socket:bind(Sock, SA) of {ok, _} -> ok; {error, BReason} -> (catch socket:close(Sock)), + Cleanup(), throw({error, {bind, BReason}}) end, case socket:listen(Sock) of ok -> - ok; - {error, LReason} -> + ok; + {error, LReason} -> (catch socket:close(Sock)), - throw({error, {listen, LReason}}) - end, + Cleanup(), + throw({error, {listen, LReason}}) + end, {ok, #{sock => Sock, opts => Opts}} end catch @@ -204,6 +277,8 @@ listen(Port, #{method := Method} = Opts) port(#{sock := Sock}) -> case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + {ok, Path}; {ok, #{port := Port}} -> {ok, Port}; {error, _} = ERROR -> @@ -213,6 +288,8 @@ port(#{sock := Sock}) -> peername(#{sock := Sock}) -> case socket:peername(Sock) of + {ok, #{family := local, path := Path}} -> + {ok, Path}; {ok, #{addr := Addr, port := Port}} -> {ok, {Addr, Port}}; {error, _} = ERROR -> diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam Binary files differindex ef402b5fee..c74ce3ce2d 100644 --- a/erts/preloaded/ebin/atomics.beam +++ b/erts/preloaded/ebin/atomics.beam diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam Binary files differindex 674d0d27fa..2aec433bcb 100644 --- a/erts/preloaded/ebin/counters.beam +++ b/erts/preloaded/ebin/counters.beam diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam Binary files differindex 0313988e3e..0c032e8e91 100644 --- a/erts/preloaded/ebin/erl_init.beam +++ b/erts/preloaded/ebin/erl_init.beam diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex 661bcd8413..13fdd7908b 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam Binary files differindex ec4d6153d1..16e17b870b 100644 --- a/erts/preloaded/ebin/erl_tracer.beam +++ b/erts/preloaded/ebin/erl_tracer.beam diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex 62dc8702e7..866f9df79f 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam Binary files differindex 669149df82..d81bc08282 100644 --- a/erts/preloaded/ebin/erts_code_purger.beam +++ b/erts/preloaded/ebin/erts_code_purger.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam Binary files differindex 6d3528c2dc..a8d492dfa5 100644 --- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam +++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex b3af713809..dd08111aad 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam Binary files differindex fc2bf6f6bd..0f7dd6efbe 100644 --- a/erts/preloaded/ebin/erts_literal_area_collector.beam +++ b/erts/preloaded/ebin/erts_literal_area_collector.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex 1d89174b25..942f29a11c 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/net.beam b/erts/preloaded/ebin/net.beam Binary files differindex f61b2b4a69..88d546a3af 100644 --- a/erts/preloaded/ebin/net.beam +++ b/erts/preloaded/ebin/net.beam diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam Binary files differindex c882e4fad4..7871b64991 100644 --- a/erts/preloaded/ebin/persistent_term.beam +++ b/erts/preloaded/ebin/persistent_term.beam diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam Binary files differindex cf671bf8f4..90d9596fe6 100644 --- a/erts/preloaded/ebin/prim_buffer.beam +++ b/erts/preloaded/ebin/prim_buffer.beam diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam Binary files differindex 24911123f9..19935eeee3 100644 --- a/erts/preloaded/ebin/prim_eval.beam +++ b/erts/preloaded/ebin/prim_eval.beam diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex 0efd954e50..a2c5f2f336 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex ff9268ad38..b7c4cbe0a8 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam Binary files differindex d319d7a343..bd51c3b271 100644 --- a/erts/preloaded/ebin/prim_zip.beam +++ b/erts/preloaded/ebin/prim_zip.beam diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam Binary files differindex 558a886565..e37aa81b7c 100644 --- a/erts/preloaded/ebin/socket.beam +++ b/erts/preloaded/ebin/socket.beam diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex 9610c94ac2..8b7c5fe2ef 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 126db66cdd..0f0d8f7a02 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -602,12 +602,13 @@ %% -define(SOCKET_TYPE_RDM, 4). -define(SOCKET_TYPE_SEQPACKET, 5). --define(SOCKET_PROTOCOL_IP, 1). --define(SOCKET_PROTOCOL_TCP, 2). --define(SOCKET_PROTOCOL_UDP, 3). --define(SOCKET_PROTOCOL_SCTP, 4). --define(SOCKET_PROTOCOL_ICMP, 5). --define(SOCKET_PROTOCOL_IGMP, 6). +-define(SOCKET_PROTOCOL_DEFAULT, 0). +-define(SOCKET_PROTOCOL_IP, 1). +-define(SOCKET_PROTOCOL_TCP, 2). +-define(SOCKET_PROTOCOL_UDP, 3). +-define(SOCKET_PROTOCOL_SCTP, 4). +-define(SOCKET_PROTOCOL_ICMP, 5). +-define(SOCKET_PROTOCOL_IGMP, 6). -define(SOCKET_LISTEN_BACKLOG_DEFAULT, 5). @@ -820,6 +821,7 @@ -define(SOCKET_SUPPORTS_OPTIONS, 16#0001). -define(SOCKET_SUPPORTS_SCTP, 16#0002). -define(SOCKET_SUPPORTS_IPV6, 16#0003). +-define(SOCKET_SUPPORTS_LOCAL, 16#0004). %% =========================================================================== @@ -875,18 +877,21 @@ info() -> -spec supports() -> [{options, supports_options()} | {sctp, boolean()} | - {ipv6, boolean()}]. + {ipv6, boolean()} | + {local, boolean()}]. supports() -> [{options, supports(options)}, {sctp, supports(sctp)}, - {ipv6, supports(ipv6)}]. + {ipv6, supports(ipv6)}, + {local, supports(local)}]. -dialyzer({nowarn_function, supports/1}). -spec supports(options) -> supports_options(); (sctp) -> boolean(); (ipv6) -> boolean(); + (local) -> boolean(); (Key1) -> false when Key1 :: term(). @@ -896,6 +901,8 @@ supports(sctp) -> nif_supports(?SOCKET_SUPPORTS_SCTP); supports(ipv6) -> nif_supports(?SOCKET_SUPPORTS_IPV6); +supports(local) -> + nif_supports(?SOCKET_SUPPORTS_LOCAL); supports(_Key1) -> false. @@ -1006,12 +1013,12 @@ supports(_Key1, _Key2, _Key3) -> Reason :: term(). open(Domain, Type) -> - open(Domain, Type, null). + open(Domain, Type, default). -spec open(Domain, Type, Protocol) -> {ok, Socket} | {error, Reason} when Domain :: domain(), Type :: type(), - Protocol :: null | protocol(), + Protocol :: default | protocol(), Socket :: socket(), Reason :: term(). @@ -1021,15 +1028,14 @@ open(Domain, Type, Protocol) -> -spec open(Domain, Type, Protocol, Extra) -> {ok, Socket} | {error, Reason} when Domain :: domain(), Type :: type(), - Protocol :: null | protocol(), + Protocol :: default | protocol(), Extra :: map(), Socket :: socket(), Reason :: term(). -open(Domain, Type, Protocol0, Extra) when is_map(Extra) -> +open(Domain, Type, Protocol, Extra) when is_map(Extra) -> try begin - Protocol = default_protocol(Protocol0, Type), EDomain = enc_domain(Domain), EType = enc_type(Domain, Type), EProtocol = enc_protocol(Type, Protocol), @@ -1052,15 +1058,6 @@ open(Domain, Type, Protocol0, Extra) when is_map(Extra) -> {error, Reason} end. -%% Note that this is just a convenience function for when the protocol was -%% not specified. If its actually specified, then that will be selected. -%% Also, this only works for the some of the type's (stream, dgram and -%% seqpacket). -default_protocol(null, stream) -> tcp; -default_protocol(null, dgram) -> udp; -default_protocol(null, seqpacket) -> sctp; -default_protocol(null, Type) -> throw({error, {no_default_protocol, Type}}); -default_protocol(Protocol, _) -> Protocol. %% =========================================================================== @@ -2355,7 +2352,7 @@ peername(#socket{ref = SockRef}) -> enc_domain(local) -> ?SOCKET_DOMAIN_LOCAL; enc_domain(inet) -> ?SOCKET_DOMAIN_INET; enc_domain(inet6) -> ?SOCKET_DOMAIN_INET6; -enc_domain(Domain) -> throw({error, {invalid_domain, Domain}}). +enc_domain(Domain) -> invalid_domain(Domain). -spec enc_type(Domain, Type) -> non_neg_integer() when Domain :: domain(), @@ -2366,22 +2363,23 @@ enc_type(_, stream) -> ?SOCKET_TYPE_STREAM; enc_type(_, dgram) -> ?SOCKET_TYPE_DGRAM; enc_type(_, raw) -> ?SOCKET_TYPE_RAW; enc_type(_, seqpacket) -> ?SOCKET_TYPE_SEQPACKET; -enc_type(_, Type) -> throw({error, {invalid_type, Type}}). +enc_type(_, Type) -> invalid_type(Type). -spec enc_protocol(Type, Protocol) -> non_neg_integer() | {raw, non_neg_integer()} when Type :: type(), Protocol :: protocol(). -enc_protocol(dgram, ip) -> ?SOCKET_PROTOCOL_IP; -enc_protocol(stream, tcp) -> ?SOCKET_PROTOCOL_TCP; -enc_protocol(dgram, udp) -> ?SOCKET_PROTOCOL_UDP; -enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; -enc_protocol(raw, icmp) -> ?SOCKET_PROTOCOL_ICMP; -enc_protocol(raw, igmp) -> ?SOCKET_PROTOCOL_IGMP; +enc_protocol(_, default) -> ?SOCKET_PROTOCOL_DEFAULT; +enc_protocol(dgram, ip) -> ?SOCKET_PROTOCOL_IP; +enc_protocol(stream, tcp) -> ?SOCKET_PROTOCOL_TCP; +enc_protocol(dgram, udp) -> ?SOCKET_PROTOCOL_UDP; +enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; +enc_protocol(raw, icmp) -> ?SOCKET_PROTOCOL_ICMP; +enc_protocol(raw, igmp) -> ?SOCKET_PROTOCOL_IGMP; enc_protocol(raw, {raw, P} = RAW) when is_integer(P) -> RAW; enc_protocol(Type, Proto) -> - throw({error, {invalid_protocol, {Type, Proto}}}). + invalid_protocol(Type, Proto). -spec enc_send_flags(Flags) -> non_neg_integer() when @@ -2532,7 +2530,7 @@ enc_setopt_value(otp, rcvbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> V; %% N: Number of reads (when specifying length = 0) %% V: Size of the "read" buffer -enc_setopt_value(otp, rcvbuf, {N, BufSz} = V, _, stream = _T, tcp = _P) +enc_setopt_value(otp, rcvbuf, {N, BufSz} = V, _, stream = _T, _P) when (is_integer(N) andalso (N > 0)) andalso (is_integer(BufSz) andalso (BufSz > 0)) -> V; @@ -3510,6 +3508,25 @@ tdiff(T1, T2) -> %% %% =========================================================================== +-spec invalid_domain(Domain) -> no_return() when + Domain :: term(). + +invalid_domain(Domain) -> + error({invalid_domain, Domain}). + +-spec invalid_type(Type) -> no_return() when + Type :: term(). + +invalid_type(Type) -> + error({invalid_type, Type}). + +-spec invalid_protocol(Type, Proto) -> no_return() when + Type :: term(), + Proto :: term(). + +invalid_protocol(Type, Proto) -> + error({invalid_protocol, {Type, Proto}}). + -spec not_supported(What) -> no_return() when What :: term(). diff --git a/erts/vsn.mk b/erts/vsn.mk index 224570fb09..53dce5e815 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 10.4 +VSN = 10.4.2 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 97c349578f..94551d6815 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -363,7 +363,16 @@ terminate_if_scope_ends(HookId, Function0, Hooks) -> Function = strip_config(Function0), case lists:keyfind(HookId, #ct_hook_config.id, Hooks) of #ct_hook_config{ id = HookId, scope = Function} = Hook -> - terminate([Hook]), + case Function of + [AllOrGroup,_] when AllOrGroup=:=post_all; + AllOrGroup=:=post_groups -> + %% The scope only contains one function (post_all + %% or post_groups), and init has not been called, + %% so skip terminate as well. + ok; + _ -> + terminate([Hook]) + end, lists:keydelete(HookId, #ct_hook_config.id, Hooks); _ -> Hooks diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl index 340b8f3d52..08c18d91e2 100644 --- a/lib/common_test/test/ct_hooks_SUITE.erl +++ b/lib/common_test/test/ct_hooks_SUITE.erl @@ -671,9 +671,15 @@ test_events(scope_suite_cth) -> {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, %% check that post_groups and post_all comes before init when hook %% is installed in suite/0 + %% And there should be no terminate after these, since init is + %% not yet called. {?eh,cth,{'_',post_groups,['_',[]]}}, - {?eh,cth,{'_',post_all,['_','_',[]]}}, - {?eh,tc_start,{ct_scope_suite_cth_SUITE,init_per_suite}}, + {negative, + {?eh,cth,{'_',terminate,['_']}}, + {?eh,cth,{'_',post_all,['_','_',[]]}}}, + {negative, + {?eh,cth,{'_',terminate,['_']}}, + {?eh,tc_start,{ct_scope_suite_cth_SUITE,init_per_suite}}}, {?eh,cth,{'_',id,[[]]}}, {?eh,cth,{'_',init,['_',[]]}}, {?eh,cth,{'_',pre_init_per_suite,[ct_scope_suite_cth_SUITE,'$proplist',[]]}}, diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 275c6268fa..f0d869381b 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,83 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.4.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed an incorrect type determination for constructed + binaries, which could cause <c>is_binary</c> checks to + succeed when they shouldn't have.</p> + <p> + Own Id: OTP-15872</p> + </item> + </list> + </section> + +</section> + +<section><title>Compiler 7.4.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The type optimization pass of the compiler could hang + or loop for a long time when analyzing a + <c>setelement/3</c> call with a varible position.</p> + <p> + Own Id: OTP-15828 Aux Id: ERL-948 </p> + </item> + <item> + <p>Certain complex receive statements would result in an + internal compiler failure.</p> + <p> + Own Id: OTP-15832 Aux Id: ERL-950 </p> + </item> + <item> + <p>Fixed an unsafe type optimization.</p> + <p> + Own Id: OTP-15838</p> + </item> + <item> + <p>Fixed a crash when optimizing compiler-generated + exceptions (like badmatch) whose offending term was a + constructed binary.</p> + <p> + Own Id: OTP-15839 Aux Id: ERL-954 </p> + </item> + <item> + <p>Fixed a bad optimization related to the <c>++/2</c> + operator, where the compiler assumed that it always + produced a list (<c>[] ++ RHS</c> returns <c>RHS</c> + verbatim, even if it's not a list).</p> + <p> + Own Id: OTP-15841</p> + </item> + <item> + <p>An <c>is_binary/1</c> test followed by + <c>is_bitstring/1</c> (or vice versa) could fail because + of an usafe optimization.</p> + <p> + Own Id: OTP-15845</p> + </item> + <item> + <p>A Core Erlang module where the last clause in a + <c>case</c> matched a map would fail to load.</p> + <p> + Own Id: OTP-15846 Aux Id: ERL-955 </p> + </item> + <item> + <p>Fixed a bug that could cause the compiler to crash + when compiling complex nested case expressions.</p> + <p> + Own Id: OTP-15848 Aux Id: ERL-956 </p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 7.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/beam_ssa_dead.erl b/lib/compiler/src/beam_ssa_dead.erl index e220a89ded..64b9b3e222 100644 --- a/lib/compiler/src/beam_ssa_dead.erl +++ b/lib/compiler/src/beam_ssa_dead.erl @@ -436,8 +436,22 @@ get_phi_arg([{Val,From}|_], From) -> Val; get_phi_arg([_|As], From) -> get_phi_arg(As, From). eval_terminator(#b_br{bool=#b_var{}=Bool}=Br, Bs, _St) -> - Val = get_value(Bool, Bs), - beam_ssa:normalize(Br#b_br{bool=Val}); + case get_value(Bool, Bs) of + #b_literal{val=Val}=Lit -> + case is_boolean(Val) of + true -> + beam_ssa:normalize(Br#b_br{bool=Lit}); + false -> + %% Non-boolean literal. This means that this `br` + %% terminator will never actually be reached with + %% these bindings. (There must be a previous two-way + %% branch that branches the other way when Bool + %% is bound to a non-boolean literal.) + none + end; + #b_var{}=Var -> + beam_ssa:normalize(Br#b_br{bool=Var}) + end; eval_terminator(#b_br{bool=#b_literal{}}=Br, _Bs, _St) -> beam_ssa:normalize(Br); eval_terminator(#b_switch{arg=Arg,fail=Fail,list=List}=Sw, Bs, St) -> diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index 417addf921..68920e7dd3 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -840,15 +840,8 @@ type({bif,Bif}, Args, Ts, _Ds) -> Type -> Type end; -type(bs_init, [#b_literal{val=Type}|Args], _Ts, _Ds) -> - case {Type,Args} of - {new,[_,#b_literal{val=Unit}]} -> - {binary,Unit}; - {append,[_,_,#b_literal{val=Unit}]} -> - {binary,Unit}; - {private_append,[_,_,#b_literal{val=Unit}]} -> - {binary,Unit} - end; +type(bs_init, _Args, _Ts, _Ds) -> + {binary, 1}; type(bs_extract, [Ctx], Ts, _Ds) -> #t_bs_match{type=Type} = get_type(Ctx, Ts), Type; @@ -896,11 +889,15 @@ type(call, [#b_remote{mod=#b_literal{val=Mod}, {_,_} -> #t_tuple{} end; - {erlang,'++',[List1,List2]} -> - case get_type(List1, Ts) =:= cons orelse - get_type(List2, Ts) =:= cons of - true -> cons; - false -> list + {erlang,'++',[LHS,RHS]} -> + LType = get_type(LHS, Ts), + RType = get_type(RHS, Ts), + case LType =:= cons orelse RType =:= cons of + true -> + cons; + false -> + %% `[] ++ RHS` yields RHS, even if RHS is not a list. + join(list, RType) end; {erlang,'--',[_,_]} -> list; diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 09a5a6c104..ebe9631e09 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -2844,10 +2844,14 @@ call_return_type_1(erlang, setelement, 3, Vst) -> setelement(3, TupleType, #{}) end; call_return_type_1(erlang, '++', 2, Vst) -> - case get_term_type({x,0}, Vst) =:= cons orelse - get_term_type({x,1}, Vst) =:= cons of - true -> cons; - false -> list + LType = get_term_type({x,0}, Vst), + RType = get_term_type({x,1}, Vst), + case LType =:= cons orelse RType =:= cons of + true -> + cons; + false -> + %% `[] ++ RHS` yields RHS, even if RHS is not a list + join(list, RType) end; call_return_type_1(erlang, '--', 2, _Vst) -> list; diff --git a/lib/compiler/test/beam_ssa_SUITE.erl b/lib/compiler/test/beam_ssa_SUITE.erl index 15cf9bcbf3..a741ebbdf9 100644 --- a/lib/compiler/test/beam_ssa_SUITE.erl +++ b/lib/compiler/test/beam_ssa_SUITE.erl @@ -22,7 +22,8 @@ -export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, init_per_group/2,end_per_group/2, calls/1,tuple_matching/1,recv/1,maps/1, - cover_ssa_dead/1,combine_sw/1,share_opt/1]). + cover_ssa_dead/1,combine_sw/1,share_opt/1, + beam_ssa_dead_crash/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -37,7 +38,8 @@ groups() -> maps, cover_ssa_dead, combine_sw, - share_opt + share_opt, + beam_ssa_dead_crash ]}]. init_per_suite(Config) -> @@ -492,6 +494,60 @@ do_share_opt(A) -> end, receive after 1 -> ok end. +beam_ssa_dead_crash(_Config) -> + not_A_B = do_beam_ssa_dead_crash(id(false), id(true)), + not_A_not_B = do_beam_ssa_dead_crash(false, false), + neither = do_beam_ssa_dead_crash(true, false), + neither = do_beam_ssa_dead_crash(true, true), + ok. + +do_beam_ssa_dead_crash(A, B) -> + %% beam_ssa_dead attempts to shortcut branches that branch other + %% branches. When a two-way branch is encountered, beam_ssa_dead + %% will simulate execution along both paths, in the hope that both + %% paths happens to end up in the same place. + %% + %% During the simulated execution of this function, the boolean + %% varible for a `br` instruction would be replaced with the + %% literal atom `nil`, which is not allowed, and would crash the + %% compiler. In practice, during the actual execution, control + %% would never be transferred to that `br` instruction when the + %% variable in question had the value `nil`. + %% + %% beam_ssa_dead has been updated to immediately abort the search + %% along the current path if there is an attempt to substitute a + %% non-boolean value into a `br` instruction. + + case + case not A of + false -> + false; + true -> + B + end + of + V + when + V /= nil + andalso + V /= false -> + not_A_B; + _ -> + case + case not A of + false -> + false; + true -> + not B + end + of + true -> + not_A_not_B; + false -> + neither + end + end. + %% The identity function. id(I) -> I. diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl index 0d1680fb15..076a604aa4 100644 --- a/lib/compiler/test/beam_type_SUITE.erl +++ b/lib/compiler/test/beam_type_SUITE.erl @@ -24,7 +24,7 @@ integers/1,numbers/1,coverage/1,booleans/1,setelement/1, cons/1,tuple/1,record_float/1,binary_float/1,float_compare/1, arity_checks/1,elixir_binaries/1,find_best/1, - test_size/1,cover_lists_functions/1]). + test_size/1,cover_lists_functions/1,list_append/1,bad_binary_unit/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -47,7 +47,9 @@ groups() -> elixir_binaries, find_best, test_size, - cover_lists_functions + cover_lists_functions, + list_append, + bad_binary_unit ]}]. init_per_suite(Config) -> @@ -501,5 +503,20 @@ cover_lists_functions(Config) -> true = is_list(Zipped), ok. +list_append(_Config) -> + %% '++'/2 has a quirk where it returns the right-hand argument as-is when + %% the left-hand is []. + hello = id([]) ++ id(hello), + ok. + +%% OTP-15872: The compiler would treat the "Unit" of bs_init instructions as +%% the unit of the result instead of the required unit of the input, causing +%% is_binary checks to be wrongly optimized away. +bad_binary_unit(_Config) -> + Bin = id(<<1,2,3>>), + Bitstring = <<Bin/binary,1:1>>, + false = is_binary(Bitstring), + ok. + id(I) -> I. diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index 94bfbb0efe..aac9de278d 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2018. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1, selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1, coverage/1,grab_bag/1,literal_binary/1, - unary_op/1,eq_types/1,match_after_return/1]). + unary_op/1,eq_types/1,match_after_return/1,match_right_tuple/1]). -include_lib("common_test/include/ct.hrl"). @@ -41,7 +41,7 @@ groups() -> shortcut_boolean,letify_guard,selectify,deselectify, underscore,match_map,map_vars_used,coverage, grab_bag,literal_binary,unary_op,eq_types, - match_after_return]}]. + match_after_return,match_right_tuple]}]. init_per_suite(Config) -> @@ -902,4 +902,24 @@ match_after_return(Config) when is_list(Config) -> mar_test_tuple(I) -> {gurka, I}. +match_right_tuple(Config) when is_list(Config) -> + %% The loader wrongly coalesced certain get_tuple_element sequences, fusing + %% the code below into a single i_get_tuple_element2 operating on {x,0} + %% even though the first one overwrites it. + %% + %% {get_tuple_element,{x,0},0,{x,0}}. + %% {get_tuple_element,{x,0},1,{x,1}}. + + Inner = {id(wrong_element), id(ok)}, + Outer = {Inner, id(wrong_tuple)}, + ok = match_right_tuple_1(Outer). + +match_right_tuple_1(T) -> + {A, _} = T, + {_, B} = A, + %% The call ensures that A is in {x,0} and B is in {x,1} + id(force_succ_regs(A, B)). + +force_succ_regs(_A, B) -> B. + id(I) -> I. diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 494de072ff..508bbc902c 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 7.4 +COMPILER_VSN = 7.4.2 diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml index b69657bfa8..5f47981855 100644 --- a/lib/crypto/doc/src/notes.xml +++ b/lib/crypto/doc/src/notes.xml @@ -31,6 +31,23 @@ </header> <p>This document describes the changes made to the Crypto application.</p> +<section><title>Crypto 4.5.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The cipher aes-ctr was disabled by misstake in + crypto:supports for cryptolibs before 1.0.1. It worked + however in the encrypt and decrypt functions.</p> + <p> + Own Id: OTP-15829</p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 4.5</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index 72a51bfec9..2315cb3c48 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 4.5 +CRYPTO_VSN = 4.5.1 diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml index dd0a2bfd7d..0930f79840 100644 --- a/lib/dialyzer/doc/src/notes.xml +++ b/lib/dialyzer/doc/src/notes.xml @@ -32,6 +32,22 @@ <p>This document describes the changes made to the Dialyzer application.</p> +<section><title>Dialyzer 4.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that caused a crash when indenting a + Dialyzer warning mentioning more than one record field. + </p> + <p> + Own Id: OTP-15861 Aux Id: ERL-953 </p> + </item> + </list> + </section> + +</section> + <section><title>Dialyzer 4.0</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl index cfe5fa9b3f..d4fe064edd 100644 --- a/lib/dialyzer/src/dialyzer.erl +++ b/lib/dialyzer/src/dialyzer.erl @@ -642,11 +642,11 @@ c(Cerl, _I) -> field_diffs(Src, false) -> Src; field_diffs(Src, true) -> - Fields = string:split(Src, " and "), + Fields = string:split(Src, " and ", all), lists:join(" and ", [field_diff(Field) || Field <- Fields]). field_diff(Field) -> - [F | Ts] = string:split(Field, "::"), + [F | Ts] = string:split(Field, "::", all), F ++ " ::" ++ t(lists:flatten(lists:join("::", Ts)), true). rec_type("record "++Src, I) -> @@ -658,7 +658,7 @@ ps("pattern "++Src, I) -> ps("variable "++_=Src, _I) -> Src; ps("record field"++Rest, I) -> - [S, TypeStr] = string:split(Rest, "of type "), + [S, TypeStr] = string:split(Rest, "of type ", all), "record field" ++ S ++ "of type " ++ t(TypeStr, I). %% Scan and parse a type or a literal, and pretty-print it using erl_pp. diff --git a/lib/dialyzer/test/small_SUITE_data/results/union_paren b/lib/dialyzer/test/small_SUITE_data/results/union_paren index 3a3526df89..1766773f2d 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/union_paren +++ b/lib/dialyzer/test/small_SUITE_data/results/union_paren @@ -1,7 +1,25 @@ -union_paren.erl:12: Function t2/0 has no local return -union_paren.erl:13: The call union_paren:t2(3.14) breaks the contract (integer() | atom()) -> integer() -union_paren.erl:19: Function t3/0 has no local return -union_paren.erl:20: The pattern 3.14 can never match the type atom() | integer() -union_paren.erl:5: Function t1/0 has no local return -union_paren.erl:6: The call union_paren:t1(3.14) breaks the contract ((A::integer()) | (B::atom())) -> integer() +union_paren.erl:20: Function r1/0 has no local return +union_paren.erl:21: Record construction #r1{f1::[4,...],f2::'undefined',f3::'undefined',f8::float()} violates the declared type of field f2::[atom() | pid() | integer()] and f3::[atom() | pid() | integer()] and f8::[atom() | pid() | integer()] +union_paren.erl:23: Function t1/0 has no local return +union_paren.erl:24: The call union_paren:t1(3.14) breaks the contract ((A::integer()) | (B::atom())) -> integer() +union_paren.erl:30: Function t2/0 has no local return +union_paren.erl:31: The call union_paren:t2(3.14) breaks the contract (integer() | atom()) -> integer() +union_paren.erl:37: Function t3/0 has no local return +union_paren.erl:38: The pattern 3.14 can never match the type atom() | integer() +union_paren.erl:44: Function c1/0 has no local return +union_paren.erl:45: The call union_paren:c1({'r0', 'a', 'undefined', 'undefined'}) breaks the contract (#r0{f1::integer() | pid()}) -> atom() +union_paren.erl:51: Function c2/0 has no local return +union_paren.erl:52: The call union_paren:c2({'r0', 'a', 'undefined', 'undefined'}) breaks the contract (#r0{f1::A::integer() | pid()}) -> atom() +union_paren.erl:58: Function c3/0 has no local return +union_paren.erl:59: The call union_paren:c3({'r0', 'a', 'undefined', 'undefined'}) breaks the contract (#r0{f1::(A::integer()) | (B::pid())}) -> atom() +union_paren.erl:65: Function c4/0 has no local return +union_paren.erl:66: The call union_paren:c4({'r0', 'a', 'undefined', 'undefined'}) breaks the contract (#r0{f1::X::(A::integer()) | (B::pid())}) -> atom() +union_paren.erl:72: Function c5/0 has no local return +union_paren.erl:73: The call union_paren:c5({'r1', ['a'], [1], ['a'], ['u']}) breaks the contract (#r1{f1::[integer()] | [pid()]}) -> atom() +union_paren.erl:79: Function c6/0 has no local return +union_paren.erl:80: The call union_paren:c6({'r1', ['a'], [1], ['a'], ['u']}) breaks the contract (#r1{f1::A::[integer()] | [pid()]}) -> atom() +union_paren.erl:86: Function c7/0 has no local return +union_paren.erl:87: The call union_paren:c7({'r1', ['a'], [1], ['a'], ['u']}) breaks the contract (#r1{f1::(A::[integer()]) | (B::[pid()])}) -> atom() +union_paren.erl:93: Function c8/0 has no local return +union_paren.erl:94: The call union_paren:c8({'r1', ['a'], [1], ['a'], ['u']}) breaks the contract (#r1{f1::X::(A::[integer()]) | (B::[pid()])}) -> atom() diff --git a/lib/dialyzer/test/small_SUITE_data/src/union_paren.erl b/lib/dialyzer/test/small_SUITE_data/src/union_paren.erl index 4691a57d98..65bda1876e 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/union_paren.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/union_paren.erl @@ -2,6 +2,24 @@ -compile(export_all). +-record(r0, + { + f1 = 4 :: atom () | integer() | pid(), + f2 :: atom() | integer() | pid(), + f3 :: A :: atom() | integer() | pid + }). + +-record(r1, + { + f1 = [4] :: [atom ()] | [integer()] | [pid()], + f2 :: [atom()] | [integer()] | [pid()], + f3 :: A :: [atom()] | [integer()] | [pid()], + f8 = [u] :: X :: [A :: atom()] | [B :: integer()] | (C :: [pid()]) + }). + +r1() -> + #r1{f8 = 3.14}. + t1() -> t1(3.14). @@ -22,3 +40,59 @@ t3() -> -spec t3(_) -> (I :: integer()) | (A :: atom()). t3(A) when is_atom(A) -> A; t3(I) when is_integer(I) -> I. + +c1() -> + c1(#r0{f1 = a}). + +-spec c1(#r0{f1 :: integer() | pid()}) -> atom(). +c1(_) -> + a. + +c2() -> + c2(#r0{f1 = a}). + +-spec c2(#r0{f1 :: A :: integer() | pid()}) -> atom(). +c2(_) -> + a. + +c3() -> + c3(#r0{f1 = a}). + +-spec c3(#r0{f1 :: (A :: integer()) | (B :: pid())}) -> atom(). +c3(_) -> + a. + +c4() -> + c4(#r0{f1 = a}). + +-spec c4(#r0{f1 :: X :: (A :: integer()) | (B :: pid())}) -> atom(). +c4(_) -> + a. + +c5() -> + c5(#r1{f1 = [a], f2 = [1], f3 = [a]}). + +-spec c5(#r1{f1 :: [integer()] | [pid()]}) -> atom(). +c5(_) -> + a. + +c6() -> + c6(#r1{f1 = [a], f2 = [1], f3 = [a]}). + +-spec c6(#r1{f1 :: A :: [integer()] | [pid()]}) -> atom(). +c6(_) -> + a. + +c7() -> + c7(#r1{f1 = [a], f2 = [1], f3 = [a]}). + +-spec c7(#r1{f1 :: (A :: [integer()]) | (B :: [pid()])}) -> atom(). +c7(_) -> + a. + +c8() -> + c8(#r1{f1 = [a], f2 = [1], f3 = [a]}). + +-spec c8(#r1{f1 :: X :: (A :: [integer()]) | (B :: [pid()])}) -> atom(). +c8(_) -> + a. diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk index 95984c7c85..466bbfd0f2 100644 --- a/lib/dialyzer/vsn.mk +++ b/lib/dialyzer/vsn.mk @@ -1 +1 @@ -DIALYZER_VSN = 4.0 +DIALYZER_VSN = 4.0.1 diff --git a/lib/erl_interface/src/decode/decode_fun.c b/lib/erl_interface/src/decode/decode_fun.c index 3a7a2b01c1..db71007505 100644 --- a/lib/erl_interface/src/decode/decode_fun.c +++ b/lib/erl_interface/src/decode/decode_fun.c @@ -77,10 +77,12 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p) } if (p != NULL) { p->u.closure.n_free_vars = n; - p->u.closure.free_var_len = ix - ix0; - p->u.closure.free_vars = ei_malloc(ix - ix0); - if (!(p->u.closure.free_vars)) return -1; - memcpy(p->u.closure.free_vars, s + ix0, ix - ix0); + p->u.closure.free_var_len = ix - ix0; + if (p->u.closure.free_var_len > 0) { + p->u.closure.free_vars = ei_malloc(p->u.closure.free_var_len); + if (!(p->u.closure.free_vars)) return -1; + memcpy(p->u.closure.free_vars, s + ix0, p->u.closure.free_var_len); + } } s += ix; *index += s-s0; @@ -146,6 +148,7 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p) else { p_arity = NULL; } + ix = 0; if (ei_decode_atom_as(s, &ix, p_module, MAXATOMLEN_UTF8, ERLANG_UTF8, NULL, NULL) < 0) return -1; @@ -171,6 +174,8 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p) } if (ei_decode_long(s, &ix, p_arity) < 0) return -1; + s += ix; + *index += s - s0; return 0; } default: diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c index 736c00e074..0622ce7d59 100644 --- a/lib/erl_interface/src/decode/decode_skip.c +++ b/lib/erl_interface/src/decode/decode_skip.c @@ -97,6 +97,7 @@ int ei_skip_term(const char* buf, int* index) break; case ERL_FUN_EXT: case ERL_NEW_FUN_EXT: + case ERL_EXPORT_EXT: if (ei_decode_fun(buf, index, NULL) < 0) return -1; break; default: diff --git a/lib/erl_interface/src/misc/ei_printterm.c b/lib/erl_interface/src/misc/ei_printterm.c index 5c40fb7747..a94d6e2ad8 100644 --- a/lib/erl_interface/src/misc/ei_printterm.c +++ b/lib/erl_interface/src/misc/ei_printterm.c @@ -121,6 +121,7 @@ static int print_term(FILE* fp, ei_x_buff* x, erlang_pid pid; erlang_port port; erlang_ref ref; + erlang_fun fun; double d; long l; @@ -306,12 +307,46 @@ static int print_term(FILE* fp, ei_x_buff* x, } break; - case ERL_FLOAT_EXT: case NEW_FLOAT_EXT: if (ei_decode_double(buf, index, &d) < 0) goto err; ch_written += xprintf(fp, x, "%f", d); break; + case ERL_MAP_EXT: + if (ei_decode_map_header(buf, &tindex, &n) < 0) goto err; + ch_written += xprintf(fp, x, "#{"); + for (i = 0; i < n; ++i) { + r = print_term(fp, x, buf, &tindex); + if (r < 0) goto err; + ch_written += r; + ch_written += xprintf(fp, x, " => "); + r = print_term(fp, x, buf, &tindex); + if (r < 0) goto err; + ch_written += r; + if (i < n-1) { + xputs(", ", fp, x); ch_written += 2; + } + } + *index = tindex; + xputc('}', fp, x); ch_written++; + break; + case ERL_FUN_EXT: + case ERL_NEW_FUN_EXT: + case ERL_EXPORT_EXT: + if (ei_decode_fun(buf, &tindex, &fun) < 0) goto err; + if (fun.type == EI_FUN_EXPORT) { + ch_written += xprintf(fp, x, "fun %s:%s/%ld", + fun.module, + fun.u.exprt.func, + fun.arity); + } else { + ch_written += xprintf(fp, x, "#Fun{%s.%ld.%lu}", + fun.module, + fun.u.closure.index, + fun.u.closure.uniq); + } + *index = tindex; + break; default: goto err; } diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl index 3451d9f503..0204b4cfd6 100644 --- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl +++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl @@ -48,7 +48,8 @@ init_per_testcase(Case, Config) -> test_ei_decode_encode(Config) when is_list(Config) -> P = runner:start(Config, ?test_ei_decode_encode), - Fun = fun (X) -> {X,true} end, + Fun1 = fun (X) -> {X,true} end, + Fun2 = fun runner:init_per_testcase/3, Pid = self(), Port = case os:type() of {win32,_} -> @@ -70,7 +71,8 @@ test_ei_decode_encode(Config) when is_list(Config) -> BigLargeB = 1 bsl 11112 + BigSmallB, BigLargeC = BigSmallA * BigSmallB * BigSmallC * BigSmallA, - send_rec(P, Fun), + send_rec(P, Fun1), + send_rec(P, Fun2), send_rec(P, Pid), send_rec(P, Port), send_rec(P, Ref), @@ -115,7 +117,7 @@ test_ei_decode_encode(Config) when is_list(Config) -> send_rec(P, {}), send_rec(P, {atom, Pid, Port, Ref}), send_rec(P, [atom, Pid, Port, Ref]), - send_rec(P, [atom | Fun]), + send_rec(P, [atom | Fun1]), send_rec(P, #{}), send_rec(P, #{key => value}), send_rec(P, maps:put(Port, Ref, #{key => value, key2 => Pid})), diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c index 85ca6c56e9..512f9ed0c7 100644 --- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c +++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c @@ -564,6 +564,7 @@ TESTCASE(test_ei_decode_encode) ei_init(); decode_encode_one(&fun_type); + decode_encode_one(&fun_type); decode_encode_one(&pid_type); decode_encode_one(&port_type); decode_encode_one(&ref_type); diff --git a/lib/erl_interface/test/ei_print_SUITE.erl b/lib/erl_interface/test/ei_print_SUITE.erl index c75ce55a7d..f2a2548183 100644 --- a/lib/erl_interface/test/ei_print_SUITE.erl +++ b/lib/erl_interface/test/ei_print_SUITE.erl @@ -26,7 +26,8 @@ -export([all/0, suite/0, init_per_testcase/2, - atoms/1, tuples/1, lists/1, strings/1]). + atoms/1, tuples/1, lists/1, strings/1, + maps/1, funs/1]). -import(runner, [get_term/1]). @@ -36,8 +37,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> - [atoms, tuples, lists, strings]. +all() -> + [atoms, tuples, lists, strings, maps, funs]. init_per_testcase(Case, Config) -> runner:init_per_testcase(?MODULE, Case, Config). @@ -142,3 +143,23 @@ strings(Config) when is_list(Config) -> runner:recv_eot(P), ok. + +maps(Config) -> + P = runner:start(Config, ?maps), + + {term, "#{}"} = get_term(P), + {term, "#{key => value}"} = get_term(P), + {term, "#{key => value, another_key => {ok, 42}}"} = get_term(P), + + runner:recv_eot(P), + ok. + +funs(Config) -> + P = runner:start(Config, ?funs), + + {term, "#Fun{some_module.42.3735928559}"} = get_term(P), + {term, "#Fun{some_module.37.195935983}"} = get_term(P), + {term, "fun erlang:abs/1"} = get_term(P), + + runner:recv_eot(P), + ok. diff --git a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c index 80be3016e6..0e2b24e45a 100644 --- a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c +++ b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c @@ -29,6 +29,46 @@ */ static void +send_printed_buf(ei_x_buff* x) +{ + char* b = NULL; + char fn[256]; + char *tmp = getenv("temp"); + FILE* f; + int n, index = 0, ver; + +#ifdef VXWORKS + tmp = "."; +#else + if (tmp == NULL) { + tmp = "/tmp"; + } +#endif + strcpy(fn, tmp); + strcat(fn, "/ei_print_test.txt"); + f = fopen(fn, "w+"); + ei_decode_version(x->buff, &index, &ver); + n = ei_print_term(f, x->buff, &index); + if (n < 0) { + fclose(f); + x->index = 0; + ei_x_format(x, "~s", "ERROR: term decoding failed"); + send_bin_term(x); + } else { + fseek(f, 0, SEEK_SET); + b = malloc(n+1); + fread(b, 1, n, f); + b[n] = '\0'; + fclose(f); + x->index = 0; + ei_x_format(x, "~s", b); + send_bin_term(x); + free(b); + } +} + + +static void send_printed3(char* format, char* p1, char* p2, int fl) { char* b = NULL; @@ -43,25 +83,7 @@ send_printed3(char* format, char* p1, char* p2, int fl) } else { ei_x_format(&x, format, p1, p2); } -#ifdef VXWORKS - tmp = "."; -#else - if (tmp == NULL) tmp = "/tmp"; -#endif - strcpy(fn, tmp); - strcat(fn, "/ei_print_test.txt"); - f = fopen(fn, "w+"); - ei_decode_version(x.buff, &index, &ver); - n = ei_print_term(f, x.buff, &index); - fseek(f, 0, SEEK_SET); - b = malloc(n+1); - fread(b, 1, n, f); - b[n] = '\0'; - fclose(f); - x.index = 0; - ei_x_format(&x, "~s", b); - send_bin_term(&x); - free(b); + send_printed_buf(&x); ei_x_free(&x); } @@ -184,4 +206,85 @@ TESTCASE(strings) report(1); } +TESTCASE(maps) +{ + ei_x_buff x; + + ei_init(); + + ei_x_new_with_version(&x); + ei_x_encode_map_header(&x, 0); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + ei_x_encode_map_header(&x, 1); + ei_x_encode_atom(&x, "key"); + ei_x_encode_atom(&x, "value"); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + ei_x_encode_map_header(&x, 2); + ei_x_encode_atom(&x, "key"); + ei_x_encode_atom(&x, "value"); + ei_x_encode_atom(&x, "another_key"); + ei_x_encode_tuple_header(&x, 2); + ei_x_encode_atom(&x, "ok"); + ei_x_encode_long(&x, 42L); + send_printed_buf(&x); + ei_x_free(&x); + + report(1); +} + +TESTCASE(funs) +{ + ei_x_buff x; + erlang_pid self; + erlang_fun fun; + + strcpy(self.node, "node@host"); + self.num = 9; + self.serial = 99; + self.creation = 1; + + ei_init(); + + ei_x_new_with_version(&x); + fun.arity = -1; /* Will encode as FUN_EXT */ + strcpy(fun.module, "some_module"); + fun.type = EI_FUN_CLOSURE; + fun.u.closure.pid = self; + fun.u.closure.index = fun.u.closure.old_index = 42; + fun.u.closure.uniq = 0xDEADBEEF; + fun.u.closure.n_free_vars = 0; + fun.u.closure.free_var_len = 0; + ei_x_encode_fun(&x, &fun); + send_printed_buf(&x); + ei_x_free(&x); + ei_x_new_with_version(&x); + fun.arity = 0; /* Will encode as NEW_FUN_EXT */ + strcpy(fun.module, "some_module"); + fun.type = EI_FUN_CLOSURE; + fun.u.closure.pid = self; + fun.u.closure.index = fun.u.closure.old_index = 37; + fun.u.closure.uniq = 0xBADBEEF; + fun.u.closure.n_free_vars = 0; + fun.u.closure.free_var_len = 0; + ei_x_encode_fun(&x, &fun); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + fun.arity = 1; + strcpy(fun.module, "erlang"); + fun.type = EI_FUN_EXPORT; + fun.u.exprt.func = "abs"; + ei_x_encode_fun(&x, &fun); + send_printed_buf(&x); + ei_x_free(&x); + + report(1); +} diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 6f68a67174..f1ff8d97f7 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -129,6 +129,21 @@ </section> +<section><title>Kernel 6.3.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix type spec for <c>seq_trace:set_token/2</c>.</p> + <p> + Own Id: OTP-15858 Aux Id: ERL-700 </p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 6.3.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -6027,4 +6042,3 @@ </section> </section> </chapter> - diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 7faef93609..964ede9bc9 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -19,6 +19,8 @@ %% -module(code). +-include_lib("kernel/include/logger.hrl"). + %% This is the interface module to the code server. It also contains %% some implementation details. See also related modules: code_*.erl %% in this directory. @@ -707,8 +709,20 @@ do_s(Lib) -> start_get_mode() -> case init:get_argument(mode) of - {ok,[["embedded"]]} -> - embedded; + {ok, [FirstMode | Rest]} -> + case Rest of + [] -> + ok; + _ -> + ?LOG_WARNING("Multiple -mode given to erl, using the first, ~p", + [FirstMode]) + end, + case FirstMode of + ["embedded"] -> + embedded; + _ -> + interactive + end; _ -> interactive end. diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index d893d44079..a63df54ff9 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -217,24 +217,29 @@ peeloff(S, AssocId) when is_port(S), is_integer(AssocId) -> Error -> Error end. --spec connect(Socket, Addr, Port, Opts) -> {ok, Assoc} | {error, inet:posix()} when +-spec connect(Socket, Addr, Port, Opts) -> + {ok, #sctp_assoc_change{state :: 'comm_up'}} | + {error, #sctp_assoc_change{state :: 'cant_assoc'}} | + {error, inet:posix()} + when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), - Opts :: [Opt :: option()], - Assoc :: #sctp_assoc_change{}. + Opts :: [Opt :: option()]. connect(S, Addr, Port, Opts) -> connect(S, Addr, Port, Opts, infinity). -spec connect(Socket, Addr, Port, Opts, Timeout) -> - {ok, Assoc} | {error, inet:posix()} when + {ok, #sctp_assoc_change{state :: 'comm_up'}} | + {error, #sctp_assoc_change{state :: 'cant_assoc'}} | + {error, inet:posix()} + when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), Opts :: [Opt :: option()], - Timeout :: timeout(), - Assoc :: #sctp_assoc_change{}. + Timeout :: timeout(). connect(S, Addr, Port, Opts, Timeout) -> case do_connect(S, Addr, Port, Opts, Timeout, true) of diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index bc4f09a023..c8c631ab23 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -141,7 +141,7 @@ init([]) -> modules => [logger_sup]}, case init:get_argument(mode) of - {ok, [["minimal"]]} -> + {ok, [["minimal"]|_]} -> {ok, {SupFlags, [Code, File, StdError, User, LoggerSup, Config, RefC, SafeSup]}}; _ -> diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index c8f1acfca4..2b078ef091 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -170,9 +170,11 @@ check_h_config(_Type,[]) -> ok. normalize_config(#{type:={file,File}}=HConfig) -> - HConfig#{type=>file,file=>File}; + normalize_config(HConfig#{type=>file,file=>File}); normalize_config(#{type:={file,File,Modes}}=HConfig) -> - HConfig#{type=>file,file=>File,modes=>Modes}; + normalize_config(HConfig#{type=>file,file=>File,modes=>Modes}); +normalize_config(#{file:=File}=HConfig) -> + HConfig#{file=>filename:absname(File)}; normalize_config(HConfig) -> HConfig. @@ -188,7 +190,7 @@ merge_default_config(Name,Type,HConfig) -> get_default_config(Name,file) -> #{type => file, - file => atom_to_list(Name), + file => filename:absname(atom_to_list(Name)), modes => [raw,append], file_check => 0, max_no_bytes => infinity, diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl index 4f9d7b3e5c..f0bd1fabe9 100644 --- a/lib/kernel/src/seq_trace.erl +++ b/lib/kernel/src/seq_trace.erl @@ -59,7 +59,7 @@ set_token({Flags,Label,Serial,_From,Lastcnt}) -> F = decode_flags(Flags), set_token2([{label,Label},{serial,{Lastcnt, Serial}} | F]). --spec set_token(Component, Val) -> {Component, OldVal} when +-spec set_token(Component, Val) -> OldVal when Component :: component(), Val :: value(), OldVal :: value(). diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 4f0847084f..6b133f8d6b 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -41,7 +41,7 @@ big_boot_embedded/1, module_status/1, native_early_modules/1, get_mode/1, - normalized_paths/1]). + normalized_paths/1, mult_embedded_flags/1]). -export([init_per_testcase/2, end_per_testcase/2, init_per_suite/1, end_per_suite/1]). @@ -72,7 +72,8 @@ all() -> on_load_purge, on_load_self_call, on_load_pending, on_load_deleted, module_status, - big_boot_embedded, native_early_modules, get_mode, normalized_paths]. + big_boot_embedded, native_early_modules, get_mode, normalized_paths, + mult_embedded_flags]. %% These need to run in order groups() -> [{sequence, [sequence], [on_load_update, @@ -354,7 +355,7 @@ load_abs(Config) when is_list(Config) -> ensure_loaded(Config) when is_list(Config) -> {module, lists} = code:ensure_loaded(lists), case init:get_argument(mode) of - {ok, [["embedded"]]} -> + {ok, [["embedded"] | _]} -> {error, embedded} = code:ensure_loaded(code_b_test), {error, badarg} = code:ensure_loaded(34), ok; @@ -1836,6 +1837,28 @@ do_normalized_paths([M|Ms]) -> do_normalized_paths([]) -> ok. +%% Make sure that the extra -mode flags are ignored +mult_embedded_flags(_Config) -> + Modes = [{" -mode embedded", embedded}, + {" -mode interactive", interactive}, + {" -mode invalid", interactive}], + + [ begin + {ArgMode, ExpectedMode} = Mode, + {ok, Node} = start_node(mode_test, ArgMode), + ExpectedMode = rpc:call(Node, code, get_mode, []), + true = stop_node(Node) + end || Mode <- Modes], + + [ begin + {ArgIgnoredMode, _} = IgnoredMode, + {ArgRelevantMode, ExpectedMode} = RelevantMode, + {ok, Node} = start_node(mode_test, ArgRelevantMode ++ ArgIgnoredMode), + ExpectedMode = rpc:call(Node, code, get_mode, []), + true = stop_node(Node) + end || IgnoredMode <- Modes, RelevantMode <- Modes], + ok. + %% Test that module_status/1 behaves as expected module_status(_Config) -> case test_server:is_cover() of diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index edf30448c4..c91808d4ae 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -36,7 +36,8 @@ show_econnreset_passive/1, econnreset_after_sync_send/1, econnreset_after_async_send_active/1, econnreset_after_async_send_active_once/1, - econnreset_after_async_send_passive/1, linger_zero/1, + econnreset_after_async_send_passive/1, + linger_zero/1, linger_zero_sndbuf/1, default_options/1, http_bad_packet/1, busy_send/1, busy_disconnect_passive/1, busy_disconnect_active/1, fill_sendq/1, partial_recv_and_close/1, @@ -80,7 +81,8 @@ all() -> show_econnreset_passive, econnreset_after_sync_send, econnreset_after_async_send_active, econnreset_after_async_send_active_once, - econnreset_after_async_send_passive, linger_zero, + econnreset_after_async_send_passive, + linger_zero, linger_zero_sndbuf, default_options, http_bad_packet, busy_send, busy_disconnect_passive, busy_disconnect_active, fill_sendq, partial_recv_and_close, @@ -1356,7 +1358,42 @@ linger_zero(Config) when is_list(Config) -> ok = gen_tcp:close(Client), ok = ct:sleep(1), undefined = erlang:port_info(Client, connected), - {error, econnreset} = gen_tcp:recv(S, PayloadSize). + {error, econnreset} = gen_tcp:recv(S, PayloadSize), + ok. + + +linger_zero_sndbuf(Config) when is_list(Config) -> + %% All the econnreset tests will prove that {linger, {true, 0}} aborts + %% a connection when the driver queue is empty. We will test here + %% that it also works when the driver queue is not empty + %% and the linger zero option is set on the listen socket. + {OS, _} = os:type(), + {ok, Listen} = + gen_tcp:listen(0, [{active, false}, + {recbuf, 4096}, + {show_econnreset, true}, + {linger, {true, 0}}]), + {ok, Port} = inet:port(Listen), + {ok, Client} = + gen_tcp:connect(localhost, Port, + [{active, false}, + {sndbuf, 4096}]), + {ok, Server} = gen_tcp:accept(Listen), + ok = gen_tcp:close(Listen), + PayloadSize = 1024 * 1024, + Payload = binary:copy(<<"0123456789ABCDEF">>, 256 * 1024), % 1 MB + ok = gen_tcp:send(Server, Payload), + case erlang:port_info(Server, queue_size) of + {queue_size, N} when N > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T -> ct:fail(T) + end, + {ok, [{linger, {true, 0}}]} = inet:getopts(Server, [linger]), + ok = gen_tcp:close(Server), + ok = ct:sleep(1), + undefined = erlang:port_info(Server, connected), + {error, closed} = gen_tcp:recv(Client, PayloadSize), + ok. %% Thanks to Luke Gorrie. Tests for a very specific problem with diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 16ab0e97fc..2b2d509860 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -132,6 +132,7 @@ all() -> bad_input, reconfig, file_opts, + relative_file_path, sync, write_failure, sync_failure, @@ -693,6 +694,54 @@ file_opts(Config) -> file_opts(cleanup, _Config) -> logger:remove_handler(?MODULE). +relative_file_path(_Config) -> + {ok,Dir} = file:get_cwd(), + AbsName1 = filename:join(Dir,?MODULE), + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{type=>file}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{cb_state := #{handler_state := #{file:=AbsName1}}} = + logger_olp:info(h_proc_name()), + {ok,#{config := #{file:=AbsName1}}} = + logger:get_handler_config(?MODULE), + ok = logger:remove_handler(?MODULE), + + RelName2 = filename:join(atom_to_list(?FUNCTION_NAME), + lists:concat([?FUNCTION_NAME,".log"])), + AbsName2 = filename:join(Dir,RelName2), + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{file => RelName2}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{cb_state := #{handler_state := #{file:=AbsName2}}} = + logger_olp:info(h_proc_name()), + {ok,#{config := #{file:=AbsName2}}} = + logger:get_handler_config(?MODULE), + logger:notice(M1=?msg,?domain), + ?check(M1), + B1 = ?bin(M1), + try_read_file(AbsName2, {ok,B1}, filesync_rep_int()), + + ok = file:set_cwd(".."), + logger:notice(M2=?msg,?domain), + ?check(M2), + B20 = ?bin(M2), + B2 = <<B1/binary,B20/binary>>, + try_read_file(AbsName2, {ok,B2}, filesync_rep_int()), + + {error,_} = logger:update_handler_config(?MODULE,config,#{file=>RelName2}), + ok = logger:update_handler_config(?MODULE,config,#{file=>AbsName2}), + ok = file:set_cwd(Dir), + ok = logger:update_handler_config(?MODULE,config,#{file=>RelName2}), + ok. +relative_file_path(cleanup,_Config) -> + logger:remove_handler(?MODULE). + sync(Config) -> Dir = ?config(priv_dir,Config), diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl index 663f910751..83a94ab087 100644 --- a/lib/kernel/test/seq_trace_SUITE.erl +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -26,6 +26,7 @@ init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2]). -export([token_set_get/1, tracer_set_get/1, print/1, + old_heap_token/1, send/1, distributed_send/1, recv/1, distributed_recv/1, trace_exit/1, distributed_exit/1, call/1, port/1, match_set_seq_token/1, gc_seq_token/1, label_capability_mismatch/1, @@ -50,6 +51,7 @@ suite() -> all() -> [token_set_get, tracer_set_get, print, send, send_literal, distributed_send, recv, distributed_recv, trace_exit, + old_heap_token, distributed_exit, call, port, match_set_seq_token, gc_seq_token, label_capability_mismatch]. @@ -149,17 +151,19 @@ tracer_set_get(Config) when is_list(Config) -> ok. print(Config) when is_list(Config) -> - lists:foreach(fun do_print/1, ?TIMESTAMP_MODES). + [do_print(TsType, Label) || TsType <- ?TIMESTAMP_MODES, + Label <- [17, "label"]]. -do_print(TsType) -> +do_print(TsType, Label) -> start_tracer(), + seq_trace:set_token(label, Label), set_token_flags([print, TsType]), - seq_trace:print(0,print1), + seq_trace:print(Label,print1), seq_trace:print(1,print2), seq_trace:print(print3), seq_trace:reset_trace(), - [{0,{print,_,_,[],print1}, Ts0}, - {0,{print,_,_,[],print3}, Ts1}] = stop_tracer(2), + [{Label,{print,_,_,[],print1}, Ts0}, + {Label,{print,_,_,[],print3}, Ts1}] = stop_tracer(2), check_ts(TsType, Ts0), check_ts(TsType, Ts1). @@ -563,6 +567,24 @@ get_port_message(Port) -> end. +%% OTP-15849 ERL-700 +%% Verify changing label on existing token when it resides on old heap. +%% Bug caused faulty ref from old to new heap. +old_heap_token(Config) when is_list(Config) -> + seq_trace:set_token(label, 1), + erlang:garbage_collect(self(), [{type, minor}]), + erlang:garbage_collect(self(), [{type, minor}]), + %% Now token tuple should be on old-heap. + %% Set a new non-literal label which should reside on new-heap. + NewLabel = {self(), "new label"}, + 1 = seq_trace:set_token(label, NewLabel), + + %% If bug, we now have a ref from old to new heap. Yet another minor gc + %% will make that a ref to deallocated memory. + erlang:garbage_collect(self(), [{type, minor}]), + {label,NewLabel} = seq_trace:get_token(label), + ok. + match_set_seq_token(doc) -> ["Tests that match spec function set_seq_token does not " diff --git a/lib/public_key/asn1/CMSAesRsaesOaep.asn1 b/lib/public_key/asn1/CMSAesRsaesOaep.asn1 new file mode 100644 index 0000000000..ca8c7b7f92 --- /dev/null +++ b/lib/public_key/asn1/CMSAesRsaesOaep.asn1 @@ -0,0 +1,39 @@ +CMSAesRsaesOaep {iso(1) member-body(2) us(840) rsadsi(113549) + pkcs(1) pkcs-9(9) smime(16) modules(0) id-mod-cms-aes(19) } + + +DEFINITIONS IMPLICIT TAGS ::= +BEGIN + +-- EXPORTS ALL -- +IMPORTS + -- PKIX + AlgorithmIdentifier + FROM PKIX1Explicit88 {iso(1) identified-organization(3) dod(6) + internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) + id-pkix1-explicit(18)}; + +-- AES information object identifiers -- + +aes OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) + organization(1) gov(101) csor(3) nistAlgorithms(4) 1 } + +-- AES using CBC-chaining mode for key sizes of 128, 192, 256 + +id-aes128-CBC OBJECT IDENTIFIER ::= { aes 2 } +id-aes192-CBC OBJECT IDENTIFIER ::= { aes 22 } +id-aes256-CBC OBJECT IDENTIFIER ::= { aes 42 } + +-- AES-IV is a the parameter for all the above object identifiers. + +AES-IV ::= OCTET STRING (SIZE(16)) + + +-- AES Key Wrap Algorithm Identifiers - Parameter is absent + +id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } +id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } +id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } + + +END diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile index a920ea87ea..10952106c6 100644 --- a/lib/public_key/asn1/Makefile +++ b/lib/public_key/asn1/Makefile @@ -42,7 +42,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN) ASN_TOP = OTP-PUB-KEY PKCS-FRAME ASN_MODULES = PKIX1Explicit88 PKIX1Implicit88 PKIX1Algorithms88 \ PKIXAttributeCertificate PKCS-1 PKCS-3 PKCS-7 PKCS-8 PKCS-10 PKCS5v2-0 OTP-PKIX \ - InformationFramework RFC5639 + InformationFramework RFC5639 CMSAesRsaesOaep ASN_ASNS = $(ASN_MODULES:%=%.asn1) ASN_ERLS = $(ASN_TOP:%=%.erl) ASN_HRLS = $(ASN_TOP:%=%.hrl) diff --git a/lib/public_key/asn1/OTP-PUB-KEY.set.asn b/lib/public_key/asn1/OTP-PUB-KEY.set.asn index b3f3ccdb77..7ab1684ff3 100644 --- a/lib/public_key/asn1/OTP-PUB-KEY.set.asn +++ b/lib/public_key/asn1/OTP-PUB-KEY.set.asn @@ -10,3 +10,5 @@ ECPrivateKey.asn1 PKCS-7.asn1 PKCS-10.asn1 RFC5639.asn1 +CMSAesRsaesOaep.asn1 + diff --git a/lib/public_key/doc/src/public_key_app.xml b/lib/public_key/doc/src/public_key_app.xml index 923a9f1dfb..5f2c50711a 100644 --- a/lib/public_key/doc/src/public_key_app.xml +++ b/lib/public_key/doc/src/public_key_app.xml @@ -51,6 +51,9 @@ Diffie-Hellman Key Agreement Standard </item> <item>Supports <url href="http://www.ietf.org/rfc/rfc2898.txt"> PKCS-5</url> - Password-Based Cryptography Standard </item> + <item>Supports <url href="http://www.ietf.org/rfc/fc3565.txt"> AES </url> - + Use of the Advanced Encryption Standard (AES) Algorithm in Cryptographic Message Syntax (CMS) + </item> <item>Supports <url href="http://www.ietf.org/rfc/rfc5208.txt"> PKCS-8</url> - Private-Key Information Syntax Standard</item> <item>Supports <url href="http://www.ietf.org/rfc/rfc5967.txt"> PKCS-10</url> - diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl index 38b5c93521..6003bf21d0 100644 --- a/lib/public_key/src/pubkey_pbe.erl +++ b/lib/public_key/src/pubkey_pbe.erl @@ -26,9 +26,7 @@ -export([encode/4, decode/4, decrypt_parameters/1, encrypt_parameters/1]). -export([pbdkdf1/4, pbdkdf2/7]). --define(DEFAULT_SHA_MAC_KEYLEN, 20). -define(ASN1_OCTET_STR_TAG, 4). --define(IV_LEN, 8). %%==================================================================== %% Internal application API @@ -41,14 +39,23 @@ %%-------------------------------------------------------------------- encode(Data, Password, "DES-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_encrypt(des_cbc, Key, IV, pbe_pad(Data, KeyDevParams)); + crypto:block_encrypt(des_cbc, Key, IV, pbe_pad(Data, block_size(des_cbc))); encode(Data, Password, "DES-EDE3-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), <<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key, - crypto:block_encrypt(des3_cbc, [Key1, Key2, Key3], IV, pbe_pad(Data)); + crypto:block_encrypt(des3_cbc, [Key1, Key2, Key3], IV, pbe_pad(Data, block_size(des_3ede))); encode(Data, Password, "RC2-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_encrypt(rc2_cbc, Key, IV, pbe_pad(Data, KeyDevParams)). + crypto:block_encrypt(rc2_cbc, Key, IV, pbe_pad(Data, block_size(rc2_cbc))); +encode(Data, Password, "AES-128-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_encrypt(aes_128_cbc, Key, IV, pbe_pad(Data, block_size(aes_128_cbc))); +encode(Data, Password, "AES-192-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_encrypt(aes_192_cbc, Key, IV, pbe_pad(Data, block_size(aes_192_cbc))); +encode(Data, Password, "AES-256-CBC"= Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_encrypt(aes_256_cbc, Key, IV, pbe_pad(Data, block_size(aes_256_cbc))). %%-------------------------------------------------------------------- -spec decode(binary(), string(), string(), term()) -> binary(). @@ -67,11 +74,13 @@ decode(Data, Password,"RC2-CBC"= Cipher, KeyDevParams) -> crypto:block_decrypt(rc2_cbc, Key, IV, Data); decode(Data, Password,"AES-128-CBC"= Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_decrypt(aes_cbc128, Key, IV, Data); -decode(Data, Password,"AES-256-CBC"= Cipher, KeyDevParams) -> + crypto:block_decrypt(aes_128_cbc, Key, IV, Data); +decode(Data, Password,"AES-192-CBC"= Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_decrypt(aes_192_cbc, Key, IV, Data); +decode(Data, Password,"AES-256-CBC"= Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_decrypt(aes_cbc256, Key, IV, Data). - + crypto:block_decrypt(aes_256_cbc, Key, IV, Data). %%-------------------------------------------------------------------- -spec pbdkdf1(iodata(), iodata(), integer(), atom()) -> binary(). @@ -150,17 +159,15 @@ do_pbdkdf1(Prev, Count, Acc, Hash) -> Result = crypto:hash(Hash, Prev), do_pbdkdf1(Result, Count-1 , <<Result/binary, Acc/binary>>, Hash). -iv(#'PBES2-params_encryptionScheme'{algorithm = Algo, - parameters = ASN1IV}) - when (Algo == ?'desCBC') or - (Algo == ?'des-EDE3-CBC') -> - <<?ASN1_OCTET_STR_TAG, ?IV_LEN, IV:?IV_LEN/binary>> = decode_handle_open_type_wrapper(ASN1IV), - IV; iv(#'PBES2-params_encryptionScheme'{algorithm = ?'rc2CBC', parameters = ASN1IV}) -> {ok, #'RC2-CBC-Parameter'{iv = IV}} = 'PKCS-FRAME':decode('RC2-CBC-Parameter', decode_handle_open_type_wrapper(ASN1IV)), - iolist_to_binary(IV). + iolist_to_binary(IV); +iv(#'PBES2-params_encryptionScheme'{algorithm = _Algo, + parameters = ASN1IV}) -> + <<?ASN1_OCTET_STR_TAG, Len:8/unsigned-big-integer, IV:Len/binary>> = decode_handle_open_type_wrapper(ASN1IV), + IV. blocks(1, N, Index, Password, Salt, Count, Prf, PrfHash, PrfLen, Acc) -> <<XorSum:N/binary, _/binary>> = xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen), @@ -217,17 +224,9 @@ pbe1_oid("RC2-CBC", md5) -> pbe1_oid("DES-CBC", md5) -> ?'pbeWithMD5AndDES-CBC'. -pbe_pad(Data, {#'PBEParameter'{}, _}) -> - pbe_pad(Data); -pbe_pad(Data, #'PBES2-params'{}) -> - pbe_pad(Data); -pbe_pad(Data, _) -> -pbe_pad(Data).%% Data. - - -pbe_pad(Data) -> - N = 8 - (erlang:byte_size(Data) rem 8), - Pad = list_to_binary(lists:duplicate(N, N)), +pbe_pad(Data, BlockSize) -> + N = BlockSize - (erlang:byte_size(Data) rem BlockSize), + Pad = binary:copy(<<N>>, N), <<Data/binary, Pad/binary>>. key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, @@ -249,11 +248,27 @@ key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, pseudo_random_function(#'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> {fun crypto:hmac/4, sha, pseudo_output_length(?'id-hmacWithSHA1')}; -pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA1'}) -> - {fun crypto:hmac/4, sha, pseudo_output_length(?'id-hmacWithSHA1')}. +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA1' = Algo}) -> + {fun crypto:hmac/4, sha, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA224'= Algo}) -> + {fun crypto:hmac/4, sha224, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA256' = Algo}) -> + {fun crypto:hmac/4, sha256, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA384' = Algo}) -> + {fun crypto:hmac/4, sha384, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA512' = Algo}) -> + {fun crypto:hmac/4, sha512, pseudo_output_length(Algo)}. pseudo_output_length(?'id-hmacWithSHA1') -> - ?DEFAULT_SHA_MAC_KEYLEN. + 20; %%160/8 +pseudo_output_length(?'id-hmacWithSHA224') -> + 28; %%%224/8 +pseudo_output_length(?'id-hmacWithSHA256') -> + 32; %%256/8 +pseudo_output_length(?'id-hmacWithSHA384') -> + 48; %%384/8 +pseudo_output_length(?'id-hmacWithSHA512') -> + 64. %%512/8 derived_key_length(_, Len) when is_integer(Len) -> Len; @@ -266,11 +281,33 @@ derived_key_length(Cipher,_) when (Cipher == ?'rc2CBC') or derived_key_length(Cipher,_) when (Cipher == ?'des-EDE3-CBC') or (Cipher == "DES-EDE3-CBC") -> 24; -derived_key_length(Cipher,_) when (Cipher == "AES-128-CBC") -> + +derived_key_length(Cipher,_) when (Cipher == "AES-128-CBC"); + (Cipher == ?'id-aes128-CBC') -> 16; -derived_key_length(Cipher,_) when (Cipher == "AES-256-CBC") -> +derived_key_length(Cipher,_) when (Cipher == "AES-192-CBC"); + (Cipher == ?'id-aes192-CBC') -> + 24; + +derived_key_length(Cipher,_) when (Cipher == "AES-256-CBC"); + (Cipher == ?'id-aes256-CBC') -> 32. +block_size(Cipher) when Cipher == rc2_cbc; + Cipher == des_cbc; + Cipher == des_3ede -> + 8; +block_size(Cipher) when Cipher == aes_128_cbc; + Cipher == aes_192_cbc; + Cipher == aes_256_cbc -> + 16. + +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes128-CBC'}) -> + "AES-128-CBC"; +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes192-CBC'}) -> + "AES-192-CBC"; +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes256-CBC'}) -> + "AES-256-CBC"; cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'desCBC'}) -> "DES-CBC"; cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'des-EDE3-CBC'}) -> diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl index 1136267411..61db282dfa 100644 --- a/lib/public_key/test/pbe_SUITE.erl +++ b/lib/public_key/test/pbe_SUITE.erl @@ -206,7 +206,10 @@ pbes2() -> [{doc,"Tests encode/decode EncryptedPrivateKeyInfo encrypted with different ciphers using PBES2"}]. pbes2(Config) when is_list(Config) -> decode_encode_key_file("pbes2_des_cbc_enc_key.pem", "password", "DES-CBC", Config), - decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config), + decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config), + decode_encode_key_file("pbes2_aes_128_enc_key.pem", "password", "AES-128-CBC", Config), + decode_encode_key_file("pbes2_aes_192_enc_key.pem", "password", "AES-192-CBC", Config), + decode_encode_key_file("pbes2_aes_256_enc_key.pem", "password", "AES-256-CBC", Config), case lists:member(rc2_cbc, proplists:get_value(ciphers, crypto:supports())) of true -> decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config); @@ -239,7 +242,6 @@ decode_encode_key_file(File, Password, Cipher, Config) -> {ok, PemKey} = file:read_file(filename:join(Datadir, File)), PemEntry = public_key:pem_decode(PemKey), - ct:pal("Pem entry: ~p" , [PemEntry]), [{Asn1Type, _, {Cipher,_} = CipherInfo} = PubEntry] = PemEntry, #'RSAPrivateKey'{} = KeyInfo = public_key:pem_entry_decode(PubEntry, Password), PemKey1 = public_key:pem_encode([public_key:pem_entry_encode(Asn1Type, KeyInfo, {CipherInfo, Password})]), diff --git a/lib/public_key/test/pbe_SUITE_data/pbes2_aes_128_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_128_enc_key.pem new file mode 100644 index 0000000000..5702119ad6 --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_128_enc_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIWrPgmqJqNpICAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBA/bbIMYqQMUDxMk9ifPR7ABIIE +0Drfqke1/ccFxk786hTh36yjVo48Xx7B3Scb92KtmyQpNaR6GbR+jhP9cxIcvmGN +YroCB896VJSIx8PraqGgIJ1hblZXyfLanB0mUnZvaaQ4xp3UJT53a0yOm5Lfd+fB +0TyaoEzca2jA5EVVh3yH6gzNsvQJRw6cQP5CAptLjiUv2jrwVGnO8x8X4egJDLZS +Sb8B5AW8h1sGsyKEEFto6gpBjVqnVn5veMoI/Cfs9qDr071+dhbps/m6pseKKp0z +8qeFM7+9Y4npD1VYg2gqOFi19QAI3gwq6tC8grOzRA8dPFUgpV9eMToVsI2OFQc1 +xnFZEV7NZVymh5HjKM1jwFy6es+5TFoMtRu6vDxKS6Y13lIlZ4oQSh8aXtG5Ylt2 +CqsKNHyDbZUpvKe/k19TBmVXQBCYFuN733jI9/4JBtpygnxwt1aXCvq/PFFGsTS4 +p1JOQvr/jaD7b4JO6IMXH1kSVxiMXKXNG7wPUNr6OWJvc7OqdclsZa7ibEx4L52x +DuFmsxQo4a3iibhbcjr436OmR5Uw2UAstB5qxWfMhkt+e7rRhCOh/3O7SAYEpt+f +Zr2VFXdGme4kR6uMCzgGiSh0qCseQXpJUZVufn/Go9r+601OJTJIQ9a2VoqlMR8o +Dd14D0gBXXaZkY60Mh8iXR/MjKDuv0KBUyBzfcpk3fLmv0PhGSkbn6j+q1jZbogm +EhI0AL5s2EoofuBdvgdusBhCrrwCMonprqR7BuaKPD0GEw5utnT5ovcUg/sjMJox +10100QwAzQScU4iG/xic/TsN+ZMumhUcYs003MsZkRLvCEFxZurEMx7819CqfhIc +NGd7ETTBSwoNf5pXRTHaTbW6pPiIeWunLUUVsRcNoBtL/cXmg+mu1zdsD7nD51mJ +vG9A7LPW7XVl2Jv2NgQoKkHYO7cVozmcz6AE2z1q+XN4LGto8JEZktb6E7UIyXXg +Ls4Tv0sn5TLgtaJ31w4+9iybNiGoVYOc4h0s5DoNR4ivcZ6n/Qnf8PTrNzejEJY6 +R/UnDbc24u0palGc1kei99d0BYodnq4OlAj7M7ML0GncftInhgA0Dp81YG5PujMa +irhvwtnD5Xysfh1YrroAEN7Qxc8+2JlpgNSFlFFkMgfibc6jvTX6/C6MaFz8hiOq +W43ZBEzjMIs23ZrJKOJGsuTdHSob+VbvqIMgS2PeGb/6g3/GjdipCbynNhX3zUOM +3j/lpZOiAwE/Bftr5FOSfTFpnyorIIeyWgROEZTTL4eSYvnBjzf+tUdXY7ltxJie +q0rpQ42X7+B4gTo8Qj/xC7LXSCldERK57cCwwITvjcHwxPyOiJ9BMI1HlRQ/Fo3C +lPYIst1xjJ67qrTm6mWkor2hUOZcg4MOOzXWuijWRGJ/Wz0H+GKWtoE2X536D6sy +a4Nwwj09oFY4Fph/SUNwy0MLpTSzikpUx6mxjbs3Odvo6tWWVcicp/dCWYCqLpGU +3axEb/qlsaRNtKJg9O3Fq7hh1BTyLNGB2ET5wSKtlSD0bDeF15bBvkHB3z2/lDls +YQ2hEHMjeSEZZyGTPqEHwtBuUwiWBBXwOIhT8nfYXbHWR0CLBLth2+E/JCaO9hD2 +V277arqNFa8nugZMwS+ragi6vbgIX4BiS/rnfYXgqaxD +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/public_key/test/pbe_SUITE_data/pbes2_aes_192_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_192_enc_key.pem new file mode 100644 index 0000000000..ee82e9f667 --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_192_enc_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIcqBCM7v+ZlkCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEWBBD93r4IWBhvry+cdfwIDOKeBIIE +0DXM8S70sMsUmwxRZQtKwGfYddEWIc9lrEdsgEEuonF6NrseRq7QdXnBSPwq5f0O +ofMZ/0OCun3Qg1ls1EdsyKdijSOq27ZhHCnmWi1Rw1ApJIAq5i/jY8U17+lUakvG +VtcsuRzlKmFxbBW44kLK7vK6xiA76HPx0I4ZXcdywR0pbLT1ubbhbQ9djLnBiYkT +odszGTyxNceEse1Hu/RhFK17tnwov0fdioKY2i9F7qfq8lYLPrusEKTY7tOVjFOh +bXeCry1BL0KTt65JVGR9xQCI0qokEU0QrCgD6skq7Vx2C/Ho1sW6h8FBFVIm6ozO +bEUtVk3Xgs5yieetha1GxJAang1VxAPemnXfOmVapoSgSv1BQyDdnk3067Sfkh64 +A5yf44BUjvJsSd/ViCVmCryoXU7KOMAdFkyRSiDDLQus6bZGEhc6f+VEikG+TZ2L +xxY4OucE2Bz67S6ycyOUpXKo0+FW0juE6NTJdlYSXWOvfciZKA83h6yAej6MfUEu +4orIvnCTVO7i3+hHybnSgftj42jrqqZzeXll8rkGHg4syrKRVaDD6qfJjgAHBJkJ +pZT4zZwuJ1puWfBykI25S4mKUnk0erq4N5jpGqdm7U14fWBWCjZN85jY4WgZZOJx +kBNO2NbmZKzZEzRGyMJ563z4l7MNfzZBHv+FeBNkX146J4ZhMbT8IXPGV9peNWqu +mY2B9RhN4hlDrd3Hfz5uiiF3UGrFkDcsPRBHWGqQ20YpuOQNno7iL8N0FWauERw1 +dvxAGVwFfUznR3wc/eyGcnRhqQhlYPspukh0IVIyEbre3yVFSG/41GQYQfg08XYd +LYiiDUu1i515/GeDvYN5VcnZ4nMhPgqfxW4rEUZjI86p++bqwqGy8eOCivkzGV3A +IFWQwlvKKzU7tSdi3uHUq5v7xQsJrALdf67JVjCCGfUZa17O41vmm58L/vKhhL2Y +mLz/H004DPsB+CtWoLwqZ8Jmb1EHwqNbna3tGHn3n63j2cV7gykZFa/zXeuBbbJ/ +t4ZIojIEzwAVKA9Xzcl3wyGCRr62WJPEcOqe4kBYREuKd22juPEm9RQgciIIj0tP +eJVpD0QarGGzERsaq7pheAiWisO+Q4cLjF8Mb3/r89abnd4AQk6meabFJIE2dXWp +LZy3I6FkNQ7L7LxNOILhnaWzWGdOBVwHeAAxfbLOzM22ewj7oUwBCRpsBJ8zl2PL +VhUjX6N26YoiR9gE1RBaVrwRkYLmkyGvrowCDoZVPxvJqbfIESQE42zGB9DbEPNp +WXCnzAg5cIjNC31We274yLE7dpNPVRXPJCRhtp7noorWVzDdKB+dFvg08bIir6Vj +1gxy8DvuZE1Gq9vqx38V7Cy2MrSpsgapw5mli4n5cMafE7Ty3j5pBJFF2f3jUn6B +7MjCrKp1d8v6MEy18J/Ugu1Lytb92LMcNtWBKmqyCSxekrUB9/FC2hWqOpdwRI6q +QMWkwshjyEhmlr2PAkBPM4uVzUFc9lBw1GzOUChkr9jiINdbsUSRJrwZ32Nc3gRY +yKzWbEELPSgRcXwXgH3QqZukvmk2tBMTIxilXqKTLmd7t/AEnIhkbqC0pfnyChyU +YlFkme0RpAXpgbDJgv+Vk+1/1s6gyaNSzT4s2Q340WIO +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/public_key/test/pbe_SUITE_data/pbes2_aes_256_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_256_enc_key.pem new file mode 100644 index 0000000000..050337aead --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_256_enc_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI4MxgpDiHxQcCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBA2g/L8XmlK2axDkeYJCltnBIIE +0C3+NQ93DzEK/9qicy1sj0Vag1M7AeJjTGGpatETCxM+eHjk4kNNeDeMV5+EmCSu +Db4P48uvHOBGGCcqdjnQovfQsAh81GWxgF3yqpd4OKn2RubMLO4/Qu+zGtt/XRKz +T0pyHHBu6hyPSOhad2SIjKWuaHepwxGYaejLP83sy6yhm0sEmyBUn4nGSTOROcqR +wd7EbwU2PYUcrRGGxtChU7MUNt48wBO50Xmri1ssPPtZV6MHio4IoIz4hqzCjvAc +VE1BqAvNIJ7icpdnL8Jqq0lfwEmGjFCkAjgov5fNW9I1b44jE2Tv5LM2urMH8InQ +9qNjTHozYQhHAk9nX4cmMgHsIhkOd7Z2M+nz8Hd1tj9DmBNOr5XbfyctgVntaMB4 +GGnThuNlX8d5giOKOcaNPMpLU1jtfDcb73mEhwCYcdo1PM0rjrYZ7qetjXJW/oHs +Nl/hIZIRpMuCRVuXHml4G+ziKbMnXUN8sbtvgkQatYFHFQOhAqZeyzWp8SlDcfqb +Zt0LlZVJEhKUYzZgKoe7SmR1rXTTCfYeB75PddyYwVgf/IkT6HJ/y1apGOP6/UJ8 +7UV6zssQA35gMsYDT36sH2hAQvA/cOFxSxrip0gm0xXOeFF0gbyZWbFqk0aULaeF +rbBoMe28akxdE4eD06b+TP2NguUGP72l3TPOlG4PQVScweMw9L3oPXOVj4Vbbd0y +DenNvRHlWIwOh/y7ADTHSWq9CE45QDBvFaTcn43JQWD8xCmhAhI/9H+fhAQUhABm +P5QoJLE2IGo8A+Gi7rfgYQb3fCgqcn8azsRJzozhE+oXxMvxEESejYTtm26FNmLg +ONTWysF9BiaKHt2IXwRX97691wZqv5wJEaxeeJxfVQ6MlAHoEDXe49VxGN4zFXuq +Yb71JdQDgM94jwc/PoUwFH2ALSkIciiKwU0xfFpptycl4qWpy9m7QTIKw0DjgCfg +MuySPRGM5jn3yVg72ux2Qf9MKNEybWjZ+Se9MJ1IZmZK5eOo6L2JsFCc0nRn908E +vn4gAgUfMxyCZ1ygXfxINVAixR+6KPHsz1QTIxTZkrlnXRsuEu1ZfBSHzmXESvJo +3I9PkP/Iekg1FBpB5xxd7mXwCj17EWqYXWsLnfd8SblMjRYd64q7hfx0oU/MJ1wi +KadkGcyAGVRyleJRBR0LleYj/2sDihrRQY4zu5UtzSMFMH0XWjSWk5+ZQb+z3iDc +Ud4GHcHiuTMH+i03ApZGWLN9v93za/15fsnZogstgJkaHxizTz5JuCkRf15xd8+O +EH77Tsfizjp+h2NF/wcr4OSD0i+H0mwZWajpZ3UmSeJ0BFK6ODEbmVycrInpHo3n +zyMJnEDTJXL3HUwZSLjO5e5cNaB+75tdHrj2yJtRLuaJFr02b0EO1MUYfuUuqlK4 +7mg7FkBsimW+CXkoLRjHYK88ibT3G+rZ/STf4S/jxiRjBi06FAql3H02K5i1umgB +0BaaQei0Z8wQxMeTEnGzL+OcJeqDA1ZRFeXe7DNGsX1jeTYKPHA/Dr2IdZqyiCr2 +xh6e7RJuUe4D2liXW8LlMdwhN/7xSinA031PgBmb8XzSRmfdHhytFkA8PiM5T2ew +NR3qXBJ/G7BuRa/t26RuKI3BMVoBQPhGx80ds10uJjxq +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 6d64a45112..9627b70eeb 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -99,7 +99,7 @@ APP_TARGET= $(EBIN)/$(APP_FILE) APPUP_SRC= $(APPUP_FILE).src APPUP_TARGET= $(EBIN)/$(APPUP_FILE) -INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_userauth.hrl ssh_xfer.hrl +INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_xfer.hrl # ---------------------------------------------------- # FLAGS diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 04453e6ef0..54e98ee10e 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -68,6 +68,25 @@ -define(string(X), ?string_utf8(X)). -define(binary(X), << ?STRING(X) >>). +-define('2bin'(X), (if is_binary(X) -> X; + is_list(X) -> list_to_binary(X); + X==undefined -> <<>> + end) ). + +%% encoding macros +-define('E...'(X), ?'2bin'(X)/binary ). +-define(Eboolean(X), ?BOOLEAN(case X of + true -> ?TRUE; + false -> ?FALSE + end) ). +-define(Ebyte(X), ?BYTE(X) ). +-define(Euint32(X), ?UINT32(X) ). +-define(Estring(X), ?STRING(?'2bin'(X)) ). +-define(Estring_utf8(X), ?string_utf8(X)/binary ). +-define(Ename_list(X), ?STRING(ssh_bits:name_list(X)) ). +-define(Empint(X), (ssh_bits:mpint(X))/binary ). +-define(Ebinary(X), ?STRING(X) ). + %% Cipher details -define(SSH_CIPHER_NONE, 0). -define(SSH_CIPHER_3DES, 3). diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl index d95e58c1bb..7c86a81108 100644 --- a/lib/ssh/src/ssh_message.erl +++ b/lib/ssh/src/ssh_message.erl @@ -34,24 +34,6 @@ -export([dbg_trace/3]). --define('2bin'(X), (if is_binary(X) -> X; - is_list(X) -> list_to_binary(X); - X==undefined -> <<>> - end) ). - --define('E...'(X), ?'2bin'(X)/binary ). --define(Eboolean(X), ?BOOLEAN(case X of - true -> ?TRUE; - false -> ?FALSE - end) ). --define(Ebyte(X), ?BYTE(X) ). --define(Euint32(X), ?UINT32(X) ). --define(Estring(X), ?STRING(?'2bin'(X)) ). --define(Estring_utf8(X), ?string_utf8(X)/binary ). --define(Ename_list(X), ?STRING(ssh_bits:name_list(X)) ). --define(Empint(X), (ssh_bits:mpint(X))/binary ). --define(Ebinary(X), ?STRING(X) ). - ucl(B) -> try unicode:characters_to_list(B) of L when is_list(L) -> L; diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index eaab13433a..a85926354e 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -61,14 +61,6 @@ -export([pack/3, adjust_algs_for_peer_version/2]). -export([decompress/2, decrypt_blocks/3, is_valid_mac/3 ]). % FIXME: remove --define(Estring(X), ?STRING((if is_binary(X) -> X; - is_list(X) -> list_to_binary(X); - X==undefined -> <<>> - end))). --define(Empint(X), (ssh_bits:mpint(X))/binary ). --define(Ebinary(X), ?STRING(X) ). --define(Euint32(X), ?UINT32(X) ). - %%%---------------------------------------------------------------------------- %%% %%% There is a difference between supported and default algorithms. The diff --git a/lib/ssh/src/ssh_userauth.hrl b/lib/ssh/src/ssh_userauth.hrl deleted file mode 100644 index 2cfc1f0f83..0000000000 --- a/lib/ssh/src/ssh_userauth.hrl +++ /dev/null @@ -1,78 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% - -%%% Description: user authentication protocol - --define(SSH_MSG_USERAUTH_REQUEST, 50). --define(SSH_MSG_USERAUTH_FAILURE, 51). --define(SSH_MSG_USERAUTH_SUCCESS, 52). --define(SSH_MSG_USERAUTH_BANNER, 53). --define(SSH_MSG_USERAUTH_PK_OK, 60). --define(SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 60). --define(SSH_MSG_USERAUTH_INFO_REQUEST, 60). --define(SSH_MSG_USERAUTH_INFO_RESPONSE, 61). - --record(ssh_msg_userauth_request, - { - user, %% string - service, %% string - method, %% string "publickey", "password" - data %% opaque - }). - --record(ssh_msg_userauth_failure, - { - authentications, %% string - partial_success %% boolean - }). - --record(ssh_msg_userauth_success, - { - }). - --record(ssh_msg_userauth_banner, - { - message, %% string - language %% string - }). - --record(ssh_msg_userauth_passwd_changereq, - { - prompt, %% string - languge %% string - }). - --record(ssh_msg_userauth_pk_ok, - { - algorithm_name, % string - key_blob % string - }). - --record(ssh_msg_userauth_info_request, - {name, - instruction, - language_tag, - num_prompts, - data}). --record(ssh_msg_userauth_info_response, - {num_responses, - data}). diff --git a/lib/ssh/test/ssh_bench_SUITE.erl b/lib/ssh/test/ssh_bench_SUITE.erl index 880c519a5e..5ff7a71c45 100644 --- a/lib/ssh/test/ssh_bench_SUITE.erl +++ b/lib/ssh/test/ssh_bench_SUITE.erl @@ -26,7 +26,7 @@ -include_lib("ssh/src/ssh.hrl"). -include_lib("ssh/src/ssh_transport.hrl"). -include_lib("ssh/src/ssh_connect.hrl"). --include_lib("ssh/src/ssh_userauth.hrl"). +-include_lib("ssh/src/ssh_auth.hrl"). %%%================================================================ %%% diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 01cd630668..6af5a500b5 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -27,6 +27,36 @@ </header> <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 9.3.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Returned "alert error string" is now same as logged alert + string</p> + <p> + Own Id: OTP-15844</p> + </item> + <item> + <p> + Fix returned extension map fields to follow the + documentation.</p> + <p> + Own Id: OTP-15862 Aux Id: ERL-951 </p> + </item> + <item> + <p> + Avoid DTLS crash due to missing gen_server return value + in DTLS packet demux process.</p> + <p> + Own Id: OTP-15864 Aux Id: ERL-962 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.3.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -119,6 +149,22 @@ </section> +<section><title>SSL 9.2.3.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Returned "alert error string" is now same as logged alert + string</p> + <p> + Own Id: OTP-15844</p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.2.3.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml index 3bd86178c8..3a472d4776 100644 --- a/lib/ssl/doc/src/standards_compliance.xml +++ b/lib/ssl/doc/src/standards_compliance.xml @@ -340,8 +340,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -479,8 +479,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 6928d7a93d..b220691e79 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -836,9 +836,12 @@ initial_flight_state(_) -> next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ dtls_record_buffer = Buf0, dtls_cipher_texts = CT0} = Buffers, + connection_env = #connection_env{negotiated_version = Version}, + static_env = #static_env{data_tag = DataTag}, ssl_options = SslOpts} = State0) -> case dtls_record:get_dtls_records(Data, - acceptable_record_versions(StateName, State0), + {DataTag, StateName, Version, + [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_DATAGRAM_VERSIONS]}, Buf0, SslOpts) of {Records, Buf1} -> CT1 = CT0 ++ Records, @@ -849,10 +852,6 @@ next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ Alert end. -acceptable_record_versions(hello, _) -> - [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_DATAGRAM_VERSIONS]; -acceptable_record_versions(_, #state{connection_env = #connection_env{negotiated_version = Version}}) -> - [Version]. dtls_handshake_events(Packets) -> lists:map(fun(Packet) -> diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl index c6431b55a9..94b350eaa5 100644 --- a/lib/ssl/src/dtls_packet_demux.erl +++ b/lib/ssl/src/dtls_packet_demux.erl @@ -154,9 +154,9 @@ handle_info({Transport, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket handle_info({PassiveTag, Socket}, #state{active_n = N, listener = Socket, - transport = {_,_,_, udp_error, PassiveTag}}) -> - next_datagram(Socket, N); - + transport = {_, _, _, _, PassiveTag}} = State) -> + next_datagram(Socket, N), + {noreply, State}; %% UDP socket does not have a connection and should not receive an econnreset %% This does however happens on some windows versions. Just ignoring it %% appears to make things work as expected! diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index a4846f42c5..8b8db7b2de 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -162,26 +162,16 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}}, Epoch. %%-------------------------------------------------------------------- --spec get_dtls_records(binary(), [ssl_record:ssl_version()], binary(), +-spec get_dtls_records(binary(), {atom(), atom(), ssl_record:ssl_version(), [ssl_record:ssl_version()]}, binary(), #ssl_options{}) -> {[binary()], binary()} | #alert{}. %% %% Description: Given old buffer and new data from UDP/SCTP, packs up a records %% and returns it as a list of tls_compressed binaries also returns leftover %% data %%-------------------------------------------------------------------- -get_dtls_records(Data, Versions, Buffer, SslOpts) -> +get_dtls_records(Data, Vinfo, Buffer, SslOpts) -> BinData = list_to_binary([Buffer, Data]), - case erlang:byte_size(BinData) of - N when N >= 3 -> - case assert_version(BinData, Versions) of - true -> - get_dtls_records_aux(BinData, [], SslOpts); - false -> - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) - end; - _ -> - get_dtls_records_aux(BinData, [], SslOpts) - end. + get_dtls_records_aux(Vinfo, BinData, [], SslOpts). %%==================================================================== %% Encoding DTLS records @@ -405,52 +395,49 @@ initial_connection_state(ConnectionEnd, BeastMitigation) -> client_verify_data => undefined, server_verify_data => undefined }. -assert_version(<<?BYTE(_), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>>, Versions) -> - is_acceptable_version({MajVer, MinVer}, Versions). -get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) -> - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), - Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) when MajVer >= 128 -> - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, - Rest/binary>> = RawDTLSRecord, Acc, SslOpts) -> +get_dtls_records_aux({DataTag, StateName, _, Versions} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), + ?UINT16(Epoch), ?UINT48(SequenceNumber), + ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, + Acc, SslOpts) when ((StateName == hello) orelse + ((StateName == certify) andalso (DataTag == udp)) orelse + ((StateName == abbreviated) andalso(DataTag == udp))) + andalso + ((Type == ?HANDSHAKE) orelse + (Type == ?ALERT)) -> ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer), + case is_acceptable_version({MajVer, MinVer}, Versions) of + true -> + get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type, + version = {MajVer, MinVer}, + epoch = Epoch, sequence_number = SequenceNumber, + fragment = Data} | Acc], SslOpts); + false -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; +get_dtls_records_aux({_, _, Version, _} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) -> + Acc, SslOpts) when (Type == ?APPLICATION_DATA) orelse + (Type == ?HANDSHAKE) orelse + (Type == ?ALERT) orelse + (Type == ?CHANGE_CIPHER_SPEC) -> ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer), + case {MajVer, MinVer} of + Version -> + get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type, + version = {MajVer, MinVer}, + epoch = Epoch, sequence_number = SequenceNumber, + fragment = Data} | Acc], SslOpts); + _ -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; +get_dtls_records_aux(_, <<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer), ?UINT16(Length), _/binary>>, _Acc, _) when Length > ?MAX_CIPHER_TEXT_LENGTH -> ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW); -get_dtls_records_aux(Data, Acc, _) -> +get_dtls_records_aux(_, Data, Acc, _) -> case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of true -> {lists:reverse(Acc), Data}; diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 6af65e09f2..20b1e85ceb 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -125,7 +125,10 @@ protocol_extensions/0, session_id/0, error_alert/0, - srp_param_type/0]). + tls_alert/0, + srp_param_type/0, + named_curve/0, + sign_scheme/0]). %% ------------------------------------------------------------------------------------------------------- @@ -191,7 +194,8 @@ | rsa_pss_pss_sha384 | rsa_pss_pss_sha512 | rsa_pkcs1_sha1 - | ecdsa_sha1. + | ecdsa_sha1. % exported + -type kex_algo() :: rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa | ecdh_ecdsa | ecdh_rsa | @@ -236,7 +240,7 @@ sect163r2 | secp160k1 | secp160r1 | - secp160r2. + secp160r2. % exported -type group() :: secp256r1 | secp384r1 | secp521r1 | ffdhe2048 | ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. @@ -279,7 +283,7 @@ bad_certificate_status_response | bad_certificate_hash_value | unknown_psk_identity | - no_application_protocol. + no_application_protocol. % exported %% ------------------------------------------------------------------------------------------------------- -type common_option() :: {protocol, protocol()} | @@ -1909,7 +1913,7 @@ validate_option(Opt, Value) -> throw({error, {options, {Opt, Value}}}). handle_cb_info({V1, V2, V3, V4}, {_,_,_,_,_}) -> - {V1,V2,V3,V4, list_to_atom(atom_to_list(V2) ++ "passive")}; + {V1,V2,V3,V4, list_to_atom(atom_to_list(V2) ++ "_passive")}; handle_cb_info(CbInfo, _) -> CbInfo. diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index 06b1b005a5..2d57b72f7b 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -32,7 +32,11 @@ -include("ssl_record.hrl"). -include("ssl_internal.hrl"). --export([decode/1, own_alert_txt/1, alert_txt/1, reason_code/2]). +-export([decode/1, + own_alert_txt/1, + alert_txt/1, + alert_txt/4, + reason_code/4]). %%==================================================================== %% Internal application API @@ -48,20 +52,29 @@ decode(Bin) -> decode(Bin, [], 0). %%-------------------------------------------------------------------- -%% -spec reason_code(#alert{}, client | server) -> -%% {tls_alert, unicode:chardata()} | closed. -%-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}. +-spec reason_code(#alert{}, client | server, ProtocolName::string(), StateName::atom()) -> + {tls_alert, {atom(), unicode:chardata()}} | closed. %% %% Description: Returns the error reason that will be returned to the %% user. %%-------------------------------------------------------------------- -reason_code(#alert{description = ?CLOSE_NOTIFY}, _) -> +reason_code(#alert{description = ?CLOSE_NOTIFY}, _, _, _) -> closed; -reason_code(#alert{description = Description, role = Role} = Alert, Role) -> - {tls_alert, {description_atom(Description), own_alert_txt(Alert)}}; -reason_code(#alert{description = Description} = Alert, Role) -> - {tls_alert, {description_atom(Description), alert_txt(Alert#alert{role = Role})}}. +reason_code(#alert{description = Description, role = Role} = Alert, Role, ProtocolName, StateName) -> + Txt = lists:flatten(alert_txt(ProtocolName, Role, StateName, own_alert_txt(Alert))), + {tls_alert, {description_atom(Description), Txt}}; +reason_code(#alert{description = Description} = Alert, Role, ProtocolName, StateName) -> + Txt = lists:flatten(alert_txt(ProtocolName, Role, StateName, alert_txt(Alert))), + {tls_alert, {description_atom(Description), Txt}}. + +%%-------------------------------------------------------------------- +-spec alert_txt(string(), server | client, StateNam::atom(), string()) -> string(). +%% +%% Description: Generates alert text for log or string part of error return. +%%-------------------------------------------------------------------- +alert_txt(ProtocolName, Role, StateName, Txt) -> + io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]). %%-------------------------------------------------------------------- -spec own_alert_txt(#alert{}) -> string(). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 345db7510f..cc4d60389e 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -124,7 +124,7 @@ handshake(#sslsocket{pid = [Pid|_]} = Socket, Timeout) -> connected -> {ok, Socket}; {ok, Ext} -> - {ok, Socket, Ext}; + {ok, Socket, no_records(Ext)}; Error -> Error end. @@ -328,34 +328,33 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> %%==================================================================== %% Alert and close handling %%==================================================================== -handle_own_alert(Alert, _, StateName, +handle_own_alert(Alert0, _, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, ssl_options = SslOpts} = State) -> try %% Try to tell the other side - send_alert(Alert, StateName, State) + send_alert(Alert0, StateName, State) catch _:_ -> %% Can crash if we are in a uninitialized state ignore end, try %% Try to tell the local user - log_alert(SslOpts#ssl_options.log_level, Role, - Connection:protocol_name(), StateName, - Alert#alert{role = Role}), + Alert = Alert0#alert{role = Role}, + log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert), handle_normal_shutdown(Alert,StateName, State) catch _:_ -> ok end, {stop, {shutdown, own_alert}, State}. -handle_normal_shutdown(Alert, _, #state{static_env = #static_env{role = Role, - socket = Socket, - transport_cb = Transport, - protocol_cb = Connection, - tracker = Tracker}, - handshake_env = #handshake_env{renegotiation = {false, first}}, - start_or_recv_from = StartFrom} = State) -> +handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = Role, + socket = Socket, + transport_cb = Transport, + protocol_cb = Connection, + tracker = Tracker}, + handshake_env = #handshake_env{renegotiation = {false, first}}, + start_or_recv_from = StartFrom} = State) -> Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker,Socket, StartFrom, Alert, Role, Connection); + alert_user(Pids, Transport, Tracker,Socket, StartFrom, Alert, Role, StateName, Connection); handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = Role, socket = Socket, @@ -366,9 +365,9 @@ handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = socket_options = Opts, start_or_recv_from = RecvFrom} = State) -> Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection). + alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, StateName, Connection). -handle_alert(#alert{level = ?FATAL} = Alert, StateName, +handle_alert(#alert{level = ?FATAL} = Alert0, StateName, #state{static_env = #static_env{role = Role, socket = Socket, host = Host, @@ -382,11 +381,11 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName, session = Session, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), + Alert = Alert0#alert{role = opposite_role(Role)}, log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), - StateName, Alert#alert{role = opposite_role(Role)}), + StateName, Alert), Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, - opposite_role(Role), Connection), + alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, StateName, Connection), {stop, {shutdown, normal}, State}; handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, @@ -396,13 +395,14 @@ handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, StateName, State) -> handle_normal_shutdown(Alert, StateName, State), {stop,{shutdown, peer_close}, State}; -handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, +handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert0, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, handshake_env = #handshake_env{renegotiation = {true, internal}}, ssl_options = SslOpts} = State) -> + Alert = Alert0#alert{role = opposite_role(Role)}, log_alert(SslOpts#ssl_options.log_level, Role, - Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), + Connection:protocol_name(), StateName, Alert), handle_normal_shutdown(Alert, StateName, State), {stop,{shutdown, peer_close}, State}; @@ -709,6 +709,7 @@ handle_session(#server_hello{cipher_suite = CipherSuite, {ExpectNPN, Protocol} = case Protocol0 of undefined -> + {false, CurrentProtocol}; _ -> {ProtoExt =:= npn, Protocol0} @@ -1445,7 +1446,7 @@ handle_info({ErrorTag, Socket, econnaborted}, StateName, } = State) when StateName =/= connection -> Pids = Connection:pids(State), alert_user(Pids, Transport, Tracker,Socket, - StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, Connection), + StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, StateName, Connection), {stop, {shutdown, normal}, State}; handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_env{socket = Socket, @@ -2907,22 +2908,22 @@ send_user(Pid, Msg) -> Pid ! Msg, ok. -alert_user(Pids, Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role, Connection) -> - alert_user(Pids, Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role, Connection); -alert_user(Pids, Transport, Tracker, Socket,_, _, _, From, Alert, Role, Connection) -> - alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, Connection). +alert_user(Pids, Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role, StateName, Connection) -> + alert_user(Pids, Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role, StateName, Connection); +alert_user(Pids, Transport, Tracker, Socket,_, _, _, From, Alert, Role, StateName, Connection) -> + alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, StateName, Connection). -alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, Connection) -> - alert_user(Pids, Transport, Tracker, Socket, false, no_pid, From, Alert, Role, Connection). +alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, StateName, Connection) -> + alert_user(Pids, Transport, Tracker, Socket, false, no_pid, From, Alert, Role, StateName, Connection). -alert_user(_, _, _, _, false = Active, Pid, From, Alert, Role, _) when From =/= undefined -> +alert_user(_, _, _, _, false = Active, Pid, From, Alert, Role, StateName, Connection) when From =/= undefined -> %% If there is an outstanding ssl_accept | recv %% From will be defined and send_or_reply will %% send the appropriate error message. - ReasonCode = ssl_alert:reason_code(Alert, Role), + ReasonCode = ssl_alert:reason_code(Alert, Role, Connection:protocol_name(), StateName), send_or_reply(Active, Pid, From, {error, ReasonCode}); -alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Connection) -> - case ssl_alert:reason_code(Alert, Role) of +alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, StateName, Connection) -> + case ssl_alert:reason_code(Alert, Role, Connection:protocol_name(), StateName) of closed -> send_or_reply(Active, Pid, From, {ssl_closed, Connection:socket(Pids, Transport, Socket, Tracker)}); @@ -2933,11 +2934,11 @@ alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Con log_alert(Level, Role, ProtocolName, StateName, #alert{role = Role} = Alert) -> Txt = ssl_alert:own_alert_txt(Alert), - Report = io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]), + Report = ssl_alert:alert_txt(ProtocolName, Role, StateName, Txt), ssl_logger:notice(Level, Report); log_alert(Level, Role, ProtocolName, StateName, Alert) -> Txt = ssl_alert:alert_txt(Alert), - Report = io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]), + Report = ssl_alert:alert_txt(ProtocolName, Role, StateName, Txt), ssl_logger:notice(Level, Report). invalidate_session(client, Host, Port, Session) -> @@ -3000,3 +3001,8 @@ new_emulated([], EmOpts) -> EmOpts; new_emulated(NewEmOpts, _) -> NewEmOpts. + +no_records(Extensions) -> + maps:map(fun(_, Value) -> + ssl_handshake:extension_value(Value) + end, Extensions). diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index ff7207a8ce..844368c761 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -66,6 +66,7 @@ sni_hostname = undefined, expecting_next_protocol_negotiation = false ::boolean(), next_protocol = undefined :: undefined | binary(), + alpn = undefined, %% Used in TLS 1.3 negotiated_protocol, hashsign_algorithm = {undefined, undefined}, cert_hashsign_algorithm = {undefined, undefined}, @@ -76,7 +77,7 @@ srp_params :: #srp_user{} | secret_printout() | 'undefined', public_key_info :: ssl_handshake:public_key_info() | 'undefined', premaster_secret :: binary() | secret_printout() | 'undefined', - server_psk_identity :: binary() | 'undefined' % server psk identity hint + server_psk_identity :: binary() | 'undefined' % server psk identity hint }). -record(connection_env, { diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index b51ba0fa2d..1f0c95701b 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -76,7 +76,8 @@ handle_client_hello_extensions/9, %% Returns server hello extensions handle_server_hello_extensions/9, select_curve/2, select_curve/3, select_hashsign/4, select_hashsign/5, - select_hashsign_algs/3, empty_extensions/2, add_server_share/3 + select_hashsign_algs/3, empty_extensions/2, add_server_share/3, + add_alpn/2, add_selected_version/1, decode_alpn/1 ]). -export([get_cert_params/1, @@ -363,7 +364,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef, CertDbHandle, CertDbRef) end catch - error:{badmatch,{asn1, Asn1Reason}} -> + error:{badmatch,{error, {asn1, Asn1Reason}}} -> %% ASN-1 decode of certificate somehow failed ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, {failed_to_decode_certificate, Asn1Reason}); error:OtherReason -> @@ -1165,6 +1166,13 @@ add_server_share(hello_retry_request, Extensions, Extensions#{key_share => #key_share_hello_retry_request{ selected_group = Group}}. +add_alpn(Extensions, ALPN0) -> + ALPN = encode_alpn([ALPN0], false), + Extensions#{alpn => ALPN}. + +add_selected_version(Extensions) -> + SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, + Extensions#{server_hello_selected_version => SupportedVersions}. kse_remove_private_key(#key_share_entry{ group = Group, diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 2651fc09bd..dabc2f8ec8 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -317,8 +317,7 @@ handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, _ -> HsEnv = State#state.handshake_env, {next_state, StateName, - State#state{protocol_buffers = Buffers, - handshake_env = + State#state{handshake_env = HsEnv#handshake_env{unprocessed_handshake_events = unprocessed_events(Events)}}, Events} end diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index 821b7000cc..117e4f059d 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -228,8 +228,8 @@ wait_cert_cr(internal, #change_cipher_spec{}, State, _Module) -> tls_connection:next_event(?FUNCTION_NAME, no_record, State); wait_cert_cr(internal, #certificate_1_3{} = Certificate, State0, _Module) -> case tls_handshake_1_3:do_wait_cert_cr(Certificate, State0) of - #alert{} = Alert -> - ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert_cr, State0); + {#alert{} = Alert, State} -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert_cr, State); {State1, NextState} -> tls_connection:next_event(NextState, no_record, State1) end; diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 12ab2015aa..4de51c9a35 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -39,8 +39,7 @@ %% Create handshake messages -export([certificate/5, certificate_verify/4, - encrypted_extensions/0, - server_hello/4]). + encrypted_extensions/0]). -export([do_start/2, do_negotiated/2, @@ -62,10 +61,10 @@ %% Create handshake messages %%==================================================================== -server_hello(MsgType, SessionId, KeyShare, ConnectionStates) -> +server_hello(MsgType, SessionId, KeyShare, ConnectionStates, ALPN) -> #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read), - Extensions = server_hello_extensions(MsgType, KeyShare), + Extensions = server_hello_extensions(MsgType, KeyShare, ALPN), #server_hello{server_version = {3,3}, %% legacy_version cipher_suite = SecParams#security_parameters.cipher_suite, compression_method = 0, %% legacy attribute @@ -74,10 +73,26 @@ server_hello(MsgType, SessionId, KeyShare, ConnectionStates) -> extensions = Extensions }. -server_hello_extensions(MsgType, KeyShare) -> +%% The server's extensions MUST contain "supported_versions". +%% Additionally, it SHOULD contain the minimal set of extensions +%% necessary for the client to generate a correct ClientHello pair. As +%% with the ServerHello, a HelloRetryRequest MUST NOT contain any +%% extensions that were not first offered by the client in its +%% ClientHello, with the exception of optionally the "cookie" (see +%% Section 4.2.2) extension. +server_hello_extensions(hello_retry_request = MsgType, KeyShare, _) -> SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, Extensions = #{server_hello_selected_version => SupportedVersions}, - ssl_handshake:add_server_share(MsgType, Extensions, KeyShare). + ssl_handshake:add_server_share(MsgType, Extensions, KeyShare); +server_hello_extensions(MsgType, KeyShare, undefined) -> + SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, + Extensions = #{server_hello_selected_version => SupportedVersions}, + ssl_handshake:add_server_share(MsgType, Extensions, KeyShare); +server_hello_extensions(MsgType, KeyShare, ALPN0) -> + Extensions0 = ssl_handshake:add_selected_version(#{}), %% {3,4} (TLS 1.3) + Extensions1 = ssl_handshake:add_alpn(Extensions0, ALPN0), + ssl_handshake:add_server_share(MsgType, Extensions1, KeyShare). + server_hello_random(server_hello, #security_parameters{server_random = Random}) -> Random; @@ -469,7 +484,8 @@ do_start(#client_hello{cipher_suites = ClientCiphers, #state{connection_states = _ConnectionStates0, ssl_options = #ssl_options{ciphers = ServerCiphers, signature_algs = ServerSignAlgs, - supported_groups = ServerGroups0}, + supported_groups = ServerGroups0, + alpn_preferred_protocols = ALPNPreferredProtocols}, session = #session{own_certificate = Cert}} = State0) -> ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined), ClientGroups = get_supported_groups(ClientGroups0), @@ -478,6 +494,9 @@ do_start(#client_hello{cipher_suites = ClientCiphers, ClientShares0 = maps:get(key_share, Extensions, undefined), ClientShares = get_key_shares(ClientShares0), + ClientALPN0 = maps:get(alpn, Extensions, undefined), + ClientALPN = ssl_handshake:decode_alpn(ClientALPN0), + ClientSignAlgs = get_signature_scheme_list( maps:get(signature_algs, Extensions, undefined)), ClientSignAlgsCert = get_signature_scheme_list( @@ -486,6 +505,9 @@ do_start(#client_hello{cipher_suites = ClientCiphers, {Ref,Maybe} = maybe(), try + %% Handle ALPN extension if ALPN is configured + ALPNProtocol = Maybe(handle_alpn(ALPNPreferredProtocols, ClientALPN)), + %% If the server does not select a PSK, then the server independently selects a %% cipher suite, an (EC)DHE group and key share for key establishment, %% and a signature algorithm/certificate pair to authenticate itself to @@ -511,8 +533,14 @@ do_start(#client_hello{cipher_suites = ClientCiphers, %% Generate server_share KeyShare = ssl_cipher:generate_server_share(Group), - State1 = update_start_state(State0, Cipher, KeyShare, SessionId, - Group, SelectedSignAlg, ClientPubKey), + State1 = update_start_state(State0, + #{cipher => Cipher, + key_share => KeyShare, + session_id => SessionId, + group => Group, + sign_alg => SelectedSignAlg, + peer_public_key => ClientPubKey, + alpn => ALPNProtocol}), %% 4.1.4. Hello Retry Request %% @@ -522,10 +550,7 @@ do_start(#client_hello{cipher_suites = ClientCiphers, %% the handshake. Maybe(send_hello_retry_request(State1, ClientPubKey, KeyShare, SessionId)) - %% TODO: - %% - session handling - %% - handle extensions: ALPN - %% (do not handle: NPN, srp, renegotiation_info, ec_point_formats) + %% TODO: session handling catch {Ref, {insufficient_security, no_suitable_groups}} -> @@ -537,7 +562,9 @@ do_start(#client_hello{cipher_suites = ClientCiphers, {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); {Ref, {insufficient_security, no_suitable_public_key}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key); + {Ref, no_application_protocol} -> + ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL) end; %% TLS Client do_start(#server_hello{cipher_suite = SelectedCipherSuite, @@ -588,8 +615,11 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite, HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions), %% Update state - State1 = update_start_state(State0, SelectedCipherSuite, ClientKeyShare, SessionId, - SelectedGroup, undefined, undefined), + State1 = update_start_state(State0, + #{cipher => SelectedCipherSuite, + key_share => ClientKeyShare, + session_id => SessionId, + group => SelectedGroup}), %% Replace ClientHello1 with a special synthetic handshake message State2 = replace_ch1_with_message_hash(State1), @@ -625,7 +655,8 @@ do_negotiated(start_handshake, dh_public_value = ClientPublicKey}, ssl_options = #ssl_options{} = SslOpts, key_share = KeyShare, - handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, + handshake_env = #handshake_env{tls_handshake_history = _HHistory0, + alpn = ALPN}, connection_env = #connection_env{private_key = CertPrivateKey}, static_env = #static_env{ cert_db = CertDbHandle, @@ -640,7 +671,7 @@ do_negotiated(start_handshake, try %% Create server_hello %% Extensions: supported_versions, key_share, (pre_shared_key) - ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0), + ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0, ALPN), {State1, _} = tls_connection:send_handshake(ServerHello, State0), @@ -702,6 +733,8 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) -> {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason), State}; {Ref, {{handshake_failure, Reason}, State}} -> {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason), State}; + {Ref, {#alert{} = Alert, State}} -> + {Alert, State}; {#alert{} = Alert, State} -> {Alert, State} end. @@ -801,8 +834,12 @@ do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite, {_, ClientPrivateKey} = get_client_private_key([SelectedGroup], ClientKeyShare), %% Update state - State1 = update_start_state(State0, SelectedCipherSuite, ClientKeyShare0, SessionId, - SelectedGroup, undefined, ServerPublicKey), + State1 = update_start_state(State0, + #{cipher => SelectedCipherSuite, + key_share => ClientKeyShare0, + session_id => SessionId, + group => SelectedGroup, + peer_public_key => ServerPublicKey}), State2 = calculate_handshake_secrets(ServerPublicKey, ClientPrivateKey, SelectedGroup, State1), @@ -858,7 +895,9 @@ do_wait_cert_cr(#certificate_1_3{} = Certificate, State0) -> {Ref, {{internal_error, Reason}, _State}} -> ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); {Ref, {{handshake_failure, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason) + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); + {Ref, {#alert{} = Alert, State}} -> + {Alert, State} end; do_wait_cert_cr(#certificate_request_1_3{} = CertificateRequest, State0) -> {Ref,Maybe} = maybe(), @@ -984,9 +1023,10 @@ compare_verify_data(_, _) -> {error, decrypt_error}. -send_hello_retry_request(#state{connection_states = ConnectionStates0} = State0, +send_hello_retry_request(#state{connection_states = ConnectionStates0, + handshake_env = #handshake_env{alpn = ALPN}} = State0, no_suitable_key, KeyShare, SessionId) -> - ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0), + ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0, ALPN), {State1, _} = tls_connection:send_handshake(ServerHello, State0), %% Update handshake history @@ -1076,13 +1116,11 @@ process_certificate(#certificate_1_3{certificate_list = Certs0}, State = store_peer_cert(State0, PeerCert, PublicKeyInfo), {ok, {State, wait_cv}}; {error, Reason} -> - State1 = calculate_traffic_secrets(State0), - State = ssl_record:step_encryption_state(State1), + State = update_encryption_state(Role, State0), {error, {Reason, State}}; - #alert{} = Alert -> - State1 = calculate_traffic_secrets(State0), - State = ssl_record:step_encryption_state(State1), - {Alert, State} + {ok, #alert{} = Alert} -> + State = update_encryption_state(Role, State0), + {error, {Alert, State}} end; false -> State1 = calculate_traffic_secrets(State0), @@ -1106,6 +1144,17 @@ is_supported_signature_algorithm([BinCert|_], SignAlgs0) -> lists:member(Scheme, SignAlgs). +%% Sets correct encryption state when sending Alerts in shared states that use different secrets. +%% - If client: use handshake secrets. +%% - If server: use traffic secrets as by this time the client's state machine +%% already stepped into the 'connection' state. +update_encryption_state(server, State0) -> + State1 = calculate_traffic_secrets(State0), + ssl_record:step_encryption_state(State1); +update_encryption_state(client, State) -> + State. + + validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host) -> ServerName = ssl_handshake:server_name(SslOptions#ssl_options.server_name_indication, Host, Role), [PeerCert | ChainCerts ] = Certs, @@ -1126,9 +1175,9 @@ validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHand {ok, {PublicKeyInfo,_}} -> {ok, {PeerCert, PublicKeyInfo}}; {error, Reason} -> - ssl_handshake:handle_path_validation_error(Reason, PeerCert, ChainCerts, - SslOptions, Options, - CertDbHandle, CertDbRef) + {ok, ssl_handshake:handle_path_validation_error(Reason, PeerCert, ChainCerts, + SslOptions, Options, + CertDbHandle, CertDbRef)} end catch error:{badmatch,{asn1, Asn1Reason}} -> @@ -1337,11 +1386,24 @@ update_connection_state(ConnectionState = #{security_parameters := SecurityParam cipher_state => cipher_init(Key, IV, FinishedKey)}. +update_start_state(State, Map) -> + Cipher = maps:get(cipher, Map, undefined), + KeyShare = maps:get(key_share, Map, undefined), + SessionId = maps:get(session_id, Map, undefined), + Group = maps:get(group, Map, undefined), + SelectedSignAlg = maps:get(sign_alg, Map, undefined), + PeerPublicKey = maps:get(peer_public_key, Map, undefined), + ALPNProtocol = maps:get(alpn, Map, undefined), + update_start_state(State, Cipher, KeyShare, SessionId, + Group, SelectedSignAlg, PeerPublicKey, + ALPNProtocol). +%% update_start_state(#state{connection_states = ConnectionStates0, + handshake_env = #handshake_env{} = HsEnv, connection_env = CEnv, session = Session} = State, Cipher, KeyShare, SessionId, - Group, SelectedSignAlg, ClientPubKey) -> + Group, SelectedSignAlg, PeerPublicKey, ALPNProtocol) -> #{security_parameters := SecParamsR0} = PendingRead = maps:get(pending_read, ConnectionStates0), #{security_parameters := SecParamsW0} = PendingWrite = @@ -1352,11 +1414,12 @@ update_start_state(#state{connection_states = ConnectionStates0, ConnectionStates0#{pending_read => PendingRead#{security_parameters => SecParamsR}, pending_write => PendingWrite#{security_parameters => SecParamsW}}, State#state{connection_states = ConnectionStates, + handshake_env = HsEnv#handshake_env{alpn = ALPNProtocol}, key_share = KeyShare, session = Session#session{session_id = SessionId, ecc = Group, sign_alg = SelectedSignAlg, - dh_public_value = ClientPubKey, + dh_public_value = PeerPublicKey, cipher_suite = Cipher}, connection_env = CEnv#connection_env{negotiated_version = {3,4}}}. @@ -1628,19 +1691,28 @@ get_server_public_key({key_share_entry, Group, PublicKey}) -> {Group, PublicKey}. -%% get_client_public_key(Group, ClientShares) -> -%% case lists:keysearch(Group, 2, ClientShares) of -%% {value, {_, _, ClientPublicKey}} -> -%% ClientPublicKey; -%% false -> -%% %% 4.1.4. Hello Retry Request -%% %% -%% %% The server will send this message in response to a ClientHello -%% %% message if it is able to find an acceptable set of parameters but the -%% %% ClientHello does not contain sufficient information to proceed with -%% %% the handshake. -%% no_suitable_key -%% end. +%% RFC 7301 - Application-Layer Protocol Negotiation Extension +%% It is expected that a server will have a list of protocols that it +%% supports, in preference order, and will only select a protocol if the +%% client supports it. In that case, the server SHOULD select the most +%% highly preferred protocol that it supports and that is also +%% advertised by the client. In the event that the server supports no +%% protocols that the client advertises, then the server SHALL respond +%% with a fatal "no_application_protocol" alert. +handle_alpn(undefined, _) -> + {ok, undefined}; +handle_alpn([], _) -> + {error, no_application_protocol}; +handle_alpn([_|_], undefined) -> + {ok, undefined}; +handle_alpn([ServerProtocol|T], ClientProtocols) -> + case lists:member(ServerProtocol, ClientProtocols) of + true -> + {ok, ServerProtocol}; + false -> + handle_alpn(T, ClientProtocols) + end. + select_cipher_suite([], _) -> {error, no_suitable_cipher}; diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 8cb98e7fa6..ce4479020e 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -164,6 +164,7 @@ api_tests() -> prf, socket_options, active_n, + internal_active_1, cipher_suites, handshake_continue, handshake_continue_timeout, @@ -271,7 +272,11 @@ tls13_test_group() -> tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client, tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client, tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client, - tls13_connection_information]. + tls13_connection_information, + tls13_ssl_server_with_alpn_ssl_client, + tls13_ssl_server_with_alpn_ssl_client_empty_alpn, + tls13_ssl_server_with_alpn_ssl_client_bad_alpn, + tls13_ssl_server_with_alpn_ssl_client_alpn]. %%-------------------------------------------------------------------- init_per_suite(Config0) -> @@ -500,6 +505,15 @@ init_per_testcase(accept_pool, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), Config end; + +init_per_testcase(internal_active_1, Config) -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, internal_active_n, 1), + ssl:start(), + ct:timetrap({seconds, 5}), + Config; + init_per_testcase(controller_dies, Config) -> ct:timetrap({seconds, 10}), Config; @@ -522,6 +536,10 @@ end_per_testcase(reuse_session_expired, Config) -> application:unset_env(ssl, session_delay_cleanup_time), end_per_testcase(default_action, Config); +end_per_testcase(internal_active_n, Config) -> + application:unset_env(ssl, internal_active_n), + end_per_testcase(default_action, Config); + end_per_testcase(Case, Config) when Case == protocol_versions; Case == empty_protocol_versions-> application:unset_env(ssl, protocol_versions), @@ -1987,6 +2005,10 @@ recv_active_once(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). + + + + %%-------------------------------------------------------------------- recv_active_n() -> [{doc,"Test recv on active (n) socket"}]. @@ -2013,6 +2035,7 @@ recv_active_n(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- %% Test case adapted from gen_tcp_misc_SUITE. active_n() -> @@ -2238,6 +2261,33 @@ upgrade_result(Socket) -> ok end. + +%%-------------------------------------------------------------------- +internal_active_1() -> + [{doc,"Test internal active 1 (behave as internal active once)"}]. + +internal_active_1(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{active, true} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{active, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- tls_upgrade_with_timeout() -> [{doc,"Test ssl_accept/3"}]. @@ -5551,11 +5601,7 @@ tls13_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) -> Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - ssl_test_lib:check_result(Server, - {error, - {tls_alert, - {certificate_required, - "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:check_server_alert(Server, certificate_required), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -5589,11 +5635,7 @@ tls13_client_auth_empty_cert_alert_ssl_server_ssl_client(Config) -> {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ClientOpts}]), - ssl_test_lib:check_result(Server, - {error, - {tls_alert, - {certificate_required, - "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:check_server_alert(Server, certificate_required), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -5745,11 +5787,7 @@ tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) -> Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - ssl_test_lib:check_result(Server, - {error, - {tls_alert, - {certificate_required, - "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:check_server_alert(Server, certificate_required), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -5785,11 +5823,7 @@ tls13_hrr_client_auth_empty_cert_alert_ssl_server_ssl_client(Config) -> {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ClientOpts}]), - ssl_test_lib:check_result(Server, - {error, - {tls_alert, - {certificate_required, - "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:check_server_alert(Server, certificate_required), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -5945,13 +5979,7 @@ tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client(Config) -> Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - ssl_test_lib:check_result( - Server, - {error, - {tls_alert, - {insufficient_security, - "received SERVER ALERT: Fatal - Insufficient Security - " - "\"No suitable signature algorithm\""}}}), + ssl_test_lib:check_server_alert(Server, insufficient_security), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -5984,13 +6012,7 @@ tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client(Config) -> {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ClientOpts}]), - ssl_test_lib:check_result( - Server, - {error, - {tls_alert, - {insufficient_security, - "received SERVER ALERT: Fatal - Insufficient Security - " - "\"No suitable signature algorithm\""}}}), + ssl_test_lib:check_server_alert(Server, insufficient_security), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -6024,12 +6046,7 @@ tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client(Config) - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - ssl_test_lib:check_result( - Server, - {error, - {tls_alert, - {certificate_required, - "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:check_server_alert(Server, certificate_required), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -6068,12 +6085,7 @@ tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client(Config) -> {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ClientOpts}]), - ssl_test_lib:check_result( - Server, - {error, - {tls_alert, - {certificate_required, - "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:check_server_alert(Server, certificate_required), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -6101,6 +6113,132 @@ tls13_connection_information(Config) -> ssl_test_lib:close_port(Client). +tls13_ssl_server_with_alpn_ssl_client() -> + [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client"}]. + +tls13_ssl_server_with_alpn_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_ssl_server_with_alpn_ssl_client_empty_alpn() -> + [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with empty ALPN"}]. + +tls13_ssl_server_with_alpn_ssl_client_empty_alpn(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_advertised_protocols, []}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_server_alert(Server, no_application_protocol), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_ssl_server_with_alpn_ssl_client_bad_alpn() -> + [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with bad ALPN"}]. + +tls13_ssl_server_with_alpn_ssl_client_bad_alpn(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_advertised_protocols, [<<1,2,3,4>>]}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_server_alert(Server, no_application_protocol), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + +tls13_ssl_server_with_alpn_ssl_client_alpn() -> + [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with correct ALPN"}]. + +tls13_ssl_server_with_alpn_ssl_client_alpn(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_advertised_protocols, [<<1,2,3,4>>, <<5,6>>]}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl index 55dee9a48f..4de4a35e59 100644 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -40,6 +40,7 @@ %%-------------------------------------------------------------------- all() -> [ + {group, 'tlsv1.3'}, {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, @@ -50,6 +51,7 @@ all() -> groups() -> [ + {'tlsv1.3', [], all_protocol_groups()}, {'tlsv1.2', [], all_protocol_groups()}, {'tlsv1.1', [], all_protocol_groups()}, {'tlsv1', [], all_protocol_groups()}, @@ -89,7 +91,8 @@ tests() -> critical_extension_verify_server, critical_extension_verify_none, customize_hostname_check, - incomplete_chain + incomplete_chain, + long_chain ]. error_handling_tests()-> @@ -300,7 +303,13 @@ server_require_peer_cert_fail(Config) when is_list(Config) -> {from, self()}, {options, [{active, Active} | BadClientOpts]}]), - ssl_test_lib:check_server_alert(Server, Client, handshake_failure). + Version = proplists:get_value(version,Config), + case Version of + 'tlsv1.3' -> + ssl_test_lib:check_server_alert(Server, Client, certificate_required); + _ -> + ssl_test_lib:check_server_alert(Server, Client, handshake_failure) + end. %%-------------------------------------------------------------------- server_require_peer_cert_empty_ok() -> @@ -853,6 +862,7 @@ invalid_signature_server(Config) when is_list(Config) -> {from, self()}, {options, [{verify, verify_peer} | ClientOpts]}]), ssl_test_lib:check_server_alert(Server, Client, unknown_ca). + %%-------------------------------------------------------------------- invalid_signature_client() -> @@ -1157,6 +1167,44 @@ incomplete_chain(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +long_chain() -> + [{doc,"Test option verify_peer"}]. +long_chain(Config) when is_list(Config) -> + #{server_config := ServerConf, + client_config := ClientConf} = public_key:pkix_test_data(#{server_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}], + intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}], + [{key, ssl_test_lib:hardcode_rsa_key(3)}], + [{key, ssl_test_lib:hardcode_rsa_key(4)}]], + peer => [{key, ssl_test_lib:hardcode_rsa_key(5)}]}, + client_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(3)}], + intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]], + peer => [{key, ssl_test_lib:hardcode_rsa_key(1)}]}}), + [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerConf), + ClientCas = proplists:get_value(cacerts, ClientConf), + + Active = proplists:get_value(active, Config), + ReceiveFunction = proplists:get_value(receive_function, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, {verify, verify_peer}, + {cacerts, [ServerRoot]} | + proplists:delete(cacerts, ServerConf)]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, + {verify, verify_peer}, + {depth, 5}, + {cacerts, ServerCas ++ ClientCas} | + proplists:delete(cacerts, ClientConf)]}]), + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 3b161a0c8a..32fd917937 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -159,6 +159,7 @@ connect(ListenSocket, Node, N, _, Timeout, SslOpts, [_|_] =ContOpts) -> case ssl:handshake(AcceptSocket, SslOpts, Timeout) of {ok, Socket0, Ext} -> + [_|_] = maps:get(sni, Ext), ct:log("Ext ~p:~n", [Ext]), ct:log("~p:~p~nssl:handshake_continue(~p,~p,~p)~n", [?MODULE,?LINE, Socket0, ContOpts,Timeout]), case ssl:handshake_continue(Socket0, ContOpts, Timeout) of @@ -427,37 +428,56 @@ check_result(Pid, Msg) -> {got, Unexpected}}, ct:fail(Reason) end. + check_server_alert(Pid, Alert) -> receive - {Pid, {error, {tls_alert, {Alert, _}}}} -> + {Pid, {error, {tls_alert, {Alert, STxt}}}} -> + check_server_txt(STxt), + ok; + {Pid, {error, closed}} -> ok end. check_server_alert(Server, Client, Alert) -> receive - {Server, {error, {tls_alert, {Alert, _}}}} -> - receive - {Client, {error, {tls_alert, {Alert, _}}}} -> - ok; - {Client, {error, closed}} -> - ok - end + {Server, {error, {tls_alert, {Alert, STxt}}}} -> + check_server_txt(STxt), + check_client_alert(Client, Alert) end. check_client_alert(Pid, Alert) -> receive - {Pid, {error, {tls_alert, {Alert, _}}}} -> + {Pid, {error, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + ok; + {Pid, {ssl_error, _, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + ok; + {Pid, {error, closed}} -> ok end. check_client_alert(Server, Client, Alert) -> receive - {Client, {error, {tls_alert, {Alert, _}}}} -> - receive - {Server, {error, {tls_alert, {Alert, _}}}} -> - ok; - {Server, {error, closed}} -> - ok - end + {Client, {error, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + check_server_alert(Server, Alert); + {Client, {ssl_error, _, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + ok; + {Client, {error, closed}} -> + ok end. +check_server_txt("TLS server" ++ _) -> + ok; +check_server_txt("DTLS server" ++ _) -> + ok; +check_server_txt(Txt) -> + ct:fail({expected_server, {got, Txt}}). +check_client_txt("TLS client" ++ _) -> + ok; +check_client_txt("DTLS client" ++ _) -> + ok; +check_client_txt(Txt) -> + ct:fail({expected_server, {got, Txt}}). wait_for_result(Server, ServerMsg, Client, ClientMsg) -> receive @@ -1084,7 +1104,15 @@ run_client_error(Opts) -> Options = proplists:get_value(options, Opts), ct:log("~p:~p~nssl:connect(~p, ~p, ~p)~n", [?MODULE,?LINE, Host, Port, Options]), Error = Transport:connect(Host, Port, Options), - Pid ! {self(), Error}. + case Error of + {error, _} -> + Pid ! {self(), Error}; + {ok, _Socket} -> + receive + {ssl_error, _, {tls_alert, _}} = SslError -> + Pid ! {self(), SslError} + end + end. accepters(N) -> accepters([], N). @@ -1623,6 +1651,8 @@ is_tls_version('dtlsv1.2') -> true; is_tls_version('dtlsv1') -> true; +is_tls_version('tlsv1.3') -> + true; is_tls_version('tlsv1.2') -> true; is_tls_version('tlsv1.1') -> @@ -2149,7 +2179,8 @@ clean_env() -> application:unset_env(ssl, session_cache_server_max), application:unset_env(ssl, ssl_pem_cache_clean), application:unset_env(ssl, bypass_pem_cache), - application:unset_env(ssl, alert_timeout). + application:unset_env(ssl, alert_timeout), + application:unset_env(ssl, internal_active_n). clean_start() -> ssl:stop(), diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index cbc32cd5a8..4e387fa403 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 9.3.1 +SSL_VSN = 9.3.2 diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 605a9f224d..5c07dd2ee6 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -31,6 +31,40 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 3.9.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that could cause a loop when formatting + terms using the control sequences <c>p</c> or <c>P</c> + and limiting the output with the option + <c>chars_limit</c>. </p> + <p> + Own Id: OTP-15875 Aux Id: ERL-967 </p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 3.9.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that could cause a failure when formatting + binaries using the control sequences <c>p</c> or <c>P</c> + and limiting the output with the option + <c>chars_limit</c>. </p> + <p> + Own Id: OTP-15847 Aux Id: ERL-957 </p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 3.9</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -272,6 +306,23 @@ </section> +<section><title>STDLIB 3.8.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that could cause a failure when formatting + binaries using the control sequences <c>p</c> or <c>P</c> + and limiting the output with the option + <c>chars_limit</c>. </p> + <p> + Own Id: OTP-15847 Aux Id: ERL-957 </p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 3.8.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl index b1a5991bf0..77f02eafe0 100644 --- a/lib/stdlib/src/io_lib_pretty.erl +++ b/lib/stdlib/src/io_lib_pretty.erl @@ -462,7 +462,9 @@ find_upper(Lower, Term, T, Dl, Dd, D, RF, Enc, Str) -> case If of {_, _, _Dots=0, _} -> % even if Len > T If; - {_, Len, _, _} when Len =< T, D1 < D orelse D < 0 -> + {_, _Len=T, _, _} -> % increasing the depth is meaningless + If; + {_, Len, _, _} when Len < T, D1 < D orelse D < 0 -> find_upper(If, Term, T, D1, Dd2, D, RF, Enc, Str); _ -> search_depth(Lower, If, Term, T, Dl, D1, RF, Enc, Str) @@ -780,6 +782,8 @@ printable_bin0(Bin, D, T, Enc) -> end, printable_bin(Bin, Len, D, Enc). +printable_bin(_Bin, 0, _D, _Enc) -> + false; printable_bin(Bin, Len, D, latin1) -> N = erlang:min(20, Len), L = binary_to_list(Bin, 1, N), diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 7038cc159c..0c270e9dd5 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -38,7 +38,10 @@ {<<"^3\\.8$">>,[restart_new_emulator]}, {<<"^3\\.8\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.8\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], + {<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^3\\.9$">>,[restart_new_emulator]}, + {<<"^3\\.9\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], [{<<"^3\\.5$">>,[restart_new_emulator]}, {<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, @@ -50,4 +53,7 @@ {<<"^3\\.8$">>,[restart_new_emulator]}, {<<"^3\\.8\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.8\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}. + {<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^3\\.9$">>,[restart_new_emulator]}, + {<<"^3\\.9\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}. diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index 9b6d8d7401..4eb5b1772c 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -32,7 +32,7 @@ io_with_huge_message_queue/1, format_string/1, maps/1, coverage/1, otp_14178_unicode_atoms/1, otp_14175/1, otp_14285/1, limit_term/1, otp_14983/1, otp_15103/1, otp_15076/1, - otp_15159/1, otp_15639/1, otp_15705/1]). + otp_15159/1, otp_15639/1, otp_15705/1, otp_15847/1, otp_15875/1]). -export([pretty/2, trf/3]). @@ -65,7 +65,7 @@ all() -> io_lib_width_too_small, io_with_huge_message_queue, format_string, maps, coverage, otp_14178_unicode_atoms, otp_14175, otp_14285, limit_term, otp_14983, otp_15103, otp_15076, otp_15159, - otp_15639, otp_15705]. + otp_15639, otp_15705, otp_15847, otp_15875]. %% Error cases for output. error_1(Config) when is_list(Config) -> @@ -2708,3 +2708,13 @@ otp_15705(_Config) -> "|кирилли́чес|" = trf("|~10ts|", [U], -1), ok. + +otp_15847(_Config) -> + T = {someRecord,<<"1234567890">>,some,more}, + "{someRecord,<<...>>,...}" = + pretty(T, [{chars_limit,20}, {encoding,latin1}]), + ok. + +otp_15875(_Config) -> + S = io_lib:format("~tp", [[{0, [<<"00">>]}]], [{chars_limit, 18}]), + "[{0,[<<48,...>>]}]" = lists:flatten(S). diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 07224afdc9..c2f586fef5 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 3.9 +STDLIB_VSN = 3.9.2 diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge index 3728402492..e071f1a430 100644 --- a/make/otp_version_tickets_in_merge +++ b/make/otp_version_tickets_in_merge @@ -1,2 +1,9 @@ -OTP-15823 -OTP-15825 +OTP-15844 +OTP-15861 +OTP-15862 +OTP-15864 +OTP-15865 +OTP-15871 +OTP-15872 +OTP-15873 +OTP-15875 diff --git a/otp_versions.table b/otp_versions.table index 5fe1c3fd9c..3eed42140a 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,5 +1,8 @@ +OTP-22.0.3 : compiler-7.4.2 dialyzer-4.0.1 erts-10.4.2 ssl-9.3.2 stdlib-3.9.2 # asn1-5.0.9 common_test-1.17.3 crypto-4.5.1 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : +OTP-22.0.2 : compiler-7.4.1 crypto-4.5.1 erts-10.4.1 stdlib-3.9.1 # asn1-5.0.9 common_test-1.17.3 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3.1 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : OTP-22.0.1 : ssl-9.3.1 # asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : OTP-22.0 : asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3 stdlib-3.9 syntax_tools-2.2 tools-3.2 wx-1.8.8 xmerl-1.3.21 # diameter-2.2.1 et-1.6.4 eunit-2.3.7 ftp-1.0.2 parsetools-2.1.8 tftp-1.0.1 : +OTP-21.3.8.3 : erts-10.3.5.2 kernel-6.3.1.1 ssl-9.2.3.2 stdlib-3.8.2.1 # asn1-5.0.8 common_test-1.17.2 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : OTP-21.3.8.2 : xmerl-1.3.20.1 # asn1-5.0.8 common_test-1.17.2 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 erts-10.3.5.1 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 ssl-9.2.3.1 stdlib-3.8.2 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 : OTP-21.3.8.1 : erts-10.3.5.1 ssl-9.2.3.1 tools-3.1.0.1 # asn1-5.0.8 common_test-1.17.2 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 stdlib-3.8.2 syntax_tools-2.1.7 tftp-1.0.1 wx-1.8.7 xmerl-1.3.20 : OTP-21.3.8 : common_test-1.17.2 eldap-1.2.7 erl_interface-3.11.3 erts-10.3.5 public_key-1.6.6 ssl-9.2.3 stdlib-3.8.2 # asn1-5.0.8 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 erl_docgen-0.9 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1 wx-1.8.7 xmerl-1.3.20 : diff --git a/system/doc/efficiency_guide/commoncaveats.xml b/system/doc/efficiency_guide/commoncaveats.xml index 7b2128d888..7b00fa5d63 100644 --- a/system/doc/efficiency_guide/commoncaveats.xml +++ b/system/doc/efficiency_guide/commoncaveats.xml @@ -62,7 +62,7 @@ dangerous in a system that runs continuously. If only certain well-defined atoms are allowed as input, <seealso marker="erts:erlang#list_to_existing_atom/1">list_to_existing_atom/1</seealso> - can be used to + can be used to guard against a denial-of-service attack. (All atoms that are allowed must have been created earlier, for example, by simply using all of them in a module and loading that module.)</p> diff --git a/system/doc/system_principles/system_principles.xml b/system/doc/system_principles/system_principles.xml index 500522d778..6986d93ed9 100644 --- a/system/doc/system_principles/system_principles.xml +++ b/system/doc/system_principles/system_principles.xml @@ -156,7 +156,8 @@ init:stop()</pre> command-line flag <c>-mode</c>.</p> <pre> % <input>erl -mode embedded</input></pre> - <p>Default mode is <c>interactive</c>.</p> + <p>Default mode is <c>interactive</c> and extra <c>-mode</c> flags are + ignored.</p> <p>The mode properties are as follows:</p> <list type="bulleted"> <item>In embedded mode, all code is loaded during system startup |