aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin83332 -> 83616 bytes
-rw-r--r--erts/configure.in2
-rw-r--r--erts/doc/src/erlang.xml43
-rw-r--r--erts/emulator/beam/atom.names4
-rw-r--r--erts/emulator/beam/beam_emu.c22
-rw-r--r--erts/emulator/beam/erl_bif_trace.c23
-rw-r--r--erts/emulator/beam/erl_lock_check.c5
-rw-r--r--erts/emulator/beam/erl_port_task.c13
-rw-r--r--erts/emulator/beam/erl_process.c1
-rw-r--r--erts/emulator/beam/erl_process.h1
-rw-r--r--erts/emulator/beam/erl_ptab.c2
-rw-r--r--erts/emulator/beam/erl_trace.c128
-rw-r--r--erts/emulator/beam/erl_trace.h2
-rw-r--r--erts/emulator/beam/erl_unicode.c4
-rwxr-xr-xerts/emulator/beam/global.h2
-rw-r--r--erts/emulator/beam/io.c2
-rw-r--r--erts/emulator/beam/utils.c18
-rw-r--r--erts/emulator/sys/win32/sys_time.c48
-rw-r--r--erts/emulator/test/trace_SUITE.erl69
-rw-r--r--erts/preloaded/ebin/erlang.beambin94140 -> 94124 bytes
-rw-r--r--erts/preloaded/src/erlang.erl7
-rw-r--r--lib/asn1/src/asn1ct_constructed_per.erl26
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl3
-rw-r--r--lib/asn1/src/asn1ct_imm.erl7
-rw-r--r--lib/asn1/test/asn1_SUITE.erl7
-rw-r--r--lib/asn1/test/asn1_SUITE_data/EnumExt.asn120
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn19
-rw-r--r--lib/asn1/test/asn1_SUITE_data/SeqExtension2.asn1208
-rw-r--r--lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl3
-rw-r--r--lib/asn1/test/asn1_test_lib.erl12
-rw-r--r--lib/asn1/test/testConstraints.erl31
-rw-r--r--lib/asn1/test/testEnumExt.erl26
-rw-r--r--lib/asn1/test/testSeqExtension.erl33
-rw-r--r--lib/common_test/src/ct_run.erl6
-rw-r--r--lib/common_test/src/ct_testspec.erl70
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE.erl928
-rw-r--r--lib/crypto/c_src/crypto.c72
-rw-r--r--lib/crypto/doc/src/crypto.xml113
-rw-r--r--lib/crypto/src/crypto.erl1648
-rw-r--r--lib/crypto/test/crypto_SUITE.erl132
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/et/vsn.mk2
-rw-r--r--lib/kernel/src/inet.erl41
-rw-r--r--lib/kernel/src/inet_db.erl65
-rw-r--r--lib/kernel/src/inet_hosts.erl9
-rw-r--r--lib/kernel/src/inet_res.erl6
-rw-r--r--lib/kernel/test/inet_SUITE.erl197
-rw-r--r--lib/kernel/test/inet_res_SUITE.erl198
-rw-r--r--lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf6
-rw-r--r--lib/kernel/test/inet_res_SUITE_data/otptest/root.zone6
-rw-r--r--lib/mnesia/src/mnesia_subscr.erl10
-rw-r--r--lib/mnesia/test/mnesia_evil_coverage_test.erl4
-rw-r--r--lib/mnesia/test/mnesia_isolation_test.erl10
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/public_key/doc/src/public_key.xml17
-rw-r--r--lib/public_key/src/public_key.erl3
-rw-r--r--lib/ssh/src/ssh.appup.src14
-rw-r--r--lib/ssl/src/ssl.appup.src4
-rw-r--r--lib/ssl/src/ssl.erl4
-rw-r--r--lib/ssl/src/ssl_cipher.erl12
-rw-r--r--lib/ssl/src/ssl_manager.erl1
-rw-r--r--lib/ssl/src/tls_connection.erl3
-rw-r--r--lib/ssl/src/tls_handshake.erl2
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/src/erl_lint.erl137
-rw-r--r--lib/stdlib/test/unicode_SUITE.erl147
-rw-r--r--lib/test_server/doc/src/notes.xml2
-rw-r--r--lib/test_server/src/configure.in1
-rw-r--r--lib/tools/doc/src/eprof.xml25
-rw-r--r--lib/tools/src/eprof.erl217
-rw-r--r--lib/tools/test/eprof_SUITE.erl243
-rw-r--r--lib/tools/test/eprof_SUITE_data/eprof_test.erl15
-rw-r--r--system/doc/reference_manual/introduction.xml2
73 files changed, 3712 insertions, 1457 deletions
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index ac593149b8..ee07e7636c 100644
--- a/bootstrap/lib/stdlib/ebin/erl_lint.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam
Binary files differ
diff --git a/erts/configure.in b/erts/configure.in
index c47c211c4e..b056ba44e2 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -429,7 +429,7 @@ case $host_os in
win32)
# The ethread library requires _WIN32_WINNT of at least 0x0403.
# -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS.
- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500 -DWINVER=0x0500"
+ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0501 -DWINVER=0x0501"
;;
darwin*)
CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE"
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 7dc59ea954..767edc1cc0 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -6058,6 +6058,49 @@ ok
notice.
</p>
</item>
+ <tag><c>{long_schedule, Time}</c></tag>
+ <item>
+ <p>If a process or port in the system runs uninterrupted
+ for at least <c>Time</c> wall clock milliseconds, a
+ message <c>{monitor, PidOrPort, long_schedule, Info}</c>
+ is sent to <c>MonitorPid</c>. <c>PidOrPort</c> is the
+ process or port that was running and <c>Info</c> is a
+ list of two-element tuples describing the event. In case
+ of a <c>pid()</c>, the tuples <c>{timeout, Millis}</c>,
+ <c>{in, Location}</c> and <c>{out, Location}</c> will be
+ present, where <c>Location</c> is either an MFA
+ (<c>{Module, Function, Arity}</c>) describing the
+ function where the process was scheduled in/out, or the
+ atom <c>undefined</c>. In case of a <c>port()</c>, the
+ tuples <c>{timeout, Millis}</c> and <c>{port_op,Op}</c>
+ will be present. <c>Op</c> will be one of <c>proc_sig</c>,
+ <c>timeout</c>, <c>input</c>, <c>output</c>,
+ <c>event</c> or <c>dist_cmd</c>, depending on which
+ driver callback was executing. <c>proc_sig</c> is an
+ internal operation and should never appear, while the
+ others represent the corresponding driver callbacks
+ <c>timeout</c>, <c>ready_input</c>, <c>ready_output</c>,
+ <c>event</c> and finally <c>outputv</c> (when the port
+ is used by distribution). The <c>Millis</c> value in
+ the <c>timeout</c> tuple will tell you the actual
+ uninterrupted execution time of the process or port,
+ which will always be <c>&gt;=</c> the <c>Time</c> value
+ supplied when starting the trace. New tuples may be
+ added to the <c>Info</c> list in the future, and the
+ order of the tuples in the list may be changed at any
+ time without prior notice.
+ </p>
+ <p>This can be used to detect problems with NIF's or
+ drivers that take too long to execute. Generally, 1 ms
+ is considered a good maximum time for a driver callback
+ or a NIF. However, a time sharing system should usually
+ consider everything below 100 ms as "possible" and
+ fairly "normal". Schedule times above that might however
+ indicate swapping or a NIF/driver that is
+ misbehaving. Misbehaving NIF's and drivers could cause
+ bad resource utilization and bad overall performance of
+ the system.</p>
+ </item>
<tag><c>{large_heap, Size}</c></tag>
<item>
<p>If a garbage collection in the system results in
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 7d86e486f1..3ee9eb0f88 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -178,6 +178,7 @@ atom disable_trace
atom disabled
atom display_items
atom dist
+atom dist_cmd
atom Div='/'
atom div
atom dlink
@@ -313,6 +314,7 @@ atom load_cancelled
atom load_failure
atom local
atom long_gc
+atom long_schedule
atom low
atom Lt='<'
atom machine
@@ -432,6 +434,7 @@ atom port
atom ports
atom port_count
atom port_limit
+atom port_op
atom print
atom priority
atom private
@@ -443,6 +446,7 @@ atom process_display
atom process_limit
atom process_dump
atom procs
+atom proc_sig
atom profile
atom protected
atom protection
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 944ed6da81..5781009f58 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -924,6 +924,7 @@ extern int count_instructions;
# define NOINLINE
#endif
+
/*
* The following functions are called directly by process_main().
* Don't inline them.
@@ -1153,6 +1154,9 @@ void process_main(void)
Eterm pt_arity; /* Used by do_put_tuple */
+ Uint64 start_time = 0; /* Monitor long schedule */
+ BeamInstr* start_time_i = NULL;
+
ERL_BITS_DECLARE_STATEP; /* Has to be last declaration */
@@ -1175,6 +1179,16 @@ void process_main(void)
do_schedule:
reds_used = REDS_IN(c_p) - FCALLS;
do_schedule1:
+
+ if (start_time != 0) {
+ Sint64 diff = erts_timestamp_millis() - start_time;
+ if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule) {
+ BeamInstr *inptr = find_function_from_pc(start_time_i);
+ BeamInstr *outptr = find_function_from_pc(c_p->i);
+ monitor_long_schedule_proc(c_p,inptr,outptr,(Uint) diff);
+ }
+ }
+
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
#if HALFWORD_HEAP
@@ -1183,11 +1197,18 @@ void process_main(void)
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
c_p = schedule(c_p, reds_used);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+ start_time = 0;
#ifdef DEBUG
pid = c_p->common.id; /* Save for debugging purpouses */
#endif
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
+
+ if (erts_system_monitor_long_schedule != 0) {
+ start_time = erts_timestamp_millis();
+ start_time_i = c_p->i;
+ }
+
reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array;
freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array;
#if !HEAP_ON_C_STACK
@@ -6151,6 +6172,7 @@ apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg)
}
+
static Eterm
new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free)
{
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 559cb3efa1..06fbbea123 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -2012,6 +2012,7 @@ void erts_system_monitor_clear(Process *c_p) {
#endif
erts_set_system_monitor(NIL);
erts_system_monitor_long_gc = 0;
+ erts_system_monitor_long_schedule = 0;
erts_system_monitor_large_heap = 0;
erts_system_monitor_flags.busy_port = 0;
erts_system_monitor_flags.busy_dist_port = 0;
@@ -2036,12 +2037,17 @@ static Eterm system_monitor_get(Process *p)
Uint hsz = 3 + (erts_system_monitor_flags.busy_dist_port ? 2 : 0) +
(erts_system_monitor_flags.busy_port ? 2 : 0);
Eterm long_gc = NIL;
+ Eterm long_schedule = NIL;
Eterm large_heap = NIL;
if (erts_system_monitor_long_gc != 0) {
hsz += 2+3;
(void) erts_bld_uint(NULL, &hsz, erts_system_monitor_long_gc);
}
+ if (erts_system_monitor_long_schedule != 0) {
+ hsz += 2+3;
+ (void) erts_bld_uint(NULL, &hsz, erts_system_monitor_long_schedule);
+ }
if (erts_system_monitor_large_heap != 0) {
hsz += 2+3;
(void) erts_bld_uint(NULL, &hsz, erts_system_monitor_large_heap);
@@ -2051,6 +2057,10 @@ static Eterm system_monitor_get(Process *p)
if (erts_system_monitor_long_gc != 0) {
long_gc = erts_bld_uint(&hp, NULL, erts_system_monitor_long_gc);
}
+ if (erts_system_monitor_long_schedule != 0) {
+ long_schedule = erts_bld_uint(&hp, NULL,
+ erts_system_monitor_long_schedule);
+ }
if (erts_system_monitor_large_heap != 0) {
large_heap = erts_bld_uint(&hp, NULL, erts_system_monitor_large_heap);
}
@@ -2059,6 +2069,10 @@ static Eterm system_monitor_get(Process *p)
Eterm t = TUPLE2(hp, am_long_gc, long_gc); hp += 3;
res = CONS(hp, t, res); hp += 2;
}
+ if (long_schedule != NIL) {
+ Eterm t = TUPLE2(hp, am_long_schedule, long_schedule); hp += 3;
+ res = CONS(hp, t, res); hp += 2;
+ }
if (large_heap != NIL) {
Eterm t = TUPLE2(hp, am_large_heap, large_heap); hp += 3;
res = CONS(hp, t, res); hp += 2;
@@ -2113,7 +2127,7 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list)
}
if (is_not_list(list)) goto error;
else {
- Uint long_gc, large_heap;
+ Uint long_gc, long_schedule, large_heap;
int busy_port, busy_dist_port;
system_blocked = 1;
@@ -2123,7 +2137,8 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list)
if (!erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, monitor_pid, 0))
goto error;
- for (long_gc = 0, large_heap = 0, busy_port = 0, busy_dist_port = 0;
+ for (long_gc = 0, long_schedule = 0, large_heap = 0,
+ busy_port = 0, busy_dist_port = 0;
is_list(list);
list = CDR(list_val(list))) {
Eterm t = CAR(list_val(list));
@@ -2133,6 +2148,9 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list)
if (tp[1] == am_long_gc) {
if (! term_to_Uint(tp[2], &long_gc)) goto error;
if (long_gc < 1) long_gc = 1;
+ } else if (tp[1] == am_long_schedule) {
+ if (! term_to_Uint(tp[2], &long_schedule)) goto error;
+ if (long_schedule < 1) long_schedule = 1;
} else if (tp[1] == am_large_heap) {
if (! term_to_Uint(tp[2], &large_heap)) goto error;
if (large_heap < 16384) large_heap = 16384;
@@ -2148,6 +2166,7 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list)
prev = system_monitor_get(p);
erts_set_system_monitor(monitor_pid);
erts_system_monitor_long_gc = long_gc;
+ erts_system_monitor_long_schedule = long_schedule;
erts_system_monitor_large_heap = large_heap;
erts_system_monitor_flags.busy_port = !!busy_port;
erts_system_monitor_flags.busy_dist_port = !!busy_dist_port;
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index bc59fe55d4..2ac5e24d3a 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -180,6 +180,11 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "efile_drv dtrace mutex", NULL },
#endif
{ "mtrace_buf", NULL },
+#ifdef __WIN32__
+#ifdef ERTS_SMP
+ { "sys_gethrtime", NULL },
+#endif
+#endif
{ "erts_alloc_hard_debug", NULL }
};
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index f753de8f52..7d53ce7152 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -1594,6 +1594,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
int fpe_was_unmasked;
erts_aint32_t state;
int active;
+ Uint64 start_time = 0;
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
@@ -1655,6 +1656,10 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
reset_handle(ptp);
+ if (erts_system_monitor_long_schedule != 0) {
+ start_time = erts_timestamp_millis();
+ }
+
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
ERTS_SMP_CHK_NO_PROC_LOCKS;
ASSERT(pp->drv_ptr);
@@ -1723,6 +1728,14 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
reds += erts_port_driver_callback_epilogue(pp, &state);
+ if (start_time != 0) {
+ Sint64 diff = erts_timestamp_millis() - start_time;
+ if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule) {
+ monitor_long_schedule_port(pp,ptp->type,(Uint) diff);
+ }
+ }
+ start_time = 0;
+
aborted_port_task:
schedule_port_task_free(ptp);
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 88eb224f84..3d161f2aa0 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -267,6 +267,7 @@ static Uint last_exact_reductions;
Uint erts_default_process_flags;
Eterm erts_system_monitor;
Eterm erts_system_monitor_long_gc;
+Uint erts_system_monitor_long_schedule;
Eterm erts_system_monitor_large_heap;
struct erts_system_monitor_flags_t erts_system_monitor_flags;
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 865ac6c43f..3c1edfad7a 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1009,6 +1009,7 @@ extern erts_smp_rwmtx_t erts_cpu_bind_rwmtx;
*/
extern Eterm erts_system_monitor;
extern Uint erts_system_monitor_long_gc;
+extern Uint erts_system_monitor_long_schedule;
extern Uint erts_system_monitor_large_heap;
struct erts_system_monitor_flags_t {
unsigned int busy_port : 1;
diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c
index 8da135b2c8..f7b5f25eac 100644
--- a/erts/emulator/beam/erl_ptab.c
+++ b/erts/emulator/beam/erl_ptab.c
@@ -1435,7 +1435,7 @@ erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next)
aid_ix = max_ix;
else
aid_ix--;
- ASSERT((aid_ix & max_ix) == (((Uint32) erts_atomic32_read_nob(&ptab->vola.tile.fid_ix)) & max_ix));
+ ASSERT((aid_ix & max_ix) == (((Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.fid_ix)) & max_ix));
#endif
}
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 848877d43e..bb6ed44523 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -2268,7 +2268,134 @@ trace_gc(Process *p, Eterm what)
#undef LOCAL_HEAP_SIZE
}
+void
+monitor_long_schedule_proc(Process *p, BeamInstr *in_fp, BeamInstr *out_fp, Uint time)
+{
+ ErlHeapFragment *bp;
+ ErlOffHeap *off_heap;
+#ifndef ERTS_SMP
+ Process *monitor_p;
+#endif
+ Uint hsz;
+ Eterm *hp, list, in_mfa = am_undefined, out_mfa = am_undefined;
+ Eterm in_tpl, out_tpl, tmo_tpl, tmo, msg;
+
+
+#ifndef ERTS_SMP
+ ASSERT(is_internal_pid(system_monitor));
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (!monitor_p || p == monitor_p) {
+ return;
+ }
+#endif
+ /*
+ * Size: {monitor, pid, long_schedule, [{timeout, T}, {in, {M,F,A}},{out,{M,F,A}}]} ->
+ * 5 (top tuple of 4), (3 (elements) * 2 (cons)) + 3 (timeout tuple of 2) + size of Timeout +
+ * (2 * 3 (in/out tuple of 2)) +
+ * 0 (unknown) or 4 (MFA tuple of 3) + 0 (unknown) or 4 (MFA tuple of 3)
+ * = 20 + (in_fp != NULL) ? 4 : 0 + (out_fp != NULL) ? 4 : 0 + size of Timeout
+ */
+ hsz = 20 + ((in_fp != NULL) ? 4 : 0) + ((out_fp != NULL) ? 4 : 0);
+ (void) erts_bld_uint(NULL, &hsz, time);
+ hp = ERTS_ALLOC_SYSMSG_HEAP(hsz, &bp, &off_heap, monitor_p);
+ tmo = erts_bld_uint(&hp, NULL, time);
+ if (in_fp != NULL) {
+ in_mfa = TUPLE3(hp,(Eterm) in_fp[0], (Eterm) in_fp[1], make_small(in_fp[2]));
+ hp +=4;
+ }
+ if (out_fp != NULL) {
+ out_mfa = TUPLE3(hp,(Eterm) out_fp[0], (Eterm) out_fp[1], make_small(out_fp[2]));
+ hp +=4;
+ }
+ tmo_tpl = TUPLE2(hp,am_timeout, tmo);
+ hp += 3;
+ in_tpl = TUPLE2(hp,am_in,in_mfa);
+ hp += 3;
+ out_tpl = TUPLE2(hp,am_out,out_mfa);
+ hp += 3;
+ list = CONS(hp,out_tpl,NIL);
+ hp += 2;
+ list = CONS(hp,in_tpl,list);
+ hp += 2;
+ list = CONS(hp,tmo_tpl,list);
+ hp += 2;
+ msg = TUPLE4(hp, am_monitor, p->common.id, am_long_schedule, list);
+ hp += 5;
+#ifdef ERTS_SMP
+ enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp);
+#else
+ erts_queue_message(monitor_p, NULL, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
+#endif
+}
+void
+monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time)
+{
+ ErlHeapFragment *bp;
+ ErlOffHeap *off_heap;
+#ifndef ERTS_SMP
+ Process *monitor_p;
+#endif
+ Uint hsz;
+ Eterm *hp, list, op;
+ Eterm op_tpl, tmo_tpl, tmo, msg;
+
+
+#ifndef ERTS_SMP
+ ASSERT(is_internal_pid(system_monitor));
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (!monitor_p) {
+ return;
+ }
+#endif
+ /*
+ * Size: {monitor, port, long_schedule, [{timeout, T}, {op, Operation}]} ->
+ * 5 (top tuple of 4), (2 (elements) * 2 (cons)) + 3 (timeout tuple of 2)
+ * + size of Timeout + 3 (op tuple of 2 atoms)
+ * = 15 + size of Timeout
+ */
+ hsz = 15;
+ (void) erts_bld_uint(NULL, &hsz, time);
+
+ hp = ERTS_ALLOC_SYSMSG_HEAP(hsz, &bp, &off_heap, monitor_p);
+ switch (type) {
+ case ERTS_PORT_TASK_PROC_SIG: op = am_proc_sig; break;
+ case ERTS_PORT_TASK_TIMEOUT: op = am_timeout; break;
+ case ERTS_PORT_TASK_INPUT: op = am_input; break;
+ case ERTS_PORT_TASK_OUTPUT: op = am_output; break;
+ case ERTS_PORT_TASK_EVENT: op = am_event; break;
+ case ERTS_PORT_TASK_DIST_CMD: op = am_dist_cmd; break;
+ default: op = am_undefined; break;
+ }
+
+ tmo = erts_bld_uint(&hp, NULL, time);
+
+ op_tpl = TUPLE2(hp,am_port_op,op);
+ hp += 3;
+
+ tmo_tpl = TUPLE2(hp,am_timeout, tmo);
+ hp += 3;
+
+ list = CONS(hp,op_tpl,NIL);
+ hp += 2;
+ list = CONS(hp,tmo_tpl,list);
+ hp += 2;
+ msg = TUPLE4(hp, am_monitor, pp->common.id, am_long_schedule, list);
+ hp += 5;
+#ifdef ERTS_SMP
+ enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, pp->common.id, NIL, msg, bp);
+#else
+ erts_queue_message(monitor_p, NULL, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
+#endif
+}
void
monitor_long_gc(Process *p, Uint time) {
@@ -3011,6 +3138,7 @@ sys_msg_disp_failure(ErtsSysMsgQ *smqp, Eterm receiver)
case SYS_MSG_TYPE_SYSMON:
if (receiver == NIL
&& !erts_system_monitor_long_gc
+ && !erts_system_monitor_long_schedule
&& !erts_system_monitor_large_heap
&& !erts_system_monitor_flags.busy_port
&& !erts_system_monitor_flags.busy_dist_port)
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
index 50fb27aab0..54d3aafdda 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -83,6 +83,8 @@ void erts_system_profile_setup_active_schedulers(void);
/* system_monitor */
void monitor_long_gc(Process *p, Uint time);
+void monitor_long_schedule_proc(Process *p, BeamInstr *in_i, BeamInstr *out_i, Uint time);
+void monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time);
void monitor_large_heap(Process *p);
void monitor_generic(Process *p, Eterm type, Eterm spec);
Uint erts_trace_flag2bit(Eterm flag);
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index ad6f8b993a..e00440b905 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -1723,14 +1723,14 @@ static BIF_RETTYPE do_bif_utf8_to_list(Process *p,
if (b_sz) {
ErlSubBin *sb;
Eterm orig;
- ERTS_DECLARE_DUMMY(Uint offset);
+ Uint offset;
ASSERT(state != ERTS_UTF8_OK);
hp = HAlloc(p, ERL_SUB_BIN_SIZE);
sb = (ErlSubBin *) hp;
ERTS_GET_REAL_BIN(orig_bin, orig, offset, bitoffs, bitsize);
sb->thing_word = HEADER_SUB_BIN;
sb->size = b_sz;
- sb->offs = num_bytes_to_process + num_processed_bytes;
+ sb->offs = offset + num_bytes_to_process + num_processed_bytes;
sb->orig = orig;
sb->bitoffs = bitoffs;
sb->bitsize = bitsize;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index d5e727bcba..12eb3bfb7c 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -740,6 +740,8 @@ void erl_drv_thr_init(void);
/* utils.c */
void erts_cleanup_offheap(ErlOffHeap *offheap);
+Uint64 erts_timestamp_millis(void);
+
Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex);
Eterm store_external_or_ref_in_proc_(Process *, Eterm);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 01e130bd64..c1e66b59af 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -4842,7 +4842,7 @@ erts_stale_drv_select(Eterm port,
if (drv_port == ERTS_INVALID_ERL_DRV_PORT) {
Port *prt = erts_port_lookup_raw(port);
if (prt)
- drv_port = ERTS_Port2ErlDrvPort(port);
+ drv_port = ERTS_Port2ErlDrvPort(prt);
else
drv_port = ERTS_INVALID_ERL_DRV_PORT;
}
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index d5d97d748a..0a833f7e66 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -3722,6 +3722,24 @@ erts_smp_ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic)
}
+/*
+ * A millisecond timestamp without time correction where there's no hrtime
+ * - for tracing on "long" things...
+ */
+Uint64 erts_timestamp_millis(void)
+{
+#ifdef HAVE_GETHRTIME
+ return (Uint64) (sys_gethrtime() / 1000000);
+#else
+ Uint64 res;
+ SysTimeval tv;
+ sys_gettimeofday(&tv);
+ res = (Uint64) tv.tv_sec*1000000;
+ res += (Uint64) tv.tv_usec;
+ return (res / 1000);
+#endif
+}
+
#ifdef DEBUG
/*
* Handy functions when using a debugger - don't use in the code!
diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c
index 2f2dfc8197..f7f0161b58 100644
--- a/erts/emulator/sys/win32/sys_time.c
+++ b/erts/emulator/sys/win32/sys_time.c
@@ -63,6 +63,8 @@
static SysHrTime wrap = 0;
static DWORD last_tick_count = 0;
+static erts_smp_mtx_t wrap_lock;
+static ULONGLONG (WINAPI *pGetTickCount64)(void) = NULL;
/* Getting timezone information is a heavy operation, so we want to do this
only once */
@@ -77,11 +79,23 @@ static int days_in_month[2][13] = {
int
sys_init_time(void)
{
+ char kernel_dll_name[] = "kernel32";
+ HMODULE module;
+
+ module = GetModuleHandle(kernel_dll_name);
+ pGetTickCount64 = (module != NULL) ?
+ (ULONGLONG (WINAPI *)(void))
+ GetProcAddress(module,"GetTickCount64") :
+ NULL;
+
if(GetTimeZoneInformation(&static_tzi) &&
static_tzi.StandardDate.wMonth != 0 &&
static_tzi.DaylightDate.wMonth != 0) {
have_static_tzi = 1;
}
+
+ erts_smp_mtx_init(&wrap_lock, "sys_gethrtime");
+
return 1;
}
@@ -363,15 +377,39 @@ sys_gettimeofday(SysTimeval *tv)
EPOCH_JULIAN_DIFF);
}
+extern int erts_initialized;
SysHrTime
sys_gethrtime(void)
{
- DWORD ticks = (SysHrTime) (GetTickCount() & 0x7FFFFFFF);
- if (ticks < (SysHrTime) last_tick_count) {
- wrap += LL_LITERAL(1) << 31;
+ if (pGetTickCount64 != NULL) {
+ return ((SysHrTime) pGetTickCount64()) * LL_LITERAL(1000000);
+ } else {
+ DWORD ticks;
+ SysHrTime res;
+ erts_smp_mtx_lock(&wrap_lock);
+ ticks = (SysHrTime) (GetTickCount() & 0x7FFFFFFF);
+ if (ticks < (SysHrTime) last_tick_count) {
+ /* Detect a race that should no longer be here... */
+ if ((((SysHrTime) last_tick_count) - ((SysHrTime) ticks)) > 1000) {
+ wrap += LL_LITERAL(1) << 31;
+ } else {
+ /*
+ * XXX Debug: Violates locking order, remove all this,
+ * after testing!
+ */
+ erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
+ erts_dsprintf(dsbufp, "Did not wrap when last_tick %d "
+ "and tick %d",
+ last_tick_count, ticks);
+ erts_send_error_to_logger_nogl(dsbufp);
+ ticks = last_tick_count;
+ }
+ }
+ last_tick_count = ticks;
+ res = ((((LONGLONG) ticks) + wrap) * LL_LITERAL(1000000));
+ erts_smp_mtx_unlock(&wrap_lock);
+ return res;
}
- last_tick_count = ticks;
- return ((((LONGLONG) ticks) + wrap) * LL_LITERAL(1000000));
}
clock_t
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index 221b65309a..caa58ae281 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -34,6 +34,7 @@
system_monitor_args/1, more_system_monitor_args/1,
system_monitor_long_gc_1/1, system_monitor_long_gc_2/1,
system_monitor_large_heap_1/1, system_monitor_large_heap_2/1,
+ system_monitor_long_schedule/1,
bad_flag/1, trace_delivered/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -52,6 +53,7 @@ all() ->
set_on_first_spawn, system_monitor_args,
more_system_monitor_args, system_monitor_long_gc_1,
system_monitor_long_gc_2, system_monitor_large_heap_1,
+ system_monitor_long_schedule,
system_monitor_large_heap_2, bad_flag, trace_delivered].
groups() ->
@@ -508,6 +510,65 @@ try_l(Val) ->
?line {Self,Comb1} = erlang:system_monitor(undefined),
?line [{large_heap,Val},{long_gc,Arbitrary2}] = lists:sort(Comb1).
+monitor_sys(Parent) ->
+ receive
+ {monitor,Pid,long_schedule,Data} when is_pid(Pid) ->
+ io:format("Long schedule of ~w: ~w~n",[Pid,Data]),
+ Parent ! {Pid,Data},
+ monitor_sys(Parent);
+ {monitor,Port,long_schedule,Data} when is_port(Port) ->
+ {name,Name} = erlang:port_info(Port,name),
+ io:format("Long schedule of ~w (~p): ~w~n",[Port,Name,Data]),
+ Parent ! {Port,Data},
+ monitor_sys(Parent);
+ Other ->
+ erlang:display(Other)
+ end.
+
+start_monitor() ->
+ Parent = self(),
+ Mpid = spawn_link(fun() -> monitor_sys(Parent) end),
+ erlang:system_monitor(Mpid,[{long_schedule,100}]),
+ erlang:yield(), % Need to be rescheduled for the trace to take
+ ok.
+
+system_monitor_long_schedule(suite) ->
+ [];
+system_monitor_long_schedule(doc) ->
+ ["Tests erlang:system_monitor(Pid, [{long_schedule,Time}])"];
+system_monitor_long_schedule(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ erl_ddll:start(),
+ case (catch load_driver(Path, slow_drv)) of
+ ok ->
+ do_system_monitor_long_schedule();
+ _Error ->
+ {skip, "Unable to load slow_drv (windows or no usleep()?)"}
+ end.
+do_system_monitor_long_schedule() ->
+ start_monitor(),
+ Port = open_port({spawn_driver,slow_drv}, []),
+ "ok" = erlang:port_control(Port,0,[]),
+ Self = self(),
+ receive
+ {Self,L} when is_list(L) ->
+ ok
+ after 1000 ->
+ ?t:fail(no_trace_of_pid)
+ end,
+ "ok" = erlang:port_control(Port,1,[]),
+ "ok" = erlang:port_control(Port,2,[]),
+ receive
+ {Port,LL} when is_list(LL) ->
+ ok
+ after 1000 ->
+ ?t:fail(no_trace_of_port)
+ end,
+ port_close(Port),
+ erlang:system_monitor(undefined),
+ ok.
+
+
-define(LONG_GC_SLEEP, 670).
system_monitor_long_gc_1(suite) ->
@@ -1521,3 +1582,11 @@ issue_non_empty_runq_warning(DeadLine, RQLen) ->
" Processes info: ~p~n",
[DeadLine div 1000, RQLen, self(), PIs]),
receive after 1000 -> ok end.
+
+load_driver(Dir, Driver) ->
+ case erl_ddll:load_driver(Dir, Driver) of
+ ok -> ok;
+ {error, Error} = Res ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ Res
+ end.
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 947fbe7c24..cdc13a656d 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index a8a4425b63..e016a50c4c 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -188,6 +188,7 @@
'busy_port' |
'busy_dist_port' |
{'long_gc', non_neg_integer()} |
+ {'long_schedule', non_neg_integer()} |
{'large_heap', non_neg_integer()}.
@@ -3553,10 +3554,8 @@ sched_wall_time(Ref, N, Acc) ->
{Ref, SWT} -> sched_wall_time(Ref, N-1, [SWT|Acc])
end.
--spec erlang:gather_gc_info_result(Ref) -> [{pos_integer(),
- pos_integer(),
- 0}] when
- Ref :: reference().
+-spec erlang:gather_gc_info_result(Ref) ->
+ {number(),number(),0} when Ref :: reference().
gather_gc_info_result(Ref) when erlang:is_reference(Ref) ->
gc_info(Ref, erlang:system_info(schedulers), {0,0}).
diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl
index efb55cf015..d279e9697f 100644
--- a/lib/asn1/src/asn1ct_constructed_per.erl
+++ b/lib/asn1/src/asn1ct_constructed_per.erl
@@ -354,8 +354,7 @@ gen_dec_constructed_imm_2(Typename, CompList,
%% we don't return named lists any more Cnames = mkcnamelist(CompList),
demit({"Result = "}), %dbg
%% return value as record
- RecordName = lists:concat([get_record_name_prefix(),
- asn1ct_gen:list2rname(Typename)]),
+ RecordName = record_name(Typename),
case Typename of
['EXTERNAL'] ->
emit({" OldFormat={'",RecordName,
@@ -377,6 +376,29 @@ gen_dec_constructed_imm_2(Typename, CompList,
end,
emit({{curr,bytes},"}"}).
+%% record_name([TypeName]) -> RecordNameString
+%% Construct a record name for the constructed type, ignoring any
+%% fake sequences that are used to represent an extension addition
+%% group. Such fake sequences never appear as a top type, and their
+%% name always start with "ExtAddGroup".
+
+record_name(Typename0) ->
+ [TopType|Typename1] = lists:reverse(Typename0),
+ Typename = filter_ext_add_groups(Typename1, [TopType]),
+ lists:concat([get_record_name_prefix(),
+ asn1ct_gen:list2rname(Typename)]).
+
+filter_ext_add_groups([H|T], Acc) when is_atom(H) ->
+ case atom_to_list(H) of
+ "ExtAddGroup"++_ ->
+ filter_ext_add_groups(T, Acc);
+ _ ->
+ filter_ext_add_groups(T, [H|Acc])
+ end;
+filter_ext_add_groups([H|T], Acc) ->
+ filter_ext_add_groups(T, [H|Acc]);
+filter_ext_add_groups([], Acc) -> Acc.
+
textual_order([#'ComponentType'{textual_order=undefined}|_],TermList) ->
TermList;
textual_order(CompList,TermList) when is_list(CompList) ->
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 30c9ab9365..69d9d51bf1 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -250,7 +250,8 @@ emit_enc_enumerated_case(Erules, C, EnumName, Count) ->
enc_ext_and_val(per, E, F, Args) ->
[E|apply(asn1ct_eval_per, F, Args)];
enc_ext_and_val(uper, E, F, Args) ->
- <<E:1,(apply(asn1ct_eval_uper, F, Args))/bitstring>>.
+ Bs = list_to_bitstring([apply(asn1ct_eval_uper, F, Args)]),
+ <<E:1,Bs/bitstring>>.
%% Object code generating for encoding and decoding
diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl
index c6803a0f96..bf362db843 100644
--- a/lib/asn1/src/asn1ct_imm.erl
+++ b/lib/asn1/src/asn1ct_imm.erl
@@ -86,7 +86,7 @@ per_dec_enumerated(BaseNamedList, NamedListExt0, Aligned) ->
bit_case(Base, Ext).
per_dec_extension_map(Aligned) ->
- Len = {add,per_dec_normally_small_number(Aligned),1},
+ Len = per_dec_normally_small_length(Aligned),
{get_bits,Len,[1,bitstring]}.
per_dec_integer(Constraint0, Aligned) ->
@@ -240,6 +240,11 @@ per_dec_normally_small_number(Aligned) ->
Unlimited = per_decode_semi_constrained(0, Aligned),
bit_case(Small, Unlimited).
+per_dec_normally_small_length(Aligned) ->
+ Small = {add,{get_bits,6,[1]},1},
+ Unlimited = decode_unconstrained_length(false, Aligned),
+ bit_case(Small, Unlimited).
+
per_decode_semi_constrained(Lb, Aligned) ->
add_lb(Lb, {get_bits,decode_unconstrained_length(false, Aligned),[8]}).
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 6be493320c..f00b23a8b2 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -454,10 +454,13 @@ testSeqDefault(Config, Rule, Opts) ->
testSeqExtension(Config) -> test(Config, fun testSeqExtension/3).
testSeqExtension(Config, Rule, Opts) ->
- asn1_test_lib:compile_all(["External", "SeqExtension"], Config,
+ asn1_test_lib:compile_all(["External",
+ "SeqExtension",
+ "SeqExtension2"],
+ Config,
[Rule|Opts]),
DataDir = ?config(data_dir, Config),
- testSeqExtension:main(DataDir, [Rule|Opts]).
+ testSeqExtension:main(Rule, DataDir, [Rule|Opts]).
testSeqExternal(Config) -> test(Config, fun testSeqExternal/3).
testSeqExternal(Config, Rule, Opts) ->
diff --git a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1
index 3a727e46bb..8dc5f3d7e1 100644
--- a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1
@@ -31,5 +31,25 @@ Seq ::= SEQUENCE {
i INTEGER
}
+EnumExtBig ::= ENUMERATED {
+ base,
+ ...,
+ e00,e01,e02,e03,e04,e05,e06,e07,e08,e09,e0a,e0b,e0c,e0d,e0e,e0f,
+ e10,e11,e12,e13,e14,e15,e16,e17,e18,e19,e1a,e1b,e1c,e1d,e1e,e1f,
+ e20,e21,e22,e23,e24,e25,e26,e27,e28,e29,e2a,e2b,e2c,e2d,e2e,e2f,
+ e30,e31,e32,e33,e34,e35,e36,e37,e38,e39,e3a,e3b,e3c,e3d,e3e,e3f,
+ e40,e41,e42,e43,e44,e45,e46,e47,e48,e49,e4a,e4b,e4c,e4d,e4e,e4f,
+ e50,e51,e52,e53,e54,e55,e56,e57,e58,e59,e5a,e5b,e5c,e5d,e5e,e5f,
+ e60,e61,e62,e63,e64,e65,e66,e67,e68,e69,e6a,e6b,e6c,e6d,e6e,e6f,
+ e70,e71,e72,e73,e74,e75,e76,e77,e78,e79,e7a,e7b,e7c,e7d,e7e,e7f,
+ e80
+}
+
+SeqBig ::= SEQUENCE {
+ b BOOLEAN,
+ e EnumExtBig,
+ i INTEGER
+}
+
END
diff --git a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
index b07dcd8baa..0e905d8839 100644
--- a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
+++ b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
@@ -118,4 +118,23 @@ AC-BarringConfig ::= SEQUENCE {
ac-BarringForSpecialAC BIT STRING (SIZE(5))
}
+InlinedSeq ::= SEQUENCE {
+ ...,
+ [[
+ s SEQUENCE {
+ a INTEGER,
+ b BOOLEAN
+ }
+ ]]
+}
+
+-- 'ExtAddGroup1' is used internally to represent fake sequences for
+-- extension addition groups. Make sure that a real sequence with that
+-- name at the top-level doesn't cause a problem.
+
+ExtAddGroup1 ::= SEQUENCE {
+ x INTEGER,
+ y INTEGER
+}
+
END
diff --git a/lib/asn1/test/asn1_SUITE_data/SeqExtension2.asn1 b/lib/asn1/test/asn1_SUITE_data/SeqExtension2.asn1
new file mode 100644
index 0000000000..44900d9d39
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/SeqExtension2.asn1
@@ -0,0 +1,208 @@
+SeqExtension2 DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+
+SeqExt66 ::= SEQUENCE {
+ ...,
+ i0 INTEGER (0..127) OPTIONAL,
+ i1 INTEGER (0..127) OPTIONAL,
+ i2 INTEGER (0..127) OPTIONAL,
+ i3 INTEGER (0..127) OPTIONAL,
+ i4 INTEGER (0..127) OPTIONAL,
+ i5 INTEGER (0..127) OPTIONAL,
+ i6 INTEGER (0..127) OPTIONAL,
+ i7 INTEGER (0..127) OPTIONAL,
+ i8 INTEGER (0..127) OPTIONAL,
+ i9 INTEGER (0..127) OPTIONAL,
+ i10 INTEGER (0..127) OPTIONAL,
+ i11 INTEGER (0..127) OPTIONAL,
+ i12 INTEGER (0..127) OPTIONAL,
+ i13 INTEGER (0..127) OPTIONAL,
+ i14 INTEGER (0..127) OPTIONAL,
+ i15 INTEGER (0..127) OPTIONAL,
+ i16 INTEGER (0..127) OPTIONAL,
+ i17 INTEGER (0..127) OPTIONAL,
+ i18 INTEGER (0..127) OPTIONAL,
+ i19 INTEGER (0..127) OPTIONAL,
+ i20 INTEGER (0..127) OPTIONAL,
+ i21 INTEGER (0..127) OPTIONAL,
+ i22 INTEGER (0..127) OPTIONAL,
+ i23 INTEGER (0..127) OPTIONAL,
+ i24 INTEGER (0..127) OPTIONAL,
+ i25 INTEGER (0..127) OPTIONAL,
+ i26 INTEGER (0..127) OPTIONAL,
+ i27 INTEGER (0..127) OPTIONAL,
+ i28 INTEGER (0..127) OPTIONAL,
+ i29 INTEGER (0..127) OPTIONAL,
+ i30 INTEGER (0..127) OPTIONAL,
+ i31 INTEGER (0..127) OPTIONAL,
+ i32 INTEGER (0..127) OPTIONAL,
+ i33 INTEGER (0..127) OPTIONAL,
+ i34 INTEGER (0..127) OPTIONAL,
+ i35 INTEGER (0..127) OPTIONAL,
+ i36 INTEGER (0..127) OPTIONAL,
+ i37 INTEGER (0..127) OPTIONAL,
+ i38 INTEGER (0..127) OPTIONAL,
+ i39 INTEGER (0..127) OPTIONAL,
+ i40 INTEGER (0..127) OPTIONAL,
+ i41 INTEGER (0..127) OPTIONAL,
+ i42 INTEGER (0..127) OPTIONAL,
+ i43 INTEGER (0..127) OPTIONAL,
+ i44 INTEGER (0..127) OPTIONAL,
+ i45 INTEGER (0..127) OPTIONAL,
+ i46 INTEGER (0..127) OPTIONAL,
+ i47 INTEGER (0..127) OPTIONAL,
+ i48 INTEGER (0..127) OPTIONAL,
+ i49 INTEGER (0..127) OPTIONAL,
+ i50 INTEGER (0..127) OPTIONAL,
+ i51 INTEGER (0..127) OPTIONAL,
+ i52 INTEGER (0..127) OPTIONAL,
+ i53 INTEGER (0..127) OPTIONAL,
+ i54 INTEGER (0..127) OPTIONAL,
+ i55 INTEGER (0..127) OPTIONAL,
+ i56 INTEGER (0..127) OPTIONAL,
+ i57 INTEGER (0..127) OPTIONAL,
+ i58 INTEGER (0..127) OPTIONAL,
+ i59 INTEGER (0..127) OPTIONAL,
+ i60 INTEGER (0..127) OPTIONAL,
+ i61 INTEGER (0..127) OPTIONAL,
+ i62 INTEGER (0..127) OPTIONAL,
+ i63 INTEGER (0..127) OPTIONAL,
+ i64 INTEGER (0..127) OPTIONAL,
+ i65 INTEGER (0..127) OPTIONAL
+}
+
+SeqExt130 ::= SEQUENCE {
+ ...,
+ i0 INTEGER (0..255) OPTIONAL,
+ i1 INTEGER (0..255) OPTIONAL,
+ i2 INTEGER (0..255) OPTIONAL,
+ i3 INTEGER (0..255) OPTIONAL,
+ i4 INTEGER (0..255) OPTIONAL,
+ i5 INTEGER (0..255) OPTIONAL,
+ i6 INTEGER (0..255) OPTIONAL,
+ i7 INTEGER (0..255) OPTIONAL,
+ i8 INTEGER (0..255) OPTIONAL,
+ i9 INTEGER (0..255) OPTIONAL,
+ i10 INTEGER (0..255) OPTIONAL,
+ i11 INTEGER (0..255) OPTIONAL,
+ i12 INTEGER (0..255) OPTIONAL,
+ i13 INTEGER (0..255) OPTIONAL,
+ i14 INTEGER (0..255) OPTIONAL,
+ i15 INTEGER (0..255) OPTIONAL,
+ i16 INTEGER (0..255) OPTIONAL,
+ i17 INTEGER (0..255) OPTIONAL,
+ i18 INTEGER (0..255) OPTIONAL,
+ i19 INTEGER (0..255) OPTIONAL,
+ i20 INTEGER (0..255) OPTIONAL,
+ i21 INTEGER (0..255) OPTIONAL,
+ i22 INTEGER (0..255) OPTIONAL,
+ i23 INTEGER (0..255) OPTIONAL,
+ i24 INTEGER (0..255) OPTIONAL,
+ i25 INTEGER (0..255) OPTIONAL,
+ i26 INTEGER (0..255) OPTIONAL,
+ i27 INTEGER (0..255) OPTIONAL,
+ i28 INTEGER (0..255) OPTIONAL,
+ i29 INTEGER (0..255) OPTIONAL,
+ i30 INTEGER (0..255) OPTIONAL,
+ i31 INTEGER (0..255) OPTIONAL,
+ i32 INTEGER (0..255) OPTIONAL,
+ i33 INTEGER (0..255) OPTIONAL,
+ i34 INTEGER (0..255) OPTIONAL,
+ i35 INTEGER (0..255) OPTIONAL,
+ i36 INTEGER (0..255) OPTIONAL,
+ i37 INTEGER (0..255) OPTIONAL,
+ i38 INTEGER (0..255) OPTIONAL,
+ i39 INTEGER (0..255) OPTIONAL,
+ i40 INTEGER (0..255) OPTIONAL,
+ i41 INTEGER (0..255) OPTIONAL,
+ i42 INTEGER (0..255) OPTIONAL,
+ i43 INTEGER (0..255) OPTIONAL,
+ i44 INTEGER (0..255) OPTIONAL,
+ i45 INTEGER (0..255) OPTIONAL,
+ i46 INTEGER (0..255) OPTIONAL,
+ i47 INTEGER (0..255) OPTIONAL,
+ i48 INTEGER (0..255) OPTIONAL,
+ i49 INTEGER (0..255) OPTIONAL,
+ i50 INTEGER (0..255) OPTIONAL,
+ i51 INTEGER (0..255) OPTIONAL,
+ i52 INTEGER (0..255) OPTIONAL,
+ i53 INTEGER (0..255) OPTIONAL,
+ i54 INTEGER (0..255) OPTIONAL,
+ i55 INTEGER (0..255) OPTIONAL,
+ i56 INTEGER (0..255) OPTIONAL,
+ i57 INTEGER (0..255) OPTIONAL,
+ i58 INTEGER (0..255) OPTIONAL,
+ i59 INTEGER (0..255) OPTIONAL,
+ i60 INTEGER (0..255) OPTIONAL,
+ i61 INTEGER (0..255) OPTIONAL,
+ i62 INTEGER (0..255) OPTIONAL,
+ i63 INTEGER (0..255) OPTIONAL,
+ i64 INTEGER (0..255) OPTIONAL,
+ i65 INTEGER (0..255) OPTIONAL,
+ i66 INTEGER (0..255) OPTIONAL,
+ i67 INTEGER (0..255) OPTIONAL,
+ i68 INTEGER (0..255) OPTIONAL,
+ i69 INTEGER (0..255) OPTIONAL,
+ i70 INTEGER (0..255) OPTIONAL,
+ i71 INTEGER (0..255) OPTIONAL,
+ i72 INTEGER (0..255) OPTIONAL,
+ i73 INTEGER (0..255) OPTIONAL,
+ i74 INTEGER (0..255) OPTIONAL,
+ i75 INTEGER (0..255) OPTIONAL,
+ i76 INTEGER (0..255) OPTIONAL,
+ i77 INTEGER (0..255) OPTIONAL,
+ i78 INTEGER (0..255) OPTIONAL,
+ i79 INTEGER (0..255) OPTIONAL,
+ i80 INTEGER (0..255) OPTIONAL,
+ i81 INTEGER (0..255) OPTIONAL,
+ i82 INTEGER (0..255) OPTIONAL,
+ i83 INTEGER (0..255) OPTIONAL,
+ i84 INTEGER (0..255) OPTIONAL,
+ i85 INTEGER (0..255) OPTIONAL,
+ i86 INTEGER (0..255) OPTIONAL,
+ i87 INTEGER (0..255) OPTIONAL,
+ i88 INTEGER (0..255) OPTIONAL,
+ i89 INTEGER (0..255) OPTIONAL,
+ i90 INTEGER (0..255) OPTIONAL,
+ i91 INTEGER (0..255) OPTIONAL,
+ i92 INTEGER (0..255) OPTIONAL,
+ i93 INTEGER (0..255) OPTIONAL,
+ i94 INTEGER (0..255) OPTIONAL,
+ i95 INTEGER (0..255) OPTIONAL,
+ i96 INTEGER (0..255) OPTIONAL,
+ i97 INTEGER (0..255) OPTIONAL,
+ i98 INTEGER (0..255) OPTIONAL,
+ i99 INTEGER (0..255) OPTIONAL,
+ i100 INTEGER (0..255) OPTIONAL,
+ i101 INTEGER (0..255) OPTIONAL,
+ i102 INTEGER (0..255) OPTIONAL,
+ i103 INTEGER (0..255) OPTIONAL,
+ i104 INTEGER (0..255) OPTIONAL,
+ i105 INTEGER (0..255) OPTIONAL,
+ i106 INTEGER (0..255) OPTIONAL,
+ i107 INTEGER (0..255) OPTIONAL,
+ i108 INTEGER (0..255) OPTIONAL,
+ i109 INTEGER (0..255) OPTIONAL,
+ i110 INTEGER (0..255) OPTIONAL,
+ i111 INTEGER (0..255) OPTIONAL,
+ i112 INTEGER (0..255) OPTIONAL,
+ i113 INTEGER (0..255) OPTIONAL,
+ i114 INTEGER (0..255) OPTIONAL,
+ i115 INTEGER (0..255) OPTIONAL,
+ i116 INTEGER (0..255) OPTIONAL,
+ i117 INTEGER (0..255) OPTIONAL,
+ i118 INTEGER (0..255) OPTIONAL,
+ i119 INTEGER (0..255) OPTIONAL,
+ i120 INTEGER (0..255) OPTIONAL,
+ i121 INTEGER (0..255) OPTIONAL,
+ i122 INTEGER (0..255) OPTIONAL,
+ i123 INTEGER (0..255) OPTIONAL,
+ i124 INTEGER (0..255) OPTIONAL,
+ i125 INTEGER (0..255) OPTIONAL,
+ i126 INTEGER (0..255) OPTIONAL,
+ i127 INTEGER (0..255) OPTIONAL,
+ i128 INTEGER (0..255) OPTIONAL,
+ i129 INTEGER (0..255) OPTIONAL
+}
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
index 00e4c707dd..8e21e6ca84 100644
--- a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
+++ b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
@@ -34,6 +34,9 @@ run(Erule) ->
run3(),
run3(Erule),
+ roundtrip('InlinedSeq', #'InlinedSeq'{s=#'InlinedSeq_s'{a=42,b=true}}),
+ roundtrip('ExtAddGroup1', #'ExtAddGroup1'{x=42,y=1023}),
+
ok.
%% From X.691 (07/2002) A.4.
diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl
index b839dfcf2a..7d7364e2a4 100644
--- a/lib/asn1/test/asn1_test_lib.erl
+++ b/lib/asn1/test/asn1_test_lib.erl
@@ -22,6 +22,7 @@
-export([compile/3]).
-export([compile_all/3]).
-export([compile_erlang/3]).
+-export([hex_to_bin/1]).
-export([ticket_7407_compile/2,ticket_7407_code/1, ticket_7678/2,
ticket_7708/2, ticket_7763/1, ticket_7876/3]).
@@ -61,6 +62,13 @@ compile_erlang(Mod, Config, Options) ->
{ok, M} = compile:file(filename:join(DataDir, Mod),
[report,{i,CaseDir},{outdir,CaseDir}|Options]).
+hex_to_bin(S) ->
+ << <<(hex2num(C)):4>> || C <- S, C =/= $\s >>.
+
+%%%
+%%% Internal functions.
+%%%
+
should_load(File, Options) ->
case lists:member(abs, Options) of
true ->
@@ -79,6 +87,10 @@ strip_extension(File, Ext) when Ext == ".asn"; Ext == ".set"; Ext == ".asn1"->
strip_extension(File, _Ext) ->
File.
+hex2num(C) when $0 =< C, C =< $9 -> C - $0;
+hex2num(C) when $A =< C, C =< $F -> C - $A + 10;
+hex2num(C) when $a =< C, C =< $f -> C - $a + 10.
+
ticket_7407_compile(Config,Option) ->
?line DataDir = ?config(data_dir,Config),
diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl
index e825302629..14e508d2b5 100644
--- a/lib/asn1/test/testConstraints.erl
+++ b/lib/asn1/test/testConstraints.erl
@@ -126,19 +126,29 @@ int_constraints(Rules) ->
%%==========================================================
roundtrip('SemiConstrained', 100),
+ v_roundtrip(Rules, 'SemiConstrained', 100+128),
roundtrip('SemiConstrained', 397249742397243),
+ roundtrip('SemiConstrained', 100 + 1 bsl 128*8),
+ roundtrip('SemiConstrained', 100 + 1 bsl 256*8),
+
roundtrip('NegSemiConstrained', -128),
+ v_roundtrip(Rules, 'NegSemiConstrained', 0),
roundtrip('NegSemiConstrained', -1),
roundtrip('NegSemiConstrained', 500),
roundtrip('SemiConstrainedExt', -65536),
roundtrip('SemiConstrainedExt', 0),
roundtrip('SemiConstrainedExt', 42),
+ v_roundtrip(Rules, 'SemiConstrainedExt', 42+128),
roundtrip('SemiConstrainedExt', 100),
roundtrip('SemiConstrainedExt', 47777789),
+ roundtrip('SemiConstrainedExt', 42 + 1 bsl 128*8),
+ roundtrip('SemiConstrainedExt', 42 + 1 bsl 256*8),
+
roundtrip('NegSemiConstrainedExt', -1023),
roundtrip('NegSemiConstrainedExt', -128),
roundtrip('NegSemiConstrainedExt', -1),
+ v_roundtrip(Rules, 'NegSemiConstrainedExt', 0),
roundtrip('NegSemiConstrainedExt', 500),
%%==========================================================
@@ -174,6 +184,21 @@ int_constraints(Rules) ->
ok.
+%% PER: Ensure that if the lower bound is Lb, Lb+16#80 is encoded
+%% in two bytes as 16#0180. (Not in three bytes as 16#010080.)
+v(ber, 'SemiConstrained', 100+128) -> "020200E4";
+v(per, 'SemiConstrained', 100+128) -> "0180";
+v(uper, 'SemiConstrained', 100+128) -> "0180";
+v(ber, 'NegSemiConstrained', 0) -> "020100";
+v(per, 'NegSemiConstrained', 0) -> "0180";
+v(uper, 'NegSemiConstrained', 0) -> "0180";
+v(ber, 'SemiConstrainedExt', 42+128) -> "020200AA";
+v(per, 'SemiConstrainedExt', 42+128) -> "000180";
+v(uper, 'SemiConstrainedExt', 42+128) -> "00C000";
+v(ber, 'NegSemiConstrainedExt', 0) -> "020100";
+v(per, 'NegSemiConstrainedExt', 0) -> "000180";
+v(uper, 'NegSemiConstrainedExt', 0) -> "00C000".
+
shorter_ext(per, "a") -> <<16#80,16#01,16#61>>;
shorter_ext(uper, "a") -> <<16#80,16#E1>>;
shorter_ext(ber, _) -> none.
@@ -183,13 +208,17 @@ refed_NNL_name(_Erule) ->
?line {error,_Reason} =
asn1_wrapper:encode('Constraints','AnotherThing',fred3).
+v_roundtrip(Erule, Type, Value) ->
+ Encoded = asn1_test_lib:hex_to_bin(v(Erule, Type, Value)),
+ Encoded = roundtrip('Constraints', Type, Value).
+
roundtrip(Type, Value) ->
roundtrip('Constraints', Type, Value).
roundtrip(Module, Type, Value) ->
{ok,Encoded} = Module:encode(Type, Value),
{ok,Value} = Module:decode(Type, Encoded),
- ok.
+ Encoded.
roundtrip_enc(Type, Value, Enc) ->
Module = 'Constraints',
diff --git a/lib/asn1/test/testEnumExt.erl b/lib/asn1/test/testEnumExt.erl
index 8840ed6d2f..8779f3b83b 100644
--- a/lib/asn1/test/testEnumExt.erl
+++ b/lib/asn1/test/testEnumExt.erl
@@ -38,7 +38,7 @@ main(Rule) when Rule =:= per; Rule =:= uper ->
%% ENUMERATED no extensionmark
B64 = <<64>>,
B64 = roundtrip('Noext', red),
- common();
+ common(Rule);
main(ber) ->
io:format("main(ber)~n",[]),
%% ENUMERATED with extensionmark (value is in root set)
@@ -56,18 +56,38 @@ main(ber) ->
roundtrip('Globalstate', preop),
roundtrip('Globalstate', com),
- common().
+ common(ber).
-common() ->
+common(Erule) ->
roundtrip('Seq', {'Seq',blue,42}),
roundtrip('Seq', {'Seq',red,42}),
roundtrip('Seq', {'Seq',green,42}),
roundtrip('Seq', {'Seq',orange,47}),
roundtrip('Seq', {'Seq',black,4711}),
roundtrip('Seq', {'Seq',magenta,4712}),
+
+ [begin
+ S = io_lib:format("e~2.016.0b", [I]),
+ E = list_to_atom(lists:flatten(S)),
+ roundtrip('SeqBig', {'SeqBig',true,E,9357})
+ end || I <- lists:seq(0, 128)],
+
+ v_roundtrip(Erule, 'SeqBig', {'SeqBig',true,e40,9357}),
+ v_roundtrip(Erule, 'SeqBig', {'SeqBig',true,e80,9357}),
ok.
roundtrip(Type, Value) ->
{ok,Encoded} = 'EnumExt':encode(Type, Value),
{ok,Value} = 'EnumExt':decode(Type, Encoded),
Encoded.
+
+v_roundtrip(Erule, Type, Value) ->
+ Encoded = roundtrip(Type, Value),
+ Encoded = asn1_test_lib:hex_to_bin(v(Erule, Value)).
+
+v(ber, {'SeqBig',true,e40,9357}) -> "300A8001 FF810141 8202248D";
+v(ber, {'SeqBig',true,e80,9357}) -> "300B8001 FF810200 81820224 8D";
+v(per, {'SeqBig',true,e40,9357}) -> "E0014002 248D";
+v(per, {'SeqBig',true,e80,9357}) -> "E0018002 248D";
+v(uper, {'SeqBig',true,e40,9357}) -> "E0280044 91A0";
+v(uper, {'SeqBig',true,e80,9357}) -> "E0300044 91A0".
diff --git a/lib/asn1/test/testSeqExtension.erl b/lib/asn1/test/testSeqExtension.erl
index 1128d9a7c3..724f485fa0 100644
--- a/lib/asn1/test/testSeqExtension.erl
+++ b/lib/asn1/test/testSeqExtension.erl
@@ -20,7 +20,7 @@
-module(testSeqExtension).
-include("External.hrl").
--export([main/2]).
+-export([main/3]).
-include_lib("test_server/include/test_server.hrl").
@@ -32,7 +32,7 @@
-record('SeqExt6',{i1,i2,i3,i4,i5,i6,i7}).
-record('SuperSeq',{s1,s2,s3,s4,s5,s6,i}).
-main(DataDir, Opts) ->
+main(Erule, DataDir, Opts) ->
roundtrip('SeqExt1', #'SeqExt1'{}),
roundtrip('SeqExt2', #'SeqExt2'{bool=true,int=99}),
@@ -92,9 +92,38 @@ main(DataDir, Opts) ->
s5={'SeqExt5'},
s6={'SeqExt6',531,601,999,777,11953},
i=BigInt} = DecodedSuperSeq,
+
+
+ %% Test more than 64 extensions.
+ roundtrip2('SeqExt66',
+ list_to_tuple(['SeqExt66'|lists:seq(0, 65)])),
+ v_roundtrip2(Erule, 'SeqExt66',
+ list_to_tuple(['SeqExt66'|
+ lists:duplicate(65, asn1_NOVALUE)++[125]])),
+ roundtrip2('SeqExt130',
+ list_to_tuple(['SeqExt130'|lists:seq(0, 129)])),
+ v_roundtrip2(Erule, 'SeqExt130',
+ list_to_tuple(['SeqExt130'|
+ lists:duplicate(129, asn1_NOVALUE)++[199]])),
ok.
roundtrip(Type, Value) ->
{ok,Encoded} = 'SeqExtension':encode(Type, Value),
{ok,Value} = 'SeqExtension':decode(Type, Encoded),
ok.
+
+v_roundtrip2(Erule, Type, Value) ->
+ Encoded = asn1_test_lib:hex_to_bin(v(Erule, Type)),
+ Encoded = roundtrip2(Type, Value).
+
+roundtrip2(Type, Value) ->
+ {ok,Encoded} = 'SeqExtension2':encode(Type, Value),
+ {ok,Value} = 'SeqExtension2':decode(Type, Encoded),
+ Encoded.
+
+v(ber, 'SeqExt66') -> "30049F41 017D";
+v(per, 'SeqExt66') -> "C0420000 00000000 00004001 FA";
+v(uper, 'SeqExt66') -> "D0800000 00000000 00101FA0";
+v(ber, 'SeqExt130') -> "30069F81 010200C7";
+v(per, 'SeqExt130') -> "C0808200 00000000 00000000 00000000 00000040 01C7";
+v(uper, 'SeqExt130') -> "E0208000 00000000 00000000 00000000 0000101C 70".
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 41d53c7b43..266ca73417 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -402,7 +402,8 @@ script_start2(Opts = #opts{vts = undefined,
Relaxed = get_start_opt(allow_user_terms, true, false, Args),
case catch ct_testspec:collect_tests_from_file(Specs1, Relaxed) of
{E,Reason} when E == error ; E == 'EXIT' ->
- {error,Reason};
+ StackTrace = erlang:get_stacktrace(),
+ {error,{invalid_testspec,{Reason,StackTrace}}};
TestSpecData ->
execute_all_specs(TestSpecData, Opts, Args, [])
end;
@@ -1101,7 +1102,8 @@ run_spec_file(Relaxed,
AbsSpecs1 = get_start_opt(join_specs, [AbsSpecs], AbsSpecs, StartOpts),
case catch ct_testspec:collect_tests_from_file(AbsSpecs1, Relaxed) of
{Error,CTReason} when Error == error ; Error == 'EXIT' ->
- exit({error,CTReason});
+ StackTrace = erlang:get_stacktrace(),
+ exit({error,{invalid_testspec,{CTReason,StackTrace}}});
TestSpecData ->
run_all_specs(TestSpecData, Opts, StartOpts, [])
end.
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 71b03c0ea6..c07ea323e6 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -253,7 +253,7 @@ collect_tests_from_file(Specs,Nodes,Relaxed) when is_list(Nodes) ->
Specs2 = [filename:absname(S) || S <- Specs1],
TS0 = #testspec{nodes=NodeRefs},
- try create_specs(Specs2,TS0,Relaxed,Join) of
+ try create_testspecs(Specs2,TS0,Relaxed,Join) of
{{[],_},SeparateTestSpecs} ->
filter_and_convert(SeparateTestSpecs);
{{_,#testspec{tests=[]}},SeparateTestSpecs} ->
@@ -262,8 +262,10 @@ collect_tests_from_file(Specs,Nodes,Relaxed) when is_list(Nodes) ->
[filter_and_convert(Joined) |
filter_and_convert(SeparateTestSpecs)]
catch
+ _:Error={error,_} ->
+ Error;
_:Error ->
- Error
+ {error,Error}
end.
filter_and_convert(Joined) when is_tuple(Joined) ->
@@ -293,9 +295,12 @@ delete_dups1([E|Es],Keep) ->
delete_dups1([],Keep) ->
Keep.
-create_specs(Specs,TestSpec,Relaxed,Join) ->
- SpecsTree = create_spec_tree(Specs,TestSpec,Join,[]),
- create_specs(SpecsTree,TestSpec,Relaxed).
+create_testspecs(Specs,TestSpec,Relaxed,Join) ->
+ %% SpecsTree = {SpecAbsName, TermsInSpec,
+ %% IncludedJoinTree, IncludedSeparateTree,
+ %% JoinSpecWithRest, RestSpecsTree}
+ SpecsTree = create_spec_tree(Specs,TestSpec,Join,[]),
+ create_specs(SpecsTree,TestSpec,TestSpec,Relaxed).
create_spec_tree([Spec|Specs],TS,JoinWithNext,Known) ->
SpecDir = filename:dirname(filename:absname(Spec)),
@@ -325,27 +330,31 @@ create_spec_tree([],_TS,_JoinWithNext,_Known) ->
[].
create_specs({Spec,Terms,InclJoin,InclSep,JoinWithNext,NextSpec},
- TestSpec,Relaxed) ->
+ TestSpec,TestSpec0,Relaxed) ->
SpecDir = filename:dirname(filename:absname(Spec)),
TestSpec1 = create_spec(Terms,TestSpec#testspec{spec_dir=SpecDir},
JoinWithNext,Relaxed),
- {{JoinSpecs1,JoinTS1},Separate1} = create_specs(InclJoin,TestSpec1,Relaxed),
+ {{JoinSpecs1,JoinTS1},Separate1} = create_specs(InclJoin,TestSpec1,
+ TestSpec0,Relaxed),
{{JoinSpecs2,JoinTS2},Separate2} =
case JoinWithNext of
true ->
- create_specs(NextSpec,JoinTS1,Relaxed);
+ create_specs(NextSpec,JoinTS1,
+ TestSpec0,Relaxed);
false ->
{{[],JoinTS1},[]}
end,
- {SepJoinSpecs,Separate3} = create_specs(InclSep,TestSpec,Relaxed),
+ {SepJoinSpecs,Separate3} = create_specs(InclSep,TestSpec0,
+ TestSpec0,Relaxed),
{SepJoinSpecs1,Separate4} =
case JoinWithNext of
true ->
{{[],TestSpec},[]};
false ->
- create_specs(NextSpec,TestSpec,Relaxed)
+ create_specs(NextSpec,TestSpec0,
+ TestSpec0,Relaxed)
end,
SpecInfo = {Spec,TestSpec1#testspec.merge_tests},
@@ -354,7 +363,6 @@ create_specs({Spec,Terms,InclJoin,InclSep,JoinWithNext,NextSpec},
[SepJoinSpecs]++Separate2++
[SepJoinSpecs1]++Separate4,
Ss /= []],
-
case {JoinWithNext,JoinSpecs1} of
{true,_} ->
{{[SpecInfo|(JoinSpecs1++JoinSpecs2)],JoinTS2},
@@ -366,7 +374,7 @@ create_specs({Spec,Terms,InclJoin,InclSep,JoinWithNext,NextSpec},
{{[SpecInfo|(JoinSpecs1++JoinSpecs2)],JoinTS2},
AllSeparate}
end;
-create_specs([],TestSpec,_Relaxed) ->
+create_specs([],TestSpec,_,_Relaxed) ->
{{[],TestSpec},[]}.
create_spec(Terms,TestSpec,JoinedByPrev,Relaxed) ->
@@ -842,7 +850,8 @@ add_tests([{cases,Node,Dir,Suite,Cs}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_cases(ref2node(Node,Spec#testspec.nodes),
ref2dir(Dir,Spec),
- Suite,Cs,Tests, Spec#testspec.merge_tests),
+ Suite,Cs,Tests,
+ Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
%% --- skip_suites ---
@@ -1246,17 +1255,22 @@ insert_cases(Node,Dir,Suite,Cases,Tests,false) when is_list(Cases) ->
append({{Node,Dir},[{Suite,Cases}]},Tests);
insert_cases(Node,Dir,Suite,Cases,Tests,true) when is_list(Cases) ->
{Tests1,Done} =
- lists:foldr(fun(All={{N,D},[{all,_}]},{Replaced,_}) when N == Node,
+ lists:foldr(fun(All={{N,D},[{all,_}]},{Merged,_}) when N == Node,
D == Dir ->
- {[All|Replaced],true};
- ({{N,D},Suites0},{Replaced,_}) when N == Node,
+ {[All|Merged],true};
+ ({{N,D},Suites0},{Merged,_}) when N == Node,
D == Dir ->
Suites1 = insert_cases1(Suite,Cases,Suites0),
- {[{{N,D},Suites1}|Replaced],true};
- (T,{Replaced,Match}) ->
- {[T|Replaced],Match}
+ {[{{N,D},Suites1}|Merged],true};
+ (T,{Merged,Match}) ->
+ {[T|Merged],Match}
end, {[],false}, Tests),
- if not Done ->
+ if Tests == [] ->
+ %% initial case with length(Cases) > 1, we need to do this
+ %% to merge possible duplicate cases in Cases
+ [{{Node,Dir},insert_cases1(Suite,Cases,[{Suite,[]}])}];
+ not Done ->
+ %% no merging done, simply add these cases to Tests
Tests ++ [{{Node,Dir},[{Suite,Cases}]}];
true ->
Tests1
@@ -1301,14 +1315,14 @@ skip_groups(Node,Dir,Suite,Groups,Cases,Cmt,Tests,false) when
skip_groups(Node,Dir,Suite,Groups,Cases,Cmt,Tests,true) when
((Cases == all) or is_list(Cases)) and is_list(Groups) ->
{Tests1,Done} =
- lists:foldr(fun({{N,D},Suites0},{Replaced,_}) when N == Node,
+ lists:foldr(fun({{N,D},Suites0},{Merged,_}) when N == Node,
D == Dir ->
Suites1 = skip_groups1(Suite,
[{Gr,Cases} || Gr <- Groups],
Cmt,Suites0),
- {[{{N,D},Suites1}|Replaced],true};
- (T,{Replaced,Match}) ->
- {[T|Replaced],Match}
+ {[{{N,D},Suites1}|Merged],true};
+ (T,{Merged,Match}) ->
+ {[T|Merged],Match}
end, {[],false}, Tests),
if not Done ->
Tests ++ [{{Node,Dir},skip_groups1(Suite,
@@ -1339,12 +1353,12 @@ skip_cases(Node,Dir,Suite,Cases,Cmt,Tests,false) when is_list(Cases) ->
append({{Node,Dir},Suites1},Tests);
skip_cases(Node,Dir,Suite,Cases,Cmt,Tests,true) when is_list(Cases) ->
{Tests1,Done} =
- lists:foldr(fun({{N,D},Suites0},{Replaced,_}) when N == Node,
+ lists:foldr(fun({{N,D},Suites0},{Merged,_}) when N == Node,
D == Dir ->
Suites1 = skip_cases1(Suite,Cases,Cmt,Suites0),
- {[{{N,D},Suites1}|Replaced],true};
- (T,{Replaced,Match}) ->
- {[T|Replaced],Match}
+ {[{{N,D},Suites1}|Merged],true};
+ (T,{Merged,Match}) ->
+ {[T|Merged],Match}
end, {[],false}, Tests),
if not Done ->
Tests ++ [{{Node,Dir},skip_cases1(Suite,Cases,Cmt,[])}];
diff --git a/lib/common_test/test/ct_testspec_3_SUITE.erl b/lib/common_test/test/ct_testspec_3_SUITE.erl
index 6b4b729552..5fa187e5b4 100644
--- a/lib/common_test/test/ct_testspec_3_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_3_SUITE.erl
@@ -284,6 +284,24 @@ events_to_check(_, 0) ->
events_to_check(Test, N) ->
test_events(Test) ++ events_to_check(Test, N-1).
+
+%%%!
+%%%! IMPORTANT NOTE ABOUT THE TEST ORDER:
+%%%!
+%%%! When merging testspec terms, CT will group the tests by TestDir and
+%%%! Suite, before term order (in testspec). That means that if tests
+%%%! are ordered like e.g:
+%%%! {Dir1,Suite11}, {Dir2,Suite21}, {Dir1,Suite12},
+%%%! the execution order after merge (even if no merge takes place),
+%%%! will be:
+%%%! {Dir1,[Suite11,Suite12]}, {Dir2,Suite21}
+%%%!
+%%%! Also, tests in a tree of included testspecs are always collected
+%%%! and merged in depth-first manner, meaning even if a particular test is
+%%%! on a higher level in the tree, it may be executed later than a test on a
+%%%! lower level.
+%%%!
+
test_events(start_separate) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
@@ -300,6 +318,7 @@ test_events(start_separate) ->
{?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{3,2,15}},
@@ -415,6 +434,7 @@ test_events(incl_separate1) ->
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{3,2,15}},
@@ -448,6 +468,7 @@ test_events(incl_separate2) ->
{?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{3,2,15}},
@@ -468,6 +489,7 @@ test_events(incl_separate2) ->
{?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{2,2,10}},
@@ -483,6 +505,7 @@ test_events(incl_separate2) ->
{?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{2,2,10}},
@@ -498,6 +521,7 @@ test_events(incl_separate2) ->
{?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{3,2,15}},
@@ -545,6 +569,7 @@ test_events(incl_join1) ->
{?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{4,4,20}},
@@ -614,6 +639,7 @@ test_events(incl_both1) ->
{?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{3,2,15}},
@@ -634,6 +660,7 @@ test_events(incl_both1) ->
{?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{2,2,10}},
@@ -649,6 +676,7 @@ test_events(incl_both1) ->
{?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{2,2,10}},
@@ -692,6 +720,7 @@ test_events(incl_both2) ->
{?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{3,2,15}},
@@ -712,6 +741,7 @@ test_events(incl_both2) ->
{?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]},
+
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{2,2,10}},
@@ -728,18 +758,890 @@ test_events(incl_both2) ->
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}];
-test_events(incl_both_and_join1) -> [];
-test_events(incl_both_and_join2) -> [];
-test_events(rec_incl_separate1) -> [];
-test_events(rec_incl_separate2) -> [];
-test_events(rec_incl_join1) -> [];
-test_events(rec_incl_join2) -> [];
-test_events(rec_incl_separate_join1) -> [];
-test_events(rec_incl_separate_join2) -> [];
-test_events(rec_incl_join_separate1) -> [];
-test_events(rec_incl_join_separate2) -> [];
-
-test_events(_) ->
- [].
+test_events(incl_both_and_join1) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{5,3,25}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{5,10,{5,5}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(incl_both_and_join2) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(rec_incl_separate1) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t23_SUITE,init_per_suite}},
+ {?eh,tc_done,{t23_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t23_SUITE,end_per_suite}},
+ {?eh,tc_done,{t23_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(rec_incl_separate2) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t23_SUITE,init_per_suite}},
+ {?eh,tc_done,{t23_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t23_SUITE,end_per_suite}},
+ {?eh,tc_done,{t23_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(rec_incl_join1) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{5,5,25}},
+ {?eh,tc_start,{t23_SUITE,init_per_suite}},
+ {?eh,tc_done,{t23_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t23_SUITE,end_per_suite}},
+ {?eh,tc_done,{t23_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{5,10,{5,5}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+test_events(rec_incl_join2) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{5,5,25}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t23_SUITE,init_per_suite}},
+ {?eh,tc_done,{t23_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{5,10,{5,5}}},
+ {?eh,tc_start,{t23_SUITE,end_per_suite}},
+ {?eh,tc_done,{t23_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(rec_incl_separate_join1) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t23_SUITE,init_per_suite}},
+ {?eh,tc_done,{t23_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t23_SUITE,end_per_suite}},
+ {?eh,tc_done,{t23_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(rec_incl_separate_join2) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t23_SUITE,init_per_suite}},
+ {?eh,tc_done,{t23_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t23_SUITE,end_per_suite}},
+ {?eh,tc_done,{t23_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(rec_incl_join_separate1) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t23_SUITE,init_per_suite}},
+ {?eh,tc_done,{t23_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t23_SUITE,end_per_suite}},
+ {?eh,tc_done,{t23_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t23_SUITE,init_per_suite}},
+ {?eh,tc_done,{t23_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t23_SUITE,end_per_suite}},
+ {?eh,tc_done,{t23_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(rec_incl_join_separate2) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t23_SUITE,init_per_suite}},
+ {?eh,tc_done,{t23_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t23_SUITE,end_per_suite}},
+ {?eh,tc_done,{t23_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec2_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}]
+ ++ flat_spec1_events() ++
+ [{?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}].
+
+%%%-----------------------------------------------------------------
+
+flat_spec1_events() ->
+ [
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,ok_tc}},
+ {?eh,tc_done,{t11_SUITE,ok_tc,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{t11_SUITE,exit_tc}},
+ {?eh,tc_done,{t11_SUITE,exit_tc,{failed,{error,kaboom}}}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ {?eh,tc_start,{t11_SUITE,to_tc}},
+ {?eh,tc_done,{t11_SUITE,to_tc,{failed,{timetrap_timeout,1}}}},
+ {?eh,test_stats,{1,2,{0,0}}},
+ {?eh,tc_start,{t11_SUITE,autoskip_tc}},
+ {?eh,tc_done,
+ {t11_SUITE,autoskip_tc,{skipped,
+ {failed,
+ {t11_SUITE,init_per_testcase,
+ {kaboom,'_'}}}}}},
+ {?eh,test_stats,{1,2,{0,1}}},
+ {?eh,tc_start,{t11_SUITE,userskip_tc}},
+ {?eh,tc_done,{t11_SUITE,userskip_tc,{skipped,"user skipped"}}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,ok_tc}},
+ {?eh,tc_done,{t21_SUITE,ok_tc,ok}},
+ {?eh,test_stats,{2,2,{1,1}}},
+ {?eh,tc_start,{t21_SUITE,exit_tc}},
+ {?eh,tc_done,{t21_SUITE,exit_tc,{failed,{error,kaboom}}}},
+ {?eh,test_stats,{2,3,{1,1}}},
+ {?eh,tc_start,{t21_SUITE,to_tc}},
+ {?eh,tc_done,{t21_SUITE,to_tc,{failed,{timetrap_timeout,1}}}},
+ {?eh,test_stats,{2,4,{1,1}}},
+ {?eh,tc_start,{t21_SUITE,autoskip_tc}},
+ {?eh,tc_done,
+ {t21_SUITE,autoskip_tc,{skipped,
+ {failed,
+ {t21_SUITE,init_per_testcase,
+ {kaboom,'_'}}}}}},
+ {?eh,test_stats,{2,4,{1,2}}},
+ {?eh,tc_start,{t21_SUITE,userskip_tc}},
+ {?eh,tc_done,{t21_SUITE,userskip_tc,{skipped,"user skipped"}}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}}].
+
+flat_spec2_events() ->
+ [
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,ok_tc}},
+ {?eh,tc_done,{t12_SUITE,ok_tc,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{t12_SUITE,exit_tc}},
+ {?eh,tc_done,{t12_SUITE,exit_tc,{failed,{error,kaboom}}}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ {?eh,tc_start,{t12_SUITE,to_tc}},
+ {?eh,tc_done,{t12_SUITE,to_tc,{failed,{timetrap_timeout,1}}}},
+ {?eh,test_stats,{1,2,{0,0}}},
+ {?eh,tc_start,{t12_SUITE,autoskip_tc}},
+ {?eh,tc_done,
+ {t12_SUITE,autoskip_tc,{skipped,
+ {failed,
+ {t12_SUITE,init_per_testcase,
+ {kaboom,'_'}}}}}},
+ {?eh,test_stats,{1,2,{0,1}}},
+ {?eh,tc_start,{t12_SUITE,userskip_tc}},
+ {?eh,tc_done,{t12_SUITE,userskip_tc,{skipped,"user skipped"}}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,ok_tc}},
+ {?eh,tc_done,{t12_SUITE,ok_tc,ok}},
+ {?eh,test_stats,{2,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,exit_tc}},
+ {?eh,tc_done,{t12_SUITE,exit_tc,{failed,{error,kaboom}}}},
+ {?eh,test_stats,{2,3,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,to_tc}},
+ {?eh,tc_done,{t12_SUITE,to_tc,{failed,{timetrap_timeout,1}}}},
+ {?eh,test_stats,{2,4,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,autoskip_tc}},
+ {?eh,tc_done,
+ {t12_SUITE,autoskip_tc,{skipped,
+ {failed,
+ {t12_SUITE,init_per_testcase,
+ {kaboom,'_'}}}}}},
+ {?eh,test_stats,{2,4,{1,2}}},
+ {?eh,tc_start,{t12_SUITE,userskip_tc}},
+ {?eh,tc_done,{t12_SUITE,userskip_tc,{skipped,"user skipped"}}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,ok_tc}},
+ {?eh,tc_done,{t22_SUITE,ok_tc,ok}},
+ {?eh,test_stats,{3,4,{2,2}}},
+ {?eh,tc_start,{t22_SUITE,exit_tc}},
+ {?eh,tc_done,{t22_SUITE,exit_tc,{failed,{error,kaboom}}}},
+ {?eh,test_stats,{3,5,{2,2}}},
+ {?eh,tc_start,{t22_SUITE,to_tc}},
+ {?eh,tc_done,{t22_SUITE,to_tc,{failed,{timetrap_timeout,1}}}},
+ {?eh,test_stats,{3,6,{2,2}}},
+ {?eh,tc_start,{t22_SUITE,autoskip_tc}},
+ {?eh,tc_done,
+ {t22_SUITE,autoskip_tc,{skipped,
+ {failed,
+ {t22_SUITE,init_per_testcase,
+ {kaboom,'_'}}}}}},
+ {?eh,test_stats,{3,6,{2,3}}},
+ {?eh,tc_start,{t22_SUITE,userskip_tc}},
+ {?eh,tc_done,{t22_SUITE,userskip_tc,{skipped,"user skipped"}}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}}].
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 98ebb21f29..00abeb9990 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -142,6 +142,19 @@
(s)[3] = (char)((i) & 0xff);\
}
+/* This shall correspond to the similar macro in crypto.erl */
+/* Current value is: erlang:system_info(context_reductions) * 10 */
+#define MAX_BYTES_TO_NIF 20000
+
+#define CONSUME_REDS(NifEnv, Ibin) \
+do { \
+ int _cost = ((Ibin).size * 100) / MAX_BYTES_TO_NIF;\
+ if (_cost) { \
+ (void) enif_consume_timeslice((NifEnv), \
+ (_cost > 100) ? 100 : _cost); \
+ } \
+ } while (0)
+
/* NIF interface declarations */
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
@@ -208,7 +221,7 @@ static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
static ERL_NIF_TERM dss_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rc4_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -338,7 +351,7 @@ static ErlNifFunc nif_funcs[] = {
{"dss_verify_nif", 4, dss_verify_nif},
{"rsa_verify_nif", 4, rsa_verify_nif},
{"aes_cbc_crypt", 4, aes_cbc_crypt},
- {"exor", 2, exor},
+ {"do_exor", 2, do_exor},
{"rc4_encrypt", 2, rc4_encrypt},
{"rc4_set_key", 1, rc4_set_key},
{"rc4_encrypt_with_state", 2, rc4_encrypt_with_state},
@@ -778,6 +791,7 @@ static ERL_NIF_TERM md5(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
}
MD5((unsigned char *) ibin.data, ibin.size,
enif_make_new_binary(env,MD5_LEN, &ret));
+ CONSUME_REDS(env,ibin);
return ret;
}
static ERL_NIF_TERM md5_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -799,6 +813,7 @@ static ERL_NIF_TERM md5_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
new_ctx = (MD5_CTX*) enif_make_new_binary(env,MD5_CTX_LEN, &ret);
memcpy(new_ctx, ctx_bin.data, MD5_CTX_LEN);
MD5_Update(new_ctx, data_bin.data, data_bin.size);
+ CONSUME_REDS(env,data_bin);
return ret;
}
static ERL_NIF_TERM md5_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -820,10 +835,11 @@ static ERL_NIF_TERM ripemd160(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ERL_NIF_TERM ret;
if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
- return enif_make_badarg(env);
+ return enif_make_badarg(env);
}
RIPEMD160((unsigned char *) ibin.data, ibin.size,
- enif_make_new_binary(env,RIPEMD160_LEN, &ret));
+ enif_make_new_binary(env,RIPEMD160_LEN, &ret));
+ CONSUME_REDS(env,ibin);
return ret;
}
static ERL_NIF_TERM ripemd160_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -838,13 +854,14 @@ static ERL_NIF_TERM ripemd160_update(ErlNifEnv* env, int argc, const ERL_NIF_TER
ErlNifBinary ctx_bin, data_bin;
ERL_NIF_TERM ret;
if (!enif_inspect_binary(env, argv[0], &ctx_bin)
- || ctx_bin.size != RIPEMD160_CTX_LEN
- || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
- return enif_make_badarg(env);
+ || ctx_bin.size != RIPEMD160_CTX_LEN
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
+ return enif_make_badarg(env);
}
new_ctx = (RIPEMD160_CTX*) enif_make_new_binary(env,RIPEMD160_CTX_LEN, &ret);
memcpy(new_ctx, ctx_bin.data, RIPEMD160_CTX_LEN);
RIPEMD160_Update(new_ctx, data_bin.data, data_bin.size);
+ CONSUME_REDS(env, data_bin);
return ret;
}
static ERL_NIF_TERM ripemd160_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -853,7 +870,7 @@ static ERL_NIF_TERM ripemd160_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM
RIPEMD160_CTX ctx_clone;
ERL_NIF_TERM ret;
if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != RIPEMD160_CTX_LEN) {
- return enif_make_badarg(env);
+ return enif_make_badarg(env);
}
memcpy(&ctx_clone, ctx_bin.data, RIPEMD160_CTX_LEN); /* writable */
RIPEMD160_Final(enif_make_new_binary(env, RIPEMD160_LEN, &ret), &ctx_clone);
@@ -871,6 +888,7 @@ static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
}
SHA1((unsigned char *) ibin.data, ibin.size,
enif_make_new_binary(env,SHA_LEN, &ret));
+ CONSUME_REDS(env,ibin);
return ret;
}
static ERL_NIF_TERM sha_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -891,6 +909,7 @@ static ERL_NIF_TERM sha_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
new_ctx = (SHA_CTX*) enif_make_new_binary(env,SHA_CTX_LEN, &ret);
memcpy(new_ctx, ctx_bin.data, SHA_CTX_LEN);
SHA1_Update(new_ctx, data_bin.data, data_bin.size);
+ CONSUME_REDS(env,data_bin);
return ret;
}
static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -917,6 +936,7 @@ static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
}
SHA224((unsigned char *) ibin.data, ibin.size,
enif_make_new_binary(env,SHA224_LEN, &ret));
+ CONSUME_REDS(env,ibin);
return ret;
#else
return atom_notsup;
@@ -945,6 +965,7 @@ static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
new_ctx = (SHA256_CTX*) enif_make_new_binary(env,sizeof(SHA256_CTX), &ret);
memcpy(new_ctx, ctx_bin.data, sizeof(SHA256_CTX));
SHA224_Update(new_ctx, data_bin.data, data_bin.size);
+ CONSUME_REDS(env,data_bin);
return ret;
#else
return atom_notsup;
@@ -978,6 +999,7 @@ static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
}
SHA256((unsigned char *) ibin.data, ibin.size,
enif_make_new_binary(env,SHA256_LEN, &ret));
+ CONSUME_REDS(env,ibin);
return ret;
#else
return atom_notsup;
@@ -1006,6 +1028,7 @@ static ERL_NIF_TERM sha256_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
new_ctx = (SHA256_CTX*) enif_make_new_binary(env,sizeof(SHA256_CTX), &ret);
memcpy(new_ctx, ctx_bin.data, sizeof(SHA256_CTX));
SHA256_Update(new_ctx, data_bin.data, data_bin.size);
+ CONSUME_REDS(env,data_bin);
return ret;
#else
return atom_notsup;
@@ -1039,6 +1062,7 @@ static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
}
SHA384((unsigned char *) ibin.data, ibin.size,
enif_make_new_binary(env,SHA384_LEN, &ret));
+ CONSUME_REDS(env,ibin);
return ret;
#else
return atom_notsup;
@@ -1067,6 +1091,7 @@ static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
new_ctx = (SHA512_CTX*) enif_make_new_binary(env,sizeof(SHA512_CTX), &ret);
memcpy(new_ctx, ctx_bin.data, sizeof(SHA512_CTX));
SHA384_Update(new_ctx, data_bin.data, data_bin.size);
+ CONSUME_REDS(env,data_bin);
return ret;
#else
return atom_notsup;
@@ -1100,6 +1125,7 @@ static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
}
SHA512((unsigned char *) ibin.data, ibin.size,
enif_make_new_binary(env,SHA512_LEN, &ret));
+ CONSUME_REDS(env,ibin);
return ret;
#else
return atom_notsup;
@@ -1128,6 +1154,7 @@ static ERL_NIF_TERM sha512_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
new_ctx = (SHA512_CTX*) enif_make_new_binary(env,sizeof(SHA512_CTX), &ret);
memcpy(new_ctx, ctx_bin.data, sizeof(SHA512_CTX));
SHA512_Update(new_ctx, data_bin.data, data_bin.size);
+ CONSUME_REDS(env,data_bin);
return ret;
#else
return atom_notsup;
@@ -1161,6 +1188,7 @@ static ERL_NIF_TERM md4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
}
MD4((unsigned char *) ibin.data, ibin.size,
enif_make_new_binary(env,MD4_LEN, &ret));
+ CONSUME_REDS(env,ibin);
return ret;
}
static ERL_NIF_TERM md4_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -1181,6 +1209,7 @@ static ERL_NIF_TERM md4_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
new_ctx = (MD4_CTX*) enif_make_new_binary(env,MD4_CTX_LEN, &ret);
memcpy(new_ctx, ctx_bin.data, MD4_CTX_LEN);
MD4_Update(new_ctx, data_bin.data, data_bin.size);
+ CONSUME_REDS(env,data_bin);
return ret;
}
static ERL_NIF_TERM md4_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -1210,6 +1239,7 @@ static ERL_NIF_TERM md5_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
}
hmac_md5(key.data, key.size, data.data, data.size, hmacbuf);
memcpy(enif_make_new_binary(env, mac_sz, &ret), hmacbuf, mac_sz);
+ CONSUME_REDS(env,data);
return ret;
}
@@ -1228,6 +1258,7 @@ static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
hmac_sha1(key.data, key.size, data.data, data.size, hmacbuf);
memcpy(enif_make_new_binary(env, mac_sz, &ret),
hmacbuf, mac_sz);
+ CONSUME_REDS(env,data);
return ret;
}
@@ -1247,6 +1278,7 @@ static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
hmac_sha224(key.data, key.size, data.data, data.size, hmacbuf);
memcpy(enif_make_new_binary(env, mac_sz, &ret),
hmacbuf, mac_sz);
+ CONSUME_REDS(env,data);
return ret;
#else
return atom_notsup;
@@ -1269,6 +1301,7 @@ static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
hmac_sha256(key.data, key.size, data.data, data.size, hmacbuf);
memcpy(enif_make_new_binary(env, mac_sz, &ret),
hmacbuf, mac_sz);
+ CONSUME_REDS(env,data);
return ret;
#else
return atom_notsup;
@@ -1291,6 +1324,7 @@ static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
hmac_sha384(key.data, key.size, data.data, data.size, hmacbuf);
memcpy(enif_make_new_binary(env, mac_sz, &ret),
hmacbuf, mac_sz);
+ CONSUME_REDS(env,data);
return ret;
#else
return atom_notsup;
@@ -1314,6 +1348,7 @@ static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
hmac_sha512(key.data, key.size, data.data, data.size, hmacbuf);
memcpy(enif_make_new_binary(env, mac_sz, &ret),
hmacbuf, mac_sz);
+ CONSUME_REDS(env,data);
return ret;
#else
return atom_notsup;
@@ -1371,6 +1406,7 @@ static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
ctx_buf = enif_make_new_binary(env, sizeof(HMAC_CTX), &ret);
memcpy(ctx_buf, context.data, context.size);
HMAC_Update((HMAC_CTX *)ctx_buf, data.data, data.size);
+ CONSUME_REDS(env,data);
return ret;
}
@@ -1427,6 +1463,7 @@ static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
DES_set_key((const_DES_cblock*)key.data, &schedule);
DES_ncbc_encrypt(text.data, enif_make_new_binary(env, text.size, &ret),
text.size, &schedule, &ivec_clone, (argv[3] == atom_true));
+ CONSUME_REDS(env,text);
return ret;
}
@@ -1446,6 +1483,7 @@ static ERL_NIF_TERM des_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
DES_set_key((const_DES_cblock*)key.data, &schedule);
DES_cfb_encrypt(text.data, enif_make_new_binary(env, text.size, &ret),
8, text.size, &schedule, &ivec_clone, (argv[3] == atom_true));
+ CONSUME_REDS(env,text);
return ret;
}
@@ -1462,6 +1500,7 @@ static ERL_NIF_TERM des_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
DES_ecb_encrypt((const_DES_cblock*)text.data,
(DES_cblock*)enif_make_new_binary(env, 8, &ret),
&schedule, (argv[2] == atom_true));
+ CONSUME_REDS(env,text);
return ret;
}
@@ -1488,6 +1527,7 @@ static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_T
DES_ede3_cbc_encrypt(text.data, enif_make_new_binary(env,text.size,&ret),
text.size, &schedule1, &schedule2, &schedule3,
&ivec_clone, (argv[5] == atom_true));
+ CONSUME_REDS(env,text);
return ret;
}
@@ -1514,6 +1554,7 @@ static ERL_NIF_TERM des_ede3_cfb_crypt_nif(ErlNifEnv* env, int argc, const ERL_N
DES_ede3_cfb_encrypt(text.data, enif_make_new_binary(env,text.size,&ret),
8, text.size, &schedule1, &schedule2, &schedule3,
&ivec_clone, (argv[5] == atom_true));
+ CONSUME_REDS(env,text);
return ret;
#else
return atom_notsup;
@@ -1540,6 +1581,7 @@ static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE
enif_make_new_binary(env, text.size, &ret),
text.size, &aes_key, ivec_clone, &new_ivlen,
(argv[3] == atom_true));
+ CONSUME_REDS(env,text);
return ret;
}
@@ -1565,6 +1607,7 @@ static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM
AES_ctr128_encrypt((unsigned char *) text.data,
enif_make_new_binary(env, text.size, &ret),
text.size, &aes_key, ivec_clone, ecount_buf, &num);
+ CONSUME_REDS(env,text);
/* To do an incremental {en|de}cryption, the state to to keep between calls
must include ivec_clone, ecount_buf and num. */
@@ -1608,6 +1651,7 @@ static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_N
num2_term = enif_make_uint(env, num);
new_state_term = enif_make_tuple4(env, state_term[0], ivec2_term, ecount2_term, num2_term);
ret = enif_make_tuple2(env, new_state_term, cipher_term);
+ CONSUME_REDS(env,text_bin);
return ret;
}
@@ -2055,10 +2099,11 @@ static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
ret_ptr = enif_make_new_binary(env, data_bin.size, &ret);
memcpy(ivec, ivec_bin.data, 16); /* writable copy */
AES_cbc_encrypt(data_bin.data, ret_ptr, data_bin.size, &aes_key, ivec, i);
+ CONSUME_REDS(env,data_bin);
return ret;
}
-static ERL_NIF_TERM exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data1, Data2) */
ErlNifBinary d1, d2;
unsigned char* ret_ptr;
@@ -2075,6 +2120,7 @@ static ERL_NIF_TERM exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
for (i=0; i<d1.size; i++) {
ret_ptr[i] = d1.data[i] ^ d2.data[i];
}
+ CONSUME_REDS(env,d1);
return ret;
}
@@ -2091,6 +2137,7 @@ static ERL_NIF_TERM rc4_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
RC4_set_key(&rc4_key, key.size, key.data);
RC4(&rc4_key, data.size, data.data,
enif_make_new_binary(env, data.size, &ret));
+ CONSUME_REDS(env,data);
return ret;
}
@@ -2123,7 +2170,7 @@ static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_N
memcpy(rc4_key, state.data, sizeof(RC4_KEY));
RC4(rc4_key, data.size, data.data,
enif_make_new_binary(env, data.size, &new_data));
-
+ CONSUME_REDS(env,data);
return enif_make_tuple2(env,new_state,new_data);
}
@@ -2150,6 +2197,7 @@ static ERL_NIF_TERM rc2_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
data_bin.size, &rc2_key,
iv_copy,
(argv[3] == atom_true));
+ CONSUME_REDS(env,data_bin);
return ret;
}
@@ -2781,6 +2829,7 @@ static ERL_NIF_TERM bf_cfb64_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM
BF_cfb64_encrypt(data_bin.data, enif_make_new_binary(env,data_bin.size,&ret),
data_bin.size, &bf_key, bf_tkey, &bf_n,
(argv[3] == atom_true ? BF_ENCRYPT : BF_DECRYPT));
+ CONSUME_REDS(env,data_bin);
return ret;
}
@@ -2804,6 +2853,7 @@ static ERL_NIF_TERM bf_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
BF_cbc_encrypt(data_bin.data, enif_make_new_binary(env,data_bin.size,&ret),
data_bin.size, &bf_key, bf_tkey,
(argv[3] == atom_true ? BF_ENCRYPT : BF_DECRYPT));
+ CONSUME_REDS(env,data_bin);
return ret;
}
@@ -2821,6 +2871,7 @@ static ERL_NIF_TERM bf_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
BF_set_key(&bf_key, key_bin.size, key_bin.data);
BF_ecb_encrypt(data_bin.data, enif_make_new_binary(env,data_bin.size,&ret),
&bf_key, (argv[2] == atom_true ? BF_ENCRYPT : BF_DECRYPT));
+ CONSUME_REDS(env,data_bin);
return ret;
}
@@ -2843,6 +2894,7 @@ static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_N
memcpy(bf_tkey, ivec_bin.data, 8);
BF_ofb64_encrypt(data_bin.data, enif_make_new_binary(env,data_bin.size,&ret),
data_bin.size, &bf_key, bf_tkey, &bf_n);
+ CONSUME_REDS(env,data_bin);
return ret;
}
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 2df407018e..99d167bfa9 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -208,7 +208,7 @@
</type>
<desc>
<p>Computes the shared secret from the private key and the other party's public key.
- See also <seealso marker="public_key:public_key#compute_key/2">public_key:compute_key/2</seealso>
+ See also <seealso marker="public_key:public_key#compute_key-2">public_key:compute_key/2</seealso>
</p>
</desc>
</func>
@@ -240,7 +240,7 @@
</type>
<desc>
<p>Generates public keys of type <c>Type</c>.
- See also <seealso marker="public_key:public_key#generate_key/1">public_key:generate_key/1</seealso>
+ See also <seealso marker="public_key:public_key#generate_key-1">public_key:generate_key/1</seealso>
</p>
</desc>
</func>
@@ -269,7 +269,7 @@
<desc>
<p>Initializes the context for streaming hash operations. <c>Type</c> determines
which digest to use. The returned context should be used as argument
- to <seealso marker="#hash_update/2">hash_update</seealso>.</p>
+ to <seealso marker="#hash_update-2">hash_update</seealso>.</p>
<p>May throw exception <c>notsup</c> in case the chosen <c>Type</c>
is not supported by the underlying OpenSSL implementation.</p>
</desc>
@@ -283,10 +283,10 @@
</type>
<desc>
<p>Updates the digest represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c>
- must have been generated using <seealso marker="#hash_init/1">hash_init</seealso>
+ must have been generated using <seealso marker="#hash_init-1">hash_init</seealso>
or a previous call to this function. <c>Data</c> can be any length. <c>NewContext</c>
must be passed into the next call to <c>hash_update</c>
- or <seealso marker="#hash_final/1">hash_final</seealso>.</p>
+ or <seealso marker="#hash_final-1">hash_final</seealso>.</p>
</desc>
</func>
<func>
@@ -297,7 +297,7 @@
</type>
<desc>
<p>Finalizes the hash operation referenced by <c>Context</c> returned
- from a previous call to <seealso marker="#hash_update/2">hash_update</seealso>.
+ from a previous call to <seealso marker="#hash_update-2">hash_update</seealso>.
The size of <c>Digest</c> is determined by the type of hash
function used to generate it.</p>
</desc>
@@ -346,10 +346,10 @@
<desc>
<p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c>
must have been generated using an HMAC init function (such as
- <seealso marker="#hmac_init/2">hmac_init</seealso>). <c>Data</c> can be any length. <c>NewContext</c>
+ <seealso marker="#hmac_init-2">hmac_init</seealso>). <c>Data</c> can be any length. <c>NewContext</c>
must be passed into the next call to <c>hmac_update</c>
- or to one of the functions <seealso marker="#hmac_final/1">hmac_final</seealso> and
- <seealso marker="#hmac_final_n/1">hmac_final_n</seealso>
+ or to one of the functions <seealso marker="#hmac_final-1">hmac_final</seealso> and
+ <seealso marker="#hmac_final_n-2">hmac_final_n</seealso>
</p>
</desc>
@@ -447,36 +447,36 @@
<v>PlainText = binary()</v>
</type>
<desc>
- <p>Decrypts the <c>ChipherText</c> (usually a session key encrypted with
- <seealso marker="#public_encrypt/3">public_encrypt/3</seealso>)
- using the <c>PrivateKey</c> and returns the
- message. The <c>Padding</c> is the padding mode that was
- used to encrypt the data,
- see <seealso marker="#public_encrypt/3">public_encrypt/3</seealso>.
- See also <seealso marker="public_key:public_key#decrypt_private/2">public_key:decrypt_private/[2,3]</seealso>
+ <p>Decrypts the <c>ChipherText</c>, encrypted with
+ <seealso marker="#public_encrypt-4">public_encrypt/4</seealso> (or equivalent function)
+ using the <c>PrivateKey</c>, and returns the
+ plaintext (message digest). This is a low level signature verification operation
+ used for instance by older versions of the SSL protocol.
+ See also <seealso marker="public_key:public_key#decrypt_private-2">public_key:decrypt_private/[2,3]</seealso>
</p>
</desc>
</func>
-
+
<func>
<name>private_encrypt(Type, PlainText, PrivateKey, Padding) -> ChipherText</name>
- <fsummary>Encrypts Msg using the private Key.</fsummary>
+ <fsummary>Encrypts PlainText using the private Key.</fsummary>
<type>
<v>Type = rsa</v>
<v>PlainText = binary()</v>
+ <d> The size of the <c>PlainText</c> must be less
+ than <c>byte_size(N)-11</c> if <c>rsa_pkcs1_padding</c> is
+ used, and <c>byte_size(N)</c> if <c>rsa_no_padding</c> is
+ used, where N is public modulus of the RSA key.</d>
<v>PrivateKey = rsa_private()</v>
<v>Padding = rsa_pkcs1_padding | rsa_no_padding</v>
<v>ChipherText = binary()</v>
</type>
<desc>
<p>Encrypts the <c>PlainText</c> using the <c>PrivateKey</c>
- and returns the cipher. The <c>Padding</c> decides what padding mode is used,
- <c>rsa_pkcs1_padding</c> is PKCS #1 v1.5 currently the most
- used mode.
- The size of the <c>Msg</c> must be less than <c>byte_size(N)-11</c> if
- <c>rsa_pkcs1_padding</c> is used, and <c>byte_size(N)</c> if <c>rsa_no_padding</c>
- is used, where N is public modulus of the RSA key.
- See also <seealso marker="public_key:public_key#encrypt_private/2">public_key:encrypt_private/[2,3]</seealso>
+ and returns the ciphertext. This is a low level signature operation
+ used for instance by older versions of the SSL protocol. See
+ also <seealso
+ marker="public_key:public_key#encrypt_private-2">public_key:encrypt_private/[2,3]</seealso>
</p>
</desc>
</func>
@@ -491,42 +491,35 @@
<v>PlainText = binary()</v>
</type>
<desc>
- <p>Decrypts the <c>ChipherText</c> (encrypted with
- <seealso marker="#private_encrypt/3">private_encrypt/3</seealso>)
- using the <c>PrivateKey</c> and returns the
- message. The <c>Padding</c> is the padding mode that was
- used to encrypt the data,
- see <seealso marker="#private_encrypt/3">private_encrypt/3</seealso>.
- See also <seealso marker="public_key:public_key#decrypt_public/2">public_key:decrypt_public/[2,3]</seealso>
+ <p>Decrypts the <c>ChipherText</c>, encrypted with
+ <seealso marker="#private_encrypt-4">private_encrypt/4</seealso>(or equivalent function)
+ using the <c>PrivateKey</c>, and returns the
+ plaintext (message digest). This is a low level signature verification operation
+ used for instance by older versions of the SSL protocol.
+ See also <seealso marker="public_key:public_key#decrypt_public-2">public_key:decrypt_public/[2,3]</seealso>
</p>
</desc>
</func>
<func>
<name>public_encrypt(Type, PlainText, PublicKey, Padding) -> ChipherText</name>
- <fsummary>Encrypts Msg using the public Key.</fsummary>
+ <fsummary>Encrypts PlainText using the public Key.</fsummary>
<type>
<v>Type = rsa</v>
<v>PlainText = binary()</v>
+ <d> The size of the <c>PlainText</c> must be less
+ than <c>byte_size(N)-11</c> if <c>rsa_pkcs1_padding</c> is
+ used, and <c>byte_size(N)</c> if <c>rsa_no_padding</c> is
+ used, where N is public modulus of the RSA key.</d>
<v>PublicKey = rsa_public()</v>
<v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v>
<v>ChipherText = binary()</v>
</type>
<desc>
- <p>Encrypts the <c>PlainText</c> (usually a session key) using
- the <c>PublicKey</c> and returns the <c>CipherText</c>. The
- <c>Padding</c> decides what padding mode is used,
- <c>rsa_pkcs1_padding</c> is PKCS #1 v1.5 currently the most
- used mode and <c>rsa_pkcs1_oaep_padding</c> is EME-OAEP as
- defined in PKCS #1 v2.0 with SHA-1, MGF1 and an empty encoding
- parameter. This mode is recommended for all new
- applications. The size of the <c>Msg</c> must be less than
- <c>byte_size(N)-11</c> if <c>rsa_pkcs1_padding</c> is
- used, <c>byte_size(N)-41</c> if
- <c>rsa_pkcs1_oaep_padding</c> is used and
- <c>byte_size(N)</c> if <c>rsa_no_padding</c> is used, where N is public modulus of the RSA key.
- See also <seealso
- marker="public_key:public_key#encrypt_public/2">public_key:encrypt_public/[2,3]</seealso>
+ <p>Encrypts the <c>PlainText</c> (message digest) using the <c>PublicKey</c>
+ and returns the <c>CipherText</c>. This is a low level signature operation
+ used for instance by older versions of the SSL protocol. See also <seealso
+ marker="public_key:public_key#encrypt_public-2">public_key:encrypt_public/[2,3]</seealso>
</p>
</desc>
</func>
@@ -563,9 +556,9 @@
<type>
<v>Algorithm = rsa | dss | ecdsa </v>
<v>Msg = binary() | {digest,binary()}</v>
- <d>The msg is either the binary "plain text" data to be
- signed or it is the hashed value of "plain text" i.e. the
- digest.</d>
+ <d>The msg is either the binary "cleartext" data to be
+ signed or it is the hashed value of "cleartext" i.e. the
+ digest (plaintext).</d>
<v>DigestType = digest_type()</v>
<v>Key = rsa_private() | dss_private() | [ecdh_private(),ecdh_params()]</v>
</type>
@@ -573,7 +566,7 @@
<p>Creates a digital signature.</p>
<p>Algorithm <c>dss</c> can only be used together with digest type
<c>sha</c>.</p>
- See also <seealso marker="public_key:public_key#sign/3">public_key:sign/3</seealso>
+ See also <seealso marker="public_key:public_key#sign-3">public_key:sign/3</seealso>
</desc>
</func>
@@ -617,8 +610,8 @@
</type>
<desc>
<p>Initializes the state for use in RC4 stream encryption
- <seealso marker="#stream_encrypt/2">stream_encrypt</seealso> and
- <seealso marker="#stream_decrypt/2">stream_decrypt</seealso></p>
+ <seealso marker="#stream_encrypt-2">stream_encrypt</seealso> and
+ <seealso marker="#stream_decrypt-2">stream_decrypt</seealso></p>
</desc>
</func>
@@ -635,8 +628,8 @@
<p>Initializes the state for use in streaming AES encryption using Counter mode (CTR).
<c>Key</c> is the AES key and must be either 128, 192, or 256 bts long. <c>IVec</c> is
an arbitrary initializing vector of 128 bits (16 bytes). This state is for use with
- <seealso marker="#stream_encrypt/2">stream_encrypt</seealso> and
- <seealso marker="#stream_decrypt/2">stream_decrypt</seealso>.</p>
+ <seealso marker="#stream_encrypt-2">stream_encrypt</seealso> and
+ <seealso marker="#stream_decrypt-2">stream_decrypt</seealso>.</p>
</desc>
</func>
@@ -650,7 +643,7 @@
<desc>
<p>Encrypts <c>PlainText</c> according to the stream cipher <c>Type</c> specified in stream_init/3.
<c>Text</c> can be any number of bytes. The initial <c>State</c> is created using
- <seealso marker="#stream_init/2">stream_init</seealso>.
+ <seealso marker="#stream_init-2">stream_init</seealso>.
<c>NewState</c> must be passed into the next call to <c>stream_encrypt</c>.</p>
</desc>
</func>
@@ -665,7 +658,7 @@
<desc>
<p>Decrypts <c>CipherText</c> according to the stream cipher <c>Type</c> specified in stream_init/3.
<c>PlainText</c> can be any number of bytes. The initial <c>State</c> is created using
- <seealso marker="#stream_init/2">stream_init</seealso>.
+ <seealso marker="#stream_init-2">stream_init</seealso>.
<c>NewState</c> must be passed into the next call to <c>stream_encrypt</c>.</p>
</desc>
</func>
@@ -692,8 +685,8 @@
<type>
<v> Algorithm = rsa | dss | ecdsa </v>
<v>Msg = binary() | {digest,binary()}</v>
- <d>The msg is either the binary "plain text" data
- or it is the hashed value of "plain text" i.e. the digest.</d>
+ <d>The msg is either the binary "cleartext" data
+ or it is the hashed value of "cleartext" i.e. the digest (plaintext).</d>
<v>DigestType = digest_type()</v>
<v>Signature = binary()</v>
<v>Key = rsa_public() | dss_public() | [ecdh_public(),ecdh_params()]</v>
@@ -703,7 +696,7 @@
<p>Algorithm <c>dss</c> can only be used together with digest type
<c>sha</c>.</p>
- See also <seealso marker="public_key:public_key#sign/3">public_key:verify/3</seealso>
+ See also <seealso marker="public_key:public_key#verify-4">public_key:verify/4</seealso>
</desc>
</func>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index e042545094..a093b45410 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -35,7 +35,6 @@
-export([private_encrypt/4, public_decrypt/4]).
-export([dh_generate_parameters/2, dh_check/1]). %% Testing see
-
%% DEPRECATED
%% Replaced by hash_*
-export([md4/1, md4_init/0, md4_update/2, md4_final/1]).
@@ -165,60 +164,8 @@
-export([info/0]).
-deprecated({info, 0, next_major_release}).
--define(FUNC_LIST, [hash, hash_init, hash_update, hash_final,
- hmac, hmac_init, hmac_update, hmac_final, hmac_final_n,
- %% deprecated
- md4, md4_init, md4_update, md4_final,
- md5, md5_init, md5_update, md5_final,
- sha, sha_init, sha_update, sha_final,
- md5_mac, md5_mac_96,
- sha_mac, sha_mac_96,
- %%
- block_encrypt, block_decrypt,
- %% deprecated
- des_cbc_encrypt, des_cbc_decrypt,
- des_cfb_encrypt, des_cfb_decrypt,
- des_ecb_encrypt, des_ecb_decrypt,
- des3_cbc_encrypt, des3_cbc_decrypt,
- des3_cfb_encrypt, des3_cfb_decrypt,
- aes_cfb_128_encrypt, aes_cfb_128_decrypt,
- rc2_cbc_encrypt, rc2_cbc_decrypt,
- rc2_40_cbc_encrypt, rc2_40_cbc_decrypt,
- aes_cbc_128_encrypt, aes_cbc_128_decrypt,
- aes_cbc_256_encrypt, aes_cbc_256_decrypt,
- blowfish_cbc_encrypt, blowfish_cbc_decrypt,
- blowfish_cfb64_encrypt, blowfish_cfb64_decrypt,
- blowfish_ecb_encrypt, blowfish_ecb_decrypt, blowfish_ofb64_encrypt,
- %%
- rand_bytes,
- strong_rand_bytes,
- rand_uniform,
- mod_pow,
- exor,
- %% deprecated
- mod_exp,strong_rand_mpint,erlint, mpint,
- %%
- sign, verify, generate_key, compute_key,
- %% deprecated
- dss_verify,dss_sign,
- rsa_verify,rsa_sign,
- rsa_public_encrypt,rsa_private_decrypt,
- rsa_private_encrypt,rsa_public_decrypt,
- dh_generate_key, dh_compute_key,
- %%
- stream_init, stream_encrypt, stream_decrypt,
- %% deprecated
- rc4_encrypt, rc4_set_key, rc4_encrypt_with_state,
- aes_ctr_encrypt, aes_ctr_decrypt,
- aes_ctr_stream_init, aes_ctr_stream_encrypt, aes_ctr_stream_decrypt,
- %%
- next_iv,
- %% deprecated
- aes_cbc_ivec,
- des_cbc_ivec, des_cfb_ivec,
- info,
- %%
- info_lib, supports]).
+%% This should correspond to the similar macro in crypto.c
+-define(MAX_BYTES_TO_NIF, 20000). %% Current value is: erlang:system_info(context_reductions) * 10
-type mpint() :: binary().
-type rsa_digest_type() :: 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'.
@@ -235,76 +182,27 @@
%%-type ec_curve() :: ec_named_curve() | ec_curve_spec().
%%-type ec_key() :: {Curve :: ec_curve(), PrivKey :: binary() | undefined, PubKey :: ec_point() | undefined}.
--define(nif_stub,nif_stub_error(?LINE)).
-
-on_load(on_load/0).
-
-define(CRYPTO_NIF_VSN,201).
-on_load() ->
- LibBaseName = "crypto",
- PrivDir = code:priv_dir(crypto),
- LibName = case erlang:system_info(build_type) of
- opt ->
- LibBaseName;
- Type ->
- LibTypeName = LibBaseName ++ "." ++ atom_to_list(Type),
- case (filelib:wildcard(
- filename:join(
- [PrivDir,
- "lib",
- LibTypeName ++ "*"])) /= []) orelse
- (filelib:wildcard(
- filename:join(
- [PrivDir,
- "lib",
- erlang:system_info(system_architecture),
- LibTypeName ++ "*"])) /= []) of
- true -> LibTypeName;
- false -> LibBaseName
- end
- end,
- Lib = filename:join([PrivDir, "lib", LibName]),
- Status = case erlang:load_nif(Lib, {?CRYPTO_NIF_VSN,Lib}) of
- ok -> ok;
- {error, {load_failed, _}}=Error1 ->
- ArchLibDir =
- filename:join([PrivDir, "lib",
- erlang:system_info(system_architecture)]),
- Candidate =
- filelib:wildcard(filename:join([ArchLibDir,LibName ++ "*" ])),
- case Candidate of
- [] -> Error1;
- _ ->
- ArchLib = filename:join([ArchLibDir, LibName]),
- erlang:load_nif(ArchLib, {?CRYPTO_NIF_VSN,ArchLib})
- end;
- Error1 -> Error1
- end,
- case Status of
- ok -> ok;
- {error, {E, Str}} ->
- error_logger:error_msg("Unable to load crypto library. Failed with error:~n\"~p, ~s\"~n"
- "OpenSSL might not be installed on this system.~n",[E,Str]),
- Status
- end.
-
+-define(nif_stub,nif_stub_error(?LINE)).
nif_stub_error(Line) ->
erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}).
+%%--------------------------------------------------------------------
+%%% API
+%%--------------------------------------------------------------------
+%% Crypto app version history:
+%% (no version): Driver implementation
+%% 2.0 : NIF implementation, requires OTP R14
+version() -> ?CRYPTO_VSN.
+
start() ->
application:start(crypto).
stop() ->
application:stop(crypto).
-info() ->
- ?FUNC_LIST.
-
-info_lib() -> ?nif_stub.
-
-algorithms() -> ?nif_stub.
-
supports()->
Algs = algorithms(),
PubKeyAlgs =
@@ -316,32 +214,20 @@ supports()->
end,
[{hashs, Algs -- [ec]},
{ciphers, [des_cbc, des_cfb, des3_cbc, des3_cbf, des_ede3, blowfish_cbc,
- blowfish_cfb64, blowfish_ofb64, blowfish_ecb, aes_cbc128, aes_cfb128, aes_cbc256, rc2_cbc, aes_ctr, rc4
+ blowfish_cfb64, blowfish_ofb64, blowfish_ecb, aes_cbc128, aes_cfb128,
+ aes_cbc256, rc2_cbc, aes_ctr, rc4
]},
PubKeyAlgs
].
-%% Crypto app version history:
-%% (no version): Driver implementation
-%% 2.0 : NIF implementation, requires OTP R14
-version() -> ?CRYPTO_VSN.
-
-%% Below Key and Data are binaries or IO-lists. IVec is a binary.
-%% Output is always a binary. Context is a binary.
-
-%%
-%% MESSAGE DIGESTS
-%%
+info_lib() -> ?nif_stub.
-spec hash(_, iodata()) -> binary().
-hash(md5, Data) -> md5(Data);
-hash(md4, Data) -> md4(Data);
-hash(sha, Data) -> sha(Data);
-hash(ripemd160, Data) -> ripemd160(Data);
-hash(sha224, Data) -> sha224(Data);
-hash(sha256, Data) -> sha256(Data);
-hash(sha384, Data) -> sha384(Data);
-hash(sha512, Data) -> sha512(Data).
+
+hash(Hash, Data0) ->
+ Data = iolist_to_binary(Data0),
+ MaxByts = max_bytes(),
+ hash(Hash, Data, erlang:byte_size(Data), MaxByts, initial).
-spec hash_init('md5'|'md4'|'ripemd160'|
'sha'|'sha224'|'sha256'|'sha384'|'sha512') -> any().
@@ -357,14 +243,10 @@ hash_init(sha512) -> {sha512, sha512_init()}.
-spec hash_update(_, iodata()) -> any().
-hash_update({md5,Context}, Data) -> {md5, md5_update(Context,Data)};
-hash_update({md4,Context}, Data) -> {md4, md4_update(Context,Data)};
-hash_update({sha,Context}, Data) -> {sha, sha_update(Context,Data)};
-hash_update({ripemd160,Context}, Data) -> {ripemd160, ripemd160_update(Context,Data)};
-hash_update({sha224,Context}, Data) -> {sha224, sha224_update(Context,Data)};
-hash_update({sha256,Context}, Data) -> {sha256, sha256_update(Context,Data)};
-hash_update({sha384,Context}, Data) -> {sha384, sha384_update(Context,Data)};
-hash_update({sha512,Context}, Data) -> {sha512, sha512_update(Context,Data)}.
+hash_update(State, Data0) ->
+ Data = iolist_to_binary(Data0),
+ MaxBytes = max_bytes(),
+ hash_update(State, Data, erlang:byte_size(Data), MaxBytes).
-spec hash_final(_) -> binary().
@@ -377,6 +259,457 @@ hash_final({sha256,Context}) -> sha256_final(Context);
hash_final({sha384,Context}) -> sha384_final(Context);
hash_final({sha512,Context}) -> sha512_final(Context).
+
+-spec hmac(_, iodata(), iodata()) -> binary().
+-spec hmac(_, iodata(), iodata(), integer()) -> binary().
+-spec hmac_init(atom(), iodata()) -> binary().
+-spec hmac_update(binary(), iodata()) -> binary().
+-spec hmac_final(binary()) -> binary().
+-spec hmac_final_n(binary(), integer()) -> binary().
+
+hmac(Type, Key, Data0) ->
+ Data = iolist_to_binary(Data0),
+ hmac(Type, Key, Data, undefined, erlang:byte_size(Data), max_bytes(), initial).
+hmac(Type, Key, Data0, MacSize) ->
+ Data = iolist_to_binary(Data0),
+ hmac(Type, Key, Data, MacSize, erlang:byte_size(Data), max_bytes(), initial).
+
+
+hmac_init(_Type, _Key) -> ?nif_stub.
+
+hmac_update(State, Data0) ->
+ Data = iolist_to_binary(Data0),
+ hmac_update(State, Data, erlang:byte_size(Data), max_bytes()).
+hmac_final(_Context) -> ? nif_stub.
+hmac_final_n(_Context, _HashLen) -> ? nif_stub.
+
+%% Ecrypt/decrypt %%%
+
+-spec block_encrypt(des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | blowfish_cbc |
+ blowfish_cfb64 | aes_cbc128 | aes_cfb128 | aes_cbc256 | rc2_cbc,
+ Key::iodata(), Ivec::binary(), Data::iodata()) -> binary().
+
+block_encrypt(des_cbc, Key, Ivec, Data) ->
+ des_cbc_encrypt(Key, Ivec, Data);
+block_encrypt(des_cfb, Key, Ivec, Data) ->
+ des_cfb_encrypt(Key, Ivec, Data);
+block_encrypt(des3_cbc, [Key1, Key2, Key3], Ivec, Data) ->
+ des3_cbc_encrypt(Key1, Key2, Key3, Ivec, Data);
+block_encrypt(des3_cbf, [Key1, Key2, Key3], Ivec, Data) ->
+ des3_cfb_encrypt(Key1, Key2, Key3, Ivec, Data);
+block_encrypt(des_ede3, [Key1, Key2, Key3], Ivec, Data) ->
+ des_ede3_cbc_encrypt(Key1, Key2, Key3, Ivec, Data);
+block_encrypt(blowfish_cbc, Key, Ivec, Data) ->
+ blowfish_cbc_encrypt(Key, Ivec, Data);
+block_encrypt(blowfish_cfb64, Key, Ivec, Data) ->
+ blowfish_cfb64_encrypt(Key, Ivec, Data);
+block_encrypt(blowfish_ofb64, Key, Ivec, Data) ->
+ blowfish_ofb64_encrypt(Key, Ivec, Data);
+block_encrypt(aes_cbc128, Key, Ivec, Data) ->
+ aes_cbc_128_encrypt(Key, Ivec, Data);
+block_encrypt(aes_cbc256, Key, Ivec, Data) ->
+ aes_cbc_256_encrypt(Key, Ivec, Data);
+block_encrypt(aes_cfb128, Key, Ivec, Data) ->
+ aes_cfb_128_encrypt(Key, Ivec, Data);
+block_encrypt(rc2_cbc, Key, Ivec, Data) ->
+ rc2_cbc_encrypt(Key, Ivec, Data).
+
+-spec block_decrypt(des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | blowfish_cbc |
+ blowfish_cfb64 | blowfish_ofb64 | aes_cbc128 | aes_cbc256 | aes_cfb128 | rc2_cbc,
+ Key::iodata(), Ivec::binary(), Data::iodata()) -> binary().
+
+block_decrypt(des_cbc, Key, Ivec, Data) ->
+ des_cbc_decrypt(Key, Ivec, Data);
+block_decrypt(des_cfb, Key, Ivec, Data) ->
+ des_cfb_decrypt(Key, Ivec, Data);
+block_decrypt(des3_cbc, [Key1, Key2, Key3], Ivec, Data) ->
+ des3_cbc_decrypt(Key1, Key2, Key3, Ivec, Data);
+block_decrypt(des3_cbf, [Key1, Key2, Key3], Ivec, Data) ->
+ des3_cfb_decrypt(Key1, Key2, Key3, Ivec, Data);
+block_decrypt(des_ede3, [Key1, Key2, Key3], Ivec, Data) ->
+ des_ede3_cbc_decrypt(Key1, Key2, Key3, Ivec, Data);
+block_decrypt(blowfish_cbc, Key, Ivec, Data) ->
+ blowfish_cbc_decrypt(Key, Ivec, Data);
+block_decrypt(blowfish_cfb64, Key, Ivec, Data) ->
+ blowfish_cfb64_decrypt(Key, Ivec, Data);
+block_decrypt(blowfish_ofb64, Key, Ivec, Data) ->
+ blowfish_ofb64_decrypt(Key, Ivec, Data);
+block_decrypt(aes_cbc128, Key, Ivec, Data) ->
+ aes_cbc_128_decrypt(Key, Ivec, Data);
+block_decrypt(aes_cbc256, Key, Ivec, Data) ->
+ aes_cbc_256_decrypt(Key, Ivec, Data);
+block_decrypt(aes_cfb128, Key, Ivec, Data) ->
+ aes_cfb_128_decrypt(Key, Ivec, Data);
+block_decrypt(rc2_cbc, Key, Ivec, Data) ->
+ rc2_cbc_decrypt(Key, Ivec, Data).
+
+-spec block_encrypt(des_ecb | blowfish_ecb, Key::iodata(), Data::iodata()) -> binary().
+
+block_encrypt(des_ecb, Key, Data) ->
+ des_ecb_encrypt(Key, Data);
+block_encrypt(blowfish_ecb, Key, Data) ->
+ blowfish_ecb_encrypt(Key, Data).
+
+-spec block_decrypt(des_ecb | blowfish_ecb, Key::iodata(), Data::iodata()) -> binary().
+
+block_decrypt(des_ecb, Key, Data) ->
+ des_ecb_decrypt(Key, Data);
+block_decrypt(blowfish_ecb, Key, Data) ->
+ blowfish_ecb_decrypt(Key, Data).
+
+-spec next_iv(des_cbc | des3_cbc | aes_cbc, Data::iodata()) -> binary().
+
+next_iv(des_cbc, Data) ->
+ des_cbc_ivec(Data);
+next_iv(des3_cbc, Data) ->
+ des_cbc_ivec(Data);
+next_iv(aes_cbc, Data) ->
+ aes_cbc_ivec(Data).
+
+-spec next_iv(des_cfb, Data::iodata(), Ivec::binary()) -> binary().
+
+next_iv(des_cfb, Data, Ivec) ->
+ des_cfb_ivec(Ivec, Data);
+next_iv(Type, Data, _Ivec) ->
+ next_iv(Type, Data).
+
+stream_init(aes_ctr, Key, Ivec) ->
+ {aes_ctr, aes_ctr_stream_init(Key, Ivec)}.
+stream_init(rc4, Key) ->
+ {rc4, rc4_set_key(Key)}.
+
+stream_encrypt(State, Data0) ->
+ Data = iolist_to_binary(Data0),
+ MaxByts = max_bytes(),
+ stream_crypt(fun do_stream_encrypt/2, State, Data, erlang:byte_size(Data), MaxByts, []).
+
+stream_decrypt(State, Data0) ->
+ Data = iolist_to_binary(Data0),
+ MaxByts = max_bytes(),
+ stream_crypt(fun do_stream_decrypt/2, State, Data, erlang:byte_size(Data), MaxByts, []).
+
+%%
+%% RAND - pseudo random numbers using RN_ functions in crypto lib
+%%
+-spec rand_bytes(non_neg_integer()) -> binary().
+-spec strong_rand_bytes(non_neg_integer()) -> binary().
+-spec rand_uniform(crypto_integer(), crypto_integer()) ->
+ crypto_integer().
+
+rand_bytes(_Bytes) -> ?nif_stub.
+
+strong_rand_bytes(Bytes) ->
+ case strong_rand_bytes_nif(Bytes) of
+ false -> erlang:error(low_entropy);
+ Bin -> Bin
+ end.
+strong_rand_bytes_nif(_Bytes) -> ?nif_stub.
+
+rand_bytes(_Bytes, _Topmask, _Bottommask) -> ?nif_stub.
+
+
+rand_uniform(From,To) when is_binary(From), is_binary(To) ->
+ case rand_uniform_nif(From,To) of
+ <<Len:32/integer, MSB, Rest/binary>> when MSB > 127 ->
+ <<(Len + 1):32/integer, 0, MSB, Rest/binary>>;
+ Whatever ->
+ Whatever
+ end;
+rand_uniform(From,To) when is_integer(From),is_integer(To) ->
+ if From < 0 ->
+ rand_uniform_pos(0, To - From) + From;
+ true ->
+ rand_uniform_pos(From, To)
+ end.
+
+rand_uniform_pos(From,To) when From < To ->
+ BinFrom = mpint(From),
+ BinTo = mpint(To),
+ case rand_uniform(BinFrom, BinTo) of
+ Result when is_binary(Result) ->
+ erlint(Result);
+ Other ->
+ Other
+ end;
+rand_uniform_pos(_,_) ->
+ error(badarg).
+
+rand_uniform_nif(_From,_To) -> ?nif_stub.
+
+
+-spec mod_pow(binary()|integer(), binary()|integer(), binary()|integer()) -> binary() | error.
+mod_pow(Base, Exponent, Prime) ->
+ case mod_exp_nif(ensure_int_as_bin(Base), ensure_int_as_bin(Exponent), ensure_int_as_bin(Prime), 0) of
+ <<0>> -> error;
+ R -> R
+ end.
+verify(dss, none, Data, Signature, Key) when is_binary(Data) ->
+ verify(dss, sha, {digest, Data}, Signature, Key);
+verify(Alg, Type, Data, Signature, Key) when is_binary(Data) ->
+ verify(Alg, Type, {digest, hash(Type, Data)}, Signature, Key);
+verify(dss, Type, Data, Signature, Key) ->
+ dss_verify_nif(Type, Data, Signature, map_ensure_int_as_bin(Key));
+verify(rsa, Type, DataOrDigest, Signature, Key) ->
+ case rsa_verify_nif(Type, DataOrDigest, Signature, map_ensure_int_as_bin(Key)) of
+ notsup -> erlang:error(notsup);
+ Bool -> Bool
+ end;
+verify(ecdsa, Type, DataOrDigest, Signature, [Key, Curve]) ->
+ case ecdsa_verify_nif(Type, DataOrDigest, Signature, term_to_ec_key(Curve, undefined, Key)) of
+ notsup -> erlang:error(notsup);
+ Bool -> Bool
+ end.
+sign(dss, none, Data, Key) when is_binary(Data) ->
+ sign(dss, sha, {digest, Data}, Key);
+sign(Alg, Type, Data, Key) when is_binary(Data) ->
+ sign(Alg, Type, {digest, hash(Type, Data)}, Key);
+sign(rsa, Type, DataOrDigest, Key) ->
+ case rsa_sign_nif(Type, DataOrDigest, map_ensure_int_as_bin(Key)) of
+ error -> erlang:error(badkey, [Type,DataOrDigest,Key]);
+ Sign -> Sign
+ end;
+sign(dss, Type, DataOrDigest, Key) ->
+ case dss_sign_nif(Type, DataOrDigest, map_ensure_int_as_bin(Key)) of
+ error -> erlang:error(badkey, [DataOrDigest, Key]);
+ Sign -> Sign
+ end;
+sign(ecdsa, Type, DataOrDigest, [Key, Curve]) ->
+ case ecdsa_sign_nif(Type, DataOrDigest, term_to_ec_key(Curve, Key, undefined)) of
+ error -> erlang:error(badkey, [Type,DataOrDigest,Key]);
+ Sign -> Sign
+ end.
+
+-spec public_encrypt(rsa, binary(), [binary()], rsa_padding()) ->
+ binary().
+-spec public_decrypt(rsa, binary(), [integer() | binary()], rsa_padding()) ->
+ binary().
+-spec private_encrypt(rsa, binary(), [integer() | binary()], rsa_padding()) ->
+ binary().
+-spec private_decrypt(rsa, binary(), [integer() | binary()], rsa_padding()) ->
+ binary().
+
+public_encrypt(rsa, BinMesg, Key, Padding) ->
+ case rsa_public_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, true) of
+ error ->
+ erlang:error(encrypt_failed, [BinMesg,Key, Padding]);
+ Sign -> Sign
+ end.
+
+%% Binary, Key = [E,N,D]
+private_decrypt(rsa, BinMesg, Key, Padding) ->
+ case rsa_private_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, false) of
+ error ->
+ erlang:error(decrypt_failed, [BinMesg,Key, Padding]);
+ Sign -> Sign
+ end.
+
+
+%% Binary, Key = [E,N,D]
+private_encrypt(rsa, BinMesg, Key, Padding) ->
+ case rsa_private_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, true) of
+ error ->
+ erlang:error(encrypt_failed, [BinMesg,Key, Padding]);
+ Sign -> Sign
+ end.
+
+%% Binary, Key = [E,N]
+public_decrypt(rsa, BinMesg, Key, Padding) ->
+ case rsa_public_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, false) of
+ error ->
+ erlang:error(decrypt_failed, [BinMesg,Key, Padding]);
+ Sign -> Sign
+ end.
+
+%%
+%% XOR - xor to iolists and return a binary
+%% NB doesn't check that they are the same size, just concatenates
+%% them and sends them to the driver
+%%
+-spec exor(iodata(), iodata()) -> binary().
+
+exor(Bin1, Bin2) ->
+ Data1 = iolist_to_binary(Bin1),
+ Data2 = iolist_to_binary(Bin2),
+ MaxBytes = max_bytes(),
+ exor(Data1, Data2, erlang:byte_size(Data1), MaxBytes, []).
+
+generate_key(Type, Params) ->
+ generate_key(Type, Params, undefined).
+
+generate_key(dh, DHParameters, PrivateKey) ->
+ dh_generate_key_nif(ensure_int_as_bin(PrivateKey),
+ map_ensure_int_as_bin(DHParameters), 0);
+
+generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, PrivArg)
+ when is_binary(Verifier), is_binary(Generator), is_binary(Prime), is_atom(Version) ->
+ Private = case PrivArg of
+ undefined -> random_bytes(32);
+ _ -> ensure_int_as_bin(PrivArg)
+ end,
+ host_srp_gen_key(Private, Verifier, Generator, Prime, Version);
+
+generate_key(srp, {user, [Generator, Prime, Version]}, PrivateArg)
+ when is_binary(Generator), is_binary(Prime), is_atom(Version) ->
+ Private = case PrivateArg of
+ undefined -> random_bytes(32);
+ _ -> PrivateArg
+ end,
+ user_srp_gen_key(Private, Generator, Prime);
+
+generate_key(ecdh, Curve, undefined) ->
+ ec_key_to_term_nif(ec_key_generate(Curve)).
+
+
+compute_key(dh, OthersPublicKey, MyPrivateKey, DHParameters) ->
+ case dh_compute_key_nif(ensure_int_as_bin(OthersPublicKey),
+ ensure_int_as_bin(MyPrivateKey),
+ map_ensure_int_as_bin(DHParameters)) of
+ error -> erlang:error(computation_failed,
+ [OthersPublicKey,MyPrivateKey,DHParameters]);
+ Ret -> Ret
+ end;
+
+compute_key(srp, HostPublic, {UserPublic, UserPrivate},
+ {user, [DerivedKey, Prime, Generator, Version | ScramblerArg]}) when
+ is_binary(Prime),
+ is_binary(Generator),
+ is_atom(Version) ->
+ HostPubBin = ensure_int_as_bin(HostPublic),
+ Multiplier = srp_multiplier(Version, Generator, Prime),
+ Scrambler = case ScramblerArg of
+ [] -> srp_scrambler(Version, ensure_int_as_bin(UserPublic),
+ HostPubBin, Prime);
+ [S] -> S
+ end,
+ srp_user_secret_nif(ensure_int_as_bin(UserPrivate), Scrambler, HostPubBin,
+ Multiplier, Generator, DerivedKey, Prime);
+
+compute_key(srp, UserPublic, {HostPublic, HostPrivate},
+ {host,[Verifier, Prime, Version | ScramblerArg]}) when
+ is_binary(Verifier),
+ is_binary(Prime),
+ is_atom(Version) ->
+ UserPubBin = ensure_int_as_bin(UserPublic),
+ Scrambler = case ScramblerArg of
+ [] -> srp_scrambler(Version, UserPubBin, ensure_int_as_bin(HostPublic), Prime);
+ [S] -> S
+ end,
+ srp_host_secret_nif(Verifier, ensure_int_as_bin(HostPrivate), Scrambler,
+ UserPubBin, Prime);
+
+compute_key(ecdh, Others, My, Curve) ->
+ ecdh_compute_key_nif(ensure_int_as_bin(Others),
+ term_to_ec_key(Curve,My,undefined)).
+
+
+random_bytes(N) ->
+ try strong_rand_bytes(N) of
+ RandBytes ->
+ RandBytes
+ catch
+ error:low_entropy ->
+ rand_bytes(N)
+ end.
+
+%%--------------------------------------------------------------------
+%%% On load
+%%--------------------------------------------------------------------
+
+on_load() ->
+ LibBaseName = "crypto",
+ PrivDir = code:priv_dir(crypto),
+ LibName = case erlang:system_info(build_type) of
+ opt ->
+ LibBaseName;
+ Type ->
+ LibTypeName = LibBaseName ++ "." ++ atom_to_list(Type),
+ case (filelib:wildcard(
+ filename:join(
+ [PrivDir,
+ "lib",
+ LibTypeName ++ "*"])) /= []) orelse
+ (filelib:wildcard(
+ filename:join(
+ [PrivDir,
+ "lib",
+ erlang:system_info(system_architecture),
+ LibTypeName ++ "*"])) /= []) of
+ true -> LibTypeName;
+ false -> LibBaseName
+ end
+ end,
+ Lib = filename:join([PrivDir, "lib", LibName]),
+ Status = case erlang:load_nif(Lib, {?CRYPTO_NIF_VSN,Lib}) of
+ ok -> ok;
+ {error, {load_failed, _}}=Error1 ->
+ ArchLibDir =
+ filename:join([PrivDir, "lib",
+ erlang:system_info(system_architecture)]),
+ Candidate =
+ filelib:wildcard(filename:join([ArchLibDir,LibName ++ "*" ])),
+ case Candidate of
+ [] -> Error1;
+ _ ->
+ ArchLib = filename:join([ArchLibDir, LibName]),
+ erlang:load_nif(ArchLib, {?CRYPTO_NIF_VSN,ArchLib})
+ end;
+ Error1 -> Error1
+ end,
+ case Status of
+ ok -> ok;
+ {error, {E, Str}} ->
+ error_logger:error_msg("Unable to load crypto library. Failed with error:~n\"~p, ~s\"~n"
+ "OpenSSL might not be installed on this system.~n",[E,Str]),
+ Status
+ end.
+%%--------------------------------------------------------------------
+%%% Internal functions (some internal API functions are part of the deprecated API)
+%%--------------------------------------------------------------------
+max_bytes() ->
+ ?MAX_BYTES_TO_NIF.
+
+%% HASH --------------------------------------------------------------------
+hash(Hash, Data, Size, Max, initial) when Size =< Max ->
+ do_hash(Hash, Data);
+hash(State0, Data, Size, Max, continue) when Size =< Max ->
+ State = do_hash_update(State0, Data),
+ hash_final(State);
+hash(Hash, Data, _Size, Max, initial) ->
+ <<Increment:Max/binary, Rest/binary>> = Data,
+ State0 = hash_init(Hash),
+ State = do_hash_update(State0, Increment),
+ hash(State, Rest, erlang:byte_size(Rest), max_bytes(), continue);
+hash(State0, Data, _Size, MaxByts, continue) ->
+ <<Increment:MaxByts/binary, Rest/binary>> = Data,
+ State = do_hash_update(State0, Increment),
+ hash(State, Rest, erlang:byte_size(Rest), max_bytes(), continue).
+
+do_hash(md5, Data) -> md5(Data);
+do_hash(md4, Data) -> md4(Data);
+do_hash(sha, Data) -> sha(Data);
+do_hash(ripemd160, Data) -> ripemd160(Data);
+do_hash(sha224, Data) -> sha224(Data);
+do_hash(sha256, Data) -> sha256(Data);
+do_hash(sha384, Data) -> sha384(Data);
+do_hash(sha512, Data) -> sha512(Data).
+
+hash_update(State, Data, Size, MaxBytes) when Size =< MaxBytes ->
+ do_hash_update(State, Data);
+hash_update(State0, Data, _, MaxBytes) ->
+ <<Increment:MaxBytes/binary, Rest/binary>> = Data,
+ State = do_hash_update(State0, Increment),
+ hash_update(State, Rest, erlang:byte_size(Rest), MaxBytes).
+
+do_hash_update({md5,Context}, Data) -> {md5, md5_update(Context,Data)};
+do_hash_update({md4,Context}, Data) -> {md4, md4_update(Context,Data)};
+do_hash_update({sha,Context}, Data) -> {sha, sha_update(Context,Data)};
+do_hash_update({ripemd160,Context}, Data) -> {ripemd160, ripemd160_update(Context,Data)};
+do_hash_update({sha224,Context}, Data) -> {sha224, sha224_update(Context,Data)};
+do_hash_update({sha256,Context}, Data) -> {sha256, sha256_update(Context,Data)};
+do_hash_update({sha384,Context}, Data) -> {sha384, sha384_update(Context,Data)};
+do_hash_update({sha512,Context}, Data) -> {sha512, sha512_update(Context,Data)}.
+
+
%%
%% MD5
%%
@@ -567,40 +900,56 @@ sha512_init_nif() -> ?nif_stub.
sha512_update_nif(_Context, _Data) -> ?nif_stub.
sha512_final_nif(_Context) -> ?nif_stub.
-%%
-%% MESSAGE AUTHENTICATION CODES
-%%
-
-%%
-%% HMAC (multiple hash options)
-%%
+%% HMAC --------------------------------------------------------------------
--spec hmac(_, iodata(), iodata()) -> binary().
--spec hmac(_, iodata(), iodata(), integer()) -> binary().
--spec hmac_init(atom(), iodata()) -> binary().
--spec hmac_update(binary(), iodata()) -> binary().
--spec hmac_final(binary()) -> binary().
--spec hmac_final_n(binary(), integer()) -> binary().
-
-hmac(md5, Key, Data) -> md5_mac(Key, Data);
-hmac(sha, Key, Data) -> sha_mac(Key, Data);
-hmac(sha224, Key, Data) -> sha224_mac(Key, Data);
-hmac(sha256, Key, Data) -> sha256_mac(Key, Data);
-hmac(sha384, Key, Data) -> sha384_mac(Key, Data);
-hmac(sha512, Key, Data) -> sha512_mac(Key, Data).
-
-hmac(md5, Key, Data, Size) -> md5_mac_n(Key, Data, Size);
-hmac(sha, Key, Data, Size) -> sha_mac_n(Key, Data, Size);
-hmac(sha224, Key, Data, Size) -> sha224_mac(Key, Data, Size);
-hmac(sha256, Key, Data, Size) -> sha256_mac(Key, Data, Size);
-hmac(sha384, Key, Data, Size) -> sha384_mac(Key, Data, Size);
-hmac(sha512, Key, Data, Size) -> sha512_mac(Key, Data, Size).
+hmac(Type, Key, Data, MacSize, Size, MaxBytes, initial) when Size =< MaxBytes ->
+ case MacSize of
+ undefined ->
+ do_hmac(Type, Key, Data);
+ _ ->
+ do_hmac(Type, Key, Data, MacSize)
+ end;
+hmac(Type, Key, Data, MacSize, _, MaxBytes, initial) ->
+ <<Increment:MaxBytes/binary, Rest/binary>> = Data,
+ State0 = hmac_init(Type, Key),
+ State = hmac_update(State0, Increment),
+ hmac(State, Rest, MacSize, erlang:byte_size(Rest), max_bytes(), continue).
+hmac(State0, Data, MacSize, Size, MaxBytes, continue) when Size =< MaxBytes ->
+ State = hmac_update(State0, Data),
+ case MacSize of
+ undefined ->
+ hmac_final(State);
+ _ ->
+ hmac_final_n(State, MacSize)
+ end;
+hmac(State0, Data, MacSize, _Size, MaxBytes, continue) ->
+ <<Increment:MaxBytes/binary, Rest/binary>> = Data,
+ State = hmac_update(State0, Increment),
+ hmac(State, Rest, MacSize, erlang:byte_size(Rest), max_bytes(), continue).
+
+hmac_update(State, Data, Size, MaxBytes) when Size =< MaxBytes ->
+ do_hmac_update(State, Data);
+hmac_update(State0, Data, _, MaxBytes) ->
+ <<Increment:MaxBytes/binary, Rest/binary>> = Data,
+ State = do_hmac_update(State0, Increment),
+ hmac_update(State, Rest, erlang:byte_size(Rest), MaxBytes).
+
+do_hmac(md5, Key, Data) -> md5_mac(Key, Data);
+do_hmac(sha, Key, Data) -> sha_mac(Key, Data);
+do_hmac(sha224, Key, Data) -> sha224_mac(Key, Data);
+do_hmac(sha256, Key, Data) -> sha256_mac(Key, Data);
+do_hmac(sha384, Key, Data) -> sha384_mac(Key, Data);
+do_hmac(sha512, Key, Data) -> sha512_mac(Key, Data).
+
+do_hmac(md5, Key, Data, Size) -> md5_mac_n(Key, Data, Size);
+do_hmac(sha, Key, Data, Size) -> sha_mac_n(Key, Data, Size);
+do_hmac(sha224, Key, Data, Size) -> sha224_mac(Key, Data, Size);
+do_hmac(sha256, Key, Data, Size) -> sha256_mac(Key, Data, Size);
+do_hmac(sha384, Key, Data, Size) -> sha384_mac(Key, Data, Size);
+do_hmac(sha512, Key, Data, Size) -> sha512_mac(Key, Data, Size).
+
+do_hmac_update(_Context, _Data) -> ? nif_stub.
-hmac_init(_Type, _Key) -> ?nif_stub.
-hmac_update(_Context, _Data) -> ? nif_stub.
-hmac_final(_Context) -> ? nif_stub.
-hmac_final_n(_Context, _HashLen) -> ? nif_stub.
-
%%
%% MD5_MAC
%%
@@ -696,172 +1045,7 @@ sha512_mac(Key, Data, MacSz) ->
sha512_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub.
-
-%% Ecrypt/decrypt %%%
-
--spec block_encrypt(des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | blowfish_cbc |
- blowfish_cfb64 | aes_cbc128 | aes_cfb128 | aes_cbc256 | rc2_cbc,
- Key::iodata(), Ivec::binary(), Data::iodata()) -> binary().
-
-block_encrypt(des_cbc, Key, Ivec, Data) ->
- des_cbc_encrypt(Key, Ivec, Data);
-block_encrypt(des_cfb, Key, Ivec, Data) ->
- des_cfb_encrypt(Key, Ivec, Data);
-block_encrypt(des3_cbc, [Key1, Key2, Key3], Ivec, Data) ->
- des3_cbc_encrypt(Key1, Key2, Key3, Ivec, Data);
-block_encrypt(des3_cbf, [Key1, Key2, Key3], Ivec, Data) ->
- des3_cfb_encrypt(Key1, Key2, Key3, Ivec, Data);
-block_encrypt(des_ede3, [Key1, Key2, Key3], Ivec, Data) ->
- des_ede3_cbc_encrypt(Key1, Key2, Key3, Ivec, Data);
-block_encrypt(blowfish_cbc, Key, Ivec, Data) ->
- blowfish_cbc_encrypt(Key, Ivec, Data);
-block_encrypt(blowfish_cfb64, Key, Ivec, Data) ->
- blowfish_cfb64_encrypt(Key, Ivec, Data);
-block_encrypt(blowfish_ofb64, Key, Ivec, Data) ->
- blowfish_ofb64_encrypt(Key, Ivec, Data);
-block_encrypt(aes_cbc128, Key, Ivec, Data) ->
- aes_cbc_128_encrypt(Key, Ivec, Data);
-block_encrypt(aes_cbc256, Key, Ivec, Data) ->
- aes_cbc_256_encrypt(Key, Ivec, Data);
-block_encrypt(aes_cfb128, Key, Ivec, Data) ->
- aes_cfb_128_encrypt(Key, Ivec, Data);
-block_encrypt(rc2_cbc, Key, Ivec, Data) ->
- rc2_cbc_encrypt(Key, Ivec, Data).
-
--spec block_decrypt(des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | blowfish_cbc |
- blowfish_cfb64 | blowfish_ofb64 | aes_cbc128 | aes_cbc256 | aes_cfb128 | rc2_cbc,
- Key::iodata(), Ivec::binary(), Data::iodata()) -> binary().
-
-block_decrypt(des_cbc, Key, Ivec, Data) ->
- des_cbc_decrypt(Key, Ivec, Data);
-block_decrypt(des_cfb, Key, Ivec, Data) ->
- des_cfb_decrypt(Key, Ivec, Data);
-block_decrypt(des3_cbc, [Key1, Key2, Key3], Ivec, Data) ->
- des3_cbc_decrypt(Key1, Key2, Key3, Ivec, Data);
-block_decrypt(des3_cbf, [Key1, Key2, Key3], Ivec, Data) ->
- des3_cfb_decrypt(Key1, Key2, Key3, Ivec, Data);
-block_decrypt(des_ede3, [Key1, Key2, Key3], Ivec, Data) ->
- des_ede3_cbc_decrypt(Key1, Key2, Key3, Ivec, Data);
-block_decrypt(blowfish_cbc, Key, Ivec, Data) ->
- blowfish_cbc_decrypt(Key, Ivec, Data);
-block_decrypt(blowfish_cfb64, Key, Ivec, Data) ->
- blowfish_cfb64_decrypt(Key, Ivec, Data);
-block_decrypt(blowfish_ofb64, Key, Ivec, Data) ->
- blowfish_ofb64_decrypt(Key, Ivec, Data);
-block_decrypt(aes_cbc128, Key, Ivec, Data) ->
- aes_cbc_128_decrypt(Key, Ivec, Data);
-block_decrypt(aes_cbc256, Key, Ivec, Data) ->
- aes_cbc_256_decrypt(Key, Ivec, Data);
-block_decrypt(aes_cfb128, Key, Ivec, Data) ->
- aes_cfb_128_decrypt(Key, Ivec, Data);
-block_decrypt(rc2_cbc, Key, Ivec, Data) ->
- rc2_cbc_decrypt(Key, Ivec, Data).
-
--spec block_encrypt(des_ecb | blowfish_ecb, Key::iodata(), Data::iodata()) -> binary().
-
-block_encrypt(des_ecb, Key, Data) ->
- des_ecb_encrypt(Key, Data);
-block_encrypt(blowfish_ecb, Key, Data) ->
- blowfish_ecb_encrypt(Key, Data).
-
--spec block_decrypt(des_ecb | blowfish_ecb, Key::iodata(), Data::iodata()) -> binary().
-
-block_decrypt(des_ecb, Key, Data) ->
- des_ecb_decrypt(Key, Data);
-block_decrypt(blowfish_ecb, Key, Data) ->
- blowfish_ecb_decrypt(Key, Data).
-
--spec next_iv(des_cbc | des3_cbc | aes_cbc, Data::iodata()) -> binary().
-
-next_iv(des_cbc, Data) ->
- des_cbc_ivec(Data);
-next_iv(des3_cbc, Data) ->
- des_cbc_ivec(Data);
-next_iv(aes_cbc, Data) ->
- aes_cbc_ivec(Data).
-
--spec next_iv(des_cfb, Data::iodata(), Ivec::binary()) -> binary().
-
-next_iv(des_cfb, Data, Ivec) ->
- des_cfb_ivec(Ivec, Data);
-next_iv(Type, Data, _Ivec) ->
- next_iv(Type, Data).
-
-stream_init(aes_ctr, Key, Ivec) ->
- {aes_ctr, aes_ctr_stream_init(Key, Ivec)}.
-stream_init(rc4, Key) ->
- {rc4, rc4_set_key(Key)}.
-stream_encrypt({aes_ctr, State0}, Data) ->
- {State, Cipher} = aes_ctr_stream_encrypt(State0, Data),
- {{aes_ctr, State}, Cipher};
-stream_encrypt({rc4, State0}, Data) ->
- {State, Cipher} = rc4_encrypt_with_state(State0, Data),
- {{rc4, State}, Cipher}.
-stream_decrypt({aes_ctr, State0}, Data) ->
- {State, Text} = aes_ctr_stream_decrypt(State0, Data),
- {{aes_ctr, State}, Text};
-stream_decrypt({rc4, State0}, Data) ->
- {State, Text} = rc4_encrypt_with_state (State0, Data),
- {{rc4, State}, Text}.
-
-%%
-%% CRYPTO FUNCTIONS
-%%
-
-%%
-%% DES - in cipher block chaining mode (CBC)
-%%
--spec des_cbc_encrypt(iodata(), binary(), iodata()) -> binary().
--spec des_cbc_decrypt(iodata(), binary(), iodata()) -> binary().
-
-des_cbc_encrypt(Key, IVec, Data) ->
- des_cbc_crypt(Key, IVec, Data, true).
-
-des_cbc_decrypt(Key, IVec, Data) ->
- des_cbc_crypt(Key, IVec, Data, false).
-
-des_cbc_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
-
-%%
-%% dec_cbc_ivec(Data) -> binary()
-%%
-%% Returns the IVec to be used in the next iteration of
-%% des_cbc_[encrypt|decrypt].
-%%
--spec des_cbc_ivec(iodata()) -> binary().
-
-des_cbc_ivec(Data) when is_binary(Data) ->
- {_, IVec} = split_binary(Data, size(Data) - 8),
- IVec;
-des_cbc_ivec(Data) when is_list(Data) ->
- des_cbc_ivec(list_to_binary(Data)).
-
-%%
-%% DES - in 8-bits cipher feedback mode (CFB)
-%%
--spec des_cfb_encrypt(iodata(), binary(), iodata()) -> binary().
--spec des_cfb_decrypt(iodata(), binary(), iodata()) -> binary().
-
-des_cfb_encrypt(Key, IVec, Data) ->
- des_cfb_crypt(Key, IVec, Data, true).
-
-des_cfb_decrypt(Key, IVec, Data) ->
- des_cfb_crypt(Key, IVec, Data, false).
-
-des_cfb_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
-
-%%
-%% dec_cfb_ivec(IVec, Data) -> binary()
-%%
-%% Returns the IVec to be used in the next iteration of
-%% des_cfb_[encrypt|decrypt].
-%%
--spec des_cfb_ivec(iodata(), iodata()) -> binary().
-
-des_cfb_ivec(IVec, Data) ->
- IVecAndData = list_to_binary([IVec, Data]),
- {_, NewIVec} = split_binary(IVecAndData, byte_size(IVecAndData) - 8),
- NewIVec.
+%% CIPHERS --------------------------------------------------------------------
%%
%% DES - in electronic codebook mode (ECB)
@@ -973,292 +1157,63 @@ aes_cfb_128_decrypt(Key, IVec, Data) ->
aes_cfb_128_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
-%%
-%% RAND - pseudo random numbers using RN_ functions in crypto lib
%%
--spec rand_bytes(non_neg_integer()) -> binary().
--spec strong_rand_bytes(non_neg_integer()) -> binary().
--spec rand_uniform(crypto_integer(), crypto_integer()) ->
- crypto_integer().
--spec strong_rand_mpint(Bits::non_neg_integer(),
- Top::-1..1,
- Bottom::0..1) -> binary().
-
-rand_bytes(_Bytes) -> ?nif_stub.
-
-strong_rand_bytes(Bytes) ->
- case strong_rand_bytes_nif(Bytes) of
- false -> erlang:error(low_entropy);
- Bin -> Bin
- end.
-strong_rand_bytes_nif(_Bytes) -> ?nif_stub.
-
-rand_bytes(_Bytes, _Topmask, _Bottommask) -> ?nif_stub.
-
-strong_rand_mpint(Bits, Top, Bottom) ->
- case strong_rand_mpint_nif(Bits,Top,Bottom) of
- false -> erlang:error(low_entropy);
- Bin -> Bin
- end.
-strong_rand_mpint_nif(_Bits, _Top, _Bottom) -> ?nif_stub.
-
+%% DES - in cipher block chaining mode (CBC)
+%%
+-spec des_cbc_encrypt(iodata(), binary(), iodata()) -> binary().
+-spec des_cbc_decrypt(iodata(), binary(), iodata()) -> binary().
-rand_uniform(From,To) when is_binary(From), is_binary(To) ->
- case rand_uniform_nif(From,To) of
- <<Len:32/integer, MSB, Rest/binary>> when MSB > 127 ->
- <<(Len + 1):32/integer, 0, MSB, Rest/binary>>;
- Whatever ->
- Whatever
- end;
-rand_uniform(From,To) when is_integer(From),is_integer(To) ->
- if From < 0 ->
- rand_uniform_pos(0, To - From) + From;
- true ->
- rand_uniform_pos(From, To)
- end.
+des_cbc_encrypt(Key, IVec, Data) ->
+ des_cbc_crypt(Key, IVec, Data, true).
-rand_uniform_pos(From,To) when From < To ->
- BinFrom = mpint(From),
- BinTo = mpint(To),
- case rand_uniform(BinFrom, BinTo) of
- Result when is_binary(Result) ->
- erlint(Result);
- Other ->
- Other
- end;
-rand_uniform_pos(_,_) ->
- error(badarg).
+des_cbc_decrypt(Key, IVec, Data) ->
+ des_cbc_crypt(Key, IVec, Data, false).
-rand_uniform_nif(_From,_To) -> ?nif_stub.
+des_cbc_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
%%
-%% mod_exp - utility for rsa generation and SRP
+%% dec_cbc_ivec(Data) -> binary()
%%
-mod_exp(Base, Exponent, Modulo)
- when is_integer(Base), is_integer(Exponent), is_integer(Modulo) ->
- bin_to_int(mod_exp_nif(int_to_bin(Base), int_to_bin(Exponent), int_to_bin(Modulo), 0));
-
-mod_exp(Base, Exponent, Modulo) ->
- mod_exp_nif(mpint_to_bin(Base),mpint_to_bin(Exponent),mpint_to_bin(Modulo), 4).
-
--spec mod_pow(binary()|integer(), binary()|integer(), binary()|integer()) -> binary() | error.
-mod_pow(Base, Exponent, Prime) ->
- case mod_exp_nif(ensure_int_as_bin(Base), ensure_int_as_bin(Exponent), ensure_int_as_bin(Prime), 0) of
- <<0>> -> error;
- R -> R
- end.
-
+%% Returns the IVec to be used in the next iteration of
+%% des_cbc_[encrypt|decrypt].
+%%
+-spec des_cbc_ivec(iodata()) -> binary().
-mod_exp_nif(_Base,_Exp,_Mod,_bin_hdr) -> ?nif_stub.
+des_cbc_ivec(Data) when is_binary(Data) ->
+ {_, IVec} = split_binary(Data, size(Data) - 8),
+ IVec;
+des_cbc_ivec(Data) when is_list(Data) ->
+ des_cbc_ivec(list_to_binary(Data)).
%%
-%% DSS, RSA - verify
+%% DES - in 8-bits cipher feedback mode (CFB)
%%
--spec dss_verify(data_or_digest(), binary(), [binary()]) -> boolean().
--spec dss_verify(dss_digest_type(), data_or_digest(), binary(), [binary()]) -> boolean().
--spec rsa_verify(data_or_digest(), binary(), [binary()]) -> boolean().
--spec rsa_verify(rsa_digest_type(), data_or_digest(), binary(), [binary()]) ->
- boolean().
-
-%% Key = [P,Q,G,Y] P,Q,G=DSSParams Y=PublicKey
-dss_verify(Data,Signature,Key) ->
- dss_verify(sha, Data, Signature, Key).
-
-dss_verify(Type,Data,Signature,Key) when is_binary(Data), Type=/=none ->
- verify(dss,Type,mpint_to_bin(Data),mpint_to_bin(Signature),map_mpint_to_bin(Key));
-dss_verify(Type,Digest,Signature,Key) ->
- verify(dss,Type,Digest,mpint_to_bin(Signature),map_mpint_to_bin(Key)).
-
-% Key = [E,N] E=PublicExponent N=PublicModulus
-rsa_verify(Data,Signature,Key) ->
- rsa_verify(sha, Data,Signature,Key).
-rsa_verify(Type, Data, Signature, Key) when is_binary(Data) ->
- verify(rsa, Type, mpint_to_bin(Data), mpint_to_bin(Signature), map_mpint_to_bin(Key));
-rsa_verify(Type, Digest, Signature, Key) ->
- verify(rsa, Type, Digest, mpint_to_bin(Signature), map_mpint_to_bin(Key)).
-
-
-verify(dss, Type, Data, Signature, Key) ->
- dss_verify_nif(Type, Data, Signature, map_ensure_int_as_bin(Key));
-
-verify(rsa, Type, DataOrDigest, Signature, Key) ->
- case rsa_verify_nif(Type, DataOrDigest, Signature, map_ensure_int_as_bin(Key)) of
- notsup -> erlang:error(notsup);
- Bool -> Bool
- end;
-verify(ecdsa, Type, DataOrDigest, Signature, [Key, Curve]) ->
- case ecdsa_verify_nif(Type, DataOrDigest, Signature, term_to_ec_key(Curve, undefined, Key)) of
- notsup -> erlang:error(notsup);
- Bool -> Bool
- end.
+-spec des_cfb_encrypt(iodata(), binary(), iodata()) -> binary().
+-spec des_cfb_decrypt(iodata(), binary(), iodata()) -> binary().
+des_cfb_encrypt(Key, IVec, Data) ->
+ des_cfb_crypt(Key, IVec, Data, true).
-dss_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub.
-rsa_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub.
-ecdsa_verify_nif(_Type, _DataOrDigest, _Signature, _Key) -> ?nif_stub.
+des_cfb_decrypt(Key, IVec, Data) ->
+ des_cfb_crypt(Key, IVec, Data, false).
+des_cfb_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
%%
-%% DSS, RSA - sign
+%% dec_cfb_ivec(IVec, Data) -> binary()
%%
-%% Key = [P,Q,G,X] P,Q,G=DSSParams X=PrivateKey
--spec dss_sign(data_or_digest(), [binary()]) -> binary().
--spec dss_sign(dss_digest_type(), data_or_digest(), [binary()]) -> binary().
--spec rsa_sign(data_or_digest(), [binary()]) -> binary().
--spec rsa_sign(rsa_digest_type(), data_or_digest(), [binary()]) -> binary().
-
-dss_sign(DataOrDigest,Key) ->
- dss_sign(sha,DataOrDigest,Key).
-dss_sign(Type, Data, Key) when is_binary(Data), Type=/=none ->
- sign(dss, Type, mpint_to_bin(Data), map_mpint_to_bin(Key));
-dss_sign(Type, Digest, Key) ->
- sign(dss, Type, Digest, map_mpint_to_bin(Key)).
-
-
-%% Key = [E,N,D] E=PublicExponent N=PublicModulus D=PrivateExponent
-rsa_sign(DataOrDigest,Key) ->
- rsa_sign(sha, DataOrDigest, Key).
-
-rsa_sign(Type, Data, Key) when is_binary(Data) ->
- sign(rsa, Type, mpint_to_bin(Data), map_mpint_to_bin(Key));
-rsa_sign(Type, Digest, Key) ->
- sign(rsa, Type, Digest, map_mpint_to_bin(Key)).
-
-map_mpint_to_bin(List) ->
- lists:map(fun(E) -> mpint_to_bin(E) end, List ).
-
-map_ensure_int_as_bin([H|_]=List) when is_integer(H) ->
- lists:map(fun(E) -> int_to_bin(E) end, List);
-map_ensure_int_as_bin(List) ->
- List.
-
-ensure_int_as_bin(Int) when is_integer(Int) ->
- int_to_bin(Int);
-ensure_int_as_bin(Bin) ->
- Bin.
-
-map_to_norm_bin([H|_]=List) when is_integer(H) ->
- lists:map(fun(E) -> int_to_bin(E) end, List);
-map_to_norm_bin(List) ->
- lists:map(fun(E) -> mpint_to_bin(E) end, List).
-
-
-sign(rsa, Type, DataOrDigest, Key) ->
- case rsa_sign_nif(Type, DataOrDigest, map_ensure_int_as_bin(Key)) of
- error -> erlang:error(badkey, [Type,DataOrDigest,Key]);
- Sign -> Sign
- end;
-sign(dss, Type, DataOrDigest, Key) ->
- case dss_sign_nif(Type, DataOrDigest, map_ensure_int_as_bin(Key)) of
- error -> erlang:error(badkey, [DataOrDigest, Key]);
- Sign -> Sign
- end;
-sign(ecdsa, Type, DataOrDigest, [Key, Curve]) ->
- case ecdsa_sign_nif(Type, DataOrDigest, term_to_ec_key(Curve, Key, undefined)) of
- error -> erlang:error(badkey, [Type,DataOrDigest,Key]);
- Sign -> Sign
- end.
-
-rsa_sign_nif(_Type,_Data,_Key) -> ?nif_stub.
-dss_sign_nif(_Type,_Data,_Key) -> ?nif_stub.
-ecdsa_sign_nif(_Type, _DataOrDigest, _Key) -> ?nif_stub.
-
-
-
-
--spec public_encrypt(rsa, binary(), [binary()], rsa_padding()) ->
- binary().
--spec public_decrypt(rsa, binary(), [integer() | binary()], rsa_padding()) ->
- binary().
--spec private_encrypt(rsa, binary(), [integer() | binary()], rsa_padding()) ->
- binary().
--spec private_decrypt(rsa, binary(), [integer() | binary()], rsa_padding()) ->
- binary().
-
-public_encrypt(rsa, BinMesg, Key, Padding) ->
- case rsa_public_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, true) of
- error ->
- erlang:error(encrypt_failed, [BinMesg,Key, Padding]);
- Sign -> Sign
- end.
-
-%% Binary, Key = [E,N,D]
-private_decrypt(rsa, BinMesg, Key, Padding) ->
- case rsa_private_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, false) of
- error ->
- erlang:error(decrypt_failed, [BinMesg,Key, Padding]);
- Sign -> Sign
- end.
-
-
-%% Binary, Key = [E,N,D]
-private_encrypt(rsa, BinMesg, Key, Padding) ->
- case rsa_private_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, true) of
- error ->
- erlang:error(encrypt_failed, [BinMesg,Key, Padding]);
- Sign -> Sign
- end.
-
-%% Binary, Key = [E,N]
-public_decrypt(rsa, BinMesg, Key, Padding) ->
- case rsa_public_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, false) of
- error ->
- erlang:error(decrypt_failed, [BinMesg,Key, Padding]);
- Sign -> Sign
- end.
-
-
+%% Returns the IVec to be used in the next iteration of
+%% des_cfb_[encrypt|decrypt].
%%
-%% rsa_public_encrypt
-%% rsa_private_decrypt
--type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'.
-
--spec rsa_public_encrypt(binary(), [binary()], rsa_padding()) ->
- binary().
--spec rsa_public_decrypt(binary(), [integer() | mpint()], rsa_padding()) ->
- binary().
--spec rsa_private_encrypt(binary(), [integer() | mpint()], rsa_padding()) ->
- binary().
--spec rsa_private_decrypt(binary(), [integer() | mpint()], rsa_padding()) ->
- binary().
-%% Binary, Key = [E,N]
-rsa_public_encrypt(BinMesg, Key, Padding) ->
- case rsa_public_crypt(BinMesg, map_to_norm_bin(Key), Padding, true) of
- error ->
- erlang:error(encrypt_failed, [BinMesg,Key, Padding]);
- Sign -> Sign
- end.
-
-rsa_public_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub.
+-spec des_cfb_ivec(iodata(), iodata()) -> binary().
-%% Binary, Key = [E,N,D]
-rsa_private_decrypt(BinMesg, Key, Padding) ->
- case rsa_private_crypt(BinMesg, map_to_norm_bin(Key), Padding, false) of
- error ->
- erlang:error(decrypt_failed, [BinMesg,Key, Padding]);
- Sign -> Sign
- end.
+des_cfb_ivec(IVec, Data) ->
+ IVecAndData = list_to_binary([IVec, Data]),
+ {_, NewIVec} = split_binary(IVecAndData, byte_size(IVecAndData) - 8),
+ NewIVec.
-rsa_private_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub.
-
-%% Binary, Key = [E,N,D]
-rsa_private_encrypt(BinMesg, Key, Padding) ->
- case rsa_private_crypt(BinMesg, map_to_norm_bin(Key), Padding, true) of
- error ->
- erlang:error(encrypt_failed, [BinMesg,Key, Padding]);
- Sign -> Sign
- end.
-
-%% Binary, Key = [E,N]
-rsa_public_decrypt(BinMesg, Key, Padding) ->
- case rsa_public_crypt(BinMesg, map_to_norm_bin(Key), Padding, false) of
- error ->
- erlang:error(decrypt_failed, [BinMesg,Key, Padding]);
- Sign -> Sign
- end.
-
%%
%% AES - with 128 or 256 bit key in cipher block chaining mode (CBC)
%%
@@ -1298,6 +1253,33 @@ aes_cbc_ivec(Data) when is_binary(Data) ->
aes_cbc_ivec(Data) when is_list(Data) ->
aes_cbc_ivec(list_to_binary(Data)).
+
+%% Stream ciphers --------------------------------------------------------------------
+
+stream_crypt(Fun, State, Data, Size, MaxByts, []) when Size =< MaxByts ->
+ Fun(State, Data);
+stream_crypt(Fun, State0, Data, Size, MaxByts, Acc) when Size =< MaxByts ->
+ {State, Cipher} = Fun(State0, Data),
+ {State, list_to_binary(lists:reverse([Cipher | Acc]))};
+stream_crypt(Fun, State0, Data, _, MaxByts, Acc) ->
+ <<Increment:MaxByts/binary, Rest/binary>> = Data,
+ {State, CipherText} = Fun(State0, Increment),
+ stream_crypt(Fun, State, Rest, erlang:byte_size(Rest), MaxByts, [CipherText | Acc]).
+
+do_stream_encrypt({aes_ctr, State0}, Data) ->
+ {State, Cipher} = aes_ctr_stream_encrypt(State0, Data),
+ {{aes_ctr, State}, Cipher};
+do_stream_encrypt({rc4, State0}, Data) ->
+ {State, Cipher} = rc4_encrypt_with_state(State0, Data),
+ {{rc4, State}, Cipher}.
+
+do_stream_decrypt({aes_ctr, State0}, Data) ->
+ {State, Text} = aes_ctr_stream_decrypt(State0, Data),
+ {{aes_ctr, State}, Text};
+do_stream_decrypt({rc4, State0}, Data) ->
+ {State, Text} = rc4_encrypt_with_state(State0, Data),
+ {{rc4, State}, Text}.
+
%%
%% AES - in counter mode (CTR)
%%
@@ -1305,7 +1287,7 @@ aes_cbc_ivec(Data) when is_list(Data) ->
binary().
-spec aes_ctr_decrypt(iodata(), binary(), iodata()) ->
binary().
-
+
aes_ctr_encrypt(_Key, _IVec, _Data) -> ?nif_stub.
aes_ctr_decrypt(_Key, _IVec, _Cipher) -> ?nif_stub.
@@ -1326,15 +1308,6 @@ aes_ctr_stream_encrypt({_Key, _IVec, _ECount, _Num}=_State, _Data) -> ?nif_stub.
aes_ctr_stream_decrypt({_Key, _IVec, _ECount, _Num}=_State, _Cipher) -> ?nif_stub.
%%
-%% XOR - xor to iolists and return a binary
-%% NB doesn't check that they are the same size, just concatenates
-%% them and sends them to the driver
-%%
--spec exor(iodata(), iodata()) -> binary().
-
-exor(_A, _B) -> ?nif_stub.
-
-%%
%% RC4 - symmetric stream cipher
%%
-spec rc4_encrypt(iodata(), iodata()) -> binary().
@@ -1363,7 +1336,76 @@ rc2_40_cbc_encrypt(Key, IVec, Data) when erlang:byte_size(Key) == 5 ->
rc2_40_cbc_decrypt(Key, IVec, Data) when erlang:byte_size(Key) == 5 ->
rc2_cbc_crypt(Key,IVec,Data,false).
-%%
+
+%% Secure remote password -------------------------------------------------------------------
+
+user_srp_gen_key(Private, Generator, Prime) ->
+ case mod_pow(Generator, Private, Prime) of
+ error ->
+ error;
+ Public ->
+ {Public, Private}
+ end.
+
+host_srp_gen_key(Private, Verifier, Generator, Prime, Version) ->
+ Multiplier = srp_multiplier(Version, Generator, Prime),
+ case srp_value_B_nif(Multiplier, Verifier, Generator, Private, Prime) of
+ error ->
+ error;
+ Public ->
+ {Public, Private}
+ end.
+
+srp_multiplier('6a', Generator, Prime) ->
+ %% k = SHA1(N | PAD(g)) from http://srp.stanford.edu/design.html
+ C0 = sha_init(),
+ C1 = sha_update(C0, Prime),
+ C2 = sha_update(C1, srp_pad_to(erlang:byte_size(Prime), Generator)),
+ sha_final(C2);
+srp_multiplier('6', _, _) ->
+ <<3/integer>>;
+srp_multiplier('3', _, _) ->
+ <<1/integer>>.
+
+srp_scrambler(Version, UserPublic, HostPublic, Prime) when Version == '6'; Version == '6a'->
+ %% SHA1(PAD(A) | PAD(B)) from http://srp.stanford.edu/design.html
+ PadLength = erlang:byte_size(Prime),
+ C0 = sha_init(),
+ C1 = sha_update(C0, srp_pad_to(PadLength, UserPublic)),
+ C2 = sha_update(C1, srp_pad_to(PadLength, HostPublic)),
+ sha_final(C2);
+srp_scrambler('3', _, HostPublic, _Prime) ->
+ %% The parameter u is a 32-bit unsigned integer which takes its value
+ %% from the first 32 bits of the SHA1 hash of B, MSB first.
+ <<U:32/bits, _/binary>> = sha(HostPublic),
+ U.
+
+srp_pad_length(Width, Length) ->
+ (Width - Length rem Width) rem Width.
+
+srp_pad_to(Width, Binary) ->
+ case srp_pad_length(Width, size(Binary)) of
+ 0 -> Binary;
+ N -> << 0:(N*8), Binary/binary>>
+ end.
+
+srp_host_secret_nif(_Verifier, _B, _U, _A, _Prime) -> ?nif_stub.
+
+srp_user_secret_nif(_A, _U, _B, _Multiplier, _Generator, _Exponent, _Prime) -> ?nif_stub.
+
+srp_value_B_nif(_Multiplier, _Verifier, _Generator, _Exponent, _Prime) -> ?nif_stub.
+
+
+%% Digital signatures --------------------------------------------------------------------
+rsa_sign_nif(_Type,_Data,_Key) -> ?nif_stub.
+dss_sign_nif(_Type,_Data,_Key) -> ?nif_stub.
+ecdsa_sign_nif(_Type, _DataOrDigest, _Key) -> ?nif_stub.
+
+dss_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub.
+rsa_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub.
+ecdsa_verify_nif(_Type, _DataOrDigest, _Signature, _Key) -> ?nif_stub.
+
+%% Public Keys --------------------------------------------------------------------
%% DH Diffie-Hellman functions
%%
@@ -1412,80 +1454,10 @@ dh_compute_key(OthersPublicKey, MyPrivateKey, DHParameters) ->
dh_compute_key_nif(_OthersPublicKey, _MyPrivateKey, _DHParameters) -> ?nif_stub.
-generate_key(Type, Params) ->
- generate_key(Type, Params, undefined).
-
-generate_key(dh, DHParameters, PrivateKey) ->
- dh_generate_key_nif(ensure_int_as_bin(PrivateKey),
- map_ensure_int_as_bin(DHParameters), 0);
-
-generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, PrivArg)
- when is_binary(Verifier), is_binary(Generator), is_binary(Prime), is_atom(Version) ->
- Private = case PrivArg of
- undefined -> random_bytes(32);
- _ -> ensure_int_as_bin(PrivArg)
- end,
- host_srp_gen_key(Private, Verifier, Generator, Prime, Version);
-
-generate_key(srp, {user, [Generator, Prime, Version]}, PrivateArg)
- when is_binary(Generator), is_binary(Prime), is_atom(Version) ->
- Private = case PrivateArg of
- undefined -> random_bytes(32);
- _ -> PrivateArg
- end,
- user_srp_gen_key(Private, Generator, Prime);
-
-generate_key(ecdh, Curve, undefined) ->
- ec_key_to_term_nif(ec_key_generate(Curve)).
-
-
ec_key_generate(_Key) -> ?nif_stub.
-
-compute_key(dh, OthersPublicKey, MyPrivateKey, DHParameters) ->
- case dh_compute_key_nif(ensure_int_as_bin(OthersPublicKey),
- ensure_int_as_bin(MyPrivateKey),
- map_ensure_int_as_bin(DHParameters)) of
- error -> erlang:error(computation_failed,
- [OthersPublicKey,MyPrivateKey,DHParameters]);
- Ret -> Ret
- end;
-
-compute_key(srp, HostPublic, {UserPublic, UserPrivate},
- {user, [DerivedKey, Prime, Generator, Version | ScramblerArg]}) when
- is_binary(Prime),
- is_binary(Generator),
- is_atom(Version) ->
- HostPubBin = ensure_int_as_bin(HostPublic),
- Multiplier = srp_multiplier(Version, Generator, Prime),
- Scrambler = case ScramblerArg of
- [] -> srp_scrambler(Version, ensure_int_as_bin(UserPublic),
- HostPubBin, Prime);
- [S] -> S
- end,
- srp_user_secret_nif(ensure_int_as_bin(UserPrivate), Scrambler, HostPubBin,
- Multiplier, Generator, DerivedKey, Prime);
-
-compute_key(srp, UserPublic, {HostPublic, HostPrivate},
- {host,[Verifier, Prime, Version | ScramblerArg]}) when
- is_binary(Verifier),
- is_binary(Prime),
- is_atom(Version) ->
- UserPubBin = ensure_int_as_bin(UserPublic),
- Scrambler = case ScramblerArg of
- [] -> srp_scrambler(Version, UserPubBin, ensure_int_as_bin(HostPublic), Prime);
- [S] -> S
- end,
- srp_host_secret_nif(Verifier, ensure_int_as_bin(HostPrivate), Scrambler,
- UserPubBin, Prime);
-
-compute_key(ecdh, Others, My, Curve) ->
- ecdh_compute_key_nif(ensure_int_as_bin(Others),
- term_to_ec_key(Curve,My,undefined)).
-
ecdh_compute_key_nif(_Others, _My) -> ?nif_stub.
-
%%
%% EC
%%
@@ -1511,65 +1483,181 @@ term_to_ec_key(Curve, PrivKey, PubKey) ->
term_to_ec_key_nif(_Curve, _PrivKey, _PubKey) -> ?nif_stub.
+%% MISC --------------------------------------------------------------------
+
+exor(Data1, Data2, Size, MaxByts, []) when Size =< MaxByts ->
+ do_exor(Data1, Data2);
+exor(Data1, Data2, Size, MaxByts, Acc) when Size =< MaxByts ->
+ Result = do_exor(Data1, Data2),
+ list_to_binary(lists:reverse([Result | Acc]));
+exor(Data1, Data2, _Size, MaxByts, Acc) ->
+ <<Increment1:MaxByts/binary, Rest1/binary>> = Data1,
+ <<Increment2:MaxByts/binary, Rest2/binary>> = Data2,
+ Result = do_exor(Increment1, Increment2),
+ exor(Rest1, Rest2, erlang:byte_size(Rest1), MaxByts, [Result | Acc]).
+
+do_exor(_A, _B) -> ?nif_stub.
-%% LOCAL FUNCTIONS
+algorithms() -> ?nif_stub.
+
+int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []);
+int_to_bin(X) -> int_to_bin_pos(X, []).
+
+int_to_bin_pos(0,Ds=[_|_]) ->
+ list_to_binary(Ds);
+int_to_bin_pos(X,Ds) ->
+ int_to_bin_pos(X bsr 8, [(X band 255)|Ds]).
+
+int_to_bin_neg(-1, Ds=[MSB|_]) when MSB >= 16#80 ->
+ list_to_binary(Ds);
+int_to_bin_neg(X,Ds) ->
+ int_to_bin_neg(X bsr 8, [(X band 255)|Ds]).
+
+bytes_to_integer(Bin) ->
+ bin_to_int(Bin).
+
+bin_to_int(Bin) when is_binary(Bin) ->
+ Bits = bit_size(Bin),
+ <<Integer:Bits/integer>> = Bin,
+ Integer;
+bin_to_int(undefined) ->
+ undefined.
+
+map_ensure_int_as_bin([H|_]=List) when is_integer(H) ->
+ lists:map(fun(E) -> int_to_bin(E) end, List);
+map_ensure_int_as_bin(List) ->
+ List.
+
+ensure_int_as_bin(Int) when is_integer(Int) ->
+ int_to_bin(Int);
+ensure_int_as_bin(Bin) ->
+ Bin.
+
+map_to_norm_bin([H|_]=List) when is_integer(H) ->
+ lists:map(fun(E) -> int_to_bin(E) end, List);
+map_to_norm_bin(List) ->
+ lists:map(fun(E) -> mpint_to_bin(E) end, List).
+
+%%--------------------------------------------------------------------
+%%% Deprecated
+%%--------------------------------------------------------------------
%%
+%% rsa_public_encrypt
+%% rsa_private_decrypt
+-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'.
-user_srp_gen_key(Private, Generator, Prime) ->
- case mod_pow(Generator, Private, Prime) of
+-spec rsa_public_encrypt(binary(), [binary()], rsa_padding()) ->
+ binary().
+-spec rsa_public_decrypt(binary(), [integer() | mpint()], rsa_padding()) ->
+ binary().
+-spec rsa_private_encrypt(binary(), [integer() | mpint()], rsa_padding()) ->
+ binary().
+-spec rsa_private_decrypt(binary(), [integer() | mpint()], rsa_padding()) ->
+ binary().
+
+%% Binary, Key = [E,N]
+rsa_public_encrypt(BinMesg, Key, Padding) ->
+ case rsa_public_crypt(BinMesg, map_to_norm_bin(Key), Padding, true) of
error ->
- error;
- Public ->
- {Public, Private}
+ erlang:error(encrypt_failed, [BinMesg,Key, Padding]);
+ Sign -> Sign
end.
-host_srp_gen_key(Private, Verifier, Generator, Prime, Version) ->
- Multiplier = srp_multiplier(Version, Generator, Prime),
- case srp_value_B_nif(Multiplier, Verifier, Generator, Private, Prime) of
- error ->
- error;
- Public ->
- {Public, Private}
- end.
+rsa_public_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub.
-srp_multiplier('6a', Generator, Prime) ->
- %% k = SHA1(N | PAD(g)) from http://srp.stanford.edu/design.html
- C0 = sha_init(),
- C1 = sha_update(C0, Prime),
- C2 = sha_update(C1, srp_pad_to(erlang:byte_size(Prime), Generator)),
- sha_final(C2);
-srp_multiplier('6', _, _) ->
- <<3/integer>>;
-srp_multiplier('3', _, _) ->
- <<1/integer>>.
+%% Binary, Key = [E,N,D]
+rsa_private_decrypt(BinMesg, Key, Padding) ->
+ case rsa_private_crypt(BinMesg, map_to_norm_bin(Key), Padding, false) of
+ error ->
+ erlang:error(decrypt_failed, [BinMesg,Key, Padding]);
+ Sign -> Sign
+ end.
-srp_scrambler(Version, UserPublic, HostPublic, Prime) when Version == '6'; Version == '6a'->
- %% SHA1(PAD(A) | PAD(B)) from http://srp.stanford.edu/design.html
- PadLength = erlang:byte_size(Prime),
- C0 = sha_init(),
- C1 = sha_update(C0, srp_pad_to(PadLength, UserPublic)),
- C2 = sha_update(C1, srp_pad_to(PadLength, HostPublic)),
- sha_final(C2);
-srp_scrambler('3', _, HostPublic, _Prime) ->
- %% The parameter u is a 32-bit unsigned integer which takes its value
- %% from the first 32 bits of the SHA1 hash of B, MSB first.
- <<U:32/bits, _/binary>> = sha(HostPublic),
- U.
+rsa_private_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub.
-srp_pad_length(Width, Length) ->
- (Width - Length rem Width) rem Width.
-srp_pad_to(Width, Binary) ->
- case srp_pad_length(Width, size(Binary)) of
- 0 -> Binary;
- N -> << 0:(N*8), Binary/binary>>
+%% Binary, Key = [E,N,D]
+rsa_private_encrypt(BinMesg, Key, Padding) ->
+ case rsa_private_crypt(BinMesg, map_to_norm_bin(Key), Padding, true) of
+ error ->
+ erlang:error(encrypt_failed, [BinMesg,Key, Padding]);
+ Sign -> Sign
end.
-srp_host_secret_nif(_Verifier, _B, _U, _A, _Prime) -> ?nif_stub.
+%% Binary, Key = [E,N]
+rsa_public_decrypt(BinMesg, Key, Padding) ->
+ case rsa_public_crypt(BinMesg, map_to_norm_bin(Key), Padding, false) of
+ error ->
+ erlang:error(decrypt_failed, [BinMesg,Key, Padding]);
+ Sign -> Sign
+ end.
-srp_user_secret_nif(_A, _U, _B, _Multiplier, _Generator, _Exponent, _Prime) -> ?nif_stub.
+map_mpint_to_bin(List) ->
+ lists:map(fun(E) -> mpint_to_bin(E) end, List ).
+
+%%
+%% DSS, RSA - sign
+%%
+%% Key = [P,Q,G,X] P,Q,G=DSSParams X=PrivateKey
+-spec dss_sign(data_or_digest(), [binary()]) -> binary().
+-spec dss_sign(dss_digest_type(), data_or_digest(), [binary()]) -> binary().
+-spec rsa_sign(data_or_digest(), [binary()]) -> binary().
+-spec rsa_sign(rsa_digest_type(), data_or_digest(), [binary()]) -> binary().
+
+dss_sign(DataOrDigest,Key) ->
+ dss_sign(sha,DataOrDigest,Key).
+dss_sign(Type, Data, Key) when is_binary(Data), Type=/=none ->
+ sign(dss, Type, mpint_to_bin(Data), map_mpint_to_bin(Key));
+dss_sign(Type, Digest, Key) ->
+ sign(dss, Type, Digest, map_mpint_to_bin(Key)).
+
+
+%% Key = [E,N,D] E=PublicExponent N=PublicModulus D=PrivateExponent
+rsa_sign(DataOrDigest,Key) ->
+ rsa_sign(sha, DataOrDigest, Key).
+
+rsa_sign(Type, Data, Key) when is_binary(Data) ->
+ sign(rsa, Type, mpint_to_bin(Data), map_mpint_to_bin(Key));
+rsa_sign(Type, Digest, Key) ->
+ sign(rsa, Type, Digest, map_mpint_to_bin(Key)).
+
+%%
+%% DSS, RSA - verify
+%%
+-spec dss_verify(data_or_digest(), binary(), [binary()]) -> boolean().
+-spec dss_verify(dss_digest_type(), data_or_digest(), binary(), [binary()]) -> boolean().
+-spec rsa_verify(data_or_digest(), binary(), [binary()]) -> boolean().
+-spec rsa_verify(rsa_digest_type(), data_or_digest(), binary(), [binary()]) ->
+ boolean().
+
+%% Key = [P,Q,G,Y] P,Q,G=DSSParams Y=PublicKey
+dss_verify(Data,Signature,Key) ->
+ dss_verify(sha, Data, Signature, Key).
+
+dss_verify(Type,Data,Signature,Key) when is_binary(Data), Type=/=none ->
+ verify(dss,Type,mpint_to_bin(Data),mpint_to_bin(Signature),map_mpint_to_bin(Key));
+dss_verify(Type,Digest,Signature,Key) ->
+ verify(dss,Type,Digest,mpint_to_bin(Signature),map_mpint_to_bin(Key)).
+
+% Key = [E,N] E=PublicExponent N=PublicModulus
+rsa_verify(Data,Signature,Key) ->
+ rsa_verify(sha, Data,Signature,Key).
+rsa_verify(Type, Data, Signature, Key) when is_binary(Data) ->
+ verify(rsa, Type, mpint_to_bin(Data), mpint_to_bin(Signature), map_mpint_to_bin(Key));
+rsa_verify(Type, Digest, Signature, Key) ->
+ verify(rsa, Type, Digest, mpint_to_bin(Signature), map_mpint_to_bin(Key)).
+
+-spec strong_rand_mpint(Bits::non_neg_integer(),
+ Top::-1..1,
+ Bottom::0..1) -> binary().
+
+strong_rand_mpint(Bits, Top, Bottom) ->
+ case strong_rand_mpint_nif(Bits,Top,Bottom) of
+ false -> erlang:error(low_entropy);
+ Bin -> Bin
+ end.
+strong_rand_mpint_nif(_Bits, _Top, _Bottom) -> ?nif_stub.
-srp_value_B_nif(_Multiplier, _Verifier, _Generator, _Exponent, _Prime) -> ?nif_stub.
%% large integer in a binary with 32bit length
%% MP representaion (SSH2)
@@ -1594,32 +1682,6 @@ mpint_pos(X) ->
<<?UINT32(Sz), Bin/binary>>
end.
-int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []);
-int_to_bin(X) -> int_to_bin_pos(X, []).
-
-%%int_to_bin_pos(X) when X >= 0 ->
-%% int_to_bin_pos(X, []).
-
-int_to_bin_pos(0,Ds=[_|_]) ->
- list_to_binary(Ds);
-int_to_bin_pos(X,Ds) ->
- int_to_bin_pos(X bsr 8, [(X band 255)|Ds]).
-
-int_to_bin_neg(-1, Ds=[MSB|_]) when MSB >= 16#80 ->
- list_to_binary(Ds);
-int_to_bin_neg(X,Ds) ->
- int_to_bin_neg(X bsr 8, [(X band 255)|Ds]).
-
-bytes_to_integer(Bin) ->
- bin_to_int(Bin).
-
-bin_to_int(Bin) when is_binary(Bin) ->
- Bits = bit_size(Bin),
- <<Integer:Bits/integer>> = Bin,
- Integer;
-bin_to_int(undefined) ->
- undefined.
-
%% int from integer in a binary with 32bit length
erlint(<<MPIntSize:32/integer,MPIntValue/binary>>) ->
Bits= MPIntSize * 8,
@@ -1629,11 +1691,71 @@ erlint(<<MPIntSize:32/integer,MPIntValue/binary>>) ->
mpint_to_bin(<<Len:32, Bin:Len/binary>>) ->
Bin.
-random_bytes(N) ->
- try strong_rand_bytes(N) of
- RandBytes ->
- RandBytes
- catch
- error:low_entropy ->
- rand_bytes(N)
- end.
+%%
+%% mod_exp - utility for rsa generation and SRP
+%%
+mod_exp(Base, Exponent, Modulo)
+ when is_integer(Base), is_integer(Exponent), is_integer(Modulo) ->
+ bin_to_int(mod_exp_nif(int_to_bin(Base), int_to_bin(Exponent), int_to_bin(Modulo), 0));
+
+mod_exp(Base, Exponent, Modulo) ->
+ mod_exp_nif(mpint_to_bin(Base),mpint_to_bin(Exponent),mpint_to_bin(Modulo), 4).
+
+mod_exp_nif(_Base,_Exp,_Mod,_bin_hdr) -> ?nif_stub.
+
+-define(FUNC_LIST, [hash, hash_init, hash_update, hash_final,
+ hmac, hmac_init, hmac_update, hmac_final, hmac_final_n,
+ %% deprecated
+ md4, md4_init, md4_update, md4_final,
+ md5, md5_init, md5_update, md5_final,
+ sha, sha_init, sha_update, sha_final,
+ md5_mac, md5_mac_96,
+ sha_mac, sha_mac_96,
+ %%
+ block_encrypt, block_decrypt,
+ %% deprecated
+ des_cbc_encrypt, des_cbc_decrypt,
+ des_cfb_encrypt, des_cfb_decrypt,
+ des_ecb_encrypt, des_ecb_decrypt,
+ des3_cbc_encrypt, des3_cbc_decrypt,
+ des3_cfb_encrypt, des3_cfb_decrypt,
+ aes_cfb_128_encrypt, aes_cfb_128_decrypt,
+ rc2_cbc_encrypt, rc2_cbc_decrypt,
+ rc2_40_cbc_encrypt, rc2_40_cbc_decrypt,
+ aes_cbc_128_encrypt, aes_cbc_128_decrypt,
+ aes_cbc_256_encrypt, aes_cbc_256_decrypt,
+ blowfish_cbc_encrypt, blowfish_cbc_decrypt,
+ blowfish_cfb64_encrypt, blowfish_cfb64_decrypt,
+ blowfish_ecb_encrypt, blowfish_ecb_decrypt, blowfish_ofb64_encrypt,
+ %%
+ rand_bytes,
+ strong_rand_bytes,
+ rand_uniform,
+ mod_pow,
+ exor,
+ %% deprecated
+ mod_exp,strong_rand_mpint,erlint, mpint,
+ %%
+ sign, verify, generate_key, compute_key,
+ %% deprecated
+ dss_verify,dss_sign,
+ rsa_verify,rsa_sign,
+ rsa_public_encrypt,rsa_private_decrypt,
+ rsa_private_encrypt,rsa_public_decrypt,
+ dh_generate_key, dh_compute_key,
+ %%
+ stream_init, stream_encrypt, stream_decrypt,
+ %% deprecated
+ rc4_encrypt, rc4_set_key, rc4_encrypt_with_state,
+ aes_ctr_encrypt, aes_ctr_decrypt,
+ aes_ctr_stream_init, aes_ctr_stream_encrypt, aes_ctr_stream_decrypt,
+ %%
+ next_iv,
+ %% deprecated
+ aes_cbc_ivec,
+ des_cbc_ivec, des_cfb_ivec,
+ info,
+ %%
+ info_lib, supports]).
+info() ->
+ ?FUNC_LIST.
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index b3bb5dbd17..58aaa78d28 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -212,16 +212,8 @@ mod_pow(Config) when is_list(Config) ->
exor() ->
[{doc, "Test the exor function"}].
exor(Config) when is_list(Config) ->
- B = <<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>>,
- Z1 = zero_bin(B),
- Z1 = crypto:exor(B, B),
- B1 = crypto:rand_bytes(100),
- B2 = crypto:rand_bytes(100),
- Z2 = zero_bin(B1),
- Z2 = crypto:exor(B1, B1),
- Z2 = crypto:exor(B2, B2),
- R = xor_bytes(B1, B2),
- R = crypto:exor(B1, B2).
+ do_exor(<<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>>),
+ do_exor(term_to_binary(lists:seq(1, 1000000))).
%%--------------------------------------------------------------------
rand_uniform() ->
[{doc, "rand_uniform and random_bytes testing"}].
@@ -229,10 +221,10 @@ rand_uniform(Config) when is_list(Config) ->
rand_uniform_aux_test(10),
10 = byte_size(crypto:rand_bytes(10)),
10 = byte_size(crypto:strong_rand_bytes(10)).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
-
hash(_, [], []) ->
ok;
hash(Type, [Msg | RestMsg], [Digest| RestDigest]) ->
@@ -491,7 +483,7 @@ do_block_iolistify({Type, Key, IV, PlainText}) ->
iolistify(<<"Test With Truncation">>)->
%% Do not iolistify as it spoils this special case
<<"Test With Truncation">>;
-iolistify(Msg)->
+iolistify(Msg) when is_binary(Msg) ->
Length = erlang:byte_size(Msg),
Split = Length div 2,
List0 = binary_to_list(Msg),
@@ -500,7 +492,9 @@ iolistify(Msg)->
[[Element], List1, List2];
{List1, List2}->
[List1, List2]
- end.
+ end;
+iolistify(Msg) ->
+ iolistify(list_to_binary(Msg)).
des_iolistify(Msg) ->
des_iolist(erlang:byte_size(Msg) div 8, Msg, []).
@@ -540,6 +534,17 @@ ipow(A, B, M, Prod) ->
ipow(A1, B1, M, (A*Prod) rem M)
end.
+do_exor(B) ->
+ Z1 = zero_bin(B),
+ Z1 = crypto:exor(B, B),
+ B1 = crypto:rand_bytes(100),
+ B2 = crypto:rand_bytes(100),
+ Z2 = zero_bin(B1),
+ Z2 = crypto:exor(B1, B1),
+ Z2 = crypto:exor(B2, B2),
+ R = xor_bytes(B1, B2),
+ R = crypto:exor(B1, B2).
+
zero_bin(N) when is_integer(N) ->
N8 = N * 8,
<<0:N8/integer>>;
@@ -586,20 +591,20 @@ group_config(md4 = Type, Config) ->
group_config(md5 = Type, Config) ->
Msgs = rfc_1321_msgs(),
Digests = rfc_1321_md5_digests(),
- Keys = rfc_2202_md5_keys(),
- Data = rfc_2202_msgs(),
- Hmac = rfc_2202_hmac_md5(),
+ Keys = rfc_2202_md5_keys() ++ [long_hmac_key(md5)],
+ Data = rfc_2202_msgs() ++ [long_msg()],
+ Hmac = rfc_2202_hmac_md5() ++ [long_hmac(md5)],
[{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config];
group_config(ripemd160 = Type, Config) ->
Msgs = ripemd160_msgs(),
Digests = ripemd160_digests(),
[{hash, {Type, Msgs, Digests}} | Config];
group_config(sha = Type, Config) ->
- Msgs = [rfc_4634_test1(), rfc_4634_test2_1()],
- Digests = rfc_4634_sha_digests(),
- Keys = rfc_2202_sha_keys(),
- Data = rfc_2202_msgs(),
- Hmac = rfc_2202_hmac_sha(),
+ Msgs = [rfc_4634_test1(), rfc_4634_test2_1(),long_msg()],
+ Digests = rfc_4634_sha_digests() ++ [long_sha_digest()],
+ Keys = rfc_2202_sha_keys() ++ [long_hmac_key(sha)],
+ Data = rfc_2202_msgs() ++ [long_msg()],
+ Hmac = rfc_2202_hmac_sha() ++ [long_hmac(sha)],
[{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config];
group_config(sha224 = Type, Config) ->
Msgs = [rfc_4634_test1(), rfc_4634_test2_1()],
@@ -609,25 +614,25 @@ group_config(sha224 = Type, Config) ->
Hmac = rfc4231_hmac_sha224(),
[{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config];
group_config(sha256 = Type, Config) ->
- Msgs = [rfc_4634_test1(), rfc_4634_test2_1()],
- Digests = rfc_4634_sha256_digests(),
- Keys = rfc_4231_keys(),
- Data = rfc_4231_msgs(),
- Hmac = rfc4231_hmac_sha256(),
+ Msgs = [rfc_4634_test1(), rfc_4634_test2_1(), long_msg()],
+ Digests = rfc_4634_sha256_digests() ++ [long_sha256_digest()],
+ Keys = rfc_4231_keys() ++ [long_hmac_key(sha256)],
+ Data = rfc_4231_msgs() ++ [long_msg()],
+ Hmac = rfc4231_hmac_sha256() ++ [long_hmac(sha256)],
[{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config];
group_config(sha384 = Type, Config) ->
- Msgs = [rfc_4634_test1(), rfc_4634_test2()],
- Digests = rfc_4634_sha384_digests(),
- Keys = rfc_4231_keys(),
- Data = rfc_4231_msgs(),
- Hmac = rfc4231_hmac_sha384(),
+ Msgs = [rfc_4634_test1(), rfc_4634_test2(), long_msg()],
+ Digests = rfc_4634_sha384_digests() ++ [long_sha384_digest()],
+ Keys = rfc_4231_keys() ++ [long_hmac_key(sha384)],
+ Data = rfc_4231_msgs() ++ [long_msg()],
+ Hmac = rfc4231_hmac_sha384() ++ [long_hmac(sha384)],
[{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config];
group_config(sha512 = Type, Config) ->
- Msgs = [rfc_4634_test1(), rfc_4634_test2()],
- Digests = rfc_4634_sha512_digests(),
- Keys = rfc_4231_keys(),
- Data = rfc_4231_msgs(),
- Hmac = rfc4231_hmac_sha512(),
+ Msgs = [rfc_4634_test1(), rfc_4634_test2(), long_msg()],
+ Digests = rfc_4634_sha512_digests() ++ [long_sha512_digest()],
+ Keys = rfc_4231_keys() ++ [long_hmac_key(sha512)],
+ Data = rfc_4231_msgs() ++ [long_msg()],
+ Hmac = rfc4231_hmac_sha512() ++ [long_hmac(sha512)],
[{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config];
group_config(rsa = Type, Config) ->
Msg = rsa_plain(),
@@ -789,6 +794,23 @@ rfc_4634_sha512_digests() ->
"454D4423643CE80E2A9AC94FA54CA49F"),
hexstr2bin("8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA17299AEADB6889018501D289E4900F7E4331B99DEC4B5433AC7D329EEB6DD26545E96E55B874BE909")].
+long_msg() ->
+ lists:duplicate(1000000, $a).
+
+long_sha_digest() ->
+ hexstr2bin("34aa973c" "d4c4daa4" "f61eeb2b" "dbad2731" "6534016f").
+
+long_sha256_digest() ->
+ hexstr2bin("cdc76e5c" "9914fb92" "81a1c7e2" "84d73e67" "f1809a48" "a497200e" "046d39cc" "c7112cd0").
+
+long_sha384_digest() ->
+ hexstr2bin("9d0e1809716474cb" "086e834e310a4a1c" "ed149e9c00f24852" "7972cec5704c2a5b"
+ "07b8b3dc38ecc4eb" "ae97ddd87f3d8985").
+
+long_sha512_digest() ->
+ hexstr2bin("e718483d0ce76964" "4e2e42c7bc15b463" "8e1f98b13b204428" "5632a803afa973eb"
+ "de0ff244877ea60a" "4cb0432ce577c31b" "eb009c5c2c49aa2e" "4eadb217ad8cc09b").
+
ripemd160_msgs() ->
[<<"">>,
<<"a">>,
@@ -852,6 +874,35 @@ hmac_key(_) ->
hmac_inc(_) ->
[<<"Sampl">>, <<"e #1">>].
+%% https://www.cosic.esat.kuleuven.be/nessie/testvectors/
+long_hmac_key(Type) when Type == sha384;
+ Type == sha512 ->
+ hexstr2bin("00112233445566778899AABBCCDDEEFF"
+ "0123456789ABCDEF0011223344556677"
+ "8899AABBCCDDEEFF0123456789ABCDEF"
+ "00112233445566778899AABBCCDDEEFF");
+long_hmac_key(_) ->
+ hexstr2bin("0123456789ABCDEF0123456789ABCDEF"
+ "0123456789ABCDEF0123456789ABCDEF"
+ "0123456789ABCDEF0123456789ABCDEF"
+ "0123456789ABCDEF0123456789ABCDEF").
+long_hmac(md5) ->
+ hexstr2bin("82FDDA30202CB6ACC6F24D4F8A50EB7A");
+long_hmac(sha) ->
+ hexstr2bin("61D1D0B6459860755FDA892938C23DD401E54A7E");
+long_hmac(sha256) ->
+ hexstr2bin("50008B8DC7ED3926936347FDC1A01E9D"
+ "5220C6CC4B038B482C0F28A4CD88CA37");
+long_hmac(sha384) ->
+ hexstr2bin("C1EB08DAFA015833D3FC6B29A387558B"
+ "3F6FA1524AA1A8EB64798D5A76A39D6E"
+ "A1465525342E060EE996277B4FFCDDC9");
+long_hmac(sha512) ->
+ hexstr2bin("D116BF471AAE1264854F1906025E846A"
+ "61618A965FCA30B695220EA2D6E547E3"
+ "F3B5A4B54E6778928C26D5D3D810498E"
+ "8DF86CB3CC1E9F66A00419B13B6B0C9A").
+
rfc_2202_hmac_md5() ->
[
hexstr2bin("9294727a3638bb1c13f48ef8158bfc9d"),
@@ -1193,7 +1244,8 @@ blowfish_ofb64() ->
rc4() ->
[{rc4, <<"apaapa">>, <<"Yo baby yo">>},
- {rc4, <<"apaapa">>, list_to_binary(lists:seq(0, 255))}
+ {rc4, <<"apaapa">>, list_to_binary(lists:seq(0, 255))},
+ {rc4, <<"apaapa">>, lists:duplicate(1000000, $a)}
].
aes_ctr() ->
@@ -1237,7 +1289,11 @@ aes_ctr() ->
hexstr2bin("30c81c46a35ce411e5fbc1191a0a52ef")},
{aes_ctr, hexstr2bin("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"),
hexstr2bin("f0f1f2f3f4f5f6f7f8f9fafbfcfdff02"),
- hexstr2bin("f69f2445df4f9b17ad2b417be66c3710")}
+ hexstr2bin("f69f2445df4f9b17ad2b417be66c3710")},
+
+ {aes_ctr, hexstr2bin("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"),
+ hexstr2bin("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"),
+ lists:duplicate(1000000, $a)}
].
rsa_plain() ->
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index 9287e105df..6f08d380ca 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1 +1 @@
-EI_VSN = 3.7.12
+EI_VSN = 3.7.13
diff --git a/lib/et/vsn.mk b/lib/et/vsn.mk
index 99532ee3f3..40cdc2b298 100644
--- a/lib/et/vsn.mk
+++ b/lib/et/vsn.mk
@@ -1 +1 @@
-ET_VSN = 1.4.4.3
+ET_VSN = 1.4.4.4
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index aada1252ad..3ea530a366 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -947,21 +947,34 @@ gethostbyname_self(Name, Type) when is_atom(Name) ->
gethostbyname_self(Name, Type)
when is_list(Name), Type =:= inet;
is_list(Name), Type =:= inet6 ->
- case inet_db:gethostname() of
- Name ->
- {ok,make_hostent(Name,
- [translate_ip(loopback, Type)],
- [], Type)};
- Self ->
+ N = inet_db:tolower(Name),
+ Self = inet_db:gethostname(),
+ %%
+ %% This is the final fallback that pretends /etc/hosts has got
+ %% a line for the hostname on the loopback address.
+ %% Lookups into /etc/hosts are case insensitive and return
+ %% what is in the file. Therefore the letter case may differ between
+ %% the returned hostent record and the hostname that was asked for.
+ %%
+ case inet_db:tolower(Self) of
+ N ->
+ {ok,
+ make_hostent(
+ Self, [translate_ip(loopback, Type)], [], Type)};
+ _ ->
case inet_db:res_option(domain) of
- "" -> {error,nxdomain};
+ "" ->
+ {error,nxdomain};
Domain ->
- case lists:append([Self,".",Domain]) of
- Name ->
- {ok,make_hostent(Name,
- [translate_ip(loopback, Type)],
- [], Type)};
- _ -> {error,nxdomain}
+ FQDN = lists:append([Self,".",Domain]),
+ case inet_db:tolower(FQDN) of
+ N ->
+ {ok,
+ make_hostent(
+ FQDN,
+ [translate_ip(loopback, Type)], [], Type)};
+ _ ->
+ {error,nxdomain}
end
end
end;
diff --git a/lib/kernel/src/inet_db.erl b/lib/kernel/src/inet_db.erl
index d4749b9756..a7679c531b 100644
--- a/lib/kernel/src/inet_db.erl
+++ b/lib/kernel/src/inet_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -845,7 +845,8 @@ init([]) ->
process_flag(trap_exit, true),
Db = ets:new(inet_db, [public, named_table]),
reset_db(Db),
- Cache = ets:new(inet_cache, [public, bag, {keypos,2}, named_table]),
+ CacheOpts = [public, bag, {keypos,#dns_rr.domain}, named_table],
+ Cache = ets:new(inet_cache, CacheOpts),
BynameOpts = [protected, bag, named_table, {keypos,1}],
ByaddrOpts = [protected, bag, named_table, {keypos,3}],
HostsByname = ets:new(inet_hosts_byname, BynameOpts),
@@ -901,15 +902,21 @@ reset_db(Db) ->
handle_call(Request, From, #state{db=Db}=State) ->
case Request of
{load_hosts_file,IPNmAs} when is_list(IPNmAs) ->
- NIPs = lists:flatten([ [{N,if tuple_size(IP) =:= 4 -> inet;
- tuple_size(IP) =:= 8 -> inet6
- end,IP} || N <- [Nm|As]]
- || {IP,Nm,As} <- IPNmAs]),
+ NIPs =
+ lists:flatten(
+ [ [{N,
+ if tuple_size(IP) =:= 4 -> inet;
+ tuple_size(IP) =:= 8 -> inet6
+ end,IP} || N <- [Nm|As]]
+ || {IP,Nm,As} <- IPNmAs]),
Byname = State#state.hosts_file_byname,
Byaddr = State#state.hosts_file_byaddr,
ets:delete_all_objects(Byname),
ets:delete_all_objects(Byaddr),
- ets:insert(Byname, NIPs),
+ %% Byname has lowercased names while Byaddr keep the name casing.
+ %% This is to be able to reconstruct the original
+ %% /etc/hosts entry.
+ ets:insert(Byname, [{tolower(N),Type,IP} || {N,Type,IP} <- NIPs]),
ets:insert(Byaddr, NIPs),
{reply, ok, State};
@@ -938,16 +945,14 @@ handle_call(Request, From, #state{db=Db}=State) ->
{reply, ok, State};
{add_rr, RR} when is_record(RR, dns_rr) ->
- RR1 = lower_rr(RR),
- ?dbg("add_rr: ~p~n", [RR1]),
- do_add_rr(RR1, Db, State),
+ ?dbg("add_rr: ~p~n", [RR]),
+ do_add_rr(RR, Db, State),
{reply, ok, State};
{del_rr, RR} when is_record(RR, dns_rr) ->
- RR1 = lower_rr(RR),
%% note. del_rr will handle wildcards !!!
Cache = State#state.cache,
- ets:match_delete(Cache, RR1),
+ ets:match_delete(Cache, RR),
{reply, ok, State};
{lookup_rr, Domain, Class, Type} ->
@@ -1225,15 +1230,18 @@ handle_set_file(ParseFun, Bin, From, State) ->
handle_rc_list(Opts, From, State)
end.
+%% Byname has lowercased names while Byaddr keep the name casing.
+%% This is to be able to reconstruct the original /etc/hosts entry.
+
do_add_host(Byname, Byaddr, Names, Type, IP) ->
do_del_host(Byname, Byaddr, IP),
- NIPs = [{tolower(N),Type,IP} || N <- Names],
- ets:insert(Byname, NIPs),
- ets:insert(Byaddr, NIPs),
+ ets:insert(Byname, [{tolower(N),Type,IP} || N <- Names]),
+ ets:insert(Byaddr, [{N,Type,IP} || N <- Names]),
ok.
do_del_host(Byname, Byaddr, IP) ->
- [ets:delete_object(Byname, NIP) || NIP <- ets:lookup(Byaddr, IP)],
+ [ets:delete_object(Byname, {tolower(Name),Type,Addr}) ||
+ {Name,Type,Addr} <- ets:lookup(Byaddr, IP)],
ets:delete(Byaddr, IP),
ok.
@@ -1369,7 +1377,7 @@ times() ->
%% lookup and remove old entries
do_lookup_rr(Domain, Class, Type) ->
- match_rr(#dns_rr{domain = tolower(Domain), class = Class,type = Type,
+ match_rr(#dns_rr{domain = Domain, class = Class,type = Type,
cnt = '_', tm = '_', ttl = '_',
bm = '_', func = '_', data = '_'}).
@@ -1393,23 +1401,20 @@ filter_rr([], _Time) -> [].
%%
-%% Lower case the domain name before storage
-%%
-lower_rr(RR) ->
- Dn = RR#dns_rr.domain,
- if is_list(Dn) ->
- RR#dns_rr { domain = tolower(Dn) };
- true -> RR
- end.
-
+%% Case fold upper-case to lower-case according to RFC 4343
+%% "Domain Name System (DNS) Case Insensitivity Clarification".
%%
-%% Map upper-case to lower-case
%% NOTE: this code is in kernel and we don't want to relay
-%% to much on stdlib
+%% to much on stdlib. Furthermore string:to_lower/1
+%% does not follow RFC 4343.
%%
tolower([]) -> [];
-tolower([C|Cs]) when C >= $A, C =< $Z -> [(C-$A)+$a|tolower(Cs)];
-tolower([C|Cs]) -> [C|tolower(Cs)].
+tolower([C|Cs]) when is_integer(C) ->
+ if C >= $A, C =< $Z ->
+ [(C-$A)+$a|tolower(Cs)];
+ true ->
+ [C|tolower(Cs)]
+ end.
dn_ip6_int(A,B,C,D,E,F,G,H) ->
dnib(H) ++ dnib(G) ++ dnib(F) ++ dnib(E) ++
diff --git a/lib/kernel/src/inet_hosts.erl b/lib/kernel/src/inet_hosts.erl
index df1d4fc0be..6e9719b4aa 100644
--- a/lib/kernel/src/inet_hosts.erl
+++ b/lib/kernel/src/inet_hosts.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,9 +38,12 @@ gethostbyname(_) -> {error, formerr}.
gethostbyname(Name, Type) when is_list(Name), is_atom(Type) ->
- case gethostbyname(Name, Type, inet_hosts_byname, inet_hosts_byaddr) of
+ %% Byname has lowercased names while Byaddr keep the name casing.
+ %% This is to be able to reconstruct the original /etc/hosts entry.
+ N = inet_db:tolower(Name),
+ case gethostbyname(N, Type, inet_hosts_byname, inet_hosts_byaddr) of
false ->
- case gethostbyname(Name, Type,
+ case gethostbyname(N, Type,
inet_hosts_file_byname,
inet_hosts_file_byaddr) of
false -> {error,nxdomain};
diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl
index 59ba408d7a..94a9f7c64d 100644
--- a/lib/kernel/src/inet_res.erl
+++ b/lib/kernel/src/inet_res.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -181,8 +181,8 @@ lookup(Name, Class, Type, Opts, Timeout) ->
lookup_filter({ok,#dns_rec{anlist=Answers}}, Class, Type) ->
[A#dns_rr.data || A <- Answers,
- A#dns_rr.class =:= Class,
- A#dns_rr.type =:= Type];
+ Class =:= any orelse A#dns_rr.class =:= Class,
+ Type =:= any orelse A#dns_rr.type =:= Type];
lookup_filter({error,_}, _, _) -> [].
%% --------------------------------------------------------------------------
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 1f7724d0dc..62ba95e1a3 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -183,80 +183,74 @@ t_gethostbyname(Config) when is_list(Config) ->
h_addr_list = [IP]},
?line HEntF_ = HEntF,
?line check_elems([{HEnt#hostent.h_aliases,[[],Aliases]}]),
+ %%
+ ?line FullNameU = toupper(FullName),
+ ?line {ok,HEntU} = inet:gethostbyname(FullNameU),
+ ?line FullNameU = toupper(HEntU#hostent.h_name),
+ ?line #hostent{
+ h_addrtype = inet,
+ h_length = 4,
+ h_addr_list = [IP]} = HEntU,
+ ?line check_elems(
+ [{[toupper(H) || H <- HEntU#hostent.h_aliases],
+ [[],[toupper(A) || A <- Aliases]]}]),
?line {DName, _DFullName, _DIPStr, _DIP, _, _, _} =
ct:get_config(test_dummy_host),
?line {error,nxdomain} = inet:gethostbyname(DName),
- ?line {error,nxdomain} = inet:gethostbyname(IP_46_Str).
+ ?line {error,nxdomain} = inet:gethostbyname(IP_46_Str),
+ ok.
t_gethostbyname_v6() -> required(v6).
t_gethostbyname_v6(doc) -> "Test the inet:gethostbyname/1 inet6 function.";
t_gethostbyname_v6(suite) -> [];
t_gethostbyname_v6(Config) when is_list(Config) ->
- ?line {Name, _, _, _,Aliases,IP_46_Str,IP_46} =
- ct:get_config(test_host_ipv4_only),
+ {Name, FullName, IPStr, IP, Aliases} =
+ ct:get_config(test_host_ipv6_only),
- case {inet:gethostbyname(IP_46_Str, inet6),
- inet:gethostbyname(Name, inet6)} of
- {{ok,HEnt46},{ok,_}} ->
- ?line HEnt46_ = HEnt46#hostent{h_name = IP_46_Str,
- h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IP_46]},
- ?line HEnt46_ = HEnt46,
- ?line check_elems([{HEnt46#hostent.h_aliases,[[],Aliases]}]),
-
- ?line {Name6, FullName6, IPStr6, IP6, Aliases6} =
- ct:get_config(test_host_ipv6_only),
- ?line {ok,_} = inet:gethostbyname(IPStr6, inet6),
- ?line {ok,HEnt6} = inet:gethostbyname(Name6, inet6),
- ?line {ok,HEnt6} = inet:gethostbyname(list_to_atom(Name6), inet6),
- ?line case HEnt6#hostent.h_addr_list of
- [IP6] -> % ipv6 ok
- ?line HEnt6_ = HEnt6#hostent{h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IP6]},
- ?line HEnt6_ = HEnt6,
- ?line check_elems([{HEnt6#hostent.h_name,[Name6,FullName6]},
- {HEnt6#hostent.h_aliases,[[],Aliases6]}]);
- _ -> % ipv4 compatible addr
- ?line {ok,HEnt4} = inet:gethostbyname(Name6, inet),
- ?line [IP4] = HEnt4#hostent.h_addr_list,
- ?line {ok,IP46_2} =
- inet_parse:ipv6_address("::ffff:"++inet_parse:ntoa(IP4)),
- ?line HEnt6_ = HEnt6#hostent{h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IP46_2]},
- ?line HEnt6_ = HEnt6,
- ?line check_elems([{HEnt6#hostent.h_name,[Name6,FullName6]}])
- end,
-
- ?line {ok,HEntF6} = inet:gethostbyname(FullName6, inet6),
- ?line case HEntF6#hostent.h_addr_list of
- [IP6] -> % ipv6 ok
- ?line HEntF6_ = HEntF6#hostent{h_name = FullName6,
- h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IP6]},
- ?line HEntF6_ = HEntF6,
- ?line check_elems([{HEntF6#hostent.h_aliases,[[],Aliases6]}]);
- _ -> % ipv4 compatible addr
- ?line {ok,HEntF4} = inet:gethostbyname(FullName6, inet),
- ?line [IPF4] = HEntF4#hostent.h_addr_list,
- ?line {ok,IPF46_2} =
- inet_parse:ipv6_address("::ffff:"++inet_parse:ntoa(IPF4)),
- ?line HEntF6_ = HEntF6#hostent{h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IPF46_2]},
- ?line HEntF6_ = HEntF6,
- ?line check_elems([{HEntF6#hostent.h_name,[Name6,FullName6]}])
- end,
-
- ?line {DName6, _DFullName6, _DIPStr6, _DIP6, _} =
- ct:get_config(test_dummy_ipv6_host),
- ?line {error,nxdomain} = inet:gethostbyname(DName6, inet6),
- ok;
- {_,_} ->
+ case inet:gethostbyname(Name, inet6) of
+ {ok,HEnt} ->
+ {ok,_} = inet:gethostbyname(IPStr, inet6),
+ {ok,HEnt} = inet:gethostbyname(list_to_atom(Name), inet6),
+ case HEnt#hostent.h_addr_list of
+ [IP] -> % IPv6 address
+ #hostent{h_addrtype = inet6,
+ h_length = 16} = HEnt,
+ check_elems(
+ [{HEnt#hostent.h_name,[Name,FullName]},
+ {HEnt#hostent.h_aliases,[[],Aliases]}]);
+ [IP46] -> % IPv4 compatible address
+ {ok,HEnt4} = inet:gethostbyname(Name, inet),
+ #hostent{h_addrtype = inet,
+ h_length = 4,
+ h_addr_list = [IP4]} = HEnt4,
+ {ok,IP46} =
+ inet_parse:ipv6_address(
+ "::ffff:" ++ inet_parse:ntoa(IP4)),
+ check_elems(
+ [{HEnt#hostent.h_name,[Name,FullName]}])
+ end,
+
+ {ok,HEntF} = inet:gethostbyname(FullName, inet6),
+ case HEntF#hostent.h_addr_list of
+ [IP] -> % IPv6 address
+ #hostent{h_name = FullName,
+ h_addrtype = inet6,
+ h_length = 16} = HEntF,
+ check_elems(
+ [{HEnt#hostent.h_aliases,[[],Aliases]}]);
+ [IP46F] -> % IPv4 compatible address
+ {ok,HEnt4F} = inet:gethostbyname(FullName, inet),
+ #hostent{h_addrtype = inet,
+ h_length = 4,
+ h_addr_list = [IP4F]} = HEnt4F,
+ {ok,IP46F} =
+ inet_parse:ipv6_address(
+ "::ffff:" ++ inet_parse:ntoa(IP4F)),
+ check_elems(
+ [{HEntF#hostent.h_name,[Name,FullName]}])
+ end;
+ _ ->
{skip, "IPv6 is not supported on this host"}
end.
@@ -290,47 +284,35 @@ t_getaddr(Config) when is_list(Config) ->
?line {error,nxdomain} = inet:getaddr(DName, inet),
?line {error,nxdomain} = inet:getaddr(DFullName, inet),
?line {ok,DIP} = inet:getaddr(DIPStr, inet),
- ?line {ok,DIP} = inet:getaddr(DIP, inet).
+ ?line {ok,DIP} = inet:getaddr(DIP, inet),
+ ok.
t_getaddr_v6() -> required(v4) ++ required(v6).
t_getaddr_v6(doc) -> "Test the inet:getaddr/2 function.";
t_getaddr_v6(suite) -> [];
t_getaddr_v6(Config) when is_list(Config) ->
- ?line {Name,FullName,IPStr,_IP,_,IP_46_Str,IP46} =
- ct:get_config(test_host_ipv4_only),
- case {inet:getaddr(IP_46_Str, inet6),inet:getaddr(Name, inet6)} of
- {{ok,IP46},{ok,V4Addr}} when V4Addr /= {0,0,0,0,0,0,0,1} ->
- %% Since we suceeded in parsing an IPv6 address string and
- %% look up the name, this computer fully supports IPv6.
- ?line {ok,IP46} = inet:getaddr(IP46, inet6),
- ?line {ok,IP46} = inet:getaddr(Name, inet6),
- ?line {ok,IP46} = inet:getaddr(FullName, inet6),
- ?line {ok,IP46} = inet:getaddr(IPStr, inet6),
-%% ?line IP4toIP6 = inet:getaddr(IPStr, inet6),
-%% ?line case IP4toIP6 of
-%% {ok,IP46} ->
-%% ?line ok;
-%% {error,nxdomain} ->
-%% ?line false =
-%% lists:member(native,
-%% inet_db:res_option(lookup))
-%% end,
- ?line {Name6, FullName6, IPStr6, IP6, _} =
- ct:get_config(test_host_ipv6_only),
- ?line {ok,_} = inet:getaddr(list_to_atom(Name6), inet6),
- ?line {ok,_} = inet:getaddr(Name6, inet6),
- ?line {ok,_} = inet:getaddr(FullName6, inet6),
- ?line {ok,IP6} = inet:getaddr(IP6, inet6),
- ?line {ok,IP6} = inet:getaddr(IPStr6, inet6),
-
- ?line {DName6, DFullName6, DIPStr6, DIP6, _} =
+ {Name,FullName,IPStr,IP,_} =
+ ct:get_config(test_host_ipv6_only),
+
+ case inet:getaddr(Name, inet6) of
+ {ok,Addr} ->
+ IP = Addr,
+ {ok,IP} = inet:getaddr(toupper(Name), inet6),
+ {ok,IP} = inet:getaddr(list_to_atom(Name), inet6),
+ {ok,IP} = inet:getaddr(list_to_atom(toupper(Name)), inet6),
+ {ok,IP} = inet:getaddr(FullName, inet6),
+ {ok,IP} = inet:getaddr(toupper(FullName), inet6),
+ {ok,IP} = inet:getaddr(IP, inet6),
+ {ok,IP} = inet:getaddr(IPStr, inet6),
+ %%
+ {DName,DFullName,DIPStr,DIP,_} =
ct:get_config(test_dummy_ipv6_host),
- ?line {error,nxdomain} = inet:getaddr(DName6, inet6),
- ?line {error,nxdomain} = inet:getaddr(DFullName6, inet6),
- ?line {ok,DIP6} = inet:getaddr(DIPStr6, inet6),
- ?line {ok,DIP6} = inet:getaddr(DIP6, inet6),
+ {error,nxdomain} = inet:getaddr(DName, inet6),
+ {error,nxdomain} = inet:getaddr(DFullName, inet6),
+ {ok,DIP} = inet:getaddr(DIPStr, inet6),
+ {ok,DIP} = inet:getaddr(DIP, inet6),
ok;
- {_,_} ->
+ _ ->
{skip, "IPv6 is not supported on this host"}
end.
@@ -608,8 +590,12 @@ t_parse_address(Func, [String|L]) ->
t_parse_address(Func, L).
parse_strict_address(Config) when is_list(Config) ->
- {ok, Ipv4} = inet:parse_strict_address("127.0.0.1"),
- {ok, Ipv6} = inet:parse_strict_address("c11:0c22:5c33:c440:55c0:c66c:77:0088").
+ {ok, {127,0,0,1}} =
+ inet:parse_strict_address("127.0.0.1"),
+ {ok, {3089,3106,23603,50240,21952,50796,119,136}} =
+ inet:parse_strict_address("c11:0c22:5c33:c440:55c0:c66c:77:0088"),
+ {ok, {3089,3106,23603,50240,0,0,119,136}} =
+ inet:parse_strict_address("c11:0c22:5c33:c440::077:0088").
t_gethostnative(suite) ->[];
t_gethostnative(doc) ->[];
@@ -1102,3 +1088,14 @@ ip_member({127,_,_,_}, [{127,_,_,_}|_]) -> true;
ip_member(K, [K|_]) -> true;
ip_member(K, [_|T]) -> ip_member(K, T);
ip_member(_, []) -> false.
+
+%% Case fold to upper case according to RFC 4343
+%%
+toupper([C|Cs]) when is_integer(C) ->
+ if $a =< C, C =< $z ->
+ [(C - $a + $A)|toupper(Cs)];
+ true ->
+ [C|toupper(Cs)]
+ end;
+toupper([]) ->
+ [].
diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl
index f3ba28e4f9..1bc93e3138 100644
--- a/lib/kernel/test/inet_res_SUITE.erl
+++ b/lib/kernel/test/inet_res_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -282,6 +282,7 @@ basic(doc) ->
basic(Config) when is_list(Config) ->
NS = ns(Config),
Name = "ns.otptest",
+ NameC = caseflip(Name),
IP = {127,0,0,254},
%%
%% nslookup
@@ -292,6 +293,17 @@ basic(Config) when is_list(Config) ->
Bin1 = inet_dns:encode(Msg1),
%%io:format("Bin1 = ~w~n", [Bin1]),
{ok,Msg1} = inet_dns:decode(Bin1),
+ %% Now with scrambled case
+ {ok,Msg1b} = inet_res:nslookup(NameC, in, a, [NS]),
+ io:format("~p~n", [Msg1b]),
+ [RR1b] = inet_dns:msg(Msg1b, anlist),
+ IP = inet_dns:rr(RR1b, data),
+ Bin1b = inet_dns:encode(Msg1b),
+ %%io:format("Bin1b = ~w~n", [Bin1b]),
+ {ok,Msg1b} = inet_dns:decode(Bin1b),
+ true =
+ (tolower(inet_dns:rr(RR1, domain))
+ =:= tolower(inet_dns:rr(RR1b, domain))),
%%
%% resolve
{ok,Msg2} = inet_res:resolve(Name, in, a, [{nameservers,[NS]},verbose]),
@@ -301,15 +313,29 @@ basic(Config) when is_list(Config) ->
Bin2 = inet_dns:encode(Msg2),
%%io:format("Bin2 = ~w~n", [Bin2]),
{ok,Msg2} = inet_dns:decode(Bin2),
+ %% Now with scrambled case
+ {ok,Msg2b} = inet_res:resolve(NameC, in, a, [{nameservers,[NS]},verbose]),
+ io:format("~p~n", [Msg2b]),
+ [RR2b] = inet_dns:msg(Msg2b, anlist),
+ IP = inet_dns:rr(RR2b, data),
+ Bin2b = inet_dns:encode(Msg2b),
+ %%io:format("Bin2b = ~w~n", [Bin2b]),
+ {ok,Msg2b} = inet_dns:decode(Bin2b),
+ true =
+ (tolower(inet_dns:rr(RR2, domain))
+ =:= tolower(inet_dns:rr(RR2b, domain))),
%%
%% lookup
[IP] = inet_res:lookup(Name, in, a, [{nameservers,[NS]},verbose]),
+ [IP] = inet_res:lookup(NameC, in, a, [{nameservers,[NS]},verbose]),
%%
%% gethostbyname
{ok,#hostent{h_addr_list=[IP]}} = inet_res:gethostbyname(Name),
+ {ok,#hostent{h_addr_list=[IP]}} = inet_res:gethostbyname(NameC),
%%
%% getbyname
{ok,#hostent{h_addr_list=[IP]}} = inet_res:getbyname(Name, a),
+ {ok,#hostent{h_addr_list=[IP]}} = inet_res:getbyname(NameC, a),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -317,63 +343,115 @@ basic(Config) when is_list(Config) ->
resolve(doc) ->
["Lookup different records using resolve/2..4"];
resolve(Config) when is_list(Config) ->
+ Class = in,
NS = ns(Config),
Domain = "otptest",
RDomain4 = "0.0.127.in-addr.arpa",
RDomain6 = "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa",
Name = "resolve."++Domain,
- L = [{in,a,Name,[{127,0,0,28}],undefined},
- {in,aaaa,Name,[{0,0,0,0,0,0,32512,28}],undefined},
- {in,cname,"cname."++Name,[Name],undefined},
- {in,a,"cname."++Name,[Name,{127,0,0,28}],undefined},
- {in,ns,"ns."++Name,[],[Name]},
- {in,soa,Domain,[],[{"ns.otptest","lsa.otptest",1,60,10,300,30}]},
+ L = [{a,Name,[{a,{127,0,0,28}}],undefined},
+ {aaaa,Name,[{aaaa,{0,0,0,0,0,0,32512,28}}],undefined},
+ {cname,"cname."++Name,[{cname,Name}],undefined},
+ {a,"cname."++Name,[{cname,Name},{a,{127,0,0,28}}],undefined},
+ {ns,"ns."++Name,[],[{ns,Name}]},
+ {soa,Domain,[],[{soa,{"ns.otptest","lsa.otptest",1,60,10,300,30}}]},
%% WKS: protocol TCP (6), services (bits) TELNET (23) and SMTP (25)
- {in,wks,"wks."++Name,[{{127,0,0,28},6,<<0,0,1,64>>}],undefined},
- {in,ptr,"28."++RDomain4,[Name],undefined},
- {in,ptr,"c.1.0.0.0.0.f.7."++RDomain6,[Name],undefined},
- {in,hinfo,Name,[{"BEAM","Erlang/OTP"}],undefined},
- {in,mx,RDomain4,[{10,"mx."++Domain}],undefined},
- {in,srv,"_srv._tcp."++Name,[{10,3,4711,Name}],undefined},
- {in,naptr,"naptr."++Name,
- [{10,5,"s","http","","_srv._tcp."++Name}],undefined},
- {in,txt,"txt."++Name,
- [["Hej ","du ","glade "],["ta ","en ","spade!"]],undefined},
- {in,mb,"mb."++Name,["mx."++Name],undefined},
- {in,mg,"mg."++Name,["lsa."++Domain],undefined},
- {in,mr,"mr."++Name,["lsa."++Domain],undefined},
- {in,minfo,"minfo."++Name,
- [{"minfo-owner."++Name,"minfo-bounce."++Name}],undefined},
- {in,any,"cname."++Name,[Name],undefined},
- {in,any,Name,[{127,0,0,28},
- {0,0,0,0,0,0,32512,28},
- {"BEAM","Erlang/OTP"}],undefined}
+ {wks,"wks."++Name,[{wks,{{127,0,0,28},6,<<0,0,1,64>>}}],undefined},
+ {ptr,"28."++RDomain4,[{ptr,Name}],undefined},
+ {ptr,"c.1.0.0.0.0.f.7."++RDomain6,[{ptr,Name}],undefined},
+ {hinfo,Name,[{hinfo,{"BEAM","Erlang/OTP"}}],undefined},
+ {mx,RDomain4,[{mx,{10,"mx."++Domain}}],undefined},
+ {srv,"_srv._tcp."++Name,[{srv,{10,3,4711,Name}}],undefined},
+ {naptr,"naptr."++Name,
+ [{naptr,{10,5,"s","http","","_srv._tcp."++Name}}],
+ undefined},
+ {txt,"txt."++Name,
+ [{txt,["Hej ","du ","glade "]},{txt,["ta ","en ","spade!"]}],
+ undefined},
+ {mb,"mb."++Name,[{mb,"mx."++Name}],undefined},
+ {mg,"mg."++Name,[{mg,"Lsa."++Domain}],undefined},
+ {mr,"mr."++Name,[{mr,"LSA."++Domain}],undefined},
+ {minfo,"minfo."++Name,
+ [{minfo,{"minfo-OWNER."++Name,"MinfoBounce."++Name}}],
+ undefined},
+ {any,"cname."++Name,[{cname,Name}],undefined},
+ {any,Name,
+ [{a,{127,0,0,28}},
+ {aaaa,{0,0,0,0,0,0,32512,28}},
+ {hinfo,{"BEAM","Erlang/OTP"}}],
+ undefined}
],
- resolve([{edns,false},{nameservers,[NS]}], L),
- resolve([{edns,0},{nameservers,[NS]}], L).
-
-resolve(_Opts, []) -> ok;
-resolve(Opts, [{Class,Type,Name,Answers,Authority}=Q|Qs]) ->
+ resolve(Class, [{edns,0},{nameservers,[NS]}], L),
+ resolve(Class, [{edns,false},{nameservers,[NS]}], L),
+ %% Again, to see ensure the cache does not mess things up
+ resolve(Class, [{edns,0},{nameservers,[NS]}], L),
+ resolve(Class, [{edns,false},{nameservers,[NS]}], L).
+
+resolve(_Class, _Opts, []) ->
+ ok;
+resolve(Class, Opts, [{Type,Nm,Answers,Authority}=Q|Qs]) ->
io:format("Query: ~p~nOptions: ~p~n", [Q,Opts]),
- {ok,Msg} = inet_res:resolve(Name, Class, Type, Opts),
+ {Name,NameC} =
+ case erlang:phash2(Q) band 4 of
+ 0 ->
+ {Nm,caseflip(Nm)};
+ _ ->
+ {caseflip(Nm),Nm}
+ end,
AnList =
if
Answers =/= undefined ->
- lists:sort(Answers);
+ normalize_answers(Answers);
true ->
undefined
end,
NsList =
if
Authority =/= undefined ->
- lists:sort(Authority);
+ normalize_answers(Authority);
true ->
undefined
end,
- case {lists:sort
- ([inet_dns:rr(RR, data) || RR <- inet_dns:msg(Msg, anlist)]),
- lists:sort
- ([inet_dns:rr(RR, data) || RR <- inet_dns:msg(Msg, nslist)])} of
+ {ok,Msg} = inet_res:resolve(Name, Class, Type, Opts),
+ check_msg(Class, Type, Msg, AnList, NsList),
+ {ok,MsgC} = inet_res:resolve(NameC, Class, Type, Opts),
+ check_msg(Class, Type, MsgC, AnList, NsList),
+ resolve(Class, Opts, Qs).
+
+
+
+normalize_answers(AnList) ->
+ lists:sort([normalize_answer(Answer) || Answer <- AnList]).
+
+normalize_answer({soa,{NS,HM,Ser,Ref,Ret,Exp,Min}}) ->
+ {tolower(NS),tolower_email(HM),Ser,Ref,Ret,Exp,Min};
+normalize_answer({mx,{Prio,DN}}) ->
+ {Prio,tolower(DN)};
+normalize_answer({srv,{Prio,Weight,Port,DN}}) ->
+ {Prio,Weight,Port,tolower(DN)};
+normalize_answer({naptr,{Order,Pref,Flags,Service,RE,Repl}}) ->
+ {Order,Pref,Flags,Service,RE,tolower(Repl)};
+normalize_answer({minfo,{RespM,ErrM}}) ->
+ {tolower_email(RespM),tolower_email(ErrM)};
+normalize_answer({T,MN}) when T =:= mg; T =:= mr ->
+ tolower_email(MN);
+normalize_answer({T,DN}) when T =:= cname; T =:= ns; T =:= ptr; T =:= mb ->
+ tolower(DN);
+normalize_answer(Answer) ->
+ Answer.
+
+check_msg(Class, Type, Msg, AnList, NsList) ->
+ io:format("check_msg Type: ~p, Msg: ~p~n.", [Type,Msg]),
+ case {normalize_answers(
+ [begin
+ Class = inet_dns:rr(RR, class),
+ {inet_dns:rr(RR, type),inet_dns:rr(RR, data)}
+ end || RR <- inet_dns:msg(Msg, anlist)]),
+ normalize_answers(
+ [begin
+ Class = inet_dns:rr(RR, class),
+ {inet_dns:rr(RR, type),inet_dns:rr(RR, data)}
+ end || RR <- inet_dns:msg(Msg, nslist)])} of
{AnList,NsList} ->
ok;
{NsList,AnList} when Type =:= ns ->
@@ -389,7 +467,7 @@ resolve(Opts, [{Class,Type,Name,Answers,Authority}=Q|Qs]) ->
end,
Buf = inet_dns:encode(Msg),
{ok,Msg} = inet_dns:decode(Buf),
- resolve(Opts, Qs).
+ ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -497,6 +575,7 @@ files_monitor(Config) when is_list(Config) ->
do_files_monitor(Config) ->
Dir = ?config(priv_dir, Config),
{ok,Hostname} = inet:gethostname(),
+ io:format("Hostname = ~p.~n", [Hostname]),
FQDN =
case inet_db:res_option(domain) of
"" ->
@@ -504,11 +583,13 @@ do_files_monitor(Config) ->
_ ->
Hostname++"."++inet_db:res_option(domain)
end,
+ io:format("FQDN = ~p.~n", [FQDN]),
HostsFile = filename:join(Dir, "files_monitor_hosts"),
ResolvConf = filename:join(Dir, "files_monitor_resolv.conf"),
ok = inet_db:res_option(resolv_conf, ResolvConf),
ok = inet_db:res_option(hosts_file, HostsFile),
[] = inet_db:res_option(search),
+ %% The inet function will use its final fallback to find this host
{ok,#hostent{h_name = Hostname,
h_addrtype = inet,
h_length = 4,
@@ -521,6 +602,7 @@ do_files_monitor(Config) ->
{error,nxdomain} = inet_res:gethostbyname(FQDN),
{ok,{127,0,0,10}} = inet:getaddr("mx.otptest", inet),
{ok,{0,0,0,0,0,0,32512,28}} = inet:getaddr("resolve.otptest", inet6),
+ %% The inet function will use its final fallback to find this host
{ok,#hostent{h_name = Hostname,
h_addrtype = inet6,
h_length = 16,
@@ -603,3 +685,41 @@ ipv4_to_ipv6() -> inet_SUITE:ipv4_to_ipv6().
ipv4_to_ipv6(Config) -> inet_SUITE:ipv4_to_ipv6(Config).
host_and_addr() -> inet_SUITE:host_and_addr().
host_and_addr(Config) -> inet_SUITE:host_and_addr(Config).
+
+
+
+%% Case flip helper
+
+caseflip([C|Cs]) when is_integer(C), $a =< C, C =< $z ->
+ [(C - $a + $A)|caseflip_skip(Cs)];
+caseflip([C|Cs]) when is_integer(C), $A =< C, C =< $Z ->
+ [(C - $A + $a)|caseflip_skip(Cs)];
+caseflip([C|Cs]) ->
+ [C|caseflip(Cs)];
+caseflip([]) ->
+ [].
+
+caseflip_skip([C|Cs]) when is_integer(C), $a =< C, C =< $z ->
+ [C|caseflip(Cs)];
+caseflip_skip([C|Cs]) when is_integer(C), $A =< C, C =< $Z ->
+ [C|caseflip(Cs)];
+caseflip_skip([C|Cs]) ->
+ [C|caseflip_skip(Cs)];
+caseflip_skip([]) ->
+ [].
+
+tolower_email([$.|Cs]) ->
+ [$.|tolower(Cs)];
+tolower_email([C|Cs]) ->
+ [C|tolower_email(Cs)].
+
+%% Case fold to lower case according to RFC 4343
+%%
+tolower([C|Cs]) when is_integer(C) ->
+ if $A =< C, C =< $Z ->
+ [(C - $A + $a)|tolower(Cs)];
+ true ->
+ [C|tolower(Cs)]
+ end;
+tolower([]) ->
+ [].
diff --git a/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf b/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf
index 0b01b25204..2d68f6e59c 100644
--- a/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf
+++ b/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf
@@ -2,11 +2,11 @@ zone "." in {
type master;
file "root.zone";
};
-zone "0.0.127.in-addr.arpa" in {
+zone "0.0.127.in-addr.arpa." in {
type master;
file "0.0.127.in-addr.arpa.zone";
};
-zone "0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" in {
+zone "0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." in {
type master;
file "0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.zone";
-}; \ No newline at end of file
+};
diff --git a/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone b/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone
index 11cba18d45..5a56eac95c 100644
--- a/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone
+++ b/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone
@@ -43,8 +43,8 @@ naptr.resolve.otptest IN NAPTR 10 5 "S" "HTTP" "" _srv._tcp.resolve.otptest
txt.resolve.otptest IN TXT "Hej " "du " "glade "
txt.resolve.otptest IN TXT "ta " "en " "spade!"
mb.resolve.otptest IN MB mx.resolve.otptest
-mg.resolve.otptest IN MG lsa.otptest
-mr.resolve.otptest IN MR lsa.otptest
-minfo.resolve.otptest IN MINFO minfo-owner.resolve.otptest minfo-bounce.resolve.otptest
+mg.resolve.otptest IN MG Lsa.otptest
+mr.resolve.otptest IN MR LSA.otptest
+minfo.resolve.otptest IN MINFO minfo-OWNER.resolve.otptest MinfoBounce.resolve.otptest
ns.otptest IN A 127.0.0.254
diff --git a/lib/mnesia/src/mnesia_subscr.erl b/lib/mnesia/src/mnesia_subscr.erl
index 415c69d508..8f78dc55e8 100644
--- a/lib/mnesia/src/mnesia_subscr.erl
+++ b/lib/mnesia/src/mnesia_subscr.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -447,8 +447,12 @@ deactivate(ClientPid, What, Var, SubscrTab) ->
{'EXIT', _} ->
unlink(ClientPid)
end,
- del_subscr(Var, What, ClientPid),
- {ok, node()}.
+ try
+ del_subscr(Var, What, ClientPid),
+ {ok, node()}
+ catch _:_ ->
+ {error, badarg}
+ end.
del_subscr(subscribers, _What, Pid) ->
mnesia_lib:del(subscribers, Pid);
diff --git a/lib/mnesia/test/mnesia_evil_coverage_test.erl b/lib/mnesia/test/mnesia_evil_coverage_test.erl
index 0df245b75d..db23a39943 100644
--- a/lib/mnesia/test/mnesia_evil_coverage_test.erl
+++ b/lib/mnesia/test/mnesia_evil_coverage_test.erl
@@ -1985,6 +1985,10 @@ subscribe_standard(Config) when is_list(Config)->
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
%% Check system events
+ ?match({error, {badarg, foo}}, mnesia:unsubscribe(foo)),
+ ?match({error, badarg}, mnesia:unsubscribe({table, foo})),
+ ?match(_, mnesia:unsubscribe(activity)),
+
?match({ok, N1}, mnesia:subscribe(system)),
?match({ok, N1}, mnesia:subscribe(activity)),
diff --git a/lib/mnesia/test/mnesia_isolation_test.erl b/lib/mnesia/test/mnesia_isolation_test.erl
index 3273bc4d40..d57f976d1f 100644
--- a/lib/mnesia/test/mnesia_isolation_test.erl
+++ b/lib/mnesia/test/mnesia_isolation_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -613,11 +613,11 @@ unbound2(Config) when is_list(Config) ->
?match_receive({B, continuing}),
%% B should now be in lock queue.
- A ! continue,
- ?match_receive({A, {atomic, ok}}),
- ?match_receive({B, {atomic, [{ul,{key,{17,42}},val}]}}),
+ A ! continue,
+ ?match_multi_receive([{A, {atomic, ok}},
+ {B, {atomic, [{ul,{key,{17,42}},val}]}}]),
ok.
-
+
receiver() ->
receive
{_Pid, begin_trans} ->
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index 7d6c5484a7..e3acea0258 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.2.11
+OS_MON_VSN = 2.2.12
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 4044c89967..bce6d58682 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -155,8 +155,8 @@
<funcs>
<func>
- <name> compute_key(OthersKey, MyKey)-></name>
- <name> compute_key(OthersKey, MyKey, Params)-></name>
+ <name>compute_key(OthersKey, MyKey)-></name>
+ <name>compute_key(OthersKey, MyKey, Params)-></name>
<fsummary> Compute shared secret</fsummary>
<type>
<v>OthersKey = #'ECPoint'{} | binary(), MyKey = #'ECPrivateKey'{} | binary()</v>
@@ -177,7 +177,8 @@
<v>Options = public_crypt_options()</v>
</type>
<desc>
- <p>Public key decryption using the private key.</p>
+ <p>Public key decryption using the private key. See also <seealso
+ marker="crypto:crypto#private_decrypt/4">crypto:private_decrypt/4</seealso></p>
</desc>
</func>
@@ -191,7 +192,8 @@
<v>Options = public_crypt_options()</v>
</type>
<desc>
- <p> Public key decryption using the public key.</p>
+ <p> Public key decryption using the public key. See also <seealso
+ marker="crypto:crypto#public_decrypt/4">crypto:public_decrypt/4</seealso></p>
</desc>
</func>
@@ -305,7 +307,9 @@
<v>Key = rsa_private_key()</v>
</type>
<desc>
- <p> Public key encryption using the private key.</p>
+ <p> Public key encryption using the private key.
+ See also <seealso
+ marker="crypto:crypto#private_encrypt/4">crypto:private_encrypt/4</seealso></p>
</desc>
</func>
@@ -317,7 +321,8 @@
<v>Key = rsa_public_key()</v>
</type>
<desc>
- <p> Public key encryption using the public key.</p>
+ <p> Public key encryption using the public key. See also <seealso
+ marker="crypto:crypto#public_encrypt/4">crypto:public_encrypt/4</seealso></p>
</desc>
</func>
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 3eea6f6ec4..cdbfe6e07c 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -252,8 +252,7 @@ decrypt_private(CipherText, Key) ->
decrypt_private(CipherText, Key, []).
decrypt_private(CipherText,
- #'RSAPrivateKey'{modulus = N, publicExponent = E,
- privateExponent = D} = Key,
+ #'RSAPrivateKey'{} = Key,
Options)
when is_binary(CipherText),
is_list(Options) ->
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index b25e0c9e37..32f7cc470b 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -19,22 +19,12 @@
{"%VSN%",
[
- {<<"2.1.4">>, [{load_module, ssh_sftp, soft_purge, soft_purge, []},
- {load_module, ssh_connection_handler, soft_purge, soft_purge, []}]},
- {<<"2.1.3">>, [{restart_application, ssh}]},
- {<<"2.1.2">>, [{restart_application, ssh}]},
- {<<"2.1.1">>, [{restart_application, ssh}]},
- {<<"2.1">>, [{restart_application, ssh}]},
+ {<<"2.1\\.*">>, [{restart_application, ssh}]},
{<<"2.0\\.*">>, [{restart_application, ssh}]},
{<<"1\\.*">>, [{restart_application, ssh}]}
],
[
- {<<"2.1.4">>, [{load_module, ssh_sftp, soft_purge, soft_purge, []},
- {load_module, ssh_connection_handler, soft_purge, soft_purge, []}]},
- {<<"2.1.3">>, [{restart_application, ssh}]},
- {<<"2.1.2">>, [{restart_application, ssh}]},
- {<<"2.1.1">>, [{restart_application, ssh}]},
- {<<"2.1">>,[{restart_application, ssh}]},
+ {<<"2.1\\.*">>,[{restart_application, ssh}]},
{<<"2.0\\.*">>, [{restart_application, ssh}]},
{<<"1\\.*">>, [{restart_application, ssh}]}
]
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index a8a494b2fc..9e5bec26f1 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,14 +1,14 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"5.2">>, [{restart_application, ssl}]},
+ {<<"5.2\\*">>, [{restart_application, ssl}]},
{<<"5.1\\*">>, [{restart_application, ssl}]},
{<<"5.0\\*">>, [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
{<<"3\\.*">>, [{restart_application, ssl}]}
],
[
- {<<"5.2">>, [{restart_application, ssl}]},
+ {<<"5.2\\*">>, [{restart_application, ssl}]},
{<<"5.1\\*">>, [{restart_application, ssl}]},
{<<"5.0\\*">>, [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 75c17b14db..0c1e47311d 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -214,8 +214,8 @@ prf(Socket, Secret, Label, Seed, WantedLength) ->
clear_pem_cache() ->
tls:clear_pem_cache().
-format_error(Reason) when is_list(Reason) ->
- tls:format_error(Reason).
+format_error(Error) ->
+ tls:format_error(Error).
random_bytes(N) ->
tls:random_bytes(N).
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 898b421dff..ec5d793d65 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1190,14 +1190,16 @@ hash_size(md5) ->
16;
hash_size(sha) ->
20;
-hash_size(sha224) ->
- 28;
+%% Uncomment when adding cipher suite that needs it
+%% hash_size(sha224) ->
+%% 28;
hash_size(sha256) ->
32;
hash_size(sha384) ->
- 48;
-hash_size(sha512) ->
- 64.
+ 48.
+%% Uncomment when adding cipher suite that needs it
+%% hash_size(sha512) ->
+%% 64.
%% RFC 5246: 6.2.3.2. CBC Block Cipher
%%
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 1b06e351cf..7af4a68461 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -104,7 +104,6 @@ connection_init(Trustedcerts, Role) ->
%%--------------------------------------------------------------------
cache_pem_file(File, DbHandle) ->
MD5 = crypto:hash(md5, File),
- MD5 = crypto:md5(File),
case ssl_pkix_db:lookup_cached_pem(DbHandle, MD5) of
[{Content,_}] ->
{ok, Content};
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 6a0461e805..246fecf34a 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -673,8 +673,7 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS
certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey},
#state{negotiated_version = Version,
- diffie_hellman_params = #'DHParameter'{prime = P,
- base = G} = Params,
+ diffie_hellman_params = #'DHParameter'{} = Params,
diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) ->
case dh_master_secret(Params, ClientPublicDhKey, ServerDhPrivateKey, State0) of
#state{} = State1 ->
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index fea75736f7..51fd2e1dc9 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -866,7 +866,7 @@ handle_ecc_point_fmt_extension(undefined) ->
handle_ecc_point_fmt_extension(_) ->
#ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}.
-handle_ecc_curves_extension(Version, undefined) ->
+handle_ecc_curves_extension(_Version, undefined) ->
undefined;
handle_ecc_curves_extension(Version, _) ->
#elliptic_curves{elliptic_curve_list = ssl_tls1:ecc_curves(Version)}.
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 1f3bef83c8..9dd151553c 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.2.1
+SSL_VSN = 5.3
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 68a8534f15..08b8541014 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -522,8 +522,7 @@ start(File, Opts) ->
warn_format = value_option(warn_format, 1, warn_format, 1,
nowarn_format, 0, Opts),
enabled_warnings = Enabled,
- file = File,
- types = default_types()
+ file = File
}.
%% is_warn_enabled(Category, St) -> boolean().
@@ -1007,7 +1006,10 @@ check_undefined_functions(#lint{called=Called0,defined=Def0}=St0) ->
check_undefined_types(#lint{usage=Usage,types=Def}=St0) ->
Used = Usage#usage.used_types,
UTAs = dict:fetch_keys(Used),
- Undef = [{TA,dict:fetch(TA, Used)} || TA <- UTAs, not dict:is_key(TA, Def)],
+ Undef = [{TA,dict:fetch(TA, Used)} ||
+ TA <- UTAs,
+ not dict:is_key(TA, Def),
+ not is_default_type(TA)],
foldl(fun ({TA,L}, St) ->
add_error(L, {undefined_type,TA}, St)
end, St0, Undef).
@@ -2440,7 +2442,7 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
end,
case (dict:is_key(TypePair, TypeDefs) orelse is_var_arity_type(TypeName)) of
true ->
- case dict:is_key(TypePair, default_types()) of
+ case is_default_type(TypePair) of
true ->
case is_newly_introduced_builtin_type(TypePair) of
%% allow some types just for bootstrapping
@@ -2488,8 +2490,8 @@ check_type({paren_type, _L, [Type]}, SeenVars, St) ->
check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]},
SeenVars, #lint{module=CurrentMod} = St) ->
St1 =
- case (dict:is_key({Name, length(Args)}, default_types())
- orelse is_var_arity_type(Name)) of
+ case is_default_type({Name, length(Args)})
+ orelse is_var_arity_type(Name) of
true -> add_error(L, {imported_predefined_type, Name}, St);
false -> St
end,
@@ -2606,63 +2608,62 @@ is_var_arity_type(union) -> true;
is_var_arity_type(record) -> true;
is_var_arity_type(_) -> false.
-default_types() ->
- DefTypes = [{any, 0},
- {arity, 0},
- {array, 0},
- {atom, 0},
- {atom, 1},
- {binary, 0},
- {binary, 2},
- {bitstring, 0},
- {bool, 0},
- {boolean, 0},
- {byte, 0},
- {char, 0},
- {dict, 0},
- {digraph, 0},
- {float, 0},
- {'fun', 0},
- {'fun', 2},
- {function, 0},
- {gb_set, 0},
- {gb_tree, 0},
- {identifier, 0},
- {integer, 0},
- {integer, 1},
- {iodata, 0},
- {iolist, 0},
- {list, 0},
- {list, 1},
- {maybe_improper_list, 0},
- {maybe_improper_list, 2},
- {mfa, 0},
- {module, 0},
- {neg_integer, 0},
- {nil, 0},
- {no_return, 0},
- {node, 0},
- {non_neg_integer, 0},
- {none, 0},
- {nonempty_list, 0},
- {nonempty_list, 1},
- {nonempty_improper_list, 2},
- {nonempty_maybe_improper_list, 0},
- {nonempty_maybe_improper_list, 2},
- {nonempty_string, 0},
- {number, 0},
- {pid, 0},
- {port, 0},
- {pos_integer, 0},
- {queue, 0},
- {range, 2},
- {reference, 0},
- {set, 0},
- {string, 0},
- {term, 0},
- {timeout, 0},
- {var, 1}],
- dict:from_list([{T, -1} || T <- DefTypes]).
+is_default_type({any, 0}) -> true;
+is_default_type({arity, 0}) -> true;
+is_default_type({array, 0}) -> true;
+is_default_type({atom, 0}) -> true;
+is_default_type({atom, 1}) -> true;
+is_default_type({binary, 0}) -> true;
+is_default_type({binary, 2}) -> true;
+is_default_type({bitstring, 0}) -> true;
+is_default_type({bool, 0}) -> true;
+is_default_type({boolean, 0}) -> true;
+is_default_type({byte, 0}) -> true;
+is_default_type({char, 0}) -> true;
+is_default_type({dict, 0}) -> true;
+is_default_type({digraph, 0}) -> true;
+is_default_type({float, 0}) -> true;
+is_default_type({'fun', 0}) -> true;
+is_default_type({'fun', 2}) -> true;
+is_default_type({function, 0}) -> true;
+is_default_type({gb_set, 0}) -> true;
+is_default_type({gb_tree, 0}) -> true;
+is_default_type({identifier, 0}) -> true;
+is_default_type({integer, 0}) -> true;
+is_default_type({integer, 1}) -> true;
+is_default_type({iodata, 0}) -> true;
+is_default_type({iolist, 0}) -> true;
+is_default_type({list, 0}) -> true;
+is_default_type({list, 1}) -> true;
+is_default_type({maybe_improper_list, 0}) -> true;
+is_default_type({maybe_improper_list, 2}) -> true;
+is_default_type({mfa, 0}) -> true;
+is_default_type({module, 0}) -> true;
+is_default_type({neg_integer, 0}) -> true;
+is_default_type({nil, 0}) -> true;
+is_default_type({no_return, 0}) -> true;
+is_default_type({node, 0}) -> true;
+is_default_type({non_neg_integer, 0}) -> true;
+is_default_type({none, 0}) -> true;
+is_default_type({nonempty_list, 0}) -> true;
+is_default_type({nonempty_list, 1}) -> true;
+is_default_type({nonempty_improper_list, 2}) -> true;
+is_default_type({nonempty_maybe_improper_list, 0}) -> true;
+is_default_type({nonempty_maybe_improper_list, 2}) -> true;
+is_default_type({nonempty_string, 0}) -> true;
+is_default_type({number, 0}) -> true;
+is_default_type({pid, 0}) -> true;
+is_default_type({port, 0}) -> true;
+is_default_type({pos_integer, 0}) -> true;
+is_default_type({queue, 0}) -> true;
+is_default_type({range, 2}) -> true;
+is_default_type({reference, 0}) -> true;
+is_default_type({set, 0}) -> true;
+is_default_type({string, 0}) -> true;
+is_default_type({term, 0}) -> true;
+is_default_type({timeout, 0}) -> true;
+is_default_type({var, 1}) -> true;
+is_default_type(_) -> false.
%% R13
is_newly_introduced_builtin_type({arity, 0}) -> true;
@@ -2776,10 +2777,7 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
L = gb_sets:to_list(ExpTs) ++ dict:fetch_keys(D),
UsedTypes = gb_sets:from_list(L),
FoldFun =
- fun(_Type, -1, AccSt) ->
- %% Default type
- AccSt;
- (Type, #typeinfo{line = FileLine}, AccSt) ->
+ fun(Type, #typeinfo{line = FileLine}, AccSt) ->
case loc(FileLine) of
{FirstFile, _} ->
case gb_sets:is_member(Type, UsedTypes) of
@@ -2801,10 +2799,7 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
check_local_opaque_types(St) ->
#lint{types=Ts, exp_types=ExpTs} = St,
FoldFun =
- fun(_Type, -1, AccSt) ->
- %% Default type
- AccSt;
- (_Type, #typeinfo{attr = type}, AccSt) ->
+ fun(_Type, #typeinfo{attr = type}, AccSt) ->
AccSt;
(Type, #typeinfo{attr = opaque, line = FileLine}, AccSt) ->
case gb_sets:is_element(Type, ExpTs) of
diff --git a/lib/stdlib/test/unicode_SUITE.erl b/lib/stdlib/test/unicode_SUITE.erl
index 4055af2741..6106a8c444 100644
--- a/lib/stdlib/test/unicode_SUITE.erl
+++ b/lib/stdlib/test/unicode_SUITE.erl
@@ -29,7 +29,7 @@
random_lists/1,
roundtrips/1,
latin1/1,
- exceptions/1]).
+ exceptions/1, binaries_errors/1]).
init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog=?t:timetrap(?t:minutes(20)),
@@ -44,7 +44,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[utf8_illegal_sequences_bif,
utf16_illegal_sequences_bif, random_lists, roundtrips,
- latin1, exceptions].
+ latin1, exceptions, binaries_errors].
groups() ->
[].
@@ -61,6 +61,149 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+binaries_errors(Config) when is_list(Config) ->
+ setlimit(10),
+ ex_binaries_errors_utf8(Config),
+ setlimit(default),
+ ex_binaries_errors_utf8(Config),
+ ex_binaries_errors_utf16_little(Config),
+ ex_binaries_errors_utf16_big(Config),
+ ex_binaries_errors_utf32_little(Config),
+ ex_binaries_errors_utf32_big(Config).
+
+ex_binaries_errors_utf8(Config) when is_list(Config) ->
+ %% Original smoke test, we should not forget the original offset...
+ <<_:8,_:8,RR2/binary>> = <<$a,$b,164,165,$c>>,
+ {error,[],<<164,165,$c>>} = unicode:characters_to_list(RR2),
+ %% Now, try with longer binary (trapping)
+ BrokenPart = list_to_binary(lists:seq(128,255)),
+ BrokenSz = byte_size(BrokenPart),
+ [ begin
+ OKList = lists:flatten(lists:duplicate(N,lists:seq(1,255))),
+ OKBin = unicode:characters_to_binary(OKList),
+ OKLen = length(OKList),
+ %% Copy to avoid that the binary get's writable
+ PartlyBroken = binary:copy(<<OKBin/binary, BrokenPart/binary>>),
+ PBSz = byte_size(PartlyBroken),
+ {error,OKList,DeepBrokenPart} =
+ unicode:characters_to_list(PartlyBroken),
+ BrokenPart = iolist_to_binary(DeepBrokenPart),
+ [ begin
+ NewList = lists:nthtail(X, OKList),
+ NewSz = byte_size(unicode:characters_to_binary(NewList)) +
+ BrokenSz,
+ Chomped = binary:part(PartlyBroken,PBSz - NewSz, NewSz),
+ true = (binary:referenced_byte_size(Chomped) =:= PBSz),
+ {error,NewList,DeepBrokenPart2} =
+ unicode:characters_to_list(Chomped),
+ BrokenPart = iolist_to_binary(DeepBrokenPart2)
+ end || X <- lists:seq(1,OKLen) ]
+ end || N <- lists:seq(1,20) ],
+ ok.
+
+ex_binaries_errors_utf16_little(Config) when is_list(Config) ->
+ BrokenPart = << <<X:16/little>> || X <- lists:seq(16#DC00,16#DFFF) >>,
+ BrokenSz = byte_size(BrokenPart),
+ [ begin
+ OKList = lists:flatten(lists:duplicate(N,lists:seq(1,255))),
+ OKBin = unicode:characters_to_binary(OKList,unicode,{utf16,little}),
+ OKLen = length(OKList),
+ %% Copy to avoid that the binary get's writable
+ PartlyBroken = binary:copy(<<OKBin/binary, BrokenPart/binary>>),
+ PBSz = byte_size(PartlyBroken),
+ {error,OKList,DeepBrokenPart} =
+ unicode:characters_to_list(PartlyBroken,{utf16,little}),
+ BrokenPart = iolist_to_binary(DeepBrokenPart),
+ [ begin
+ NewList = lists:nthtail(X, OKList),
+ NewSz = byte_size(unicode:characters_to_binary(NewList,unicode,{utf16,little})) +
+ BrokenSz,
+ Chomped = binary:part(PartlyBroken,PBSz - NewSz, NewSz),
+ true = (binary:referenced_byte_size(Chomped) =:= PBSz),
+ {error,NewList,DeepBrokenPart2} =
+ unicode:characters_to_list(Chomped,{utf16,little}),
+ BrokenPart = iolist_to_binary(DeepBrokenPart2)
+ end || X <- lists:seq(1,OKLen) ]
+ end || N <- lists:seq(1,15) ],
+ ok.
+ex_binaries_errors_utf16_big(Config) when is_list(Config) ->
+ BrokenPart = << <<X:16/big>> || X <- lists:seq(16#DC00,16#DFFF) >>,
+ BrokenSz = byte_size(BrokenPart),
+ [ begin
+ OKList = lists:flatten(lists:duplicate(N,lists:seq(1,255))),
+ OKBin = unicode:characters_to_binary(OKList,unicode,{utf16,big}),
+ OKLen = length(OKList),
+ %% Copy to avoid that the binary get's writable
+ PartlyBroken = binary:copy(<<OKBin/binary, BrokenPart/binary>>),
+ PBSz = byte_size(PartlyBroken),
+ {error,OKList,DeepBrokenPart} =
+ unicode:characters_to_list(PartlyBroken,{utf16,big}),
+ BrokenPart = iolist_to_binary(DeepBrokenPart),
+ [ begin
+ NewList = lists:nthtail(X, OKList),
+ NewSz = byte_size(unicode:characters_to_binary(NewList,unicode,{utf16,big})) +
+ BrokenSz,
+ Chomped = binary:part(PartlyBroken,PBSz - NewSz, NewSz),
+ true = (binary:referenced_byte_size(Chomped) =:= PBSz),
+ {error,NewList,DeepBrokenPart2} =
+ unicode:characters_to_list(Chomped,{utf16,big}),
+ BrokenPart = iolist_to_binary(DeepBrokenPart2)
+ end || X <- lists:seq(1,OKLen) ]
+ end || N <- lists:seq(1,15) ],
+ ok.
+
+ex_binaries_errors_utf32_big(Config) when is_list(Config) ->
+ BrokenPart = << <<X:32/big>> || X <- lists:seq(16#DC00,16#DFFF) >>,
+ BrokenSz = byte_size(BrokenPart),
+ [ begin
+ OKList = lists:flatten(lists:duplicate(N,lists:seq(1,255))),
+ OKBin = unicode:characters_to_binary(OKList,unicode,{utf32,big}),
+ OKLen = length(OKList),
+ %% Copy to avoid that the binary get's writable
+ PartlyBroken = binary:copy(<<OKBin/binary, BrokenPart/binary>>),
+ PBSz = byte_size(PartlyBroken),
+ {error,OKList,DeepBrokenPart} =
+ unicode:characters_to_list(PartlyBroken,{utf32,big}),
+ BrokenPart = iolist_to_binary(DeepBrokenPart),
+ [ begin
+ NewList = lists:nthtail(X, OKList),
+ NewSz = byte_size(unicode:characters_to_binary(NewList,unicode,{utf32,big})) +
+ BrokenSz,
+ Chomped = binary:part(PartlyBroken,PBSz - NewSz, NewSz),
+ true = (binary:referenced_byte_size(Chomped) =:= PBSz),
+ {error,NewList,DeepBrokenPart2} =
+ unicode:characters_to_list(Chomped,{utf32,big}),
+ BrokenPart = iolist_to_binary(DeepBrokenPart2)
+ end || X <- lists:seq(1,OKLen) ]
+ end || N <- lists:seq(1,15) ],
+ ok.
+
+ex_binaries_errors_utf32_little(Config) when is_list(Config) ->
+ BrokenPart = << <<X:32/little>> || X <- lists:seq(16#DC00,16#DFFF) >>,
+ BrokenSz = byte_size(BrokenPart),
+ [ begin
+ OKList = lists:flatten(lists:duplicate(N,lists:seq(1,255))),
+ OKBin = unicode:characters_to_binary(OKList,unicode,{utf32,little}),
+ OKLen = length(OKList),
+ %% Copy to avoid that the binary get's writable
+ PartlyBroken = binary:copy(<<OKBin/binary, BrokenPart/binary>>),
+ PBSz = byte_size(PartlyBroken),
+ {error,OKList,DeepBrokenPart} =
+ unicode:characters_to_list(PartlyBroken,{utf32,little}),
+ BrokenPart = iolist_to_binary(DeepBrokenPart),
+ [ begin
+ NewList = lists:nthtail(X, OKList),
+ NewSz = byte_size(unicode:characters_to_binary(NewList,unicode,{utf32,little})) +
+ BrokenSz,
+ Chomped = binary:part(PartlyBroken,PBSz - NewSz, NewSz),
+ true = (binary:referenced_byte_size(Chomped) =:= PBSz),
+ {error,NewList,DeepBrokenPart2} =
+ unicode:characters_to_list(Chomped,{utf32,little}),
+ BrokenPart = iolist_to_binary(DeepBrokenPart2)
+ end || X <- lists:seq(1,OKLen) ]
+ end || N <- lists:seq(1,15) ],
+ ok.
+
exceptions(Config) when is_list(Config) ->
diff --git a/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml
index b35929f1e6..0eab939a46 100644
--- a/lib/test_server/doc/src/notes.xml
+++ b/lib/test_server/doc/src/notes.xml
@@ -226,8 +226,6 @@
unicode:characters_to_binary for conversion between
binaries and strings instead of binary_to_list and
list_to_binary. </item> </list></p>
- <p>
- Own Id: OTP-10783</p>
</item>
</list>
</section>
diff --git a/lib/test_server/src/configure.in b/lib/test_server/src/configure.in
index 785bab395c..b8fddf1481 100644
--- a/lib/test_server/src/configure.in
+++ b/lib/test_server/src/configure.in
@@ -276,6 +276,7 @@ AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"]))
dnl Checks for library functions.
AC_CHECK_FUNCS(strerror)
AC_CHECK_FUNCS(vsnprintf)
+AC_CHECK_FUNCS(usleep)
# First check if the library is available, then if we can choose between
# two versions of gethostbyname
diff --git a/lib/tools/doc/src/eprof.xml b/lib/tools/doc/src/eprof.xml
index 82eb8dd284..8397204a43 100644
--- a/lib/tools/doc/src/eprof.xml
+++ b/lib/tools/doc/src/eprof.xml
@@ -52,12 +52,14 @@
<func>
<name>start_profiling(Rootset) -> profiling | {error, Reason}</name>
<name>start_profiling(Rootset,Pattern) -> profiling | {error, Reason}</name>
+ <name>start_profiling(Rootset,Pattern,Options) -> profiling | {error, Reason}</name>
<fsummary>Start profiling.</fsummary>
<type>
<v>Rootset = [atom() | pid()]</v>
- <v>Pattern = {Module, Function, Arity}</v>
- <v>Module = Function = atom()</v>
- <v>Arity = integer()</v>
+ <v>Pattern = {Module, Function, Arity}</v>
+ <v>Module = Function = atom()</v>
+ <v>Arity = integer()</v>
+ <v>Options = [set_on_spawn]</v>
<v>Reason = term()</v>
</type>
<desc>
@@ -70,6 +72,9 @@
<p>A pattern can be selected to narrow the profiling. For instance a
specific module can be selected, and only the code executed in that
module will be profiled.</p>
+ <p>The <c>set_on_spawn</c> option will active call time tracing for
+ all processes spawned by processes in the rootset. This is
+ the default behaviour.</p>
</desc>
</func>
<func>
@@ -82,19 +87,22 @@
</func>
<func>
<name>profile(Fun) -> profiling | {error, Reason}</name>
+ <name>profile(Fun, Options) -> profiling | {error, Reason}</name>
<name>profile(Rootset) -> profiling | {error, Reason}</name>
<name>profile(Rootset,Fun) -> {ok, Value} | {error,Reason}</name>
<name>profile(Rootset,Fun,Pattern) -> {ok, Value} | {error, Reason}</name>
<name>profile(Rootset,Module,Function,Args) -> {ok, Value} | {error, Reason}</name>
<name>profile(Rootset,Module,Function,Args,Pattern) -> {ok, Value} | {error, Reason}</name>
+ <name>profile(Rootset,Module,Function,Args,Pattern,Options) -> {ok, Value} | {error, Reason}</name>
<fsummary>Start profiling.</fsummary>
<type>
<v>Rootset = [atom() | pid()]</v>
- <v>Fun = fun() -> term()</v>
- <v>Pattern = {Module, Function, Arity}</v>
+ <v>Fun = fun() -> term() end</v>
+ <v>Pattern = {Module, Function, Arity}</v>
<v>Module = Function = atom()</v>
<v>Args = [term()]</v>
- <v>Arity = integer()</v>
+ <v>Arity = integer()</v>
+ <v>Options = [set_on_spawn]</v>
<v>Value = Reason = term()</v>
</type>
<desc>
@@ -108,8 +116,11 @@
<c>Rootset</c>, the function returns <c>{ok,Value}</c> when
<c>Fun()</c>/<c>apply</c> returns with the value <c>Value</c>, or
<c>{error,Reason}</c> if <c>Fun()</c>/<c>apply</c> fails with
- exit reason <c>Reason</c>. Otherwise it returns <c>{error, Reason}</c>
+ exit reason <c>Reason</c>. Otherwise it returns <c>{error, Reason}</c>
immediately.</p>
+ <p>The <c>set_on_spawn</c> option will active call time tracing for
+ all processes spawned by processes in the rootset. This is
+ the default behaviour.</p>
<p>The programmer must ensure that the function given as argument
is truly synchronous and that no work continues after
the function has returned a value.</p>
diff --git a/lib/tools/src/eprof.erl b/lib/tools/src/eprof.erl
index 87fdc1fa34..bc9345fa62 100644
--- a/lib/tools/src/eprof.erl
+++ b/lib/tools/src/eprof.erl
@@ -26,7 +26,7 @@
-export([start/0,
stop/0,
dump/0,
- start_profiling/1, start_profiling/2,
+ start_profiling/1, start_profiling/2, start_profiling/3,
profile/1, profile/2, profile/3, profile/4, profile/5,
stop_profiling/0,
analyze/0, analyze/1, analyze/2,
@@ -39,6 +39,8 @@
handle_info/2,
terminate/2,
code_change/3]).
+
+
-record(bpd, {
n = 0, % number of total calls
us = 0, % sum of uS for all calls
@@ -46,14 +48,18 @@
mfa = [] % list of {Mfa, {Count, Us}}
}).
+-define(default_options, [{set_on_spawn, true}]).
+-define(default_pattern, {'_','_','_'}).
+
-record(state, {
- profiling = false,
- pattern = {'_','_','_'},
- rootset = [],
- fd = undefined,
- start_ts = undefined,
- reply = undefined,
- bpd = #bpd{}
+ profiling = false,
+ pattern = ?default_pattern,
+ rootset = [],
+ trace_opts = [],
+ fd = undefined,
+ start_ts = undefined,
+ reply = undefined,
+ bpd = #bpd{}
}).
@@ -67,26 +73,6 @@
start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []).
stop() -> gen_server:call(?MODULE, stop, infinity).
-profile(Fun) when is_function(Fun) ->
- profile([], Fun);
-profile(Rs) when is_list(Rs) ->
- start_profiling(Rs).
-
-profile(Pids, Fun) ->
- profile(Pids, Fun, {'_','_','_'}).
-
-profile(Pids, Fun, Pattern) ->
- profile(Pids, erlang, apply, [Fun,[]], Pattern).
-
-profile(Pids, M, F, A) ->
- profile(Pids, M, F, A, {'_','_','_'}).
-
-profile(Pids, M, F, A, Pattern) ->
- start(),
- gen_server:call(?MODULE, {profile,Pids,Pattern,M,F,A},infinity).
-
-dump() ->
- gen_server:call(?MODULE, dump, infinity).
analyze() ->
analyze(procs).
@@ -98,17 +84,53 @@ analyze(Opts) when is_list(Opts) ->
analyze(Type, Opts) when is_list(Opts) ->
gen_server:call(?MODULE, {analyze, Type, Opts}, infinity).
+%% odd duck, should only been start_profiling/1
+profile(Rootset) when is_list(Rootset) ->
+ start_profiling(Rootset);
+
+profile(Fun) when is_function(Fun) ->
+ profile([], Fun).
+
+profile(Fun, Opts) when is_function(Fun), is_list(Opts) ->
+ profile([], erlang, apply, [Fun, []], ?default_pattern, Opts);
+
+profile(Rootset, Fun) when is_list(Rootset), is_function(Fun) ->
+ profile(Rootset, Fun, ?default_pattern).
+
+profile(Rootset, Fun, Pattern) when is_list(Rootset), is_function(Fun) ->
+ profile(Rootset, Fun, Pattern, ?default_options).
+
+profile(Rootset, Fun, Pattern, Options) when is_list(Rootset), is_function(Fun), is_list(Options) ->
+ profile(Rootset, erlang, apply, [Fun,[]], Pattern, Options);
+
+profile(Rootset, M, F, A) when is_list(Rootset), is_atom(M), is_atom(F), is_list(A) ->
+ profile(Rootset, M, F, A, ?default_pattern).
+
+profile(Rootset, M, F, A, Pattern) when is_list(Rootset), is_atom(M), is_atom(F), is_list(A) ->
+ profile(Rootset, M, F, A, Pattern, ?default_options).
+
+%% Returns when M:F/A has terminated
+profile(Rootset, M, F, A, Pattern, Options) ->
+ start(),
+ gen_server:call(?MODULE, {profile_start, Rootset, Pattern, {M,F,A}, Options}, infinity).
+
+dump() ->
+ gen_server:call(?MODULE, dump, infinity).
+
log(File) ->
gen_server:call(?MODULE, {logfile, File}, infinity).
+%% Does not block
start_profiling(Rootset) ->
- start_profiling(Rootset, {'_','_','_'}).
+ start_profiling(Rootset, ?default_pattern).
start_profiling(Rootset, Pattern) ->
+ start_profiling(Rootset, Pattern, ?default_options).
+start_profiling(Rootset, Pattern, Options) ->
start(),
- gen_server:call(?MODULE, {profile, Rootset, Pattern}, infinity).
+ gen_server:call(?MODULE, {profile_start, Rootset, Pattern, undefined, Options}, infinity).
stop_profiling() ->
- gen_server:call(?MODULE, stop_profiling, infinity).
+ gen_server:call(?MODULE, profile_stop, infinity).
%% -------------------------------------------------------------------- %%
@@ -151,74 +173,75 @@ handle_call({analyze, Type, _Opts}, _, S) ->
%% profile
-handle_call({profile, _Rootset, _Pattern, _M,_F,_A}, _From, #state{ profiling = true } = S) ->
+handle_call({profile_start, _Rootset, _Pattern, _MFA, _Opts}, _From, #state{ profiling = true } = S) ->
{reply, {error, already_profiling}, S};
-handle_call({profile, Rootset, Pattern, M,F,A}, From, #state{fd = Fd } = S) ->
+handle_call({profile_start, Rootset, Pattern, {M,F,A}, Opts}, From, #state{fd = Fd } = S) ->
+
+ ok = set_pattern_trace(false, S#state.pattern),
+ _ = set_process_trace(false, S#state.rootset, S#state.trace_opts),
- set_pattern_trace(false, S#state.pattern),
- set_process_trace(false, S#state.rootset),
+ Topts = get_trace_options(Opts),
+ Pid = setup_profiling(M,F,A),
- Pid = setup_profiling(M,F,A),
- case set_process_trace(true, [Pid|Rootset]) of
+ case set_process_trace(true, [Pid|Rootset], Topts) of
true ->
- set_pattern_trace(true, Pattern),
+ ok = set_pattern_trace(true, Pattern),
T0 = now(),
- execute_profiling(Pid),
+ ok = execute_profiling(Pid),
{noreply, #state{
- profiling = true,
- rootset = [Pid|Rootset],
- start_ts = T0,
- reply = From,
- fd = Fd,
- pattern = Pattern
+ profiling = true,
+ rootset = [Pid|Rootset],
+ start_ts = T0,
+ reply = From,
+ fd = Fd,
+ trace_opts = Topts,
+ pattern = Pattern
}};
false ->
exit(Pid, eprof_kill),
{reply, error, #state{ fd = Fd}}
end;
-handle_call({profile, _Rootset, _Pattern}, _From, #state{ profiling = true } = S) ->
- {reply, {error, already_profiling}, S};
-
-handle_call({profile, Rootset, Pattern}, From, #state{ fd = Fd } = S) ->
+handle_call({profile_start, Rootset, Pattern, undefined, Opts}, From, #state{ fd = Fd } = S) ->
- set_pattern_trace(false, S#state.pattern),
- set_process_trace(false, S#state.rootset),
+ ok = set_pattern_trace(false, S#state.pattern),
+ true = set_process_trace(false, S#state.rootset, S#state.trace_opts),
+ Topts = get_trace_options(Opts),
- case set_process_trace(true, Rootset) of
+ case set_process_trace(true, Rootset, Topts) of
true ->
T0 = now(),
- set_pattern_trace(true, Pattern),
+ ok = set_pattern_trace(true, Pattern),
{reply, profiling, #state{
- profiling = true,
- rootset = Rootset,
- start_ts = T0,
- reply = From,
- fd = Fd,
- pattern = Pattern
+ profiling = true,
+ rootset = Rootset,
+ start_ts = T0,
+ reply = From,
+ fd = Fd,
+ trace_opts = Topts,
+ pattern = Pattern
}};
false ->
{reply, error, #state{ fd = Fd }}
end;
-handle_call(stop_profiling, _From, #state{ profiling = false } = S) ->
+handle_call(profile_stop, _From, #state{ profiling = false } = S) ->
{reply, profiling_already_stopped, S};
-handle_call(stop_profiling, _From, #state{ profiling = true } = S) ->
-
- set_pattern_trace(pause, S#state.pattern),
+handle_call(profile_stop, _From, #state{ profiling = true } = S) ->
+ ok = set_pattern_trace(pause, S#state.pattern),
Bpd = collect_bpd(),
-
- set_process_trace(false, S#state.rootset),
- set_pattern_trace(false, S#state.pattern),
+ _ = set_process_trace(false, S#state.rootset, S#state.trace_opts),
+ ok = set_pattern_trace(false, S#state.pattern),
{reply, profiling_stopped, S#state{
- profiling = false,
- rootset = [],
- pattern = {'_','_','_'},
- bpd = Bpd
+ profiling = false,
+ rootset = [],
+ trace_opts = [],
+ pattern = ?default_pattern,
+ bpd = Bpd
}};
%% logfile
@@ -261,33 +284,33 @@ handle_info({'EXIT', _, eprof_kill}, S) ->
{noreply, S};
handle_info({'EXIT', _, Reason}, #state{ reply = FromTag } = S) ->
- set_process_trace(false, S#state.rootset),
- set_pattern_trace(false, S#state.pattern),
+ _ = set_process_trace(false, S#state.rootset, S#state.trace_opts),
+ ok = set_pattern_trace(false, S#state.pattern),
gen_server:reply(FromTag, {error, Reason}),
{noreply, S#state{
- profiling = false,
- rootset = [],
- pattern = {'_','_','_'}
+ profiling = false,
+ rootset = [],
+ trace_opts = [],
+ pattern = ?default_pattern
}};
% check if Pid is spawned process?
handle_info({_Pid, {answer, Result}}, #state{ reply = {From,_} = FromTag} = S) ->
- set_pattern_trace(pause, S#state.pattern),
-
- Bpd = collect_bpd(),
-
- set_process_trace(false, S#state.rootset),
- set_pattern_trace(false, S#state.pattern),
+ ok = set_pattern_trace(pause, S#state.pattern),
+ Bpd = collect_bpd(),
+ _ = set_process_trace(false, S#state.rootset, S#state.trace_opts),
+ ok = set_pattern_trace(false, S#state.pattern),
catch unlink(From),
gen_server:reply(FromTag, {ok, Result}),
{noreply, S#state{
- profiling = false,
- rootset = [],
- pattern = {'_','_','_'},
- bpd = Bpd
+ profiling = false,
+ rootset = [],
+ trace_opts = [],
+ pattern = ?default_pattern,
+ bpd = Bpd
}}.
%% -------------------------------------------------------------------- %%
@@ -297,11 +320,11 @@ handle_info({_Pid, {answer, Result}}, #state{ reply = {From,_} = FromTag} = S) -
%% -------------------------------------------------------------------- %%
terminate(_Reason, #state{ fd = undefined }) ->
- set_pattern_trace(false, {'_','_','_'}),
+ ok = set_pattern_trace(false, ?default_pattern),
ok;
terminate(_Reason, #state{ fd = Fd }) ->
- file:close(Fd),
- set_pattern_trace(false, {'_','_','_'}),
+ ok = file:close(Fd),
+ ok = set_pattern_trace(false, ?default_pattern),
ok.
%% -------------------------------------------------------------------- %%
@@ -330,7 +353,19 @@ spin_profile(M, F, A) ->
end.
execute_profiling(Pid) ->
- Pid ! {self(), execute}.
+ Pid ! {self(), execute},
+ ok.
+
+
+get_trace_options([]) ->
+ [call];
+get_trace_options([{set_on_spawn, true}|Opts]) ->
+ [set_on_spawn | get_trace_options(Opts)];
+get_trace_options([set_on_spawn|Opts]) ->
+ [set_on_spawn | get_trace_options(Opts)];
+get_trace_options([_|Opts]) ->
+ get_trace_options(Opts).
+
set_pattern_trace(Flag, Pattern) ->
erlang:system_flag(multi_scheduling, block),
@@ -339,10 +374,6 @@ set_pattern_trace(Flag, Pattern) ->
erlang:system_flag(multi_scheduling, unblock),
ok.
-set_process_trace(Flag, Pids) ->
- % do we need procs for meta info?
- % could be useful
- set_process_trace(Flag, Pids, [call, set_on_spawn]).
set_process_trace(_, [], _) -> true;
set_process_trace(Flag, [Pid|Pids], Options) when is_pid(Pid) ->
try
diff --git a/lib/tools/test/eprof_SUITE.erl b/lib/tools/test/eprof_SUITE.erl
index 3283fa571f..148622cf07 100644
--- a/lib/tools/test/eprof_SUITE.erl
+++ b/lib/tools/test/eprof_SUITE.erl
@@ -21,12 +21,14 @@
-include_lib("test_server/include/test_server.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,tiny/1,eed/1,basic/1]).
+ init_per_group/2,end_per_group/2]).
+
+-export([tiny/1,eed/1,basic/1,basic_option/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [basic, tiny, eed].
+ [basic, basic_option, tiny, eed].
groups() ->
[].
@@ -49,140 +51,185 @@ basic(Config) when is_list(Config) ->
%% load eprof_test and change directory
- ?line {ok, OldCurDir} = file:get_cwd(),
+ {ok, OldCurDir} = file:get_cwd(),
Datadir = ?config(data_dir, Config),
Privdir = ?config(priv_dir, Config),
- ?line {ok,eprof_test} = compile:file(filename:join(Datadir, "eprof_test"),
+ {ok,eprof_test} = compile:file(filename:join(Datadir, "eprof_test"),
[trace,{outdir, Privdir}]),
- ?line ok = file:set_cwd(Privdir),
- ?line code:purge(eprof_test),
- ?line {module,eprof_test} = code:load_file(eprof_test),
+ ok = file:set_cwd(Privdir),
+ code:purge(eprof_test),
+ {module,eprof_test} = code:load_file(eprof_test),
%% rootset profiling
- ?line ensure_eprof_stopped(),
- ?line profiling = eprof:profile([self()]),
- ?line {error, already_profiling} = eprof:profile([self()]),
- ?line profiling_stopped = eprof:stop_profiling(),
- ?line profiling_already_stopped = eprof:stop_profiling(),
- ?line profiling = eprof:start_profiling([self(),self(),self()]),
- ?line profiling_stopped = eprof:stop_profiling(),
+ ensure_eprof_stopped(),
+ profiling = eprof:profile([self()]),
+ {error, already_profiling} = eprof:profile([self()]),
+ profiling_stopped = eprof:stop_profiling(),
+ profiling_already_stopped = eprof:stop_profiling(),
+ profiling = eprof:start_profiling([self(),self(),self()]),
+ profiling_stopped = eprof:stop_profiling(),
%% with patterns
- ?line profiling = eprof:start_profiling([self()], {?MODULE, '_', '_'}),
- ?line {error, already_profiling} = eprof:start_profiling([self()], {?MODULE, '_', '_'}),
- ?line profiling_stopped = eprof:stop_profiling(),
- ?line profiling = eprof:start_profiling([self()], {?MODULE, start_stop, '_'}),
- ?line profiling_stopped = eprof:stop_profiling(),
- ?line profiling = eprof:start_profiling([self()], {?MODULE, start_stop, 1}),
- ?line profiling_stopped = eprof:stop_profiling(),
+ profiling = eprof:start_profiling([self()], {?MODULE, '_', '_'}),
+ {error, already_profiling} = eprof:start_profiling([self()], {?MODULE, '_', '_'}),
+ profiling_stopped = eprof:stop_profiling(),
+ profiling = eprof:start_profiling([self()], {?MODULE, start_stop, '_'}),
+ profiling_stopped = eprof:stop_profiling(),
+ profiling = eprof:start_profiling([self()], {?MODULE, start_stop, 1}),
+ profiling_stopped = eprof:stop_profiling(),
%% with fun
- ?line {ok, _} = eprof:profile(fun() -> eprof_test:go(10) end),
- ?line profiling = eprof:profile([self()]),
- ?line {error, already_profiling} = eprof:profile(fun() -> eprof_test:go(10) end),
- ?line profiling_stopped = eprof:stop_profiling(),
- ?line {ok, _} = eprof:profile(fun() -> eprof_test:go(10) end),
- ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end),
- ?line Pid = whereis(eprof),
- ?line {ok, _} = eprof:profile(erlang:processes() -- [Pid], fun() -> eprof_test:go(10) end),
- ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, '_', '_'}),
- ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, go, '_'}),
- ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, go, 1}),
- ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, dec, 1}),
+ {ok, _} = eprof:profile(fun() -> eprof_test:go(10) end),
+ profiling = eprof:profile([self()]),
+ {error, already_profiling} = eprof:profile(fun() -> eprof_test:go(10) end),
+ profiling_stopped = eprof:stop_profiling(),
+ {ok, _} = eprof:profile(fun() -> eprof_test:go(10) end),
+ {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end),
+ Pid = whereis(eprof),
+ {ok, _} = eprof:profile(erlang:processes() -- [Pid], fun() -> eprof_test:go(10) end),
+ {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, '_', '_'}),
+ {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, go, '_'}),
+ {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, go, 1}),
+ {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, dec, 1}),
%% error case
- ?line error = eprof:profile([Pid], fun() -> eprof_test:go(10) end),
- ?line Pid = whereis(eprof),
- ?line error = eprof:profile([Pid], fun() -> eprof_test:go(10) end),
- ?line A = spawn(fun() -> receive _ -> ok end end),
- ?line profiling = eprof:profile([A]),
- ?line true = exit(A, kill_it),
- ?line profiling_stopped = eprof:stop_profiling(),
- ?line {error,_} = eprof:profile(fun() -> a = b end),
+ error = eprof:profile([Pid], fun() -> eprof_test:go(10) end),
+ Pid = whereis(eprof),
+ error = eprof:profile([Pid], fun() -> eprof_test:go(10) end),
+ A = spawn(fun() -> receive _ -> ok end end),
+ profiling = eprof:profile([A]),
+ true = exit(A, kill_it),
+ profiling_stopped = eprof:stop_profiling(),
+ {error,_} = eprof:profile(fun() -> a = b end),
%% with mfa
- ?line {ok, _} = eprof:profile([], eprof_test, go, [10]),
- ?line {ok, _} = eprof:profile([], eprof_test, go, [10], {eprof_test, dec, 1}),
+ {ok, _} = eprof:profile([], eprof_test, go, [10]),
+ {ok, _} = eprof:profile([], eprof_test, go, [10], {eprof_test, dec, 1}),
%% dump
- ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, '_', '_'}),
- ?line [{_, Mfas}] = eprof:dump(),
- ?line Dec_mfa = {eprof_test, dec, 1},
- ?line Go_mfa = {eprof_test, go, 1},
- ?line {value, {Go_mfa, { 1, _Time1}}} = lists:keysearch(Go_mfa, 1, Mfas),
- ?line {value, {Dec_mfa, {11, _Time2}}} = lists:keysearch(Dec_mfa, 1, Mfas),
+ {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, '_', '_'}),
+ [{_, Mfas}] = eprof:dump(),
+ Dec_mfa = {eprof_test, dec, 1},
+ Go_mfa = {eprof_test, go, 1},
+ {value, {Go_mfa, { 1, _Time1}}} = lists:keysearch(Go_mfa, 1, Mfas),
+ {value, {Dec_mfa, {11, _Time2}}} = lists:keysearch(Dec_mfa, 1, Mfas),
%% change current working directory
- ?line ok = file:set_cwd(OldCurDir),
- ?line stopped = eprof:stop(),
+ ok = file:set_cwd(OldCurDir),
+ stopped = eprof:stop(),
+ ok.
+
+basic_option(Config) when is_list(Config) ->
+ %% load eprof_test and change directory
+
+ {ok, OldCurDir} = file:get_cwd(),
+ Datadir = ?config(data_dir, Config),
+ Privdir = ?config(priv_dir, Config),
+ {ok,eprof_test} = compile:file(filename:join(Datadir, "eprof_test"),
+ [trace,{outdir, Privdir}]),
+ ok = file:set_cwd(Privdir),
+ code:purge(eprof_test),
+ {module,eprof_test} = code:load_file(eprof_test),
+
+ % vanilla
+ {ok, _} = eprof:profile(fun() -> eprof_test:do(10) end, [{set_on_spawn, true}]),
+
+ [{_, MfasDo1},{_, MfasLists1}] = eprof:dump(),
+ Mfas1 = MfasDo1 ++ MfasLists1,
+
+ {value, {_, {11, _}}} = lists:keysearch({eprof_test,dec,1}, 1, Mfas1),
+ {value, {_, { 1, _}}} = lists:keysearch({eprof_test, go,1}, 1, Mfas1),
+ {value, {_, { 9, _}}} = lists:keysearch({lists, split_2,5}, 1, Mfas1),
+ {value, {_, { 4, _}}} = lists:keysearch({lists, seq_loop,3}, 1, Mfas1),
+
+ {ok, _} = eprof:profile(fun() -> eprof_test:do(10) end, [set_on_spawn]),
+
+ [{_, MfasDo2},{_, MfasLists2}] = eprof:dump(),
+ Mfas2 = MfasDo2 ++ MfasLists2,
+ {value, {_, {11, _}}} = lists:keysearch({eprof_test,dec,1}, 1, Mfas2),
+ {value, {_, { 1, _}}} = lists:keysearch({eprof_test, go,1}, 1, Mfas2),
+ {value, {_, { 9, _}}} = lists:keysearch({lists, split_2,5}, 1, Mfas2),
+ {value, {_, { 4, _}}} = lists:keysearch({lists, seq_loop,3}, 1, Mfas2),
+
+ % disable trace set_on_spawn
+ {ok, _} = eprof:profile(fun() -> eprof_test:do(10) end, []),
+ [{_, Mfas3}] = eprof:dump(),
+ {value, {_, {11, _}}} = lists:keysearch({eprof_test,dec,1}, 1, Mfas3),
+ {value, {_, { 1, _}}} = lists:keysearch({eprof_test, go,1}, 1, Mfas3),
+ false = lists:keysearch({lists, split_2,5}, 1, Mfas3),
+ false = lists:keysearch({lists, seq_loop,3}, 1, Mfas3),
+
+ %% change current working directory
+ ok = file:set_cwd(OldCurDir),
+ stopped = eprof:stop(),
ok.
tiny(suite) -> [];
tiny(Config) when is_list(Config) ->
- ?line ensure_eprof_stopped(),
- ?line {ok, OldCurDir} = file:get_cwd(),
+ ensure_eprof_stopped(),
+ {ok, OldCurDir} = file:get_cwd(),
Datadir = ?config(data_dir, Config),
Privdir = ?config(priv_dir, Config),
- ?line TTrap=?t:timetrap(60*1000),
+ TTrap=?t:timetrap(60*1000),
% (Trace)Compile to priv_dir and make sure the correct version is loaded.
- ?line {ok,eprof_suite_test} = compile:file(filename:join(Datadir,
+ {ok,eprof_suite_test} = compile:file(filename:join(Datadir,
"eprof_suite_test"),
[trace,{outdir, Privdir}]),
- ?line ok = file:set_cwd(Privdir),
- ?line code:purge(eprof_suite_test),
- ?line {module,eprof_suite_test} = code:load_file(eprof_suite_test),
- ?line {ok,_Pid} = eprof:start(),
- ?line nothing_to_analyze = eprof:analyze(),
- ?line nothing_to_analyze = eprof:analyze(total),
- ?line eprof:profile([], eprof_suite_test, test, [Config]),
- ?line ok = eprof:analyze(),
- ?line ok = eprof:analyze(total),
- ?line ok = eprof:log("eprof_SUITE_logfile"),
- ?line stopped = eprof:stop(),
- ?line ?t:timetrap_cancel(TTrap),
- ?line ok = file:set_cwd(OldCurDir),
+ ok = file:set_cwd(Privdir),
+ code:purge(eprof_suite_test),
+ {module,eprof_suite_test} = code:load_file(eprof_suite_test),
+ {ok,_Pid} = eprof:start(),
+ nothing_to_analyze = eprof:analyze(),
+ nothing_to_analyze = eprof:analyze(total),
+ eprof:profile([], eprof_suite_test, test, [Config]),
+ ok = eprof:analyze(),
+ ok = eprof:analyze(total),
+ ok = eprof:log("eprof_SUITE_logfile"),
+ stopped = eprof:stop(),
+ ?t:timetrap_cancel(TTrap),
+ ok = file:set_cwd(OldCurDir),
ok.
eed(suite) -> [];
eed(Config) when is_list(Config) ->
- ?line ensure_eprof_stopped(),
- ?line Datadir = ?config(data_dir, Config),
- ?line Privdir = ?config(priv_dir, Config),
- ?line TTrap=?t:timetrap(5*60*1000),
+ ensure_eprof_stopped(),
+ Datadir = ?config(data_dir, Config),
+ Privdir = ?config(priv_dir, Config),
+ TTrap=?t:timetrap(5*60*1000),
%% (Trace)Compile to priv_dir and make sure the correct version is loaded.
- ?line code:purge(eed),
- ?line {ok,eed} = c:c(filename:join(Datadir, "eed"), [trace,{outdir,Privdir}]),
- ?line {ok,_Pid} = eprof:start(),
- ?line Script = filename:join(Datadir, "ed.script"),
- ?line ok = file:set_cwd(Datadir),
- ?line {T1,_} = statistics(runtime),
- ?line ok = eed:file(Script),
- ?line ok = eed:file(Script),
- ?line ok = eed:file(Script),
- ?line ok = eed:file(Script),
- ?line ok = eed:file(Script),
- ?line ok = eed:file(Script),
- ?line ok = eed:file(Script),
- ?line ok = eed:file(Script),
- ?line ok = eed:file(Script),
- ?line ok = eed:file(Script),
- ?line {T2,_} = statistics(runtime),
- ?line {ok,ok} = eprof:profile([], eed, file, [Script]),
- ?line {T3,_} = statistics(runtime),
- ?line profiling_already_stopped = eprof:stop_profiling(),
- ?line ok = eprof:analyze(),
- ?line ok = eprof:analyze(total),
- ?line ok = eprof:log("eprof_SUITE_logfile"),
- ?line stopped = eprof:stop(),
- ?line ?t:timetrap_cancel(TTrap),
+ code:purge(eed),
+ {ok,eed} = c:c(filename:join(Datadir, "eed"), [trace,{outdir,Privdir}]),
+ {ok,_Pid} = eprof:start(),
+ Script = filename:join(Datadir, "ed.script"),
+ ok = file:set_cwd(Datadir),
+ {T1,_} = statistics(runtime),
+ ok = eed:file(Script),
+ ok = eed:file(Script),
+ ok = eed:file(Script),
+ ok = eed:file(Script),
+ ok = eed:file(Script),
+ ok = eed:file(Script),
+ ok = eed:file(Script),
+ ok = eed:file(Script),
+ ok = eed:file(Script),
+ ok = eed:file(Script),
+ {T2,_} = statistics(runtime),
+ {ok,ok} = eprof:profile([], eed, file, [Script]),
+ {T3,_} = statistics(runtime),
+ profiling_already_stopped = eprof:stop_profiling(),
+ ok = eprof:analyze(),
+ ok = eprof:analyze(total),
+ ok = eprof:log("eprof_SUITE_logfile"),
+ stopped = eprof:stop(),
+ ?t:timetrap_cancel(TTrap),
try
S = lists:flatten(io_lib:format("~p times slower",
[10*(T3-T2)/(T2-T1)])),
@@ -198,5 +245,5 @@ ensure_eprof_stopped() ->
undefined ->
ok;
Pid ->
- ?line stopped=eprof:stop()
+ stopped=eprof:stop()
end.
diff --git a/lib/tools/test/eprof_SUITE_data/eprof_test.erl b/lib/tools/test/eprof_SUITE_data/eprof_test.erl
index 33c428e893..2d9e4c2945 100644
--- a/lib/tools/test/eprof_SUITE_data/eprof_test.erl
+++ b/lib/tools/test/eprof_SUITE_data/eprof_test.erl
@@ -1,5 +1,5 @@
-module(eprof_test).
--export([go/1]).
+-export([go/1, do/1]).
go(N) ->
0 = dec(N),
@@ -7,3 +7,16 @@ go(N) ->
dec(0) -> 0;
dec(N) -> dec(N - 1).
+
+
+
+load(N, Pid) ->
+ _ = lists:sort(lists:reverse(lists:seq(1, N))),
+ Pid ! {self(), ok}.
+
+
+do(N) ->
+ Me = self(),
+ Pid = spawn_link(fun() -> load(N, Me) end),
+ ok = go(N),
+ receive {Pid, ok} -> ok end.
diff --git a/system/doc/reference_manual/introduction.xml b/system/doc/reference_manual/introduction.xml
index 7737c34469..678b1964a6 100644
--- a/system/doc/reference_manual/introduction.xml
+++ b/system/doc/reference_manual/introduction.xml
@@ -75,7 +75,7 @@
<title>Reserved Words</title>
<p>The following are reserved words in Erlang:</p>
<p>after and andalso band begin bnot bor bsl bsr bxor case catch
- cond div end fun if let not of or orelse query receive rem try
+ cond div end fun if let not of or orelse receive rem try
when xor</p>
</section>