aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/aclocal.m46
-rw-r--r--erts/doc/src/erl.xml11
-rw-r--r--erts/doc/src/erl_driver.xml4
-rw-r--r--erts/doc/src/erl_nif.xml26
-rw-r--r--erts/doc/src/erlang.xml242
-rw-r--r--erts/emulator/beam/erl_trace.c36
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl20
-rw-r--r--erts/emulator/test/distribution_SUITE.erl20
-rw-r--r--erts/emulator/test/process_SUITE.erl5
-rw-r--r--erts/emulator/test/trace_call_time_SUITE.erl4
-rw-r--r--erts/etc/common/heart.c4
-rw-r--r--erts/etc/unix/etp-commands.in131
-rw-r--r--lib/common_test/src/cth_log_redirect.erl9
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl10
-rw-r--r--lib/erl_interface/configure.in4
-rw-r--r--lib/erl_interface/src/misc/show_msg.c2
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c10
-rw-r--r--lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c4
-rw-r--r--lib/erl_interface/test/runner.erl6
-rw-r--r--lib/inets/doc/src/notes.xml17
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl7
-rw-r--r--lib/inets/test/httpd_SUITE.erl112
-rw-r--r--lib/inets/test/httpd_SUITE_data/mime_types.txt100
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml8
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl4
-rw-r--r--lib/kernel/test/heart_SUITE.erl7
-rw-r--r--lib/os_mon/test/cpu_sup_SUITE.erl2
-rw-r--r--lib/os_mon/test/os_mon_SUITE.erl9
-rw-r--r--lib/public_key/doc/src/public_key.xml38
-rw-r--r--lib/public_key/src/public_key.erl118
-rw-r--r--lib/public_key/test/public_key_SUITE.erl39
-rw-r--r--lib/ssh/src/ssh.erl3
-rw-r--r--lib/ssh/src/ssh_dbg.erl44
-rw-r--r--lib/ssl/doc/src/notes.xml45
-rw-r--r--lib/ssl/doc/src/ssl.xml81
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache_api.xml15
-rw-r--r--lib/ssl/src/Makefile1
-rw-r--r--lib/ssl/src/dtls_connection.erl408
-rw-r--r--lib/ssl/src/dtls_connection.hrl1
-rw-r--r--lib/ssl/src/dtls_handshake.erl208
-rw-r--r--lib/ssl/src/dtls_record.erl115
-rw-r--r--lib/ssl/src/ssl.app.src1
-rw-r--r--lib/ssl/src/ssl_connection.erl44
-rw-r--r--lib/ssl/src/ssl_connection.hrl7
-rw-r--r--lib/ssl/src/ssl_crl_cache.erl5
-rw-r--r--lib/ssl/src/ssl_crl_cache_api.erl7
-rw-r--r--lib/ssl/src/ssl_crl_hash_dir.erl106
-rw-r--r--lib/ssl/src/ssl_handshake.erl44
-rw-r--r--lib/ssl/src/ssl_record.erl18
-rw-r--r--lib/ssl/src/ssl_record.hrl2
-rw-r--r--lib/ssl/src/tls_connection.erl50
-rw-r--r--lib/ssl/src/tls_handshake.erl14
-rw-r--r--lib/ssl/test/make_certs.erl15
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl5
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl203
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl10
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl32
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl2
-rw-r--r--otp_versions.table1
-rw-r--r--system/doc/top/templates/index.html.src2
60 files changed, 1877 insertions, 627 deletions
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index 86799186fd..013bfe5652 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -128,13 +128,13 @@ MIXED_MSYS=no
AC_MSG_CHECKING(for mixed cygwin or msys and native VC++ environment)
if test "X$host" = "Xwin32" -a "x$GCC" != "xyes"; then
if test -x /usr/bin/msys-?.0.dll; then
- CFLAGS="-O2"
+ CFLAGS="$CFLAGS -O2"
MIXED_MSYS=yes
AC_MSG_RESULT([MSYS and VC])
MIXED_MSYS_VC=yes
CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MSYS_VC"
elif test -x /usr/bin/cygpath; then
- CFLAGS="-O2"
+ CFLAGS="$CFLAGS -O2"
MIXED_CYGWIN=yes
AC_MSG_RESULT([Cygwin and VC])
MIXED_CYGWIN_VC=yes
@@ -162,7 +162,7 @@ if test "x$MIXED_MSYS" != "xyes"; then
AC_MSG_CHECKING(for mixed cygwin and native MinGW environment)
if test "X$host" = "Xwin32" -a "x$GCC" = x"yes"; then
if test -x /usr/bin/cygpath; then
- CFLAGS="-O2"
+ CFLAGS="$CFLAGS -O2"
MIXED_CYGWIN=yes
AC_MSG_RESULT([yes])
MIXED_CYGWIN_MINGW=yes
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 5d5bfb141f..7b90a1ccca 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -643,8 +643,7 @@
<p>Sets the default binary virtual heap size of processes to the size
<c><![CDATA[Size]]></c>.</p>
</item>
- <marker id="+hmax"/>
- <tag><c><![CDATA[+hmax Size]]></c></tag>
+ <tag><marker id="+hmax"/><c><![CDATA[+hmax Size]]></c></tag>
<item>
<p>Sets the default maximum heap size of processes to the size
<c><![CDATA[Size]]></c>. If <c>+hmax</c> is not given, the default is <c>0</c>
@@ -653,8 +652,7 @@
<seealso marker="erlang#process_flag_max_heap_size">
<c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
</item>
- <marker id="+hmaxel"/>
- <tag><c><![CDATA[+hmaxel true|false]]></c></tag>
+ <tag><marker id="+hmaxel"/><c><![CDATA[+hmaxel true|false]]></c></tag>
<item>
<p>Sets whether to send an error logger message for processes that reach
the maximum heap size or not. If <c>+hmaxel</c> is not given, the default is <c>true</c>.
@@ -662,8 +660,7 @@
<seealso marker="erlang#process_flag_max_heap_size">
<c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
</item>
- <marker id="+hmaxk"/>
- <tag><c><![CDATA[+hmaxk true|false]]></c></tag>
+ <tag><marker id="+hmaxk"/><c><![CDATA[+hmaxk true|false]]></c></tag>
<item>
<p>Sets whether to kill processes that reach the maximum heap size or not. If
<c>+hmaxk</c> is not given, the default is <c>true</c>. For more information,
@@ -676,7 +673,7 @@
<p>Sets the initial process dictionary size of processes to the size
<c><![CDATA[Size]]></c>.</p>
</item>
- <tag><marker id="+hmqd"><c>+hmqd off_heap|on_heap</c></marker></tag>
+ <tag><marker id="+hmqd"/><c>+hmqd off_heap|on_heap</c></tag>
<item><p>
Sets the default value for the process flag
<c>message_queue_data</c>. If <c>+hmqd</c> is not
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 175b7f6bfb..82215ead46 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -348,14 +348,14 @@
all will work as before.</p>
</item>
<tag><marker id="time_measurement"/>Time Measurement</tag>
- <item><p>Support for time measurement in drivers:
+ <item><p>Support for time measurement in drivers:</p>
<list>
<item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item>
<item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item>
<item><seealso marker="#erl_drv_monotonic_time"><c>erl_drv_monotonic_time()</c></seealso></item>
<item><seealso marker="#erl_drv_time_offset"><c>erl_drv_time_offset()</c></seealso></item>
<item><seealso marker="#erl_drv_convert_time_unit"><c>erl_drv_convert_time_unit()</c></seealso></item>
- </list></p>
+ </list>
</item>
</taglist>
</section>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 123d353432..8b02b3bae1 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -518,13 +518,15 @@ ok
</item>
<tag><marker id="reload"/>int (*reload)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)</tag>
- <note><p>The reload mechanism is <em>deprecated</em>. It was only intended
- as a development feature. Do not use it as an upgrade method for
- live production systems. It might be removed in future releases. Be sure
- to pass <c>reload</c> as <c>NULL</c> to <seealso marker="#ERL_NIF_INIT">ERL_NIF_INIT</seealso>
- to disable it when not used.</p>
- </note>
- <item><p><c>reload</c> is called when the NIF library is loaded
+
+ <item>
+ <note><p>The reload mechanism is <em>deprecated</em>. It was only intended
+ as a development feature. Do not use it as an upgrade method for
+ live production systems. It might be removed in future releases. Be sure
+ to pass <c>reload</c> as <c>NULL</c> to <seealso marker="#ERL_NIF_INIT">ERL_NIF_INIT</seealso>
+ to disable it when not used.</p>
+ </note>
+ <p><c>reload</c> is called when the NIF library is loaded
and there is already a previously loaded library for this
module code.</p>
<p>Works the same as <c>load</c>. The only difference is that
@@ -583,9 +585,9 @@ ok
<p/>
<code type="none">
typedef struct {
- const char* <em>name</em>;
- unsigned <em>arity</em>;
- ERL_NIF_TERM (*<em>fptr</em>)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ const char* name;
+ unsigned arity;
+ ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
unsigned flags;
} ErlNifFunc;
</code>
@@ -618,8 +620,8 @@ typedef struct {
<p/>
<code type="none">
typedef struct {
- unsigned <em>size</em>;
- unsigned char* <em>data</em>;
+ unsigned size;
+ unsigned char* data;
} ErlNifBinary;
</code>
<p><c>ErlNifBinary</c> contains transient information about an
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index fa13e4c142..6289f033b2 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -4361,86 +4361,86 @@ os_prompt% </pre>
<marker id="process_flag_max_heap_size"/>
<func>
<name name="process_flag" arity="2" clause_i="5"/>
- <type name="max_heap_size"/>
<fsummary>Sets process flag <c>max_heap_size</c> for the calling process.</fsummary>
+ <type name="max_heap_size"/>
<desc>
<p>
This flag sets the maximum heap size for the calling process.
If <c><anno>MaxHeapSize</anno></c> is an integer, the system default
values for <c>kill</c> and <c>error_logger</c> are used.
- <taglist>
- <tag><c>size</c></tag>
- <item>
- <p>
- The maximum size in words of the process. If set to zero, the
- heap size limit is disabled. Badarg will be thrown if the value is
- smaller than
- <seealso marker="#process_flag_min_heap_size"><c>min_heap_size</c></seealso>.
- The size check is only done when a garbage collection is triggered.
- </p>
- <p>
- <c>size</c> is the entire heap of the process when garbage collection
- is triggered, this includes all generational heaps, the process stack,
- any <seealso marker="#process_flag_message_queue_data">
- messages that are considered to be part of the heap</seealso> and any
- extra memory that the garbage collector needs during collection.
- </p>
- <p>
- <c>size</c> is the same as can be retrieved using
- <seealso marker="#process_info_total_heap_size">
- <c>erlang:process_info(Pid, total_heap_size)</c></seealso>,
- or by adding <c>heap_block_size</c>, <c>old_heap_block_size</c>
- and <c>mbuf_size</c> from <seealso marker="#process_info_garbage_collection_info">
- <c>erlang:process_info(Pid, garbage_collection_info)</c></seealso>.
- </p>
- </item>
- <tag><c>kill</c></tag>
- <item>
- <p>
- When set to <c>true</c> the runtime system will send an
- untrappable exit signal with reason <c>kill</c> to the process
- if the maximum heap size is reached. The garbage collection
- that triggered the <c>kill</c> will not be completed, instead the
- process will exit as soon as is possible. When set to <c>false</c>
- no exit signal will be sent to the process, instead it will
- continue executing.
- </p>
- <p>
- If <c>kill</c> is not defined in the map
- the system default will be used. The default system default
- is <c>true</c>. It can be changed by either the erl
- <seealso marker="erl#+hmaxk">+hmaxk</seealso> option,
- or <seealso marker="#system_flag_max_heap_size"><c>
- erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.
- </p>
- </item>
- <tag><c>error_logger</c></tag>
- <item>
- <p>
- When set to <c>true</c> the runtime system will send a
- message to the current <seealso marker="kernel:error_logger"><c>error_logger</c></seealso>
- containing details about the process when the maximum
- heap size is reached. One <c>error_logger</c> report will
- be sent each time the limit is reached.
- </p>
- <p>
- If <c>error_logger</c> is not defined in the map the system
- default will be used. The default system default is <c>true</c>.
- It can be changed by either the erl <seealso marker="erl#+hmaxel">+hmaxel</seealso>
- option, or <seealso marker="#system_flag_max_heap_size"><c>
- erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.
- </p>
- </item>
+ </p>
+ <taglist>
+ <tag><c>size</c></tag>
+ <item>
+ <p>
+ The maximum size in words of the process. If set to zero, the
+ heap size limit is disabled. Badarg will be thrown if the value is
+ smaller than
+ <seealso marker="#process_flag_min_heap_size"><c>min_heap_size</c></seealso>.
+ The size check is only done when a garbage collection is triggered.
+ </p>
+ <p>
+ <c>size</c> is the entire heap of the process when garbage collection
+ is triggered, this includes all generational heaps, the process stack,
+ any <seealso marker="#process_flag_message_queue_data">
+ messages that are considered to be part of the heap</seealso> and any
+ extra memory that the garbage collector needs during collection.
+ </p>
+ <p>
+ <c>size</c> is the same as can be retrieved using
+ <seealso marker="#process_info_total_heap_size">
+ <c>erlang:process_info(Pid, total_heap_size)</c></seealso>,
+ or by adding <c>heap_block_size</c>, <c>old_heap_block_size</c>
+ and <c>mbuf_size</c> from <seealso marker="#process_info_garbage_collection_info">
+ <c>erlang:process_info(Pid, garbage_collection_info)</c></seealso>.
+ </p>
+ </item>
+ <tag><c>kill</c></tag>
+ <item>
+ <p>
+ When set to <c>true</c> the runtime system will send an
+ untrappable exit signal with reason <c>kill</c> to the process
+ if the maximum heap size is reached. The garbage collection
+ that triggered the <c>kill</c> will not be completed, instead the
+ process will exit as soon as is possible. When set to <c>false</c>
+ no exit signal will be sent to the process, instead it will
+ continue executing.
+ </p>
<p>
- The heap size of a process is quite hard to predict, especially the
- amount of memory that is used during the garbage collection. When
- contemplating using this option, it is recommended to first run
- it in production with <c>kill</c> set to <c>false</c> and inspect
- the <c>error_logger</c> reports to see what the normal peak sizes
- of the processes in the system is and then tune the value
- accordingly.
+ If <c>kill</c> is not defined in the map
+ the system default will be used. The default system default
+ is <c>true</c>. It can be changed by either the erl
+ <seealso marker="erl#+hmaxk">+hmaxk</seealso> option,
+ or <seealso marker="#system_flag_max_heap_size"><c>
+ erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.
</p>
- </taglist>
+ </item>
+ <tag><c>error_logger</c></tag>
+ <item>
+ <p>
+ When set to <c>true</c> the runtime system will send a
+ message to the current <seealso marker="kernel:error_logger"><c>error_logger</c></seealso>
+ containing details about the process when the maximum
+ heap size is reached. One <c>error_logger</c> report will
+ be sent each time the limit is reached.
+ </p>
+ <p>
+ If <c>error_logger</c> is not defined in the map the system
+ default will be used. The default system default is <c>true</c>.
+ It can be changed by either the erl <seealso marker="erl#+hmaxel">+hmaxel</seealso>
+ option, or <seealso marker="#system_flag_max_heap_size"><c>
+ erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.
+ </p>
+ </item>
+ </taglist>
+ <p>
+ The heap size of a process is quite hard to predict, especially the
+ amount of memory that is used during the garbage collection. When
+ contemplating using this option, it is recommended to first run
+ it in production with <c>kill</c> set to <c>false</c> and inspect
+ the <c>error_logger</c> reports to see what the normal peak sizes
+ of the processes in the system is and then tune the value
+ accordingly.
</p>
</desc>
</func>
@@ -4797,8 +4797,10 @@ os_prompt% </pre>
The content of <c><anno>GCInfo</anno></c> can be changed without
prior notice.</p>
</item>
- <marker id="process_info_garbage_collection_info"/>
- <tag><c>{garbage_collection_info, <anno>GCInfo</anno>}</c></tag>
+ <tag>
+ <marker id="process_info_garbage_collection_info"/>
+ <c>{garbage_collection_info, <anno>GCInfo</anno>}</c>
+ </tag>
<item>
<p><c><anno>GCInfo</anno></c> is a list containing miscellaneous
detailed information about garbage collection for this process.
@@ -4986,8 +4988,10 @@ os_prompt% </pre>
total suspend count on <c><anno>Suspendee</anno></c>,
only the parts contributed by <c><anno>Pid</anno></c>.</p>
</item>
- <marker id="process_info_total_heap_size"/>
- <tag><c>{total_heap_size, <anno>Size</anno>}</c></tag>
+ <tag>
+ <marker id="process_info_total_heap_size"/>
+ <c>{total_heap_size, <anno>Size</anno>}</c>
+ </tag>
<item>
<p><c><anno>Size</anno></c> is the total size, in words, of all heap
fragments of the process. This includes the process stack and
@@ -6631,8 +6635,8 @@ ok
<marker id="system_flag_max_heap_size"></marker>
<func>
<name name="system_flag" arity="2" clause_i="8"/>
- <type name="max_heap_size"/>
<fsummary>Sets system flag <c>max_heap_size</c></fsummary>
+ <type name="max_heap_size"/>
<desc>
<p>
Sets the default maximum heap size settings for processes.
@@ -7136,9 +7140,9 @@ ok
<name name="system_info" arity="1" clause_i="37"/>
<name name="system_info" arity="1" clause_i="38"/>
<name name="system_info" arity="1" clause_i="39"/>
+ <fsummary>Information about the default process heap settings.</fsummary>
<type name="message_queue_data"/>
<type name="max_heap_size"/>
- <fsummary>Information about the default process heap settings.</fsummary>
<desc>
<taglist>
<tag><c>fullsweep_after</c></tag>
@@ -7183,7 +7187,7 @@ ok
where <c><anno>MinHeapSize</anno></c> is the current
system-wide minimum heap size for spawned processes.</p>
</item>
- <tag><marker id="system_info_message_queue_data"><c>message_queue_data</c></marker></tag>
+ <tag><marker id="system_info_message_queue_data"/><c>message_queue_data</c></tag>
<item>
<p>Returns the default value of the <c>message_queue_data</c>
process flag which is either <c>off_heap</c>, or <c>on_heap</c>.
@@ -7664,7 +7668,7 @@ ok
and
<seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
</item>
- <tag><marker id="system_info_otp_release"><c>otp_release</c></marker></tag>
+ <tag><marker id="system_info_otp_release"/><c>otp_release</c></tag>
<item>
<marker id="system_info_otp_release"></marker>
<p>Returns a string containing the OTP release number of the
@@ -8617,21 +8621,21 @@ timestamp() ->
<tag><c>send</c></tag>
<item>
<p>Traces sending of messages.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_send">send</seealso></c> and
- <c><seealso marker="#trace_3_trace_messages_send_to_non_existing_process">send_to_non_existing_process</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_send"><c>send</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_send_to_non_existing_process"><c>send_to_non_existing_process</c></seealso>.</p>
</item>
<tag><c>'receive'</c></tag>
<item>
<p>Traces receiving of messages.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_receive">'receive'</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_receive"><c>'receive'</c></seealso>.</p>
</item>
<tag><c>call</c></tag>
<item>
<p>Traces certain function calls. Specify which function
calls to trace by calling
<seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_call">call</seealso></c> and
- <c><seealso marker="#trace_3_trace_messages_return_from">return_from</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_call"><c>call</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_return_from"><c>return_from</c></seealso>.</p>
</item>
<tag><c>silent</c></tag>
<item>
@@ -8649,9 +8653,9 @@ timestamp() ->
specification function <c>{silent,Bool}</c>, giving
a high degree of control of which functions with which
arguments that trigger the trace.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_call">call</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_return_from">return_from</seealso></c>, and
- <c><seealso marker="#trace_3_trace_messages_return_to">return_to</seealso></c>. Or rather, the absence of.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_call"><c>call</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_return_from"><c>return_from</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_return_to"><c>return_to</c></seealso>. Or rather, the absence of.</p>
</item>
<tag><c>return_to</c></tag>
<item>
@@ -8672,43 +8676,43 @@ timestamp() ->
<p>To get trace messages containing return values from
functions, use the <c>{return_trace}</c> match
specification action instead.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_return_to">return_to</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_return_to"><c>return_to</c></seealso>.</p>
</item>
<tag><c>procs</c></tag>
<item>
<p>Traces process-related events.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_spawn">spawn</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_spawned">spawned</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_exit">exit</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_register">register</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_unregister">unregister</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_link">link</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_unlink">unlink</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_getting_linked">getting_linked</seealso></c>, and
- <c><seealso marker="#trace_3_trace_messages_getting_unlinked">getting_unlinked</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_spawn"><c>spawn</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_spawned"><c>spawned</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_exit"><c>exit</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_register"><c>register</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_unregister"><c>unregister</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_link"><c>link</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_unlink"><c>unlink</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_getting_linked"><c>getting_linked</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_getting_unlinked"><c>getting_unlinked</c></seealso>.</p>
</item>
<tag><c>ports</c></tag>
<item>
<p>Traces port-related events.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_open">open</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_closed">closed</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_register">register</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_unregister">unregister</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_getting_linked">getting_linked</seealso></c>, and
- <c><seealso marker="#trace_3_trace_messages_getting_unlinked">getting_unlinked</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_open"><c>open</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_closed"><c>closed</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_register"><c>register</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_unregister"><c>unregister</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_getting_linked"><c>getting_linked</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_getting_unlinked"><c>getting_unlinked</c></seealso>.</p>
</item>
<tag><c>running</c></tag>
<item>
<p>Traces scheduling of processes.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_in_proc">in</seealso></c> and
- <c><seealso marker="#trace_3_trace_messages_out_proc">out</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_in_proc"><c>in</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_out_proc"><c>out</c></seealso>.</p>
</item>
<tag><c>exiting</c></tag>
<item>
<p>Traces scheduling of exiting processes.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_in_exiting_proc">in_exiting</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_out_exiting_proc">out_exiting</seealso></c>, and
- <c><seealso marker="#trace_3_trace_messages_out_exited_proc">out_exited</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_in_exiting_proc"><c>in_exiting</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_out_exiting_proc"><c>out_exiting</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_out_exited_proc"><c>out_exited</c></seealso>.</p>
</item>
<tag><c>running_procs</c></tag>
<item>
@@ -8716,21 +8720,21 @@ timestamp() ->
However this option also includes schedule events when the
process executes within the context of a port without
being scheduled out itself.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_in_proc">in</seealso></c> and
- <c><seealso marker="#trace_3_trace_messages_out_proc">out</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_in_proc"><c>in</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_out_proc"><c>out</c></seealso>.</p>
</item>
<tag><c>running_ports</c></tag>
<item>
<p>Traces scheduling of ports.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_in_port">in</seealso></c> and
- <c><seealso marker="#trace_3_trace_messages_out_port">out</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_in_port"><c>in</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_out_port"><c>out</c></seealso>.</p>
</item>
<tag><c>garbage_collection</c></tag>
<item>
<p>Traces garbage collections of processes.</p>
- <p>Message tags: <c><seealso marker="#trace_3_trace_messages_gc_minor_start">gc_minor_start</seealso></c>,
- <c><seealso marker="#trace_3_trace_messages_gc_max_heap_size">gc_max_heap_size</seealso></c> and
- <c><seealso marker="#trace_3_trace_messages_gc_minor_end">gc_minor_end</seealso></c>.</p>
+ <p>Message tags: <seealso marker="#trace_3_trace_messages_gc_minor_start"><c>gc_minor_start</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_gc_max_heap_size"><c>gc_max_heap_size</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_gc_minor_end"><c>gc_minor_end</c></seealso>.</p>
</item>
<tag><c>timestamp</c></tag>
<item>
@@ -8758,7 +8762,7 @@ timestamp() ->
<seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
monotonic time</seealso> time-stamp in all trace messages. The
time-stamp (Ts) has the same format and value as produced by
- <c><seealso marker="#monotonic_time-1">erlang:monotonic_time(nano_seconds)</seealso></c>.
+ <seealso marker="#monotonic_time-1"><c>erlang:monotonic_time(nano_seconds)</c></seealso>.
This flag overrides the <c>cpu_timestamp</c> flag.</p>
</item>
<tag><c>strict_monotonic_timestamp</c></tag>
@@ -8768,8 +8772,8 @@ timestamp() ->
monotonic time</seealso> and a monotonically increasing
integer in all trace messages. The time-stamp (Ts) has the
same format and value as produced by
- <c>{<seealso marker="#monotonic_time-1">erlang:monotonic_time(nano_seconds)</seealso>,
- <seealso marker="#unique_integer-1">erlang:unique_integer([monotonic])</seealso>}</c>.
+ <c>{</c><seealso marker="#monotonic_time-1"><c>erlang:monotonic_time(nano_seconds)</c></seealso><c>,</c>
+ <seealso marker="#unique_integer-1"><c>erlang:unique_integer([monotonic])</c></seealso><c>}</c>.
This flag overrides the <c>cpu_timestamp</c> flag.</p>
</item>
<tag><c>arity</c></tag>
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 3dca58d60b..4cf38bf894 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -2115,32 +2115,36 @@ profile_runnable_proc(Process *p, Eterm status){
Eterm *hp, msg;
Eterm where = am_undefined;
ErlHeapFragment *bp = NULL;
- int use_current = 1;
+ BeamInstr *current = NULL;
#ifndef ERTS_SMP
#define LOCAL_HEAP_SIZE (4 + 6 + ERTS_TRACE_PATCH_TS_MAX_SIZE)
-
DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
hp = local_heap;
#else
+ ErtsThrPrgrDelayHandle dhndl;
Uint hsz = 4 + 6 + patch_ts_size(erts_system_profile_ts_type)-1;
#endif
-
- if (ERTS_PROC_IS_EXITING(p)) {
- use_current = 0;
- /* could probably set 'where' to 'exiting' here,
- * though it's not documented as such */
- } else {
- if (!p->current) {
- p->current = find_function_from_pc(p->i);
+ /* Assumptions:
+ * We possibly don't have the MAIN_LOCK for the process p here.
+ * We assume that we can read from p->current and p->i atomically
+ */
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay(); /* suspend purge operations */
+#endif
+
+ if (!ERTS_PROC_IS_EXITING(p)) {
+ if (p->current) {
+ current = p->current;
+ } else {
+ current = find_function_from_pc(p->i);
}
- use_current = p->current != NULL;
}
#ifdef ERTS_SMP
- if (!use_current) {
+ if (!current) {
hsz -= 4;
}
@@ -2148,11 +2152,15 @@ profile_runnable_proc(Process *p, Eterm status){
hp = bp->mem;
#endif
- if (use_current) {
- where = TUPLE3(hp, p->current[0], p->current[1], make_small(p->current[2])); hp += 4;
+ if (current) {
+ where = TUPLE3(hp, current[0], current[1], make_small(current[2])); hp += 4;
} else {
where = make_small(0);
}
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
erts_smp_mtx_lock(&smq_mtx);
diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index 22a1c0b765..95042ac802 100644
--- a/erts/emulator/test/bs_construct_SUITE.erl
+++ b/erts/emulator/test/bs_construct_SUITE.erl
@@ -23,6 +23,7 @@
-module(bs_construct_SUITE).
-export([all/0, suite/0,
+ init_per_suite/1, end_per_suite/1,
test1/1, test2/1, test3/1, test4/1, test5/1, testf/1,
not_used/1, in_guard/1,
mem_leak/1, coerce_to_float/1, bjorn/1,
@@ -43,6 +44,12 @@ all() ->
copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width,
bad_append, bs_add_overflow].
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ application:stop(os_mon).
+
big(1) ->
57285702734876389752897683.
@@ -882,10 +889,14 @@ append_unit_16(Bin) ->
%% Produce a large result of bs_add that, if cast to signed int, would overflow
%% into a negative number that fits a smallnum.
-bs_add_overflow(Config) ->
+bs_add_overflow(_Config) ->
+ Memsize = memsize(),
+ io:format("Memsize = ~w Bytes~n", [Memsize]),
case erlang:system_info(wordsize) of
8 ->
{skip, "64-bit architecture"};
+ _ when Memsize < (2 bsl 30) ->
+ {skip, "Less then 2 GB of memory"};
4 ->
Large = <<0:((1 bsl 30)-1)>>,
{'EXIT',{system_limit,_}} =
@@ -894,5 +905,10 @@ bs_add_overflow(Config) ->
Large/bits>>),
ok
end.
-
+
id(I) -> I.
+
+memsize() ->
+ application:ensure_all_started(os_mon),
+ {Tot,_Used,_} = memsup:get_memory_data(),
+ Tot.
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 26780f6017..c6939a695d 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -187,8 +187,13 @@ bulk_sendsend2(Terms, BinSize, BusyBufSize) ->
{ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)),
_Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]),
{Elapsed, {_TermsN, SizeN}, MonitorCount} =
- receive {sendersender, BigRes} ->
- BigRes
+ receive
+ %% On some platforms (windows), the time taken is 0 so we
+ %% simulate that some little time has passed.
+ {sendersender, {0.0,T,MC}} ->
+ {0.0015, T, MC};
+ {sendersender, BigRes} ->
+ BigRes
end,
stop_node(NodeRecv),
stop_node(NodeSend),
@@ -1042,10 +1047,13 @@ atom_roundtrip_r15b(Config) when is_list(Config) ->
ct:timetrap({minutes, 6}),
AtomData = atom_data(),
verify_atom_data(AtomData),
- {ok, Node} = start_node(Config, [], "r15b"),
- do_atom_roundtrip(Node, AtomData),
- stop_node(Node),
- ok;
+ case start_node(Config, [], "r15b") of
+ {ok, Node} ->
+ do_atom_roundtrip(Node, AtomData),
+ stop_node(Node);
+ {error, timeout} ->
+ {skip,"Unable to start OTP R15B release"}
+ end;
false ->
{skip,"No OTP R15B available"}
end.
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index 4ebc1f5782..dae8990f56 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -2581,7 +2581,10 @@ enable_internal_state() ->
_ -> erts_debug:set_internal_state(available_internal_state, true)
end.
-sys_mem_cond_run(ReqSizeMB, TestFun) when is_integer(ReqSizeMB) ->
+sys_mem_cond_run(OrigReqSizeMB, TestFun) when is_integer(OrigReqSizeMB) ->
+ %% Debug normally needs more memory, so double the requirement
+ Debug = erlang:system_info(debug_compiled),
+ ReqSizeMB = if Debug -> OrigReqSizeMB * 2; true -> OrigReqSizeMB end,
case total_memory() of
TotMem when is_integer(TotMem), TotMem >= ReqSizeMB ->
TestFun();
diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl
index 40c8bc4340..6582ad134b 100644
--- a/erts/emulator/test/trace_call_time_SUITE.erl
+++ b/erts/emulator/test/trace_call_time_SUITE.erl
@@ -351,7 +351,7 @@ combo(Config) when is_list(Config) ->
%% Tests tracing of bifs
bif(Config) when is_list(Config) ->
P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- M = 1000000,
+ M = 5000000,
%%
2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]),
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
@@ -381,7 +381,7 @@ bif(Config) when is_list(Config) ->
nif(Config) when is_list(Config) ->
load_nif(Config),
P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
- M = 1000000,
+ M = 5000000,
%%
1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]),
1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]),
diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c
index a4008186c4..d67b997d6d 100644
--- a/erts/etc/common/heart.c
+++ b/erts/etc/common/heart.c
@@ -529,7 +529,7 @@ kill_old_erlang(void){
char* envvar = NULL;
envvar = get_env(HEART_NO_KILL);
- if (!envvar || strcmp(envvar, "TRUE") == 0)
+ if (envvar && strcmp(envvar, "TRUE") == 0)
return;
if(heart_beat_kill_pid != 0){
@@ -566,7 +566,7 @@ kill_old_erlang(void){
char *envvar = NULL;
envvar = get_env(HEART_NO_KILL);
- if (!envvar || strcmp(envvar, "TRUE") == 0)
+ if (envvar && strcmp(envvar, "TRUE") == 0)
return;
envvar = get_env(HEART_KILL_SIGNAL);
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 8f9945c4b4..15fb718c47 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -1807,52 +1807,53 @@ define etp-process-info
# Args: Process*
#
printf " Pid: "
- etp-1 ($arg0)->common.id
+ set $etp_proc = ((Process*)$arg0)
+ etp-1 $etp_proc->common.id
printf "\n State: "
- etp-proc-state $arg0
+ etp-proc-state $etp_proc
if $proxy_process != 0
- printf " Pointer: (Process *) %p\n", $arg0
+ printf " Pointer: (Process *) %p\n", $etp_proc
printf " *** PROXY process struct *** refer to: \n"
- etp-pid2proc-1 $arg0->common.id
+ etp-pid2proc-1 $etp_proc->common.id
etp-process-info $proc
else
- if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0
- if ($arg0->common.u.alive.reg)
+ if (*(((Uint32 *) &($etp_proc->state))) & 0x4) == 0
+ if ($etp_proc->common.u.alive.reg)
printf " Registered name: "
- etp-1 $arg0->common.u.alive.reg->name
+ etp-1 $etp_proc->common.u.alive.reg->name
printf "\n"
end
end
- if ($arg0->current)
+ if ($etp_proc->current)
printf " Current function: "
- etp-1 $arg0->current[0]
+ etp-1 $etp_proc->current[0]
printf ":"
- etp-1 $arg0->current[1]
- printf "/%d\n", $arg0->current[2]
+ etp-1 $etp_proc->current[1]
+ printf "/%d\n", $etp_proc->current[2]
end
- if ($arg0->cp)
+ if ($etp_proc->cp)
printf " CP: "
- etp-cp-1 $arg0->cp
+ etp-cp-1 $etp_proc->cp
printf "\n"
end
- if ($arg0->i)
+ if ($etp_proc->i)
printf " I: "
- etp-cp-1 $arg0->i
+ etp-cp-1 $etp_proc->i
printf "\n"
end
- printf " Heap size: %ld\n", $arg0->heap_sz
- if ($arg0->old_heap)
- printf " Old-heap size: %ld\n", $arg0->old_hend - $arg0->old_heap
+ printf " Heap size: %ld\n", $etp_proc->heap_sz
+ if ($etp_proc->old_heap)
+ printf " Old-heap size: %ld\n", $etp_proc->old_hend - $etp_proc->old_heap
end
- printf " Mbuf size: %ld\n", $arg0->mbuf_sz
+ printf " Mbuf size: %ld\n", $etp_proc->mbuf_sz
if (etp_smp_compiled)
- printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len
+ printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($etp_proc->msg.len + $etp_proc->msg_inq.len), $etp_proc->msg.len, $etp_proc->msg_inq.len
else
- printf " Msgq len: %d\n", $arg0->msg.len
+ printf " Msgq len: %d\n", $etp_proc->msg.len
end
printf " Parent: "
- etp-1 $arg0->parent
- printf "\n Pointer: (Process *) %p\n", $arg0
+ etp-1 $etp_proc->parent
+ printf "\n Pointer: (Process *) %p\n", $etp_proc
end
end
@@ -1918,57 +1919,58 @@ end
define etp-process-memory-info
# Args: Process*
#
- if ((*(((Uint32 *) &(((Process *) $arg0)->state)))) & 0x400000)
+ set $etp_pmem_proc = ((Process *) $arg0)
+ if ((*(((Uint32 *) &($etp_pmem_proc->state)))) & 0x400000)
set $proxy_process = 1
else
set $proxy_process = 0
end
printf " "
- etp-1 $arg0->common.id
- printf ": (Process *) %p ", $arg0
+ etp-1 $etp_pmem_proc->common.id
+ printf ": (Process *) %p ", $etp_pmem_proc
if $proxy_process != 0
- printf "(Process *) %p ", $arg0
+ printf "(Process *) %p ", $etp_pmem_proc
printf " *** PROXY process struct *** refer to next: \n"
- etp-pid2proc-1 $arg0->common.id
+ etp-pid2proc-1 $etp_pmem_proc->common.id
printf " -"
etp-process-memory-info $proc
else
- printf " [Heap: %5ld", $arg0->heap_sz
- if ($arg0->old_heap)
- printf " | %5ld", $arg0->old_hend - $arg0->old_heap
+ printf " [Heap: %5ld", $etp_pmem_proc->heap_sz
+ if ($etp_pmem_proc->old_heap)
+ printf " | %5ld", $etp_pmem_proc->old_hend - $etp_pmem_proc->old_heap
else
printf " | none "
end
- printf "] [Mbuf: %5ld", $arg0->mbuf_sz
+ printf "] [Mbuf: %5ld", $etp_pmem_proc->mbuf_sz
if (etp_smp_compiled)
- printf " | %3ld (%3ld | %3ld)", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len
+ printf " | %3ld (%3ld | %3ld)", ($etp_pmem_proc->msg.len + $etp_pmem_proc->msg_inq.len), $etp_pmem_proc->msg.len, $etp_pmem_proc->msg_inq.len
else
- printf " | %3ld", $arg0->msg.len
+ printf " | %3ld", $etp_pmem_proc->msg.len
end
printf "] "
- if ($arg0->i)
+ if ($etp_pmem_proc->i)
printf " I: "
- etp-cp-1 $arg0->i
+ etp-cp-1 $etp_pmem_proc->i
printf " "
end
- if ($arg0->current)
- etp-1 $arg0->current[0]
+ if ($etp_pmem_proc->current)
+ etp-1 $etp_pmem_proc->current[0]
printf ":"
- etp-1 $arg0->current[1]
- printf "/%d ", $arg0->current[2]
+ etp-1 $etp_pmem_proc->current[1]
+ printf "/%d ", $etp_pmem_proc->current[2]
end
- if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0
- if ($arg0->common.u.alive.reg)
- etp-1 $arg0->common.u.alive.reg->name
+ if (*(((Uint32 *) &(((Process *) $etp_pmem_proc)->state))) & 0x4) == 0
+ if ($etp_pmem_proc->common.u.alive.reg)
+ etp-1 $etp_pmem_proc->common.u.alive.reg->name
printf " "
end
end
- if ($arg0->cp)
+ if ($etp_pmem_proc->cp)
printf " CP: "
- etp-cp-1 $arg0->cp
+ etp-cp-1 $etp_pmem_proc->cp
printf " "
end
printf "\n"
@@ -2166,23 +2168,24 @@ define etp-port-info
# Args: Port*
#
printf " Port: "
- etp-1 $arg0->common.id
- printf "\n Name: %s\n", $arg0->name
+ set $etp_pinfo_port = ((Port*)$arg0)
+ etp-1 $etp_pinfo_port->common.id
+ printf "\n Name: %s\n", $etp_pinfo_port->name
printf " State:"
- etp-port-state $arg0
+ etp-port-state $etp_pinfo_port
printf " Scheduler flags:"
- etp-port-sched-flags $arg0
- if (*(((Uint32 *) &(((Port *) $arg0)->state))) & 0x5C00) == 0
- if ($arg0->common.u.alive.reg)
+ etp-port-sched-flags $etp_pinfo_port
+ if (*(((Uint32 *) &($etp_pinfo_port->state))) & 0x5C00) == 0
+ if ($etp_pinfo_port->common.u.alive.reg)
printf " Registered name: "
- etp-1 $arg0->common.u.alive.reg->name
+ etp-1 $etp_pinfo_port->common.u.alive.reg->name
printf "\n"
end
end
printf " Connected: "
- set $connected = *(((Eterm *) &(((Port *) $arg0)->connected)))
+ set $connected = *(((Eterm *) &(((Port *) $etp_pinfo_port)->connected)))
etp-1 $connected
- printf "\n Pointer: (Port *) %p\n", $arg0
+ printf "\n Pointer: (Port *) %p\n", $etp_pinfo_port
end
document etp-port-info
@@ -3694,6 +3697,26 @@ document etp-address-to-beam-opcode
%---------------------------------------------------------------------------
end
+define etp-compile-debug
+ shell (cd $ERL_TOP && make emulator FLAVOR=smp TYPE=debug)
+end
+
+document etp-compile-debug
+%---------------------------------------------------------------------------
+% Re-compile the debug erlang emulator
+%---------------------------------------------------------------------------
+end
+
+define etp-compile
+ shell (cd $ERL_TOP && make emulator)
+end
+
+document etp-compile
+%---------------------------------------------------------------------------
+% Re-compile the erlang emulator
+%---------------------------------------------------------------------------
+end
+
############################################################################
# Toolbox parameter handling
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index 33a3813a16..6d77d7ee9e 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -127,7 +127,7 @@ handle_event(Event, #eh_state{log_func = LogFunc} = State) ->
_Else ->
{ok, ErrLogType} = application:get_env(sasl, errlog_type),
SReport = sasl_report:format_report(group_leader(), ErrLogType,
- tag_event(Event)),
+ tag_event(Event, local)),
if is_list(SReport) ->
SaslHeader = format_header(State),
case LogFunc of
@@ -142,8 +142,9 @@ handle_event(Event, #eh_state{log_func = LogFunc} = State) ->
ignore
end
end,
+ %% note that error_logger (unlike sasl) expects UTC time
EReport = error_logger_tty_h:write_event(
- tag_event(Event),io_lib),
+ tag_event(Event, utc), io_lib),
if is_list(EReport) ->
ErrHeader = format_header(State),
case LogFunc of
@@ -220,7 +221,9 @@ terminate(_) ->
terminate(_Arg, _State) ->
ok.
-tag_event(Event) ->
+tag_event(Event, utc) ->
+ {calendar:universal_time(), Event};
+tag_event(Event, _) ->
{calendar:local_time(), Event}.
set_curr_func(CurrFunc, Config) ->
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl
index 7b9b5687e5..bd1ac54781 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl
@@ -40,6 +40,7 @@ suite() ->
%% @end
%%--------------------------------------------------------------------
init_per_suite(Config) ->
+ application:start(sasl),
Gen = spawn(fun() -> gen() end),
[{gen,Gen}|Config].
@@ -52,6 +53,7 @@ end_per_suite(Config) ->
Gen = proplists:get_value(gen, Config),
exit(Gen, kill),
ct:sleep(100),
+ application:stop(sasl),
ok.
%%--------------------------------------------------------------------
@@ -90,7 +92,8 @@ end_per_testcase(_TestCase, _Config) ->
%% @end
%%--------------------------------------------------------------------
groups() ->
- [{g1,[parallel,{repeat,10}],[tc1,tc2,tc3]}].
+ [{g1,[parallel,{repeat,10}],[tc1,tc2,tc3]},
+ {g2,[{repeat,10}],[tc1,tc2,tc3]}].
%%--------------------------------------------------------------------
%% @spec all() -> GroupsAndTestCases | {skip,Reason}
@@ -101,7 +104,7 @@ groups() ->
%% @end
%%--------------------------------------------------------------------
all() ->
- [{group,g1}].
+ [{group,g1},{group,g2}].
tc1(_) ->
ct:sleep(100),
@@ -121,5 +124,6 @@ gen() ->
gen_loop(N) ->
ct:log("Logger iteration: ~p", [N]),
error_logger:error_report(N),
- ct:sleep(200),
+ error_logger:info_report(progress, N),
+ ct:sleep(150),
gen_loop(N+1).
diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in
index 9e52a2adcf..0a8fbf513c 100644
--- a/lib/erl_interface/configure.in
+++ b/lib/erl_interface/configure.in
@@ -100,7 +100,9 @@ AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(void *)
AC_CHECK_SIZEOF(long long)
-if test $ac_cv_sizeof_void_p = 8; then
+dnl We set EI_64BIT mode when long is 8 bytes, this makes things
+dnl work on windows and unix correctly
+if test $ac_cv_sizeof_long = 8; then
CFLAGS="$CFLAGS -DEI_64BIT"
fi
diff --git a/lib/erl_interface/src/misc/show_msg.c b/lib/erl_interface/src/misc/show_msg.c
index 81accab4b6..5868cccba6 100644
--- a/lib/erl_interface/src/misc/show_msg.c
+++ b/lib/erl_interface/src/misc/show_msg.c
@@ -40,6 +40,8 @@
# include <time.h>
# endif
# endif
+#else
+# include <time.h>
#endif
#include "eiext.h"
diff --git a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
index 30f5fe33a0..cfe9083065 100644
--- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
+++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
@@ -377,8 +377,14 @@ TESTCASE(test_ei_decode_ulong)
EI_DECODE_2 (decode_ulong, 11, unsigned long, ll(0x8000000000000000));
EI_DECODE_2 (decode_ulong, 11, unsigned long, ll(0xffffffffffffffff));
} else {
- EI_DECODE_2 (decode_ulong, 7, unsigned long, 0x80000000);
- EI_DECODE_2 (decode_ulong, 7, unsigned long, 0xffffffff);
+ if (sizeof(void*) > 4) {
+ /* Windows */
+ EI_DECODE_2_FAIL(decode_ulong, 11, unsigned long, ll(0x8000000000000000));
+ EI_DECODE_2_FAIL(decode_ulong, 11, unsigned long, ll(0xffffffffffffffff));
+ } else {
+ EI_DECODE_2 (decode_ulong, 7, unsigned long, 0x80000000);
+ EI_DECODE_2 (decode_ulong, 7, unsigned long, 0xffffffff);
+ }
}
EI_DECODE_2_FAIL(decode_ulong, 9, unsigned long, ll(0x7fffffffffff));
diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c b/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c
index 687a45bbab..d97f218a26 100644
--- a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c
+++ b/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c
@@ -149,7 +149,7 @@ TESTCASE(round_trip_conversion)
{
int v;
- for (v = 8; v; v <<= 1) {
+ for (v = 8, n = 0; n < (sizeof(v)*8-4-1); v <<= 1, n++) {
for (i=-4; i<4; i++) {
encode_decode(erl_mk_int(v+i), "INT");
encode_decode(erl_mk_int(-(v+i)), "NEG INT");
@@ -166,7 +166,7 @@ TESTCASE(round_trip_conversion)
}
{
long long v;
- for (v = 8; v; v <<= 1) {
+ for (v = 8, n = 0; n < (sizeof(v)*8-4-1); v <<= 1, n++) {
for (i=-4; i<4; i++) {
encode_decode(erl_mk_longlong(v+i), "LONGLONG");
encode_decode(erl_mk_longlong(-(v+i)), "NEG LONGLONG");
diff --git a/lib/erl_interface/test/runner.erl b/lib/erl_interface/test/runner.erl
index 9a27eda038..1084eec2a3 100644
--- a/lib/erl_interface/test/runner.erl
+++ b/lib/erl_interface/test/runner.erl
@@ -55,7 +55,7 @@ test(Tc, Timeout) ->
%% Returns: {ok, Port}
start({Prog, Tc}) when is_list(Prog), is_integer(Tc) ->
- Port = open_port({spawn, Prog}, [{packet, 4}]),
+ Port = open_port({spawn, Prog}, [{packet, 4}, exit_status]),
Command = [Tc div 256, Tc rem 256],
Port ! {self(), {command, Command}},
Port.
@@ -125,7 +125,9 @@ get_term(Port, Timeout) ->
get_reply(Port, Timeout) when is_port(Port) ->
receive
{Port, {data, Reply}} ->
- Reply
+ Reply;
+ Fail when element(1, Fail) == Port ->
+ ct:fail("Got unexpected message from port: ~p",[Fail])
after Timeout ->
ct:fail("No response from C program")
end.
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 5cebce18a9..ab2cc5ab21 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,22 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.2.3</title>
+ <section><title>Inets 6.2.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Handle multiple \t in mime types file</p>
+ <p>
+ Own Id: OTP-13663 Aux Id: seq13132 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.2.3</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index 6a801c973d..e5182ca23c 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -1004,7 +1004,8 @@ read_config_file(Stream, SoFar) ->
%% Ignore commented lines for efficiency later ..
read_config_file(Stream, SoFar);
Line ->
- NewLine = re:replace(clean(Line),"[\t\r\f ]"," ", [{return,list}]),
+ NewLine = re:replace(white_space_clean(Line),
+ "[\t\r\f ]"," ", [{return,list}, global]),
case NewLine of
[] ->
%% Also ignore empty lines ..
@@ -1020,7 +1021,7 @@ parse_mime_types(Stream,MimeTypesList) ->
eof ->
eof;
String ->
- white_space_clean(String)
+ re:replace(white_space_clean(String), "[\t\r\f ]"," ", [{return,list}, global])
end,
parse_mime_types(Stream, MimeTypesList, Line).
parse_mime_types(Stream, MimeTypesList, eof) ->
@@ -1042,6 +1043,8 @@ parse_mime_types(Stream, MimeTypesList, Line) ->
suffixes(_MimeType,[]) ->
[];
+suffixes(MimeType,[""|Rest]) ->
+ suffixes(MimeType, Rest);
suffixes(MimeType,[Suffix|Rest]) ->
[{Suffix,MimeType}|suffixes(MimeType,Rest)].
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index f5167116ce..3194b5ad3d 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -72,7 +72,8 @@ all() ->
{group, https_security},
{group, http_reload},
{group, https_reload},
- {group, http_mime_types}
+ {group, http_mime_types},
+ mime_types_format
].
groups() ->
@@ -1296,6 +1297,115 @@ non_disturbing(Config) when is_list(Config)->
inets_test_lib:close(Type, Socket),
[{server_name, "httpd_non_disturbing_" ++ Version}] = httpd:info(Server, [server_name]).
+%%-------------------------------------------------------------------------
+mime_types_format(Config) when is_list(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ MimeTypes = filename:join(DataDir, "mime_types.txt"),
+ {ok,[{"wrl","x-world/x-vrml"},
+ {"vrml","x-world/x-vrml"},
+ {"ice","x-conference/x-cooltalk"},
+ {"movie","video/x-sgi-movie"},
+ {"avi","video/x-msvideo"},
+ {"qt","video/quicktime"},
+ {"mov","video/quicktime"},
+ {"mpeg","video/mpeg"},
+ {"mpg","video/mpeg"},
+ {"mpe","video/mpeg"},
+ {"sgml","text/x-sgml"},
+ {"sgm","text/x-sgml"},
+ {"etx","text/x-setext"},
+ {"tsv","text/tab-separated-values"},
+ {"rtx","text/richtext"},
+ {"txt","text/plain"},
+ {"html","text/html"},
+ {"htm","text/html"},
+ {"css","text/css"},
+ {"xwd","image/x-xwindowdump"},
+ {"xpm","image/x-xpixmap"},
+ {"xbm","image/x-xbitmap"},
+ {"rgb","image/x-rgb"},
+ {"ppm","image/x-portable-pixmap"},
+ {"pgm","image/x-portable-graymap"},
+ {"pbm","image/x-portable-bitmap"},
+ {"pnm","image/x-portable-anymap"},
+ {"ras","image/x-cmu-raster"},
+ {"tiff","image/tiff"},
+ {"tif","image/tiff"},
+ {"png","image/png"},
+ {"jpeg","image/jpeg"},
+ {"jpg","image/jpeg"},
+ {"jpe","image/jpeg"},
+ {"ief","image/ief"},
+ {"gif","image/gif"},
+ {"pdb","chemical/x-pdb"},
+ {"xyz","chemical/x-pdb"},
+ {"wav","audio/x-wav"},
+ {"ra","audio/x-realaudio"},
+ {"rpm","audio/x-pn-realaudio-plugin"},
+ {"ram","audio/x-pn-realaudio"},
+ {"aif","audio/x-aiff"},
+ {"aiff","audio/x-aiff"},
+ {"aifc","audio/x-aiff"},
+ {"mpga","audio/mpeg"},
+ {"mp2","audio/mpeg"},
+ {"au","audio/basic"},
+ {"snd","audio/basic"},
+ {"zip","application/zip"},
+ {"src","application/x-wais-source"},
+ {"ustar","application/x-ustar"},
+ {"ms","application/x-troff-ms"},
+ {"me","application/x-troff-me"},
+ {"man","application/x-troff-man"},
+ {"t","application/x-troff"},
+ {"tr","application/x-troff"},
+ {"roff","application/x-troff"},
+ {"texinfo","application/x-texinfo"},
+ {"texi","application/x-texinfo"},
+ {"tex","application/x-tex"},
+ {"tcl","application/x-tcl"},
+ {"tar","application/x-tar"},
+ {"sv4crc","application/x-sv4crc"},
+ {"sv4cpio","application/x-sv4cpio"},
+ {"sit","application/x-stuffit"},
+ {"shar","application/x-shar"},
+ {"sh","application/x-sh"},
+ {"nc","application/x-netcdf"},
+ {"cdf","application/x-netcdf"},
+ {"mif","application/x-mif"},
+ {"latex","application/x-latex"},
+ {"skp","application/x-koan"},
+ {"skd","application/x-koan"},
+ {"skt","application/x-koan"},
+ {"skm","application/x-koan"},
+ {"cgi","application/x-httpd-cgi"},
+ {"hdf","application/x-hdf"},
+ {"gz","application/x-gzip"},
+ {"gtar","application/x-gtar"},
+ {"dvi","application/x-dvi"},
+ {"dcr","application/x-director"},
+ {"dir","application/x-director"},
+ {"dxr","application/x-director"},
+ {"csh","application/x-csh"},
+ {"cpio","application/x-cpio"},
+ {"Z","application/x-compress"},
+ {"vcd","application/x-cdlink"},
+ {"bcpio","application/x-bcpio"},
+ {"rtf","application/rtf"},
+ {"ppt","application/powerpoint"},
+ {"ai","application/postscript"},
+ {"eps","application/postscript"},
+ {"ps","application/postscript"},
+ {"pdf","application/pdf"},
+ {"oda","application/oda"},
+ {"bin","application/octet-stream"},
+ {"dms","application/octet-stream"},
+ {"lha","application/octet-stream"},
+ {"lzh","application/octet-stream"},
+ {"exe","application/octet-stream"},
+ {"class","application/octet-stream"},
+ {"doc","application/msword"},
+ {"cpt","application/mac-compactpro"},
+ {"hqx","application/mac-binhex40"}]} = httpd_conf:load_mime_types(MimeTypes).
%%--------------------------------------------------------------------
%% Internal functions -----------------------------------
diff --git a/lib/inets/test/httpd_SUITE_data/mime_types.txt b/lib/inets/test/httpd_SUITE_data/mime_types.txt
new file mode 100644
index 0000000000..3149a119d5
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/mime_types.txt
@@ -0,0 +1,100 @@
+# This is a comment. I love comments.
+
+
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40 hqx
+application/mac-compactpro cpt
+application/macwriteii
+application/msword doc
+application/news-message-id
+application/news-transmission
+application/octet-stream bin dms lha lzh exe class
+application/oda oda
+application/pdf pdf
+application/postscript ai eps ps
+application/powerpoint ppt
+application/remote-printing
+application/rtf rtf
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio bcpio
+application/x-cdlink vcd
+application/x-compress Z
+application/x-cpio cpio
+application/x-csh csh
+application/x-director dcr dir dxr
+application/x-dvi dvi
+application/x-gtar gtar
+application/x-gzip gz
+application/x-hdf hdf
+application/x-httpd-cgi cgi
+application/x-koan skp skd skt skm
+application/x-latex latex
+application/x-mif mif
+application/x-netcdf nc cdf
+application/x-sh sh
+application/x-shar shar
+application/x-stuffit sit
+application/x-sv4cpio sv4cpio
+application/x-sv4crc sv4crc
+application/x-tar tar
+application/x-tcl tcl
+application/x-tex tex
+application/x-texinfo texinfo texi
+application/x-troff t tr roff
+application/x-troff-man man
+application/x-troff-me me
+application/x-troff-ms ms
+application/x-ustar ustar
+application/x-wais-source src
+application/zip zip
+audio/basic au snd
+audio/mpeg mpga mp2
+audio/x-aiff aif aiff aifc
+audio/x-pn-realaudio ram
+audio/x-pn-realaudio-plugin rpm
+audio/x-realaudio ra
+audio/x-wav wav
+chemical/x-pdb pdb xyz
+image/gif gif
+image/ief ief
+image/jpeg jpeg jpg jpe
+image/png png
+image/tiff tiff tif
+image/x-cmu-raster ras
+image/x-portable-anymap pnm
+image/x-portable-bitmap pbm
+image/x-portable-graymap pgm
+image/x-portable-pixmap ppm
+image/x-rgb rgb
+image/x-xbitmap xbm
+image/x-xpixmap xpm
+image/x-xwindowdump xwd
+message/external-body
+message/news
+message/partial
+message/rfc822
+multipart/alternative
+multipart/appledouble
+multipart/digest
+multipart/mixed
+multipart/parallel
+text/css css
+text/html html htm
+text/plain txt
+text/richtext rtx
+text/tab-separated-values tsv
+text/x-setext etx
+text/x-sgml sgml sgm
+video/mpeg mpeg mpg mpe
+video/quicktime qt mov
+video/x-msvideo avi
+video/x-sgi-movie movie
+x-conference/x-cooltalk ice
+x-world/x-vrml wrl vrml
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index 88135ea43d..83242c2df8 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -232,6 +232,14 @@ do_recv(Sock, Bs) ->
that receives messages from the socket. If called by any other
process than the current controlling process,
<c>{error, not_owner}</c> is returned.</p>
+ <p>If the socket is set in active mode, this function
+ will transfer any messages in the mailbox of the caller
+ to the new controlling process.
+ If any other process is interacting with the socket while
+ the transfer is happening, the transfer may not work correctly
+ and messages may remain in the caller's mailbox. For instance
+ changing the sockets active mode before the transfere is complete
+ may cause this.</p>
</desc>
</func>
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index 6f6f53309e..54298e6309 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -135,8 +135,8 @@ t_recv_delim(Config) when is_list(Config) ->
{ok, Client} = gen_tcp:connect(localhost, Port, Opts),
{ok, A} = gen_tcp:accept(L),
ok = gen_tcp:send(A, "abcXefgX"),
- {ok, "abcX"} = gen_tcp:recv(Client, 0, 0),
- {ok, "efgX"} = gen_tcp:recv(Client, 0, 0),
+ {ok, "abcX"} = gen_tcp:recv(Client, 0, 200),
+ {ok, "efgX"} = gen_tcp:recv(Client, 0, 200),
ok = gen_tcp:close(Client),
ok = gen_tcp:close(A),
ok.
diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl
index e63ed34973..45032faf6d 100644
--- a/lib/kernel/test/heart_SUITE.erl
+++ b/lib/kernel/test/heart_SUITE.erl
@@ -74,7 +74,8 @@ all() -> [
set_cmd, clear_cmd, get_cmd,
callback_api,
options_api,
- kill_pid
+ kill_pid,
+ heart_no_kill
].
groups() ->
@@ -634,7 +635,9 @@ suicide_by_heart() ->
end.
non_suicide_by_heart() ->
- P = open_port({spawn,"heart -ht 11 -pid "++os:getpid()},[exit_status, {env, {"HEART_NO_KILL", "TRUE"}}, {packet,2}]),
+ P = open_port({spawn,"heart -ht 11 -pid "++os:getpid()},
+ [exit_status, {env, [{"HEART_NO_KILL", "TRUE"}]},
+ {packet,2}]),
receive X -> X end,
%% Just hang and wait for heart to timeout
receive
diff --git a/lib/os_mon/test/cpu_sup_SUITE.erl b/lib/os_mon/test/cpu_sup_SUITE.erl
index 41115ee6e2..7122d23503 100644
--- a/lib/os_mon/test/cpu_sup_SUITE.erl
+++ b/lib/os_mon/test/cpu_sup_SUITE.erl
@@ -256,7 +256,7 @@ unavailable(Config) when is_list(Config) ->
restart(Config) when is_list(Config) ->
ok = application:set_env(os_mon, start_cpu_sup, true),
- {ok, _Pid} = supervisor:restart_child(os_mon_sup, cpu_sup),
+ supervisor:restart_child(os_mon_sup, cpu_sup),
ok.
%% Aux
diff --git a/lib/os_mon/test/os_mon_SUITE.erl b/lib/os_mon/test/os_mon_SUITE.erl
index 033a5ae162..c373b5d851 100644
--- a/lib/os_mon/test/os_mon_SUITE.erl
+++ b/lib/os_mon/test/os_mon_SUITE.erl
@@ -21,7 +21,7 @@
-include_lib("common_test/include/ct.hrl").
%% Test server specific exports
--export([all/0, suite/0]).
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]).
%% Test cases
-export([app_file/1, appup_file/1, config/1]).
@@ -36,6 +36,13 @@ all() ->
_OS -> [app_file, appup_file]
end.
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ application:stop(os_mon),
+ ok.
+
%% Testing .app file
app_file(Config) when is_list(Config) ->
ok = test_server:app_test(os_mon),
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 1aa601dc55..74bb06b9b2 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -701,6 +701,23 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
</func>
<func>
+ <name>pkix_match_dist_point(CRL, DistPoint) -> boolean()</name>
+ <fsummary>Checks whether the given distribution point matches the
+ Issuing Distribution Point of the CRL.</fsummary>
+
+ <type>
+ <v>CRL = der_encoded() | #'CertificateList'{} </v>
+ <v>DistPoint = #'DistributionPoint'{}</v>
+ </type>
+ <desc>
+ <p>Checks whether the given distribution point matches the
+ Issuing Distribution Point of the CRL, as described in RFC 5280.
+ If the CRL doesn't have an Issuing Distribution Point extension,
+ the distribution point always matches.</p>
+ </desc>
+ </func>
+
+ <func>
<name>pkix_sign(#'OTPTBSCertificate'{}, Key) -> der_encoded()</name>
<fsummary>Signs certificate.</fsummary>
<type>
@@ -819,7 +836,26 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
<p>Verifies a digital signature.</p>
</desc>
</func>
-
+
+ <func>
+ <name>short_name_hash(Name) -> string()</name>
+ <type>
+ <v>Name = issuer_name()</v>
+ </type>
+ <desc>
+ <p>Generates a short hash of an issuer name. The hash is
+ returned as a string containing eight hexadecimal digits.</p>
+
+ <p>The return value of this function is the same as the result
+ of the commands <c>openssl crl -hash</c> and
+ <c>openssl x509 -issuer_hash</c>, when passed the issuer name of
+ a CRL or a certificate, respectively. This hash is used by the
+ <c>c_rehash</c> tool to maintain a directory of symlinks to CRL
+ files, in order to facilitate looking up a CRL by its issuer
+ name.</p>
+ </desc>
+ </func>
+
</funcs>
</erlref>
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index a5944bd604..d23abfe256 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -53,8 +53,10 @@
pkix_crls_validate/3,
pkix_dist_point/1,
pkix_dist_points/1,
+ pkix_match_dist_point/2,
pkix_crl_verify/2,
- pkix_crl_issuer/1
+ pkix_crl_issuer/1,
+ short_name_hash/1
]).
-export_type([public_key/0, private_key/0, pem_entry/0,
@@ -524,6 +526,38 @@ pkix_dist_points(OtpCert) ->
[], Value).
%%--------------------------------------------------------------------
+-spec pkix_match_dist_point(der_encoded() | #'CertificateList'{},
+ #'DistributionPoint'{}) -> boolean().
+%% Description: Check whether the given distribution point matches
+%% the "issuing distribution point" of the CRL.
+%%--------------------------------------------------------------------
+pkix_match_dist_point(CRL, DistPoint) when is_binary(CRL) ->
+ pkix_match_dist_point(der_decode('CertificateList', CRL), DistPoint);
+pkix_match_dist_point(#'CertificateList'{},
+ #'DistributionPoint'{distributionPoint = asn1_NOVALUE}) ->
+ %% No distribution point name specified - that's considered a match.
+ true;
+pkix_match_dist_point(#'CertificateList'{
+ tbsCertList =
+ #'TBSCertList'{
+ crlExtensions = Extensions}},
+ #'DistributionPoint'{
+ distributionPoint = {fullName, DPs}}) ->
+ case pubkey_cert:select_extension(?'id-ce-issuingDistributionPoint', Extensions) of
+ undefined ->
+ %% If the CRL doesn't have an IDP extension, it
+ %% automatically qualifies.
+ true;
+ #'Extension'{extnValue = IDPValue} ->
+ %% If the CRL does have an IDP extension, it must match
+ %% the given DistributionPoint to be considered a match.
+ IDPEncoded = der_decode('IssuingDistributionPoint', IDPValue),
+ #'IssuingDistributionPoint'{distributionPoint = {fullName, IDPs}} =
+ pubkey_cert_records:transform(IDPEncoded, decode),
+ pubkey_crl:match_one(IDPs, DPs)
+ end.
+
+%%--------------------------------------------------------------------
-spec pkix_sign(#'OTPTBSCertificate'{},
rsa_private_key() | dsa_private_key()) -> Der::binary().
%%
@@ -785,6 +819,17 @@ oid2ssh_curvename(?'secp384r1') -> <<"nistp384">>;
oid2ssh_curvename(?'secp521r1') -> <<"nistp521">>.
%%--------------------------------------------------------------------
+-spec short_name_hash({rdnSequence, [#'AttributeTypeAndValue'{}]}) ->
+ string().
+
+%% Description: Generates OpenSSL-style hash of a name.
+%%--------------------------------------------------------------------
+short_name_hash({rdnSequence, _Attributes} = Name) ->
+ HashThis = encode_name_for_short_hash(Name),
+ <<HashValue:32/little, _/binary>> = crypto:hash(sha, HashThis),
+ string:to_lower(string:right(integer_to_list(HashValue, 16), 8, $0)).
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
do_verify(DigestOrPlainText, DigestType, Signature,
@@ -1047,3 +1092,74 @@ ec_key({PubKey, PrivateKey}, Params) ->
parameters = Params,
publicKey = PubKey}.
+encode_name_for_short_hash({rdnSequence, Attributes0}) ->
+ Attributes = lists:map(fun normalise_attribute/1, Attributes0),
+ {Encoded, _} = 'OTP-PUB-KEY':'enc_RDNSequence'(Attributes, []),
+ Encoded.
+
+%% Normalise attribute for "short hash". If the attribute value
+%% hasn't been decoded yet, decode it so we can normalise it.
+normalise_attribute([#'AttributeTypeAndValue'{
+ type = _Type,
+ value = Binary} = ATV]) when is_binary(Binary) ->
+ case pubkey_cert_records:transform(ATV, decode) of
+ #'AttributeTypeAndValue'{value = Binary} ->
+ %% Cannot decode attribute; return original.
+ [ATV];
+ DecodedATV = #'AttributeTypeAndValue'{} ->
+ %% The new value will either be String or {Encoding,String}.
+ normalise_attribute([DecodedATV])
+ end;
+normalise_attribute([#'AttributeTypeAndValue'{
+ type = _Type,
+ value = {Encoding, String}} = ATV])
+ when
+ Encoding =:= utf8String;
+ Encoding =:= printableString;
+ Encoding =:= teletexString;
+ Encoding =:= ia5String ->
+ %% These string types all give us something that the unicode
+ %% module understands.
+ NewValue = normalise_attribute_value(String),
+ [ATV#'AttributeTypeAndValue'{value = NewValue}];
+normalise_attribute([#'AttributeTypeAndValue'{
+ type = _Type,
+ value = String} = ATV]) when is_list(String) ->
+ %% A string returned by pubkey_cert_records:transform/2, for
+ %% certain attributes that commonly have incorrect value types.
+ NewValue = normalise_attribute_value(String),
+ [ATV#'AttributeTypeAndValue'{value = NewValue}].
+
+normalise_attribute_value(String) ->
+ Converted = unicode:characters_to_binary(String),
+ NormalisedString = normalise_string(Converted),
+ %% We can't use the encoding function for the actual type of the
+ %% attribute, since some of them don't allow utf8Strings, which is
+ %% the required encoding when creating the hash.
+ {NewBinary, _} = 'OTP-PUB-KEY':'enc_X520CommonName'({utf8String, NormalisedString}, []),
+ NewBinary.
+
+normalise_string(String) ->
+ %% Normalise attribute values as required for "short hashes", as
+ %% implemented by OpenSSL.
+
+ %% Remove ASCII whitespace from beginning and end.
+ TrimmedLeft = re:replace(String, "^[\s\f\n\r\t\v]+", "", [unicode, global]),
+ TrimmedRight = re:replace(TrimmedLeft, "[\s\f\n\r\t\v]+$", "", [unicode, global]),
+ %% Convert multiple whitespace characters to a single space.
+ Collapsed = re:replace(TrimmedRight, "[\s\f\n\r\t\v]+", "\s", [unicode, global]),
+ %% Convert ASCII characters to lowercase
+ Lower = ascii_to_lower(Collapsed),
+ %% And we're done!
+ Lower.
+
+ascii_to_lower(String) ->
+ %% Can't use string:to_lower/1, because that changes Latin-1
+ %% characters as well.
+ << <<(if $A =< C, C =< $Z ->
+ C + ($a - $A);
+ true ->
+ C
+ end)>>
+ ||
+ <<C>> <= iolist_to_binary(String) >>.
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 71a77efa2e..b22b69a0f2 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -44,7 +44,8 @@ all() ->
encrypt_decrypt,
{group, sign_verify},
pkix, pkix_countryname, pkix_emailaddress, pkix_path_validation,
- pkix_iso_rsa_oid, pkix_iso_dsa_oid, pkix_crl, general_name].
+ pkix_iso_rsa_oid, pkix_iso_dsa_oid, pkix_crl, general_name,
+ short_cert_issuer_hash, short_crl_issuer_hash].
groups() ->
[{pem_decode_encode, [], [dsa_pem, rsa_pem, ec_pem, encrypted_pem,
@@ -817,6 +818,42 @@ general_name(Config) when is_list(Config) ->
authorityCertSerialNumber =
1}).
%%--------------------------------------------------------------------
+short_cert_issuer_hash() ->
+ [{doc, "Test OpenSSL-style hash for certificate issuer"}].
+
+short_cert_issuer_hash(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ [{'Certificate', CertDER, _}] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "client_cert.pem")),
+
+ %% This hash value was obtained by running:
+ %% openssl x509 -in client_cert.pem -issuer_hash -noout
+ CertIssuerHash = "d4c8d7e5",
+
+ #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{issuer = Issuer}} =
+ public_key:pkix_decode_cert(CertDER, otp),
+
+ CertIssuerHash = public_key:short_name_hash(Issuer).
+
+%%--------------------------------------------------------------------
+short_crl_issuer_hash() ->
+ [{doc, "Test OpenSSL-style hash for CRL issuer"}].
+
+short_crl_issuer_hash(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ [{'CertificateList', CrlDER, _}] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "idp_crl.pem")),
+
+ %% This hash value was obtained by running:
+ %% openssl crl -in idp_crl.pem -hash -noout
+ CrlIssuerHash = "d6134ed3",
+
+ Issuer = public_key:pkix_crl_issuer(CrlDER),
+
+ CrlIssuerHash = public_key:short_name_hash(Issuer).
+
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
asn1_encode_decode({Asn1Type, Der, not_encrypted} = Entry) ->
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 65f1acc6a6..0570853a9b 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -601,10 +601,13 @@ handle_option([{profile, _ID} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{max_random_length_padding, _Bool} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{tstflg, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
+handle_ssh_option({tstflg,_F} = Opt) -> Opt;
handle_ssh_option({minimal_remote_max_packet_size, Value} = Opt) when is_integer(Value), Value >=0 ->
Opt;
handle_ssh_option({system_dir, Value} = Opt) when is_list(Value) ->
diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl
index fbf85cfcfc..480795cfc7 100644
--- a/lib/ssh/src/ssh_dbg.erl
+++ b/lib/ssh/src/ssh_dbg.erl
@@ -23,7 +23,8 @@
-module(ssh_dbg).
-export([messages/0,
- messages/1
+ messages/1,
+ stop/0
]).
-include("ssh.hrl").
@@ -40,34 +41,59 @@ messages() -> messages(fun(String,_D) -> io:format(String) end).
messages(Write) when is_function(Write,2) ->
catch dbg:start(),
-
- Handler = fun msg_formater/2,
- InitialData = #data{writer = Write},
- {ok,_} = dbg:tracer(process, {Handler, InitialData}),
-
+ setup_tracer(Write),
dbg:p(new,c),
+ dbg_ssh_messages().
+
+dbg_ssh_messages() ->
dbg:tp(ssh_message,encode,1, x),
dbg:tp(ssh_message,decode,1, x),
dbg:tpl(ssh_transport,select_algorithm,3, x).
+%%%----------------------------------------------------------------
+stop() ->
+ dbg:stop().
+
%%%================================================================
msg_formater({trace,Pid,call,{ssh_message,encode,[Msg]}}, D) ->
fmt("~nSEND ~p ~s~n", [Pid,wr_record(shrink_bin(Msg))], D);
-
+msg_formater({trace,_Pid,return_from,{ssh_message,encode,1},_Res}, D) ->
+ D;
+
+msg_formater({trace,_Pid,call,{ssh_message,decode,_}}, D) ->
+ D;
msg_formater({trace,Pid,return_from,{ssh_message,decode,1},Msg}, D) ->
fmt("~nRECV ~p ~s~n", [Pid,wr_record(shrink_bin(Msg))], D);
+msg_formater({trace,_Pid,call,{ssh_transport,select_algorithm,_}}, D) ->
+ D;
msg_formater({trace,Pid,return_from,{ssh_transport,select_algorithm,3},{ok,Alg}}, D) ->
fmt("~nALGORITHMS ~p~n~s~n", [Pid, wr_record(Alg)], D);
-msg_formater(_, D) ->
- D.
+msg_formater({trace,Pid,send,ErlangMsg,Dest}, D) ->
+ fmt("~nERL MSG ~p SEND TO ~p~n ~p~n", [Pid,Dest, shrink_bin(ErlangMsg)], D);
+
+msg_formater({trace,Pid,'receive',ErlangMsg}, D) ->
+ fmt("~nERL MSG ~p RECIEVE~n ~p~n", [Pid,shrink_bin(ErlangMsg)], D);
+
+msg_formater(M, D) ->
+ fmt("~nDBG ~n~p~n", [shrink_bin(M)], D).
+
+%% msg_formater(_, D) ->
+%% D.
fmt(Fmt, Args, D=#data{writer=Write,acc=Acc}) ->
D#data{acc = Write(io_lib:format(Fmt, Args), Acc)}.
%%%----------------------------------------------------------------
+setup_tracer(Write) ->
+ Handler = fun msg_formater/2,
+ InitialData = #data{writer = Write},
+ {ok,_} = dbg:tracer(process, {Handler, InitialData}),
+ ok.
+
+%%%----------------------------------------------------------------
shrink_bin(B) when is_binary(B), size(B)>100 -> {'*** SHRINKED BIN',size(B),element(1,split_binary(B,20)),'***'};
shrink_bin(L) when is_list(L) -> lists:map(fun shrink_bin/1, L);
shrink_bin(T) when is_tuple(T) -> list_to_tuple(shrink_bin(tuple_to_list(T)));
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index e9b523d9e1..c427db24a4 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -28,6 +28,51 @@
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 7.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correct ssl:prf/5 to use the negotiated cipher suite's
+ prf function in ssl:prf/5 instead of the default prf.</p>
+ <p>
+ Own Id: OTP-13546</p>
+ </item>
+ <item>
+ <p>
+ Timeouts may have the value 0, guards have been corrected
+ to allow this</p>
+ <p>
+ Own Id: OTP-13635</p>
+ </item>
+ <item>
+ <p>
+ Change of internal handling of hash sign pairs as the
+ used one enforced to much restrictions making some valid
+ combinations unavailable.</p>
+ <p>
+ Own Id: OTP-13670</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Create a little randomness in sending of session
+ invalidation messages, to mitigate load when whole table
+ is invalidated.</p>
+ <p>
+ Own Id: OTP-13490</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 7.3.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index bed82cdb91..4ea000802f 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -331,39 +331,88 @@ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_valid
<tag><c>{crl_check, boolean() | peer | best_effort }</c></tag>
<item>
- Perform CRL (Certificate Revocation List) verification
+ <p>Perform CRL (Certificate Revocation List) verification
<seealso marker="public_key:public_key#pkix_crls_validate-3">
(public_key:pkix_crls_validate/3)</seealso> on all the certificates during the path validation
<seealso
marker="public_key:public_key#pkix_path_validation-3">(public_key:pkix_path_validation/3)
</seealso>
- of the certificate chain. Defaults to false.
+ of the certificate chain. Defaults to <c>false</c>.</p>
- <p><c>peer</c> - check is only performed on
- the peer certificate.</p>
+ <taglist>
+ <tag><c>peer</c></tag>
+ <item>check is only performed on the peer certificate.</item>
- <p><c>best_effort</c> - if certificate revocation status can not be determined
- it will be accepted as valid.</p>
+ <tag><c>best_effort</c></tag>
+ <item>if certificate revocation status can not be determined
+ it will be accepted as valid.</item>
+ </taglist>
<p>The CA certificates specified for the connection will be used to
construct the certificate chain validating the CRLs.</p>
- <p>The CRLs will be fetched from a local or external cache see
+ <p>The CRLs will be fetched from a local or external cache. See
<seealso marker="ssl:ssl_crl_cache_api">ssl_crl_cache_api(3)</seealso>.</p>
</item>
<tag><c>{crl_cache, {Module :: atom(), {DbHandle :: internal | term(), Args :: list()}}}</c></tag>
<item>
- <p>Module defaults to ssl_crl_cache with <c> DbHandle </c> internal and an
- empty argument list. The following arguments may be specified for the internal cache.</p>
+ <p>Specify how to perform lookup and caching of certificate revocation lists.
+ <c>Module</c> defaults to <seealso marker="ssl:ssl_crl_cache">ssl_crl_cache</seealso>
+ with <c> DbHandle </c> being <c>internal</c> and an
+ empty argument list.</p>
+
+ <p>There are two implementations available:</p>
+
<taglist>
- <tag><c>{http, timeout()}</c></tag>
- <item><p>
- Enables fetching of CRLs specified as http URIs in<seealso
- marker="public_key:public_key_records"> X509 certificate extensions.</seealso>
- Requires the OTP inets application.</p>
- </item>
- </taglist>
+ <tag><c>ssl_crl_cache</c></tag>
+ <item>
+ <p>This module maintains a cache of CRLs. CRLs can be
+ added to the cache using the function <seealso
+ marker="ssl:ssl_crl_cache#insert-1">ssl_crl_cache:insert/1</seealso>,
+ and optionally automatically fetched through HTTP if the
+ following argument is specified:</p>
+
+ <taglist>
+ <tag><c>{http, timeout()}</c></tag>
+ <item><p>
+ Enables fetching of CRLs specified as http URIs in<seealso
+ marker="public_key:public_key_records">X509 certificate extensions</seealso>.
+ Requires the OTP inets application.</p>
+ </item>
+ </taglist>
+ </item>
+
+ <tag><c>ssl_crl_hash_dir</c></tag>
+ <item>
+ <p>This module makes use of a directory where CRLs are
+ stored in files named by the hash of the issuer name.</p>
+
+ <p>The file names consist of eight hexadecimal digits
+ followed by <c>.rN</c>, where <c>N</c> is an integer,
+ e.g. <c>1a2b3c4d.r0</c>. For the first version of the
+ CRL, <c>N</c> starts at zero, and for each new version,
+ <c>N</c> is incremented by one. The OpenSSL utility
+ <c>c_rehash</c> creates symlinks according to this
+ pattern.</p>
+
+ <p>For a given hash value, this module finds all
+ consecutive <c>.r*</c> files starting from zero, and those
+ files taken together make up the revocation list. CRL
+ files whose <c>nextUpdate</c> fields are in the past, or
+ that are issued by a different CA that happens to have the
+ same name hash, are excluded.</p>
+
+ <p>The following argument is required:</p>
+
+ <taglist>
+ <tag><c>{dir, string()}</c></tag>
+ <item><p>Specifies the directory in which the CRLs can be found.</p></item>
+ </taglist>
+
+ </item>
+ </taglist>
+
</item>
<tag><c>{partial_chain, fun(Chain::[DerCert]) -> {trusted_ca, DerCert} |
diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml
index 03ac010bfe..7440b6ef04 100644
--- a/lib/ssl/doc/src/ssl_crl_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml
@@ -76,10 +76,13 @@
</func>
<func>
+ <name>lookup(DistributionPoint, Issuer, DbHandle) -> not_available | CRLs </name>
<name>lookup(DistributionPoint, DbHandle) -> not_available | CRLs </name>
<fsummary> </fsummary>
<type>
<v> DistributionPoint = dist_point() </v>
+ <v> Issuer = <seealso
+ marker="public_key:public_key">public_key:issuer_name()</seealso> </v>
<v> DbHandle = cache_ref() </v>
<v> CRLs = [<seealso
marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
@@ -87,6 +90,18 @@
<desc> <p>Lookup the CRLs belonging to the distribution point <c> Distributionpoint</c>.
This function may choose to only look in the cache or to follow distribution point
links depending on how the cache is administrated. </p>
+
+ <p>The <c>Issuer</c> argument contains the issuer name of the
+ certificate to be checked. Normally the returned CRL should
+ be issued by this issuer, except if the <c>cRLIssuer</c> field
+ of <c>DistributionPoint</c> has a value, in which case that
+ value should be used instead.</p>
+
+ <p>In an earlier version of this API, the <c>lookup</c>
+ function received two arguments, omitting <c>Issuer</c>. For
+ compatibility, this is still supported: if there is no
+ <c>lookup/3</c> function in the callback module,
+ <c>lookup/2</c> is called instead.</p>
</desc>
</func>
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index 7a7a373487..b625db0656 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -70,6 +70,7 @@ MODULES= \
ssl_session_cache \
ssl_crl\
ssl_crl_cache \
+ ssl_crl_hash_dir \
ssl_socket \
ssl_listen_tracker_sup \
tls_record \
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 60a61bc901..b8be686b99 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -42,9 +42,8 @@
-export([next_record/1, next_event/3]).
%% Handshake handling
--export([%%renegotiate/2,
- send_handshake/2, send_change_cipher/2]).
-
+-export([%%renegotiate/2,
+ send_handshake/2, queue_handshake/2, queue_change_cipher/2]).
%% Alert and close handling
-export([%%send_alert/2, handle_own_alert/4, handle_close_alert/3,
@@ -53,11 +52,12 @@
]).
%% Data handling
+
-export([%%write_application_data/3,
read_application_data/2,
- %%passive_receive/2,
- next_record_if_active/1 %%,
- %%handle_common_event/4
+ passive_receive/2, next_record_if_active/1%,
+ %%handle_common_event/4,
+ %handle_packet/3
]).
%% gen_statem state functions
@@ -72,13 +72,13 @@
%%====================================================================
%% Internal application API
%%====================================================================
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts,
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts,
User, {CbModule, _,_, _} = CbInfo,
Timeout) ->
try
{ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket,
Opts, User, CbInfo]),
- {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
ok = ssl_connection:handshake(SslSocket, Timeout),
{ok, SslSocket}
catch
@@ -86,13 +86,13 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts,
Error
end;
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_} = Opts,
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = Opts,
User, {CbModule, _,_, _} = CbInfo,
Timeout) ->
try
{ok, Pid} = dtls_connection_sup:start_child_dist([Role, Host, Port, Socket,
Opts, User, CbInfo]),
- {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
ok = ssl_connection:handshake(SslSocket, Timeout),
{ok, SslSocket}
catch
@@ -100,14 +100,37 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_} = Opts,
Error
end.
-send_handshake(Handshake, #state{negotiated_version = Version,
- tls_handshake_history = Hist0,
- connection_states = ConnectionStates0} = State0) ->
- {BinHandshake, ConnectionStates, Hist} =
+send_handshake(Handshake, State) ->
+ send_handshake_flight(queue_handshake(Handshake, State)).
+
+queue_flight_buffer(Msg, #state{negotiated_version = Version,
+ connection_states = #connection_states{
+ current_write =
+ #connection_state{epoch = Epoch}},
+ flight_buffer = Flight} = State) ->
+ State#state{flight_buffer = Flight ++ [{Version, Epoch, Msg}]}.
+
+queue_handshake(Handshake, #state{negotiated_version = Version,
+ tls_handshake_history = Hist0,
+ connection_states = ConnectionStates0} = State0) ->
+ {Frag, ConnectionStates, Hist} =
encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
- send_flight(BinHandshake, State0#state{connection_states = ConnectionStates,
- tls_handshake_history = Hist
- }).
+ queue_flight_buffer(Frag, State0#state{connection_states = ConnectionStates,
+ tls_handshake_history = Hist}).
+
+send_handshake_flight(#state{socket = Socket,
+ transport_cb = Transport,
+ flight_buffer = Flight,
+ connection_states = ConnectionStates0} = State0) ->
+
+ {Encoded, ConnectionStates} =
+ encode_handshake_flight(Flight, ConnectionStates0),
+
+ Transport:send(Socket, Encoded),
+ State0#state{flight_buffer = [], connection_states = ConnectionStates}.
+
+queue_change_cipher(Msg, State) ->
+ queue_flight_buffer(Msg, State).
send_alert(Alert, #state{negotiated_version = Version,
socket = Socket,
@@ -118,15 +141,6 @@ send_alert(Alert, #state{negotiated_version = Version,
Transport:send(Socket, BinMsg),
State0#state{connection_states = ConnectionStates}.
-send_change_cipher(Msg, #state{connection_states = ConnectionStates0,
- socket = Socket,
- negotiated_version = Version,
- transport_cb = Transport} = State0) ->
- {BinChangeCipher, ConnectionStates} =
- encode_change_cipher(Msg, Version, ConnectionStates0),
- Transport:send(Socket, BinChangeCipher),
- State0#state{connection_states = ConnectionStates}.
-
%%====================================================================
%% tls_connection_sup API
%%====================================================================
@@ -196,28 +210,44 @@ error({call, From}, Msg, State) ->
error(_, _, _) ->
{keep_state_and_data, [postpone]}.
-hello(internal, #client_hello{client_version = ClientVersion} = Hello,
- #state{connection_states = ConnectionStates0,
- port = Port, session = #session{own_certificate = Cert} = Session0,
- renegotiation = {Renegotiation, _},
- session_cache = Cache,
- session_cache_cb = CacheCb,
- ssl_options = SslOpts} = State) ->
+%%--------------------------------------------------------------------
+-spec hello(gen_statem:event_type(),
+ #hello_request{} | #client_hello{} | #server_hello{} | term(),
+ #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+hello(internal, #client_hello{client_version = ClientVersion,
+ extensions = #hello_extensions{ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves}} = Hello,
+ State = #state{connection_states = ConnectionStates0,
+ port = Port, session = #session{own_certificate = Cert} = Session0,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
+ session_cache_cb = CacheCb,
+ negotiated_protocol = CurrentProtocol,
+ key_algorithm = KeyExAlg,
+ ssl_options = SslOpts}) ->
+
case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
- ConnectionStates0, Cert}, Renegotiation) of
- {Version, {Type, Session},
- ConnectionStates,
- #hello_extensions{ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves} = ServerHelloExt, HashSign} ->
- ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt, HashSign},
- State#state{connection_states = ConnectionStates,
+ ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ClientVersion, hello, State);
+ {Version, {Type, Session},
+ ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
+ Protocol = case Protocol0 of
+ undefined -> CurrentProtocol;
+ _ -> Protocol0
+ end,
+
+ ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
+ State#state{connection_states = ConnectionStates,
negotiated_version = Version,
+ hashsign_algorithm = HashSign,
session = Session,
- client_ecc = {EllipticCurves, EcPointFormats}}, ?MODULE);
- #alert{} = Alert ->
- handle_own_alert(Alert, ClientVersion, hello, State)
+ client_ecc = {EllipticCurves, EcPointFormats},
+ negotiated_protocol = Protocol}, ?MODULE)
end;
-hello(internal, Hello,
+hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
role = client,
@@ -261,8 +291,7 @@ connection(internal, #hello_request{}, #state{host = Host, port = Port,
renegotiation = {Renegotiation, _}} = State0) ->
Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
- %% TODO DTLS version State1 = send_handshake(Hello, State0),
- State1 = State0,
+ State1 = send_handshake(Hello, State0),
{Record, State} =
next_record(
State1#state{session = Session0#session{session_id
@@ -309,11 +338,24 @@ handle_info({Protocol, _, Data}, StateName,
{stop, {shutdown, own_alert}}
end;
handle_info({CloseTag, Socket}, StateName,
- #state{socket = Socket, close_tag = CloseTag,
- negotiated_version = _Version} = State) ->
+ #state{socket = Socket, close_tag = CloseTag,
+ negotiated_version = Version} = State) ->
+ %% Note that as of DTLS 1.2 (TLS 1.1),
+ %% failure to properly close a connection no longer requires that a
+ %% session not be resumed. This is a change from DTLS 1.0 to conform
+ %% with widespread implementation practice.
+ case Version of
+ {254, N} when N =< 253 ->
+ ok;
+ _ ->
+ %% As invalidate_sessions here causes performance issues,
+ %% we will conform to the widespread implementation
+ %% practice and go aginst the spec
+ %%invalidate_session(Role, Host, Port, Session)
+ ok
+ end,
handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
{stop, {shutdown, transport_closed}};
-
handle_info(Msg, StateName, State) ->
ssl_connection:handle_info(Msg, StateName, State).
@@ -343,76 +385,82 @@ format_status(Type, Data) ->
%%% Internal functions
%%--------------------------------------------------------------------
encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
- Seq = sequence(ConnectionStates0),
- {EncHandshake, FragmentedHandshake} = dtls_handshake:encode_handshake(Handshake, Version,
- Seq),
+ {Seq, ConnectionStates} = sequence(ConnectionStates0),
+ {EncHandshake, Frag} = dtls_handshake:encode_handshake(Handshake, Version, Seq),
Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake),
- {Encoded, ConnectionStates} =
- dtls_record:encode_handshake(FragmentedHandshake,
- Version, ConnectionStates0),
- {Encoded, ConnectionStates, Hist}.
-
-next_record(#state{%%flight = #flight{state = finished},
- protocol_buffers =
- #protocol_buffers{dtls_packets = [], dtls_cipher_texts = [CT | Rest]}
- = Buffers,
- connection_states = ConnStates0} = State) ->
- case dtls_record:decode_cipher_text(CT, ConnStates0) of
- {Plain, ConnStates} ->
- {Plain, State#state{protocol_buffers =
- Buffers#protocol_buffers{dtls_cipher_texts = Rest},
- connection_states = ConnStates}};
- #alert{} = Alert ->
- {Alert, State}
- end;
-next_record(#state{socket = Socket,
- transport_cb = Transport} = State) -> %% when FlightState =/= finished
- ssl_socket:setopts(Transport, Socket, [{active,once}]),
- {no_record, State};
-
+ {Frag, ConnectionStates, Hist}.
-next_record(State) ->
- {no_record, State}.
+encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
+ dtls_record:encode_change_cipher_spec(Version, ConnectionStates).
+encode_handshake_flight(Flight, ConnectionStates) ->
+ MSS = 1400,
+ encode_handshake_records(Flight, ConnectionStates, MSS, init_pack_records()).
-next_event(StateName, Record, State) ->
- next_event(StateName, Record, State, []).
+encode_handshake_records([], CS, _MSS, Recs) ->
+ {finish_pack_records(Recs), CS};
-next_event(connection = StateName, no_record, State0, Actions) ->
- case next_record_if_active(State0) of
- {no_record, State} ->
- ssl_connection:hibernate_after(StateName, State, Actions);
- {#ssl_tls{} = Record, State} ->
- {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]};
- {#alert{} = Alert, State} ->
- {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+encode_handshake_records([{Version, _Epoch, Frag = #change_cipher_spec{}}|Tail], ConnectionStates0, MSS, Recs0) ->
+ {Encoded, ConnectionStates} =
+ encode_change_cipher(Frag, Version, ConnectionStates0),
+ Recs = append_pack_records([Encoded], MSS, Recs0),
+ encode_handshake_records(Tail, ConnectionStates, MSS, Recs);
+
+encode_handshake_records([{Version, Epoch, {MsgType, MsgSeq, Bin}}|Tail], CS0, MSS, Recs0 = {Buf0, _}) ->
+ Space = MSS - iolist_size(Buf0),
+ Len = byte_size(Bin),
+ {Encoded, CS} =
+ encode_handshake_record(Version, Epoch, Space, MsgType, MsgSeq, Len, Bin, 0, MSS, [], CS0),
+ Recs = append_pack_records(Encoded, MSS, Recs0),
+ encode_handshake_records(Tail, CS, MSS, Recs).
+
+%% TODO: move to dtls_handshake????
+encode_handshake_record(_Version, _Epoch, _Space, _MsgType, _MsgSeq, _Len, <<>>, _Offset, _MRS, Encoded, CS)
+ when length(Encoded) > 0 ->
+ %% make sure we encode at least one segment (for empty messages like Server Hello Done
+ {lists:reverse(Encoded), CS};
+
+encode_handshake_record(Version, Epoch, Space, MsgType, MsgSeq, Len, Bin,
+ Offset, MRS, Encoded0, CS0) ->
+ MaxFragmentLen = Space - 25,
+ case Bin of
+ <<BinFragment:MaxFragmentLen/bytes, Rest/binary>> ->
+ ok;
+ _ ->
+ BinFragment = Bin,
+ Rest = <<>>
+ end,
+ FragLength = byte_size(BinFragment),
+ Frag = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragLength), BinFragment],
+ %% TODO Real solution, now avoid dialyzer error {Encoded, CS} = ssl_record:encode_handshake({Epoch, Frag}, Version, CS0),
+ {Encoded, CS} = ssl_record:encode_handshake(Frag, Version, CS0),
+ encode_handshake_record(Version, Epoch, MRS, MsgType, MsgSeq, Len, Rest, Offset + FragLength, MRS, [Encoded|Encoded0], CS).
+
+init_pack_records() ->
+ {[], []}.
+
+append_pack_records([], MSS, Recs = {Buf0, Acc0}) ->
+ Remaining = MSS - iolist_size(Buf0),
+ if Remaining < 12 ->
+ {[], [lists:reverse(Buf0)|Acc0]};
+ true ->
+ Recs
end;
-next_event(StateName, Record, State, Actions) ->
- case Record of
- no_record ->
- {next_state, StateName, State, Actions};
- #ssl_tls{} = Record ->
- {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]};
- #alert{} = Alert ->
- {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+append_pack_records([Head|Tail], MSS, {Buf0, Acc0}) ->
+ TotLen = iolist_size(Buf0) + iolist_size(Head),
+ if TotLen > MSS ->
+ append_pack_records(Tail, MSS, {[Head], [lists:reverse(Buf0)|Acc0]});
+ true ->
+ append_pack_records(Tail, MSS, {[Head|Buf0], Acc0})
end.
-send_flight(Fragments, #state{transport_cb = Transport, socket = Socket,
- protocol_buffers = _PBuffers} = State) ->
- Transport:send(Socket, Fragments),
- %% Start retransmission
- %% State#state{protocol_buffers =
- %% (PBuffers#protocol_buffers){ #flight{state = waiting}}}}.
- State.
+finish_pack_records({[], Acc}) ->
+ lists:reverse(Acc);
+finish_pack_records({Buf, Acc}) ->
+ lists:reverse([lists:reverse(Buf)|Acc]).
-handle_own_alert(_,_,_, State) -> %% Place holder
- {stop, {shutdown, own_alert}, State}.
-
-handle_normal_shutdown(_, _, _State) -> %% Place holder
- ok.
-
-encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
- dtls_record:encode_change_cipher_spec(Version, ConnectionStates).
+%% decode_alerts(Bin) ->
+%% ssl_alert:decode(Bin).
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
@@ -451,21 +499,137 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
start_or_recv_from = undefined,
protocol_cb = ?MODULE
}.
-read_application_data(_,State) ->
- {#ssl_tls{fragment = <<"place holder">>}, State}.
-next_tls_record(<<>>, _State) ->
- #alert{}; %% Place holder
-next_tls_record(_, State) ->
- {#ssl_tls{fragment = <<"place holder">>}, State}.
+next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{
+ dtls_record_buffer = Buf0,
+ dtls_cipher_texts = CT0} = Buffers} = State0) ->
+ case dtls_record:get_dtls_records(Data, Buf0) of
+ {Records, Buf1} ->
+ CT1 = CT0 ++ Records,
+ next_record(State0#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_record_buffer = Buf1,
+ dtls_cipher_texts = CT1}});
+
+ #alert{} = Alert ->
+ Alert
+ end.
+
+next_record(#state{%%flight = #flight{state = finished},
+ protocol_buffers =
+ #protocol_buffers{dtls_packets = [], dtls_cipher_texts = [CT | Rest]}
+ = Buffers,
+ connection_states = ConnStates0} = State) ->
+ case dtls_record:decode_cipher_text(CT, ConnStates0) of
+ {Plain, ConnStates} ->
+ {Plain, State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnStates}};
+ #alert{} = Alert ->
+ {Alert, State}
+ end;
+next_record(#state{socket = Socket,
+ transport_cb = Transport} = State) -> %% when FlightState =/= finished
+ ssl_socket:setopts(Transport, Socket, [{active,once}]),
+ {no_record, State};
+next_record(State) ->
+ {no_record, State}.
-sequence(_) ->
- %%TODO real imp
- 1.
-next_record_if_active(State =
- #state{socket_options =
- #socket_options{active = false}}) ->
+next_record_if_active(State =
+ #state{socket_options =
+ #socket_options{active = false}}) ->
{no_record ,State};
next_record_if_active(State) ->
next_record(State).
+
+passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
+ case Buffer of
+ <<>> ->
+ {Record, State} = next_record(State0),
+ next_event(StateName, Record, State);
+ _ ->
+ {Record, State} = read_application_data(<<>>, State0),
+ next_event(StateName, Record, State)
+ end.
+
+next_event(StateName, Record, State) ->
+ next_event(StateName, Record, State, []).
+
+next_event(connection = StateName, no_record, State0, Actions) ->
+ case next_record_if_active(State0) of
+ {no_record, State} ->
+ ssl_connection:hibernate_after(StateName, State, Actions);
+ {#ssl_tls{} = Record, State} ->
+ {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]};
+ {#alert{} = Alert, State} ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ end;
+next_event(StateName, Record, State, Actions) ->
+ case Record of
+ no_record ->
+ {next_state, StateName, State, Actions};
+ #ssl_tls{} = Record ->
+ {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]};
+ #alert{} = Alert ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ end.
+
+read_application_data(_,State) ->
+ {#ssl_tls{fragment = <<"place holder">>}, State}.
+
+handle_own_alert(_,_,_, State) -> %% Place holder
+ {stop, {shutdown, own_alert}, State}.
+
+handle_normal_shutdown(_, _, _State) -> %% Place holder
+ ok.
+
+%% TODO This generates dialyzer warnings, has to be handled differently.
+%% handle_packet(Address, Port, Packet) ->
+%% try dtls_record:get_dtls_records(Packet, <<>>) of
+%% %% expect client hello
+%% {[#ssl_tls{type = ?HANDSHAKE, version = {254, _}} = Record], <<>>} ->
+%% handle_dtls_client_hello(Address, Port, Record);
+%% _Other ->
+%% {error, not_dtls}
+%% catch
+%% _Class:_Error ->
+%% {error, not_dtls}
+%% end.
+
+%% handle_dtls_client_hello(Address, Port,
+%% #ssl_tls{epoch = Epoch, sequence_number = Seq,
+%% version = Version} = Record) ->
+%% {[{Hello, _}], _} =
+%% dtls_handshake:get_dtls_handshake(Record,
+%% dtls_handshake:dtls_handshake_new_flight(undefined)),
+%% #client_hello{client_version = {Major, Minor},
+%% random = Random,
+%% session_id = SessionId,
+%% cipher_suites = CipherSuites,
+%% compression_methods = CompressionMethods} = Hello,
+%% CookieData = [address_to_bin(Address, Port),
+%% <<?BYTE(Major), ?BYTE(Minor)>>,
+%% Random, SessionId, CipherSuites, CompressionMethods],
+%% Cookie = crypto:hmac(sha, <<"secret">>, CookieData),
+
+%% case Hello of
+%% #client_hello{cookie = Cookie} ->
+%% accept;
+
+%% _ ->
+%% %% generate HelloVerifyRequest
+%% {RequestFragment, _} = dtls_handshake:encode_handshake(
+%% dtls_handshake:hello_verify_request(Cookie),
+%% Version, 0),
+%% HelloVerifyRequest =
+%% dtls_record:encode_tls_cipher_text(?HANDSHAKE, Version, Epoch, Seq, RequestFragment),
+%% {reply, HelloVerifyRequest}
+%% end.
+
+%% address_to_bin({A,B,C,D}, Port) ->
+%% <<0:80,16#ffff:16,A,B,C,D,Port:16>>;
+%% address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
+%% <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
+
+sequence(#connection_states{dtls_write_msg_seq = Seq} = CS) ->
+ {Seq, CS#connection_states{dtls_write_msg_seq = Seq + 1}}.
diff --git a/lib/ssl/src/dtls_connection.hrl b/lib/ssl/src/dtls_connection.hrl
index 69137b520b..ee3daa3c14 100644
--- a/lib/ssl/src/dtls_connection.hrl
+++ b/lib/ssl/src/dtls_connection.hrl
@@ -31,6 +31,7 @@
-record(protocol_buffers, {
dtls_packets = [], %%::[binary()], % Not yet handled decode ssl/tls packets.
dtls_record_buffer = <<>>, %%:: binary(), % Buffer of incomplete records
+ dtls_fragment_state, %%:: [], % DTLS fragments
dtls_handshake_buffer = <<>>, %%:: binary(), % Buffer of incomplete handshakes
dtls_cipher_texts = [], %%:: [binary()],
dtls_cipher_texts_next %%:: [binary()] % Received for Epoch not yet active
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 4f48704cac..5a799cf441 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -24,8 +24,8 @@
-include("ssl_alert.hrl").
-export([client_hello/8, client_hello/9, hello/4,
- get_dtls_handshake/2,
- %%dtls_handshake_new_flight/1, dtls_handshake_new_epoch/1,
+ hello_verify_request/1, get_dtls_handshake/2,
+ dtls_handshake_new_flight/1, dtls_handshake_new_epoch/1,
encode_handshake/3]).
-type dtls_handshake() :: #client_hello{} | #hello_verify_request{} |
@@ -92,96 +92,135 @@ hello(#server_hello{server_version = Version, random = Random,
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end;
-hello(#client_hello{client_version = ClientVersion}, _Options, {_,_,_,_,ConnectionStates,_}, _Renegotiation) ->
- %% Return correct typ to make dialyzer happy until we have time to make the real imp.
- HashSigns = tls_v1:default_signature_algs(dtls_v1:corresponding_tls_version(ClientVersion)),
- {ClientVersion, {new, #session{}}, ConnectionStates, #hello_extensions{},
- %% Placeholder for real hasign handling
- hd(HashSigns)}.
-
-%% hello(Address, Port,
-%% #ssl_tls{epoch = _Epoch, sequence_number = _Seq,
-%% version = Version} = Record) ->
-%% case get_dtls_handshake(Record,
-%% dtls_handshake_new_flight(undefined)) of
-%% {[Hello | _], _} ->
-%% hello(Address, Port, Version, Hello);
-%% {retransmit, HandshakeState} ->
-%% {retransmit, HandshakeState}
-%% end.
-
-%% hello(Address, Port, Version, Hello) ->
-%% #client_hello{client_version = {Major, Minor},
-%% random = Random,
-%% session_id = SessionId,
-%% cipher_suites = CipherSuites,
-%% compression_methods = CompressionMethods} = Hello,
-%% CookieData = [address_to_bin(Address, Port),
-%% <<?BYTE(Major), ?BYTE(Minor)>>,
-%% Random, SessionId, CipherSuites, CompressionMethods],
-%% Cookie = crypto:hmac(sha, <<"secret">>, CookieData),
-
-%% case Hello of
-%% #client_hello{cookie = Cookie} ->
-%% accept;
-%% _ ->
-%% %% generate HelloVerifyRequest
-%% HelloVerifyRequest = enc_hs(#hello_verify_request{protocol_version = Version,
-%% cookie = Cookie},
-%% Version, 0, 1400),
-%% {reply, HelloVerifyRequest}
-%% end.
+hello(#client_hello{client_version = ClientVersion} = Hello,
+ #ssl_options{versions = Versions} = SslOpts,
+ Info, Renegotiation) ->
+ Version = ssl_handshake:select_version(dtls_record, ClientVersion, Versions),
+ %%
+ %% TODO: handle Cipher Fallback
+ %%
+ handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation).
+
+-spec hello_verify_request(binary()) -> #hello_verify_request{}.
+%%
+%% Description: Creates a hello verify request message sent by server to
+%% verify client
+%%--------------------------------------------------------------------
+hello_verify_request(Cookie) ->
+ %% TODO: DTLS Versions?????
+ #hello_verify_request{protocol_version = {254, 255}, cookie = Cookie}.
+
+%%--------------------------------------------------------------------
%% %%--------------------------------------------------------------------
encode_handshake(Handshake, Version, MsgSeq) ->
{MsgType, Bin} = enc_handshake(Handshake, Version),
Len = byte_size(Bin),
- EncHandshake = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(0), ?uint24(Len), Bin],
- FragmentedHandshake = dtls_fragment(erlang:iolist_size(EncHandshake), MsgType, Len, MsgSeq, Bin, 0, []),
- {EncHandshake, FragmentedHandshake}.
+ Enc = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(0), ?uint24(Len), Bin],
+ Frag = {MsgType, MsgSeq, Bin},
+ {Enc, Frag}.
%%--------------------------------------------------------------------
--spec get_dtls_handshake(#ssl_tls{}, #dtls_hs_state{} | binary()) ->
+-spec get_dtls_handshake(#ssl_tls{}, #dtls_hs_state{} | undefined) ->
{[dtls_handshake()], #dtls_hs_state{}} | {retransmit, #dtls_hs_state{}}.
%%
%% Description: Given a DTLS state and new data from ssl_record, collects
%% and returns it as a list of handshake messages, also returns a new
%% DTLS state
%%--------------------------------------------------------------------
-get_dtls_handshake(Record, <<>>) ->
- get_dtls_handshake_aux(Record, #dtls_hs_state{}); %% Init handshake state!?
-get_dtls_handshake(Record, HsState) ->
- get_dtls_handshake_aux(Record, HsState).
+get_dtls_handshake(Records, undefined) ->
+ HsState = #dtls_hs_state{highest_record_seq = 0,
+ starting_read_seq = 0,
+ fragments = gb_trees:empty(),
+ completed = []},
+ get_dtls_handshake(Records, HsState);
+get_dtls_handshake(Records, HsState0) when is_list(Records) ->
+ HsState1 = lists:foldr(fun get_dtls_handshake_aux/2, HsState0, Records),
+ get_dtls_handshake_completed(HsState1);
+get_dtls_handshake(Record, HsState0) when is_record(Record, ssl_tls) ->
+ HsState1 = get_dtls_handshake_aux(Record, HsState0),
+ get_dtls_handshake_completed(HsState1).
-%% %%--------------------------------------------------------------------
-%% -spec dtls_handshake_new_epoch(#dtls_hs_state{}) -> #dtls_hs_state{}.
-%% %%
-%% %% Description: Reset the DTLS decoder state for a new Epoch
-%% %%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+-spec dtls_handshake_new_epoch(#dtls_hs_state{}) -> #dtls_hs_state{}.
+%%
+%% Description: Reset the DTLS decoder state for a new Epoch
+%%--------------------------------------------------------------------
%% dtls_handshake_new_epoch(<<>>) ->
%% dtls_hs_state_init();
-%% dtls_handshake_new_epoch(HsState) ->
-%% HsState#dtls_hs_state{highest_record_seq = 0,
-%% starting_read_seq = HsState#dtls_hs_state.current_read_seq,
-%% fragments = gb_trees:empty(), completed = []}.
-
-%% %--------------------------------------------------------------------
-%% -spec dtls_handshake_new_flight(integer() | undefined) -> #dtls_hs_state{}.
-%% %
-%% % Description: Init the DTLS decoder state for a new Flight
-%% dtls_handshake_new_flight(ExpectedReadReq) ->
-%% #dtls_hs_state{current_read_seq = ExpectedReadReq,
-%% highest_record_seq = 0,
-%% starting_read_seq = 0,
-%% fragments = gb_trees:empty(), completed = []}.
+dtls_handshake_new_epoch(HsState) ->
+ HsState#dtls_hs_state{highest_record_seq = 0,
+ starting_read_seq = HsState#dtls_hs_state.current_read_seq,
+ fragments = gb_trees:empty(), completed = []}.
+
+%--------------------------------------------------------------------
+-spec dtls_handshake_new_flight(integer() | undefined) -> #dtls_hs_state{}.
+%
+% Description: Init the DTLS decoder state for a new Flight
+dtls_handshake_new_flight(ExpectedReadReq) ->
+ #dtls_hs_state{current_read_seq = ExpectedReadReq,
+ highest_record_seq = 0,
+ starting_read_seq = 0,
+ fragments = gb_trees:empty(), completed = []}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+handle_client_hello(Version, #client_hello{session_id = SugesstedId,
+ cipher_suites = CipherSuites,
+ compression_methods = Compressions,
+ random = Random,
+ extensions = #hello_extensions{elliptic_curves = Curves,
+ signature_algs = ClientHashSigns} = HelloExt},
+ #ssl_options{versions = Versions,
+ signature_algs = SupportedHashSigns} = SslOpts,
+ {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) ->
+ case dtls_record:is_acceptable_version(Version, Versions) of
+ true ->
+ AvailableHashSigns = ssl_handshake:available_signature_algs(
+ ClientHashSigns, SupportedHashSigns, Cert,
+ dtls_v1:corresponding_tls_version(Version)),
+ ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(Version)),
+ {Type, #session{cipher_suite = CipherSuite} = Session1}
+ = ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions,
+ Port, Session0#session{ecc = ECCCurve}, Version,
+ SslOpts, Cache, CacheCb, Cert),
+ case CipherSuite of
+ no_suite ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
+ _ ->
+ {KeyExAlg,_,_,_} = ssl_cipher:suite_definition(CipherSuite),
+ case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of
+ #alert{} = Alert ->
+ Alert;
+ HashSign ->
+ handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt,
+ SslOpts, Session1, ConnectionStates0,
+ Renegotiation, HashSign)
+ end
+ end;
+ false ->
+ ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
+ end.
+
+handle_client_hello_extensions(Version, Type, Random, CipherSuites,
+ HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation, HashSign) ->
+ try ssl_handshake:handle_client_hello_extensions(dtls_record, Random, CipherSuites,
+ HelloExt, dtls_v1:corresponding_tls_version(Version),
+ SslOpts, Session0, ConnectionStates0, Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ {Session, ConnectionStates, Protocol, ServerHelloExt} ->
+ {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign}
+ catch throw:Alert ->
+ Alert
+ end.
+
handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) ->
case ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite,
- Compression, HelloExt, Version,
+ Compression, HelloExt,
+ dtls_v1:corresponding_tls_version(Version),
SslOpt, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
Alert;
@@ -189,16 +228,8 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
{Version, SessionId, ConnectionStates, ProtoExt, Protocol}
end.
-dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc)
- when byte_size(Bin) + 12 < Mss ->
- FragmentLen = byte_size(Bin),
- BinMsg = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragmentLen), Bin],
- lists:reverse([BinMsg|Acc]);
-dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc) ->
- FragmentLen = Mss - 12,
- <<Fragment:FragmentLen/bytes, Rest/binary>> = Bin,
- BinMsg = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragmentLen), Fragment],
- dtls_fragment(Mss, MsgType, Len, MsgSeq, Rest, Offset + FragmentLen, [BinMsg|Acc]).
+get_dtls_handshake_completed(HsState = #dtls_hs_state{completed = Completed}) ->
+ {lists:reverse(Completed), HsState#dtls_hs_state{completed = []}}.
get_dtls_handshake_aux(#ssl_tls{version = Version,
sequence_number = SeqNo,
@@ -214,25 +245,18 @@ get_dtls_handshake_aux(Version, SeqNo,
case reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq,
FragmentOffset, FragmentLength,
Body, HsState0) of
- {retransmit, HsState1} ->
- case Rest of
- <<>> ->
- {retransmit, HsState1};
- _ ->
- get_dtls_handshake_aux(Version, SeqNo, Rest, HsState1)
- end;
{HsState1, HighestSeqNo, MsgBody} ->
HsState2 = dec_dtls_fragment(Version, HighestSeqNo, Type, Length, MessageSeq, MsgBody, HsState1),
HsState3 = process_dtls_fragments(Version, HsState2),
get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3);
+
HsState2 ->
HsState3 = process_dtls_fragments(Version, HsState2),
get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3)
end;
get_dtls_handshake_aux(_Version, _SeqNo, <<>>, HsState) ->
- {lists:reverse(HsState#dtls_hs_state.completed),
- HsState#dtls_hs_state{completed = []}}.
+ HsState.
dec_dtls_fragment(Version, SeqNo, Type, Length, MessageSeq, MsgBody,
HsState = #dtls_hs_state{highest_record_seq = HighestSeqNo, completed = Acc}) ->
@@ -299,12 +323,6 @@ reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
{HsState, SeqNo, Body};
reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, 0, Length,
- _Body, HsState =
- #dtls_hs_state{current_read_seq = CurrentReadSeq})
- when MessageSeq < CurrentReadSeq ->
- {retransmit, HsState};
-
-reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, 0, Length,
_Body, HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
when MessageSeq < CurrentReadSeq ->
HsState;
@@ -419,6 +437,8 @@ enc_handshake(#hello_verify_request{protocol_version = {Major, Minor},
?BYTE(CookieLength),
Cookie/binary>>};
+enc_handshake(#hello_request{}, _Version) ->
+ {?HELLO_REQUEST, <<>>};
enc_handshake(#client_hello{client_version = {Major, Minor},
random = Random,
session_id = SessionID,
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index e79e1cede0..5387fcafa8 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -36,11 +36,12 @@
-export([decode_cipher_text/2]).
%% Encoding
--export([encode_plain_text/4, encode_handshake/3, encode_change_cipher_spec/2]).
+-export([encode_plain_text/4, encode_tls_cipher_text/5, encode_change_cipher_spec/2]).
%% Protocol version handling
--export([protocol_version/1, lowest_protocol_version/2, lowest_protocol_version/1,
- highest_protocol_version/1, supported_protocol_versions/0,
+-export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2,
+ highest_protocol_version/1, highest_protocol_version/2,
+ is_higher/2, supported_protocol_versions/0,
is_acceptable_version/2]).
%% DTLS Epoch handling
@@ -208,14 +209,6 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
false ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end.
-%%--------------------------------------------------------------------
--spec encode_handshake(iolist(), dtls_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
-%%
-%% Description: Encodes a handshake message to send on the ssl-socket.
-%%--------------------------------------------------------------------
-encode_handshake(Frag, Version, ConnectionStates) ->
- encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
%%--------------------------------------------------------------------
-spec encode_change_cipher_spec(dtls_version(), #connection_states{}) ->
@@ -271,20 +264,39 @@ lowest_protocol_version(Versions) ->
%%
%% Description: Highest protocol version present in a list
%%--------------------------------------------------------------------
-highest_protocol_version([Ver | Vers]) ->
- highest_protocol_version(Ver, Vers).
+highest_protocol_version([]) ->
+ highest_protocol_version();
+highest_protocol_version(Versions) ->
+ [Ver | Vers] = Versions,
+ highest_list_protocol_version(Ver, Vers).
-highest_protocol_version(Version, []) ->
+%%--------------------------------------------------------------------
+-spec highest_protocol_version(dtls_version(), dtls_version()) -> dtls_version().
+%%
+%% Description: Highest protocol version of two given versions
+%%--------------------------------------------------------------------
+highest_protocol_version(Version = {M, N}, {M, O}) when N < O ->
+ Version;
+highest_protocol_version({M, _},
+ Version = {M, _}) ->
Version;
-highest_protocol_version(Version = {N, M}, [{N, O} | Rest]) when M < O ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version({M, _}, [Version = {M, _} | Rest]) ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version(Version = {M,_}, [{N,_} | Rest]) when M < N ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version(_, [Version | Rest]) ->
- highest_protocol_version(Version, Rest).
+highest_protocol_version(Version = {M,_},
+ {N, _}) when M < N ->
+ Version;
+highest_protocol_version(_,Version) ->
+ Version.
+%%--------------------------------------------------------------------
+-spec is_higher(V1 :: dtls_version(), V2::dtls_version()) -> boolean().
+%%
+%% Description: Is V1 > V2
+%%--------------------------------------------------------------------
+is_higher({M, N}, {M, O}) when N < O ->
+ true;
+is_higher({M, _}, {N, _}) when M < N ->
+ true;
+is_higher(_, _) ->
+ false.
%%--------------------------------------------------------------------
-spec supported_protocol_versions() -> [dtls_version()].
@@ -301,27 +313,33 @@ supported_protocol_versions() ->
{ok, []} ->
lists:map(Fun, supported_protocol_versions([]));
{ok, Vsns} when is_list(Vsns) ->
- supported_protocol_versions(Vsns);
+ supported_protocol_versions(lists:map(Fun, Vsns));
{ok, Vsn} ->
- supported_protocol_versions([Vsn])
+ supported_protocol_versions([Fun(Vsn)])
end.
supported_protocol_versions([]) ->
- Vsns = supported_connection_protocol_versions([]),
+ Vsns = case sufficient_dtlsv1_2_crypto_support() of
+ true ->
+ ?ALL_DATAGRAM_SUPPORTED_VERSIONS;
+ false ->
+ ?MIN_DATAGRAM_SUPPORTED_VERSIONS
+ end,
application:set_env(ssl, dtls_protocol_version, Vsns),
Vsns;
supported_protocol_versions([_|_] = Vsns) ->
- Vsns.
-
-%% highest_protocol_version() ->
-%% highest_protocol_version(supported_protocol_versions()).
-
-lowest_protocol_version() ->
- lowest_protocol_version(supported_protocol_versions()).
-
-supported_connection_protocol_versions([]) ->
- ?ALL_DATAGRAM_SUPPORTED_VERSIONS.
+ case sufficient_dtlsv1_2_crypto_support() of
+ true ->
+ Vsns;
+ false ->
+ case Vsns -- ['dtlsv1.2'] of
+ [] ->
+ ?MIN_SUPPORTED_VERSIONS;
+ NewVsns ->
+ NewVsns
+ end
+ end.
%%--------------------------------------------------------------------
-spec is_acceptable_version(dtls_version(), Supported :: [dtls_version()]) -> boolean().
@@ -419,6 +437,18 @@ set_connection_state_by_epoch(ConnectionStates0 =
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+
+
+lowest_list_protocol_version(Ver, []) ->
+ Ver;
+lowest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest).
+
+highest_list_protocol_version(Ver, []) ->
+ Ver;
+highest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest).
+
encode_tls_cipher_text(Type, {MajVer, MinVer}, Epoch, Seq, Fragment) ->
Length = erlang:iolist_size(Fragment),
[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Epoch),
@@ -432,6 +462,16 @@ calc_mac_hash(#connection_state{mac_secret = MacSecret,
mac_hash(Version, MacAlg, MacSecret, NewSeq, Type,
Length, Fragment).
+highest_protocol_version() ->
+ highest_protocol_version(supported_protocol_versions()).
+
+lowest_protocol_version() ->
+ lowest_protocol_version(supported_protocol_versions()).
+
+sufficient_dtlsv1_2_crypto_support() ->
+ CryptoSupport = crypto:supports(),
+ proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
+
mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
dtls_v1:mac_hash(Version, MacAlg, MacSecret, SeqNo, Type,
Length, Fragment).
@@ -439,8 +479,3 @@ mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
calc_aad(Type, {MajVer, MinVer}, Epoch, SeqNo) ->
NewSeq = (Epoch bsl 48) + SeqNo,
<<NewSeq:64/integer, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
-
-lowest_list_protocol_version(Ver, []) ->
- Ver;
-lowest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
- lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest).
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 937a3b1bd1..6467cedf9d 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -44,6 +44,7 @@
ssl_crl,
ssl_crl_cache,
ssl_crl_cache_api,
+ ssl_crl_hash_dir,
%% App structure
ssl_app,
ssl_sup,
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 90e0810241..53282998d0 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1049,9 +1049,9 @@ format_status(terminate, [_, StateName, State]) ->
srp_params = ?SECRET_PRINTOUT,
srp_keys = ?SECRET_PRINTOUT,
premaster_secret = ?SECRET_PRINTOUT,
- ssl_options = NewOptions}
+ ssl_options = NewOptions,
+ flight_buffer = ?SECRET_PRINTOUT}
}}]}].
-
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -1120,7 +1120,7 @@ resumed_server_hello(#state{session = Session,
server_hello(ServerHello, State0, Connection) ->
CipherSuite = ServerHello#server_hello.cipher_suite,
{KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
- State = Connection:send_handshake(ServerHello, State0),
+ State = Connection:queue_handshake(ServerHello, State0),
State#state{key_algorithm = KeyAlgorithm}.
server_hello_done(State, Connection) ->
@@ -1166,7 +1166,7 @@ certify_client(#state{client_certificate_requested = true, role = client,
session = #session{own_certificate = OwnCert}}
= State, Connection) ->
Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client),
- Connection:send_handshake(Certificate, State);
+ Connection:queue_handshake(Certificate, State);
certify_client(#state{client_certificate_requested = false} = State, _) ->
State.
@@ -1182,7 +1182,7 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret,
Version, HashSign, PrivateKey, Handshake0) of
#certificate_verify{} = Verified ->
- Connection:send_handshake(Verified, State);
+ Connection:queue_handshake(Verified, State);
ignore ->
State;
#alert{} = Alert ->
@@ -1276,7 +1276,7 @@ certify_server(#state{cert_db = CertDbHandle,
session = #session{own_certificate = OwnCert}} = State, Connection) ->
case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of
Cert = #certificate{} ->
- Connection:send_handshake(Cert, State);
+ Connection:queue_handshake(Cert, State);
Alert = #alert{} ->
throw(Alert)
end.
@@ -1303,7 +1303,7 @@ key_exchange(#state{role = server, key_algorithm = Algo,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- State = Connection:send_handshake(Msg, State0),
+ State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = DHKeys};
key_exchange(#state{role = server, private_key = Key, key_algorithm = Algo} = State, _)
@@ -1328,7 +1328,7 @@ key_exchange(#state{role = server, key_algorithm = Algo,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- State = Connection:send_handshake(Msg, State0),
+ State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = ECDHKeys};
key_exchange(#state{role = server, key_algorithm = psk,
@@ -1350,7 +1350,7 @@ key_exchange(#state{role = server, key_algorithm = psk,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- Connection:send_handshake(Msg, State0);
+ Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = server, key_algorithm = dhe_psk,
ssl_options = #ssl_options{psk_identity = PskIdentityHint},
@@ -1371,7 +1371,7 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- State = Connection:send_handshake(Msg, State0),
+ State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = DHKeys};
key_exchange(#state{role = server, key_algorithm = rsa_psk,
@@ -1393,7 +1393,7 @@ key_exchange(#state{role = server, key_algorithm = rsa_psk,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- Connection:send_handshake(Msg, State0);
+ Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = server, key_algorithm = Algo,
ssl_options = #ssl_options{user_lookup_fun = LookupFun},
@@ -1422,7 +1422,7 @@ key_exchange(#state{role = server, key_algorithm = Algo,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- State = Connection:send_handshake(Msg, State0),
+ State = Connection:queue_handshake(Msg, State0),
State#state{srp_params = SrpParams,
srp_keys = Keys};
@@ -1432,7 +1432,7 @@ key_exchange(#state{role = client,
negotiated_version = Version,
premaster_secret = PremasterSecret} = State0, Connection) ->
Msg = rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo),
- Connection:send_handshake(Msg, State0);
+ Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
key_algorithm = Algorithm,
@@ -1443,7 +1443,7 @@ key_exchange(#state{role = client,
Algorithm == dhe_rsa;
Algorithm == dh_anon ->
Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}),
- Connection:send_handshake(Msg, State0);
+ Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
key_algorithm = Algorithm,
@@ -1453,7 +1453,7 @@ key_exchange(#state{role = client,
Algorithm == ecdh_ecdsa; Algorithm == ecdh_rsa;
Algorithm == ecdh_anon ->
Msg = ssl_handshake:key_exchange(client, Version, {ecdh, Keys}),
- Connection:send_handshake(Msg, State0);
+ Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
ssl_options = SslOpts,
@@ -1461,7 +1461,7 @@ key_exchange(#state{role = client,
negotiated_version = Version} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, Version,
{psk, SslOpts#ssl_options.psk_identity}),
- Connection:send_handshake(Msg, State0);
+ Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
ssl_options = SslOpts,
@@ -1471,7 +1471,7 @@ key_exchange(#state{role = client,
Msg = ssl_handshake:key_exchange(client, Version,
{dhe_psk,
SslOpts#ssl_options.psk_identity, DhPubKey}),
- Connection:send_handshake(Msg, State0);
+ Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
ssl_options = SslOpts,
key_algorithm = rsa_psk,
@@ -1481,7 +1481,7 @@ key_exchange(#state{role = client,
= State0, Connection) ->
Msg = rsa_psk_key_exchange(Version, SslOpts#ssl_options.psk_identity,
PremasterSecret, PublicKeyInfo),
- Connection:send_handshake(Msg, State0);
+ Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
key_algorithm = Algorithm,
@@ -1492,7 +1492,7 @@ key_exchange(#state{role = client,
Algorithm == srp_rsa;
Algorithm == srp_anon ->
Msg = ssl_handshake:key_exchange(client, Version, {srp, ClientPubKey}),
- Connection:send_handshake(Msg, State0).
+ Connection:queue_handshake(Msg, State0).
rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
when Algorithm == ?rsaEncryption;
@@ -1539,7 +1539,7 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer,
HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns, Version, [Version]),
Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef,
HashSigns, Version),
- State = Connection:send_handshake(Msg, State0),
+ State = Connection:queue_handshake(Msg, State0),
State#state{client_certificate_requested = true};
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
@@ -1583,10 +1583,10 @@ next_protocol(#state{expecting_next_protocol_negotiation = false} = State, _) ->
State;
next_protocol(#state{negotiated_protocol = NextProtocol} = State0, Connection) ->
NextProtocolMessage = ssl_handshake:next_protocol(NextProtocol),
- Connection:send_handshake(NextProtocolMessage, State0).
+ Connection:queue_handshake(NextProtocolMessage, State0).
cipher_protocol(State, Connection) ->
- Connection:send_change_cipher(#change_cipher_spec{}, State).
+ Connection:queue_change_cipher(#change_cipher_spec{}, State).
finished(#state{role = Role, negotiated_version = Version,
session = Session,
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index 7682cb86ea..4b54943ddf 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -84,7 +84,12 @@
client_ecc, % {Curves, PointFmt}
tracker :: pid() | 'undefined', %% Tracker process for listen socket
sni_hostname = undefined,
- downgrade
+ downgrade,
+ flight_buffer = [] :: list() %% Buffer of TLS/DTLS records, used during the TLS handshake
+ %% to when possible pack more than on TLS record into the
+ %% underlaying packet format. Introduced by DTLS - RFC 4347.
+ %% The mecahnism is also usefull in TLS although we do not
+ %% need to worry about packet loss in TLS.
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
diff --git a/lib/ssl/src/ssl_crl_cache.erl b/lib/ssl/src/ssl_crl_cache.erl
index 60e7427737..647e0465fe 100644
--- a/lib/ssl/src/ssl_crl_cache.erl
+++ b/lib/ssl/src/ssl_crl_cache.erl
@@ -28,7 +28,7 @@
-behaviour(ssl_crl_cache_api).
--export([lookup/2, select/2, fresh_crl/2]).
+-export([lookup/3, select/2, fresh_crl/2]).
-export([insert/1, insert/2, delete/1]).
%%====================================================================
@@ -36,9 +36,10 @@
%%====================================================================
lookup(#'DistributionPoint'{distributionPoint = {fullName, Names}},
+ _Issuer,
CRLDbInfo) ->
get_crls(Names, CRLDbInfo);
-lookup(_,_) ->
+lookup(_,_,_) ->
not_available.
select(Issuer, {{_Cache, Mapping},_}) ->
diff --git a/lib/ssl/src/ssl_crl_cache_api.erl b/lib/ssl/src/ssl_crl_cache_api.erl
index d7b31f280e..c3a57e2f49 100644
--- a/lib/ssl/src/ssl_crl_cache_api.erl
+++ b/lib/ssl/src/ssl_crl_cache_api.erl
@@ -24,8 +24,9 @@
-include_lib("public_key/include/public_key.hrl").
--type db_handle() :: term().
+-type db_handle() :: term().
+-type issuer_name() :: {rdnSequence, [#'AttributeTypeAndValue'{}]}.
--callback lookup(#'DistributionPoint'{}, db_handle()) -> not_available | [public_key:der_encoded()].
--callback select(term(), db_handle()) -> [public_key:der_encoded()].
+-callback lookup(#'DistributionPoint'{}, issuer_name(), db_handle()) -> not_available | [public_key:der_encoded()].
+-callback select(issuer_name(), db_handle()) -> [public_key:der_encoded()].
-callback fresh_crl(#'DistributionPoint'{}, public_key:der_encoded()) -> public_key:der_encoded().
diff --git a/lib/ssl/src/ssl_crl_hash_dir.erl b/lib/ssl/src/ssl_crl_hash_dir.erl
new file mode 100644
index 0000000000..bb62737232
--- /dev/null
+++ b/lib/ssl/src/ssl_crl_hash_dir.erl
@@ -0,0 +1,106 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016-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%
+
+-module(ssl_crl_hash_dir).
+
+-include_lib("public_key/include/public_key.hrl").
+
+-behaviour(ssl_crl_cache_api).
+
+-export([lookup/3, select/2, fresh_crl/2]).
+
+lookup(#'DistributionPoint'{cRLIssuer = CRLIssuer} = DP, CertIssuer, CRLDbInfo) ->
+ Issuer =
+ case CRLIssuer of
+ asn1_NOVALUE ->
+ %% If the distribution point extension doesn't
+ %% indicate a CRL issuer, use the certificate issuer.
+ CertIssuer;
+ _ ->
+ CRLIssuer
+ end,
+ %% Find all CRLs for this issuer, and return those that match the
+ %% given distribution point.
+ AllCRLs = select(Issuer, CRLDbInfo),
+ lists:filter(fun(DER) ->
+ public_key:pkix_match_dist_point(DER, DP)
+ end, AllCRLs).
+
+fresh_crl(#'DistributionPoint'{}, CurrentCRL) ->
+ CurrentCRL.
+
+select(Issuer, {_DbHandle, [{dir, Dir}]}) ->
+ case find_crls(Issuer, Dir) of
+ [_|_] = DERs ->
+ DERs;
+ [] ->
+ %% That's okay, just report that we didn't find any CRL.
+ %% If the crl_check setting is best_effort, ssl_handshake
+ %% is happy with that, but if it's true, this is an error.
+ [];
+ {error, Error} ->
+ error_logger:error_report(
+ [{cannot_find_crl, Error},
+ {dir, Dir},
+ {module, ?MODULE},
+ {line, ?LINE}]),
+ []
+ end.
+
+find_crls(Issuer, Dir) ->
+ case filelib:is_dir(Dir) of
+ true ->
+ Hash = public_key:short_name_hash(Issuer),
+ find_crls(Issuer, Hash, Dir, 0, []);
+ false ->
+ {error, not_a_directory}
+ end.
+
+find_crls(Issuer, Hash, Dir, N, Acc) ->
+ Filename = filename:join(Dir, Hash ++ ".r" ++ integer_to_list(N)),
+ case file:read_file(Filename) of
+ {error, enoent} ->
+ Acc;
+ {ok, Bin} ->
+ try maybe_parse_pem(Bin) of
+ DER when is_binary(DER) ->
+ %% Found one file. Let's see if there are more.
+ find_crls(Issuer, Hash, Dir, N + 1, [DER] ++ Acc)
+ catch
+ error:Error ->
+ %% Something is wrong with the file. Report
+ %% it, and try the next one.
+ error_logger:error_report(
+ [{crl_parse_error, Error},
+ {filename, Filename},
+ {module, ?MODULE},
+ {line, ?LINE}]),
+ find_crls(Issuer, Hash, Dir, N + 1, Acc)
+ end
+ end.
+
+maybe_parse_pem(<<"-----BEGIN", _/binary>> = PEM) ->
+ %% It's a PEM encoded file. Need to extract the DER
+ %% encoded data.
+ [{'CertificateList', DER, not_encrypted}] = public_key:pem_decode(PEM),
+ DER;
+maybe_parse_pem(DER) when is_binary(DER) ->
+ %% Let's assume it's DER-encoded.
+ DER.
+
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 0787e151c0..9c3fe9d73b 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -65,7 +65,7 @@
%% Cipher suites handling
-export([available_suites/2, available_signature_algs/3, cipher_suites/2,
- select_session/11, supported_ecc/1]).
+ select_session/11, supported_ecc/1, available_signature_algs/4]).
%% Extensions handling
-export([client_hello_extensions/6,
@@ -2128,13 +2128,14 @@ crl_check_same_issuer(OtpCert, _, Dps, Options) ->
public_key:pkix_crls_validate(OtpCert, Dps, Options).
dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) ->
- case public_key:pkix_dist_points(OtpCert) of
- [] ->
- no_dps;
- DistPoints ->
- distpoints_lookup(DistPoints, Callback, CRLDbHandle)
- end;
-
+ case public_key:pkix_dist_points(OtpCert) of
+ [] ->
+ no_dps;
+ DistPoints ->
+ Issuer = OtpCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.issuer,
+ distpoints_lookup(DistPoints, Issuer, Callback, CRLDbHandle)
+ end;
+
dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) ->
DP = #'DistributionPoint'{distributionPoint = {fullName, GenNames}} =
public_key:pkix_dist_point(OtpCert),
@@ -2145,12 +2146,20 @@ dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) ->
end, GenNames),
[{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs].
-distpoints_lookup([], _, _) ->
+distpoints_lookup([], _, _, _) ->
[];
-distpoints_lookup([DistPoint | Rest], Callback, CRLDbHandle) ->
- case Callback:lookup(DistPoint, CRLDbHandle) of
+distpoints_lookup([DistPoint | Rest], Issuer, Callback, CRLDbHandle) ->
+ Result =
+ try Callback:lookup(DistPoint, Issuer, CRLDbHandle)
+ catch
+ error:undef ->
+ %% The callback module still uses the 2-argument
+ %% version of the lookup function.
+ Callback:lookup(DistPoint, CRLDbHandle)
+ end,
+ case Result of
not_available ->
- distpoints_lookup(Rest, Callback, CRLDbHandle);
+ distpoints_lookup(Rest, Issuer, Callback, CRLDbHandle);
CRLs ->
[{DistPoint, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs]
end.
@@ -2182,3 +2191,14 @@ bad_key(#'RSAPrivateKey'{}) ->
unacceptable_rsa_key;
bad_key(#'ECPrivateKey'{}) ->
unacceptable_ecdsa_key.
+
+available_signature_algs(undefined, SupportedHashSigns, _, {Major, Minor}) when
+ (Major >= 3) andalso (Minor >= 3) ->
+ SupportedHashSigns;
+available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns,
+ _, {Major, Minor}) when (Major >= 3) andalso (Minor >= 3) ->
+ sets:to_list(sets:intersection(sets:from_list(ClientHashSigns),
+ sets:from_list(SupportedHashSigns)));
+available_signature_algs(_, _, _, _) ->
+ undefined.
+
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 0a086f5eeb..5bb1c92c2d 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -72,7 +72,8 @@ init_connection_states(Role, BeastMitigation) ->
ConnectionEnd = record_protocol_role(Role),
Current = initial_connection_state(ConnectionEnd, BeastMitigation),
Pending = empty_connection_state(ConnectionEnd, BeastMitigation),
- #connection_states{current_read = Current,
+ #connection_states{dtls_write_msg_seq = 1, % only used by dtls
+ current_read = Current,
pending_read = Pending,
current_write = Current,
pending_write = Pending
@@ -320,14 +321,25 @@ encode_handshake(Frag, Version,
beast_mitigation = BeastMitigation,
security_parameters =
#security_parameters{bulk_cipher_algorithm = BCA}}} =
- ConnectionStates) ->
+ ConnectionStates)
+when is_list(Frag) ->
case iolist_size(Frag) of
N when N > ?MAX_PLAIN_TEXT_LENGTH ->
Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
encode_iolist(?HANDSHAKE, Data, Version, ConnectionStates);
_ ->
encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates)
- end.
+ end;
+%% TODO: this is a workarround for DTLS
+%%
+%% DTLS need to select the connection write state based on Epoch it wants to
+%% send this fragment in. That Epoch does not nessarily has to be the same
+%% as the current_write epoch.
+%% The right solution might be to pass the WriteState instead of the ConnectionStates,
+%% however, this will require substantion API changes.
+encode_handshake(Frag, Version, ConnectionStates) ->
+ encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
+
%%--------------------------------------------------------------------
-spec encode_alert_record(#alert{}, ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 87fde35258..a41264ff9b 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -46,6 +46,8 @@
}).
-record(connection_states, {
+ dtls_write_msg_seq, %% Only used by DTLS
+
current_read,
pending_read,
current_write,
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index eaf2dd002d..9880befa94 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -49,8 +49,9 @@
-export([next_record/1, next_event/3]).
%% Handshake handling
--export([renegotiate/2, send_handshake/2, send_change_cipher/2,
- reinit_handshake_data/1, handle_sni_extension/2]).
+-export([renegotiate/2, send_handshake/2,
+ queue_handshake/2, queue_change_cipher/2,
+ reinit_handshake_data/1, handle_sni_extension/2]).
%% Alert and close handling
-export([send_alert/2, handle_own_alert/4, handle_close_alert/3,
@@ -102,17 +103,32 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} =
Error
end.
-send_handshake(Handshake, #state{negotiated_version = Version,
- socket = Socket,
- transport_cb = Transport,
- tls_handshake_history = Hist0,
- connection_states = ConnectionStates0} = State0) ->
+send_handshake(Handshake, State) ->
+ send_handshake_flight(queue_handshake(Handshake, State)).
+
+queue_handshake(Handshake, #state{negotiated_version = Version,
+ tls_handshake_history = Hist0,
+ flight_buffer = Flight0,
+ connection_states = ConnectionStates0} = State0) ->
{BinHandshake, ConnectionStates, Hist} =
encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
- Transport:send(Socket, BinHandshake),
State0#state{connection_states = ConnectionStates,
- tls_handshake_history = Hist
- }.
+ tls_handshake_history = Hist,
+ flight_buffer = Flight0 ++ [BinHandshake]}.
+
+send_handshake_flight(#state{socket = Socket,
+ transport_cb = Transport,
+ flight_buffer = Flight} = State0) ->
+ Transport:send(Socket, Flight),
+ State0#state{flight_buffer = []}.
+
+queue_change_cipher(Msg, #state{negotiated_version = Version,
+ flight_buffer = Flight0,
+ connection_states = ConnectionStates0} = State0) ->
+ {BinChangeCipher, ConnectionStates} =
+ encode_change_cipher(Msg, Version, ConnectionStates0),
+ State0#state{connection_states = ConnectionStates,
+ flight_buffer = Flight0 ++ [BinChangeCipher]}.
send_alert(Alert, #state{negotiated_version = Version,
socket = Socket,
@@ -123,15 +139,6 @@ send_alert(Alert, #state{negotiated_version = Version,
Transport:send(Socket, BinMsg),
State0#state{connection_states = ConnectionStates}.
-send_change_cipher(Msg, #state{connection_states = ConnectionStates0,
- socket = Socket,
- negotiated_version = Version,
- transport_cb = Transport} = State0) ->
- {BinChangeCipher, ConnectionStates} =
- encode_change_cipher(Msg, Version, ConnectionStates0),
- Transport:send(Socket, BinChangeCipher),
- State0#state{connection_states = ConnectionStates}.
-
reinit_handshake_data(State) ->
%% premaster_secret, public_key_info and tls_handshake_info
%% are only needed during the handshake phase.
@@ -141,7 +148,7 @@ reinit_handshake_data(State) ->
public_key_info = undefined,
tls_handshake_history = ssl_handshake:init_handshake_history()
}.
-
+
%%====================================================================
%% tls_connection_sup API
%%====================================================================
@@ -504,7 +511,8 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, Us
allow_renegotiate = SSLOptions#ssl_options.client_renegotiation,
start_or_recv_from = undefined,
protocol_cb = ?MODULE,
- tracker = Tracker
+ tracker = Tracker,
+ flight_buffer = []
}.
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 397f963ad5..566b7db332 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -159,7 +159,8 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) ->
case tls_record:is_acceptable_version(Version, Versions) of
true ->
- AvailableHashSigns = available_signature_algs(ClientHashSigns, SupportedHashSigns, Cert, Version),
+ AvailableHashSigns = ssl_handshake:available_signature_algs(
+ ClientHashSigns, SupportedHashSigns, Cert, Version),
ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(Version)),
{Type, #session{cipher_suite = CipherSuite} = Session1}
= ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions,
@@ -284,14 +285,3 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
{Version, SessionId, ConnectionStates, ProtoExt, Protocol}
end.
-available_signature_algs(undefined, SupportedHashSigns, _, {Major, Minor}) when
- (Major >= 3) andalso (Minor >= 3) ->
- SupportedHashSigns;
-available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns,
- _, {Major, Minor}) when (Major >= 3) andalso (Minor >= 3) ->
- sets:to_list(sets:intersection(sets:from_list(ClientHashSigns),
- sets:from_list(SupportedHashSigns)));
-available_signature_algs(_, _, _, _) ->
- undefined.
-
-
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 5eebf773a7..009bcd81ad 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -172,16 +172,29 @@ revoke(Root, CA, User, C) ->
gencrl(Root, CA, C).
gencrl(Root, CA, C) ->
+ %% By default, the CRL is valid for 24 hours from now.
+ gencrl(Root, CA, C, 24).
+
+gencrl(Root, CA, C, CrlHours) ->
CACnfFile = filename:join([Root, CA, "ca.cnf"]),
CACRLFile = filename:join([Root, CA, "crl.pem"]),
Cmd = [C#config.openssl_cmd, " ca"
" -gencrl ",
- " -crlhours 24",
+ " -crlhours ", integer_to_list(CrlHours),
" -out ", CACRLFile,
" -config ", CACnfFile],
Env = [{"ROOTDIR", filename:absname(Root)}],
cmd(Cmd, Env).
+can_generate_expired_crls(C) ->
+ %% OpenSSL can generate CRLs with an expiration date in the past,
+ %% if we pass a negative number for -crlhours. However, LibreSSL
+ %% rejects this with the error "invalid argument -24: too small".
+ %% Let's check which one we have.
+ Cmd = [C#config.openssl_cmd, " ca -crlhours -24"],
+ Output = os:cmd(Cmd),
+ 0 =:= string:str(Output, "too small").
+
verify(Root, CA, User, C) ->
CAFile = filename:join([Root, User, "cacerts.pem"]),
CACRLFile = filename:join([Root, CA, "crl.pem"]),
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 93b6ae66dc..efa5faa218 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -347,10 +347,13 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
TestCase == psk_with_hint_cipher_suites;
TestCase == ciphers_rsa_signed_certs;
TestCase == ciphers_rsa_signed_certs_openssl_names;
+ TestCase == ciphers_dsa_signed_certs;
+ TestCase == ciphers_dsa_signed_certs_openssl_names;
+ TestCase == anonymous_cipher_suites;
TestCase == versions_option,
TestCase == tls_tcp_connect_big ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 30}),
+ ct:timetrap({seconds, 60}),
Config;
init_per_testcase(rizzo, Config) ->
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index aa321407b7..00636e5660 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -41,20 +41,26 @@ groups() ->
[
{check_true, [], [{group, v2_crl},
{group, v1_crl},
- {group, idp_crl}]},
+ {group, idp_crl},
+ {group, crl_hash_dir}]},
{check_peer, [], [{group, v2_crl},
{group, v1_crl},
- {group, idp_crl}]},
+ {group, idp_crl},
+ {group, crl_hash_dir}]},
{check_best_effort, [], [{group, v2_crl},
{group, v1_crl},
- {group, idp_crl}]},
+ {group, idp_crl},
+ {group, crl_hash_dir}]},
{v2_crl, [], basic_tests()},
{v1_crl, [], basic_tests()},
- {idp_crl, [], basic_tests()}].
+ {idp_crl, [], basic_tests()},
+ {crl_hash_dir, [], basic_tests() ++ crl_hash_dir_tests()}].
basic_tests() ->
[crl_verify_valid, crl_verify_revoked, crl_verify_no_crl].
+crl_hash_dir_tests() ->
+ [crl_hash_dir_collision, crl_hash_dir_expired].
init_per_suite(Config) ->
case os:find_executable("openssl") of
@@ -101,7 +107,24 @@ init_per_group(Group, Config0) ->
CertDir = filename:join(proplists:get_value(priv_dir, Config0), Group),
{CertOpts, Config} = init_certs(CertDir, Group, Config0),
{ok, _} = make_certs:all(DataDir, CertDir, CertOpts),
- [{cert_dir, CertDir}, {idp_crl, false} | Config]
+ case Group of
+ crl_hash_dir ->
+ CrlDir = filename:join(CertDir, "crls"),
+ %% Copy CRLs to their hashed filenames.
+ %% Find the hashes with 'openssl crl -noout -hash -in crl.pem'.
+ populate_crl_hash_dir(CertDir, CrlDir,
+ [{"erlangCA", "d6134ed3"},
+ {"otpCA", "d4c8d7e5"}],
+ replace),
+ CrlCacheOpts = [{crl_cache,
+ {ssl_crl_hash_dir,
+ {internal, [{dir, CrlDir}]}}}];
+ _ ->
+ CrlCacheOpts = []
+ end,
+ [{crl_cache_opts, CrlCacheOpts},
+ {cert_dir, CertDir},
+ {idp_crl, false} | Config]
end.
end_per_group(_GroupName, Config) ->
@@ -164,9 +187,10 @@ crl_verify_valid(Config) when is_list(Config) ->
{crl_cache, {ssl_crl_cache, {internal, [{http, 5000}]}}},
{verify, verify_peer}];
false ->
- [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
- {crl_check, Check},
- {verify, verify_peer}]
+ ?config(crl_cache_opts, Config) ++
+ [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}]
end,
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -196,9 +220,10 @@ crl_verify_revoked(Config) when is_list(Config) ->
{crl_check, Check},
{verify, verify_peer}];
false ->
- [{cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])},
- {crl_check, Check},
- {verify, verify_peer}]
+ ?config(crl_cache_opts, Config) ++
+ [{cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}]
end,
crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
@@ -251,6 +276,134 @@ crl_verify_no_crl(Config) when is_list(Config) ->
crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts)
end.
+crl_hash_dir_collision() ->
+ [{doc,"Verify ssl_crl_hash_dir behaviour with hash collisions"}].
+crl_hash_dir_collision(Config) when is_list(Config) ->
+ PrivDir = ?config(cert_dir, Config),
+ Check = ?config(crl_check, Config),
+
+ %% Create two CAs whose names hash to the same value
+ CA1 = "hash-collision-0000000000",
+ CA2 = "hash-collision-0258497583",
+ CertsConfig = make_certs:make_config([]),
+ make_certs:intermediateCA(PrivDir, CA1, "erlangCA", CertsConfig),
+ make_certs:intermediateCA(PrivDir, CA2, "erlangCA", CertsConfig),
+
+ make_certs:enduser(PrivDir, CA1, "collision-client-1", CertsConfig),
+ make_certs:enduser(PrivDir, CA2, "collision-client-2", CertsConfig),
+
+ [ServerOpts1, ServerOpts2] =
+ [
+ [{keyfile, filename:join([PrivDir, EndUser, "key.pem"])},
+ {certfile, filename:join([PrivDir, EndUser, "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, EndUser, "cacerts.pem"])}]
+ || EndUser <- ["collision-client-1", "collision-client-2"]],
+
+ %% Add CRLs for our new CAs into the CRL hash directory.
+ %% Find the hashes with 'openssl crl -noout -hash -in crl.pem'.
+ CrlDir = filename:join(PrivDir, "crls"),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA1, "b68fc624"},
+ {CA2, "b68fc624"}],
+ replace),
+
+ ClientOpts = ?config(crl_cache_opts, Config) ++
+ [{cacertfile, filename:join([PrivDir, "erlangCA", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% Neither certificate revoked; both succeed.
+ crl_verify_valid(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts),
+ crl_verify_valid(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts),
+
+ make_certs:revoke(PrivDir, CA1, "collision-client-1", CertsConfig),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA1, "b68fc624"},
+ {CA2, "b68fc624"}],
+ replace),
+
+ %% First certificate revoked; first fails, second succeeds.
+ crl_verify_error(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts,
+ "certificate revoked"),
+ crl_verify_valid(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts),
+
+ make_certs:revoke(PrivDir, CA2, "collision-client-2", CertsConfig),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA1, "b68fc624"},
+ {CA2, "b68fc624"}],
+ replace),
+
+ %% Second certificate revoked; both fail.
+ crl_verify_error(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts,
+ "certificate revoked"),
+ crl_verify_error(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts,
+ "certificate revoked"),
+
+ ok.
+
+crl_hash_dir_expired() ->
+ [{doc,"Verify ssl_crl_hash_dir behaviour with expired CRLs"}].
+crl_hash_dir_expired(Config) when is_list(Config) ->
+ PrivDir = ?config(cert_dir, Config),
+ Check = ?config(crl_check, Config),
+
+ CA = "CRL-maybe-expired-CA",
+ %% Add "issuing distribution point", to ensure that verification
+ %% fails if there is no valid CRL.
+ CertsConfig = make_certs:make_config([{issuing_distribution_point, true}]),
+ make_certs:can_generate_expired_crls(CertsConfig)
+ orelse throw({skip, "cannot generate CRLs with expiry date in the past"}),
+ make_certs:intermediateCA(PrivDir, CA, "erlangCA", CertsConfig),
+ EndUser = "CRL-maybe-expired",
+ make_certs:enduser(PrivDir, CA, EndUser, CertsConfig),
+
+ ServerOpts = [{keyfile, filename:join([PrivDir, EndUser, "key.pem"])},
+ {certfile, filename:join([PrivDir, EndUser, "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, EndUser, "cacerts.pem"])}],
+ ClientOpts = ?config(crl_cache_opts, Config) ++
+ [{cacertfile, filename:join([PrivDir, CA, "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}],
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% First make a CRL that expired yesterday.
+ make_certs:gencrl(PrivDir, CA, CertsConfig, -24),
+ CrlDir = filename:join(PrivDir, "crls"),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA, "1627b4b0"}],
+ replace),
+
+ %% Since the CRL has expired, it's treated as missing, and the
+ %% outcome depends on the crl_check setting.
+ case Check of
+ true ->
+ %% The error "revocation status undetermined" gets turned
+ %% into "bad certificate".
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "bad certificate");
+ peer ->
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "bad certificate");
+ best_effort ->
+ %% In "best effort" mode, we consider the certificate not
+ %% to be revoked if we can't find the appropriate CRL.
+ crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts)
+ end,
+
+ %% Now make a CRL that expires tomorrow.
+ make_certs:gencrl(PrivDir, CA, CertsConfig, 24),
+ CrlDir = filename:join(PrivDir, "crls"),
+ populate_crl_hash_dir(PrivDir, CrlDir,
+ [{CA, "1627b4b0"}],
+ add),
+
+ %% With a valid CRL, verification should always pass.
+ crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts),
+
+ ok.
+
crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts) ->
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -311,3 +464,31 @@ make_dir_path(PathComponents) ->
rename_crl(Filename) ->
file:rename(Filename, Filename ++ ".notfound").
+
+populate_crl_hash_dir(CertDir, CrlDir, CAsHashes, AddOrReplace) ->
+ ok = filelib:ensure_dir(filename:join(CrlDir, "crls")),
+ case AddOrReplace of
+ replace ->
+ %% Delete existing files, so we can override them.
+ [ok = file:delete(FileToDelete) ||
+ {_CA, Hash} <- CAsHashes,
+ FileToDelete <- filelib:wildcard(
+ filename:join(CrlDir, Hash ++ ".r*"))];
+ add ->
+ ok
+ end,
+ %% Create new files, incrementing suffix if needed to find unique names.
+ [{ok, _} =
+ file:copy(filename:join([CertDir, CA, "crl.pem"]),
+ find_free_name(CrlDir, Hash, 0))
+ || {CA, Hash} <- CAsHashes],
+ ok.
+
+find_free_name(CrlDir, Hash, N) ->
+ Name = filename:join(CrlDir, Hash ++ ".r" ++ integer_to_list(N)),
+ case filelib:is_file(Name) of
+ true ->
+ find_free_name(CrlDir, Hash, N + 1);
+ false ->
+ Name
+ end.
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index f0ce82f4fe..5ebf9bb2de 100644
--- a/lib/ssl/test/ssl_dist_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_SUITE.erl
@@ -413,7 +413,7 @@ use_interface(Config) when is_list(Config) ->
NH1,
fun() ->
[inet:sockname(P) ||
- P <- erlang:ports(),
+ P <- inet_ports(),
{ok, Port} =:= (catch inet:port(P))]
end),
%% And check that it's actually listening on localhost.
@@ -705,9 +705,11 @@ try_setting_priority(TestFun, Config) ->
get_socket_priorities() ->
[Priority ||
{ok,[{priority,Priority}]} <-
- [inet:getopts(Port, [priority]) ||
- Port <- erlang:ports(),
- element(2, erlang:port_info(Port, name)) =:= "tcp_inet"]].
+ [inet:getopts(Port, [priority]) || Port <- inet_ports()]].
+
+inet_ports() ->
+ [Port || Port <- erlang:ports(),
+ element(2, erlang:port_info(Port, name)) =:= "tcp_inet"].
%%
%% test_server side api
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index 042d57c0e2..e49d432c21 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -41,6 +41,10 @@
-define(MANY, 1000).
-define(SOME, 50).
+-define(BASE_TIMEOUT_SECONDS, 15).
+-define(SOME_SCALE, 20).
+-define(MANY_SCALE, 20).
+
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
@@ -167,7 +171,7 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(_TestCase, Config) ->
- ct:timetrap({seconds, 90}),
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS}),
Config.
@@ -182,6 +186,7 @@ packet_raw_passive_many_small() ->
[{doc,"Test packet option {packet, raw} in passive mode."}].
packet_raw_passive_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, raw}",
packet(Config, Data, send, passive_raw, ?MANY, raw, false).
@@ -191,6 +196,7 @@ packet_raw_passive_some_big() ->
[{doc,"Test packet option {packet, raw} in passive mode."}].
packet_raw_passive_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, passive_raw, ?SOME, raw, false).
%%--------------------------------------------------------------------
@@ -198,6 +204,7 @@ packet_0_passive_many_small() ->
[{doc,"Test packet option {packet, 0} in passive mode."}].
packet_0_passive_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 0}, equivalent to packet raw.",
packet(Config, Data, send, passive_raw, ?MANY, 0, false).
@@ -206,6 +213,7 @@ packet_0_passive_some_big() ->
[{doc,"Test packet option {packet, 0} in passive mode."}].
packet_0_passive_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, passive_raw, ?SOME, 0, false).
@@ -214,6 +222,7 @@ packet_1_passive_many_small() ->
[{doc,"Test packet option {packet, 1} in passive mode."}].
packet_1_passive_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 1}",
packet(Config, Data, send, passive_recv_packet, ?MANY, 1, false).
@@ -222,6 +231,7 @@ packet_1_passive_some_big() ->
[{doc,"Test packet option {packet, 1} in passive mode."}].
packet_1_passive_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(255, "1")),
packet(Config, Data, send, passive_recv_packet, ?SOME, 1, false).
@@ -230,6 +240,7 @@ packet_2_passive_many_small() ->
[{doc,"Test packet option {packet, 2} in passive mode"}].
packet_2_passive_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 2}",
packet(Config, Data, send, passive_recv_packet, ?MANY, 2, false).
@@ -238,6 +249,7 @@ packet_2_passive_some_big() ->
[{doc,"Test packet option {packet, 2} in passive mode"}].
packet_2_passive_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, passive_recv_packet, ?SOME, 2, false).
@@ -246,6 +258,7 @@ packet_4_passive_many_small() ->
[{doc,"Test packet option {packet, 4} in passive mode"}].
packet_4_passive_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 4}",
packet(Config, Data, send, passive_recv_packet, ?MANY, 4, false).
@@ -254,6 +267,7 @@ packet_4_passive_some_big() ->
[{doc,"Test packet option {packet, 4} in passive mode"}].
packet_4_passive_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, passive_recv_packet, ?SOME, 4, false).
@@ -270,6 +284,7 @@ packet_raw_active_once_some_big() ->
[{doc,"Test packet option {packet, raw} in active once mode."}].
packet_raw_active_once_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send_raw, active_once_raw, ?SOME, raw, once).
@@ -278,6 +293,7 @@ packet_0_active_once_many_small() ->
[{doc,"Test packet option {packet, 0} in active once mode."}].
packet_0_active_once_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 0}",
packet(Config, Data, send_raw, active_once_raw, ?MANY, 0, once).
@@ -286,6 +302,7 @@ packet_0_active_once_some_big() ->
[{doc,"Test packet option {packet, 0} in active once mode."}].
packet_0_active_once_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send_raw, active_once_raw, ?SOME, 0, once).
@@ -302,6 +319,7 @@ packet_1_active_once_some_big() ->
[{doc,"Test packet option {packet, 1} in active once mode."}].
packet_1_active_once_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(255, "1")),
packet(Config, Data, send, active_once_packet, ?SOME, 1, once).
@@ -311,6 +329,7 @@ packet_2_active_once_many_small() ->
[{doc,"Test packet option {packet, 2} in active once mode"}].
packet_2_active_once_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 2}",
packet(Config, Data, send, active_once_packet, ?MANY, 2, once).
@@ -319,6 +338,7 @@ packet_2_active_once_some_big() ->
[{doc,"Test packet option {packet, 2} in active once mode"}].
packet_2_active_once_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, active_once_raw, ?SOME, 2, once).
@@ -328,6 +348,7 @@ packet_4_active_once_many_small() ->
packet_4_active_once_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 4}",
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
packet(Config, Data, send, active_once_packet, ?MANY, 4, once).
%%--------------------------------------------------------------------
@@ -335,6 +356,7 @@ packet_4_active_once_some_big() ->
[{doc,"Test packet option {packet, 4} in active once mode"}].
packet_4_active_once_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, active_once_packet, ?SOME, 4, once).
@@ -343,6 +365,7 @@ packet_raw_active_many_small() ->
[{doc,"Test packet option {packet, raw} in active mode."}].
packet_raw_active_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, raw}",
packet(Config, Data, send_raw, active_raw, ?MANY, raw, true).
@@ -351,6 +374,7 @@ packet_raw_active_some_big() ->
[{doc,"Test packet option {packet, raw} in active mode."}].
packet_raw_active_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send_raw, active_raw, ?SOME, raw, true).
@@ -359,6 +383,7 @@ packet_0_active_many_small() ->
[{doc,"Test packet option {packet, 0} in active mode."}].
packet_0_active_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 0}",
packet(Config, Data, send_raw, active_raw, ?MANY, 0, true).
@@ -375,6 +400,7 @@ packet_1_active_many_small() ->
[{doc,"Test packet option {packet, 1} in active mode."}].
packet_1_active_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 1}",
packet(Config, Data, send, active_packet, ?MANY, 1, true).
@@ -383,6 +409,7 @@ packet_1_active_some_big() ->
[{doc,"Test packet option {packet, 1} in active mode."}].
packet_1_active_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(255, "1")),
packet(Config, Data, send, active_packet, ?SOME, 1, true).
@@ -391,6 +418,7 @@ packet_2_active_many_small() ->
[{doc,"Test packet option {packet, 2} in active mode"}].
packet_2_active_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 2}",
packet(Config, Data, send, active_packet, ?MANY, 2, true).
@@ -407,6 +435,7 @@ packet_4_active_many_small() ->
[{doc,"Test packet option {packet, 4} in active mode"}].
packet_4_active_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, 4}",
packet(Config, Data, send, active_packet, ?MANY, 4, true).
@@ -415,6 +444,7 @@ packet_4_active_some_big() ->
[{doc,"Test packet option {packet, 4} in active mode"}].
packet_4_active_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, active_packet, ?SOME, 4, true).
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index d9a4657a79..e1710bb2c4 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -169,7 +169,7 @@ init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs;
special_init(TestCase, Config);
init_per_testcase(TestCase, Config) ->
- ct:timetrap({seconds, 10}),
+ ct:timetrap({seconds, 20}),
special_init(TestCase, Config).
special_init(TestCase, Config)
diff --git a/otp_versions.table b/otp_versions.table
index f68986d831..af476841ab 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-18.3.4 : inets-6.2.4 ssl-7.3.3 # asn1-4.0.2 common_test-1.12.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.3 : common_test-1.12.1 inets-6.2.3 ssl-7.3.2 # asn1-4.0.2 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.2 : inets-6.2.2 ssl-7.3.1 # asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.1 : erts-7.3.1 inets-6.2.1 mnesia-4.13.4 # asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 ssl-7.3 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
diff --git a/system/doc/top/templates/index.html.src b/system/doc/top/templates/index.html.src
index ffda92f45f..2ece44b15f 100644
--- a/system/doc/top/templates/index.html.src
+++ b/system/doc/top/templates/index.html.src
@@ -36,7 +36,7 @@ limitations under the License.
<div id="leftnav">
<div class="innertube">
<img alt="Erlang logo" src="erlang-logo.png"/ >
-<!-- small><a href="glossary.html">Glossary</a> |-->
+<p/>
<small><a href="applications.html">Applications</a><br>
<a href="man_index.html">Modules</a></small>
<p/>