aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/doc/src/fascicules.xml18
-rw-r--r--lib/asn1/doc/src/notes.xml25
-rw-r--r--lib/asn1/src/asn1ct.erl40
-rw-r--r--lib/asn1/src/asn1ct_gen.erl47
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl18
-rw-r--r--lib/asn1/test/asn1_SUITE.erl4
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/Makefile22
-rw-r--r--lib/common_test/doc/src/ct.xml36
-rw-r--r--lib/common_test/doc/src/fascicules.xml18
-rw-r--r--lib/common_test/doc/src/notes.xml27
-rw-r--r--lib/common_test/doc/src/part_notes.xml41
-rw-r--r--lib/common_test/doc/src/part_notes_history.xml35
-rw-r--r--lib/common_test/src/ct.erl35
-rw-r--r--lib/common_test/src/ct_config.erl1
-rw-r--r--lib/common_test/src/ct_default_gl.erl1
-rw-r--r--lib/common_test/src/ct_event.erl1
-rw-r--r--lib/common_test/src/ct_gen_conn.erl9
-rw-r--r--lib/common_test/src/ct_hooks_lock.erl1
-rw-r--r--lib/common_test/src/ct_logs.erl20
-rw-r--r--lib/common_test/src/ct_master.erl2
-rw-r--r--lib/common_test/src/ct_master_event.erl1
-rw-r--r--lib/common_test/src/ct_master_logs.erl1
-rw-r--r--lib/common_test/src/ct_repeat.erl2
-rw-r--r--lib/common_test/src/ct_run.erl14
-rw-r--r--lib/common_test/src/ct_slave.erl1
-rw-r--r--lib/common_test/src/ct_telnet_client.erl1
-rw-r--r--lib/common_test/src/ct_testspec.erl7
-rw-r--r--lib/common_test/src/ct_util.erl68
-rw-r--r--lib/common_test/src/ct_webtool.erl1
-rw-r--r--lib/common_test/src/ct_webtool_sup.erl1
-rw-r--r--lib/common_test/src/cth_log_redirect.erl1
-rw-r--r--lib/common_test/src/test_server.erl10
-rw-r--r--lib/common_test/src/test_server_ctrl.erl37
-rw-r--r--lib/common_test/src/test_server_gl.erl1
-rw-r--r--lib/common_test/src/test_server_io.erl9
-rw-r--r--lib/common_test/src/test_server_node.erl1
-rw-r--r--lib/common_test/src/test_server_sup.erl3
-rw-r--r--lib/common_test/src/vts.erl2
-rw-r--r--lib/common_test/test/Makefile3
-rw-r--r--lib/common_test/test/ct_auto_clean_SUITE.erl262
-rw-r--r--lib/common_test/test/ct_auto_clean_SUITE_data/ac_SUITE.erl181
-rw-r--r--lib/common_test/test/ct_auto_clean_SUITE_data/cth_auto_clean.erl214
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/Makefile26
-rw-r--r--lib/compiler/doc/src/fascicules.xml15
-rw-r--r--lib/compiler/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/compiler/doc/src/notes.xml49
-rw-r--r--lib/compiler/doc/src/part_notes.xml40
-rw-r--r--lib/compiler/doc/src/part_notes_history.xml40
-rw-r--r--lib/compiler/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/compiler/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/compiler/src/beam_asm.erl9
-rw-r--r--lib/compiler/src/beam_listing.erl14
-rw-r--r--lib/compiler/src/beam_type.erl26
-rw-r--r--lib/compiler/src/beam_validator.erl14
-rw-r--r--lib/compiler/src/compile.erl6
-rw-r--r--lib/compiler/test/beam_type_SUITE.erl64
-rw-r--r--lib/compiler/test/compile_SUITE.erl12
-rw-r--r--lib/compiler/test/compile_SUITE_data/deterministic_module.erl21
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/cosEvent/doc/src/Makefile37
-rw-r--r--lib/cosEvent/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/cosEvent/doc/src/fascicules.xml18
-rw-r--r--lib/cosEvent/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/cosEvent/doc/src/notes.xml17
-rw-r--r--lib/cosEvent/doc/src/part_notes.xml38
-rw-r--r--lib/cosEvent/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/cosEvent/doc/src/summary.html.src1
-rw-r--r--lib/cosEvent/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/cosEvent/vsn.mk2
-rw-r--r--lib/cosEventDomain/doc/src/Makefile39
-rw-r--r--lib/cosEventDomain/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/cosEventDomain/doc/src/fascicules.xml18
-rw-r--r--lib/cosEventDomain/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/cosEventDomain/doc/src/notes.xml17
-rw-r--r--lib/cosEventDomain/doc/src/part_notes.xml37
-rw-r--r--lib/cosEventDomain/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/cosEventDomain/doc/src/summary.html.src1
-rw-r--r--lib/cosEventDomain/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/cosEventDomain/vsn.mk2
-rw-r--r--lib/cosFileTransfer/doc/src/Makefile33
-rw-r--r--lib/cosFileTransfer/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/cosFileTransfer/doc/src/fascicules.xml18
-rw-r--r--lib/cosFileTransfer/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/cosFileTransfer/doc/src/notes.xml17
-rw-r--r--lib/cosFileTransfer/doc/src/part_notes.xml37
-rw-r--r--lib/cosFileTransfer/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/cosFileTransfer/doc/src/summary.html.src1
-rw-r--r--lib/cosFileTransfer/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/cosFileTransfer/vsn.mk2
-rw-r--r--lib/cosNotification/doc/src/Makefile33
-rw-r--r--lib/cosNotification/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/cosNotification/doc/src/fascicules.xml18
-rw-r--r--lib/cosNotification/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/cosNotification/doc/src/notes.xml17
-rw-r--r--lib/cosNotification/doc/src/part_notes.xml37
-rw-r--r--lib/cosNotification/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/cosNotification/doc/src/summary.html.src1
-rw-r--r--lib/cosNotification/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/cosNotification/vsn.mk2
-rw-r--r--lib/cosProperty/doc/src/Makefile37
-rw-r--r--lib/cosProperty/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/cosProperty/doc/src/fascicules.xml18
-rw-r--r--lib/cosProperty/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/cosProperty/doc/src/notes.xml17
-rw-r--r--lib/cosProperty/doc/src/part_notes.xml37
-rw-r--r--lib/cosProperty/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/cosProperty/doc/src/summary.html.src1
-rw-r--r--lib/cosProperty/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/cosProperty/vsn.mk2
-rw-r--r--lib/cosTime/doc/src/Makefile35
-rw-r--r--lib/cosTime/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/cosTime/doc/src/fascicules.xml18
-rw-r--r--lib/cosTime/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/cosTime/doc/src/notes.xml17
-rw-r--r--lib/cosTime/doc/src/part_notes.xml37
-rw-r--r--lib/cosTime/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/cosTime/doc/src/summary.html.src1
-rw-r--r--lib/cosTime/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/cosTime/vsn.mk2
-rw-r--r--lib/cosTransactions/doc/src/Makefile35
-rw-r--r--lib/cosTransactions/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/cosTransactions/doc/src/fascicules.xml18
-rw-r--r--lib/cosTransactions/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/cosTransactions/doc/src/notes.xml17
-rw-r--r--lib/cosTransactions/doc/src/part_notes.xml37
-rw-r--r--lib/cosTransactions/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/cosTransactions/doc/src/summary.html.src1
-rw-r--r--lib/cosTransactions/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/cosTransactions/vsn.mk2
-rw-r--r--lib/crypto/c_src/Makefile.in25
-rw-r--r--lib/crypto/c_src/crypto.c1664
-rw-r--r--lib/crypto/c_src/otp_test_engine.c264
-rw-r--r--lib/crypto/doc/src/Makefile20
-rw-r--r--lib/crypto/doc/src/crypto.xml261
-rw-r--r--lib/crypto/doc/src/engine_keys.xml129
-rw-r--r--lib/crypto/doc/src/engine_load.xml110
-rw-r--r--lib/crypto/doc/src/fascicules.xml18
-rw-r--r--lib/crypto/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/crypto/doc/src/notes.xml70
-rw-r--r--lib/crypto/doc/src/usersguide.xml7
-rw-r--r--lib/crypto/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/crypto/src/Makefile18
-rw-r--r--lib/crypto/src/crypto.erl433
-rw-r--r--lib/crypto/test/Makefile7
-rw-r--r--lib/crypto/test/crypto_SUITE.erl31
-rw-r--r--lib/crypto/test/engine_SUITE.erl660
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/dsa_private_key.pem9
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/dsa_public_key.pem12
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_private_key.pem8
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_public_key.pem6
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key.pem28
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key_pwd.pem30
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key.pem9
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key_pwd.pem9
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/doc/src/Makefile2
-rw-r--r--lib/debugger/doc/src/fascicules.xml15
-rw-r--r--lib/debugger/doc/src/notes.xml15
-rw-r--r--lib/debugger/src/dbg_wx_win.erl2
-rw-r--r--lib/debugger/vsn.mk2
-rw-r--r--lib/dialyzer/doc/src/Makefile18
-rw-r--r--lib/dialyzer/doc/src/fascicules.xml18
-rw-r--r--lib/dialyzer/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/dialyzer/doc/src/notes.xml23
-rw-r--r--lib/dialyzer/doc/src/part_notes.xml37
-rw-r--r--lib/dialyzer/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl6
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl26
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl12
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/map_anon_fun2
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/map_anon_fun.erl9
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl15
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl13
-rw-r--r--lib/dialyzer/test/plt_SUITE.erl40
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/record_match3
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/abs.erl9
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/bsL.erl13
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl14
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/record_match.erl17
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/doc/src/notes.xml16
-rw-r--r--lib/diameter/doc/src/seealso.ent2
-rw-r--r--lib/diameter/src/diameter.appup.src6
-rw-r--r--lib/diameter/vsn.mk2
-rw-r--r--lib/edoc/doc/src/Makefile18
-rw-r--r--lib/edoc/doc/src/fascicules.xml15
-rw-r--r--lib/edoc/doc/src/notes.xml15
-rw-r--r--lib/edoc/doc/src/part_notes.xml39
-rw-r--r--lib/edoc/priv/Makefile17
-rw-r--r--lib/edoc/priv/edoc_generate (renamed from lib/edoc/priv/edoc_generate.src)36
-rw-r--r--lib/edoc/src/Makefile3
-rw-r--r--lib/edoc/src/edoc.app.src3
-rw-r--r--lib/edoc/src/edoc_specs.erl2
-rw-r--r--lib/edoc/src/otpsgml_layout.erl836
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/eldap/doc/src/Makefile2
-rw-r--r--lib/eldap/doc/src/fascicules.xml18
-rw-r--r--lib/eldap/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/eldap/doc/src/notes.xml15
-rw-r--r--lib/eldap/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/eldap/vsn.mk2
-rw-r--r--lib/erl_docgen/doc/src/fasc_dtds.xml116
-rw-r--r--lib/erl_docgen/doc/src/fascicules.xml15
-rw-r--r--lib/erl_docgen/doc/src/notes.xml21
-rw-r--r--lib/erl_docgen/priv/xsl/db_funcs.xsl136
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl10
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl87
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf_params.xsl128
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl2
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/erl_interface/doc/src/Makefile31
-rw-r--r--lib/erl_interface/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/erl_interface/doc/src/notes.xml15
-rw-r--r--lib/erl_interface/doc/src/part_notes.xml39
-rw-r--r--lib/erl_interface/doc/src/part_notes_history.xml37
-rw-r--r--lib/erl_interface/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/erl_interface/src/Makefile2
-rw-r--r--lib/erl_interface/src/Makefile.in2
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/eunit/doc/src/Makefile28
-rw-r--r--lib/eunit/doc/src/fascicules.xml18
-rw-r--r--lib/eunit/doc/src/notes.xml15
-rw-r--r--lib/eunit/doc/src/part_notes.xml40
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl9
-rw-r--r--lib/hipe/cerl/erl_types.erl8
-rw-r--r--lib/hipe/doc/src/Makefile18
-rw-r--r--lib/hipe/doc/src/fascicules.xml12
-rw-r--r--lib/hipe/doc/src/hipe_app.xml66
-rw-r--r--lib/hipe/doc/src/notes.xml33
-rw-r--r--lib/hipe/doc/src/part_notes.xml36
-rw-r--r--lib/hipe/llvm/hipe_llvm.erl2
-rw-r--r--lib/hipe/main/hipe.app.src2
-rw-r--r--lib/hipe/rtl/hipe_rtl_binary_construct.erl19
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_construct.erl13
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/ic/doc/src/Makefile27
-rw-r--r--lib/ic/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/ic/doc/src/fascicules.xml18
-rw-r--r--lib/ic/doc/src/ic.gifbin17015 -> 0 bytes
-rw-r--r--lib/ic/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/ic/doc/src/notes.xml17
-rw-r--r--lib/ic/doc/src/part_notes.xml38
-rw-r--r--lib/ic/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/ic/doc/src/summary.html.src1
-rw-r--r--lib/ic/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE_data/c_client.c8
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c8
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c8
-rw-r--r--lib/ic/vsn.mk2
-rw-r--r--lib/inets/doc/src/Makefile23
-rw-r--r--lib/inets/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/inets/doc/src/fascicules.xml19
-rw-r--r--lib/inets/doc/src/inets.gifbin9763 -> 0 bytes
-rw-r--r--lib/inets/doc/src/min_head.gifbin2652 -> 0 bytes
-rw-r--r--lib/inets/doc/src/mod_esi.xml2
-rw-r--r--lib/inets/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/inets/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/inets/doc/src/notes.xml32
-rw-r--r--lib/inets/doc/src/part_notes.xml40
-rw-r--r--lib/inets/doc/src/part_notes_history.xml35
-rw-r--r--lib/inets/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/inets/doc/src/summary.html.src1
-rw-r--r--lib/inets/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/inets/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl18
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl4
-rw-r--r--lib/inets/src/http_client/httpc_response.erl40
-rw-r--r--lib/inets/src/http_server/httpd_script_env.erl4
-rw-r--r--lib/inets/src/inets_app/inets.appup.src4
-rw-r--r--lib/inets/test/Makefile11
-rw-r--r--lib/inets/test/http_test_lib.erl180
-rw-r--r--lib/inets/test/httpc_SUITE.erl113
-rw-r--r--lib/inets/test/httpd_bench_SUITE.erl846
-rw-r--r--lib/inets/test/httpd_bench_SUITE_data/1M_filebin0 -> 1000000 bytes
-rw-r--r--lib/inets/test/httpd_bench_SUITE_data/1k_filebin0 -> 1000 bytes
-rw-r--r--lib/inets/test/httpd_block.erl372
-rw-r--r--lib/inets/test/httpd_test_lib.erl73
-rw-r--r--lib/inets/test/inets.spec4
-rw-r--r--lib/inets/test/inets_bench.spec1
-rw-r--r--lib/inets/test/inets_test_lib.erl38
-rw-r--r--lib/inets/test/make_certs.erl530
-rw-r--r--lib/inets/test/old_httpd_SUITE.erl2347
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/Makefile.src14
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/cgi_echo.c97
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/Makefile210
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/auth/group3
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd4
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat9
-rwxr-xr-xlib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh6
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf79
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf63
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf269
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types465
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf66
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml70
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html9
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml35
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml30
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml29
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml29
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml33
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html25
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html22
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html7
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html4
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html1
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html9
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html9
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/README161
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gifbin246 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gifbin242 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gifbin247 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gifbin2326 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gifbin216 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gifbin233 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gifbin205 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gifbin246 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gifbin246 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gifbin148 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gifbin308 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gifbin251 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gifbin268 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gifbin247 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gifbin235 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gifbin755 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gifbin781 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gifbin785 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gifbin745 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gifbin786 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gifbin780 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gifbin791 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gifbin796 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gifbin784 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gifbin784 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gifbin587 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gifbin576 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gifbin242 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gifbin251 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gifbin246 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gifbin1038 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gifbin214 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gifbin225 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gifbin163 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gifbin238 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gifbin236 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gifbin225 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gifbin242 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gifbin243 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gifbin219 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gifbin221 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gifbin220 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gifbin249 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gifbin217 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gifbin223 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gifbin1822 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gifbin11977 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gifbin274 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gifbin309 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gifbin286 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gifbin268 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gifbin276 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gifbin172 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gifbin249 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gifbin243 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gifbin237 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gifbin251 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gifbin249 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gifbin188 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gifbin198 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gifbin198 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gifbin191 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gifbin193 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gifbin189 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gifbin186 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gifbin185 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gifbin173 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gifbin254 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gifbin2748 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gifbin244 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gifbin267 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gifbin172 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gifbin258 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gifbin263 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gifbin242 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gifbin248 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gifbin221 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gifbin285 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gifbin264 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gifbin89 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gifbin53 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gifbin243 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gifbin251 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gifbin229 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gifbin242 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gifbin245 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gifbin164 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gifbin236 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gifbin236 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gifbin228 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gifbin261 -> 0 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip1
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem31
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem31
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/Makefile38
-rw-r--r--lib/jinterface/doc/src/fascicules.xml18
-rw-r--r--lib/jinterface/doc/src/index.html.src99
-rw-r--r--lib/jinterface/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/jinterface/doc/src/notes.xml15
-rw-r--r--lib/jinterface/doc/src/part_notes.xml39
-rw-r--r--lib/jinterface/doc/src/part_notes_history.xml37
-rw-r--r--lib/jinterface/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/jinterface/doc/src/summary.html.src1
-rw-r--r--lib/jinterface/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile3
-rw-r--r--lib/jinterface/test/jinterface_SUITE.erl20
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/Makefile15
-rw-r--r--lib/kernel/doc/src/fascicules.xml15
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml7
-rw-r--r--lib/kernel/doc/src/kernel_app.xml20
-rw-r--r--lib/kernel/doc/src/notes.xml35
-rw-r--r--lib/kernel/doc/src/os.xml43
-rw-r--r--lib/kernel/doc/src/part_notes.xml40
-rw-r--r--lib/kernel/doc/src/part_notes_history.xml40
-rw-r--r--lib/kernel/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/kernel/src/code_server.erl27
-rw-r--r--lib/kernel/src/disk_log.erl4
-rw-r--r--lib/kernel/src/erl_boot_server.erl37
-rw-r--r--lib/kernel/src/erts_debug.erl26
-rw-r--r--lib/kernel/src/group_history.erl4
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl5
-rw-r--r--lib/kernel/src/kernel.app.src2
-rw-r--r--lib/kernel/src/kernel.appup.src6
-rw-r--r--lib/kernel/src/os.erl37
-rw-r--r--lib/kernel/src/user.erl6
-rw-r--r--lib/kernel/test/code_SUITE.erl11
-rw-r--r--lib/kernel/test/code_SUITE_data/upgrade_client.erl1
-rw-r--r--lib/kernel/test/disk_log_SUITE.erl2
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl8
-rw-r--r--lib/kernel/test/global_SUITE.erl17
-rw-r--r--lib/kernel/test/inet_SUITE.erl8
-rw-r--r--lib/kernel/test/os_SUITE.erl18
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl15
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/megaco/doc/src/Makefile35
-rw-r--r--lib/megaco/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/megaco/doc/src/book.xml5
-rw-r--r--lib/megaco/doc/src/fascicules.xml18
-rw-r--r--lib/megaco/doc/src/files.mk11
-rw-r--r--lib/megaco/doc/src/index.html.src113
-rw-r--r--lib/megaco/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/megaco/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/megaco/doc/src/notes.xml176
-rw-r--r--lib/megaco/doc/src/part_notes.xml40
-rw-r--r--lib/megaco/doc/src/part_notes_history.xml41
-rw-r--r--lib/megaco/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/megaco/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/megaco/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/megaco/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/Makefile35
-rw-r--r--lib/mnesia/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/mnesia/doc/src/fascicules.xml18
-rw-r--r--lib/mnesia/doc/src/mnesia.gifbin15184 -> 0 bytes
-rw-r--r--lib/mnesia/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/mnesia/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/mnesia/doc/src/notes.xml67
-rw-r--r--lib/mnesia/doc/src/part_notes.xml42
-rw-r--r--lib/mnesia/doc/src/part_notes_history.xml42
-rw-r--r--lib/mnesia/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/mnesia/doc/src/summary.html.src1
-rw-r--r--lib/mnesia/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/mnesia/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/mnesia/src/mnesia.erl3
-rw-r--r--lib/mnesia/src/mnesia_checkpoint.erl4
-rw-r--r--lib/mnesia/src/mnesia_log.erl4
-rw-r--r--lib/mnesia/src/mnesia_schema.erl16
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/Makefile25
-rw-r--r--lib/observer/doc/src/fascicules.xml18
-rw-r--r--lib/observer/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/observer/doc/src/notes.xml67
-rw-r--r--lib/observer/doc/src/part_notes.xml39
-rw-r--r--lib/observer/doc/src/part_notes_history.xml39
-rw-r--r--lib/observer/include/etop.hrl2
-rw-r--r--lib/observer/src/cdv_bin_cb.erl2
-rw-r--r--lib/observer/src/cdv_detail_wx.erl11
-rw-r--r--lib/observer/src/cdv_info_wx.erl4
-rw-r--r--lib/observer/src/cdv_port_cb.erl17
-rw-r--r--lib/observer/src/cdv_proc_cb.erl4
-rw-r--r--lib/observer/src/cdv_sched_cb.erl18
-rw-r--r--lib/observer/src/crashdump_viewer.erl899
-rw-r--r--lib/observer/src/crashdump_viewer.hrl16
-rw-r--r--lib/observer/src/observer_html_lib.erl6
-rw-r--r--lib/observer/src/observer_lib.erl75
-rw-r--r--lib/observer/src/observer_port_wx.erl4
-rw-r--r--lib/observer/src/observer_pro_wx.erl5
-rw-r--r--lib/observer/src/observer_procinfo.erl6
-rw-r--r--lib/observer/src/observer_trace_wx.erl2
-rw-r--r--lib/observer/src/observer_tv_wx.erl355
-rw-r--r--lib/observer/test/crashdump_helper.erl53
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl196
-rw-r--r--lib/observer/test/observer_SUITE.erl7
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/doc/src/Makefile30
-rw-r--r--lib/odbc/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/odbc/doc/src/fascicules.xml19
-rw-r--r--lib/odbc/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/odbc/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/odbc/doc/src/notes.xml55
-rw-r--r--lib/odbc/doc/src/odbc.gifbin15184 -> 0 bytes
-rw-r--r--lib/odbc/doc/src/odbc_index.gifbin15184 -> 0 bytes
-rw-r--r--lib/odbc/doc/src/part_notes.xml41
-rw-r--r--lib/odbc/doc/src/part_notes_history.xml35
-rw-r--r--lib/odbc/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/odbc/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/odbc/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/orber/doc/src/CosNaming.xml6
-rw-r--r--lib/orber/doc/src/CosNaming_NamingContext.xml45
-rw-r--r--lib/orber/doc/src/CosNaming_NamingContextExt.xml7
-rw-r--r--lib/orber/doc/src/Makefile25
-rw-r--r--lib/orber/doc/src/any.xml7
-rw-r--r--lib/orber/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/orber/doc/src/ch_debugging.xml4
-rw-r--r--lib/orber/doc/src/ch_exceptions.xml4
-rw-r--r--lib/orber/doc/src/ch_idl_to_erlang_mapping.xml10
-rw-r--r--lib/orber/doc/src/ch_install.xml24
-rw-r--r--lib/orber/doc/src/ch_interceptors.xml15
-rw-r--r--lib/orber/doc/src/ch_naming_service.xml36
-rw-r--r--lib/orber/doc/src/ch_orberweb.xml5
-rw-r--r--lib/orber/doc/src/ch_stubs.xml4
-rw-r--r--lib/orber/doc/src/corba.xml14
-rw-r--r--lib/orber/doc/src/fascicules.xml18
-rw-r--r--lib/orber/doc/src/fixed.xml7
-rw-r--r--lib/orber/doc/src/lname.xml10
-rw-r--r--lib/orber/doc/src/lname_component.xml10
-rw-r--r--lib/orber/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/orber/doc/src/notes.xml23
-rw-r--r--lib/orber/doc/src/orber.gifbin17015 -> 0 bytes
-rw-r--r--lib/orber/doc/src/part_notes.xml37
-rw-r--r--lib/orber/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/orber/doc/src/summary.html.src1
-rw-r--r--lib/orber/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/orber/vsn.mk2
-rw-r--r--lib/os_mon/doc/src/Makefile27
-rw-r--r--lib/os_mon/doc/src/fascicules.xml15
-rw-r--r--lib/os_mon/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/os_mon/doc/src/notes.xml15
-rw-r--r--lib/os_mon/doc/src/part_notes.xml37
-rw-r--r--lib/os_mon/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/os_mon/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/otp_mibs/doc/src/Makefile15
-rw-r--r--lib/otp_mibs/doc/src/fascicules.xml19
-rw-r--r--lib/otp_mibs/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/otp_mibs/doc/src/notes.xml15
-rw-r--r--lib/otp_mibs/doc/src/part_notes.xml38
-rw-r--r--lib/otp_mibs/vsn.mk2
-rw-r--r--lib/parsetools/doc/src/Makefile24
-rw-r--r--lib/parsetools/doc/src/fascicules.xml15
-rw-r--r--lib/parsetools/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/parsetools/doc/src/notes.xml15
-rw-r--r--lib/parsetools/doc/src/part_notes.xml44
-rw-r--r--lib/parsetools/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/parsetools/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/parsetools/include/leexinc.hrl4
-rw-r--r--lib/parsetools/vsn.mk2
-rw-r--r--lib/public_key/doc/src/Makefile8
-rw-r--r--lib/public_key/doc/src/fascicules.xml19
-rw-r--r--lib/public_key/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/public_key/doc/src/notes.xml25
-rw-r--r--lib/public_key/doc/src/part_notes.xml39
-rw-r--r--lib/public_key/doc/src/public_key.xml119
-rw-r--r--lib/public_key/doc/src/public_key_records.xml6
-rw-r--r--lib/public_key/src/pubkey_cert.erl152
-rw-r--r--lib/public_key/src/pubkey_pbe.erl3
-rw-r--r--lib/public_key/src/pubkey_ssh.erl138
-rw-r--r--lib/public_key/src/public_key.erl56
-rw-r--r--lib/public_key/test/public_key_SUITE.erl79
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ec_key_param0.pem28
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ec_key_param1.pem25
-rw-r--r--lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName_IP.pem22
-rw-r--r--lib/public_key/test/public_key_SUITE_data/verify_hostname_ip.conf3
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/runtime_tools/doc/src/Makefile27
-rw-r--r--lib/runtime_tools/doc/src/fascicules.xml15
-rw-r--r--lib/runtime_tools/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/runtime_tools/doc/src/notes.xml32
-rw-r--r--lib/runtime_tools/doc/src/part_notes.xml39
-rw-r--r--lib/runtime_tools/doc/src/part_notes_history.xml39
-rw-r--r--lib/runtime_tools/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/runtime_tools/src/erts_alloc_config.erl8
-rw-r--r--lib/runtime_tools/src/system_information.erl232
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/Makefile26
-rw-r--r--lib/sasl/doc/src/fascicules.xml18
-rw-r--r--lib/sasl/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/sasl/doc/src/notes.xml20
-rw-r--r--lib/sasl/doc/src/part_notes.xml39
-rw-r--r--lib/sasl/doc/src/part_notes_history.xml39
-rw-r--r--lib/sasl/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/sasl/src/format_lib_supp.erl6
-rw-r--r--lib/sasl/test/rb_SUITE.erl18
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/doc/src/Makefile38
-rw-r--r--lib/snmp/doc/src/book.gifbin1081 -> 0 bytes
-rw-r--r--lib/snmp/doc/src/fascicules.xml18
-rw-r--r--lib/snmp/doc/src/files.mk21
-rw-r--r--lib/snmp/doc/src/index.html.src99
-rw-r--r--lib/snmp/doc/src/min_head.gifbin2652 -> 0 bytes
-rw-r--r--lib/snmp/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/snmp/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/snmp/doc/src/notes.xml429
-rw-r--r--lib/snmp/doc/src/part_notes.xml41
-rw-r--r--lib/snmp/doc/src/part_notes_history.xml42
-rw-r--r--lib/snmp/doc/src/ref_man.gifbin1530 -> 0 bytes
-rw-r--r--lib/snmp/doc/src/snmp.gifbin15889 -> 0 bytes
-rw-r--r--lib/snmp/doc/src/snmp_impl_example_agent.xml10
-rw-r--r--lib/snmp/doc/src/summary.html.src1
-rw-r--r--lib/snmp/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/snmp/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/snmp/test/snmp_agent_test.erl28
-rw-r--r--lib/snmp/test/snmp_manager_test.erl29
-rw-r--r--lib/snmp/test/snmp_to_snmpnet_SUITE.erl25
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/Makefile18
-rw-r--r--lib/ssh/doc/src/fascicules.xml18
-rw-r--r--lib/ssh/doc/src/notes.xml111
-rw-r--r--lib/ssh/doc/src/part_notes.xml38
-rw-r--r--lib/ssh/doc/src/ssh.xml18
-rw-r--r--lib/ssh/doc/src/ssh_client_key_api.xml33
-rw-r--r--lib/ssh/doc/src/ssh_server_key_api.xml28
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml7
-rw-r--r--lib/ssh/src/ssh.app.src4
-rw-r--r--lib/ssh/src/ssh.erl6
-rw-r--r--lib/ssh/src/ssh.hrl4
-rw-r--r--lib/ssh/src/ssh_acceptor_sup.erl5
-rw-r--r--lib/ssh/src/ssh_auth.erl14
-rw-r--r--lib/ssh/src/ssh_channel_sup.erl11
-rw-r--r--lib/ssh/src/ssh_cli.erl17
-rw-r--r--lib/ssh/src/ssh_connection.erl28
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl65
-rw-r--r--lib/ssh/src/ssh_connection_sup.erl5
-rw-r--r--lib/ssh/src/ssh_dbg.erl211
-rw-r--r--lib/ssh/src/ssh_message.erl20
-rw-r--r--lib/ssh/src/ssh_options.erl14
-rw-r--r--lib/ssh/src/ssh_subsystem_sup.erl8
-rw-r--r--lib/ssh/src/ssh_sup.erl15
-rw-r--r--lib/ssh/src/ssh_system_sup.erl9
-rw-r--r--lib/ssh/src/ssh_transport.erl131
-rw-r--r--lib/ssh/src/sshc_sup.erl5
-rw-r--r--lib/ssh/src/sshd_sup.erl6
-rw-r--r--lib/ssh/test/Makefile4
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl9
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl10
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl154
-rw-r--r--lib/ssh/test/ssh_bench_SUITE.erl48
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl1399
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image38
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh28
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run27
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image72
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image71
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all89
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key12
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key2565
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key3846
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key5217
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key27
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa12
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa5
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa2565
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa3846
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa5217
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa27
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub1
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl80
-rw-r--r--lib/ssh/test/ssh_engine_SUITE.erl146
-rw-r--r--lib/ssh/test/ssh_engine_SUITE_data/dsa_private_key.pem9
-rw-r--r--lib/ssh/test/ssh_engine_SUITE_data/ecdsa_private_key.pem8
-rw-r--r--lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key.pem28
-rw-r--r--lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key_pwd.pem30
-rw-r--r--lib/ssh/test/ssh_key_cb_engine_keys.erl62
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl122
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl11
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl5
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl6
-rw-r--r--lib/ssh/test/ssh_sup_SUITE.erl113
-rw-r--r--lib/ssh/test/ssh_test_lib.erl78
-rw-r--r--lib/ssh/test/ssh_test_lib.hrl12
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl362
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/Makefile24
-rw-r--r--lib/ssl/doc/src/fascicules.xml19
-rw-r--r--lib/ssl/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/ssl/doc/src/notes.xml227
-rw-r--r--lib/ssl/doc/src/pkix_certs.xml59
-rw-r--r--lib/ssl/doc/src/release_notes.xml50
-rw-r--r--lib/ssl/doc/src/ssl.xml111
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml128
-rw-r--r--lib/ssl/doc/src/ssl_introduction.xml2
-rw-r--r--lib/ssl/doc/src/using_ssl.xml81
-rw-r--r--lib/ssl/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/ssl/src/dtls_connection.erl1118
-rw-r--r--lib/ssl/src/dtls_handshake.erl163
-rw-r--r--lib/ssl/src/dtls_record.erl308
-rw-r--r--lib/ssl/src/dtls_socket.erl31
-rw-r--r--lib/ssl/src/dtls_udp_listener.erl7
-rw-r--r--lib/ssl/src/dtls_v1.erl8
-rw-r--r--lib/ssl/src/inet_tls_dist.erl8
-rw-r--r--lib/ssl/src/ssl.app.src2
-rw-r--r--lib/ssl/src/ssl.erl202
-rw-r--r--lib/ssl/src/ssl_alert.erl6
-rw-r--r--lib/ssl/src/ssl_cipher.erl1430
-rw-r--r--lib/ssl/src/ssl_config.erl10
-rw-r--r--lib/ssl/src/ssl_connection.erl451
-rw-r--r--lib/ssl/src/ssl_crl_cache.erl6
-rw-r--r--lib/ssl/src/ssl_dist_sup.erl67
-rw-r--r--lib/ssl/src/ssl_handshake.erl1940
-rw-r--r--lib/ssl/src/ssl_internal.hrl13
-rw-r--r--lib/ssl/src/ssl_record.erl24
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl30
-rw-r--r--lib/ssl/src/tls_connection.erl649
-rw-r--r--lib/ssl/src/tls_handshake.erl198
-rw-r--r--lib/ssl/src/tls_record.erl273
-rw-r--r--lib/ssl/src/tls_socket.erl43
-rw-r--r--lib/ssl/test/Makefile1
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl172
-rw-r--r--lib/ssl/test/ssl_engine_SUITE.erl142
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl45
-rw-r--r--lib/ssl/test/ssl_test_lib.erl195
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl45
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/Makefile17
-rw-r--r--lib/stdlib/doc/src/assert_hrl.xml2
-rw-r--r--lib/stdlib/doc/src/digraph.xml4
-rw-r--r--lib/stdlib/doc/src/ets.xml10
-rw-r--r--lib/stdlib/doc/src/fascicules.xml18
-rw-r--r--lib/stdlib/doc/src/filelib.xml4
-rw-r--r--lib/stdlib/doc/src/filename.xml19
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml54
-rw-r--r--lib/stdlib/doc/src/notes.xml43
-rw-r--r--lib/stdlib/doc/src/part_notes.xml39
-rw-r--r--lib/stdlib/doc/src/part_notes_history.xml39
-rw-r--r--lib/stdlib/doc/src/timer.xml2
-rw-r--r--lib/stdlib/doc/src/unicode.xml9
-rw-r--r--lib/stdlib/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/stdlib/src/epp.erl18
-rw-r--r--lib/stdlib/src/erl_tar.erl2
-rw-r--r--lib/stdlib/src/ets.erl6
-rw-r--r--lib/stdlib/src/filelib.erl21
-rw-r--r--lib/stdlib/src/filename.erl17
-rw-r--r--lib/stdlib/src/gen_event.erl5
-rw-r--r--lib/stdlib/src/gen_fsm.erl4
-rw-r--r--lib/stdlib/src/gen_statem.erl1573
-rw-r--r--lib/stdlib/src/io_lib.erl4
-rw-r--r--lib/stdlib/src/io_lib_fread.erl4
-rw-r--r--lib/stdlib/src/lib.erl6
-rw-r--r--lib/stdlib/src/shell.erl6
-rw-r--r--lib/stdlib/src/string.erl756
-rw-r--r--lib/stdlib/src/sys.erl6
-rw-r--r--lib/stdlib/test/epp_SUITE.erl6
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl7
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl6
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/unicode12
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/unicode22
-rw-r--r--lib/stdlib/test/ets_SUITE.erl26
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl35
-rw-r--r--lib/stdlib/test/filename_SUITE.erl120
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl8
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl14
-rw-r--r--lib/stdlib/test/io_SUITE.erl6
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl16
-rw-r--r--lib/stdlib/test/re_SUITE.erl15
-rw-r--r--lib/stdlib/test/shell_SUITE.erl25
-rw-r--r--lib/stdlib/test/string_SUITE.erl125
-rw-r--r--lib/stdlib/test/unicode_util_SUITE.erl17
-rwxr-xr-xlib/stdlib/uc_spec/gen_unicode_mod.escript31
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/doc/src/Makefile18
-rw-r--r--lib/syntax_tools/doc/src/fascicules.xml18
-rw-r--r--lib/syntax_tools/doc/src/notes.xml15
-rw-r--r--lib/syntax_tools/doc/src/part_notes.xml42
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/tools/doc/src/Makefile20
-rw-r--r--lib/tools/doc/src/fascicules.xml18
-rw-r--r--lib/tools/doc/src/lcnt.xml2
-rw-r--r--lib/tools/doc/src/note.gifbin1539 -> 0 bytes
-rw-r--r--lib/tools/doc/src/notes.xml15
-rw-r--r--lib/tools/doc/src/part_notes.xml39
-rw-r--r--lib/tools/doc/src/part_notes_history.xml39
-rw-r--r--lib/tools/doc/src/warning.gifbin1498 -> 0 bytes
-rw-r--r--lib/tools/emacs/Makefile20
-rw-r--r--lib/tools/emacs/erlang-skels.el248
-rw-r--r--lib/tools/emacs/erlang.el127
-rw-r--r--lib/tools/emacs/test.erl.indented784
-rw-r--r--lib/tools/emacs/test.erl.orig784
-rw-r--r--lib/tools/src/cover.erl18
-rw-r--r--lib/tools/src/lcnt.erl9
-rw-r--r--lib/tools/test/cover_SUITE.erl28
-rw-r--r--lib/tools/test/emacs_SUITE.erl73
-rw-r--r--lib/tools/test/emacs_SUITE_data/comments25
-rw-r--r--lib/tools/test/emacs_SUITE_data/comprehensions47
-rw-r--r--lib/tools/test/emacs_SUITE_data/funcs174
-rw-r--r--lib/tools/test/emacs_SUITE_data/highlight78
-rw-r--r--lib/tools/test/emacs_SUITE_data/icr157
-rw-r--r--lib/tools/test/emacs_SUITE_data/macros31
-rw-r--r--lib/tools/test/emacs_SUITE_data/records35
-rw-r--r--lib/tools/test/emacs_SUITE_data/terms174
-rw-r--r--lib/tools/test/emacs_SUITE_data/try_catch166
-rw-r--r--lib/tools/test/emacs_SUITE_data/type_specs110
-rw-r--r--lib/tools/test/lcnt_SUITE.erl15
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/wx/c_src/wxe_driver.c23
-rw-r--r--lib/wx/doc/src/Makefile4
-rw-r--r--lib/wx/doc/src/fascicules.xml15
-rw-r--r--lib/wx/doc/src/notes.xml16
-rw-r--r--lib/wx/doc/src/part_notes.xml38
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--lib/xmerl/doc/src/Makefile32
-rw-r--r--lib/xmerl/doc/src/fascicules.xml18
-rw-r--r--lib/xmerl/doc/src/notes.xml15
-rw-r--r--lib/xmerl/doc/src/part_notes.xml40
-rw-r--r--lib/xmerl/vsn.mk2
843 files changed, 22266 insertions, 18084 deletions
diff --git a/lib/asn1/doc/src/fascicules.xml b/lib/asn1/doc/src/fascicules.xml
deleted file mode 100644
index 837b4f57f4..0000000000
--- a/lib/asn1/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index ae6660c143..1abe983221 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -32,6 +32,31 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 5.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ There was a issue with BER encoding and the
+ <c>undec_rest</c> option in generated decoders. An
+ exception could be thrown instead of returning an error
+ tuple.</p>
+ <p>
+ Own Id: OTP-14786 Aux Id: ERL-518 </p>
+ </item>
+ <item>
+ <p>
+ The asn1ct:test functions crashed on decoders generated
+ with options <c>no_ok_wrapper</c>, <c>undec_rest</c>.</p>
+ <p>
+ Own Id: OTP-14787 Aux Id: ERL-518 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 5.0.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index f36d71a601..81a2735a0d 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -1335,25 +1335,39 @@ test_value(Module, Type, Value) ->
in_process(fun() ->
case catch Module:encode(Type, Value) of
{ok, Bytes} ->
- NewBytes = prepare_bytes(Bytes),
- case Module:decode(Type, NewBytes) of
- {ok, Value} ->
- {ok, {Module, Type, Value}};
- {ok, Res} ->
- {error, {asn1,
- {encode_decode_mismatch,
- {{Module, Type, Value}, Res}}}};
- Error ->
- {error, {asn1,
- {{decode,
- {Module, Type, Value}, Error}}}}
- end;
+ test_value_decode(Module, Type, Value, Bytes);
+ Bytes when is_binary(Bytes) ->
+ test_value_decode(Module, Type, Value, Bytes);
Error ->
{error, {asn1,
{encode, {{Module, Type, Value}, Error}}}}
end
end).
+
+test_value_decode(Module, Type, Value, Bytes) ->
+ NewBytes = prepare_bytes(Bytes),
+ case Module:decode(Type, NewBytes) of
+ {ok,Value} -> {ok, {Module,Type,Value}};
+ {ok,Value,<<>>} -> {ok, {Module,Type,Value}};
+ Value -> {ok, {Module,Type,Value}};
+ {Value,<<>>} -> {ok, {Module,Type,Value}};
+
+ %% Errors:
+ {ok, Res} ->
+ {error, {asn1,
+ {encode_decode_mismatch,
+ {{Module, Type, Value}, Res}}}};
+ {ok, Res, Rest} ->
+ {error, {asn1,
+ {encode_decode_mismatch,
+ {{Module, Type, Value}, {Res,Rest}}}}};
+ Error ->
+ {error, {asn1,
+ {{decode,
+ {Module, Type, Value}, Error}}}}
+ end.
+
value(Module, Type) -> value(Module, Type, []).
value(Module, Type, Includes) ->
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index 806f8420ec..da9f6ac559 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -707,6 +707,7 @@ gen_exports([_|_]=L0, Prefix, Arity) ->
pgen_dispatcher(Erules, []) ->
gen_info_functions(Erules);
pgen_dispatcher(Gen, Types) ->
+ %% MODULE HEAD
emit(["-export([encode/2,decode/2]).",nl,nl]),
gen_info_functions(Gen),
@@ -714,6 +715,7 @@ pgen_dispatcher(Gen, Types) ->
NoFinalPadding = lists:member(no_final_padding, Options),
NoOkWrapper = proplists:get_bool(no_ok_wrapper, Options),
+ %% ENCODER
Call = case Gen of
#gen{erule=per,aligned=true} ->
asn1ct_func:need({per,complete,1}),
@@ -740,6 +742,7 @@ pgen_dispatcher(Gen, Types) ->
end,
emit([nl,nl]),
+ %% DECODER
ReturnRest = proplists:get_bool(undec_rest, Gen#gen.options),
Data = case Gen#gen.erule =:= ber andalso ReturnRest of
true -> "Data0";
@@ -747,6 +750,12 @@ pgen_dispatcher(Gen, Types) ->
end,
emit(["decode(Type, ",Data,") ->",nl]),
+
+ case NoOkWrapper of
+ false -> emit(["try",nl]);
+ true -> ok
+ end,
+
DecWrap =
case {Gen,ReturnRest} of
{#gen{erule=ber},false} ->
@@ -754,32 +763,38 @@ pgen_dispatcher(Gen, Types) ->
"element(1, ber_decode_nif(Data))";
{#gen{erule=ber},true} ->
asn1ct_func:need({ber,ber_decode_nif,1}),
- emit(["{Data,Rest} = ber_decode_nif(Data0),",nl]),
+ emit([" {Data,Rest} = ber_decode_nif(Data0),",nl]),
"Data";
{_,_} ->
"Data"
end,
- emit([case NoOkWrapper of
- false -> "try";
- true -> "case"
- end, " decode_disp(Type, ",DecWrap,") of",nl]),
- case Gen of
- #gen{erule=ber} ->
- emit([" Result ->",nl]);
- #gen{erule=per} ->
- emit([" {Result,Rest} ->",nl])
- end,
- case ReturnRest of
- false -> result_line(NoOkWrapper, ["Result"]);
- true -> result_line(NoOkWrapper, ["Result","Rest"])
+
+ DecodeDisp = ["decode_disp(Type, ",DecWrap,")"],
+ case {Gen,ReturnRest} of
+ {#gen{erule=ber},true} ->
+ emit([" Result = ",DecodeDisp,",",nl]),
+ result_line(NoOkWrapper, ["Result","Rest"]);
+ {#gen{erule=ber},false} ->
+ emit([" Result = ",DecodeDisp,",",nl]),
+ result_line(NoOkWrapper, ["Result"]);
+
+
+ {#gen{erule=per},true} ->
+ emit([" {Result,Rest} = ",DecodeDisp,",",nl]),
+ result_line(NoOkWrapper, ["Result","Rest"]);
+ {#gen{erule=per},false} ->
+ emit([" {Result,_Rest} = ",DecodeDisp,",",nl]),
+ result_line(NoOkWrapper, ["Result"])
end,
+
case NoOkWrapper of
false ->
emit([nl,try_catch(),nl,nl]);
true ->
- emit([nl,"end.",nl,nl])
+ emit([".",nl,nl])
end,
+ %% REST of MODULE
gen_decode_partial_incomplete(Gen),
gen_partial_inc_dispatcher(Gen),
@@ -787,7 +802,7 @@ pgen_dispatcher(Gen, Types) ->
gen_dispatcher(Types, "decode_disp", "dec_").
result_line(NoOkWrapper, Items) ->
- S = [" "|case NoOkWrapper of
+ S = [" "|case NoOkWrapper of
false -> result_line_1(["ok"|Items]);
true -> result_line_1(Items)
end],
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 82e9326294..c09b0f47d1 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -47,14 +47,20 @@ dialyzer_suppressions(#gen{erule=per,aligned=Aligned}) ->
false -> uper;
true -> per
end,
- case asn1ct_func:is_used({Mod,complete,1}) of
+ suppress({Mod,complete,1}),
+ suppress({per_common,to_bitstring,2}),
+ emit([" ok.",nl]).
+
+suppress({M,F,A}=MFA) ->
+ case asn1ct_func:is_used(MFA) of
false ->
ok;
true ->
- emit([" _ = complete(Arg),",nl])
- end,
- emit([" ok.",nl]).
-
+ Args =
+ [lists:concat(["element(",I,", Arg)"])
+ || I <- lists:seq(1, A)],
+ emit([" ",{call,M,F,Args},com,nl])
+ end.
gen_encode(Erules,Type) when is_record(Type,typedef) ->
gen_encode_user(Erules,Type).
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index b98a704e28..bfeffa969f 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -1355,8 +1355,8 @@ xref_export_all(_Config) ->
[] ->
ok;
[_|_] ->
- S = [io_lib:format("~p:~p/~p\n", [M,F,A]) || {M,F,A} <- Unused],
- io:format("There are unused functions:\n\n~s\n", [S]),
+ Msg = [io_lib:format("~p:~p/~p\n", [M,F,A]) || {M,F,A} <- Unused],
+ io:format("There are unused functions:\n\n~s\n", [Msg]),
?t:fail(unused_functions)
end.
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index ef83b9e3dc..4cd89089e9 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1 +1 @@
-ASN1_VSN = 5.0.3
+ASN1_VSN = 5.0.4
diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile
index faa2d58a06..293ef591cb 100644
--- a/lib/common_test/doc/src/Makefile
+++ b/lib/common_test/doc/src/Makefile
@@ -57,11 +57,10 @@ XML_REF3_FILES = ct.xml \
ct_testspec.xml
XML_REF6_FILES = common_test_app.xml
-XML_PART_FILES = part.xml
-# part_notes.xml \
-# part_notes_history.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = \
+ introduction.xml \
basics_chapter.xml \
getting_started_chapter.xml \
install_chapter.xml \
@@ -76,8 +75,7 @@ XML_CHAPTER_FILES = \
event_handler_chapter.xml \
ct_hooks_chapter.xml \
dependencies_chapter.xml \
- notes.xml \
- notes_history.xml
+ notes.xml
BOOK_FILES = book.xml
@@ -111,10 +109,10 @@ SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
TOP_SPECS_FILE = specs.xml
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
SPECS_FLAGS = -I../../include -I../../../snmp/include \
-I../../../kernel/include
@@ -131,14 +129,14 @@ $(TOP_PDF_FILE): $(XML_FILES)
pdf: $(TOP_PDF_FILE)
-html: gifs $(HTML_REF_MAN_FILE)
+html: gifs $(HTML_REF_MAN_FILE)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
man: $(MAN6_FILES) $(MAN3_FILES) $(MAN1_FILES)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
@@ -147,11 +145,11 @@ clean clean_docs:
rm -f $(MAN6DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f $(SPECDIR)/*
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml
index 1a3cfdb0c5..afd8741cd1 100644
--- a/lib/common_test/doc/src/ct.xml
+++ b/lib/common_test/doc/src/ct.xml
@@ -1060,6 +1060,42 @@
</desc>
</func>
+ <func>
+ <name>remaining_test_procs() -&gt; {TestProcs,SharedGL,OtherGLs}</name>
+ <fsummary>>This function will return the identity of test- and group
+ leader processes that are still running at the time of this call.</fsummary>
+ <type>
+ <v>TestProcs = [{pid(),GL}]</v>
+ <v>GL = pid()</v>
+ <v>SharedGL = pid()</v>
+ <v>OtherGLs = [pid()]</v>
+ </type>
+ <desc><marker id="remaining_test_procs-0"/>
+ <p>This function will return the identity of test- and group
+ leader processes that are still running at the time of this call.
+ <c>TestProcs</c> are processes in the system that have a Common Test IO
+ process as group leader. <c>SharedGL</c> is the central Common Test
+ IO process, responsible for printing to log files for configuration
+ functions and sequentially executing test cases. <c>OtherGLs</c> are
+ Common Test IO processes that print to log files for test cases
+ in parallel test case groups.</p>
+ <p>The process information returned by this function may be
+ used to locate and terminate remaining processes after tests have
+ finished executing. The function would typically by called from
+ Common Test Hook functions.</p>
+ <p>Note that processes that execute configuration functions or
+ test cases are never included in <c>TestProcs</c>. It is therefore safe
+ to use post configuration hook functions (such as post_end_per_suite,
+ post_end_per_group, post_end_per_testcase) to terminate all processes
+ in <c>TestProcs</c> that have the current group leader process as its group
+ leader.</p>
+ <p>Note also that the shared group leader (<c>SharedGL</c>) must never be
+ terminated by the user, only by Common Test. Group leader processes
+ for parallel test case groups (<c>OtherGLs</c>) may however be terminated
+ in post_end_per_group hook functions.</p>
+ </desc>
+ </func>
+
<func>
<name>remove_config(Callback, Config) -&gt; ok</name>
<fsummary>Removes configuration variables (together with
diff --git a/lib/common_test/doc/src/fascicules.xml b/lib/common_test/doc/src/fascicules.xml
deleted file mode 100644
index c4a28a699a..0000000000
--- a/lib/common_test/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index b039023e0f..c6b928bb5d 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,33 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.15.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A new function, <c>ct:remaining_test_procs/0</c>, returns
+ the identity of test- and group leader processes that are
+ still running at the time of the call.</p>
+ <p>
+ Own Id: OTP-13832</p>
+ </item>
+ <item>
+ <p>
+ A "latest test result" link is now displayed in the
+ footer of each test index page, which performs a jump to
+ the most recently generated test index. This is useful
+ for making quick comparisons of results between test runs
+ without having to traverse the log file tree.</p>
+ <p>
+ Own Id: OTP-14281</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.15.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/common_test/doc/src/part_notes.xml b/lib/common_test/doc/src/part_notes.xml
deleted file mode 100644
index 360c535e96..0000000000
--- a/lib/common_test/doc/src/part_notes.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Common Test</title>
- <prepared>Peter Andersson</prepared>
- <docno></docno>
- <date>2007-12-01</date>
- <rev></rev>
- <file>part_notes.xml</file>
- </header>
- <description>
- <p>Common Test - tool for automated testing, based on the Erlang/OTP Test Server.</p>
- <p>For information about older versions see
- <url href="part_notes_history_frame.html">release notes
- history</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
-
diff --git a/lib/common_test/doc/src/part_notes_history.xml b/lib/common_test/doc/src/part_notes_history.xml
deleted file mode 100644
index d13bb858db..0000000000
--- a/lib/common_test/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Common Test</title>
- <prepared>Peter Andersson</prepared>
- <docno></docno>
- <date>2007-12-01</date>
- <rev></rev>
- <file>part_notes.xml</file>
- </header>
- <include file="notes_history"></include>
-</part>
-
-
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index a12c0c9101..4c4dc8bede 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -89,6 +89,8 @@
-export([get_target_name/1]).
-export([parse_table/1, listenv/1]).
+-export([remaining_test_procs/0]).
+
%%----------------------------------------------------------------------
%% Exported types
%%----------------------------------------------------------------------
@@ -1474,3 +1476,36 @@ continue() ->
%%% in order to let the test case proceed.</p>
continue(TestCase) ->
test_server:continue(TestCase).
+
+
+%%%-----------------------------------------------------------------
+%%% @spec remaining_test_procs() -> {TestProcs,SharedGL,OtherGLs}
+%%% TestProcs = [{pid(),GL}]
+%%% GL = SharedGL = pid()
+%%% OtherGLs = [pid()]
+%%%
+%%% @doc <p>This function will return the identity of test- and group
+%%% leader processes that are still running at the time of this call.
+%%% TestProcs are processes in the system that have a Common Test IO
+%%% process as group leader. SharedGL is the central Common Test
+%%% IO process, responsible for printing to log files for configuration
+%%% functions and sequentially executing test cases. OtherGLs are
+%%% Common Test IO processes that print to log files for test cases
+%%% in parallel test case groups.</p>
+%%% <p>The process information returned by this function may be
+%%% used to locate and terminate remaining processes after tests have
+%%% finished executing. The function would typically by called from
+%%% Common Test Hook functions.</p>
+%%% <p>Note that processes that execute configuration functions or
+%%% test cases are never included in TestProcs. It is therefore safe
+%%% to use post configuration hook functions (such as post_end_per_suite,
+%%% post_end_per_group, post_end_per_testcase) to terminate all processes
+%%% in TestProcs that have the current group leader process as its group
+%%% leader.</p>
+%%% <p>Note also that the shared group leader (SharedGL) must never be
+%%% terminated by the user, only by Common Test. Group leader processes
+%%% for parallel test case groups (OtherGLs) may however be terminated
+%%% in post_end_per_group hook functions.</p>
+%%%
+remaining_test_procs() ->
+ ct_util:remaining_test_procs().
diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl
index d48ae830bb..9cb9b0ba16 100644
--- a/lib/common_test/src/ct_config.erl
+++ b/lib/common_test/src/ct_config.erl
@@ -81,6 +81,7 @@ start(Mode) ->
do_start(Parent) ->
process_flag(trap_exit,true),
+ ct_util:mark_process(),
register(ct_config_server,self()),
ct_util:create_table(?attr_table,bag,#ct_conf.key),
{ok,StartDir} = file:get_cwd(),
diff --git a/lib/common_test/src/ct_default_gl.erl b/lib/common_test/src/ct_default_gl.erl
index d1b52e5f4f..9ae430c546 100644
--- a/lib/common_test/src/ct_default_gl.erl
+++ b/lib/common_test/src/ct_default_gl.erl
@@ -55,6 +55,7 @@ stop() ->
init([ParentGL]) ->
register(?MODULE, self()),
+ ct_util:mark_process(),
{ok,#{parent_gl_pid => ParentGL,
parent_gl_monitor => erlang:monitor(process,ParentGL)}}.
diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl
index 1a0ee4f3cd..8b5bba7600 100644
--- a/lib/common_test/src/ct_event.erl
+++ b/lib/common_test/src/ct_event.erl
@@ -137,6 +137,7 @@ is_alive() ->
%% this function is called to initialize the event handler.
%%--------------------------------------------------------------------
init(RecvPids) ->
+ ct_util:mark_process(),
%% RecvPids = [{RecvTag,Pid}]
{ok,#state{receivers=RecvPids}}.
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index badb7c52ae..456bfd8bd1 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -186,9 +186,11 @@ end_log() ->
do_within_time(Fun,Timeout) ->
Self = self(),
Silent = get(silent),
- TmpPid = spawn_link(fun() -> put(silent,Silent),
- R = Fun(),
- Self ! {self(),R}
+ TmpPid = spawn_link(fun() ->
+ ct_util:mark_process(),
+ put(silent,Silent),
+ R = Fun(),
+ Self ! {self(),R}
end),
ConnPid = get(conn_pid),
receive
@@ -301,6 +303,7 @@ return({To,Ref},Result) ->
init_gen(Parent,Opts) ->
process_flag(trap_exit,true),
+ ct_util:mark_process(),
put(silent,false),
try (Opts#gen_opts.callback):init(Opts#gen_opts.name,
Opts#gen_opts.address,
diff --git a/lib/common_test/src/ct_hooks_lock.erl b/lib/common_test/src/ct_hooks_lock.erl
index fea298e535..a82be288e1 100644
--- a/lib/common_test/src/ct_hooks_lock.erl
+++ b/lib/common_test/src/ct_hooks_lock.erl
@@ -78,6 +78,7 @@ release() ->
%% @doc Initiates the server
init(Id) ->
+ ct_util:mark_process(),
{ok, #state{ id = Id }}.
%% @doc Handling call messages
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index ba7660fe6a..fb6a095b57 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -666,6 +666,7 @@ log_timestamp({MS,S,US}) ->
logger(Parent, Mode, Verbosity) ->
register(?MODULE,self()),
+ ct_util:mark_process(),
%%! Below is a temporary workaround for the limitation of
%%! max one test run per second.
%%! --->
@@ -1004,6 +1005,7 @@ print_to_log(async, FromPid, Category, TCGL, Content, EscChars, State) ->
if FromPid /= TCGL ->
IoFun = create_io_fun(FromPid, CtLogFd, EscChars),
fun() ->
+ ct_util:mark_process(),
test_server:permit_io(TCGL, self()),
%% Since asynchronous io gets can get buffered if
@@ -1035,6 +1037,7 @@ print_to_log(async, FromPid, Category, TCGL, Content, EscChars, State) ->
end;
true ->
fun() ->
+ ct_util:mark_process(),
unexpected_io(FromPid, Category, ?MAX_IMPORTANCE,
Content, CtLogFd, EscChars)
end
@@ -3017,6 +3020,7 @@ simulate() ->
S = self(),
Pid = spawn(fun() ->
register(?MODULE,self()),
+ ct_util:mark_process(),
S ! {self(),started},
simulate_logger_loop()
end),
@@ -3144,8 +3148,8 @@ locate_priv_file(FileName) ->
filename:join(get(ct_run_dir), FileName);
_ ->
%% executed on other process than ct_logs
- {ok,RunDir} = get_log_dir(true),
- filename:join(RunDir, FileName)
+ {ok,LogDir} = get_log_dir(true),
+ filename:join(LogDir, FileName)
end,
case filelib:is_file(PrivResultFile) of
true ->
@@ -3227,6 +3231,10 @@ get_ts_html_wrapper(TestName, Logdir, PrintLabel, Cwd, TableCols, Encoding) ->
?all_runs_name), Cwd),
TestIndex = make_relative(filename:join(filename:dirname(CtLogdir),
?index_name), Cwd),
+ LatestTest = make_relative(filename:join(filename:dirname(CtLogdir),
+ ?suitelog_name++".latest.html"),
+ Cwd),
+
case Basic of
true ->
TileFile = filename:join(filename:join(CTPath,"priv"),"tile1.jpg"),
@@ -3253,7 +3261,9 @@ get_ts_html_wrapper(TestName, Logdir, PrintLabel, Cwd, TableCols, Encoding) ->
"<a href=\"", uri(AllRuns),
"\">Test run history\n</a> | ",
"<a href=\"", uri(TestIndex),
- "\">Top level test index\n</a>\n</p>\n",
+ "\">Top level test index\n</a> | ",
+ "<a href=\"", uri(LatestTest),
+ "\">Latest test result</a>\n</p>\n",
Copyright,"</center>\n</body>\n</html>\n"]};
_ ->
Copyright =
@@ -3300,7 +3310,9 @@ get_ts_html_wrapper(TestName, Logdir, PrintLabel, Cwd, TableCols, Encoding) ->
"<a href=\"", uri(AllRuns),
"\">Test run history\n</a> | ",
"<a href=\"", uri(TestIndex),
- "\">Top level test index\n</a>\n</p>\n",
+ "\">Top level test index\n</a> | ",
+ "<a href=\"", uri(LatestTest),
+ "\">Latest test result</a>\n</p>\n",
Copyright,"</center>\n</body>\n</html>\n"]}
end.
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 6e6d1879c2..ef2aff69b7 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -346,6 +346,7 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,
case whereis(ct_master) of
undefined ->
register(ct_master,self()),
+ ct_util:mark_process(),
ok;
_Pid ->
io:format("~nWarning: ct_master already running!~n"),
@@ -690,6 +691,7 @@ refresh_logs([],Refreshed) ->
init_node_ctrl(MasterPid,Cookie,Opts) ->
%% make sure tests proceed even if connection to master is lost
process_flag(trap_exit, true),
+ ct_util:mark_process(),
MasterNode = node(MasterPid),
group_leader(whereis(user),self()),
io:format("~n********** node_ctrl process ~w started on ~w **********~n",
diff --git a/lib/common_test/src/ct_master_event.erl b/lib/common_test/src/ct_master_event.erl
index d535d1274e..bd4d1efc92 100644
--- a/lib/common_test/src/ct_master_event.erl
+++ b/lib/common_test/src/ct_master_event.erl
@@ -116,6 +116,7 @@ sync_notify(Event) ->
%% this function is called to initialize the event handler.
%%--------------------------------------------------------------------
init(_) ->
+ ct_util:mark_process(),
ct_master_logs:log("CT Master Event Handler started","",[]),
{ok,#state{}}.
diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl
index d8ecd641ed..c4bb2cc69f 100644
--- a/lib/common_test/src/ct_master_logs.erl
+++ b/lib/common_test/src/ct_master_logs.erl
@@ -88,6 +88,7 @@ stop() ->
init(Parent,LogDir,Nodes) ->
register(?MODULE,self()),
+ ct_util:mark_process(),
Time = calendar:local_time(),
RunDir = make_dirname(Time),
RunDirAbs = filename:join(LogDir,RunDir),
diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl
index c043c9846c..177ef37d1f 100644
--- a/lib/common_test/src/ct_repeat.erl
+++ b/lib/common_test/src/ct_repeat.erl
@@ -70,6 +70,7 @@ loop_test(If,Args) when is_list(Args) ->
CtrlPid = self(),
spawn(
fun() ->
+ ct_util:mark_process(),
stop_after(CtrlPid,Secs,ForceStop)
end)
end,
@@ -134,6 +135,7 @@ spawn_tester(script,Ctrl,Args) ->
spawn_tester(func,Ctrl,Opts) ->
Tester = fun() ->
+ ct_util:mark_process(),
case catch ct_run:run_test2(Opts) of
{'EXIT',Reason} ->
exit(Reason);
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 14f28f9ca3..05b1e70098 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -250,6 +250,8 @@ finish(Tracing, ExitStatus, Args) ->
end.
script_start1(Parent, Args) ->
+ %% tag this process
+ ct_util:mark_process(),
%% read general start flags
Label = get_start_opt(label, fun([Lbl]) -> Lbl end, Args),
Profile = get_start_opt(profile, fun([Prof]) -> Prof end, Args),
@@ -956,7 +958,10 @@ run_test(StartOpts) when is_list(StartOpts) ->
-spec run_test1_fun(_) -> fun(() -> no_return()).
run_test1_fun(StartOpts) ->
- fun() -> run_test1(StartOpts) end.
+ fun() ->
+ ct_util:mark_process(),
+ run_test1(StartOpts)
+ end.
run_test1(StartOpts) when is_list(StartOpts) ->
case proplists:get_value(refresh_logs, StartOpts) of
@@ -1447,7 +1452,10 @@ run_testspec(TestSpec) ->
-spec run_testspec1_fun(_) -> fun(() -> no_return()).
run_testspec1_fun(TestSpec) ->
- fun() -> run_testspec1(TestSpec) end.
+ fun() ->
+ ct_util:mark_process(),
+ run_testspec1(TestSpec)
+ end.
run_testspec1(TestSpec) ->
{ok,Cwd} = file:get_cwd(),
@@ -1906,10 +1914,12 @@ possibly_spawn(true, Tests, Skip, Opts) ->
CTUtilSrv = whereis(ct_util_server),
Supervisor =
fun() ->
+ ct_util:mark_process(),
process_flag(trap_exit, true),
link(CTUtilSrv),
TestRun =
fun() ->
+ ct_util:mark_process(),
TestResult = (catch do_run_test(Tests, Skip, Opts)),
case TestResult of
{EType,_} = Error when EType == user_error;
diff --git a/lib/common_test/src/ct_slave.erl b/lib/common_test/src/ct_slave.erl
index 4188bd7c3b..b39195483b 100644
--- a/lib/common_test/src/ct_slave.erl
+++ b/lib/common_test/src/ct_slave.erl
@@ -282,6 +282,7 @@ monitor_master(MasterNode) ->
% code of the masterdeath-waiter process
monitor_master_int(MasterNode) ->
+ ct_util:mark_process(),
erlang:monitor_node(MasterNode, true),
receive
{nodedown, MasterNode}->
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index c8d217cd2a..76e4b9ea70 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.erl
@@ -118,6 +118,7 @@ get_data(Pid) ->
%%%-----------------------------------------------------------------
%%% Internal functions
init(Parent, Server, Port, Timeout, KeepAlive, NoDelay, ConnName) ->
+ ct_util:mark_process(),
case gen_tcp:connect(Server, Port, [list,{packet,0},{nodelay,NoDelay}], Timeout) of
{ok,Sock} ->
dbg("~tp connected to: ~tp (port: ~w, keep_alive: ~w)\n",
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index bb445bb0d2..bd3755722f 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1425,7 +1425,12 @@ skip_groups1(Suite,Groups,Cmt,Suites0) ->
GrAndCases1 = GrAndCases0 ++ SkipGroups,
insert_in_order({Suite,GrAndCases1},Suites0,replace);
false ->
- insert_in_order({Suite,SkipGroups},Suites0,replace)
+ case Suites0 of
+ [{all,_}=All|Skips]->
+ [All|Skips++[{Suite,SkipGroups}]];
+ _ ->
+ insert_in_order({Suite,SkipGroups},Suites0,replace)
+ end
end.
skip_cases(Node,Dir,Suite,Cases,Cmt,Tests,false) when is_list(Cases) ->
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index abf131f4df..468edc4bee 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -65,6 +65,9 @@
-export([warn_duplicates/1]).
+-export([mark_process/0, mark_process/1, is_marked/1, is_marked/2,
+ remaining_test_procs/0]).
+
-export([get_profile_data/0, get_profile_data/1,
get_profile_data/2, open_url/3]).
@@ -126,6 +129,7 @@ start(Mode, LogDir, Verbosity) ->
do_start(Parent, Mode, LogDir, Verbosity) ->
process_flag(trap_exit,true),
register(ct_util_server,self()),
+ mark_process(),
create_table(?conn_table,#conn.handle),
create_table(?board_table,2),
create_table(?suite_table,#suite_data.key),
@@ -934,6 +938,70 @@ warn_duplicates(Suites) ->
%%% @spec
%%%
%%% @doc
+mark_process() ->
+ mark_process(system).
+
+mark_process(Type) ->
+ put(ct_process_type, Type).
+
+is_marked(Pid) ->
+ is_marked(Pid, system).
+
+is_marked(Pid, Type) ->
+ case process_info(Pid, dictionary) of
+ {dictionary,List} ->
+ Type == proplists:get_value(ct_process_type, List);
+ undefined ->
+ false
+ end.
+
+remaining_test_procs() ->
+ Procs = processes(),
+ {SharedGL,OtherGLs,Procs2} =
+ lists:foldl(
+ fun(Pid, ProcTypes = {Shared,Other,Procs1}) ->
+ case is_marked(Pid, group_leader) of
+ true ->
+ if not is_pid(Shared) ->
+ case test_server_io:get_gl(true) of
+ Pid ->
+ {Pid,Other,
+ lists:delete(Pid,Procs1)};
+ _ ->
+ {Shared,[Pid|Other],Procs1}
+ end;
+ true -> % SharedGL already found
+ {Shared,[Pid|Other],Procs1}
+ end;
+ false ->
+ case is_marked(Pid) of
+ true ->
+ {Shared,Other,lists:delete(Pid,Procs1)};
+ false ->
+ ProcTypes
+ end
+ end
+ end, {undefined,[],Procs}, Procs),
+
+ AllGLs = [SharedGL | OtherGLs],
+ TestProcs =
+ lists:flatmap(fun(Pid) ->
+ case process_info(Pid, group_leader) of
+ {group_leader,GL} ->
+ case lists:member(GL, AllGLs) of
+ true -> [{Pid,GL}];
+ false -> []
+ end;
+ undefined ->
+ []
+ end
+ end, Procs2),
+ {TestProcs, SharedGL, OtherGLs}.
+
+%%%-----------------------------------------------------------------
+%%% @spec
+%%%
+%%% @doc
get_profile_data() ->
get_profile_data(all).
diff --git a/lib/common_test/src/ct_webtool.erl b/lib/common_test/src/ct_webtool.erl
index 9016aca899..82aa78fc4b 100644
--- a/lib/common_test/src/ct_webtool.erl
+++ b/lib/common_test/src/ct_webtool.erl
@@ -343,6 +343,7 @@ code_change(_,State,_)->
% Start the gen_server
%----------------------------------------------------------------------
init({Path,Config})->
+ ct_util:mark_process(),
case filelib:is_dir(Path) of
true ->
{ok, Table} = get_tool_files_data(),
diff --git a/lib/common_test/src/ct_webtool_sup.erl b/lib/common_test/src/ct_webtool_sup.erl
index c02ec69d04..6c6dbde0a6 100644
--- a/lib/common_test/src/ct_webtool_sup.erl
+++ b/lib/common_test/src/ct_webtool_sup.erl
@@ -46,6 +46,7 @@ stop(Pid)->
%% {error, Reason}
%%----------------------------------------------------------------------
init(_StartArgs) ->
+ ct_util:mark_process(),
%%Child1 =
%%Child2 ={webcover_backend,{webcover_backend,start_link,[]},permanent,2000,worker,[webcover_backend]},
%%{ok,{{simple_one_for_one,5,10},[Child1]}}.
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index 8b29d0f96d..77f90c0df6 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -56,6 +56,7 @@ id(_Opts) ->
?MODULE.
init(?MODULE, _Opts) ->
+ ct_util:mark_process(),
error_logger:add_report_handler(?MODULE),
tc_log_async.
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index dc6b7a536c..e56106408f 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -415,6 +415,7 @@ run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
St = #st{ref=Ref,pid=Pid,mf={Mod,Func},last_known_loc=unknown,
status=starting,ret_val=[],comment="",timeout=infinity,
config=hd(Args)},
+ ct_util:mark_process(),
run_test_case_msgloop(St).
%% Ugly bug (pre R5A):
@@ -784,6 +785,7 @@ spawn_fw_call(Mod,IPTC={init_per_testcase,Func},CurrConf,Pid,
Why,Loc,SendTo) ->
FwCall =
fun() ->
+ ct_util:mark_process(),
Skip = {skip,{failed,{Mod,init_per_testcase,Why}}},
%% if init_per_testcase fails, the test case
%% should be skipped
@@ -814,6 +816,7 @@ spawn_fw_call(Mod,EPTC={end_per_testcase,Func},EndConf,Pid,
Why,_Loc,SendTo) ->
FwCall =
fun() ->
+ ct_util:mark_process(),
{RetVal,Report} =
case proplists:get_value(tc_status, EndConf) of
undefined ->
@@ -863,6 +866,7 @@ spawn_fw_call(Mod,EPTC={end_per_testcase,Func},EndConf,Pid,
spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
FwCall =
fun() ->
+ ct_util:mark_process(),
test_server_sup:framework_call(report, [framework_error,
{{FwMod,FwFunc},
FwError}]),
@@ -879,6 +883,7 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
spawn_link(FwCall);
spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
+ ct_util:mark_process(),
{Func1,EndTCFunc} = case Func of
CF when CF == init_per_suite; CF == end_per_suite;
CF == init_per_group; CF == end_per_group ->
@@ -917,6 +922,7 @@ start_job_proxy() ->
%% The io_reply_proxy is not the most satisfying solution but it works...
io_reply_proxy(ReplyTo) ->
+ ct_util:mark_process(),
receive
IoReply when is_tuple(IoReply),
element(1, IoReply) == io_reply ->
@@ -926,6 +932,7 @@ io_reply_proxy(ReplyTo) ->
end.
job_proxy_msgloop() ->
+ ct_util:mark_process(),
receive
%%
@@ -1803,6 +1810,7 @@ break(CBM, TestCase, Comment) ->
spawn_break_process(Pid, PName) ->
spawn(fun() ->
register(PName, self()),
+ ct_util:mark_process(),
receive
continue -> continue(Pid);
cancel -> ok
@@ -2000,6 +2008,7 @@ time_ms_apply(Func, TCPid, MultAndScale) ->
user_timetrap_supervisor(Func, Spawner, TCPid, GL, T0, MultAndScale) ->
process_flag(trap_exit, true),
+ ct_util:mark_process(),
Spawner ! {self(),infinity},
MonRef = monitor(process, TCPid),
UserTTSup = self(),
@@ -2570,6 +2579,7 @@ run_on_shielded_node(Fun, CArgs) when is_function(Fun), is_list(CArgs) ->
-spec start_job_proxy_fun(_, _) -> fun(() -> no_return()).
start_job_proxy_fun(Master, Fun) ->
fun () ->
+ ct_util:mark_process(),
_ = start_job_proxy(),
receive
Ref ->
diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl
index 71978c7267..8ef28b3343 100644
--- a/lib/common_test/src/test_server_ctrl.erl
+++ b/lib/common_test/src/test_server_ctrl.erl
@@ -89,6 +89,7 @@
-define(logdir_ext, ".logs").
-define(data_dir_suffix, "_data/").
-define(suitelog_name, "suite.log").
+-define(suitelog_latest_name, "suite.log.latest").
-define(coverlog_name, "cover.html").
-define(raw_coverlog_name, "cover.log").
-define(cross_coverlog_name, "cross_cover.html").
@@ -1126,6 +1127,7 @@ init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels,
RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) ->
process_flag(trap_exit, true),
_ = test_server_io:start_link(),
+ put(app, common_test),
put(test_server_name, Name),
put(test_server_dir, Dir),
put(test_server_total_time, 0),
@@ -1150,6 +1152,12 @@ init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels,
end,
%% before first print, read and set logging options
+ FWLogDir =
+ case test_server_sup:framework_call(get_log_dir, [], []) of
+ {ok,FwDir} -> FwDir;
+ _ -> filename:dirname(Dir)
+ end,
+ put(test_server_framework_logdir, FWLogDir),
LogOpts = test_server_sup:framework_call(get_logopts, [], []),
put(test_server_logopts, LogOpts),
@@ -1711,6 +1719,12 @@ start_log_file() ->
test_server_io:set_fd(html, Html),
test_server_io:set_fd(unexpected_io, Unexpected),
+ %% we must assume the redirection file (to the latest suite index) can
+ %% be stored on the level above the log directory of the current test
+ TopDir = filename:dirname(get(test_server_framework_logdir)),
+ RedirectLink = filename:join(TopDir, ?suitelog_latest_name ++ ?html_ext),
+ make_html_link(RedirectLink, HtmlName, redirect),
+
make_html_link(filename:absname(?last_test ++ ?html_ext),
HtmlName, filename:basename(Dir)),
LinkName = filename:join(Dir, ?last_link),
@@ -1739,11 +1753,18 @@ make_html_link(LinkName, Target, Explanation) ->
false ->
"file:" ++ uri_encode(Target)
end,
- H = [html_header(Explanation),
- "<h1>Last test</h1>\n"
- "<a href=\"",Href,"\">",Explanation,"</a>\n"
- "</body>\n</html>\n"],
+ H = if Explanation == redirect ->
+ Meta = ["<meta http-equiv=\"refresh\" "
+ "content=\"0; url=", Href, "\" />\n"],
+ [html_header("redirect", Meta), "</html>\n"];
+ true ->
+ [html_header(Explanation),
+ "<h1>Last test</h1>\n"
+ "<a href=\"",Href,"\">",Explanation,"</a>\n"
+ "</body>\n</html>\n"]
+ end,
ok = write_html_file(LinkName, H).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% start_minor_log_file(Mod, Func, ParallelTC) -> AbsName
@@ -3704,6 +3725,7 @@ run_test_case(Ref, Num, Mod, Func, Args, RunInit, TimetrapData, Mode) ->
spawn_link(
fun() ->
process_flag(trap_exit, true),
+ ct_util:mark_process(),
_ = [put(Key, Val) || {Key,Val} <- Dictionary],
set_io_buffering({tc,Main}),
run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
@@ -5655,6 +5677,13 @@ html_header(Title) ->
"<body bgcolor=\"white\" text=\"black\" "
"link=\"blue\" vlink=\"purple\" alink=\"red\">\n"].
+html_header(Title, Meta) ->
+ ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
+ "<!-- autogenerated by '", atom_to_list(?MODULE), "'. -->\n"
+ "<html>\n"
+ "<head>\n"
+ "<title>", Title, "</title>\n"] ++ Meta ++ ["</head>\n"].
+
open_html_file(File) ->
open_utf8_file(File).
diff --git a/lib/common_test/src/test_server_gl.erl b/lib/common_test/src/test_server_gl.erl
index ce7682d101..24dd5cd54c 100644
--- a/lib/common_test/src/test_server_gl.erl
+++ b/lib/common_test/src/test_server_gl.erl
@@ -132,6 +132,7 @@ set_props(GL, PropList) ->
%%% Internal functions.
init([TSIO]) ->
+ ct_util:mark_process(group_leader),
EscChars = case application:get_env(test_server, esc_chars) of
{ok,ECBool} -> ECBool;
_ -> true
diff --git a/lib/common_test/src/test_server_io.erl b/lib/common_test/src/test_server_io.erl
index 062e3bd8ff..ef31521950 100644
--- a/lib/common_test/src/test_server_io.erl
+++ b/lib/common_test/src/test_server_io.erl
@@ -184,6 +184,7 @@ reset_state() ->
init([]) ->
process_flag(trap_exit, true),
+ ct_util:mark_process(),
Empty = gb_trees:empty(),
{ok,Shared} = test_server_gl:start_link(self()),
{ok,#st{fds=Empty,shared_gl=Shared,gls=gb_sets:empty(),
@@ -262,7 +263,7 @@ handle_call(reset_state, From, #st{phase=stopping,pending_ops=Ops}=St) ->
{Result,NewSt1}
end,
{noreply,St#st{pending_ops=[{From,Op}|Ops]}};
-handle_call(reset_state, _From, #st{fds=Fds,tags=Tags,gls=Gls,
+handle_call(reset_state, _From, #st{fds=Fds,tags=Tags,shared_gl=Shared0,gls=Gls,
offline_buffer=OfflineBuff}) ->
%% close open log files
lists:foreach(fun(Tag) ->
@@ -273,6 +274,7 @@ handle_call(reset_state, _From, #st{fds=Fds,tags=Tags,gls=Gls,
file:close(Fd)
end
end, Tags),
+ test_server_gl:stop(Shared0),
GlList = gb_sets:to_list(Gls),
_ = [test_server_gl:stop(GL) || GL <- GlList],
timer:sleep(100),
@@ -320,7 +322,7 @@ handle_call(finish, From, St) ->
handle_info({'EXIT',Pid,normal}, #st{gls=Gls0,stopping=From}=St) ->
Gls = gb_sets:delete_any(Pid, Gls0),
- case gb_sets:is_empty(Gls) andalso stopping =/= undefined of
+ case gb_sets:is_empty(Gls) andalso From =/= undefined of
true ->
%% No more group leaders left.
gen_server:reply(From, ok),
@@ -329,6 +331,9 @@ handle_info({'EXIT',Pid,normal}, #st{gls=Gls0,stopping=From}=St) ->
%% Wait for more group leaders to finish.
{noreply,St#st{gls=Gls,phase=stopping}}
end;
+handle_info({'EXIT',Pid,killed}, #st{gls=Gls0}=St) ->
+ %% forced termination of group leader
+ {noreply,St#st{gls=gb_sets:delete_any(Pid, Gls0)}};
handle_info({'EXIT',_Pid,Reason}, _St) ->
exit(Reason);
handle_info(stop_group_leaders, #st{gls=Gls}=St) ->
diff --git a/lib/common_test/src/test_server_node.erl b/lib/common_test/src/test_server_node.erl
index c0d7e12721..b3b6ae3d92 100644
--- a/lib/common_test/src/test_server_node.erl
+++ b/lib/common_test/src/test_server_node.erl
@@ -747,6 +747,7 @@ unpack(Bin) ->
id(I) -> I.
print_data(Port) ->
+ ct_util:mark_process(),
receive
{Port, {data, Bytes}} ->
io:put_chars(Bytes),
diff --git a/lib/common_test/src/test_server_sup.erl b/lib/common_test/src/test_server_sup.erl
index 21f4be22fe..6ddbf1ad27 100644
--- a/lib/common_test/src/test_server_sup.erl
+++ b/lib/common_test/src/test_server_sup.erl
@@ -56,6 +56,7 @@ timetrap(Timeout0, Scale, Pid) ->
timetrap(Timeout0, ReportTVal, Scale, Pid) ->
process_flag(priority, max),
+ ct_util:mark_process(),
Timeout = if not Scale -> Timeout0;
true -> test_server:timetrap_scale_factor() * Timeout0
end,
@@ -773,6 +774,7 @@ framework_call(Callback,Func,Args,DefaultReturn) ->
false ->
ok
end,
+ ct_util:mark_process(),
try apply(Mod,Func,Args) of
Result ->
Result
@@ -850,6 +852,7 @@ util_start() ->
undefined ->
spawn_link(fun() ->
register(?MODULE, self()),
+ put(app, common_test),
util_loop(#util_state{starter=Starter})
end),
ok;
diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl
index 99a109cfe8..83fcde2f48 100644
--- a/lib/common_test/src/vts.erl
+++ b/lib/common_test/src/vts.erl
@@ -157,6 +157,7 @@ test_info(_VtsPid,Type,Data) ->
init(Parent) ->
register(?MODULE,self()),
process_flag(trap_exit,true),
+ ct_util:mark_process(),
Parent ! {self(),started},
{ok,Cwd} = file:get_cwd(),
InitState = #state{start_dir=Cwd},
@@ -284,6 +285,7 @@ run_test1(State=#state{tests=Tests,current_log_dir=LogDir,
logopts=LogOpts}) ->
Self=self(),
RunTest = fun() ->
+ ct_util:mark_process(),
case ct_run:do_run(Tests,[],LogDir,LogOpts) of
{error,_Reason} ->
aborted();
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 0d9149f489..ecd1f727a2 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -73,7 +73,8 @@ MODULES= \
ct_log_SUITE \
ct_SUITE \
ct_keep_logs_SUITE \
- ct_unicode_SUITE
+ ct_unicode_SUITE \
+ ct_auto_clean_SUITE
ERL_FILES= $(MODULES:%=%.erl)
HRL_FILES= test_server_test_lib.hrl
diff --git a/lib/common_test/test/ct_auto_clean_SUITE.erl b/lib/common_test/test/ct_auto_clean_SUITE.erl
new file mode 100644
index 0000000000..fd81430d0d
--- /dev/null
+++ b/lib/common_test/test/ct_auto_clean_SUITE.erl
@@ -0,0 +1,262 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_auto_clean_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) -> Config1 | {skip,Reason}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the suite.
+%%
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ DataDir = ?config(data_dir, Config),
+ CTHs = filelib:wildcard(filename:join(DataDir,"cth_*.erl")),
+ ct:pal("CTHs: ~p",[CTHs]),
+ [ct:pal("Compiling ~p: ~p",
+ [FileName,compile:file(FileName,[{outdir,DataDir},debug_info])]) ||
+ FileName <- CTHs],
+ ct_test_support:init_per_suite([{path_dirs,[DataDir]} | Config]).
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config) -> void()
+%%
+%% Config = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after the suite.
+%%--------------------------------------------------------------------
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) -> Config1 |
+%% {skip,Reason}
+%% TestCase = atom()
+%% Name of the test case that is about to run.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the test case.
+%%
+%% Description: Initialization before each test case.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config) -> void()
+%%
+%% TestCase = atom()
+%% Name of the test case that is finished.
+%% Config = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after each test case.
+%%--------------------------------------------------------------------
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+%%--------------------------------------------------------------------
+%% Function: all(Clause) -> Descr | TestCases | {skip,Reason}
+%%
+%% Clause = doc | suite
+%% Indicates expected return value.
+%% Descr = [string()] | []
+%% String that describes the test suite.
+%% TestCases = [TestCase]
+%% TestCase = atom()
+%% Name of a test case.
+%% Reason = term()
+%% The reason for skipping the test suite.
+%%
+%% Description: Returns a description of the test suite (doc) and a
+%% list of all test cases in the suite (suite).
+%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [clean].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Function: TestCase(Arg) -> Descr | Spec | ok | exit() | {skip,Reason}
+%%
+%% Arg = doc | suite | Config
+%% Indicates expected behaviour and return value.
+%% Config = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Descr = [string()] | []
+%% String that describes the test case.
+%% Spec = [tuple()] | []
+%% A test specification.
+%% Reason = term()
+%% The reason for skipping the test case.
+%%
+%% Description: Test case function. Returns a description of the test
+%% case (doc), then returns a test specification (suite),
+%% or performs the actual test (Config).
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+
+clean(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ ACSuite = filename:join(DataDir, "ac_SUITE"),
+ Opts0 = ct_test_support:get_opts(Config),
+ Opts = eh_opts(Config) ++ Opts0 ++ [{suite,ACSuite},
+ {ct_hooks,[cth_auto_clean]}],
+
+ ERPid = ct_test_support:start_event_receiver(Config),
+
+ ok = ct_test_support:run(Opts, Config),
+
+ Events = ct_test_support:get_events(ERPid, Config),
+ ct_test_support:log_events(?FUNCTION_NAME,
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+ TestEvents = events_to_check(?FUNCTION_NAME),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+eh_opts(Config) ->
+ Level = ?config(trace_level, Config),
+ [{event_handler,{?eh,[{cbm,ct_test_support},{trace_level,Level}]}}].
+
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ events(Test) ++ events_to_check(Test, N-1).
+
+events(clean) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,9}},
+
+ {?eh,tc_start,{ac_SUITE,init_per_suite}},
+ {?eh,tc_done,{ac_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ac_SUITE,tc1}},
+ {?eh,tc_done,{ac_SUITE,tc1,ok}},
+
+ {?eh,test_stats,{1,0,{0,0}}},
+
+ {?eh,tc_start,{ac_SUITE,tc2}},
+ {?eh,tc_done,{ac_SUITE,tc2,ok}},
+
+ {?eh,test_stats,{2,0,{0,0}}},
+
+ [{?eh,tc_start,{ac_SUITE,{init_per_group,s1,[]}}},
+ {?eh,tc_done,{ac_SUITE,{init_per_group,s1,[]},ok}},
+
+ {?eh,tc_start,{ac_SUITE,stc1}},
+ {?eh,tc_done,{ac_SUITE,stc1,ok}},
+
+ {?eh,test_stats,{3,0,{0,0}}},
+
+ {?eh,tc_start,{ac_SUITE,stc2}},
+ {?eh,tc_done,{ac_SUITE,stc2,ok}},
+
+ {?eh,test_stats,{4,0,{0,0}}},
+
+ {?eh,tc_start,{ac_SUITE,{end_per_group,s1,[]}}},
+ {?eh,tc_done,{ac_SUITE,{end_per_group,s1,[]},ok}}],
+
+ {parallel,
+ [{?eh,tc_start,{ac_SUITE,{init_per_group,p1,[parallel]}}},
+ {?eh,tc_done,{ac_SUITE,{init_per_group,p1,[parallel]},ok}},
+
+ {?eh,tc_start,{ac_SUITE,ptc1}},
+ {?eh,tc_start,{ac_SUITE,ptc2}},
+ {?eh,tc_done,{ac_SUITE,ptc1,ok}},
+ {?eh,test_stats,{5,0,{0,0}}},
+ {?eh,tc_done,{ac_SUITE,ptc2,ok}},
+ {?eh,test_stats,{6,0,{0,0}}},
+
+ {?eh,tc_start,{ac_SUITE,{end_per_group,p1,[parallel]}}},
+ {?eh,tc_done,{ac_SUITE,{end_per_group,p1,[parallel]},ok}}]},
+
+ [{?eh,tc_start,{ac_SUITE,{init_per_group,s2,[]}}},
+ {?eh,tc_done,{ac_SUITE,{init_per_group,s2,[]},ok}},
+
+ {?eh,tc_start,{ac_SUITE,stc1}},
+ {?eh,tc_done,{ac_SUITE,stc1,ok}},
+
+ {?eh,test_stats,{7,0,{0,0}}},
+
+ {?eh,tc_start,{ac_SUITE,stc2}},
+ {?eh,tc_done,{ac_SUITE,stc2,ok}},
+
+ {?eh,test_stats,{8,0,{0,0}}},
+
+ {?eh,tc_start,{ac_SUITE,{end_per_group,s2,[]}}},
+ {?eh,tc_done,{ac_SUITE,{end_per_group,s2,[]},ok}}],
+
+ {?eh,tc_start,{ac_SUITE,tc1}},
+ {?eh,tc_done,{ac_SUITE,tc1,ok}},
+
+ {?eh,test_stats,{9,0,{0,0}}},
+
+ {?eh,tc_start,{ac_SUITE,end_per_suite}},
+ {?eh,tc_done,{ac_SUITE,end_per_suite,ok}},
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_auto_clean_SUITE_data/ac_SUITE.erl b/lib/common_test/test/ct_auto_clean_SUITE_data/ac_SUITE.erl
new file mode 100644
index 0000000000..dae7c1e22c
--- /dev/null
+++ b/lib/common_test/test/ct_auto_clean_SUITE_data/ac_SUITE.erl
@@ -0,0 +1,181 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ac_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,30}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ start_processes(),
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> term() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ start_processes(),
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ start_processes(),
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% term() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ start_processes(),
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ start_processes(),
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% term() | {save_config,Config1} | {fail,Reason}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ start_processes(),
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%% @end
+%%--------------------------------------------------------------------
+groups() ->
+ [{s1,[],[stc1,stc2]},
+ {p1,[parallel],[ptc1,ptc2]},
+ {s2,[],[stc1,stc2]}].
+
+%%! What about nested groups??
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [
+ [tc1,tc2],
+ {group,s1},
+ {group,p1},
+ {group,s2},
+ tc1
+ ].
+
+tc1(_Config) ->
+ start_processes(),
+ ok.
+
+tc2(_Config) ->
+ start_processes(),
+ ok.
+
+stc1(_Config) ->
+ start_processes(),
+ ok.
+
+stc2(_Config) ->
+ start_processes(),
+ ok.
+
+ptc1(_Config) ->
+ start_processes(),
+ ok.
+
+ptc2(_Config) ->
+ start_processes(),
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%%
+
+start_processes() ->
+ Init = fun() ->
+ process_flag(trap_exit, true),
+ do_spawn(fun() -> receive _ -> ok end end),
+ receive _ ->
+ ok
+ end
+ end,
+ do_spawn(Init).
+
+do_spawn(Fun) ->
+ Pid = spawn(Fun),
+ ct:log("Process ~w started with group leader ~w",
+ [Pid,element(2, process_info(Pid, group_leader))]),
+ Pid.
diff --git a/lib/common_test/test/ct_auto_clean_SUITE_data/cth_auto_clean.erl b/lib/common_test/test/ct_auto_clean_SUITE_data/cth_auto_clean.erl
new file mode 100644
index 0000000000..137c81969d
--- /dev/null
+++ b/lib/common_test/test/ct_auto_clean_SUITE_data/cth_auto_clean.erl
@@ -0,0 +1,214 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(cth_auto_clean).
+
+%% CTH Callbacks
+-export([id/1, init/2,
+ pre_init_per_suite/3, post_init_per_suite/4,
+ pre_end_per_suite/3, post_end_per_suite/4,
+ pre_init_per_group/4, post_init_per_group/5,
+ pre_end_per_group/4, post_end_per_group/5,
+ pre_init_per_testcase/4, post_init_per_testcase/5,
+ pre_end_per_testcase/4, post_end_per_testcase/5]).
+
+id(_Opts) ->
+ ?MODULE.
+
+init(?MODULE, _Opts) ->
+ ok.
+
+pre_init_per_suite(_Suite, Config, State) ->
+ identify(?FUNCTION_NAME),
+ SharedGL = test_server_io:get_gl(true),
+ SharedGL = find_and_kill(),
+ do_until(fun() -> ct:remaining_test_procs() end, {[],SharedGL,[]}),
+ %% get status of processes at startup, to be compared with end result
+ {Config, [{all_procs,processes()} | State]}.
+
+post_init_per_suite(_Suite, _Config, Return, State) ->
+ identify(?FUNCTION_NAME),
+ SharedGL = find_and_kill(),
+ do_until(fun() -> ct:remaining_test_procs() end, {[],SharedGL,[]}),
+ {Return, State}.
+
+pre_end_per_suite(_Suite, Config, State) ->
+ identify(?FUNCTION_NAME),
+ SharedGL = find_and_kill(),
+ do_until(fun() -> ct:remaining_test_procs() end, {[],SharedGL,[]}),
+ {Config, State}.
+
+post_end_per_suite(_Suite, _Config, Return, State) ->
+ identify(?FUNCTION_NAME),
+ SharedGL = find_and_kill(),
+ do_until(fun() -> ct:remaining_test_procs() end, {[],SharedGL,[]}),
+ AllProcs = processes(),
+ Remaining = AllProcs--proplists:get_value(all_procs, State),
+ ct:pal("Final remaining processes = ~p", [Remaining]),
+ %% only the end_per_suite process shoud remain at this point!
+ Remaining = [self()],
+ {Return, State}.
+
+pre_init_per_group(_Suite, _Group, Config, State) ->
+ identify(?FUNCTION_NAME),
+ SharedGL = find_and_kill(procs_and_gls),
+ do_until(fun() -> ct:remaining_test_procs() end, {[],SharedGL,[]}),
+ {Config, State}.
+
+post_init_per_group(_Suite, _Group, _Config, Result, State) ->
+ identify(?FUNCTION_NAME),
+ SharedGL = find_and_kill(procs_and_gls),
+ do_until(fun() -> ct:remaining_test_procs() end, {[],SharedGL,[]}),
+ {Result, State}.
+
+pre_init_per_testcase(_Suite, _TC, Config, State) ->
+ identify(?FUNCTION_NAME),
+ ThisGL = group_leader(),
+ find_and_kill(proc, ThisGL),
+ case proplists:get_value(tc_group_properties, Config) of
+ [{name,_},parallel] ->
+ timer:sleep(1000);
+ _ ->
+ do_until(fun() -> element(1,ct:remaining_test_procs()) end, [])
+ end,
+ {Config, State}.
+
+post_init_per_testcase(_Suite, _TC, Config, Return, State) ->
+ identify(?FUNCTION_NAME),
+ ThisGL = group_leader(),
+ find_and_kill(proc, ThisGL),
+ case proplists:get_value(tc_group_properties, Config) of
+ [{name,_},parallel] ->
+ timer:sleep(1000);
+ _ ->
+ do_until(fun() -> element(1,ct:remaining_test_procs()) end, [])
+ end,
+ {Return, State}.
+
+pre_end_per_testcase(_Suite, _TC, Config, State) ->
+ identify(?FUNCTION_NAME),
+ ThisGL = group_leader(),
+ find_and_kill(proc, ThisGL),
+ case proplists:get_value(tc_group_properties, Config) of
+ [{name,_},parallel] ->
+ timer:sleep(1000);
+ _ ->
+ do_until(fun() -> element(1,ct:remaining_test_procs()) end, [])
+ end,
+ {Config, State}.
+
+post_end_per_testcase(_Suite, _TC, Config, Result, State) ->
+ identify(?FUNCTION_NAME),
+ ThisGL = group_leader(),
+ find_and_kill(proc, ThisGL),
+ case proplists:get_value(tc_group_properties, Config) of
+ [{name,_},parallel] ->
+ timer:sleep(1000);
+ _ ->
+ do_until(fun() -> element(1,ct:remaining_test_procs()) end, [])
+ end,
+ {Result, State}.
+
+pre_end_per_group(_Suite, _Group, Config, State) ->
+ identify(?FUNCTION_NAME),
+ SharedGL = find_and_kill(procs_and_gls),
+ do_until(fun() -> ct:remaining_test_procs() end, {[],SharedGL,[]}),
+ {Config, State}.
+
+post_end_per_group(_Suite, _Group, _Config, Return, State) ->
+ identify(?FUNCTION_NAME),
+ SharedGL = find_and_kill(procs_and_gls),
+ do_until(fun() -> ct:remaining_test_procs() end, {[],SharedGL,[]}),
+ {Return, State}.
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+identify(Func) ->
+ ct:pal("********** THIS IS ~w on ~w", [Func, self()]),
+ ok.
+
+find_and_kill() ->
+ find_and_kill(procs).
+
+find_and_kill(procs) ->
+ {Procs,SharedGL,_ParallelGLs} = ct:remaining_test_procs(),
+ ct:pal("Remaining test processes = ~p", [pi(Procs)]),
+ [pkill(P, kill) || {P,_GL} <- Procs],
+ SharedGL;
+
+find_and_kill(procs_and_gls) ->
+ {Procs,SharedGL,GLs} = ct:remaining_test_procs(),
+ ct:pal("Remaining test processes = ~p", [pi(Procs)]),
+ [pkill(P, kill) || {P,_GL} <- Procs],
+ ct:pal("Remaining group leaders = ~p", [pi(GLs)]),
+ [pkill(GL, kill) || GL <- GLs, GL /= SharedGL],
+ SharedGL.
+
+find_and_kill(proc, ProcGL) ->
+ {Procs,SharedGL,GLs} = ct:remaining_test_procs(),
+ ct:pal("Remaining test processes = ~p", [pi(Procs++GLs)]),
+ [pkill(P, kill) || {P,GL} <- Procs, GL == ProcGL],
+ SharedGL.
+
+pi([{P,_GL}|Ps]) ->
+ pi([P|Ps]);
+pi([P|Ps]) ->
+ case node() == node(P) of
+ true ->
+ {_,GL} = process_info(P,group_leader),
+ {_,CF} = process_info(P,current_function),
+ {_,IC} = process_info(P,initial_call),
+ {_,D} = process_info(P,dictionary),
+ Shared = test_server_io:get_gl(true),
+ User = whereis(user),
+ if (GL /= P) and (GL /= Shared) and (GL /= User) ->
+ [{P,GL,CF,IC,D} | pi([GL|Ps])];
+ true ->
+ [{P,GL,CF,IC,D} | pi(Ps)]
+ end;
+ false ->
+ pi(Ps)
+ end;
+pi([]) ->
+ [].
+
+do_until(Fun, Until) ->
+ io:format("Will do until ~p~n", [Until]),
+ do_until(Fun, Until, 1000).
+
+do_until(_, Until, 0) ->
+ io:format("Couldn't get ~p~n", [Until]),
+ exit({not_reached,Until});
+
+do_until(Fun, Until, N) ->
+ case Fun() of
+ Until ->
+ ok;
+ _Tmp ->
+ do_until(Fun, Until, N-1)
+ end.
+
+pkill(P, How) ->
+ ct:pal("KILLING ~w NOW!", [P]),
+ exit(P, How).
+
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index 7b959ebfe3..96fdc89853 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.15.2
+COMMON_TEST_VSN = 1.15.3
diff --git a/lib/compiler/doc/src/Makefile b/lib/compiler/doc/src/Makefile
index c6864cb835..13210de040 100644
--- a/lib/compiler/doc/src/Makefile
+++ b/lib/compiler/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1997-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
include $(ERL_TOP)/make/target.mk
@@ -38,13 +38,12 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = compile.xml
-XML_PART_FILES = part_notes.xml part_notes_history.xml
-XML_CHAPTER_FILES = notes.xml notes_history.xml
+XML_PART_FILES =
+XML_CHAPTER_FILES = notes.xml
BOOK_FILES = book.xml
-GIF_FILES = \
- warning.gif
+GIF_FILES =
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
@@ -64,9 +63,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -86,17 +85,17 @@ man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -110,4 +109,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
release_spec:
-
diff --git a/lib/compiler/doc/src/fascicules.xml b/lib/compiler/doc/src/fascicules.xml
deleted file mode 100644
index fadd37eefb..0000000000
--- a/lib/compiler/doc/src/fascicules.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/compiler/doc/src/note.gif b/lib/compiler/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/compiler/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index bd095c422a..f4a3f9875b 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 7.1.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The '<c>deterministic</c>' option was not recognized
+ when given in a <c>-compile()</c> attribute in the source
+ code.</p>
+ <p>
+ Own Id: OTP-14773 Aux Id: ERL-498 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 7.1.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -294,6 +310,23 @@
</section>
+<section><title>Compiler 7.0.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fail labels on guard BIFs weren't taken into account
+ during an optimization pass, and a bug in the validation
+ pass sometimes prevented this from being noticed when a
+ fault occurred.</p>
+ <p>
+ Own Id: OTP-14522 Aux Id: ERIERL-48 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 7.0.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -493,6 +526,22 @@
</section>
+
+<section><title>Compiler 6.0.3.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fail labels on guard BIFs weren't taken into account
+ during an optimization pass, and a bug in the validation
+ pass sometimes prevented this from being noticed when a
+ fault occurred.</p>
+ <p>
+ Own Id: OTP-14522 Aux Id: ERIERL-48 </p>
+ </item>
+ </list>
+ </section>
+</section>
+
<section><title>Compiler 6.0.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/doc/src/part_notes.xml b/lib/compiler/doc/src/part_notes.xml
deleted file mode 100644
index c1f0ff3861..0000000000
--- a/lib/compiler/doc/src/part_notes.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Compiler Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date>2004-09-07</date>
- <rev>1.0</rev>
- </header>
- <description>
- <p>The <em>Compiler</em> application compiles Erlang
- code to byte-code. The highly compact byte-code is executed by
- the Erlang emulator.</p>
- <p>For information about older versions, see
- <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/compiler/doc/src/part_notes_history.xml b/lib/compiler/doc/src/part_notes_history.xml
deleted file mode 100644
index 4019676b83..0000000000
--- a/lib/compiler/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2006</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>Compiler Release Notes History</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>Compiler</em> application compiles Erlang
- code to byte-code. The highly compact byte-code is executed by
- the Erlang emulator.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/compiler/doc/src/user_guide.gif b/lib/compiler/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/compiler/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/compiler/doc/src/warning.gif b/lib/compiler/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/compiler/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index c35efdfc9d..f7c838e392 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -182,7 +182,8 @@ build_file(Code, Attr, Dict, NumLabels, NumFuncs, ExtraChunks, SourceFile, Opts,
Essentials1 = [iolist_to_binary(C) || C <- Essentials0],
MD5 = module_md5(Essentials1),
Essentials = finalize_fun_table(Essentials1, MD5),
- {Attributes,Compile} = build_attributes(Opts, SourceFile, Attr, MD5),
+ {Attributes,Compile} = build_attributes(Opts, CompilerOpts, SourceFile,
+ Attr, MD5),
AttrChunk = chunk(<<"Attr">>, Attributes),
CompileChunk = chunk(<<"CInf">>, Compile),
@@ -264,16 +265,16 @@ flatten_exports(Exps) ->
flatten_imports(Imps) ->
list_to_binary(map(fun({M,F,A}) -> <<M:32,F:32,A:32>> end, Imps)).
-build_attributes(Opts, SourceFile, Attr, MD5) ->
+build_attributes(Opts, CompilerOpts, SourceFile, Attr, MD5) ->
Misc0 = case SourceFile of
[] -> [];
[_|_] -> [{source,SourceFile}]
end,
- Misc = case member(slim, Opts) of
+ Misc = case member(slim, CompilerOpts) of
false -> Misc0;
true -> []
end,
- Compile = case member(deterministic, Opts) of
+ Compile = case member(deterministic, CompilerOpts) of
false ->
[{options,Opts},{version,?COMPILER_VSN}|Misc];
true ->
diff --git a/lib/compiler/src/beam_listing.erl b/lib/compiler/src/beam_listing.erl
index 94b47cf568..836378727b 100644
--- a/lib/compiler/src/beam_listing.erl
+++ b/lib/compiler/src/beam_listing.erl
@@ -24,6 +24,7 @@
-include("core_parse.hrl").
-include("v3_kernel.hrl").
-include("v3_life.hrl").
+-include("beam_disasm.hrl").
-import(lists, [foreach/2]).
@@ -59,6 +60,19 @@ module(Stream, {Mod,Exp,Attr,Code,NumLabels}) ->
[Name, Arity, Entry]),
io:put_chars(Stream, format_asm(Asm))
end, Code);
+module(Stream, Code) when is_binary(Code) ->
+ #beam_file{ module = Module, compile_info = CInfo } = beam_disasm:file(Code),
+ Loaded = code:is_loaded(Module),
+ Sticky = code:is_sticky(Module),
+ [code:unstick_mod(Module) || Sticky],
+
+ {module, Module} = code:load_binary(Module, proplists:get_value(source, CInfo), Code),
+ ok = erts_debug:df(Stream, Module),
+
+ %% Restore loaded module
+ _ = [{module, Module} = code:load_file(Module) || Loaded =/= false],
+ [code:stick_mod(Module) || Sticky],
+ ok;
module(Stream, [_|_]=Fs) ->
%% Form-based abstract format.
foreach(fun (F) -> io:format(Stream, "~p.\n", [F]) end, Fs).
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 3d842a6fd3..fc2c7a991b 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -117,14 +117,6 @@ simplify_basic_1([{test,is_tuple,_,[R]}=I|Is], Ts, Acc) ->
{tuple,_,_} -> simplify_basic_1(Is, Ts, Acc);
_ -> simplify_basic_1(Is, Ts, [I|Acc])
end;
-simplify_basic_1([{test,test_arity,_,[R,Arity]}=I|Is], Ts0, Acc) ->
- case tdb_find(R, Ts0) of
- {tuple,Arity,_} ->
- simplify_basic_1(Is, Ts0, Acc);
- _Other ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc])
- end;
simplify_basic_1([{test,is_map,_,[R]}=I|Is], Ts0, Acc) ->
case tdb_find(R, Ts0) of
map -> simplify_basic_1(Is, Ts0, Acc);
@@ -147,14 +139,6 @@ simplify_basic_1([{test,is_eq_exact,Fail,[R,{atom,_}=Atom]}=I|Is0], Ts0, Acc0) -
end,
Ts = update(I, Ts0),
simplify_basic_1(Is0, Ts, Acc);
-simplify_basic_1([{test,is_record,_,[R,{atom,_}=Tag,{integer,Arity}]}=I|Is], Ts0, Acc) ->
- case tdb_find(R, Ts0) of
- {tuple,Arity,[Tag]} ->
- simplify_basic_1(Is, Ts0, Acc);
- _Other ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc])
- end;
simplify_basic_1([{select,select_val,Reg,_,_}=I0|Is], Ts, Acc) ->
I = case tdb_find(Reg, Ts) of
{integer,Range} ->
@@ -367,6 +351,8 @@ flt_need_heap_2({set,_,_,get_list}, H, Fl) ->
{[],H,Fl};
flt_need_heap_2({set,_,_,{try_catch,_,_}}, H, Fl) ->
{[],H,Fl};
+flt_need_heap_2({set,_,_,init}, H, Fl) ->
+ {[],H,Fl};
%% All other instructions should cause the insertion of an allocation
%% instruction if needed.
flt_need_heap_2(_, H, Fl) ->
@@ -928,10 +914,10 @@ merge_type_info({tuple,Sz1,[]}, {tuple,_Sz2,First}=Tuple2) ->
merge_type_info({tuple,Sz1,First}, Tuple2);
merge_type_info({tuple,_Sz1,First}=Tuple1, {tuple,Sz2,_}) ->
merge_type_info(Tuple1, {tuple,Sz2,First});
-merge_type_info(integer, {integer,_}=Int) ->
- Int;
-merge_type_info({integer,_}=Int, integer) ->
- Int;
+merge_type_info(integer, {integer,_}) ->
+ integer;
+merge_type_info({integer,_}, integer) ->
+ integer;
merge_type_info({integer,{Min1,Max1}}, {integer,{Min2,Max2}}) ->
{integer,{max(Min1, Min2),min(Max1, Max2)}};
merge_type_info(NewType, _) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index be8908dd6b..ea38969814 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -529,9 +529,10 @@ valfun_4({bif,Op,{f,Fail},Src,Dst}, Vst0) ->
Type = bif_type(Op, Src, Vst),
set_type_reg(Type, Dst, Vst);
valfun_4({gc_bif,Op,{f,Fail},Live,Src,Dst}, #vst{current=St0}=Vst0) ->
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
St = kill_heap_allocation(St0),
Vst1 = Vst0#vst{current=St},
- verify_live(Live, Vst1),
Vst2 = branch_state(Fail, Vst1),
Vst = prune_x_regs(Live, Vst2),
validate_src(Src, Vst),
@@ -685,6 +686,7 @@ valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst));
valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
if
is_integer(Sz) ->
ok;
@@ -697,6 +699,7 @@ valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
set_type_reg(binary, Dst, Vst);
valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
if
is_integer(Sz) ->
ok;
@@ -709,6 +712,7 @@ valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
set_type_reg(binary, Dst, Vst);
valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
assert_term(Bits, Vst0),
assert_term(Bin, Vst0),
Vst1 = heap_alloc(Heap, Vst0),
@@ -944,6 +948,7 @@ deallocate(#vst{current=St}=Vst) ->
test_heap(Heap, Live, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
Vst = prune_x_regs(Live, Vst0),
heap_alloc(Heap, Vst).
@@ -1324,7 +1329,12 @@ branch_arities([Sz,{f,L}|T], Tuple, #vst{current=St}=Vst0)
Vst = branch_state(L, Vst1),
branch_arities(T, Tuple, Vst#vst{current=St}).
-branch_state(0, #vst{}=Vst) -> Vst;
+branch_state(0, #vst{}=Vst) ->
+ %% If the instruction fails, the stack may be scanned
+ %% looking for a catch tag. Therefore the Y registers
+ %% must be initialized at this point.
+ verify_y_init(Vst),
+ Vst;
branch_state(L, #vst{current=St,branched=B}=Vst) ->
Vst#vst{
branched=case gb_trees:is_defined(L, B) of
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index aa2d224bb4..50b0ba76f8 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -787,8 +787,10 @@ asm_passes() ->
| binary_passes()].
binary_passes() ->
- [{native_compile,fun test_native/1,fun native_compile/2},
- {unless,binary,?pass(save_binary,not_werror)}].
+ [{iff,'to_dis',{listing,"dis"}},
+ {native_compile,fun test_native/1,fun native_compile/2},
+ {unless,binary,?pass(save_binary,not_werror)}
+ ].
%%%
%%% Compiler passes.
diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl
index 86146c614f..d44fa60997 100644
--- a/lib/compiler/test/beam_type_SUITE.erl
+++ b/lib/compiler/test/beam_type_SUITE.erl
@@ -22,7 +22,8 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
integers/1,coverage/1,booleans/1,setelement/1,cons/1,
- tuple/1,record_float/1,binary_float/1,float_compare/1]).
+ tuple/1,record_float/1,binary_float/1,float_compare/1,
+ arity_checks/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -40,7 +41,8 @@ groups() ->
tuple,
record_float,
binary_float,
- float_compare
+ float_compare,
+ arity_checks
]}].
init_per_suite(Config) ->
@@ -64,6 +66,15 @@ integers(_Config) ->
college = do_integers_3(),
+ zero = do_integers_4(<<0:1>>, 0),
+ one = do_integers_4(<<1:1>>, 0),
+ other = do_integers_4(<<1:1>>, 2),
+
+ zero = do_integers_5(0, 0),
+ one = do_integers_5(0, 1),
+ two = do_integers_5(0, 2),
+ three = do_integers_5(0, 3),
+
ok.
do_integers_1(B0) ->
@@ -86,6 +97,30 @@ do_integers_3() ->
1 -> 0
end.
+do_integers_4(<<X:1,T/bits>>, C) ->
+ %% Binary matching gives the range 0-1 for X.
+ %% The range for `X bor C` is unknown. It must not be inherited
+ %% from X. (`X bor C` will reuse the register used for X.)
+ case X bor C of
+ 0 -> do_integers_4(T, C, zero);
+ 1 -> do_integers_4(T, C, one);
+ _ -> do_integers_4(T, C, other)
+ end.
+
+do_integers_4(_, _, Res) ->
+ Res.
+
+do_integers_5(X0, Y0) ->
+ %% X and Y will use the same register.
+ X = X0 band 1,
+ Y = Y0 band 3,
+ case Y of
+ 0 -> zero;
+ 1 -> one;
+ 2 -> two;
+ 3 -> three
+ end.
+
coverage(_Config) ->
{'EXIT',{badarith,_}} = (catch id(1) bsl 0.5),
{'EXIT',{badarith,_}} = (catch id(2.0) bsl 2),
@@ -171,6 +206,31 @@ do_float_compare(X) ->
_T -> Y > 0
end.
+arity_checks(_Config) ->
+ %% ERL-549: an unsafe optimization removed a test_arity instruction,
+ %% causing the following to return 'broken' instead of 'ok'.
+ ok = do_record_arity_check({rgb, 255, 255, 255, 1}),
+ ok = do_tuple_arity_check({255, 255, 255, 1}).
+
+-record(rgb, {r = 255, g = 255, b = 255}).
+
+do_record_arity_check(RGB) when
+ (element(2, RGB) >= 0), (element(2, RGB) =< 255),
+ (element(3, RGB) >= 0), (element(3, RGB) =< 255),
+ (element(4, RGB) >= 0), (element(4, RGB) =< 255) ->
+ if
+ element(1, RGB) =:= rgb, is_record(RGB, rgb) -> broken;
+ true -> ok
+ end.
+
+do_tuple_arity_check(RGB) when is_tuple(RGB),
+ (element(1, RGB) >= 0), (element(1, RGB) =< 255),
+ (element(2, RGB) >= 0), (element(2, RGB) =< 255),
+ (element(3, RGB) >= 0), (element(3, RGB) =< 255) ->
+ case RGB of
+ {255, _, _} -> broken;
+ _ -> ok
+ end.
id(I) ->
I.
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index f647a4030d..96897d612d 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -119,9 +119,19 @@ file_1(Config) when is_list(Config) ->
true = exists(Target),
passed = run(Target, test, []),
+ %% Test option 'deterministic' as a compiler attribute.
+ Det = deterministic_module,
+ {DetPath, DetTarget} = get_files(Config, Det, "det_target"),
+ {ok,Det,DetCode} = compile:file(DetPath, [binary]),
+ {module,Det} = code:load_binary(Det, "", DetCode),
+ [{version,_}] = Det:module_info(compile),
+ true = code:delete(Det),
+ false = code:purge(Det),
+
%% Cleanup.
ok = file:delete(Target),
ok = file:del_dir(filename:dirname(Target)),
+ ok = file:del_dir(filename:dirname(DetTarget)),
%% There should not be any messages in the messages.
receive
@@ -398,6 +408,7 @@ do_file_listings(DataDir, PrivDir, [File|Files]) ->
ok = file:delete(filename:join(Listings, File ++ ".core")),
do_listing(Simple, TargetDir, to_core, ".core"),
do_listing(Simple, TargetDir, to_kernel, ".kernel"),
+ do_listing(Simple, TargetDir, to_dis, ".dis"),
%% Final clean up.
lists:foreach(fun(F) -> ok = file:delete(F) end,
@@ -413,6 +424,7 @@ listings_big(Config) when is_list(Config) ->
do_listing(Big, TargetDir, 'E'),
do_listing(Big, TargetDir, 'P'),
do_listing(Big, TargetDir, dkern, ".kernel"),
+ do_listing(Big, TargetDir, to_dis, ".dis"),
TargetNoext = filename:rootname(Target, code:objfile_extension()),
{ok,big} = compile:file(TargetNoext, [from_asm,{outdir,TargetDir}]),
diff --git a/lib/compiler/test/compile_SUITE_data/deterministic_module.erl b/lib/compiler/test/compile_SUITE_data/deterministic_module.erl
new file mode 100644
index 0000000000..5e0e29c25e
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/deterministic_module.erl
@@ -0,0 +1,21 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(deterministic_module).
+-compile([deterministic]).
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 435a57aac2..082786c7d8 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 7.1.3
+COMPILER_VSN = 7.1.4
diff --git a/lib/cosEvent/doc/src/Makefile b/lib/cosEvent/doc/src/Makefile
index ba505d6f7b..4fcb210e5a 100644
--- a/lib/cosEvent/doc/src/Makefile
+++ b/lib/cosEvent/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1999-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
#
@@ -49,31 +49,26 @@ XML_REF3_FILES = \
cosEventApp.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml
+ part.xml
XML_CHAPTER_FILES = \
ch_contents.xml \
ch_introduction.xml \
ch_event_service.xml \
- notes.xml
+ notes.xml
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-TECHNICAL_DESCR_FILES =
+TECHNICAL_DESCR_FILES =
GIF_FILES = \
- book.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif \
e_s_components.gif \
- e_s_models.gif
+ e_s_models.gif
-PS_FILES =
+PS_FILES =
# ----------------------------------------------------
@@ -84,7 +79,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -97,10 +92,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -130,11 +125,11 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INDEX_TARGET): $(INDEX_SRC)
sed -e 's;%VSN%;$(VSN);' $(INDEX_SRC) > $(INDEX_TARGET)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/cosEvent/doc/src/book.gif b/lib/cosEvent/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/cosEvent/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosEvent/doc/src/fascicules.xml b/lib/cosEvent/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/cosEvent/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/cosEvent/doc/src/notes.gif b/lib/cosEvent/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/cosEvent/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosEvent/doc/src/notes.xml b/lib/cosEvent/doc/src/notes.xml
index fe94cb64d3..ba0b0d88db 100644
--- a/lib/cosEvent/doc/src/notes.xml
+++ b/lib/cosEvent/doc/src/notes.xml
@@ -33,7 +33,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosEvent 2.2.1</title>
+ <section><title>cosEvent 2.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosEvent 2.2.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosEvent/doc/src/part_notes.xml b/lib/cosEvent/doc/src/part_notes.xml
deleted file mode 100644
index 37103a5592..0000000000
--- a/lib/cosEvent/doc/src/part_notes.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1999</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>cosEvent Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date>1999-04-20</date>
- <rev>1.0</rev>
- <file>part_notes.xml</file>
- </header>
- <description>
- <p>The cosEvent Application is an Erlang implementation of a CORBA Service
- CosEvent.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/cosEvent/doc/src/ref_man.gif b/lib/cosEvent/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/cosEvent/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosEvent/doc/src/summary.html.src b/lib/cosEvent/doc/src/summary.html.src
deleted file mode 100644
index 6196223480..0000000000
--- a/lib/cosEvent/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-Orber OMG Event Service \ No newline at end of file
diff --git a/lib/cosEvent/doc/src/user_guide.gif b/lib/cosEvent/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/cosEvent/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosEvent/vsn.mk b/lib/cosEvent/vsn.mk
index c39bed9fe4..578950294a 100644
--- a/lib/cosEvent/vsn.mk
+++ b/lib/cosEvent/vsn.mk
@@ -1,2 +1,2 @@
-COSEVENT_VSN = 2.2.1
+COSEVENT_VSN = 2.2.2
diff --git a/lib/cosEventDomain/doc/src/Makefile b/lib/cosEventDomain/doc/src/Makefile
index 0f18e43547..9faf65394d 100644
--- a/lib/cosEventDomain/doc/src/Makefile
+++ b/lib/cosEventDomain/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2001-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2001-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
#
@@ -44,31 +44,24 @@ XML_REF3_FILES = \
cosEventDomainApp.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml
+ part.xml
XML_CHAPTER_FILES = \
ch_contents.xml \
ch_introduction.xml \
ch_QoS.xml \
ch_event_domain_service.xml \
- notes.xml
+ notes.xml
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-TECHNICAL_DESCR_FILES =
+TECHNICAL_DESCR_FILES =
-GIF_FILES = \
- book.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif
-
-
-PS_FILES =
+GIF_FILES =
+PS_FILES =
# ----------------------------------------------------
@@ -78,7 +71,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -91,10 +84,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -124,11 +117,11 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INDEX_TARGET): $(INDEX_SRC)
sed -e 's;%VSN%;$(VSN);' $(INDEX_SRC) > $(INDEX_TARGET)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/cosEventDomain/doc/src/book.gif b/lib/cosEventDomain/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/cosEventDomain/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosEventDomain/doc/src/fascicules.xml b/lib/cosEventDomain/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/cosEventDomain/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/cosEventDomain/doc/src/notes.gif b/lib/cosEventDomain/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/cosEventDomain/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosEventDomain/doc/src/notes.xml b/lib/cosEventDomain/doc/src/notes.xml
index 5e5bb2c33e..bd0a119ad2 100644
--- a/lib/cosEventDomain/doc/src/notes.xml
+++ b/lib/cosEventDomain/doc/src/notes.xml
@@ -32,7 +32,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosEventDomain 1.2.1</title>
+ <section><title>cosEventDomain 1.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosEventDomain 1.2.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosEventDomain/doc/src/part_notes.xml b/lib/cosEventDomain/doc/src/part_notes.xml
deleted file mode 100644
index af6864decf..0000000000
--- a/lib/cosEventDomain/doc/src/part_notes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2001</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>cosEventDomain Release Notes</title>
- <prepared>Niclas Eklund</prepared>
- <docno></docno>
- <date>2001-08-20</date>
- <rev>1.0</rev>
- </header>
- <description>
- <p>The cosEventDomain Application is an Erlang implementation of a CORBA Service
- CosEventDomainAdmin.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/cosEventDomain/doc/src/ref_man.gif b/lib/cosEventDomain/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/cosEventDomain/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosEventDomain/doc/src/summary.html.src b/lib/cosEventDomain/doc/src/summary.html.src
deleted file mode 100644
index 922c956633..0000000000
--- a/lib/cosEventDomain/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-Orber OMG Event Domain Service \ No newline at end of file
diff --git a/lib/cosEventDomain/doc/src/user_guide.gif b/lib/cosEventDomain/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/cosEventDomain/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosEventDomain/vsn.mk b/lib/cosEventDomain/vsn.mk
index 4e10d6ac60..0c063a00f9 100644
--- a/lib/cosEventDomain/vsn.mk
+++ b/lib/cosEventDomain/vsn.mk
@@ -1,2 +1,2 @@
-COSEVENTDOMAIN_VSN = 1.2.1
+COSEVENTDOMAIN_VSN = 1.2.2
diff --git a/lib/cosFileTransfer/doc/src/Makefile b/lib/cosFileTransfer/doc/src/Makefile
index 8d8c736f1b..fcaa88c1e2 100644
--- a/lib/cosFileTransfer/doc/src/Makefile
+++ b/lib/cosFileTransfer/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2000-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
#
@@ -46,8 +46,7 @@ XML_REF3_FILES = \
CosFileTransfer_FileIterator.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml
+ part.xml
XML_CHAPTER_FILES = \
ch_contents.xml \
ch_introduction.xml \
@@ -59,18 +58,14 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-TECHNICAL_DESCR_FILES =
+TECHNICAL_DESCR_FILES =
GIF_FILES = \
- book.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif \
CosFileTransfer.gif
-PS_FILES =
+PS_FILES =
# ----------------------------------------------------
@@ -83,7 +78,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html)
@@ -95,10 +90,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -127,11 +122,11 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INDEX_TARGET): $(INDEX_SRC)
sed -e 's;%VSN%;$(VSN);' $(INDEX_SRC) > $(INDEX_TARGET)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/cosFileTransfer/doc/src/book.gif b/lib/cosFileTransfer/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/cosFileTransfer/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosFileTransfer/doc/src/fascicules.xml b/lib/cosFileTransfer/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/cosFileTransfer/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/cosFileTransfer/doc/src/notes.gif b/lib/cosFileTransfer/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/cosFileTransfer/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosFileTransfer/doc/src/notes.xml b/lib/cosFileTransfer/doc/src/notes.xml
index 58ab087014..e0b4bdf64b 100644
--- a/lib/cosFileTransfer/doc/src/notes.xml
+++ b/lib/cosFileTransfer/doc/src/notes.xml
@@ -31,7 +31,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosFileTransfer 1.2.1</title>
+ <section><title>cosFileTransfer 1.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosFileTransfer 1.2.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosFileTransfer/doc/src/part_notes.xml b/lib/cosFileTransfer/doc/src/part_notes.xml
deleted file mode 100644
index d91de39b1f..0000000000
--- a/lib/cosFileTransfer/doc/src/part_notes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2000</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>cosFileTransfer Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date>2000-08-24</date>
- <rev>1.0</rev>
- </header>
- <description>
- <p>The cosFileTransfer Application is an Erlang implementation of the OMG
- CORBA FileTransfer Service.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/cosFileTransfer/doc/src/ref_man.gif b/lib/cosFileTransfer/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/cosFileTransfer/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosFileTransfer/doc/src/summary.html.src b/lib/cosFileTransfer/doc/src/summary.html.src
deleted file mode 100644
index 4c66e147e9..0000000000
--- a/lib/cosFileTransfer/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-Orber OMG File Transfer Service.
diff --git a/lib/cosFileTransfer/doc/src/user_guide.gif b/lib/cosFileTransfer/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/cosFileTransfer/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosFileTransfer/vsn.mk b/lib/cosFileTransfer/vsn.mk
index e271c05242..561f11fbb2 100644
--- a/lib/cosFileTransfer/vsn.mk
+++ b/lib/cosFileTransfer/vsn.mk
@@ -1 +1 @@
-COSFILETRANSFER_VSN = 1.2.1
+COSFILETRANSFER_VSN = 1.2.2
diff --git a/lib/cosNotification/doc/src/Makefile b/lib/cosNotification/doc/src/Makefile
index 6207290101..43ed8294c4 100644
--- a/lib/cosNotification/doc/src/Makefile
+++ b/lib/cosNotification/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2000-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
#
@@ -70,8 +70,7 @@ XML_REF3_FILES = \
XML_PART_FILES = \
- part.xml \
- part_notes.xml
+ part.xml
XML_CHAPTER_FILES = \
ch_contents.xml \
ch_introduction.xml \
@@ -85,19 +84,15 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-TECHNICAL_DESCR_FILES =
+TECHNICAL_DESCR_FILES =
GIF_FILES = \
- book.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif \
eventstructure.gif \
notificationFlow.gif
-PS_FILES =
+PS_FILES =
# ----------------------------------------------------
@@ -107,7 +102,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -120,10 +115,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -152,11 +147,11 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INDEX_TARGET): $(INDEX_SRC)
sed -e 's;%VSN%;$(VSN);' $(INDEX_SRC) > $(INDEX_TARGET)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/cosNotification/doc/src/book.gif b/lib/cosNotification/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/cosNotification/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosNotification/doc/src/fascicules.xml b/lib/cosNotification/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/cosNotification/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/cosNotification/doc/src/notes.gif b/lib/cosNotification/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/cosNotification/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosNotification/doc/src/notes.xml b/lib/cosNotification/doc/src/notes.xml
index 1237000153..bf0fc73548 100644
--- a/lib/cosNotification/doc/src/notes.xml
+++ b/lib/cosNotification/doc/src/notes.xml
@@ -32,7 +32,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosNotification 1.2.2</title>
+ <section><title>cosNotification 1.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosNotification 1.2.2</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosNotification/doc/src/part_notes.xml b/lib/cosNotification/doc/src/part_notes.xml
deleted file mode 100644
index c999d885c1..0000000000
--- a/lib/cosNotification/doc/src/part_notes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2000</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>cosNotification Release Notes</title>
- <prepared>Niclas Eklund</prepared>
- <docno></docno>
- <date>2000-01-31</date>
- <rev>1.0</rev>
- </header>
- <description>
- <p>The cosNotification Application is an Erlang implementation of the OMG
- CORBA Notification Service.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/cosNotification/doc/src/ref_man.gif b/lib/cosNotification/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/cosNotification/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosNotification/doc/src/summary.html.src b/lib/cosNotification/doc/src/summary.html.src
deleted file mode 100644
index 92ade4f9cd..0000000000
--- a/lib/cosNotification/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-Orber OMG Notification Service \ No newline at end of file
diff --git a/lib/cosNotification/doc/src/user_guide.gif b/lib/cosNotification/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/cosNotification/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosNotification/vsn.mk b/lib/cosNotification/vsn.mk
index 0d95ab4853..1677057670 100644
--- a/lib/cosNotification/vsn.mk
+++ b/lib/cosNotification/vsn.mk
@@ -1,2 +1,2 @@
-COSNOTIFICATION_VSN = 1.2.2
+COSNOTIFICATION_VSN = 1.2.3
diff --git a/lib/cosProperty/doc/src/Makefile b/lib/cosProperty/doc/src/Makefile
index 6b210500f9..ec11ca1735 100644
--- a/lib/cosProperty/doc/src/Makefile
+++ b/lib/cosProperty/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2000-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
#
@@ -46,11 +46,10 @@ XML_REF3_FILES = \
CosPropertyService_PropertiesIterator.xml \
CosPropertyService_PropertyNamesIterator.xml
-XML_REF6_FILES =
+XML_REF6_FILES =
XML_PART_FILES = \
- part.xml \
- part_notes.xml
+ part.xml
XML_CHAPTER_FILES = \
ch_contents.xml \
ch_introduction.xml \
@@ -61,17 +60,13 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-TECHNICAL_DESCR_FILES =
+TECHNICAL_DESCR_FILES =
-GIF_FILES = \
- book.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif
+GIF_FILES =
-PS_FILES =
+PS_FILES =
# ----------------------------------------------------
@@ -82,7 +77,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -97,10 +92,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -129,11 +124,11 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INDEX_TARGET): $(INDEX_SRC)
sed -e 's;%VSN%;$(VSN);' $(INDEX_SRC) > $(INDEX_TARGET)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/cosProperty/doc/src/book.gif b/lib/cosProperty/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/cosProperty/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosProperty/doc/src/fascicules.xml b/lib/cosProperty/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/cosProperty/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/cosProperty/doc/src/notes.gif b/lib/cosProperty/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/cosProperty/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosProperty/doc/src/notes.xml b/lib/cosProperty/doc/src/notes.xml
index e5d22982c5..4de246de67 100644
--- a/lib/cosProperty/doc/src/notes.xml
+++ b/lib/cosProperty/doc/src/notes.xml
@@ -33,7 +33,22 @@
</header>
- <section><title>cosProperty 1.2.2</title>
+ <section><title>cosProperty 1.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosProperty 1.2.2</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosProperty/doc/src/part_notes.xml b/lib/cosProperty/doc/src/part_notes.xml
deleted file mode 100644
index bb3221e41c..0000000000
--- a/lib/cosProperty/doc/src/part_notes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2000</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>cosProperty Release Notes</title>
- <prepared>Niclas Eklund</prepared>
- <docno></docno>
- <date>2000-06-07</date>
- <rev>1.0</rev>
- </header>
- <description>
- <p>The cosProperty Application is an Erlang implementation of the OMG
- CORBA Property Service.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/cosProperty/doc/src/ref_man.gif b/lib/cosProperty/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/cosProperty/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosProperty/doc/src/summary.html.src b/lib/cosProperty/doc/src/summary.html.src
deleted file mode 100644
index 87370c659c..0000000000
--- a/lib/cosProperty/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-Orber OMG Property Service. \ No newline at end of file
diff --git a/lib/cosProperty/doc/src/user_guide.gif b/lib/cosProperty/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/cosProperty/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosProperty/vsn.mk b/lib/cosProperty/vsn.mk
index 78ba88445d..a3a952346e 100644
--- a/lib/cosProperty/vsn.mk
+++ b/lib/cosProperty/vsn.mk
@@ -1,2 +1,2 @@
-COSPROPERTY_VSN = 1.2.2
+COSPROPERTY_VSN = 1.2.3
diff --git a/lib/cosTime/doc/src/Makefile b/lib/cosTime/doc/src/Makefile
index f3fdafa58a..6886d2812c 100644
--- a/lib/cosTime/doc/src/Makefile
+++ b/lib/cosTime/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2000-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
#
@@ -46,8 +46,7 @@ XML_REF3_FILES = \
CosTimerEvent_TimerEventService.xml \
XML_PART_FILES = \
- part.xml \
- part_notes.xml
+ part.xml
XML_CHAPTER_FILES = \
ch_contents.xml \
ch_introduction.xml \
@@ -58,17 +57,13 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-TECHNICAL_DESCR_FILES =
+TECHNICAL_DESCR_FILES =
-GIF_FILES = \
- book.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif
+GIF_FILES =
-PS_FILES =
+PS_FILES =
# ----------------------------------------------------
@@ -78,7 +73,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -91,10 +86,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -123,11 +118,11 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INDEX_TARGET): $(INDEX_SRC)
sed -e 's;%VSN%;$(VSN);' $(INDEX_SRC) > $(INDEX_TARGET)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/cosTime/doc/src/book.gif b/lib/cosTime/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/cosTime/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosTime/doc/src/fascicules.xml b/lib/cosTime/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/cosTime/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/cosTime/doc/src/notes.gif b/lib/cosTime/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/cosTime/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosTime/doc/src/notes.xml b/lib/cosTime/doc/src/notes.xml
index 686d9e6add..16e02f8b1f 100644
--- a/lib/cosTime/doc/src/notes.xml
+++ b/lib/cosTime/doc/src/notes.xml
@@ -33,7 +33,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosTime 1.2.2</title>
+ <section><title>cosTime 1.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosTime 1.2.2</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosTime/doc/src/part_notes.xml b/lib/cosTime/doc/src/part_notes.xml
deleted file mode 100644
index dbc9185038..0000000000
--- a/lib/cosTime/doc/src/part_notes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2000</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>cosTime Release Notes</title>
- <prepared>Niclas Eklund</prepared>
- <docno></docno>
- <date>2000-01-31</date>
- <rev>1.0</rev>
- </header>
- <description>
- <p>The cosTime Application is an Erlang implementation of the OMG
- CORBA Time and TimerEvent Services.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/cosTime/doc/src/ref_man.gif b/lib/cosTime/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/cosTime/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosTime/doc/src/summary.html.src b/lib/cosTime/doc/src/summary.html.src
deleted file mode 100644
index 78e383d275..0000000000
--- a/lib/cosTime/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-Orber OMG Timer and TimerEvent Services.
diff --git a/lib/cosTime/doc/src/user_guide.gif b/lib/cosTime/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/cosTime/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosTime/vsn.mk b/lib/cosTime/vsn.mk
index 7c9cae2d2f..7d6fcbebcd 100644
--- a/lib/cosTime/vsn.mk
+++ b/lib/cosTime/vsn.mk
@@ -1,2 +1,2 @@
-COSTIME_VSN = 1.2.2
+COSTIME_VSN = 1.2.3
diff --git a/lib/cosTransactions/doc/src/Makefile b/lib/cosTransactions/doc/src/Makefile
index b70d7647b1..3c054593a9 100644
--- a/lib/cosTransactions/doc/src/Makefile
+++ b/lib/cosTransactions/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1999-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
#
@@ -49,8 +49,7 @@ XML_REF3_FILES = \
# CosTransactions_Synchronization.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml
+ part.xml
XML_CHAPTER_FILES = \
ch_contents.xml \
ch_introduction.xml \
@@ -62,17 +61,13 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-TECHNICAL_DESCR_FILES =
+TECHNICAL_DESCR_FILES =
-GIF_FILES = \
- book.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif
+GIF_FILES =
-PS_FILES =
+PS_FILES =
# ----------------------------------------------------
@@ -82,7 +77,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -95,10 +90,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -127,11 +122,11 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INDEX_TARGET): $(INDEX_SRC)
sed -e 's;%VSN%;$(VSN);' $(INDEX_SRC) > $(INDEX_TARGET)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/cosTransactions/doc/src/book.gif b/lib/cosTransactions/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/cosTransactions/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosTransactions/doc/src/fascicules.xml b/lib/cosTransactions/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/cosTransactions/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/cosTransactions/doc/src/notes.gif b/lib/cosTransactions/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/cosTransactions/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosTransactions/doc/src/notes.xml b/lib/cosTransactions/doc/src/notes.xml
index 85ace1208b..2401c04c3f 100644
--- a/lib/cosTransactions/doc/src/notes.xml
+++ b/lib/cosTransactions/doc/src/notes.xml
@@ -33,7 +33,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosTransactions 1.3.2</title>
+ <section><title>cosTransactions 1.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosTransactions 1.3.2</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosTransactions/doc/src/part_notes.xml b/lib/cosTransactions/doc/src/part_notes.xml
deleted file mode 100644
index 48cc04aa4c..0000000000
--- a/lib/cosTransactions/doc/src/part_notes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1999</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>cosTransactions Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date>1999-04-14</date>
- <rev>2.0</rev>
- </header>
- <description>
- <p>The cosTransactions Application is an Erlang implementation of the OMG
- CORBA Transaction Service.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/cosTransactions/doc/src/ref_man.gif b/lib/cosTransactions/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/cosTransactions/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosTransactions/doc/src/summary.html.src b/lib/cosTransactions/doc/src/summary.html.src
deleted file mode 100644
index 8fb7d6ea20..0000000000
--- a/lib/cosTransactions/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-Orber OMG Transaction Service \ No newline at end of file
diff --git a/lib/cosTransactions/doc/src/user_guide.gif b/lib/cosTransactions/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/cosTransactions/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/cosTransactions/vsn.mk b/lib/cosTransactions/vsn.mk
index ab163d83c2..bba2058231 100644
--- a/lib/cosTransactions/vsn.mk
+++ b/lib/cosTransactions/vsn.mk
@@ -1 +1 @@
-COSTRANSACTIONS_VSN = 1.3.2
+COSTRANSACTIONS_VSN = 1.3.3
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index af7c209c75..31124ba477 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2016. All Rights Reserved.
+# Copyright Ericsson AB 1999-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -78,12 +78,16 @@ CRYPTO_STATIC_OBJS = $(OBJDIR)/crypto_static$(TYPEMARKER).o\
NIF_ARCHIVE = $(LIBDIR)/crypto$(TYPEMARKER).a
+TEST_ENGINE_OBJS = $(OBJDIR)/otp_test_engine$(TYPEMARKER).o
+
ifeq ($(findstring win32,$(TARGET)), win32)
NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).dll
CALLBACK_LIB = $(LIBDIR)/crypto_callback$(TYPEMARKER).dll
+TEST_ENGINE_LIB = $(LIBDIR)/otp_test_engine$(TYPEMARKER).dll
else
NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).so
CALLBACK_LIB = $(LIBDIR)/crypto_callback$(TYPEMARKER).so
+TEST_ENGINE_LIB = $(LIBDIR)/otp_test_engine$(TYPEMARKER).so
endif
ifeq ($(HOST_OS),)
@@ -129,10 +133,22 @@ ALL_STATIC_CFLAGS = $(DED_STATIC_CFLAGS) $(INCLUDES)
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
-debug opt valgrind: $(NIF_LIB) $(CALLBACK_LIB)
+debug opt valgrind: $(NIF_LIB) $(CALLBACK_LIB) $(TEST_ENGINE_LIB)
static_lib: $(NIF_ARCHIVE)
+$(OBJDIR)/otp_test_engine$(TYPEMARKER).o: otp_test_engine.c
+ $(V_at)$(INSTALL_DIR) $(OBJDIR)
+ $(V_CC) -c -o $@ $(ALL_CFLAGS) $<
+
+$(LIBDIR)/otp_test_engine$(TYPEMARKER).so: $(TEST_ENGINE_OBJS)
+ $(V_at)$(INSTALL_DIR) $(LIBDIR)
+ $(V_LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(CRYPTO_LINK_LIB)
+
+$(LIBDIR)/otp_test_engine$(TYPEMARKER).dll: $(TEST_ENGINE_OBJS)
+ $(V_at)$(INSTALL_DIR) $(LIBDIR)
+ $(V_LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(TEST_ENGINE_OBJS) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME)
+
$(OBJDIR)/%$(TYPEMARKER).o: %.c
$(V_at)$(INSTALL_DIR) $(OBJDIR)
$(V_CC) -c -o $@ $(ALL_CFLAGS) $<
@@ -170,6 +186,7 @@ ifeq ($(findstring win32,$(TARGET)), win32)
rm -f $(LIBDIR)/crypto.debug.dll
rm -f $(LIBDIR)/crypto_callback.dll
rm -f $(LIBDIR)/crypto_callback.debug.dll
+ rm -f $(LIBDIR)/otp_test_engine.dll
else
rm -f $(LIBDIR)/crypto.so
rm -f $(LIBDIR)/crypto.debug.so
@@ -177,6 +194,7 @@ else
rm -f $(LIBDIR)/crypto_callback.so
rm -f $(LIBDIR)/crypto_callback.debug.so
rm -f $(LIBDIR)/crypto_callback.valgrind.so
+ rm -f $(LIBDIR)/otp_test_engine.so
endif
rm -f $(OBJDIR)/crypto.o
rm -f $(OBJDIR)/crypto_static.o
@@ -187,6 +205,7 @@ endif
rm -f $(OBJDIR)/crypto_callback.o
rm -f $(OBJDIR)/crypto_callback.debug.o
rm -f $(OBJDIR)/crypto_callback.valgrind.o
+ rm -f $(OBJDIR)/otp_test_engine.o
rm -f core *~
docs:
@@ -206,6 +225,8 @@ ifeq ($(DYNAMIC_CRYPTO_LIB),yes)
$(INSTALL_PROGRAM) $(CALLBACK_OBJS) "$(RELSYSDIR)/priv/obj"
$(INSTALL_PROGRAM) $(CALLBACK_LIB) "$(RELSYSDIR)/priv/lib"
endif
+ $(INSTALL_PROGRAM) $(TEST_ENGINE_OBJS) "$(RELSYSDIR)/priv/obj"
+ $(INSTALL_PROGRAM) $(TEST_ENGINE_LIB) "$(RELSYSDIR)/priv/lib"
release_docs_spec:
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 1d9c1e0f88..9a3ea07c97 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1,4 +1,4 @@
-/*
+/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2010-2017. All Rights Reserved.
@@ -19,8 +19,8 @@
*/
/*
- * Purpose: Dynamically loadable NIF library for cryptography.
- * Based on OpenSSL.
+ * Purpose: Dynamically loadable NIF library for cryptography.
+ * Based on OpenSSL.
*/
#ifdef __WIN32__
@@ -60,6 +60,8 @@
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
/* Helper macro to construct a OPENSSL_VERSION_NUMBER.
* See openssl/opensslv.h
@@ -79,9 +81,9 @@
*
* Therefor works tests like this as intendend:
* OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0)
- * (The test is for example "2.4.2" >= "1.0.0" although the test
+ * (The test is for example "2.4.2" >= "1.0.0" although the test
* with the cloned OpenSSL test would be "1.0.1" >= "1.0.0")
- *
+ *
* But tests like this gives wrong result:
* OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
* (The test is false since "2.4.2" < "1.1.0". It should have been
@@ -119,6 +121,10 @@
#include <openssl/modes.h>
#endif
+#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'h')
+#define HAS_ENGINE_SUPPORT
+#endif
+
#include "crypto_callback.h"
#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \
@@ -173,6 +179,12 @@
# define HAVE_ECB_IVEC_BUG
#endif
+#define HAVE_RSA_SSLV23_PADDING
+#if defined(HAS_LIBRESSL) \
+ && LIBRESSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(2,6,1)
+# undef HAVE_RSA_SSLV23_PADDING
+#endif
+
#if defined(HAVE_CMAC)
#include <openssl/cmac.h>
#endif
@@ -240,7 +252,7 @@
/* 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 MAX_BYTES_TO_NIF 20000
#define CONSUME_REDS(NifEnv, Ibin) \
do { \
@@ -277,7 +289,7 @@ static HMAC_CTX *HMAC_CTX_new()
static void HMAC_CTX_free(HMAC_CTX *ctx)
{
HMAC_CTX_cleanup(ctx);
- return CRYPTO_free(ctx);
+ CRYPTO_free(ctx);
}
#define EVP_MD_CTX_new() EVP_MD_CTX_create()
@@ -342,6 +354,10 @@ static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const
static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key);
static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g);
+static INLINE void DSA_get0_pqg(const DSA *dsa,
+ const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
+static INLINE void DSA_get0_key(const DSA *dsa,
+ const BIGNUM **pub_key, const BIGNUM **priv_key);
static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
{
@@ -358,6 +374,23 @@ static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
return 1;
}
+static INLINE void
+DSA_get0_pqg(const DSA *dsa, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
+{
+ *p = dsa->p;
+ *q = dsa->q;
+ *g = dsa->g;
+}
+
+static INLINE void
+DSA_get0_key(const DSA *dsa, const BIGNUM **pub_key, const BIGNUM **priv_key)
+{
+ if (pub_key) *pub_key = dsa->pub_key;
+ if (priv_key) *priv_key = dsa->priv_key;
+}
+
+
+
static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key);
static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g);
static INLINE int DH_set_length(DH *dh, long length);
@@ -387,6 +420,8 @@ static INLINE int DH_set_length(DH *dh, long length)
return 1;
}
+
+
static INLINE void
DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
{
@@ -398,8 +433,8 @@ DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
static INLINE void
DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
{
- *pub_key = dh->pub_key;
- *priv_key = dh->priv_key;
+ if (pub_key) *pub_key = dh->pub_key;
+ if (priv_key) *priv_key = dh->priv_key;
}
#else /* End of compatibility definitions. */
@@ -442,13 +477,13 @@ static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM pkey_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -467,6 +502,22 @@ static ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_N
static ERL_NIF_TERM chacha20_poly1305_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM chacha20_poly1305_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i);
+static ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
/* helpers */
static void init_algorithms_types(ErlNifEnv*);
static void init_digest_types(ErlNifEnv* env);
@@ -478,6 +529,10 @@ static int term2point(ErlNifEnv* env, ERL_NIF_TERM term,
#endif
static ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn);
+#ifdef HAS_ENGINE_SUPPORT
+static int zero_terminate(ErlNifBinary bin, char **buf);
+#endif
+
static int library_refc = 0; /* number of users of this dynamic library */
static ErlNifFunc nif_funcs[] = {
@@ -511,13 +566,13 @@ static ErlNifFunc nif_funcs[] = {
{"rc4_encrypt_with_state", 2, rc4_encrypt_with_state},
{"pkey_sign_nif", 5, pkey_sign_nif},
{"pkey_verify_nif", 6, pkey_verify_nif},
- {"rsa_public_crypt", 4, rsa_public_crypt},
- {"rsa_private_crypt", 4, rsa_private_crypt},
+ {"pkey_crypt_nif", 6, pkey_crypt_nif},
{"rsa_generate_key_nif", 2, rsa_generate_key_nif},
{"dh_generate_parameters_nif", 2, dh_generate_parameters_nif},
{"dh_check", 1, dh_check},
{"dh_generate_key_nif", 4, dh_generate_key_nif},
{"dh_compute_key_nif", 3, dh_compute_key_nif},
+ {"privkey_to_pubkey_nif", 2, privkey_to_pubkey_nif},
{"srp_value_B_nif", 5, srp_value_B_nif},
{"srp_user_secret_nif", 7, srp_user_secret_nif},
{"srp_host_secret_nif", 5, srp_host_secret_nif},
@@ -531,12 +586,27 @@ static ErlNifFunc nif_funcs[] = {
{"aes_gcm_decrypt", 5, aes_gcm_decrypt},
{"chacha20_poly1305_encrypt", 4, chacha20_poly1305_encrypt},
- {"chacha20_poly1305_decrypt", 5, chacha20_poly1305_decrypt}
+ {"chacha20_poly1305_decrypt", 5, chacha20_poly1305_decrypt},
+
+ {"engine_by_id_nif", 1, engine_by_id_nif},
+ {"engine_init_nif", 1, engine_init_nif},
+ {"engine_finish_nif", 1, engine_finish_nif},
+ {"engine_free_nif", 1, engine_free_nif},
+ {"engine_load_dynamic_nif", 0, engine_load_dynamic_nif},
+ {"engine_ctrl_cmd_strings_nif", 3, engine_ctrl_cmd_strings_nif},
+ {"engine_register_nif", 2, engine_register_nif},
+ {"engine_unregister_nif", 2, engine_unregister_nif},
+ {"engine_add_nif", 1, engine_add_nif},
+ {"engine_remove_nif", 1, engine_remove_nif},
+ {"engine_get_first_nif", 0, engine_get_first_nif},
+ {"engine_get_next_nif", 1, engine_get_next_nif},
+ {"engine_get_id_nif", 1, engine_get_id_nif},
+ {"engine_get_all_methods_nif", 0, engine_get_all_methods_nif}
+
};
ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload)
-
#define MD5_CTX_LEN (sizeof(MD5_CTX))
#define MD4_CTX_LEN (sizeof(MD4_CTX))
#define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX))
@@ -549,6 +619,7 @@ static ERL_NIF_TERM atom_error;
static ERL_NIF_TERM atom_rsa_pkcs1_padding;
static ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding;
static ERL_NIF_TERM atom_rsa_no_padding;
+static ERL_NIF_TERM atom_signature_md;
static ERL_NIF_TERM atom_undefined;
static ERL_NIF_TERM atom_ok;
@@ -589,8 +660,14 @@ static ERL_NIF_TERM atom_rsa;
static ERL_NIF_TERM atom_dss;
static ERL_NIF_TERM atom_ecdsa;
static ERL_NIF_TERM atom_rsa_mgf1_md;
+static ERL_NIF_TERM atom_rsa_oaep_label;
+static ERL_NIF_TERM atom_rsa_oaep_md;
+static ERL_NIF_TERM atom_rsa_pad; /* backwards compatibility */
static ERL_NIF_TERM atom_rsa_padding;
static ERL_NIF_TERM atom_rsa_pkcs1_pss_padding;
+#ifdef HAVE_RSA_SSLV23_PADDING
+static ERL_NIF_TERM atom_rsa_sslv23_padding;
+#endif
static ERL_NIF_TERM atom_rsa_x931_padding;
static ERL_NIF_TERM atom_rsa_pss_saltlen;
static ERL_NIF_TERM atom_sha224;
@@ -600,7 +677,33 @@ static ERL_NIF_TERM atom_sha512;
static ERL_NIF_TERM atom_md5;
static ERL_NIF_TERM atom_ripemd160;
-
+#ifdef HAS_ENGINE_SUPPORT
+static ERL_NIF_TERM atom_bad_engine_method;
+static ERL_NIF_TERM atom_bad_engine_id;
+static ERL_NIF_TERM atom_ctrl_cmd_failed;
+static ERL_NIF_TERM atom_engine_init_failed;
+static ERL_NIF_TERM atom_register_engine_failed;
+static ERL_NIF_TERM atom_add_engine_failed;
+static ERL_NIF_TERM atom_remove_engine_failed;
+static ERL_NIF_TERM atom_engine_method_not_supported;
+
+static ERL_NIF_TERM atom_engine_method_rsa;
+static ERL_NIF_TERM atom_engine_method_dsa;
+static ERL_NIF_TERM atom_engine_method_dh;
+static ERL_NIF_TERM atom_engine_method_rand;
+static ERL_NIF_TERM atom_engine_method_ecdh;
+static ERL_NIF_TERM atom_engine_method_ecdsa;
+static ERL_NIF_TERM atom_engine_method_ciphers;
+static ERL_NIF_TERM atom_engine_method_digests;
+static ERL_NIF_TERM atom_engine_method_store;
+static ERL_NIF_TERM atom_engine_method_pkey_meths;
+static ERL_NIF_TERM atom_engine_method_pkey_asn1_meths;
+static ERL_NIF_TERM atom_engine_method_ec;
+
+static ERL_NIF_TERM atom_engine;
+static ERL_NIF_TERM atom_key_id;
+static ERL_NIF_TERM atom_password;
+#endif
static ErlNifResourceType* hmac_context_rtype;
struct hmac_context
@@ -725,11 +828,13 @@ static struct cipher_type_t cipher_types[] =
static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len);
+
/*
#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n")
#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1)
#define PRINTF_ERR2(FMT, A1, A2) enif_fprintf(stderr, FMT "\n", A1, A2)
*/
+
#define PRINTF_ERR0(FMT)
#define PRINTF_ERR1(FMT,A1)
#define PRINTF_ERR2(FMT,A1,A2)
@@ -755,6 +860,23 @@ static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) {
}
#endif
+// Engine
+#ifdef HAS_ENGINE_SUPPORT
+static ErlNifResourceType* engine_ctx_rtype;
+struct engine_ctx {
+ ENGINE *engine;
+ char *id;
+};
+static void engine_ctx_dtor(ErlNifEnv* env, struct engine_ctx* ctx) {
+ PRINTF_ERR0("engine_ctx_dtor");
+ if(ctx->id) {
+ PRINTF_ERR1(" non empty ctx->id=%s", ctx->id);
+ enif_free(ctx->id);
+ } else
+ PRINTF_ERR0(" empty ctx->id=NULL");
+}
+#endif
+
static int verify_lib_version(void)
{
const unsigned long libv = SSLeay();
@@ -790,7 +912,7 @@ static char crypto_callback_name[] = "crypto_callback";
static int change_basename(ErlNifBinary* bin, char* buf, int bufsz, const char* newfile)
{
int i;
-
+
for (i = bin->size; i > 0; i--) {
if (bin->data[i-1] == '/')
break;
@@ -866,12 +988,23 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
return __LINE__;
}
#endif
+#ifdef HAS_ENGINE_SUPPORT
+ engine_ctx_rtype = enif_open_resource_type(env, NULL, "ENGINE_CTX",
+ (ErlNifResourceDtor*) engine_ctx_dtor,
+ ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
+ NULL);
+ if (!engine_ctx_rtype) {
+ PRINTF_ERR0("CRYPTO: Could not open resource type 'ENGINE_CTX'");
+ return __LINE__;
+ }
+
if (library_refc > 0) {
/* Repeated loading of this library (module upgrade).
* Atoms and callbacks are already set, we are done.
*/
return 0;
}
+#endif
atom_true = enif_make_atom(env,"true");
atom_false = enif_make_atom(env,"false");
@@ -895,6 +1028,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
atom_rsa_pkcs1_padding = enif_make_atom(env,"rsa_pkcs1_padding");
atom_rsa_pkcs1_oaep_padding = enif_make_atom(env,"rsa_pkcs1_oaep_padding");
atom_rsa_no_padding = enif_make_atom(env,"rsa_no_padding");
+ atom_signature_md = enif_make_atom(env,"signature_md");
atom_undefined = enif_make_atom(env,"undefined");
atom_ok = enif_make_atom(env,"ok");
atom_not_prime = enif_make_atom(env,"not_prime");
@@ -933,8 +1067,14 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
atom_dss = enif_make_atom(env,"dss");
atom_ecdsa = enif_make_atom(env,"ecdsa");
atom_rsa_mgf1_md = enif_make_atom(env,"rsa_mgf1_md");
+ atom_rsa_oaep_label = enif_make_atom(env,"rsa_oaep_label");
+ atom_rsa_oaep_md = enif_make_atom(env,"rsa_oaep_md");
+ atom_rsa_pad = enif_make_atom(env,"rsa_pad"); /* backwards compatibility */
atom_rsa_padding = enif_make_atom(env,"rsa_padding");
atom_rsa_pkcs1_pss_padding = enif_make_atom(env,"rsa_pkcs1_pss_padding");
+#ifdef HAVE_RSA_SSLV23_PADDING
+ atom_rsa_sslv23_padding = enif_make_atom(env,"rsa_sslv23_padding");
+#endif
atom_rsa_x931_padding = enif_make_atom(env,"rsa_x931_padding");
atom_rsa_pss_saltlen = enif_make_atom(env,"rsa_pss_saltlen");
atom_sha224 = enif_make_atom(env,"sha224");
@@ -944,6 +1084,33 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
atom_md5 = enif_make_atom(env,"md5");
atom_ripemd160 = enif_make_atom(env,"ripemd160");
+#ifdef HAS_ENGINE_SUPPORT
+ atom_bad_engine_method = enif_make_atom(env,"bad_engine_method");
+ atom_bad_engine_id = enif_make_atom(env,"bad_engine_id");
+ atom_ctrl_cmd_failed = enif_make_atom(env,"ctrl_cmd_failed");
+ atom_engine_init_failed = enif_make_atom(env,"engine_init_failed");
+ atom_engine_method_not_supported = enif_make_atom(env,"engine_method_not_supported");
+ atom_add_engine_failed = enif_make_atom(env,"add_engine_failed");
+ atom_remove_engine_failed = enif_make_atom(env,"remove_engine_failed");
+
+ atom_engine_method_rsa = enif_make_atom(env,"engine_method_rsa");
+ atom_engine_method_dsa = enif_make_atom(env,"engine_method_dsa");
+ atom_engine_method_dh = enif_make_atom(env,"engine_method_dh");
+ atom_engine_method_rand = enif_make_atom(env,"engine_method_rand");
+ atom_engine_method_ecdh = enif_make_atom(env,"engine_method_ecdh");
+ atom_engine_method_ecdsa = enif_make_atom(env,"engine_method_ecdsa");
+ atom_engine_method_store = enif_make_atom(env,"engine_method_store");
+ atom_engine_method_ciphers = enif_make_atom(env,"engine_method_ciphers");
+ atom_engine_method_digests = enif_make_atom(env,"engine_method_digests");
+ atom_engine_method_pkey_meths = enif_make_atom(env,"engine_method_pkey_meths");
+ atom_engine_method_pkey_asn1_meths = enif_make_atom(env,"engine_method_pkey_asn1_meths");
+ atom_engine_method_ec = enif_make_atom(env,"engine_method_ec");
+
+ atom_engine = enif_make_atom(env,"engine");
+ atom_key_id = enif_make_atom(env,"key_id");
+ atom_password = enif_make_atom(env,"password");
+#endif
+
init_digest_types(env);
init_cipher_types(env);
init_algorithms_types(env);
@@ -965,24 +1132,24 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
#else /* !HAVE_DYNAMIC_CRYPTO_LIB */
funcp = &get_crypto_callbacks;
#endif
-
+
#ifdef OPENSSL_THREADS
enif_system_info(&sys_info, sizeof(sys_info));
if (sys_info.scheduler_threads > 1) {
- nlocks = CRYPTO_num_locks();
+ nlocks = CRYPTO_num_locks();
}
/* else no need for locks */
#endif
-
+
ccb = (*funcp)(nlocks);
-
+
if (!ccb || ccb->sizeof_me != sizeof(*ccb)) {
PRINTF_ERR0("Invalid 'crypto_callbacks'");
return __LINE__;
}
-
+
CRYPTO_set_mem_functions(ccb->crypto_alloc, ccb->crypto_realloc, ccb->crypto_free);
-
+
#ifdef OPENSSL_THREADS
if (nlocks > 0) {
CRYPTO_set_locking_callback(ccb->locking_function);
@@ -1178,11 +1345,11 @@ static ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
* Version string is still from library though.
*/
- memcpy(enif_make_new_binary(env, name_sz, &name_term), libname, name_sz);
+ memcpy(enif_make_new_binary(env, name_sz, &name_term), libname, name_sz);
memcpy(enif_make_new_binary(env, ver_sz, &ver_term), ver, ver_sz);
return enif_make_list1(env, enif_make_tuple3(env, name_term,
- enif_make_int(env, ver_num),
+ enif_make_int(env, ver_num),
ver_term));
}
@@ -1217,6 +1384,8 @@ static ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, int argc, const ERL_NIF_TER
}
}
+
+#if defined(HAVE_EC)
static ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env)
{
ERL_NIF_TERM reason;
@@ -1225,6 +1394,7 @@ static ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env)
else
return enif_make_badarg(env);
}
+#endif
static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type, Data) */
@@ -1660,7 +1830,7 @@ static ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
{/* (Context, Data) */
ErlNifBinary data;
struct hmac_context* obj;
-
+
if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data)) {
return enif_make_badarg(env);
@@ -1696,13 +1866,13 @@ static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
enif_mutex_unlock(obj->mtx);
return enif_make_badarg(env);
}
-
+
HMAC_Final(obj->ctx, mac_buf, &mac_len);
HMAC_CTX_free(obj->ctx);
obj->alive = 0;
enif_mutex_unlock(obj->mtx);
- if (argc == 2 && req_len < mac_len) {
+ if (argc == 2 && req_len < mac_len) {
/* Only truncate to req_len bytes if asked. */
mac_len = req_len;
}
@@ -2013,7 +2183,7 @@ static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_
}
static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* ({Key, IVec, ECount, Num}, Data) */
+{/* ({Key, IVec, ECount, Num}, Data) */
ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin;
AES_KEY aes_key;
unsigned int num;
@@ -2034,14 +2204,14 @@ static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_N
return enif_make_badarg(env);
}
- ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term);
+ ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term);
ecount2_buf = enif_make_new_binary(env, ecount_bin.size, &ecount2_term);
-
+
memcpy(ivec2_buf, ivec_bin.data, 16);
memcpy(ecount2_buf, ecount_bin.data, ecount_bin.size);
AES_ctr128_encrypt((unsigned char *) text_bin.data,
- enif_make_new_binary(env, text_bin.size, &cipher_term),
+ enif_make_new_binary(env, text_bin.size, &cipher_term),
text_bin.size, &aes_key, ivec2_buf, ecount2_buf, &num);
num2_term = enif_make_uint(env, num);
@@ -2344,7 +2514,7 @@ out_err:
}
static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Bytes) */
+{/* (Bytes) */
unsigned bytes;
unsigned char* data;
ERL_NIF_TERM ret;
@@ -2438,7 +2608,7 @@ static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
bn_to = BN_new();
BN_sub(bn_to, bn_rand, bn_from);
- BN_pseudo_rand_range(bn_rand, bn_to);
+ BN_pseudo_rand_range(bn_rand, bn_to);
BN_add(bn_rand, bn_rand, bn_from);
dlen = BN_num_bytes(bn_rand);
data = enif_make_new_binary(env, dlen+4, &ret);
@@ -2456,7 +2626,7 @@ static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
BIGNUM *bn_base=NULL, *bn_exponent=NULL, *bn_modulo=NULL, *bn_result;
BN_CTX *bn_ctx;
unsigned char* ptr;
- unsigned dlen;
+ unsigned dlen;
unsigned bin_hdr; /* return type: 0=plain binary, 4: mpint */
unsigned extra_byte;
ERL_NIF_TERM ret;
@@ -2477,7 +2647,7 @@ static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
dlen = BN_num_bytes(bn_result);
extra_byte = bin_hdr && BN_is_bit_set(bn_result, dlen*8-1);
ptr = enif_make_new_binary(env, bin_hdr+extra_byte+dlen, &ret);
- if (bin_hdr) {
+ if (bin_hdr) {
put_int32(ptr, extra_byte+dlen);
ptr[4] = 0; /* extra zeroed byte to ensure a positive mpint */
ptr += bin_hdr + extra_byte;
@@ -2537,6 +2707,7 @@ static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len)
return NULL;
}
+
static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data1, Data2) */
ErlNifBinary d1, d2;
@@ -2570,7 +2741,7 @@ static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
return enif_make_badarg(env);
}
RC4_set_key((RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &ret),
- key.size, key.data);
+ key.size, key.data);
return ret;
#else
return enif_raise_exception(env, atom_notsup);
@@ -2722,118 +2893,6 @@ static int get_dss_public_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa)
return 1;
}
-static int rsa_pad(ERL_NIF_TERM term, int* padding)
-{
- if (term == atom_rsa_pkcs1_padding) {
- *padding = RSA_PKCS1_PADDING;
- }
- else if (term == atom_rsa_pkcs1_oaep_padding) {
- *padding = RSA_PKCS1_OAEP_PADDING;
- }
- else if (term == atom_rsa_no_padding) {
- *padding = RSA_NO_PADDING;
- }
- else {
- return 0;
- }
- return 1;
-}
-
-static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Data, PublKey=[E,N], Padding, IsEncrypt) */
- ErlNifBinary data_bin, ret_bin;
- ERL_NIF_TERM head, tail;
- int padding, i;
- RSA* rsa;
- BIGNUM *e, *n;
-
- rsa = RSA_new();
-
- if (!enif_inspect_binary(env, argv[0], &data_bin)
- || !enif_get_list_cell(env, argv[1], &head, &tail)
- || !get_bn_from_bin(env, head, &e)
- || !enif_get_list_cell(env, tail, &head, &tail)
- || !get_bn_from_bin(env, head, &n)
- || !enif_is_empty_list(env,tail)
- || !rsa_pad(argv[2], &padding)) {
-
- RSA_free(rsa);
- return enif_make_badarg(env);
- }
- (void) RSA_set0_key(rsa, n, e, NULL);
-
- enif_alloc_binary(RSA_size(rsa), &ret_bin);
-
- if (argv[3] == atom_true) {
- ERL_VALGRIND_ASSERT_MEM_DEFINED(data_bin.data,data_bin.size);
- i = RSA_public_encrypt(data_bin.size, data_bin.data,
- ret_bin.data, rsa, padding);
- if (i > 0) {
- ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i);
- }
- }
- else {
- i = RSA_public_decrypt(data_bin.size, data_bin.data,
- ret_bin.data, rsa, padding);
- if (i > 0) {
- ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i);
- enif_realloc_binary(&ret_bin, i);
- }
- }
- RSA_free(rsa);
- if (i > 0) {
- return enif_make_binary(env,&ret_bin);
- }
- else {
- enif_release_binary(&ret_bin);
- return atom_error;
- }
-}
-
-static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Data, Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Padding, IsEncrypt) */
- ErlNifBinary data_bin, ret_bin;
- int padding, i;
- RSA* rsa;
-
- rsa = RSA_new();
-
- if (!enif_inspect_binary(env, argv[0], &data_bin)
- || !get_rsa_private_key(env, argv[1], rsa)
- || !rsa_pad(argv[2], &padding)) {
-
- RSA_free(rsa);
- return enif_make_badarg(env);
- }
-
- enif_alloc_binary(RSA_size(rsa), &ret_bin);
-
- if (argv[3] == atom_true) {
- ERL_VALGRIND_ASSERT_MEM_DEFINED(data_bin.data,data_bin.size);
- i = RSA_private_encrypt(data_bin.size, data_bin.data,
- ret_bin.data, rsa, padding);
- if (i > 0) {
- ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i);
- }
- }
- else {
- i = RSA_private_decrypt(data_bin.size, data_bin.data,
- ret_bin.data, rsa, padding);
- if (i > 0) {
- ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i);
- enif_realloc_binary(&ret_bin, i);
- }
- }
- RSA_free(rsa);
- if (i > 0) {
- return enif_make_binary(env,&ret_bin);
- }
- else {
- enif_release_binary(&ret_bin);
- return atom_error;
- }
-}
-
/* Creates a term which can be parsed by get_rsa_private_key(). This is a list of plain integer binaries (not mpints). */
static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa)
{
@@ -2950,7 +3009,7 @@ static ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF
static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (PrimeLen, Generator) */
int prime_len, generator;
- DH* dh_params;
+ DH* dh_params = NULL;
int p_len, g_len;
unsigned char *p_ptr, *g_ptr;
ERL_NIF_TERM ret_p, ret_g;
@@ -2961,8 +3020,8 @@ static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const E
return enif_make_badarg(env);
}
- dh_params = DH_generate_parameters(prime_len, generator, NULL, NULL);
- if (dh_params == NULL) {
+
+ if (DH_generate_parameters_ex(dh_params, prime_len, generator, NULL)) {
return atom_error;
}
DH_get0_pqg(dh_params, &dh_p, &dh_q, &dh_g);
@@ -2975,7 +3034,7 @@ static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const E
BN_bn2bin(dh_g, g_ptr);
ERL_VALGRIND_MAKE_MEM_DEFINED(p_ptr, p_len);
ERL_VALGRIND_MAKE_MEM_DEFINED(g_ptr, g_len);
- return enif_make_list2(env, ret_p, ret_g);
+ return enif_make_list2(env, ret_p, ret_g);
}
static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -2985,9 +3044,9 @@ static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
ERL_NIF_TERM ret, head, tail;
BIGNUM *dh_p, *dh_g;
- if (!enif_get_list_cell(env, argv[0], &head, &tail)
+ if (!enif_get_list_cell(env, argv[0], &head, &tail)
|| !get_bn_from_bin(env, head, &dh_p)
- || !enif_get_list_cell(env, tail, &head, &tail)
+ || !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_bin(env, head, &dh_g)
|| !enif_is_empty_list(env,tail)) {
@@ -3004,12 +3063,12 @@ static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
else if (i & DH_NOT_SUITABLE_GENERATOR) ret = atom_not_suitable_generator;
else ret = enif_make_tuple2(env, atom_unknown, enif_make_uint(env, i));
}
- else { /* Check Failed */
+ else { /* Check Failed */
ret = enif_make_tuple2(env, atom_error, atom_check_failed);
}
DH_free(dh_params);
return ret;
-}
+}
static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */
@@ -3111,7 +3170,7 @@ static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_T
i = DH_compute_key(ret_bin.data, other_pub_key, dh_params);
if (i > 0) {
if (i != ret_bin.size) {
- enif_realloc_binary(&ret_bin, i);
+ enif_realloc_binary(&ret_bin, i);
}
ret = enif_make_binary(env, &ret_bin);
}
@@ -3906,9 +3965,70 @@ static int get_pkey_sign_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF
return PKEY_OK;
}
-static int get_pkey_sign_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, EVP_PKEY **pkey)
+
+#ifdef HAS_ENGINE_SUPPORT
+static int get_engine_and_key_id(ErlNifEnv *env, ERL_NIF_TERM key, char ** id, ENGINE **e)
{
- if (algorithm == atom_rsa) {
+ ERL_NIF_TERM engine_res, key_id_term;
+ struct engine_ctx *ctx;
+ ErlNifBinary key_id_bin;
+
+ if (!enif_get_map_value(env, key, atom_engine, &engine_res) ||
+ !enif_get_resource(env, engine_res, engine_ctx_rtype, (void**)&ctx) ||
+ !enif_get_map_value(env, key, atom_key_id, &key_id_term) ||
+ !enif_inspect_binary(env, key_id_term, &key_id_bin)) {
+ return 0;
+ }
+ else {
+ *e = ctx->engine;
+ return zero_terminate(key_id_bin, id);
+ }
+}
+
+
+static char *get_key_password(ErlNifEnv *env, ERL_NIF_TERM key) {
+ ERL_NIF_TERM tmp_term;
+ ErlNifBinary pwd_bin;
+ char *pwd;
+ if (enif_get_map_value(env, key, atom_password, &tmp_term) &&
+ enif_inspect_binary(env, tmp_term, &pwd_bin) &&
+ zero_terminate(pwd_bin, &pwd)
+ ) return pwd;
+
+ return NULL;
+}
+
+static int zero_terminate(ErlNifBinary bin, char **buf) {
+ *buf = enif_alloc(bin.size+1);
+ if (!*buf)
+ return 0;
+ memcpy(*buf, bin.data, bin.size);
+ *(*buf+bin.size) = 0;
+ return 1;
+}
+#endif
+
+static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, EVP_PKEY **pkey)
+{
+ if (enif_is_map(env, key)) {
+#ifdef HAS_ENGINE_SUPPORT
+ /* Use key stored in engine */
+ ENGINE *e;
+ char *id;
+ char *password;
+
+ if (!get_engine_and_key_id(env, key, &id, &e))
+ return PKEY_BADARG;
+ password = get_key_password(env, key);
+ *pkey = ENGINE_load_private_key(e, id, NULL, password);
+ if (!*pkey)
+ return PKEY_BADARG;
+ enif_free(id);
+#else
+ return PKEY_BADARG;
+#endif
+ }
+ else if (algorithm == atom_rsa) {
RSA *rsa = RSA_new();
if (!get_rsa_private_key(env, key, rsa)) {
@@ -3965,8 +4085,86 @@ static int get_pkey_sign_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TER
return PKEY_OK;
}
+
+static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key,
+ EVP_PKEY **pkey)
+{
+ if (enif_is_map(env, key)) {
+#ifdef HAS_ENGINE_SUPPORT
+ /* Use key stored in engine */
+ ENGINE *e;
+ char *id;
+ char *password;
+
+ if (!get_engine_and_key_id(env, key, &id, &e))
+ return PKEY_BADARG;
+ password = get_key_password(env, key);
+ *pkey = ENGINE_load_public_key(e, id, NULL, password);
+ if (!pkey)
+ return PKEY_BADARG;
+ enif_free(id);
+#else
+ return PKEY_BADARG;
+#endif
+ } else if (algorithm == atom_rsa) {
+ RSA *rsa = RSA_new();
+
+ if (!get_rsa_public_key(env, key, rsa)) {
+ RSA_free(rsa);
+ return PKEY_BADARG;
+ }
+
+ *pkey = EVP_PKEY_new();
+ if (!EVP_PKEY_assign_RSA(*pkey, rsa)) {
+ EVP_PKEY_free(*pkey);
+ RSA_free(rsa);
+ return PKEY_BADARG;
+ }
+ } else if (algorithm == atom_ecdsa) {
+#if defined(HAVE_EC)
+ EC_KEY *ec = NULL;
+ const ERL_NIF_TERM *tpl_terms;
+ int tpl_arity;
+
+ if (enif_get_tuple(env, key, &tpl_arity, &tpl_terms) && tpl_arity == 2
+ && enif_is_tuple(env, tpl_terms[0]) && enif_is_binary(env, tpl_terms[1])
+ && get_ec_key(env, tpl_terms[0], atom_undefined, tpl_terms[1], &ec)) {
+
+ *pkey = EVP_PKEY_new();
+ if (!EVP_PKEY_assign_EC_KEY(*pkey, ec)) {
+ EVP_PKEY_free(*pkey);
+ EC_KEY_free(ec);
+ return PKEY_BADARG;
+ }
+ } else {
+ return PKEY_BADARG;
+ }
+#else
+ return PKEY_NOTSUP;
+#endif
+ } else if (algorithm == atom_dss) {
+ DSA *dsa = DSA_new();
+
+ if (!get_dss_public_key(env, key, dsa)) {
+ DSA_free(dsa);
+ return PKEY_BADARG;
+ }
+
+ *pkey = EVP_PKEY_new();
+ if (!EVP_PKEY_assign_DSA(*pkey, dsa)) {
+ EVP_PKEY_free(*pkey);
+ DSA_free(dsa);
+ return PKEY_BADARG;
+ }
+ } else {
+ return PKEY_BADARG;
+ }
+
+ return PKEY_OK;
+}
+
static ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
-{/* (Algorithm, Type, Data|{digest,Digest}, Key, Options) */
+{/* (Algorithm, Type, Data|{digest,Digest}, Key|#{}, Options) */
int i;
const EVP_MD *md = NULL;
unsigned char md_value[EVP_MAX_MD_SIZE];
@@ -3986,6 +4184,13 @@ enif_get_atom(env,argv[0],buf,1024,ERL_NIF_LATIN1); printf("algo=%s ",buf);
enif_get_atom(env,argv[1],buf,1024,ERL_NIF_LATIN1); printf("hash=%s ",buf);
printf("\r\n");
*/
+
+#ifndef HAS_ENGINE_SUPPORT
+ if (enif_is_map(env, argv[3])) {
+ return atom_notsup;
+ }
+#endif
+
i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen);
if (i != PKEY_OK) {
if (i == PKEY_NOTSUP)
@@ -4002,15 +4207,14 @@ printf("\r\n");
return enif_make_badarg(env);
}
- if (get_pkey_sign_key(env, argv[0], argv[3], &pkey) != PKEY_OK) {
+ if (get_pkey_private_key(env, argv[0], argv[3], &pkey) != PKEY_OK) {
return enif_make_badarg(env);
}
#ifdef HAS_EVP_PKEY_CTX
-/* printf("EVP interface\r\n");
- */
ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx) goto badarg;
+
if (EVP_PKEY_sign_init(ctx) <= 0) goto badarg;
if (md != NULL && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) goto badarg;
@@ -4097,66 +4301,6 @@ printf("\r\n");
}
-static int get_pkey_verify_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key,
- EVP_PKEY **pkey)
-{
- if (algorithm == atom_rsa) {
- RSA *rsa = RSA_new();
-
- if (!get_rsa_public_key(env, key, rsa)) {
- RSA_free(rsa);
- return PKEY_BADARG;
- }
-
- *pkey = EVP_PKEY_new();
- if (!EVP_PKEY_assign_RSA(*pkey, rsa)) {
- EVP_PKEY_free(*pkey);
- RSA_free(rsa);
- return PKEY_BADARG;
- }
- } else if (algorithm == atom_ecdsa) {
-#if defined(HAVE_EC)
- EC_KEY *ec = NULL;
- const ERL_NIF_TERM *tpl_terms;
- int tpl_arity;
-
- if (enif_get_tuple(env, key, &tpl_arity, &tpl_terms) && tpl_arity == 2
- && enif_is_tuple(env, tpl_terms[0]) && enif_is_binary(env, tpl_terms[1])
- && get_ec_key(env, tpl_terms[0], atom_undefined, tpl_terms[1], &ec)) {
-
- *pkey = EVP_PKEY_new();
- if (!EVP_PKEY_assign_EC_KEY(*pkey, ec)) {
- EVP_PKEY_free(*pkey);
- EC_KEY_free(ec);
- return PKEY_BADARG;
- }
- } else {
- return PKEY_BADARG;
- }
-#else
- return PKEY_NOTSUP;
-#endif
- } else if (algorithm == atom_dss) {
- DSA *dsa = DSA_new();
-
- if (!get_dss_public_key(env, key, dsa)) {
- DSA_free(dsa);
- return PKEY_BADARG;
- }
-
- *pkey = EVP_PKEY_new();
- if (!EVP_PKEY_assign_DSA(*pkey, dsa)) {
- EVP_PKEY_free(*pkey);
- DSA_free(dsa);
- return PKEY_BADARG;
- }
- } else {
- return PKEY_BADARG;
- }
-
- return PKEY_OK;
-}
-
static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{/* (Algorithm, Type, Data|{digest,Digest}, Signature, Key, Options) */
int i;
@@ -4172,6 +4316,12 @@ static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
unsigned char *tbs; /* data to be signed */
size_t tbslen;
+#ifndef HAS_ENGINE_SUPPORT
+ if (enif_is_map(env, argv[4])) {
+ return atom_notsup;
+ }
+#endif
+
if (!enif_inspect_binary(env, argv[3], &sig_bin)) {
return enif_make_badarg(env);
}
@@ -4192,12 +4342,12 @@ static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
return enif_make_badarg(env);
}
- if (get_pkey_verify_key(env, argv[0], argv[4], &pkey) != PKEY_OK) {
+ if (get_pkey_public_key(env, argv[0], argv[4], &pkey) != PKEY_OK) {
return enif_make_badarg(env);
}
#ifdef HAS_EVP_PKEY_CTX
-/* printf("EVP interface\r\n");
+/* printf("EVP interface\r\n");
*/
ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx) goto badarg;
@@ -4269,6 +4419,471 @@ static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
}
+/*--------------------------------*/
+
+static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options,
+ PKeyCryptOptions *opt)
+{
+ ERL_NIF_TERM head, tail;
+ const ERL_NIF_TERM *tpl_terms;
+ int tpl_arity;
+ const EVP_MD *opt_md;
+ int i;
+
+ if (!enif_is_list(env, options)) {
+ return PKEY_BADARG;
+ }
+
+ /* defaults */
+ if (algorithm == atom_rsa) {
+ opt->rsa_mgf1_md = NULL;
+ opt->rsa_oaep_label.data = NULL;
+ opt->rsa_oaep_label.size = 0;
+ opt->rsa_oaep_md = NULL;
+ opt->rsa_padding = RSA_PKCS1_PADDING;
+ opt->signature_md = NULL;
+ }
+
+ if (enif_is_empty_list(env, options)) {
+ return PKEY_OK;
+ }
+
+ if (algorithm == atom_rsa) {
+ tail = options;
+ while (enif_get_list_cell(env, tail, &head, &tail)) {
+ if (enif_get_tuple(env, head, &tpl_arity, &tpl_terms) && tpl_arity == 2) {
+ if (tpl_terms[0] == atom_rsa_padding
+ || tpl_terms[0] == atom_rsa_pad /* Compatibility */
+ ) {
+ if (tpl_terms[1] == atom_rsa_pkcs1_padding) {
+ opt->rsa_padding = RSA_PKCS1_PADDING;
+ } else if (tpl_terms[1] == atom_rsa_pkcs1_oaep_padding) {
+ opt->rsa_padding = RSA_PKCS1_OAEP_PADDING;
+#ifdef HAVE_RSA_SSLV23_PADDING
+ } else if (tpl_terms[1] == atom_rsa_sslv23_padding) {
+ opt->rsa_padding = RSA_SSLV23_PADDING;
+#endif
+ } else if (tpl_terms[1] == atom_rsa_x931_padding) {
+ opt->rsa_padding = RSA_X931_PADDING;
+ } else if (tpl_terms[1] == atom_rsa_no_padding) {
+ opt->rsa_padding = RSA_NO_PADDING;
+ } else {
+ return PKEY_BADARG;
+ }
+ } else if (tpl_terms[0] == atom_signature_md && enif_is_atom(env, tpl_terms[1])) {
+ i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md);
+ if (i != PKEY_OK) {
+ return i;
+ }
+ opt->signature_md = opt_md;
+ } else if (tpl_terms[0] == atom_rsa_mgf1_md && enif_is_atom(env, tpl_terms[1])) {
+#ifndef HAVE_RSA_OAEP_MD
+ if (tpl_terms[1] != atom_sha)
+ return PKEY_NOTSUP;
+#endif
+ i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md);
+ if (i != PKEY_OK) {
+ return i;
+ }
+ opt->rsa_mgf1_md = opt_md;
+ } else if (tpl_terms[0] == atom_rsa_oaep_label
+ && enif_inspect_binary(env, tpl_terms[1], &(opt->rsa_oaep_label))) {
+#ifdef HAVE_RSA_OAEP_MD
+ continue;
+#else
+ return PKEY_NOTSUP;
+#endif
+ } else if (tpl_terms[0] == atom_rsa_oaep_md && enif_is_atom(env, tpl_terms[1])) {
+#ifndef HAVE_RSA_OAEP_MD
+ if (tpl_terms[1] != atom_sha)
+ return PKEY_NOTSUP;
+#endif
+ i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md);
+ if (i != PKEY_OK) {
+ return i;
+ }
+ opt->rsa_oaep_md = opt_md;
+ } else {
+ return PKEY_BADARG;
+ }
+ } else {
+ return PKEY_BADARG;
+ }
+ }
+ } else {
+ return PKEY_BADARG;
+ }
+
+ return PKEY_OK;
+}
+
+static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{/* (Algorithm, Data, PublKey=[E,N]|[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Options, IsPrivate, IsEncrypt) */
+ int i;
+ EVP_PKEY *pkey;
+#ifdef HAS_EVP_PKEY_CTX
+ EVP_PKEY_CTX *ctx;
+#else
+ RSA *rsa;
+#endif
+ PKeyCryptOptions crypt_opt;
+ ErlNifBinary in_bin, out_bin, tmp_bin;
+ size_t outlen;
+#ifdef HAVE_RSA_SSLV23_PADDING
+ size_t tmplen;
+#endif
+ int is_private = (argv[4] == atom_true),
+ is_encrypt = (argv[5] == atom_true);
+ int algo_init = 0;
+
+/* char algo[1024]; */
+
+#ifndef HAS_ENGINE_SUPPORT
+ if (enif_is_map(env, argv[2])) {
+ return atom_notsup;
+ }
+#endif
+
+ if (!enif_inspect_binary(env, argv[1], &in_bin)) {
+ return enif_make_badarg(env);
+ }
+
+ i = get_pkey_crypt_options(env, argv[0], argv[3], &crypt_opt);
+ if (i != PKEY_OK) {
+ if (i == PKEY_NOTSUP)
+ return atom_notsup;
+ else
+ return enif_make_badarg(env);
+ }
+
+ if (is_private) {
+ if (get_pkey_private_key(env, argv[0], argv[2], &pkey) != PKEY_OK) {
+ return enif_make_badarg(env);
+ }
+ } else {
+ if (get_pkey_public_key(env, argv[0], argv[2], &pkey) != PKEY_OK) {
+ return enif_make_badarg(env);
+ }
+ }
+
+ out_bin.data = NULL;
+ out_bin.size = 0;
+ tmp_bin.data = NULL;
+ tmp_bin.size = 0;
+
+#ifdef HAS_EVP_PKEY_CTX
+ ctx = EVP_PKEY_CTX_new(pkey, NULL);
+ if (!ctx) goto badarg;
+
+/* enif_get_atom(env,argv[0],algo,1024,ERL_NIF_LATIN1); */
+
+ if (is_private) {
+ if (is_encrypt) {
+ /* private encrypt */
+ if ((algo_init=EVP_PKEY_sign_init(ctx)) <= 0) {
+ /* fprintf(stderr,"BADARG %s private encrypt algo_init=%d %s:%d\r\n", algo, algo_init, __FILE__, __LINE__); */
+ goto badarg;
+ }
+ } else {
+ /* private decrypt */
+ if ((algo_init=EVP_PKEY_decrypt_init(ctx)) <= 0) {
+ /* fprintf(stderr,"BADARG %s private decrypt algo_init=%d %s:%d\r\n", algo, algo_init, __FILE__, __LINE__); */
+ goto badarg;
+ }
+ }
+ } else {
+ if (is_encrypt) {
+ /* public encrypt */
+ if ((algo_init=EVP_PKEY_encrypt_init(ctx)) <= 0) {
+ /* fprintf(stderr,"BADARG %s public encrypt algo_init=%d %s:%d\r\n", algo,algo_init,__FILE__, __LINE__); */
+ goto badarg;
+ }
+ } else {
+ /* public decrypt */
+ if ((algo_init=EVP_PKEY_verify_recover_init(ctx)) <= 0) {
+ /* fprintf(stderr,"BADARG %s public decrypt algo_init=%d %s:%d\r\n", algo,algo_init,__FILE__, __LINE__); */
+ goto badarg;
+ }
+ }
+ }
+
+ if (argv[0] == atom_rsa) {
+ if (crypt_opt.signature_md != NULL
+ && EVP_PKEY_CTX_set_signature_md(ctx, crypt_opt.signature_md) <= 0)
+ goto badarg;
+#ifdef HAVE_RSA_SSLV23_PADDING
+ if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) {
+ if (is_encrypt) {
+ RSA *rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa == NULL) goto badarg;
+ tmplen = RSA_size(rsa);
+ if (!enif_alloc_binary(tmplen, &tmp_bin)) goto badarg;
+ if (RSA_padding_add_SSLv23(tmp_bin.data, tmplen, in_bin.data, in_bin.size) <= 0)
+ goto badarg;
+ in_bin = tmp_bin;
+ }
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0) goto badarg;
+ } else
+#endif
+ {
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, crypt_opt.rsa_padding) <= 0) goto badarg;
+ }
+#ifdef HAVE_RSA_OAEP_MD
+ if (crypt_opt.rsa_padding == RSA_PKCS1_OAEP_PADDING) {
+ if (crypt_opt.rsa_oaep_md != NULL
+ && EVP_PKEY_CTX_set_rsa_oaep_md(ctx, crypt_opt.rsa_oaep_md) <= 0)
+ goto badarg;
+ if (crypt_opt.rsa_mgf1_md != NULL
+ && EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, crypt_opt.rsa_mgf1_md) <= 0) goto badarg;
+ if (crypt_opt.rsa_oaep_label.data != NULL && crypt_opt.rsa_oaep_label.size > 0) {
+ unsigned char *label_copy;
+ label_copy = OPENSSL_malloc(crypt_opt.rsa_oaep_label.size);
+ if (label_copy == NULL) goto badarg;
+ memcpy((void *)(label_copy), (const void *)(crypt_opt.rsa_oaep_label.data),
+ crypt_opt.rsa_oaep_label.size);
+ if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy,
+ crypt_opt.rsa_oaep_label.size) <= 0) {
+ OPENSSL_free(label_copy);
+ label_copy = NULL;
+ goto badarg;
+ }
+ }
+ }
+#endif
+ }
+
+ if (is_private) {
+ if (is_encrypt) {
+ /* private_encrypt */
+ i = EVP_PKEY_sign(ctx, NULL, &outlen, in_bin.data, in_bin.size);
+ } else {
+ /* private_decrypt */
+ i = EVP_PKEY_decrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size);
+ }
+ } else {
+ if (is_encrypt) {
+ /* public_encrypt */
+ i = EVP_PKEY_encrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size);
+ } else {
+ /* public_decrypt */
+ i = EVP_PKEY_verify_recover(ctx, NULL, &outlen, in_bin.data, in_bin.size);
+ }
+ }
+ /* fprintf(stderr,"i = %d %s:%d\r\n", i, __FILE__, __LINE__); */
+
+ if (i != 1) goto badarg;
+
+ enif_alloc_binary(outlen, &out_bin);
+
+ if (is_private) {
+ if (is_encrypt) {
+ /* private_encrypt */
+ i = EVP_PKEY_sign(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size);
+ } else {
+ /* private_decrypt */
+ i = EVP_PKEY_decrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size);
+ }
+ } else {
+ if (is_encrypt) {
+ /* public_encrypt */
+ i = EVP_PKEY_encrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size);
+ } else {
+ /* public_decrypt */
+ i = EVP_PKEY_verify_recover(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size);
+ }
+ }
+
+#else
+ /* Non-EVP cryptolib. Only support RSA */
+
+ if (argv[0] != atom_rsa) {
+ algo_init = -2; /* exitcode: notsup */
+ goto badarg;
+ }
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ enif_alloc_binary(RSA_size(rsa), &out_bin);
+
+ if (is_private) {
+ if (is_encrypt) {
+ /* non-evp rsa private encrypt */
+ ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size);
+ i = RSA_private_encrypt(in_bin.size, in_bin.data,
+ out_bin.data, rsa, crypt_opt.rsa_padding);
+ if (i > 0) {
+ ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i);
+ }
+ } else {
+ /* non-evp rsa private decrypt */
+ i = RSA_private_decrypt(in_bin.size, in_bin.data,
+ out_bin.data, rsa, crypt_opt.rsa_padding);
+ if (i > 0) {
+ ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i);
+ enif_realloc_binary(&out_bin, i);
+ }
+ }
+ } else {
+ if (is_encrypt) {
+ /* non-evp rsa public encrypt */
+ ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size);
+ i = RSA_public_encrypt(in_bin.size, in_bin.data,
+ out_bin.data, rsa, crypt_opt.rsa_padding);
+ if (i > 0) {
+ ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i);
+ }
+ } else {
+ /* non-evp rsa public decrypt */
+ i = RSA_public_decrypt(in_bin.size, in_bin.data,
+ out_bin.data, rsa, crypt_opt.rsa_padding);
+ if (i > 0) {
+ ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i);
+ enif_realloc_binary(&out_bin, i);
+ }
+ }
+ }
+
+ outlen = i;
+ RSA_free(rsa);
+#endif
+
+ if ((i > 0) && argv[0] == atom_rsa && !is_encrypt) {
+#ifdef HAVE_RSA_SSLV23_PADDING
+ if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) {
+ RSA *rsa = EVP_PKEY_get1_RSA(pkey);
+ unsigned char *p;
+ if (rsa == NULL) goto badarg;
+ tmplen = RSA_size(rsa);
+ if (!enif_alloc_binary(tmplen, &tmp_bin)) goto badarg;
+ p = out_bin.data;
+ p++;
+ i = RSA_padding_check_SSLv23(tmp_bin.data, tmplen, p, out_bin.size - 1, tmplen);
+ if (i >= 0) {
+ outlen = i;
+ in_bin = out_bin;
+ out_bin = tmp_bin;
+ tmp_bin = in_bin;
+ i = 1;
+ }
+ }
+#endif
+ }
+
+ if (tmp_bin.data != NULL) {
+ enif_release_binary(&tmp_bin);
+ }
+
+#ifdef HAS_EVP_PKEY_CTX
+ EVP_PKEY_CTX_free(ctx);
+#else
+#endif
+ EVP_PKEY_free(pkey);
+ if (i > 0) {
+ ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, outlen);
+ if (outlen != out_bin.size) {
+ enif_realloc_binary(&out_bin, outlen);
+ ERL_VALGRIND_ASSERT_MEM_DEFINED(out_bin.data, outlen);
+ }
+ return enif_make_binary(env, &out_bin);
+ } else {
+ enif_release_binary(&out_bin);
+ return atom_error;
+ }
+
+ badarg:
+ if (out_bin.data != NULL) {
+ enif_release_binary(&out_bin);
+ }
+ if (tmp_bin.data != NULL) {
+ enif_release_binary(&tmp_bin);
+ }
+#ifdef HAS_EVP_PKEY_CTX
+ EVP_PKEY_CTX_free(ctx);
+#else
+#endif
+ EVP_PKEY_free(pkey);
+ if (algo_init == -2)
+ return atom_notsup;
+ else
+ return enif_make_badarg(env);
+}
+
+
+
+/*--------------------------------*/
+static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{ /* (Algorithm, PrivKey | KeyMap) */
+ EVP_PKEY *pkey;
+ ERL_NIF_TERM alg = argv[0];
+ ERL_NIF_TERM result[8];
+ if (get_pkey_private_key(env, alg, argv[1], &pkey) != PKEY_OK) {
+ return enif_make_badarg(env);
+ }
+
+ if (alg == atom_rsa) {
+ const BIGNUM *n = NULL, *e = NULL, *d = NULL;
+ RSA *rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa) {
+ RSA_get0_key(rsa, &n, &e, &d);
+ result[0] = bin_from_bn(env, e); // Exponent E
+ result[1] = bin_from_bn(env, n); // Modulus N = p*q
+ EVP_PKEY_free(pkey);
+ return enif_make_list_from_array(env, result, 2);
+ }
+
+ } else if (argv[0] == atom_dss) {
+ const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL;
+ DSA *dsa = EVP_PKEY_get1_DSA(pkey);
+ if (dsa) {
+ DSA_get0_pqg(dsa, &p, &q, &g);
+ DSA_get0_key(dsa, &pub_key, NULL);
+ result[0] = bin_from_bn(env, p);
+ result[1] = bin_from_bn(env, q);
+ result[2] = bin_from_bn(env, g);
+ result[3] = bin_from_bn(env, pub_key);
+ EVP_PKEY_free(pkey);
+ return enif_make_list_from_array(env, result, 4);
+ }
+
+ } else if (argv[0] == atom_ecdsa) {
+#if defined(HAVE_EC)
+ /* not yet implemented
+ EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
+ if (ec) {
+ / * Example of result:
+ {
+ Curve = {Field, Prime, Point, Order, CoFactor} =
+ {
+ Field = {prime_field,<<255,...,255>>},
+ Prime = {<<255,...,252>>,
+ <<90,...,75>>,
+ <<196,...,144>>
+ },
+ Point = <<4,...,245>>,
+ Order = <<255,...,81>>,
+ CoFactor = <<1>>
+ },
+ Key = <<151,...,62>>
+ }
+ or
+ {
+ Curve =
+ {characteristic_two_field,
+ M,
+ Basis = {tpbasis, _}
+ | {ppbasis, k1, k2, k3}
+ },
+ Key
+ }
+ * /
+ EVP_PKEY_free(pkey);
+ return enif_make_list_from_array(env, ..., ...);
+ */
+#endif
+ }
+
+ if (pkey) EVP_PKEY_free(pkey);
+ return enif_make_badarg(env);
+}
+
/*================================================================*/
static ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -4280,3 +4895,600 @@ static ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
RAND_seed(seed_bin.data,seed_bin.size);
return atom_ok;
}
+
+/*================================================================*/
+/* Engine */
+/*================================================================*/
+static ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (EngineId) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret;
+ ErlNifBinary engine_id_bin;
+ char *engine_id;
+ ENGINE *engine;
+ struct engine_ctx *ctx;
+
+ // Get Engine Id
+ if(!enif_inspect_binary(env, argv[0], &engine_id_bin)) {
+ PRINTF_ERR0("engine_by_id_nif Leaved: badarg");
+ return enif_make_badarg(env);
+ } else {
+ engine_id = enif_alloc(engine_id_bin.size+1);
+ (void) memcpy(engine_id, engine_id_bin.data, engine_id_bin.size);
+ engine_id[engine_id_bin.size] = '\0';
+ }
+
+ engine = ENGINE_by_id(engine_id);
+ if(!engine) {
+ enif_free(engine_id);
+ PRINTF_ERR0("engine_by_id_nif Leaved: {error, bad_engine_id}");
+ return enif_make_tuple2(env, atom_error, atom_bad_engine_id);
+ }
+
+ ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx));
+ ctx->engine = engine;
+ ctx->id = engine_id;
+
+ ret = enif_make_resource(env, ctx);
+ enif_release_resource(ctx);
+
+ return enif_make_tuple2(env, atom_ok, ret);
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret = atom_ok;
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_init_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+ if (!ENGINE_init(ctx->engine)) {
+ //ERR_print_errors_fp(stderr);
+ PRINTF_ERR0("engine_init_nif Leaved: {error, engine_init_failed}");
+ return enif_make_tuple2(env, atom_error, atom_engine_init_failed);
+ }
+
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_free_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ ENGINE_free(ctx->engine);
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_finish_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ ENGINE_finish(ctx->engine);
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* () */
+#ifdef HAS_ENGINE_SUPPORT
+ ENGINE_load_dynamic();
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine, Commands) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret = atom_ok;
+ unsigned int cmds_len = 0;
+ char **cmds = NULL;
+ struct engine_ctx *ctx;
+ int i, optional = 0;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ PRINTF_ERR1("Engine Id: %s\r\n", ENGINE_get_id(ctx->engine));
+
+ // Get Command List
+ if(!enif_get_list_length(env, argv[1], &cmds_len)) {
+ PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Bad Command List");
+ return enif_make_badarg(env);
+ } else {
+ cmds_len *= 2; // Key-Value list from erlang
+ cmds = enif_alloc((cmds_len+1)*sizeof(char*));
+ if(get_engine_load_cmd_list(env, argv[1], cmds, 0)) {
+ PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Couldn't read Command List");
+ ret = enif_make_badarg(env);
+ goto error;
+ }
+ }
+
+ if(!enif_get_int(env, argv[2], &optional)) {
+ PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Parameter optional not an integer");
+ return enif_make_badarg(env);
+ }
+
+ for(i = 0; i < cmds_len; i+=2) {
+ PRINTF_ERR2("Cmd: %s:%s\r\n",
+ cmds[i] ? cmds[i] : "(NULL)",
+ cmds[i+1] ? cmds[i+1] : "(NULL)");
+ if(!ENGINE_ctrl_cmd_string(ctx->engine, cmds[i], cmds[i+1], optional)) {
+ PRINTF_ERR2("Command failed: %s:%s\r\n",
+ cmds[i] ? cmds[i] : "(NULL)",
+ cmds[i+1] ? cmds[i+1] : "(NULL)");
+ //ENGINE_free(ctx->engine);
+ ret = enif_make_tuple2(env, atom_error, atom_ctrl_cmd_failed);
+ PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: {error, ctrl_cmd_failed}");
+ goto error;
+ }
+ }
+
+ error:
+ for(i = 0; cmds != NULL && cmds[i] != NULL; i++)
+ enif_free(cmds[i]);
+ enif_free(cmds);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_add_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ if (!ENGINE_add(ctx->engine)) {
+ PRINTF_ERR0("engine_add_nif Leaved: {error, add_engine_failed}");
+ return enif_make_tuple2(env, atom_error, atom_add_engine_failed);
+ }
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_remove_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ if (!ENGINE_remove(ctx->engine)) {
+ PRINTF_ERR0("engine_remove_nif Leaved: {error, remove_engine_failed}");
+ return enif_make_tuple2(env, atom_error, atom_remove_engine_failed);
+ }
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine, EngineMethod) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+ unsigned int method;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_register_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+ // Get Method
+ if (!enif_get_uint(env, argv[1], &method)) {
+ PRINTF_ERR0("engine_register_nif Leaved: Parameter Method not an uint");
+ return enif_make_badarg(env);
+ }
+
+ switch(method)
+ {
+#ifdef ENGINE_METHOD_RSA
+ case ENGINE_METHOD_RSA:
+ if (!ENGINE_register_RSA(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DSA
+ case ENGINE_METHOD_DSA:
+ if (!ENGINE_register_DSA(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DH
+ case ENGINE_METHOD_DH:
+ if (!ENGINE_register_DH(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_RAND
+ case ENGINE_METHOD_RAND:
+ if (!ENGINE_register_RAND(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_ECDH
+ case ENGINE_METHOD_ECDH:
+ if (!ENGINE_register_ECDH(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_ECDSA
+ case ENGINE_METHOD_ECDSA:
+ if (!ENGINE_register_ECDSA(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_STORE
+ case ENGINE_METHOD_STORE:
+ if (!ENGINE_register_STORE(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_CIPHERS
+ case ENGINE_METHOD_CIPHERS:
+ if (!ENGINE_register_ciphers(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DIGESTS
+ case ENGINE_METHOD_DIGESTS:
+ if (!ENGINE_register_digests(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_PKEY_METHS
+ case ENGINE_METHOD_PKEY_METHS:
+ if (!ENGINE_register_pkey_meths(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_PKEY_ASN1_METHS
+ case ENGINE_METHOD_PKEY_ASN1_METHS:
+ if (!ENGINE_register_pkey_asn1_meths(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_EC
+ case ENGINE_METHOD_EC:
+ if (!ENGINE_register_EC(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+ default:
+ return enif_make_tuple2(env, atom_error, atom_engine_method_not_supported);
+ break;
+ }
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine, EngineMethod) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+ unsigned int method;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_unregister_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+ // Get Method
+ if (!enif_get_uint(env, argv[1], &method)) {
+ PRINTF_ERR0("engine_unregister_nif Leaved: Parameter Method not an uint");
+ return enif_make_badarg(env);
+ }
+
+ switch(method)
+ {
+#ifdef ENGINE_METHOD_RSA
+ case ENGINE_METHOD_RSA:
+ ENGINE_unregister_RSA(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DSA
+ case ENGINE_METHOD_DSA:
+ ENGINE_unregister_DSA(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DH
+ case ENGINE_METHOD_DH:
+ ENGINE_unregister_DH(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_RAND
+ case ENGINE_METHOD_RAND:
+ ENGINE_unregister_RAND(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_ECDH
+ case ENGINE_METHOD_ECDH:
+ ENGINE_unregister_ECDH(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_ECDSA
+ case ENGINE_METHOD_ECDSA:
+ ENGINE_unregister_ECDSA(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_STORE
+ case ENGINE_METHOD_STORE:
+ ENGINE_unregister_STORE(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_CIPHERS
+ case ENGINE_METHOD_CIPHERS:
+ ENGINE_unregister_ciphers(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DIGESTS
+ case ENGINE_METHOD_DIGESTS:
+ ENGINE_unregister_digests(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_PKEY_METHS
+ case ENGINE_METHOD_PKEY_METHS:
+ ENGINE_unregister_pkey_meths(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_PKEY_ASN1_METHS
+ case ENGINE_METHOD_PKEY_ASN1_METHS:
+ ENGINE_unregister_pkey_asn1_meths(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_EC
+ case ENGINE_METHOD_EC:
+ ENGINE_unregister_EC(ctx->engine);
+ break;
+#endif
+ default:
+ break;
+ }
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret;
+ ENGINE *engine;
+ ErlNifBinary engine_bin;
+ struct engine_ctx *ctx;
+
+ engine = ENGINE_get_first();
+ if(!engine) {
+ enif_alloc_binary(0, &engine_bin);
+ engine_bin.size = 0;
+ return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin));
+ }
+
+ ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx));
+ ctx->engine = engine;
+ ctx->id = NULL;
+
+ ret = enif_make_resource(env, ctx);
+ enif_release_resource(ctx);
+
+ return enif_make_tuple2(env, atom_ok, ret);
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret;
+ ENGINE *engine;
+ ErlNifBinary engine_bin;
+ struct engine_ctx *ctx, *next_ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_get_next_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+ engine = ENGINE_get_next(ctx->engine);
+ if (!engine) {
+ enif_alloc_binary(0, &engine_bin);
+ engine_bin.size = 0;
+ return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin));
+ }
+
+ next_ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx));
+ next_ctx->engine = engine;
+ next_ctx->id = NULL;
+
+ ret = enif_make_resource(env, next_ctx);
+ enif_release_resource(next_ctx);
+
+ return enif_make_tuple2(env, atom_ok, ret);
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ ErlNifBinary engine_id_bin;
+ const char *engine_id;
+ int size;
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_get_id_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ engine_id = ENGINE_get_id(ctx->engine);
+ if (!engine_id) {
+ enif_alloc_binary(0, &engine_id_bin);
+ engine_id_bin.size = 0;
+ return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_id_bin));
+ }
+
+ size = strlen(engine_id);
+ enif_alloc_binary(size, &engine_id_bin);
+ engine_id_bin.size = size;
+ memcpy(engine_id_bin.data, engine_id, size);
+
+ return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_id_bin));
+#else
+ return atom_notsup;
+#endif
+}
+
+static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i)
+{
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM head, tail;
+ const ERL_NIF_TERM *tmp_tuple;
+ ErlNifBinary tmpbin;
+ int arity;
+ char* tmpstr;
+
+ if(!enif_is_empty_list(env, term)) {
+ if(!enif_get_list_cell(env, term, &head, &tail)) {
+ cmds[i] = NULL;
+ return -1;
+ } else {
+ if(!enif_get_tuple(env, head, &arity, &tmp_tuple) || arity != 2) {
+ cmds[i] = NULL;
+ return -1;
+ } else {
+ if(!enif_inspect_binary(env, tmp_tuple[0], &tmpbin)) {
+ cmds[i] = NULL;
+ return -1;
+ } else {
+ tmpstr = enif_alloc(tmpbin.size+1);
+ (void) memcpy(tmpstr, tmpbin.data, tmpbin.size);
+ tmpstr[tmpbin.size] = '\0';
+ cmds[i++] = tmpstr;
+ }
+ if(!enif_inspect_binary(env, tmp_tuple[1], &tmpbin)) {
+ cmds[i] = NULL;
+ return -1;
+ } else {
+ if(tmpbin.size == 0)
+ cmds[i++] = NULL;
+ else {
+ tmpstr = enif_alloc(tmpbin.size+1);
+ (void) memcpy(tmpstr, tmpbin.data, tmpbin.size);
+ tmpstr[tmpbin.size] = '\0';
+ cmds[i++] = tmpstr;
+ }
+ }
+ return get_engine_load_cmd_list(env, tail, cmds, i);
+ }
+ }
+ } else {
+ cmds[i] = NULL;
+ return 0;
+ }
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* () */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM method_array[12];
+ int i = 0;
+
+#ifdef ENGINE_METHOD_RSA
+ method_array[i++] = atom_engine_method_rsa;
+#endif
+#ifdef ENGINE_METHOD_DSA
+ method_array[i++] = atom_engine_method_dsa;
+#endif
+#ifdef ENGINE_METHOD_DH
+ method_array[i++] = atom_engine_method_dh;
+#endif
+#ifdef ENGINE_METHOD_RAND
+ method_array[i++] = atom_engine_method_rand;
+#endif
+#ifdef ENGINE_METHOD_ECDH
+ method_array[i++] = atom_engine_method_ecdh;
+#endif
+#ifdef ENGINE_METHOD_ECDSA
+ method_array[i++] = atom_engine_method_ecdsa;
+#endif
+#ifdef ENGINE_METHOD_STORE
+ method_array[i++] = atom_engine_method_store;
+#endif
+#ifdef ENGINE_METHOD_CIPHERS
+ method_array[i++] = atom_engine_method_ciphers;
+#endif
+#ifdef ENGINE_METHOD_DIGESTS
+ method_array[i++] = atom_engine_method_digests;
+#endif
+#ifdef ENGINE_METHOD_PKEY_METHS
+ method_array[i++] = atom_engine_method_pkey_meths;
+#endif
+#ifdef ENGINE_METHOD_PKEY_ASN1_METHS
+ method_array[i++] = atom_engine_method_pkey_asn1_meths;
+#endif
+#ifdef ENGINE_METHOD_EC
+ method_array[i++] = atom_engine_method_ec;
+#endif
+
+ return enif_make_list_from_array(env, method_array, i);
+#else
+ return atom_notsup;
+#endif
+}
diff --git a/lib/crypto/c_src/otp_test_engine.c b/lib/crypto/c_src/otp_test_engine.c
new file mode 100644
index 0000000000..5c6122c06a
--- /dev/null
+++ b/lib/crypto/c_src/otp_test_engine.c
@@ -0,0 +1,264 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2017-2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef _WIN32
+#define OPENSSL_OPT_WINDLL
+#endif
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/engine.h>
+#include <openssl/md5.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+
+#define PACKED_OPENSSL_VERSION(MAJ, MIN, FIX, P) \
+ ((((((((MAJ << 8) | MIN) << 8 ) | FIX) << 8) | (P-'a'+1)) << 4) | 0xf)
+
+#define PACKED_OPENSSL_VERSION_PLAIN(MAJ, MIN, FIX) \
+ PACKED_OPENSSL_VERSION(MAJ,MIN,FIX,('a'-1))
+
+#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) \
+ || defined(LIBRESSL_VERSION_NUMBER)
+#define OLD
+#endif
+
+static const char *test_engine_id = "MD5";
+static const char *test_engine_name = "MD5 test engine";
+
+/* The callback that does the job of fetching keys on demand by the Engine */
+EVP_PKEY* test_key_load(ENGINE *er, const char *id, UI_METHOD *ui_method, void *callback_data);
+
+
+static int test_init(ENGINE *e) {
+ printf("OTP Test Engine Initializatzion!\r\n");
+
+ /* Load all digest and cipher algorithms. Needed for password protected private keys */
+ OpenSSL_add_all_algorithms();
+
+ return 111;
+}
+
+static void add_test_data(unsigned char *md, unsigned int len)
+{
+ unsigned int i;
+
+ for (i=0; i<len; i++) {
+ md[i] = (unsigned char)(i & 0xff);
+ }
+}
+
+/* MD5 part */
+#undef data
+#ifdef OLD
+#define data(ctx) ((MD5_CTX *)ctx->md_data)
+#endif
+
+static int test_engine_md5_init(EVP_MD_CTX *ctx) {
+ fprintf(stderr, "MD5 initialized\r\n");
+#ifdef OLD
+ return MD5_Init(data(ctx));
+#else
+ return 1;
+#endif
+}
+
+static int test_engine_md5_update(EVP_MD_CTX *ctx,const void *data, size_t count)
+{
+ fprintf(stderr, "MD5 update\r\n");
+#ifdef OLD
+ return MD5_Update(data(ctx), data, (size_t)count);
+#else
+ return 1;
+#endif
+}
+
+static int test_engine_md5_final(EVP_MD_CTX *ctx,unsigned char *md) {
+#ifdef OLD
+ int ret;
+
+ fprintf(stderr, "MD5 final size of EVP_MD: %lu\r\n", sizeof(EVP_MD));
+ ret = MD5_Final(md, data(ctx));
+
+ if (ret > 0) {
+ add_test_data(md, MD5_DIGEST_LENGTH);
+ }
+ return ret;
+#else
+ fprintf(stderr, "MD5 final\r\n");
+ add_test_data(md, MD5_DIGEST_LENGTH);
+ return 1;
+#endif
+}
+
+#ifdef OLD
+static EVP_MD test_engine_md5_method= {
+ NID_md5, /* The name ID for MD5 */
+ NID_undef, /* IGNORED: MD5 with private key encryption NID */
+ MD5_DIGEST_LENGTH, /* Size of MD5 result, in bytes */
+ 0, /* Flags */
+ test_engine_md5_init, /* digest init */
+ test_engine_md5_update, /* digest update */
+ test_engine_md5_final, /* digest final */
+ NULL, /* digest copy */
+ NULL, /* digest cleanup */
+ EVP_PKEY_NULL_method, /* IGNORED: pkey methods */
+ MD5_CBLOCK, /* Internal blocksize, see rfc1321/md5.h */
+ sizeof(EVP_MD *) + sizeof(MD5_CTX),
+ NULL, /* IGNORED: control function */
+};
+#endif
+
+static int test_digest_ids[] = {NID_md5};
+
+static int test_engine_digest_selector(ENGINE *e, const EVP_MD **digest,
+ const int **nids, int nid) {
+ int ok = 1;
+ if (!digest) {
+ *nids = test_digest_ids;
+ fprintf(stderr, "Digest is empty! Nid:%d\r\n", nid);
+ return 2;
+ }
+ fprintf(stderr, "Digest no %d requested\r\n",nid);
+ if (nid == NID_md5) {
+#ifdef OLD
+ *digest = &test_engine_md5_method;
+#else
+ EVP_MD *md = EVP_MD_meth_new(NID_md5, NID_undef);
+ if (!md ||
+ !EVP_MD_meth_set_result_size(md, MD5_DIGEST_LENGTH) ||
+ !EVP_MD_meth_set_flags(md, 0) ||
+ !EVP_MD_meth_set_init(md, test_engine_md5_init) ||
+ !EVP_MD_meth_set_update(md, test_engine_md5_update) ||
+ !EVP_MD_meth_set_final(md, test_engine_md5_final) ||
+ !EVP_MD_meth_set_copy(md, NULL) ||
+ !EVP_MD_meth_set_cleanup(md, NULL) ||
+ !EVP_MD_meth_set_input_blocksize(md, MD5_CBLOCK) ||
+ !EVP_MD_meth_set_app_datasize(md, sizeof(EVP_MD *) + sizeof(MD5_CTX)) ||
+ !EVP_MD_meth_set_ctrl(md, NULL))
+ {
+ ok = 0;
+ *digest = NULL;
+ } else
+ {
+ *digest = md;
+ }
+#endif
+ }
+ else {
+ ok = 0;
+ *digest = NULL;
+ }
+
+ return ok;
+}
+
+
+static int bind_helper(ENGINE * e, const char *id)
+{
+ if (!ENGINE_set_id(e, test_engine_id) ||
+ !ENGINE_set_name(e, test_engine_name) ||
+ !ENGINE_set_init_function(e, test_init) ||
+ !ENGINE_set_digests(e, &test_engine_digest_selector) ||
+ /* For testing of key storage in an Engine: */
+ !ENGINE_set_load_privkey_function(e, &test_key_load) ||
+ !ENGINE_set_load_pubkey_function(e, &test_key_load)
+ )
+ return 0;
+
+ return 1;
+}
+
+IMPLEMENT_DYNAMIC_CHECK_FN();
+
+IMPLEMENT_DYNAMIC_BIND_FN(bind_helper);
+
+/********************************************************
+ *
+ * Engine storage simulation
+ *
+ */
+int pem_passwd_cb_fun(char *buf, int size, int rwflag, void *password);
+
+EVP_PKEY* test_key_load(ENGINE *er, const char *id, UI_METHOD *ui_method, void *callback_data)
+{
+ EVP_PKEY *pkey = NULL;
+ FILE *f = fopen(id, "r");
+
+ if (!f) {
+ fprintf(stderr, "%s:%d fopen(%s) failed\r\n", __FILE__,__LINE__,id);
+ return NULL;
+ }
+
+ /* First try to read as a private key. If that fails, try to read as a public key: */
+ pkey = PEM_read_PrivateKey(f, NULL, pem_passwd_cb_fun, callback_data);
+ if (!pkey) {
+ /* ERR_print_errors_fp (stderr); */
+ fclose(f);
+ f = fopen(id, "r");
+ pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
+ }
+ fclose(f);
+
+ if (!pkey) {
+ fprintf(stderr, "%s:%d Key read from file %s failed.\r\n", __FILE__,__LINE__,id);
+ if (callback_data)
+ fprintf(stderr, "Pwd = \"%s\".\r\n", (char *)callback_data);
+ fprintf(stderr, "Contents of file \"%s\":\r\n",id);
+ f = fopen(id, "r");
+ { /* Print the contents of the key file */
+ char c;
+ while (!feof(f)) {
+ switch (c=fgetc(f)) {
+ case '\n':
+ case '\r': putc('\r',stderr); putc('\n',stderr); break;
+ default: putc(c, stderr);
+ }
+ }
+ }
+ fprintf(stderr, "File contents printed.\r\n");
+ fclose(f);
+ return NULL;
+ }
+
+ return pkey;
+}
+
+
+int pem_passwd_cb_fun(char *buf, int size, int rwflag, void *password)
+{
+ int i;
+
+ fprintf(stderr, "In pem_passwd_cb_fun\r\n");
+ if (!password)
+ return 0;
+
+ i = strlen(password);
+ if (i < size) {
+ /* whole pwd (incl terminating 0) fits */
+ fprintf(stderr, "Got FULL pwd %d(%d) chars\r\n", i, size);
+ memcpy(buf, (char*)password, i+1);
+ return i+1;
+ } else {
+ fprintf(stderr, "Got TO LONG pwd %d(%d) chars\r\n", i, size);
+ /* meaningless with a truncated password */
+ return 0;
+ }
+}
diff --git a/lib/crypto/doc/src/Makefile b/lib/crypto/doc/src/Makefile
index 9c503b8fe0..aa987d2b39 100644
--- a/lib/crypto/doc/src/Makefile
+++ b/lib/crypto/doc/src/Makefile
@@ -9,11 +9,11 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
# AB. All Rights Reserved.''
-#
+#
# $Id$
#
include $(ERL_TOP)/make/target.mk
@@ -38,13 +38,13 @@ XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = crypto.xml
XML_REF6_FILES = crypto_app.xml
-XML_PART_FILES = release_notes.xml usersguide.xml
-XML_CHAPTER_FILES = notes.xml licenses.xml fips.xml
+XML_PART_FILES = usersguide.xml
+XML_CHAPTER_FILES = notes.xml licenses.xml fips.xml engine_load.xml engine_keys.xml
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
GIF_FILES =
@@ -63,9 +63,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -73,7 +73,6 @@ XML_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -86,7 +85,7 @@ man: $(MAN3_FILES) $(MAN6_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt valgrind:
+debug opt valgrind:
clean clean_docs clean_tex:
rm -rf $(HTMLDIR)/*
@@ -97,7 +96,7 @@ clean clean_docs clean_tex:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -114,4 +113,3 @@ release_docs_spec: docs
release_spec:
-
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 5b2c46a004..464799b320 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,6 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
</legalnotice>
<title>crypto</title>
@@ -68,11 +67,11 @@
<section>
<title>DATA TYPES </title>
-
- <code>key_value() = integer() | binary() </code>
+
+ <code>key_value() = integer() | binary() </code>
<p>Always <c>binary()</c> when used as return value</p>
- <code>rsa_public() = [key_value()] = [E, N] </code>
+ <code>rsa_public() = [key_value()] = [E, N] </code>
<p> Where E is the public exponent and N is public modulus. </p>
<code>rsa_private() = [key_value()] = [E, N, D] | [E, N, D, P1, P2, E1, E2, C] </code>
@@ -85,7 +84,7 @@
<code>dss_public() = [key_value()] = [P, Q, G, Y] </code>
<p>Where P, Q and G are the dss parameters and Y is the public key.</p>
- <code>dss_private() = [key_value()] = [P, Q, G, X] </code>
+ <code>dss_private() = [key_value()] = [P, Q, G, X] </code>
<p>Where P, Q and G are the dss parameters and X is the private key.</p>
<code>srp_public() = key_value() </code>
@@ -109,15 +108,16 @@
<code>ecdh_private() = key_value() </code>
- <code>ecdh_params() = ec_named_curve() | ec_explicit_curve()</code>
+ <code>ecdh_params() = ec_named_curve() | ec_explicit_curve()</code>
<code>ec_explicit_curve() =
- {ec_field(), Prime :: key_value(), Point :: key_value(), Order :: integer(), CoFactor :: none | integer()} </code>
+ {ec_field(), Prime :: key_value(), Point :: key_value(), Order :: integer(),
+ CoFactor :: none | integer()} </code>
<code>ec_field() = {prime_field, Prime :: integer()} |
{characteristic_two_field, M :: integer(), Basis :: ec_basis()}</code>
- <code>ec_basis() = {tpbasis, K :: non_neg_integer()} |
+ <code>ec_basis() = {tpbasis, K :: non_neg_integer()} |
{ppbasis, K1 :: non_neg_integer(), K2 :: non_neg_integer(), K3 :: non_neg_integer()} |
onbasis</code>
@@ -136,16 +136,34 @@
See also <seealso marker="#supports-0">crypto:supports/0</seealso>
</p>
+ <marker id="engine_key_ref_type"/>
+ <code>engine_key_ref() = #{engine := engine_ref(),
+ key_id := key_id(),
+ password => password()}</code>
+
+ <code>engine_ref() = term()</code>
+ <p>The result of a call to <seealso marker="#engine_load-3">engine_load/3</seealso>.
+ </p>
+
+ <code>key_id() = string() | binary()</code>
+ <p>Identifies the key to be used. The format depends on the loaded engine. It is passed to
+ the <c>ENGINE_load_(private|public)_key</c> functions in libcrypto.
+ </p>
+
+ <code>password() = string() | binary()</code>
+ <p>The key's password
+ </p>
+
<code>stream_cipher() = rc4 | aes_ctr </code>
- <code>block_cipher() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ige256 | blowfish_cbc |
+ <code>block_cipher() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ige256 | blowfish_cbc |
blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cfb | des_ede3 | rc2_cbc </code>
- <code>aead_cipher() = aes_gcm | chacha20_poly1305 </code>
+ <code>aead_cipher() = aes_gcm | chacha20_poly1305 </code>
- <code>stream_key() = aes_key() | rc4_key() </code>
+ <code>stream_key() = aes_key() | rc4_key() </code>
- <code>block_key() = aes_key() | blowfish_key() | des_key()| des3_key() </code>
+ <code>block_key() = aes_key() | blowfish_key() | des_key()| des3_key() </code>
<code>aes_key() = iodata() </code> <p>Key length is 128, 192 or 256 bits</p>
@@ -174,13 +192,17 @@
Note that both md4 and md5 are recommended only for compatibility with existing applications.
</p>
<code> cipher_algorithms() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ctr | aes_gcm |
- aes_ige256 | blowfish_cbc | blowfish_cfb64 | chacha20_poly1305 | des_cbc | des_cfb |
- des3_cbc | des3_cfb | des_ede3 | rc2_cbc | rc4 </code>
- <code> mac_algorithms() = hmac | cmac</code>
- <code> public_key_algorithms() = rsa |dss | ecdsa | dh | ecdh | ec_gf2m</code>
+ aes_ige256 | blowfish_cbc | blowfish_cfb64 | chacha20_poly1305 | des_cbc |
+ des_cfb | des3_cbc | des3_cfb | des_ede3 | rc2_cbc | rc4 </code>
+ <code> mac_algorithms() = hmac | cmac</code>
+ <code> public_key_algorithms() = rsa |dss | ecdsa | dh | ecdh | ec_gf2m</code>
<p>Note that ec_gf2m is not strictly a public key algorithm, but a restriction on what curves are supported
with ecdsa and ecdh.
</p>
+ <code>engine_method_type() = engine_method_rsa | engine_method_dsa | engine_method_dh |
+ engine_method_rand | engine_method_ecdh | engine_method_ecdsa |
+ engine_method_ciphers | engine_method_digests | engine_method_store |
+ engine_method_pkey_meths | engine_method_pkey_asn1_meths</code>
</section>
@@ -261,13 +283,13 @@
is not supported by the underlying OpenSSL implementation.</p>
</desc>
</func>
-
+
<func>
<name>bytes_to_integer(Bin) -> Integer </name>
<fsummary>Convert binary representation, of an integer, to an Erlang integer.</fsummary>
<type>
<v>Bin = binary() - as returned by crypto functions</v>
-
+
<v>Integer = integer() </v>
</type>
<desc>
@@ -439,7 +461,7 @@
</type>
<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
+ 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>
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
@@ -580,7 +602,7 @@
<type>
<v>Type = rsa</v>
<v>CipherText = binary()</v>
- <v>PrivateKey = rsa_private()</v>
+ <v>PrivateKey = rsa_private() | engine_key_ref()</v>
<v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v>
<v>PlainText = binary()</v>
</type>
@@ -594,7 +616,22 @@
</p>
</desc>
</func>
-
+
+ <func>
+ <name>privkey_to_pubkey(Type, EnginePrivateKeyRef) -> PublicKey</name>
+ <fsummary>Fetches a public key from an Engine stored private key.</fsummary>
+ <type>
+ <v>Type = rsa | dss</v>
+ <v>EnginePrivateKeyRef = engine_key_ref()</v>
+ <v>PublicKey = rsa_public() | dss_public()</v>
+ </type>
+ <desc>
+ <p>Fetches the corresponding public key from a private key stored in an Engine.
+ The key must be of the type indicated by the Type parameter.
+ </p>
+ </desc>
+ </func>
+
<func>
<name>private_encrypt(Type, PlainText, PrivateKey, Padding) -> CipherText</name>
<fsummary>Encrypts PlainText using the private Key.</fsummary>
@@ -605,7 +642,7 @@
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>PrivateKey = rsa_private() | engine_key_ref()</v>
<v>Padding = rsa_pkcs1_padding | rsa_no_padding</v>
<v>CipherText = binary()</v>
</type>
@@ -624,7 +661,7 @@
<type>
<v>Type = rsa</v>
<v>CipherText = binary()</v>
- <v>PublicKey = rsa_public() </v>
+ <v>PublicKey = rsa_public() | engine_key_ref()</v>
<v>Padding = rsa_pkcs1_padding | rsa_no_padding</v>
<v>PlainText = binary()</v>
</type>
@@ -649,7 +686,7 @@
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>PublicKey = rsa_public() | engine_key_ref()</v>
<v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v>
<v>CipherText = binary()</v>
</type>
@@ -702,7 +739,7 @@
signed or it is the hashed value of "cleartext" i.e. the
digest (plaintext).</d>
<v>DigestType = rsa_digest_type() | dss_digest_type() | ecdsa_digest_type()</v>
- <v>Key = rsa_private() | dss_private() | [ecdh_private(),ecdh_params()]</v>
+ <v>Key = rsa_private() | dss_private() | [ecdh_private(),ecdh_params()] | engine_key_ref()</v>
<v>Options = sign_options()</v>
</type>
<desc>
@@ -893,7 +930,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre>
or it is the hashed value of "cleartext" i.e. the digest (plaintext).</d>
<v>DigestType = rsa_digest_type() | dss_digest_type() | ecdsa_digest_type()</v>
<v>Signature = binary()</v>
- <v>Key = rsa_public() | dss_public() | [ecdh_public(),ecdh_params()]</v>
+ <v>Key = rsa_public() | dss_public() | [ecdh_public(),ecdh_params()] | engine_key_ref()</v>
<v>Options = sign_options()</v>
</type>
<desc>
@@ -905,6 +942,175 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre>
</desc>
</func>
+ <!-- Engine functions -->
+ <func>
+ <name>engine_get_all_methods() -> Result</name>
+ <fsummary>Return list of all possible engine methods</fsummary>
+ <type>
+ <v>Result = [EngineMethod::atom()]</v>
+ </type>
+ <desc>
+ <p>
+ Returns a list of all possible engine methods.
+ </p>
+ <p>
+ May throw exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_load(EngineId, PreCmds, PostCmds) -> Result</name>
+ <fsummary>Dynamical load an encryption engine</fsummary>
+ <type>
+ <v>EngineId = unicode:chardata()</v>
+ <v>PreCmds, PostCmds = [{unicode:chardata(), unicode:chardata()}]</v>
+ <v>Result = {ok, Engine::term()} | {error, Reason::term()}</v>
+ </type>
+ <desc>
+ <p>
+ Loads the OpenSSL engine given by <c>EngineId</c> if it is available and then returns ok and
+ an engine handle. This function is the same as calling <c>engine_load/4</c> with
+ <c>EngineMethods</c> set to a list of all the possible methods. An error tuple is
+ returned if the engine can't be loaded.
+ </p>
+ <p>
+ The function throws a badarg if the parameters are in wrong format.
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_load(EngineId, PreCmds, PostCmds, EngineMethods) -> Result</name>
+ <fsummary>Dynamical load an encryption engine</fsummary>
+ <type>
+ <v>EngineId = unicode:chardata()</v>
+ <v>PreCmds, PostCmds = [{unicode:chardata(), unicode:chardata()}]</v>
+ <v>EngineMethods = [engine_method_type()]</v>
+ <v>Result = {ok, Engine::term()} | {error, Reason::term()}</v>
+ </type>
+ <desc>
+ <p>
+ Loads the OpenSSL engine given by <c>EngineId</c> if it is available and then returns ok and
+ an engine handle. An error tuple is returned if the engine can't be loaded.
+ </p>
+ <p>
+ The function throws a badarg if the parameters are in wrong format.
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_unload(Engine) -> Result</name>
+ <fsummary>Dynamical load an encryption engine</fsummary>
+ <type>
+ <v>Engine = term()</v>
+ <v>Result = ok | {error, Reason::term()}</v>
+ </type>
+ <desc>
+ <p>
+ Unloads the OpenSSL engine given by <c>EngineId</c>.
+ An error tuple is returned if the engine can't be unloaded.
+ </p>
+ <p>
+ The function throws a badarg if the parameter is in wrong format.
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_list() -> Result</name>
+ <fsummary>List the known engine ids</fsummary>
+ <type>
+ <v>Result = [EngineId::unicode:chardata()]</v>
+ </type>
+ <desc>
+ <p>List the id's of all engines in OpenSSL's internal list.</p>
+ <p>
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_ctrl_cmd_string(Engine, CmdName, CmdArg) -> Result</name>
+ <fsummary>Sends ctrl commands to an OpenSSL engine</fsummary>
+ <type>
+ <v>Engine = term()</v>
+ <v>CmdName = unicode:chardata()</v>
+ <v>CmdArg = unicode:chardata()</v>
+ <v>Result = ok | {error, Reason::term()}</v>
+ </type>
+ <desc>
+ <p>
+ Sends ctrl commands to the OpenSSL engine given by <c>Engine</c>.
+ This function is the same as calling <c>engine_ctrl_cmd_string/4</c> with
+ <c>Optional</c> set to <c>false</c>.
+ </p>
+ <p>
+ The function throws a badarg if the parameters are in wrong format.
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_ctrl_cmd_string(Engine, CmdName, CmdArg, Optional) -> Result</name>
+ <fsummary>Sends ctrl commands to an OpenSSL engine</fsummary>
+ <type>
+ <v>Engine = term()</v>
+ <v>CmdName = unicode:chardata()</v>
+ <v>CmdArg = unicode:chardata()</v>
+ <v>Optional = boolean()</v>
+ <v>Result = ok | {error, Reason::term()}</v>
+ </type>
+ <desc>
+ <p>
+ Sends ctrl commands to the OpenSSL engine given by <c>Engine</c>.
+ <c>Optional</c> is a boolean argument that can relax the semantics of the function.
+ If set to <c>true</c> it will only return failure if the ENGINE supported the given
+ command name but failed while executing it, if the ENGINE doesn't support the command
+ name it will simply return success without doing anything. In this case we assume
+ the user is only supplying commands specific to the given ENGINE so we set this to
+ <c>false</c>.
+ </p>
+ <p>
+ The function throws a badarg if the parameters are in wrong format.
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ </desc>
+ </func>
+
</funcs>
<!-- Maybe put this in the users guide -->
@@ -979,4 +1185,3 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre>
<!-- </p> -->
<!-- </section> -->
</erlref>
-
diff --git a/lib/crypto/doc/src/engine_keys.xml b/lib/crypto/doc/src/engine_keys.xml
new file mode 100644
index 0000000000..38714fed8a
--- /dev/null
+++ b/lib/crypto/doc/src/engine_keys.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2017</year><year>2017</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+ </legalnotice>
+ <title>Engine Stored Keys</title>
+ <prepared>Hans Nilsson</prepared>
+ <date>2017-11-10</date>
+ <file>engine_keys.xml</file>
+ </header>
+ <p>
+ <marker id="engine_key"></marker>
+ This chapter describes the support in the crypto application for using public and private keys stored in encryption engines.
+ </p>
+
+ <section>
+ <title>Background</title>
+ <p>
+ <url href="https://www.openssl.org/">OpenSSL</url> exposes an Engine API, which makes
+ it possible to plug in alternative implementations for some of the cryptographic
+ operations implemented by OpenSSL.
+ See the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ for details and how to load an Engine.
+ </p>
+ <p>
+ An engine could among other tasks provide a storage for
+ private or public keys. Such a storage could be made safer than the normal file system. Thoose techniques are not
+ described in this User's Guide. Here we concentrate on how to use private or public keys stored in
+ such an engine.
+ </p>
+ <p>
+ The storage engine must call <c>ENGINE_set_load_privkey_function</c> and <c>ENGINE_set_load_pubkey_function</c>.
+ See the OpenSSL cryptolib's <url href="https://www.openssl.org/docs/manpages.html">manpages</url>.
+ </p>
+ <p>
+ OTP/Crypto requires that the user provides two or three items of information about the key. The application used
+ by the user is usually on a higher level, for example in
+ <seealso marker="ssl:ssl#key_option_def">SSL</seealso>. If using
+ the crypto application directly, it is required that:
+ </p>
+ <list>
+ <item>an Engine is loaded, see the chapter on <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ or the <seealso marker="crypto:crypto#engine_load-3">Reference Manual</seealso>
+ </item>
+ <item>a reference to a key in the Engine is available. This should be an Erlang string or binary and depends
+ on the Engine loaded
+ </item>
+ <item>an Erlang map is constructed with the Engine reference, the key reference and possibly a key passphrase if
+ needed by the Engine. See the <seealso marker="crypto:crypto#engine_key_ref_type">Reference Manual</seealso> for
+ details of the map.
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Use Cases</title>
+ <section>
+ <title>Sign with an engine stored private key</title>
+ <p>
+ This example shows how to construct a key reference that is used in a sign operation.
+ The actual key is stored in the engine that is loaded at prompt 1.
+ </p>
+ <code>
+1> {ok, EngineRef} = crypto:engine_load(....).
+...
+{ok,#Ref&lt;0.2399045421.3028942852.173962>}
+2> PrivKey = #{engine => EngineRef,
+ key_id => "id of the private key in Engine"}.
+...
+3> Signature = crypto:sign(rsa, sha, &lt;&lt;"The message">>, PrivKey).
+&lt;&lt;65,6,125,254,54,233,84,77,83,63,168,28,169,214,121,76,
+ 207,177,124,183,156,185,160,243,36,79,125,230,231,...>>
+ </code>
+ </section>
+
+ <section>
+ <title>Verify with an engine stored public key</title>
+ <p>
+ Here the signature and message in the last example is verifyed using the public key.
+ The public key is stored in an engine, only to exemplify that it is possible. The public
+ key could of course be handled openly as usual.
+ </p>
+ <code>
+4> PublicKey = #{engine => EngineRef,
+ key_id => "id of the public key in Engine"}.
+...
+5> crypto:verify(rsa, sha, &lt;&lt;"The message">>, Signature, PublicKey).
+true
+6>
+ </code>
+ </section>
+
+ <section>
+ <title>Using a password protected private key</title>
+ <p>
+ The same example as the first sign example, except that a password protects the key down in the Engine.
+ </p>
+ <code>
+6> PrivKeyPwd = #{engine => EngineRef,
+ key_id => "id of the pwd protected private key in Engine",
+ password => "password"}.
+...
+7> crypto:sign(rsa, sha, &lt;&lt;"The message">>, PrivKeyPwd).
+&lt;&lt;140,80,168,101,234,211,146,183,231,190,160,82,85,163,
+ 175,106,77,241,141,120,72,149,181,181,194,154,175,76,
+ 223,...>>
+8>
+ </code>
+
+ </section>
+
+ </section>
+</chapter>
diff --git a/lib/crypto/doc/src/engine_load.xml b/lib/crypto/doc/src/engine_load.xml
new file mode 100644
index 0000000000..e5c3f5d561
--- /dev/null
+++ b/lib/crypto/doc/src/engine_load.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2017</year><year>2017</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+ </legalnotice>
+ <title>Engine Load</title>
+ <prepared>Lars Thorsén</prepared>
+ <date>2017-08-22</date>
+ <file>engine_load.xml</file>
+ </header>
+ <p>
+ <marker id="engine_load"></marker>
+ This chapter describes the support for loading encryption engines in the crypto application.
+ </p>
+
+ <section>
+ <title>Background</title>
+ <p>
+ OpenSSL exposes an Engine API, which makes it possible to plug in alternative
+ implementations for some or all of the cryptographic operations implemented by OpenSSL.
+ When configured appropriately, OpenSSL calls the engine's implementation of these
+ operations instead of its own.
+ </p>
+ <p>
+ Typically, OpenSSL engines provide a hardware implementation of specific cryptographic
+ operations. The hardware implementation usually offers improved performance over its
+ software-based counterpart, which is known as cryptographic acceleration.
+ </p>
+ </section>
+
+ <section>
+ <title>Use Cases</title>
+ <section>
+ <title>Dynamically load an engine from default directory</title>
+ <p>
+ If the engine is located in the OpenSSL/LibreSSL installation <c>engines</c> directory.
+ </p>
+ <code>
+1> {ok, Engine} = crypto:engine_load(&lt;&lt;"otp_test_engine">>, [], []).
+ {ok, #Ref}</code>
+ <note>
+ <p>The file name requirement on the engine dynamic library can differ between SSL versions.</p>
+ </note>
+ </section>
+
+ <section>
+ <title>Load an engine with the dynamic engine</title>
+ <p>
+ Load an engine with the help of the dynamic engine by giving the path to the library.
+ </p>
+ <code>
+ 2> {ok, Engine} = crypto:engine_load(&lt;&lt;"dynamic">>,
+ [{&lt;&lt;"SO_PATH">>,
+ &lt;&lt;"/some/path/otp_test_engine.so">>},
+ {&lt;&lt;"ID">>, &lt;&lt;"MD5">>},
+ &lt;&lt;"LOAD">>],
+ []).
+ {ok, #Ref}</code>
+ <note>
+ <p>The dynamic engine is not supported in LibreSSL from version 2.2.1</p>
+ </note>
+ </section>
+
+ <section>
+ <title>Load an engine and replace some methods</title>
+ <p>
+ Load an engine with the help of the dynamic engine and just
+ replace some engine methods.
+ </p>
+ <code>
+ 3> Methods = crypto:engine_get_all_methods() -- [engine_method_dh,engine_method_rand,
+engine_method_ciphers,engine_method_digests, engine_method_store,
+engine_method_pkey_meths, engine_method_pkey_asn1_meths].
+[engine_method_rsa,engine_method_dsa,
+ engine_method_ecdh,engine_method_ecdsa]
+ 4> {ok, Engine} = crypto:engine_load(&lt;&lt;"dynamic">>,
+ [{&lt;&lt;"SO_PATH">>,
+ &lt;&lt;"/some/path/otp_test_engine.so">>},
+ {&lt;&lt;"ID">>, &lt;&lt;"MD5">>},
+ &lt;&lt;"LOAD">>],
+ [],
+ Methods).
+ {ok, #Ref}</code>
+ </section>
+
+ <section>
+ <title>List all engines currently loaded</title>
+ <code>
+ 5> crypto:engine_list().
+[&lt;&lt;"dynamic">>, &lt;&lt;"MD5">>]</code>
+ </section>
+
+ </section>
+</chapter>
diff --git a/lib/crypto/doc/src/fascicules.xml b/lib/crypto/doc/src/fascicules.xml
deleted file mode 100644
index cbc266cd30..0000000000
--- a/lib/crypto/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="usersguide" href="usersguide_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="release_notes" href="release_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/crypto/doc/src/note.gif b/lib/crypto/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/crypto/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 9376e6f649..dbeb886d7b 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,76 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The compatibility function <c>void HMAC_CTX_free</c> in
+ <c>crypto.c</c> erroneously tried to return a value.</p>
+ <p>
+ Own Id: OTP-14720</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Rewrite public and private key encode/decode with EVP
+ api. New RSA padding options added. This is a modified
+ half of PR-838.</p>
+ <p>
+ Own Id: OTP-14446</p>
+ </item>
+ <item>
+ <p>
+ The crypto API is extended to use private/public keys
+ stored in an Engine for sign/verify or encrypt/decrypt
+ operations.</p>
+ <p>
+ The ssl application provides an API to use this new
+ engine concept in TLS.</p>
+ <p>
+ Own Id: OTP-14448</p>
+ </item>
+ <item>
+ <p> Add support to plug in alternative implementations
+ for some or all of the cryptographic operations supported
+ by the OpenSSL Engine API. When configured appropriately,
+ OpenSSL calls the engine's implementation of these
+ operations instead of its own. </p>
+ <p>
+ Own Id: OTP-14567</p>
+ </item>
+ <item>
+ <p>
+ Replaced a call of the OpenSSL deprecated function
+ <c>DH_generate_parameters</c> in <c>crypto.c</c>.</p>
+ <p>
+ Own Id: OTP-14639</p>
+ </item>
+ <item>
+ <p>
+ Documentation added about how to use keys stored in an
+ Engine.</p>
+ <p>
+ Own Id: OTP-14735 Aux Id: OTP-14448 </p>
+ </item>
+ <item>
+ <p> Add engine_ ctrl_cmd_string/3,4 the OpenSSL Engine
+ support in crypto. </p>
+ <p>
+ Own Id: OTP-14801</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 4.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/doc/src/usersguide.xml b/lib/crypto/doc/src/usersguide.xml
index 7971aefff4..e2ba1fe160 100644
--- a/lib/crypto/doc/src/usersguide.xml
+++ b/lib/crypto/doc/src/usersguide.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>Crypto User's Guide</title>
@@ -48,5 +48,6 @@
</description>
<xi:include href="licenses.xml"/>
<xi:include href="fips.xml"/>
+ <xi:include href="engine_load.xml"/>
+ <xi:include href="engine_keys.xml"/>
</part>
-
diff --git a/lib/crypto/doc/src/warning.gif b/lib/crypto/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/crypto/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/crypto/src/Makefile b/lib/crypto/src/Makefile
index aea8a5a71c..edad0e6b61 100644
--- a/lib/crypto/src/Makefile
+++ b/lib/crypto/src/Makefile
@@ -39,8 +39,7 @@ MODULES= \
crypto \
crypto_ec_curves
-HRL_FILES=
-
+HRL_FILES=
ERL_FILES= $(MODULES:%=%.erl)
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
@@ -56,16 +55,16 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -DCRYPTO_VSN=\"$(VSN)\" -Werror
+ERL_COMPILE_FLAGS += -DCRYPTO_VSN=\"$(VSN)\" -Werror -I../include
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-debug opt valgrind: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+debug opt valgrind: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
@@ -78,7 +77,7 @@ docs:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
@@ -89,10 +88,3 @@ release_spec: opt
$(APPUP_TARGET) "$(RELSYSDIR)/ebin"
release_docs_spec:
-
-
-
-
-
-
-
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 1df05462c9..1a1b4f98b5 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -42,12 +42,28 @@
-export([public_encrypt/4, private_decrypt/4]).
-export([private_encrypt/4, public_decrypt/4]).
-export([dh_generate_parameters/2, dh_check/1]). %% Testing see
+-export([privkey_to_pubkey/2]).
-export([ec_curve/1, ec_curves/0]).
-export([rand_seed/1]).
+%% Engine
+-export([
+ engine_get_all_methods/0,
+ engine_load/3,
+ engine_load/4,
+ engine_unload/1,
+ engine_list/0,
+ engine_ctrl_cmd_string/3,
+ engine_ctrl_cmd_string/4
+ ]).
+
+-export_type([engine_ref/0,
+ key_id/0,
+ password/0
+ ]).
-%% Private. For tests.
--export([packed_openssl_version/4]).
+%% Private. For tests.
+-export([packed_openssl_version/4, engine_methods_convert_to_bitmask/2, get_test_engine/0]).
-deprecated({rand_uniform, 2, next_major_release}).
@@ -420,46 +436,66 @@ sign(Algorithm, Type, Data, Key, Options) ->
Signature -> Signature
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, [rsa, 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, [rsa, BinMesg,Key, Padding]);
- Sign -> Sign
- end.
+-type key_id() :: string() | binary() .
+-type password() :: string() | binary() .
-%% 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, [rsa, BinMesg,Key, Padding]);
- Sign -> Sign
- end.
+-type engine_key_ref() :: #{engine := engine_ref(),
+ key_id := key_id(),
+ password => password(),
+ term() => term()
+ }.
+
+-type pk_algs() :: rsa | ecdsa | dss .
+-type pk_key() :: engine_key_ref() | [integer() | binary()] .
+-type pk_opt() :: list() | rsa_padding() .
+
+-spec public_encrypt(pk_algs(), binary(), pk_key(), pk_opt()) -> binary().
+-spec public_decrypt(pk_algs(), binary(), pk_key(), pk_opt()) -> binary().
+-spec private_encrypt(pk_algs(), binary(), pk_key(), pk_opt()) -> binary().
+-spec private_decrypt(pk_algs(), binary(), pk_key(), pk_opt()) -> binary().
+
+public_encrypt(Algorithm, In, Key, Options) when is_list(Options) ->
+ case pkey_crypt_nif(Algorithm, In, format_pkey(Algorithm, Key), Options, false, true) of
+ error -> erlang:error(encrypt_failed, [Algorithm, In, Key, Options]);
+ notsup -> erlang:error(notsup);
+ Out -> Out
+ end;
+%% Backwards compatible
+public_encrypt(Algorithm = rsa, In, Key, Padding) when is_atom(Padding) ->
+ public_encrypt(Algorithm, In, Key, [{rsa_padding, Padding}]).
+
+private_decrypt(Algorithm, In, Key, Options) when is_list(Options) ->
+ case pkey_crypt_nif(Algorithm, In, format_pkey(Algorithm, Key), Options, true, false) of
+ error -> erlang:error(decrypt_failed, [Algorithm, In, Key, Options]);
+ notsup -> erlang:error(notsup);
+ Out -> Out
+ end;
+%% Backwards compatible
+private_decrypt(Algorithm = rsa, In, Key, Padding) when is_atom(Padding) ->
+ private_decrypt(Algorithm, In, Key, [{rsa_padding, Padding}]).
+
+private_encrypt(Algorithm, In, Key, Options) when is_list(Options) ->
+ case pkey_crypt_nif(Algorithm, In, format_pkey(Algorithm, Key), Options, true, true) of
+ error -> erlang:error(encrypt_failed, [Algorithm, In, Key, Options]);
+ notsup -> erlang:error(notsup);
+ Out -> Out
+ end;
+%% Backwards compatible
+private_encrypt(Algorithm = rsa, In, Key, Padding) when is_atom(Padding) ->
+ private_encrypt(Algorithm, In, Key, [{rsa_padding, Padding}]).
+
+public_decrypt(Algorithm, In, Key, Options) when is_list(Options) ->
+ case pkey_crypt_nif(Algorithm, In, format_pkey(Algorithm, Key), Options, false, false) of
+ error -> erlang:error(decrypt_failed, [Algorithm, In, Key, Options]);
+ notsup -> erlang:error(notsup);
+ Out -> Out
+ end;
+%% Backwards compatible
+public_decrypt(Algorithm = rsa, In, Key, Padding) when is_atom(Padding) ->
+ public_decrypt(Algorithm, In, Key, [{rsa_padding, Padding}]).
-%% 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, [rsa, BinMesg,Key, Padding]);
- Sign -> Sign
- end.
%%
%% XOR - xor to iolists and return a binary
@@ -559,10 +595,174 @@ compute_key(ecdh, Others, My, Curve) ->
nif_curve_params(Curve),
ensure_int_as_bin(My)).
+%%======================================================================
+%% Engine functions
+%%======================================================================
+%%----------------------------------------------------------------------
+%% Function: engine_get_all_methods/0
+%%----------------------------------------------------------------------
+-type engine_method_type() :: engine_method_rsa | engine_method_dsa | engine_method_dh |
+ engine_method_rand | engine_method_ecdh | engine_method_ecdsa |
+ engine_method_ciphers | engine_method_digests | engine_method_store |
+ engine_method_pkey_meths | engine_method_pkey_asn1_meths |
+ engine_method_ec.
+
+-type engine_ref() :: term().
+
+-spec engine_get_all_methods() ->
+ [engine_method_type()].
+engine_get_all_methods() ->
+ notsup_to_error(engine_get_all_methods_nif()).
+
+%%----------------------------------------------------------------------
+%% Function: engine_load/3
+%%----------------------------------------------------------------------
+-spec engine_load(EngineId::unicode:chardata(),
+ PreCmds::[{unicode:chardata(), unicode:chardata()}],
+ PostCmds::[{unicode:chardata(), unicode:chardata()}]) ->
+ {ok, Engine::engine_ref()} | {error, Reason::term()}.
+engine_load(EngineId, PreCmds, PostCmds) when is_list(PreCmds), is_list(PostCmds) ->
+ engine_load(EngineId, PreCmds, PostCmds, engine_get_all_methods()).
+
+%%----------------------------------------------------------------------
+%% Function: engine_load/4
+%%----------------------------------------------------------------------
+-spec engine_load(EngineId::unicode:chardata(),
+ PreCmds::[{unicode:chardata(), unicode:chardata()}],
+ PostCmds::[{unicode:chardata(), unicode:chardata()}],
+ EngineMethods::[engine_method_type()]) ->
+ {ok, Engine::term()} | {error, Reason::term()}.
+engine_load(EngineId, PreCmds, PostCmds, EngineMethods) when is_list(PreCmds),
+ is_list(PostCmds) ->
+ try
+ ok = notsup_to_error(engine_load_dynamic_nif()),
+ case notsup_to_error(engine_by_id_nif(ensure_bin_chardata(EngineId))) of
+ {ok, Engine} ->
+ ok = engine_load_1(Engine, PreCmds, PostCmds, EngineMethods),
+ {ok, Engine};
+ {error, Error1} ->
+ {error, Error1}
+ end
+ catch
+ throw:Error2 ->
+ Error2
+ end.
+
+engine_load_1(Engine, PreCmds, PostCmds, EngineMethods) ->
+ try
+ ok = engine_nif_wrapper(engine_ctrl_cmd_strings_nif(Engine, ensure_bin_cmds(PreCmds), 0)),
+ ok = engine_nif_wrapper(engine_add_nif(Engine)),
+ ok = engine_nif_wrapper(engine_init_nif(Engine)),
+ engine_load_2(Engine, PostCmds, EngineMethods),
+ ok
+ catch
+ throw:Error ->
+ %% The engine couldn't initialise, release the structural reference
+ ok = engine_free_nif(Engine),
+ throw(Error)
+ end.
+
+engine_load_2(Engine, PostCmds, EngineMethods) ->
+ try
+ ok = engine_nif_wrapper(engine_ctrl_cmd_strings_nif(Engine, ensure_bin_cmds(PostCmds), 0)),
+ [ok = engine_nif_wrapper(engine_register_nif(Engine, engine_method_atom_to_int(Method))) ||
+ Method <- EngineMethods],
+ ok
+ catch
+ throw:Error ->
+ %% The engine registration failed, release the functional reference
+ ok = engine_finish_nif(Engine),
+ throw(Error)
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: engine_unload/1
+%%----------------------------------------------------------------------
+-spec engine_unload(Engine::term()) ->
+ ok | {error, Reason::term()}.
+engine_unload(Engine) ->
+ engine_unload(Engine, engine_get_all_methods()).
+
+-spec engine_unload(Engine::term(), EngineMethods::[engine_method_type()]) ->
+ ok | {error, Reason::term()}.
+engine_unload(Engine, EngineMethods) ->
+ try
+ [ok = engine_nif_wrapper(engine_unregister_nif(Engine, engine_method_atom_to_int(Method))) ||
+ Method <- EngineMethods],
+ ok = engine_nif_wrapper(engine_remove_nif(Engine)),
+ %% Release the functional reference from engine_init_nif
+ ok = engine_nif_wrapper(engine_finish_nif(Engine)),
+ %% Release the structural reference from engine_by_id_nif
+ ok = engine_nif_wrapper(engine_free_nif(Engine))
+ catch
+ throw:Error ->
+ Error
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: engine_list/0
+%%----------------------------------------------------------------------
+-spec engine_list() ->
+ [EngineId::binary()].
+engine_list() ->
+ case notsup_to_error(engine_get_first_nif()) of
+ {ok, <<>>} ->
+ [];
+ {ok, Engine} ->
+ case notsup_to_error(engine_get_id_nif(Engine)) of
+ {ok, <<>>} ->
+ engine_list(Engine, []);
+ {ok, EngineId} ->
+ engine_list(Engine, [EngineId])
+ end
+ end.
+
+engine_list(Engine0, IdList) ->
+ case notsup_to_error(engine_get_next_nif(Engine0)) of
+ {ok, <<>>} ->
+ lists:reverse(IdList);
+ {ok, Engine1} ->
+ case notsup_to_error(engine_get_id_nif(Engine1)) of
+ {ok, <<>>} ->
+ engine_list(Engine1, IdList);
+ {ok, EngineId} ->
+ engine_list(Engine1, [EngineId |IdList])
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: engine_ctrl_cmd_string/3
+%%----------------------------------------------------------------------
+-spec engine_ctrl_cmd_string(Engine::term(),
+ CmdName::unicode:chardata(),
+ CmdArg::unicode:chardata()) ->
+ ok | {error, Reason::term()}.
+engine_ctrl_cmd_string(Engine, CmdName, CmdArg) ->
+ engine_ctrl_cmd_string(Engine, CmdName, CmdArg, false).
+
+%%----------------------------------------------------------------------
+%% Function: engine_ctrl_cmd_string/4
+%%----------------------------------------------------------------------
+-spec engine_ctrl_cmd_string(Engine::term(),
+ CmdName::unicode:chardata(),
+ CmdArg::unicode:chardata(),
+ Optional::boolean()) ->
+ ok | {error, Reason::term()}.
+engine_ctrl_cmd_string(Engine, CmdName, CmdArg, Optional) ->
+ case engine_ctrl_cmd_strings_nif(Engine,
+ ensure_bin_cmds([{CmdName, CmdArg}]),
+ bool_to_int(Optional)) of
+ ok ->
+ ok;
+ notsup ->
+ erlang:error(notsup);
+ {error, Error} ->
+ {error, Error}
+ end.
+
%%--------------------------------------------------------------------
%%% On load
%%--------------------------------------------------------------------
-
on_load() ->
LibBaseName = "crypto",
PrivDir = code:priv_dir(crypto),
@@ -622,12 +822,12 @@ path2bin(Path) when is_list(Path) ->
end.
%%--------------------------------------------------------------------
-%%% Internal functions
+%%% Internal functions
%%--------------------------------------------------------------------
max_bytes() ->
?MAX_BYTES_TO_NIF.
-notsup_to_error(notsup) ->
+notsup_to_error(notsup) ->
erlang:error(notsup);
notsup_to_error(Other) ->
Other.
@@ -751,7 +951,7 @@ do_stream_decrypt({rc4, State0}, Data) ->
%%
-%% AES - in counter mode (CTR) with state maintained for multi-call streaming
+%% AES - in counter mode (CTR) with state maintained for multi-call streaming
%%
-type ctr_state() :: { iodata(), binary(), binary(), integer() } | binary().
@@ -760,11 +960,11 @@ do_stream_decrypt({rc4, State0}, Data) ->
{ ctr_state(), binary() }.
-spec aes_ctr_stream_decrypt(ctr_state(), binary()) ->
{ ctr_state(), binary() }.
-
+
aes_ctr_stream_init(_Key, _IVec) -> ?nif_stub.
aes_ctr_stream_encrypt(_State, _Data) -> ?nif_stub.
aes_ctr_stream_decrypt(_State, _Cipher) -> ?nif_stub.
-
+
%%
%% RC4 - symmetric stream cipher
%%
@@ -849,22 +1049,22 @@ pkey_verify_nif(_Algorithm, _Type, _Data, _Signature, _Key, _Options) -> ?nif_st
rsa_generate_key_nif(_Bits, _Exp) -> ?nif_stub.
%% DH Diffie-Hellman functions
-%%
+%%
%% Generate (and check) Parameters is not documented because they are implemented
%% for testing (and offline parameter generation) only.
-%% From the openssl doc:
+%% From the openssl doc:
%% DH_generate_parameters() may run for several hours before finding a suitable prime.
-%% Thus dh_generate_parameters may in this implementation block
+%% Thus dh_generate_parameters may in this implementation block
%% the emulator for several hours.
%%
-%% usage: dh_generate_parameters(1024, 2 or 5) ->
+%% usage: dh_generate_parameters(1024, 2 or 5) ->
%% [Prime=mpint(), SharedGenerator=mpint()]
dh_generate_parameters(PrimeLen, Generator) ->
case dh_generate_parameters_nif(PrimeLen, Generator) of
error -> erlang:error(generation_failed, [PrimeLen,Generator]);
Ret -> Ret
- end.
+ end.
dh_generate_parameters_nif(_PrimeLen, _Generator) -> ?nif_stub.
@@ -890,6 +1090,24 @@ ec_curves() ->
ec_curve(X) ->
crypto_ec_curves:curve(X).
+
+privkey_to_pubkey(Alg, EngineMap) when Alg == rsa; Alg == dss; Alg == ecdsa ->
+ try privkey_to_pubkey_nif(Alg, format_pkey(Alg,EngineMap))
+ of
+ [_|_]=L -> map_ensure_bin_as_int(L);
+ X -> X
+ catch
+ error:badarg when Alg==ecdsa ->
+ {error, notsup};
+ error:badarg ->
+ {error, not_found};
+ error:notsup ->
+ {error, notsup}
+ end.
+
+privkey_to_pubkey_nif(_Alg, _EngineMap) -> ?nif_stub.
+
+
%%
%% EC
%%
@@ -957,6 +1175,19 @@ ensure_int_as_bin(Int) when is_integer(Int) ->
ensure_int_as_bin(Bin) ->
Bin.
+map_ensure_bin_as_int(List) when is_list(List) ->
+ lists:map(fun ensure_bin_as_int/1, List).
+
+ensure_bin_as_int(Bin) when is_binary(Bin) ->
+ bin_to_int(Bin);
+ensure_bin_as_int(E) ->
+ E.
+
+format_pkey(_Alg, #{engine:=_, key_id:=T}=M) when is_binary(T) -> format_pwd(M);
+format_pkey(_Alg, #{engine:=_, key_id:=T}=M) when is_list(T) -> format_pwd(M#{key_id:=list_to_binary(T)});
+format_pkey(_Alg, #{engine:=_ }=M) -> error({bad_key_id, M});
+format_pkey(_Alg, #{}=M) -> error({bad_engine_map, M});
+%%%
format_pkey(rsa, Key) ->
map_ensure_int_as_bin(Key);
format_pkey(ecdsa, [Key, Curve]) ->
@@ -966,19 +1197,20 @@ format_pkey(dss, Key) ->
format_pkey(_, Key) ->
Key.
+format_pwd(#{password := Pwd}=M) when is_list(Pwd) -> M#{password := list_to_binary(Pwd)};
+format_pwd(M) -> M.
+
%%--------------------------------------------------------------------
%%
-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'.
-rsa_public_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub.
-
-rsa_private_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub.
+pkey_crypt_nif(_Algorithm, _In, _Key, _Options, _IsPrivate, _IsEncrypt) -> ?nif_stub.
%% large integer in a binary with 32bit length
%% MP representaion (SSH2)
mpint(X) when X < 0 -> mpint_neg(X);
mpint(X) -> mpint_pos(X).
-
+
-define(UINT32(X), X:32/unsigned-big-integer).
@@ -986,7 +1218,7 @@ mpint_neg(X) ->
Bin = int_to_bin_neg(X, []),
Sz = byte_size(Bin),
<<?UINT32(Sz), Bin/binary>>.
-
+
mpint_pos(X) ->
Bin = int_to_bin_pos(X, []),
<<MSB,_/binary>> = Bin,
@@ -1008,7 +1240,6 @@ erlint(<<MPIntSize:32/integer,MPIntValue/binary>>) ->
%%
mod_exp_nif(_Base,_Exp,_Mod,_bin_hdr) -> ?nif_stub.
-
%%%----------------------------------------------------------------
%% 9470495 == V(0,9,8,zh).
%% 268435615 == V(1,0,0,i).
@@ -1019,3 +1250,95 @@ packed_openssl_version(MAJ, MIN, FIX, P0) ->
P1 = atom_to_list(P0),
P = lists:sum([C-$a||C<-P1]),
((((((((MAJ bsl 8) bor MIN) bsl 8 ) bor FIX) bsl 8) bor (P+1)) bsl 4) bor 16#f).
+
+%%--------------------------------------------------------------------
+%% Engine nifs
+engine_by_id_nif(_EngineId) -> ?nif_stub.
+engine_init_nif(_Engine) -> ?nif_stub.
+engine_finish_nif(_Engine) -> ?nif_stub.
+engine_free_nif(_Engine) -> ?nif_stub.
+engine_load_dynamic_nif() -> ?nif_stub.
+engine_ctrl_cmd_strings_nif(_Engine, _Cmds, _Optional) -> ?nif_stub.
+engine_add_nif(_Engine) -> ?nif_stub.
+engine_remove_nif(_Engine) -> ?nif_stub.
+engine_register_nif(_Engine, _EngineMethod) -> ?nif_stub.
+engine_unregister_nif(_Engine, _EngineMethod) -> ?nif_stub.
+engine_get_first_nif() -> ?nif_stub.
+engine_get_next_nif(_Engine) -> ?nif_stub.
+engine_get_id_nif(_Engine) -> ?nif_stub.
+engine_get_all_methods_nif() -> ?nif_stub.
+
+%%--------------------------------------------------------------------
+%% Engine internals
+engine_nif_wrapper(ok) ->
+ ok;
+engine_nif_wrapper(notsup) ->
+ erlang:error(notsup);
+engine_nif_wrapper({error, Error}) ->
+ throw({error, Error}).
+
+ensure_bin_chardata(CharData) when is_binary(CharData) ->
+ CharData;
+ensure_bin_chardata(CharData) ->
+ unicode:characters_to_binary(CharData).
+
+ensure_bin_cmds(CMDs) ->
+ ensure_bin_cmds(CMDs, []).
+
+ensure_bin_cmds([], Acc) ->
+ lists:reverse(Acc);
+ensure_bin_cmds([{Key, Value} |CMDs], Acc) ->
+ ensure_bin_cmds(CMDs, [{ensure_bin_chardata(Key), ensure_bin_chardata(Value)} | Acc]);
+ensure_bin_cmds([Key | CMDs], Acc) ->
+ ensure_bin_cmds(CMDs, [{ensure_bin_chardata(Key), <<"">>} | Acc]).
+
+engine_methods_convert_to_bitmask([], BitMask) ->
+ BitMask;
+engine_methods_convert_to_bitmask(engine_method_all, _BitMask) ->
+ 16#FFFF;
+engine_methods_convert_to_bitmask(engine_method_none, _BitMask) ->
+ 16#0000;
+engine_methods_convert_to_bitmask([M |Ms], BitMask) ->
+ engine_methods_convert_to_bitmask(Ms, BitMask bor engine_method_atom_to_int(M)).
+
+bool_to_int(true) -> 1;
+bool_to_int(false) -> 0.
+
+engine_method_atom_to_int(engine_method_rsa) -> 16#0001;
+engine_method_atom_to_int(engine_method_dsa) -> 16#0002;
+engine_method_atom_to_int(engine_method_dh) -> 16#0004;
+engine_method_atom_to_int(engine_method_rand) -> 16#0008;
+engine_method_atom_to_int(engine_method_ecdh) -> 16#0010;
+engine_method_atom_to_int(engine_method_ecdsa) -> 16#0020;
+engine_method_atom_to_int(engine_method_ciphers) -> 16#0040;
+engine_method_atom_to_int(engine_method_digests) -> 16#0080;
+engine_method_atom_to_int(engine_method_store) -> 16#0100;
+engine_method_atom_to_int(engine_method_pkey_meths) -> 16#0200;
+engine_method_atom_to_int(engine_method_pkey_asn1_meths) -> 16#0400;
+engine_method_atom_to_int(engine_method_ec) -> 16#0800;
+engine_method_atom_to_int(X) ->
+ erlang:error(badarg, [X]).
+
+get_test_engine() ->
+ Type = erlang:system_info(system_architecture),
+ LibDir = filename:join([code:priv_dir(crypto), "lib"]),
+ ArchDir = filename:join([LibDir, Type]),
+ case filelib:is_dir(ArchDir) of
+ true -> check_otp_test_engine(ArchDir);
+ false -> check_otp_test_engine(LibDir)
+ end.
+
+check_otp_test_engine(LibDir) ->
+ case filelib:wildcard("otp_test_engine*", LibDir) of
+ [] ->
+ {error, notexist};
+ [LibName] ->
+ LibPath = filename:join(LibDir,LibName),
+ case filelib:is_file(LibPath) of
+ true ->
+ {ok, unicode:characters_to_binary(LibPath)};
+ false ->
+ {error, notexist}
+ end
+ end.
+
diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile
index 138081d386..e046a25338 100644
--- a/lib/crypto/test/Makefile
+++ b/lib/crypto/test/Makefile
@@ -7,7 +7,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES = \
blowfish_SUITE \
- crypto_SUITE
+ crypto_SUITE \
+ engine_SUITE
ERL_FILES= $(MODULES:%=%.erl)
@@ -27,7 +28,7 @@ RELSYSDIR = $(RELEASE_PATH)/crypto_test
# FLAGS
# ----------------------------------------------------
ERL_MAKE_FLAGS +=
-ERL_COMPILE_FLAGS +=
+ERL_COMPILE_FLAGS += +nowarn_export_all
EBIN = .
MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile)
@@ -77,7 +78,7 @@ release_spec:
release_tests_spec: $(TEST_TARGET)
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)"
- @tar cfh - crypto_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ @tar cfh - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
chmod -R u+w "$(RELSYSDIR)"
release_docs_spec:
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 88f13d766c..6dab459df6 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -122,10 +122,15 @@ groups() ->
{sha512, [], [hash, hmac]},
{rsa, [], [sign_verify,
public_encrypt,
+ private_encrypt,
generate
]},
- {dss, [], [sign_verify]},
- {ecdsa, [], [sign_verify]},
+ {dss, [], [sign_verify
+ %% Does not work yet: ,public_encrypt, private_encrypt
+ ]},
+ {ecdsa, [], [sign_verify
+ %% Does not work yet: ,public_encrypt, private_encrypt
+ ]},
{dh, [], [generate_compute]},
{ecdh, [], [compute, generate]},
{srp, [], [generate_compute]},
@@ -193,7 +198,7 @@ init_per_suite(Config) ->
%% This is NOT how you want to do seeding, it is just here
%% to make the tests pass. Check your OS manual for how you
%% really want to seed.
- {H,M,L} = erlang:now(),
+ {H,M,L} = erlang:timestamp(),
Bin = <<H:24,M:20,L:20>>,
crypto:rand_seed(<< <<Bin/binary>> || _ <- lists:seq(1,16) >>),
Config
@@ -439,10 +444,16 @@ sign_verify(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
public_encrypt() ->
- [{doc, "Test public_encrypt/decrypt and private_encrypt/decrypt functions. "}].
+ [{doc, "Test public_encrypt/decrypt "}].
public_encrypt(Config) when is_list(Config) ->
Params = proplists:get_value(pub_priv_encrypt, Config),
- lists:foreach(fun do_public_encrypt/1, Params),
+ lists:foreach(fun do_public_encrypt/1, Params).
+
+%%--------------------------------------------------------------------
+private_encrypt() ->
+ [{doc, "Test private_encrypt/decrypt functions. "}].
+private_encrypt(Config) when is_list(Config) ->
+ Params = proplists:get_value(pub_priv_encrypt, Config),
lists:foreach(fun do_private_encrypt/1, Params).
%%--------------------------------------------------------------------
@@ -819,7 +830,7 @@ do_private_encrypt({_Type, _Public, _Private, _Msg, rsa_pkcs1_oaep_padding}) ->
ok; %% Not supported by openssl
do_private_encrypt({Type, Public, Private, Msg, Padding}) ->
PrivEcn = (catch crypto:private_encrypt(Type, Msg, Private, Padding)),
- case crypto:public_decrypt(rsa, PrivEcn, Public, Padding) of
+ case crypto:public_decrypt(Type, PrivEcn, Public, Padding) of
Msg ->
ok;
Other ->
@@ -1233,7 +1244,9 @@ group_config(dss = Type, Config) ->
SignVerify = [{Type, Hash, Public, Private, Msg}
|| Hash <- DssHashs,
lists:member(Hash, SupportedHashs)],
- [{sign_verify, SignVerify} | Config];
+ MsgPubEnc = <<"7896345786348 Asldi">>,
+ PubPrivEnc = [{dss, Public, Private, MsgPubEnc, []}],
+ [{sign_verify, SignVerify}, {pub_priv_encrypt, PubPrivEnc} | Config];
group_config(ecdsa = Type, Config) ->
{Private, Public} = ec_key_named(),
@@ -1243,7 +1256,9 @@ group_config(ecdsa = Type, Config) ->
SignVerify = [{Type, Hash, Public, Private, Msg}
|| Hash <- DssHashs,
lists:member(Hash, SupportedHashs)],
- [{sign_verify, SignVerify} | Config];
+ MsgPubEnc = <<"7896345786348 Asldi">>,
+ PubPrivEnc = [{ecdsa, Public, Private, MsgPubEnc, []}],
+ [{sign_verify, SignVerify}, {pub_priv_encrypt, PubPrivEnc} | Config];
group_config(srp, Config) ->
GenerateCompute = [srp3(), srp6(), srp6a(), srp6a_smaller_prime()],
[{generate_compute, GenerateCompute} | Config];
diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl
new file mode 100644
index 0000000000..f206f967c7
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE.erl
@@ -0,0 +1,660 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(engine_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds, 10}}
+ ].
+
+all() ->
+ [
+ get_all_possible_methods,
+ engine_load_all_methods,
+ engine_load_some_methods,
+ bad_arguments,
+ unknown_engine,
+ pre_command_fail_bad_value,
+ pre_command_fail_bad_key,
+ failed_engine_init,
+ ctrl_cmd_string,
+ ctrl_cmd_string_optional,
+ {group, engine_stored_key}
+ ].
+
+groups() ->
+ [{engine_stored_key, [],
+ [sign_verify_rsa,
+ sign_verify_dsa,
+ sign_verify_ecdsa,
+ sign_verify_rsa_pwd,
+ sign_verify_rsa_pwd_bad_pwd,
+ priv_encrypt_pub_decrypt_rsa,
+ priv_encrypt_pub_decrypt_rsa_pwd,
+ pub_encrypt_priv_decrypt_rsa,
+ pub_encrypt_priv_decrypt_rsa_pwd,
+ get_pub_from_priv_key_rsa,
+ get_pub_from_priv_key_rsa_pwd,
+ get_pub_from_priv_key_rsa_pwd_no_pwd,
+ get_pub_from_priv_key_rsa_pwd_bad_pwd,
+ get_pub_from_priv_key_dsa,
+ get_pub_from_priv_key_ecdsa
+ ]}].
+
+
+init_per_suite(Config) ->
+ try crypto:start() of
+ ok ->
+ Config;
+ {error,{already_started,crypto}} ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+init_per_group(engine_stored_key, Config) ->
+ case load_storage_engine(Config) of
+ {ok, E} ->
+ KeyDir = key_dir(Config),
+ [{storage_engine,E}, {storage_dir,KeyDir} | Config];
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {error, notsup} ->
+ {skip, "Engine not supported on this OpenSSL version"};
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"};
+ Other ->
+ ct:log("Engine load failed: ~p",[Other]),
+ {fail, "Engine load failed"}
+ end;
+init_per_group(_Group, Config0) ->
+ Config0.
+
+end_per_group(engine_stored_key, Config) ->
+ case proplists:get_value(storage_engine, Config) of
+ undefined ->
+ ok;
+ E ->
+ ok = crypto:engine_unload(E)
+ end;
+end_per_group(_, _) ->
+ ok.
+
+%%--------------------------------------------------------------------
+init_per_testcase(_Case, Config) ->
+ Config.
+end_per_testcase(_Case, _Config) ->
+ ok.
+
+%%-------------------------------------------------------------------------
+%% Test cases starts here.
+%%-------------------------------------------------------------------------
+get_all_possible_methods() ->
+ [{doc, "Just fetch all possible engine methods supported."}].
+
+get_all_possible_methods(Config) when is_list(Config) ->
+ try
+ List = crypto:engine_get_all_methods(),
+ ct:log("crypto:engine_get_all_methods() -> ~p\n", [List]),
+ ok
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+engine_load_all_methods()->
+ [{doc, "Use a dummy md5 engine that does not implement md5"
+ "but rather returns a static binary to test that crypto:engine_load "
+ "functions works."}].
+
+engine_load_all_methods(Config) when is_list(Config) ->
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ try
+ Md5Hash1 = <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
+ Md5Hash1 = crypto:hash(md5, "Don't panic"),
+ Md5Hash2 = <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ []) of
+ {ok, E} ->
+ case crypto:hash(md5, "Don't panic") of
+ Md5Hash1 ->
+ ct:fail(fail_to_load_still_original_engine);
+ Md5Hash2 ->
+ ok;
+ _ ->
+ ct:fail(fail_to_load_engine)
+ end,
+ ok = crypto:engine_unload(E),
+ case crypto:hash(md5, "Don't panic") of
+ Md5Hash2 ->
+ ct:fail(fail_to_unload_still_test_engine);
+ Md5Hash1 ->
+ ok;
+ _ ->
+ ct:fail(fail_to_unload_engine)
+ end;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end
+ end.
+
+engine_load_some_methods()->
+ [{doc, "Use a dummy md5 engine that does not implement md5"
+ "but rather returns a static binary to test that crypto:engine_load "
+ "functions works."}].
+
+engine_load_some_methods(Config) when is_list(Config) ->
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ try
+ Md5Hash1 = <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
+ Md5Hash1 = crypto:hash(md5, "Don't panic"),
+ Md5Hash2 = <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
+ EngineMethods = crypto:engine_get_all_methods() --
+ [engine_method_dh,engine_method_rand,
+ engine_method_ciphers, engine_method_store,
+ engine_method_pkey_meths, engine_method_pkey_asn1_meths],
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ [],
+ EngineMethods) of
+ {ok, E} ->
+ case crypto:hash(md5, "Don't panic") of
+ Md5Hash1 ->
+ ct:fail(fail_to_load_engine_still_original);
+ Md5Hash2 ->
+ ok;
+ _ ->
+ ct:fail(fail_to_load_engine)
+ end,
+ ok = crypto:engine_unload(E),
+ case crypto:hash(md5, "Don't panic") of
+ Md5Hash2 ->
+ ct:fail(fail_to_unload_still_test_engine);
+ Md5Hash1 ->
+ ok;
+ _ ->
+ ct:fail(fail_to_unload_engine)
+ end;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end
+ end.
+
+%%-------------------------------------------------------------------------
+%% Error cases
+bad_arguments()->
+ [{doc, "Test different arguments in bad format."}].
+
+bad_arguments(Config) when is_list(Config) ->
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ try
+ try
+ crypto:engine_load(fail_engine, [], [])
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
+ crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ 1,
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ [])
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
+ crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {'ID', <<"MD5">>},
+ <<"LOAD">>],
+ [])
+ catch
+ error:badarg ->
+ ok
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end
+ end.
+
+unknown_engine() ->
+ [{doc, "Try to load a non existent engine."}].
+
+unknown_engine(Config) when is_list(Config) ->
+ try
+ {error, bad_engine_id} = crypto:engine_load(<<"fail_engine">>, [], []),
+ ok
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+pre_command_fail_bad_value() ->
+ [{doc, "Test pre command due to bad value"}].
+
+pre_command_fail_bad_value(Config) when is_list(Config) ->
+ DataDir = unicode:characters_to_binary(code:priv_dir(crypto)),
+ try
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>,
+ <<DataDir/binary, <<"/libfail_engine.so">>/binary >>},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ []) of
+ {error, ctrl_cmd_failed} ->
+ ok;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+pre_command_fail_bad_key() ->
+ [{doc, "Test pre command due to bad key"}].
+
+pre_command_fail_bad_key(Config) when is_list(Config) ->
+ try
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_WRONG_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ []) of
+ {error, ctrl_cmd_failed} ->
+ ok;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+failed_engine_init()->
+ [{doc, "Test failing engine init due to missed pre command"}].
+
+failed_engine_init(Config) when is_list(Config) ->
+ try
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>}],
+ []) of
+ {error, add_engine_failed} ->
+ ok;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+
+ctrl_cmd_string()->
+ [{doc, "Test that a not known optional ctrl comand do not fail"}].
+ctrl_cmd_string(Config) when is_list(Config) ->
+ try
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ []) of
+ {ok, E} ->
+ case crypto:engine_ctrl_cmd_string(E, <<"TEST">>, <<"17">>) of
+ ok ->
+ ct:fail(fail_ctrl_cmd_should_fail);
+ {error,ctrl_cmd_failed} ->
+ ok
+ end,
+ ok = crypto:engine_unload(E);
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+ctrl_cmd_string_optional()->
+ [{doc, "Test that a not known optional ctrl comand do not fail"}].
+ctrl_cmd_string_optional(Config) when is_list(Config) ->
+ try
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ []) of
+ {ok, E} ->
+ case crypto:engine_ctrl_cmd_string(E, <<"TEST">>, <<"17">>, true) of
+ ok ->
+ ok;
+ _ ->
+ ct:fail(fail_ctrl_cmd_string)
+ end,
+ ok = crypto:engine_unload(E);
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+%%%----------------------------------------------------------------
+%%% Pub/priv key storage tests. Thoose are for testing the crypto.erl
+%%% support for using priv/pub keys stored in an engine.
+
+sign_verify_rsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key.pem")},
+ sign_verify(rsa, sha, Priv, Pub).
+
+sign_verify_dsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "dsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "dsa_public_key.pem")},
+ sign_verify(dss, sha, Priv, Pub).
+
+sign_verify_ecdsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "ecdsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "ecdsa_public_key.pem")},
+ sign_verify(ecdsa, sha, Priv, Pub).
+
+sign_verify_rsa_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key_pwd.pem"),
+ password => "password"},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key_pwd.pem")},
+ sign_verify(rsa, sha, Priv, Pub).
+
+sign_verify_rsa_pwd_bad_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key_pwd.pem"),
+ password => "Bad password"},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key_pwd.pem")},
+ try sign_verify(rsa, sha, Priv, Pub) of
+ _ -> {fail, "PWD prot pubkey sign succeded with no pwd!"}
+ catch
+ error:badarg -> ok
+ end.
+
+priv_encrypt_pub_decrypt_rsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key.pem")},
+ priv_enc_pub_dec(rsa, Priv, Pub, rsa_pkcs1_padding).
+
+priv_encrypt_pub_decrypt_rsa_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key_pwd.pem"),
+ password => "password"},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key_pwd.pem")},
+ priv_enc_pub_dec(rsa, Priv, Pub, rsa_pkcs1_padding).
+
+pub_encrypt_priv_decrypt_rsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key.pem")},
+ pub_enc_priv_dec(rsa, Pub, Priv, rsa_pkcs1_padding).
+
+pub_encrypt_priv_decrypt_rsa_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key_pwd.pem"),
+ password => "password"},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key_pwd.pem")},
+ pub_enc_priv_dec(rsa, Pub, Priv, rsa_pkcs1_padding).
+
+get_pub_from_priv_key_rsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key.pem")},
+ case crypto:privkey_to_pubkey(rsa, Priv) of
+ {error, not_found} ->
+ {fail, "Key not found"};
+ {error, notsup} ->
+ {skip, "RSA not supported"};
+ {error, Error} ->
+ {fail, {wrong_error,Error}};
+ Pub ->
+ ct:log("rsa Pub = ~p",[Pub]),
+ sign_verify(rsa, sha, Priv, Pub)
+ end.
+
+get_pub_from_priv_key_rsa_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key_pwd.pem"),
+ password => "password"},
+ case crypto:privkey_to_pubkey(rsa, Priv) of
+ {error, not_found} ->
+ {fail, "Key not found"};
+ {error, notsup} ->
+ {skip, "RSA not supported"};
+ {error, Error} ->
+ {fail, {wrong_error,Error}};
+ Pub ->
+ ct:log("rsa Pub = ~p",[Pub]),
+ sign_verify(rsa, sha, Priv, Pub)
+ end.
+
+get_pub_from_priv_key_rsa_pwd_no_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key_pwd.pem")},
+ case crypto:privkey_to_pubkey(rsa, Priv) of
+ {error, not_found} ->
+ ok;
+ {error, notsup} ->
+ {skip, "RSA not supported"};
+ {error, Error} ->
+ {fail, {wrong_error,Error}};
+ Pub ->
+ ct:log("rsa Pub = ~p",[Pub]),
+ {fail, "PWD prot pubkey fetch succeded although no pwd!"}
+ end.
+
+get_pub_from_priv_key_rsa_pwd_bad_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key_pwd.pem"),
+ password => "Bad password"},
+ case crypto:privkey_to_pubkey(rsa, Priv) of
+ {error, not_found} ->
+ ok;
+ {error, notsup} ->
+ {skip, "RSA not supported"};
+ {error, Error} ->
+ {fail, {wrong_error,Error}};
+ Pub ->
+ ct:log("rsa Pub = ~p",[Pub]),
+ {fail, "PWD prot pubkey fetch succeded with bad pwd!"}
+ end.
+
+get_pub_from_priv_key_dsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "dsa_private_key.pem")},
+ case crypto:privkey_to_pubkey(dss, Priv) of
+ {error, not_found} ->
+ {fail, "Key not found"};
+ {error, notsup} ->
+ {skip, "DSA not supported"};
+ {error, Error} ->
+ {fail, {wrong_error,Error}};
+ Pub ->
+ ct:log("dsa Pub = ~p",[Pub]),
+ sign_verify(dss, sha, Priv, Pub)
+ end.
+
+get_pub_from_priv_key_ecdsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "ecdsa_private_key.pem")},
+ case crypto:privkey_to_pubkey(ecdsa, Priv) of
+ {error, not_found} ->
+ {fail, "Key not found"};
+ {error, notsup} ->
+ {skip, "ECDSA not supported"};
+ {error, Error} ->
+ {fail, {wrong_error,Error}};
+ Pub ->
+ ct:log("ecdsa Pub = ~p",[Pub]),
+ sign_verify(ecdsa, sha, Priv, Pub)
+ end.
+
+%%%================================================================
+%%% Help for engine_stored_pub_priv_keys* test cases
+%%%
+load_storage_engine(_Config) ->
+ case crypto:get_test_engine() of
+ {ok, Engine} ->
+ try crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ <<"LOAD">>],
+ [])
+ catch
+ error:notsup ->
+ {error, notsup}
+ end;
+
+ {error, Error} ->
+ {error, Error}
+ end.
+
+
+key_dir(Config) ->
+ DataDir = unicode:characters_to_binary(proplists:get_value(data_dir, Config)),
+ filename:join(DataDir, "pkcs8").
+
+
+engine_ref(Config) ->
+ proplists:get_value(storage_engine, Config).
+
+key_id(Config, File) ->
+ filename:join(proplists:get_value(storage_dir,Config), File).
+
+pubkey_alg_supported(Alg) ->
+ lists:member(Alg,
+ proplists:get_value(public_keys, crypto:supports())).
+
+
+pub_enc_priv_dec(Alg, KeyEnc, KeyDec, Padding) ->
+ case pubkey_alg_supported(Alg) of
+ true ->
+ PlainText = <<"Hej på dig">>,
+ CryptoText = crypto:public_encrypt(Alg, PlainText, KeyEnc, Padding),
+ case crypto:private_decrypt(Alg, CryptoText, KeyDec, Padding) of
+ PlainText -> ok;
+ _ -> {fail, "Encrypt-decrypt error"}
+ end;
+ false ->
+ {skip, lists:concat([Alg," is not supported by cryptolib"])}
+ end.
+
+priv_enc_pub_dec(Alg, KeyEnc, KeyDec, Padding) ->
+ case pubkey_alg_supported(Alg) of
+ true ->
+ PlainText = <<"Hej på dig">>,
+ CryptoText = crypto:private_encrypt(Alg, PlainText, KeyEnc, Padding),
+ case crypto:public_decrypt(Alg, CryptoText, KeyDec, Padding) of
+ PlainText -> ok;
+ _ -> {fail, "Encrypt-decrypt error"}
+ end;
+ false ->
+ {skip, lists:concat([Alg," is not supported by cryptolib"])}
+ end.
+
+sign_verify(Alg, Sha, KeySign, KeyVerify) ->
+ case pubkey_alg_supported(Alg) of
+ true ->
+ PlainText = <<"Hej på dig">>,
+ Signature = crypto:sign(Alg, Sha, PlainText, KeySign),
+ case crypto:verify(Alg, Sha, PlainText, Signature, KeyVerify) of
+ true -> ok;
+ _ -> {fail, "Sign-verify error"}
+ end;
+ false ->
+ {skip, lists:concat([Alg," is not supported by cryptolib"])}
+ end.
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_private_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_private_key.pem
new file mode 100644
index 0000000000..778ffac675
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_private_key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PRIVATE KEY-----
+MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAMyitTMR7vPbpqyAXJpqnB0AhFwQ
+F87IE+JKFl5bD/MSkhhRV5sM73HUU1ooXY0FjhZ+cdLUCATuZR5ta4ydANqWIcAB
+gX3IwF1B4zf5SXEKTWkUYneL9dOKtiZLtoG28swrk8xMxwX+0fLHkltCEj6FiTW9
+PFrv8GmIfV6DjcI9AhUAqXWbb3RtoN9Ld28fVMhGZrj3LJUCgYEAwnxGHGBMpJaF
+2w7zAw3jHjL8PMYlV6vnufGHQlwF0ZUXJxRsvagMb/X1qACTu2VPYEVoLQGM3cfH
+EhHoQmvSXGAyTfR7Bmn3gf1n/s/DcFbdZduUCZ/rAyIrfd0eSbc1I+kZk85UCsKK
+w/IYdlqcuYa4Cgm2TapT5uEMqH4jhzEEFgIULh8swEUWmU8aJNWsrWl4eCiuUUg=
+-----END PRIVATE KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_public_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_public_key.pem
new file mode 100644
index 0000000000..0fa5428828
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_public_key.pem
@@ -0,0 +1,12 @@
+-----BEGIN PUBLIC KEY-----
+MIIBtzCCASwGByqGSM44BAEwggEfAoGBAMyitTMR7vPbpqyAXJpqnB0AhFwQF87I
+E+JKFl5bD/MSkhhRV5sM73HUU1ooXY0FjhZ+cdLUCATuZR5ta4ydANqWIcABgX3I
+wF1B4zf5SXEKTWkUYneL9dOKtiZLtoG28swrk8xMxwX+0fLHkltCEj6FiTW9PFrv
+8GmIfV6DjcI9AhUAqXWbb3RtoN9Ld28fVMhGZrj3LJUCgYEAwnxGHGBMpJaF2w7z
+Aw3jHjL8PMYlV6vnufGHQlwF0ZUXJxRsvagMb/X1qACTu2VPYEVoLQGM3cfHEhHo
+QmvSXGAyTfR7Bmn3gf1n/s/DcFbdZduUCZ/rAyIrfd0eSbc1I+kZk85UCsKKw/IY
+dlqcuYa4Cgm2TapT5uEMqH4jhzEDgYQAAoGAXPygOFYdeKgfLmuIC303cESYXvic
+e2GNJomv8vaWLZmbLVVDfwA1fNsuF1hZkWw8f7aYaN9iZ3yl9u4Yk4TbJKkqfJqd
+dgVt288SUqvi+NMHODUzYi9KAOXxupXffZSvdu54gKRaDuFTZ5XNcRqIJWGYlJYg
+NVHF5FPZ4Bk2FYA=
+-----END PUBLIC KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_private_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_private_key.pem
new file mode 100644
index 0000000000..a45522064f
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_private_key.pem
@@ -0,0 +1,8 @@
+-----BEGIN PRIVATE KEY-----
+MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBparGjr0KcdNrVM2J
+G0mW5ltP1QyvxDqBMyWLWo3fruRZv6Qoohl5skd1u4O+KJoM/UrrSTOXI/MDR7NN
+i1yl7O+hgYkDgYYABAG8K2XVsK0ahG9+HIIPwCO0pJY8ulwSTXwIjkCGyB2lpglh
+8qJmRzuyGcfRTslv8wfv0sPlT9H9PKDvgrTUL7rvQQDdOODNgVPXSecUoXoPn+X+
+eqxs77bjx+A5x0t/i3m5PfkaNPh5MZ1H/bWuOOdj2ZXZw0R4rlVc0zVrgnPU8L8S
+BQ==
+-----END PRIVATE KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_public_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_public_key.pem
new file mode 100644
index 0000000000..6d22fe43fe
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_public_key.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBvCtl1bCtGoRvfhyCD8AjtKSWPLpc
+Ek18CI5AhsgdpaYJYfKiZkc7shnH0U7Jb/MH79LD5U/R/Tyg74K01C+670EA3Tjg
+zYFT10nnFKF6D5/l/nqsbO+248fgOcdLf4t5uT35GjT4eTGdR/21rjjnY9mV2cNE
+eK5VXNM1a4Jz1PC/EgU=
+-----END PUBLIC KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key.pem
new file mode 100644
index 0000000000..ea0e3d3958
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCwwb0/ddXGXTFK
+4FLxXdV6a/WJMSoPPS55RvZIAHFsiTtvPLbJ8LxDsZ6wSVZLN0/UQ4wdWn9jftyj
+U5/IxBVG8XOtKimTMvm3/ZOzVLueGHBbrLYscRv9oL85ulTKHWgrZDu0lBX5JJTI
+v5UTCErzJRQbka9DG1GaBgDb1PlXfkzBWMwfsBZmwoC77KvCcIGCgbW/XCY03TP2
+3Tg8drvpByMStddP2FQ4fZ91qFUzPu8uhZEsqSQTFlmhgGEx7dLlky0xvu62RuAD
+RTpINpcWZtWDHTdssOqu653LwwqBY8lBopCZ/4Af8QR3ZYkQhen1YLEbVheXRuzI
+LSCZIiJNAgMBAAECggEBAJH4/fxpqQkvr2Shy33Pu1xlyhnpw01gfn/jrcKasxEq
+aC4eWup86E2TY3U8q4pkfIXU3uLi+O9HNpmflwargNLc1mY8uqb44ygiv5bLNEKE
+9k2PXcdoBfC4jxPyoNFl5cBn/7LK1TazEjiTl15na9ZPWcLG1pG5/vMPYCgsQ1sP
+8J3c4E3aaXIj9QceYxBprl490OCzieGyZlRipncz3g4UShRc/b4cycvDZOJpmAy4
+zbWTcBcSMPVPi5coF0K8UcimiqZkotfb/2RLc433i34IdsIXMM+brdq+g8rmjg5a
++oQPy02M6tFApBruEhAz8DGgaLtDY6MLtyZAt3SjXnUCgYEA1zLgamdTHOqrrmIi
+eIQBnAJiyIfcY8B9SX1OsLGYFCHiPVwgUY35B2c7MavMsGcExJhtE+uxU7o5djtM
+R6r9cRHOXJ6EQwa8OwzzPqbM17/YqNDeK39bc9WOFUqRWrhDhVMPy6z8rmZr73mG
+IUC7mBNx/1GBdVYXIlsXzC96dI8CgYEA0kUAhz6I5nyPa70NDEUYHLHf3IW1BCmE
+UoVbraSePJtIEY/IqFx7oDuFo30d4n5z+8ICCtyid1h/Cp3mf3akOiqltYUfgV1G
+JgcEjKKYWEnO7cfFyO7LB7Y3GYYDJNy6EzVWPiwTGk9ZTfFJEESmHC45Unxgd17m
+Dx/R58rFgWMCgYBQXQWFdtSI5fH7C1bIHrPjKNju/h2FeurOuObcAVZDnmu4cmD3
+U8d9xkVKxVeJQM99A1coq0nrdI3k4zwXP3mp8fZYjDHkPe2pN6rW6L9yiohEcsuk
+/siON1/5/4DMmidM8LnjW9R45HLGWWGHpX7oyco2iJ+Jy/6Tq+T1MX3PbQKBgQCm
+hdsbQJ0u3CrBSmFQ/E9SOlRt0r4+45pVuCOY6yweF2QF9HcXTtbhWQJHLclDHJ5C
+Ha18aKuKFN3XzKFFBPKe1jOSBDGlQ/dQGnKx5fr8wMdObM3oiaTlIJuWbRmEUgJT
+QARjDIi8Z2b0YUhZx+Q9oSXoe3PyVYehJrQX+/BavQKBgQCIr7Zp0rQPbfqcTL+M
+OYHUoNcb14f9f8hXeXHQOqVpsGwxGdRQAU9wbx/4+obKB5xIkzBsVNcJwavisNja
+hegnGjTB/9Hc4m+5bMGwH0bhS2eQO4o+YYM2ypDmFQqDLRfFUlZ5PVHffm/aA9+g
+GanNBCsmtoHtV6CJ1UZ7NmBuIA==
+-----END PRIVATE KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key_pwd.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key_pwd.pem
new file mode 100644
index 0000000000..501662fc35
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key_pwd.pem
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIh888Iq6gxuMCAggA
+MBQGCCqGSIb3DQMHBAic/11YZ8Nt5gSCBMjG/Jb4qiMoBS50iQvHXqcETPE+0NBr
+jhsn9w94LkdRBstMPAsoKmY98Er96Rnde/NfmqlU9CupKTkd7Ce5poBf72Y6KMED
+cPURyjbGRFsu6x9skXB2obhyKYEqAEF2oQAg4Qbe5v1qXBIgDuC/NgiJnM+w2zCZ
+LkHSZB2/NmcnvDzcgPF7TM8pTO23xCJ33m37qjfWvHsgocVqZmL9wQ4+wr/NMYjJ
+pJvX1OHW1vBsZsXh40WchalYRSB1VeO368QfsE8coRJztqbMzdce9EQdMB6Q6jlO
+cetd3moLIoMP4I7HW0/SgokbycTbRiYSvRyU1TGc2WbW6BrFZV24IckcnnVUFatf
+6HKUcaYLG68dJcRgs5QMGkcmgVvlddENHFmHZlo0eym/xSiUl/AT8/5odscm6ML8
+wW5sneax+TF4J2eYmiN7yjAUCodXVTNYNDVKo6uUhntlymbM0o4UitVIbPIfTDHl
+sxJAEZ7vpuPqeNMxUk6G6zipuEjqsVbnuFSBSZmgKiGYcifRPUmqqINa3DdS4WVx
+xaPWdHbHVRD//ze3h/FsA+1lIE5q2kUE0xXseJA1ISog++kJp14XeaaL2j/tx3Ob
+OsbcaOAD/IUw/ItDt9kn0qzfnar7sS0Wov8AmJQxHmH7Lm93jHTLM05yE0AR/eBr
+Mig2ZdC+9OqVC+GPuBkRjSs8NpltQIDroz6EV9IMwPwXm0szSYoyoPLmlHJUdnLs
+ZUef+au6hYkEJBrvuisagnq5eT/fCV3hsjD7yODebNU2CmBTo6X2PRx/xsBHRMWl
+QkoM9PBdSCnKv6HpHl4pchuoqU2NpFjN0BCaad6aHfZSTnqgzK4bEh1oO6dI8/rB
+/eh71JyFFG5J4xbpaqz5Su01V1iwU5leK5bDwqals4M4+ZGHGciou7qnXUmX2fJl
+r6DlMUa/xy+A2ZG0NuZR05yk2oB3+KVNMgp6zFty3XaxwoNtc8GTLtLnBnIh2rlP
+mE1+I65LRWwrNQalPeOAUrYuEzhyp2Df7a8Ykas5PUH7MGR/S0Ge/dLxtE2bJuK4
+znbLAsGhvo/SbNxYqIp6D4iDtd3va6yUGncy41paA/vTKFVvXZDrXcwJQYYCVOGT
+OwdzNuozU8Dc7oxsd8oakfC46kvmVaOrGvZbm56PFfprcaL/Hslska5xxEni/eZe
+WRxZbCBhAVqS1pn5zkDQVUe9uFlR/x39Qi01HIlKLBsjpSs6qQsFArMe8hgXmXLG
+xP+dyVuOE18NzSewdEjeqSRKIM7Qi8EOjZsI4HdSRBY7bh9VhmaVXDZiCSf33TTE
+3y8nimzQAeuGoYg6WqHmWWC2Qnpki2HlaIH/ayXEyQWkP/qvg61e8ovdg9Fy8JOO
+0AacXVt5zj0q00AW5bKx7usi4NIjZedi86hUm6H19aBm7r86BKjwYTEI/GOcdrbV
+9HC/8ayOimgwiAG3gq+aLioWym+Z6KnsbVd7XReVbvM/InQx54WA2y5im0A+/c67
+oQFFPV84XGX9waeqv/K4Wzkm6HW+qVAEM67482VGOf0PVrlQMno6dOotT/Y7ljoZ
+2iz0LmN9yylJnLPDrr1i6gzbs5OhhUgbF5LI2YP2wWdCZTl/DrKSIvQZWl8U+tw3
+ciA=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key.pem
new file mode 100644
index 0000000000..d3fb5a2cc9
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsMG9P3XVxl0xSuBS8V3V
+emv1iTEqDz0ueUb2SABxbIk7bzy2yfC8Q7GesElWSzdP1EOMHVp/Y37co1OfyMQV
+RvFzrSopkzL5t/2Ts1S7nhhwW6y2LHEb/aC/ObpUyh1oK2Q7tJQV+SSUyL+VEwhK
+8yUUG5GvQxtRmgYA29T5V35MwVjMH7AWZsKAu+yrwnCBgoG1v1wmNN0z9t04PHa7
+6QcjErXXT9hUOH2fdahVMz7vLoWRLKkkExZZoYBhMe3S5ZMtMb7utkbgA0U6SDaX
+FmbVgx03bLDqruudy8MKgWPJQaKQmf+AH/EEd2WJEIXp9WCxG1YXl0bsyC0gmSIi
+TQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key_pwd.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key_pwd.pem
new file mode 100644
index 0000000000..f74361cead
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key_pwd.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxquo1Na8C+kjeW0YESGm
+vE1bgNW9xh+SQjU1fv/97ePK8mQW2zO1h/vUNz23pfZAKjQu3rlFW/VgGJQ0LgCs
+8Gr/HbMwNcCJzuFMePUrnWn/qBeR7OKUZCJ3E1pp4kwsTdGDDO7jPtNzKf0bdKlg
+G2GHfZWhUediRX8NsRg12X1odVPuRGVRsyJ952YODk9PFjK7pro7Ynf3Icx7di9d
+PXL5vEcKSRdomXvt1rgM8XVHES94RQqoz60ZhfV2JnPfa9V8qu0KaGntpEr7p4rQ
+5BSiLFPjPOArjsD5tKyo8ldKCdQjLfisEp7AetfMjLPVVPw9o/SmCjDxsYWTVRQ2
+tQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 1dceebb4e4..da3915a4fc 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 4.1
+CRYPTO_VSN = 4.2
diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile
index 0f724b6f17..cc0b8861d3 100644
--- a/lib/debugger/doc/src/Makefile
+++ b/lib/debugger/doc/src/Makefile
@@ -41,7 +41,7 @@ XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = debugger.xml i.xml int.xml
XML_PART_FILES = part.xml
-XML_CHAPTER_FILES = debugger_chapter.xml notes.xml
+XML_CHAPTER_FILES = introduction.xml debugger_chapter.xml notes.xml
BOOK_FILES = book.xml
diff --git a/lib/debugger/doc/src/fascicules.xml b/lib/debugger/doc/src/fascicules.xml
deleted file mode 100644
index 154c8a3b6d..0000000000
--- a/lib/debugger/doc/src/fascicules.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
-</fascicules>
-
diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml
index 21fe7d449d..e71746e30d 100644
--- a/lib/debugger/doc/src/notes.xml
+++ b/lib/debugger/doc/src/notes.xml
@@ -33,6 +33,21 @@
<p>This document describes the changes made to the Debugger
application.</p>
+<section><title>Debugger 4.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Do not quote variables and button names in Debugger
+ windows. The bug was introduced in Erlang/OTP 20.1. </p>
+ <p>
+ Own Id: OTP-14802</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Debugger 4.2.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/debugger/src/dbg_wx_win.erl b/lib/debugger/src/dbg_wx_win.erl
index 9f59915476..f1298154ab 100644
--- a/lib/debugger/src/dbg_wx_win.erl
+++ b/lib/debugger/src/dbg_wx_win.erl
@@ -299,7 +299,7 @@ open_help(_Parent, HelpHtmlFile) ->
%%--------------------------------------------------------------------
to_string(Atom) when is_atom(Atom) ->
- io_lib:format("~tw", [Atom]);
+ atom_to_list(Atom);
to_string(Integer) when is_integer(Integer) ->
integer_to_list(Integer);
to_string([]) -> "";
diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk
index 72cedb2240..57da7e5618 100644
--- a/lib/debugger/vsn.mk
+++ b/lib/debugger/vsn.mk
@@ -1 +1 @@
-DEBUGGER_VSN = 4.2.3
+DEBUGGER_VSN = 4.2.4
diff --git a/lib/dialyzer/doc/src/Makefile b/lib/dialyzer/doc/src/Makefile
index 8fe6cd30eb..3463b589e6 100644
--- a/lib/dialyzer/doc/src/Makefile
+++ b/lib/dialyzer/doc/src/Makefile
@@ -9,11 +9,11 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
# AB. All Rights Reserved.''
-#
+#
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -36,7 +36,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = dialyzer.xml typer.xml
-XML_PART_FILES = part.xml part_notes.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = dialyzer_chapter.xml notes.xml
BOOK_FILES = book.xml
@@ -65,9 +65,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -85,19 +85,19 @@ html: gifs $(HTML_REF_MAN_FILE)
man: $(MAN3_FILES)
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/dialyzer/doc/src/fascicules.xml b/lib/dialyzer/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/dialyzer/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/dialyzer/doc/src/note.gif b/lib/dialyzer/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/dialyzer/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index 6a6e65cb94..a1eecfb3fe 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -32,6 +32,29 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 3.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> The error message returned from Dialyzer when, for
+ example, a modified record field type is not a subtype of
+ the declared type, no longer includes a call stack. The
+ bug was introduced in Erlang/OTP 19.3. </p>
+ <p>
+ Own Id: OTP-14742</p>
+ </item>
+ <item>
+ <p> A bug relating to maps and never returning functions
+ has been fixed. </p>
+ <p>
+ Own Id: OTP-14743</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 3.2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/doc/src/part_notes.xml b/lib/dialyzer/doc/src/part_notes.xml
deleted file mode 100644
index 4a0a0af2d1..0000000000
--- a/lib/dialyzer/doc/src/part_notes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2006</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Dialyzer Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- <file>part_notes.xml</file>
- </header>
- <description>
- <p><em>Dialyzer</em> is a static analysis tool that identifies software discrepancies such as type errors, unreachable code, unnecessary tests, etc in single Erlang modules or entire (sets of) applications.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/dialyzer/doc/src/warning.gif b/lib/dialyzer/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/dialyzer/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index a4b42c9367..9993c68fed 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -165,7 +165,11 @@ analysis_start(Parent, Analysis, LegalWarnings) ->
remote_type_postprocessing(TmpCServer, Args) ->
Fun = fun() ->
- exit(remote_type_postproc(TmpCServer, Args))
+ exit(try remote_type_postproc(TmpCServer, Args) of
+ R -> R
+ catch
+ throw:{error,_}=Error -> Error
+ end)
end,
{Pid, Ref} = erlang:spawn_monitor(Fun),
dialyzer_codeserver:give_away(TmpCServer, Pid),
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 8367432ac5..ea3523a965 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -1235,6 +1235,13 @@ handle_tuple(Tree, Map, State) ->
State2 = state__add_warning(State1, ?WARN_OPAQUE,
Tree, Msg),
{State2, Map1, t_none()};
+ {error, record, ErrorPat, ErrorType, _} ->
+ Msg = {record_match,
+ [format_patterns(ErrorPat),
+ format_type(ErrorType, State1)]},
+ State2 = state__add_warning(State1, ?WARN_MATCHING,
+ Tree, Msg),
+ {State2, Map1, t_none()};
{Map2, ETypes} ->
{State1, Map2, t_tuple(ETypes)}
end
@@ -3116,7 +3123,10 @@ state__add_warning(#state{warnings = Warnings, warning_mode = true} = State,
state__remove_added_warnings(OldState, NewState) ->
#state{warnings = OldWarnings} = OldState,
#state{warnings = NewWarnings} = NewState,
- {NewWarnings -- OldWarnings, NewState#state{warnings = OldWarnings}}.
+ case NewWarnings =:= OldWarnings of
+ true -> {[], NewState};
+ false -> {NewWarnings -- OldWarnings, NewState#state{warnings = OldWarnings}}
+ end.
state__add_warnings(Warns, #state{warnings = Warnings} = State) ->
State#state{warnings = Warns ++ Warnings}.
@@ -3433,19 +3443,19 @@ state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) ->
{Fun, Sig, Contract, LocalRet}.
forward_args(Fun, ArgTypes, #state{work = Work, fun_tab = FunTab} = State) ->
- {OldArgTypes, OldOut, Fixpoint} =
+ {NewArgTypes, OldOut, Fixpoint} =
case dict:find(Fun, FunTab) of
- {ok, {not_handled, {OldArgTypes0, OldOut0}}} ->
- {OldArgTypes0, OldOut0, false};
+ {ok, {not_handled, {_OldArgTypesAreNone, OldOut0}}} ->
+ {ArgTypes, OldOut0, false};
{ok, {OldArgTypes0, OldOut0}} ->
- {OldArgTypes0, OldOut0,
- t_is_subtype(t_product(ArgTypes), t_product(OldArgTypes0))}
+ NewArgTypes0 = [t_sup(X, Y) ||
+ {X, Y} <- lists:zip(ArgTypes, OldArgTypes0)],
+ {NewArgTypes0, OldOut0,
+ t_is_equal(t_product(NewArgTypes0), t_product(OldArgTypes0))}
end,
case Fixpoint of
true -> State;
false ->
- NewArgTypes = [t_sup(X, Y) ||
- {X, Y} <- lists:zip(ArgTypes, OldArgTypes)],
NewWork = add_work(Fun, Work),
?debug("~tw: forwarding args ~ts\n",
[state__lookup_name(Fun, State),
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index c4d8f45447..d03326ec97 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -41,7 +41,7 @@
t_is_float/1, t_is_fun/1,
t_is_integer/1, t_non_neg_integer/0,
t_is_list/1, t_is_nil/1, t_is_none/1, t_is_number/1,
- t_is_singleton/1,
+ t_is_singleton/1, t_is_none_or_unit/1,
t_limit/2, t_list/0, t_list/1,
t_list_elements/1, t_nonempty_list/1, t_maybe_improper_list/0,
@@ -528,13 +528,14 @@ traverse(Tree, DefinedVars, State) ->
false -> t_any();
true ->
MT = t_inf(lookup_type(MapVar, Map), t_map()),
- case t_is_none(MT) of
+ case t_is_none_or_unit(MT) of
true -> t_none();
false ->
DisjointFromKeyType =
fun(ShadowKey) ->
- t_is_none(t_inf(lookup_type(ShadowKey, Map),
- KeyType))
+ ST = t_inf(lookup_type(ShadowKey, Map),
+ KeyType),
+ t_is_none_or_unit(ST)
end,
case lists:all(DisjointFromKeyType, ShadowKeys) of
true -> t_map_get(KeyType, MT);
@@ -567,7 +568,8 @@ traverse(Tree, DefinedVars, State) ->
case cerl:is_literal(OpTree) andalso
cerl:concrete(OpTree) =:= exact of
true ->
- case t_is_none(t_inf(ShadowedKeys, KeyType)) of
+ ST = t_inf(ShadowedKeys, KeyType),
+ case t_is_none_or_unit(ST) of
true ->
t_map_put({KeyType, t_any()}, AccType);
false ->
diff --git a/lib/dialyzer/test/map_SUITE_data/results/map_anon_fun b/lib/dialyzer/test/map_SUITE_data/results/map_anon_fun
new file mode 100644
index 0000000000..cfca5b1407
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/map_anon_fun
@@ -0,0 +1,2 @@
+
+map_anon_fun.erl:4: Function g/1 will never be called
diff --git a/lib/dialyzer/test/map_SUITE_data/src/map_anon_fun.erl b/lib/dialyzer/test/map_SUITE_data/src/map_anon_fun.erl
new file mode 100644
index 0000000000..e77016d68a
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/map_anon_fun.erl
@@ -0,0 +1,9 @@
+-module(map_anon_fun).
+
+%% Not exported.
+g(A) ->
+ maps:map(fun F(K, {V, _C}) ->
+ F(K, V);
+ F(_K, _V) ->
+ #{ system => {A} }
+ end, #{}).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl
new file mode 100644
index 0000000000..44149f4199
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl
@@ -0,0 +1,15 @@
+-module(same).
+
+-export([baz/1]).
+
+-record(bar, {
+ a :: same_type:st(integer()),
+ b :: same_type:st(atom())
+ }).
+
+baz(Bar) ->
+ _ = wrap_find(0, Bar#bar.a),
+ wrap_find(0, Bar#bar.b).
+
+wrap_find(K, D) ->
+ same_type:t(K, D).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl
new file mode 100644
index 0000000000..855a5d30be
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl
@@ -0,0 +1,13 @@
+-module(same_type).
+
+-export([t/2]).
+
+-export_type([st/1]).
+
+%% When unopaqued all specializations of st/1 are equal.
+-opaque st(_A) :: {st, tuple()}.
+
+-spec t(_, st(_)) -> _.
+
+t(K, V) ->
+ {K, V}.
diff --git a/lib/dialyzer/test/plt_SUITE.erl b/lib/dialyzer/test/plt_SUITE.erl
index ebe79b2a6d..680f5b5088 100644
--- a/lib/dialyzer/test/plt_SUITE.erl
+++ b/lib/dialyzer/test/plt_SUITE.erl
@@ -9,14 +9,14 @@
-export([suite/0, all/0, build_plt/1, beam_tests/1, update_plt/1,
local_fun_same_as_callback/1,
remove_plt/1, run_plt_check/1, run_succ_typings/1,
- bad_dialyzer_attr/1, merge_plts/1]).
+ bad_dialyzer_attr/1, merge_plts/1, bad_record_type/1]).
suite() ->
[{timetrap, ?plt_timeout}].
all() -> [build_plt, beam_tests, update_plt, run_plt_check,
remove_plt, run_succ_typings, local_fun_same_as_callback,
- bad_dialyzer_attr, merge_plts].
+ bad_dialyzer_attr, merge_plts, bad_record_type].
build_plt(Config) ->
OutDir = ?config(priv_dir, Config),
@@ -283,8 +283,8 @@ bad_dialyzer_attr(Config) ->
{dialyzer_error,
"Analysis failed with error:\n" ++ Str1} =
(catch dialyzer:run(Opts)),
- P1 = string:str(Str1, "dial.erl:2: function undef/0 undefined"),
- true = P1 > 0,
+ S1 = string:find(Str1, "dial.erl:2: function undef/0 undefined"),
+ true = is_list(S1),
Prog2 = <<"-module(dial).
-dialyzer({no_return, [{undef,1,2}]}).">>,
@@ -292,9 +292,9 @@ bad_dialyzer_attr(Config) ->
{dialyzer_error,
"Analysis failed with error:\n" ++ Str2} =
(catch dialyzer:run(Opts)),
- P2 = string:str(Str2, "dial.erl:2: badly formed dialyzer "
- "attribute: {no_return,{undef,1,2}}"),
- true = P2 > 0,
+ S2 = string:find(Str2, "dial.erl:2: badly formed dialyzer "
+ "attribute: {no_return,{undef,1,2}}"),
+ true = is_list(S2),
ok.
@@ -369,6 +369,32 @@ create_plts(Mod1, Mod2, Config) ->
%% End of merge_plts().
+bad_record_type(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Source = lists:concat([bad_record_type, ".erl"]),
+ Filename = filename:join(PrivDir, Source),
+ PltFilename = dialyzer_common:plt_file(PrivDir),
+
+ Opts = [{files, [Filename]},
+ {check_plt, false},
+ {from, src_code},
+ {init_plt, PltFilename}],
+
+ Prog = <<"-module(bad_record_type).
+ -export([r/0]).
+ -record(r, {f = 3 :: integer()}).
+ -spec r() -> #r{f :: atom()}.
+ r() ->
+ #r{}.">>,
+ ok = file:write_file(Filename, Prog),
+ {dialyzer_error,
+ "Analysis failed with error:\n" ++ Str} =
+ (catch dialyzer:run(Opts)),
+ P = string:str(Str,
+ "bad_record_type.erl:4: Illegal declaration of #r{f}"),
+ true = P > 0,
+ ok.
+
erlang_beam() ->
case code:where_is_file("erlang.beam") of
non_existing ->
diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_match b/lib/dialyzer/test/small_SUITE_data/results/record_match
new file mode 100644
index 0000000000..a0dd6f560a
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/record_match
@@ -0,0 +1,3 @@
+
+record_match.erl:16: Function select/0 has no local return
+record_match.erl:17: Matching of pattern {'b_literal', 'undefined'} tagged with a record name violates the declared type of #b_local{} | #b_remote{}
diff --git a/lib/dialyzer/test/small_SUITE_data/src/abs.erl b/lib/dialyzer/test/small_SUITE_data/src/abs.erl
index 251e24cdfc..0e38c3dbb7 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/abs.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/abs.erl
@@ -5,7 +5,7 @@
-export([t/0]).
t() ->
- Fs = [fun i1/0, fun i2/0, fun i3/0, fun i4/0, fun f1/0],
+ Fs = [fun i1/0, fun i2/0, fun i3/0, fun i4/0, fun f1/0, fun erl_551/0],
_ = [catch F() || F <- Fs],
ok.
@@ -60,6 +60,13 @@ f1() ->
f1(A) ->
abs(A).
+erl_551() ->
+ accept(9),
+ accept(-3).
+
+accept(Number) when abs(Number) >= 8 -> first;
+accept(_Number) -> second.
+
-spec int() -> integer().
int() ->
diff --git a/lib/dialyzer/test/small_SUITE_data/src/bsL.erl b/lib/dialyzer/test/small_SUITE_data/src/bsL.erl
new file mode 100644
index 0000000000..b2fdc16324
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/bsL.erl
@@ -0,0 +1,13 @@
+-module(bsL).
+
+-export([t/0]).
+
+%% Found in lib/observer/test/crashdump_helper.erl.
+
+t() ->
+ Size = 60,
+ <<H:16/unit:8>> = erlang:md5(<<Size:32>>),
+ true = H < 20,
+ true = H > 2,
+ Data = ((H bsl (8*150)) div (H+7919)),
+ <<Data:Size/unit:8>>.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl b/lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl
new file mode 100644
index 0000000000..2dc00d272a
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl
@@ -0,0 +1,14 @@
+-module(erl_tar_table).
+
+%% OTP-14860, PR 1670.
+
+-export([t/0, v/0, x/0]).
+
+t() ->
+ {ok, ["file"]} = erl_tar:table("table.tar").
+
+v() ->
+ {ok, [{_,_,_,_,_,_,_}]} = erl_tar:table("table.tar", [verbose]).
+
+x() ->
+ {ok, ["file"]} = erl_tar:table("table.tar", []).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/record_match.erl b/lib/dialyzer/test/small_SUITE_data/src/record_match.erl
new file mode 100644
index 0000000000..8e9b91937f
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/record_match.erl
@@ -0,0 +1,17 @@
+-module(record_match).
+
+-export([select/0]).
+
+-record(b_literal, {val}).
+-record(b_remote, {mod,name,arity}).
+-record(b_local, {name,arity}).
+
+-type b_remote() :: #b_remote{}.
+-type b_local() :: #b_local{}.
+
+-type argument() :: b_remote() | b_local().
+
+-record(b_set, {args=[] :: [argument()]}).
+
+select() ->
+ #b_set{args=[#b_remote{},#b_literal{}]}.
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index d130b14fec..1b46f66602 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 3.2.2
+DIALYZER_VSN = 3.2.3
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index eded788419..ba4525fd20 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -43,6 +43,22 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 2.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix documentation typo: peer_down/3 was written where
+ peer_down/3 was intended.</p>
+ <p>
+ Own Id: OTP-14805</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 2.1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/diameter/doc/src/seealso.ent b/lib/diameter/doc/src/seealso.ent
index c5a53670d0..72d74c103c 100644
--- a/lib/diameter/doc/src/seealso.ent
+++ b/lib/diameter/doc/src/seealso.ent
@@ -79,7 +79,7 @@ significant.
<!ENTITY app_handle_answer '<seealso marker="diameter_app#Mod:handle_answer-4">handle_answer/4</seealso>'>
<!ENTITY app_handle_request '<seealso marker="diameter_app#Mod:handle_request-3">handle_request/3</seealso>'>
<!ENTITY app_handle_error '<seealso marker="diameter_app#Mod:handle_error-4">handle_error/4</seealso>'>
-<!ENTITY app_peer_down '<seealso marker="diameter_app#Mod:peer_down-3">peer_up/3</seealso>'>
+<!ENTITY app_peer_down '<seealso marker="diameter_app#Mod:peer_down-3">peer_down/3</seealso>'>
<!ENTITY app_peer_up '<seealso marker="diameter_app#Mod:peer_up-3">peer_up/3</seealso>'>
<!ENTITY app_pick_peer '<seealso marker="diameter_app#Mod:pick_peer-4">pick_peer/4</seealso>'>
<!ENTITY app_prepare_retransmit '<seealso marker="diameter_app#Mod:prepare_retransmit-3">prepare_retransmit/3</seealso>'>
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index d0e58e8410..7da59f8b25 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -56,7 +56,8 @@
{"2.0", [{restart_application, diameter}]}, %% 20.0
{"2.1", [{load_module, diameter_gen}, %% 20.1
{update, diameter_reg, {advanced, "2.1"}}]},
- {"2.1.1", [{load_module, diameter_gen}]}
+ {"2.1.1", [{load_module, diameter_gen}]}, %% 20.1.2
+ {"2.1.2", []} %% 20.1.3
],
[
{"0.9", [{restart_application, diameter}]},
@@ -93,6 +94,7 @@
{"1.12.2", [{restart_application, diameter}]},
{"2.0", [{restart_application, diameter}]},
{"2.1", [{restart_application, diameter}]},
- {"2.1.1", [{load_module, diameter_gen}]}
+ {"2.1.1", [{load_module, diameter_gen}]},
+ {"2.1.2", []}
]
}.
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index bfb260ed8f..0c852d75cd 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -17,5 +17,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 2.1.2
+DIAMETER_VSN = 2.1.3
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/edoc/doc/src/Makefile b/lib/edoc/doc/src/Makefile
index ce7945a1bb..ca9ea66e3c 100644
--- a/lib/edoc/doc/src/Makefile
+++ b/lib/edoc/doc/src/Makefile
@@ -9,11 +9,11 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
# AB. All Rights Reserved.''
-#
+#
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -47,7 +47,7 @@ XML_REF3_FILES = \
edoc_lib.xml \
edoc_run.xml
-XML_PART_FILES = part.xml part_notes.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = chapter.xml
XML_NOTES_FILES = notes.xml
@@ -80,10 +80,10 @@ TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -109,19 +109,19 @@ $(XML_CHAPTER_FILES): ../overview.edoc
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/edoc/doc/src/fascicules.xml b/lib/edoc/doc/src/fascicules.xml
deleted file mode 100644
index 154c8a3b6d..0000000000
--- a/lib/edoc/doc/src/fascicules.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
-</fascicules>
-
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index 96d7597d83..240789e876 100644
--- a/lib/edoc/doc/src/notes.xml
+++ b/lib/edoc/doc/src/notes.xml
@@ -32,6 +32,21 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> The map type is correctly denoted as <c>map()</c> in
+ function specifications and types. </p>
+ <p>
+ Own Id: OTP-14777</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.9.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/edoc/doc/src/part_notes.xml b/lib/edoc/doc/src/part_notes.xml
deleted file mode 100644
index 6a79435ff0..0000000000
--- a/lib/edoc/doc/src/part_notes.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2007</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>EDoc Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>EDoc</em> is the Erlang program documentation generator.
- Inspired by the Javadoc (TM) tool for the Java (TM) programming
- language, EDoc is adapted to the conventions of the Erlang world,
- and has several features not found in Javadoc.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/edoc/priv/Makefile b/lib/edoc/priv/Makefile
index 9873136201..45bff37b39 100644
--- a/lib/edoc/priv/Makefile
+++ b/lib/edoc/priv/Makefile
@@ -1,6 +1,6 @@
#
# Copyright (C) 2004, Ericsson Telecommunications
-# Author: Richard Carlsson, Bertil Karlsson
+# Author: Richard Carlsson, Bertil Karlsson
#
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -9,9 +9,6 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# Application version
# ----------------------------------------------------
include ../vsn.mk
-include ../../xmerl/vsn.mk
-include ../../syntax_tools/vsn.mk
-
# ----------------------------------------------------
# Release directory specification
@@ -23,21 +20,12 @@ RELSYSDIR = $(RELEASE_PATH)/lib/edoc-$(EDOC_VSN)
# Common Macros
#
-GEN_SCRIPT_SRC = edoc_generate.src
GEN_SCRIPT = edoc_generate
PRIV_FILES = stylesheet.css erlang.png edoc.dtd
-debug opt: $(GEN_SCRIPT)
-
-$(GEN_SCRIPT): ../vsn.mk ../../xmerl/vsn.mk ../../syntax_tools/vsn.mk \
- $(GEN_SCRIPT_SRC)
- $(vsn_verbose)sed -e "s/%EDOC_VSN%/$(EDOC_VSN)/g" \
- -e "s/%XMERL_VSN%/$(XMERL_VSN)/g" \
- -e "s/%SYNTAX_TOOLS_VSN%/$(SYNTAX_TOOLS_VSN)/g" \
- $(GEN_SCRIPT_SRC) > $(GEN_SCRIPT)
+debug opt:
clean:
- rm -f $(GEN_SCRIPT)
rm -f core *~
docs:
@@ -54,4 +42,3 @@ release_spec: opt
release_docs_spec:
-
diff --git a/lib/edoc/priv/edoc_generate.src b/lib/edoc/priv/edoc_generate
index eeaa20f959..0492623c7f 100644
--- a/lib/edoc/priv/edoc_generate.src
+++ b/lib/edoc/priv/edoc_generate
@@ -10,28 +10,18 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-# Portions created by Ericsson are Copyright 1999-2000, Ericsson
+# Portions created by Ericsson are Copyright 1999-2000, Ericsson
# Utvecklings AB. All Rights Reserved.''
-#
-
-#EDOC_DIR=/clearcase/otp/internal_tools/edoc
-EDOC_DIR=/home/otp/sgml/edoc-%EDOC_VSN%
-SYNTAX_TOOLS_DIR=/home/otp/sgml/syntax_tools-%SYNTAX_TOOLS_VSN%
-XMERL_DIR=/home/otp/sgml/xmerl-%XMERL_VSN%
+#
-FILE=
APP=
TITLE=
VSN=
while [ $# -gt 0 ]; do
case $1 in
- -file)
- FILE=$2;
- shift;
- shift;;
-app)
APP=$2;
shift;
@@ -47,28 +37,14 @@ while [ $# -gt 0 ]; do
esac
done
-if [ -n "$FILE" ] ; then
- EDOC_ARGS="[{layout,otpsgml_layout},{dir,\".\"},{file_suffix,\".sgml\"},{preprocess,true},{includes,[\"$XMERL_DIR/include\"]}]"
- erl -boot start_clean -noshell \
- -pa $EDOC_DIR/ebin \
- -pa $SYNTAX_TOOLS_DIR/ebin \
- -pa $XMERL_DIR/ebin \
- -run edoc_run file $FILE $EDOC_ARGS \
- -s erlang halt
-elif [ -n "$APP" -a -n "$TITLE" ] ; then
+if [ -n "$APP" -a -n "$TITLE" ] ; then
erl -boot start_clean -noshell \
- -pa $EDOC_DIR/ebin \
- -pa $SYNTAX_TOOLS_DIR/ebin \
- -pa $XMERL_DIR/ebin \
-run edoc_run application $APP "\".\"" "[{title,$TITLE}]" \
-s erlang halt
-elif [ -n "$APP" -a -n "$VSN" ] ; then
+elif [ -n "$APP" -a -n "$VSN" ] ; then
erl -boot start_clean -noshell \
- -pa $EDOC_DIR/ebin \
- -pa $SYNTAX_TOOLS_DIR/ebin \
- -pa $XMERL_DIR/ebin \
-run edoc_run application $APP "\".\"" "[{def,{vsn,\"$VSN\"}}]" \
-s erlang halt
else
- echo "Usage: docb_edoc [-file Filename] | [-app Appname [-title Title |-vsn Vsn]]"
+ echo "Usage: edoc_generate [-app Appname [-title Title |-vsn Vsn]]"
fi
diff --git a/lib/edoc/src/Makefile b/lib/edoc/src/Makefile
index 4e5a4182da..ea2f45dc4c 100644
--- a/lib/edoc/src/Makefile
+++ b/lib/edoc/src/Makefile
@@ -29,8 +29,7 @@ SOURCES= \
edoc.erl edoc_data.erl edoc_doclet.erl edoc_extract.erl \
edoc_layout.erl edoc_lib.erl edoc_macros.erl edoc_parser.erl \
edoc_refs.erl edoc_report.erl edoc_run.erl edoc_scanner.erl \
- edoc_specs.erl edoc_tags.erl edoc_types.erl edoc_wiki.erl \
- otpsgml_layout.erl
+ edoc_specs.erl edoc_tags.erl edoc_types.erl edoc_wiki.erl
OBJECTS=$(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
diff --git a/lib/edoc/src/edoc.app.src b/lib/edoc/src/edoc.app.src
index e4b9040c78..43343e2ae8 100644
--- a/lib/edoc/src/edoc.app.src
+++ b/lib/edoc/src/edoc.app.src
@@ -18,8 +18,7 @@
edoc_specs,
edoc_tags,
edoc_types,
- edoc_wiki,
- otpsgml_layout]},
+ edoc_wiki]},
{registered,[]},
{applications, [compiler,kernel,stdlib,syntax_tools]},
{env, []},
diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl
index fb04bfce0e..4e45e42f20 100644
--- a/lib/edoc/src/edoc_specs.erl
+++ b/lib/edoc/src/edoc_specs.erl
@@ -372,7 +372,7 @@ d2e({type,_,binary,[Base,Unit]}, _Prec) ->
{integer,_,U} = erl_eval:partial_eval(Unit),
#t_binary{base_size = B, unit_size = U};
d2e({type,_,map,any}, _Prec) ->
- #t_map{types = []};
+ #t_type{name = #t_name{name = map}, args = []};
d2e({type,_,map,Es}, _Prec) ->
#t_map{types = d2e(Es) };
d2e({type,_,map_field_assoc,[K,V]}, Prec) ->
diff --git a/lib/edoc/src/otpsgml_layout.erl b/lib/edoc/src/otpsgml_layout.erl
deleted file mode 100644
index 295daed551..0000000000
--- a/lib/edoc/src/otpsgml_layout.erl
+++ /dev/null
@@ -1,836 +0,0 @@
-%% =====================================================================
-%% Licensed under the Apache License, Version 2.0 (the "License"); you may
-%% not use this file except in compliance with the License. You may obtain
-%% a copy of the License at <http://www.apache.org/licenses/LICENSE-2.0>
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% Alternatively, you may use this file under the terms of the GNU Lesser
-%% General Public License (the "LGPL") as published by the Free Software
-%% Foundation; either version 2.1, or (at your option) any later version.
-%% If you wish to allow use of your version of this file only under the
-%% terms of the LGPL, you should delete the provisions above and replace
-%% them with the notice and other provisions required by the LGPL; see
-%% <http://www.gnu.org/licenses/>. If you do not delete the provisions
-%% above, a recipient may use your version of this file under the terms of
-%% either the Apache License or the LGPL.
-%%
-%% @author Richard Carlsson <[email protected]>
-%% @author Kenneth Lundin <[email protected]>
-%% @copyright 2001-2004 Richard Carlsson
-%% @see edoc_layout
-%% @end
-%% =====================================================================
-
-%% @doc The OTP SGML layout module for EDoc. See the module {@link edoc}
-%% for details on usage.
-
-%% Note that this is written so that it is *not* depending on edoc.hrl!
-
--module(otpsgml_layout).
-
--export([module/2, overview/2,type/1]).
-
--import(edoc_report, [report/2]).
-
--include_lib("xmerl/include/xmerl.hrl").
-
--define(SGML_EXPORT, xmerl_otpsgml).
--define(DEFAULT_XML_EXPORT, ?SGML_EXPORT).
--define(STYLESHEET, "stylesheet.css").
--define(NL, "\n").
--define(DESCRIPTION_TITLE, "Description").
--define(DESCRIPTION_LABEL, "description").
--define(DATA_TYPES_TITLE, "Data Types").
--define(DATA_TYPES_LABEL, "types").
--define(FUNCTION_INDEX_TITLE, "Function Index").
--define(FUNCTION_INDEX_LABEL, "index").
--define(FUNCTIONS_TITLE, "Function Details").
--define(FUNCTIONS_LABEL, "functions").
-
-
-%% @doc The layout function.
-%%
-%% Options:
-%% <dl>
-%% <dt>{@type {index_columns, integer()@}}
-%% </dt>
-%% <dd>Specifies the number of column pairs used for the function
-%% index tables. The default value is 1.
-%% </dd>
-%% <dt>{@type {stylesheet, string()@}}
-%% </dt>
-%% <dd>Specifies the URI used for referencing the stylesheet. The
-%% default value is `"stylesheet.css"'. If an empty string is
-%% specified, no stylesheet reference will be generated.
-%% </dd>
-%% <dt>{@type {xml_export, Module::atom()@}}
-%% </dt>
-%% <dd>Specifies an {@link //xmerl. `xmerl'} callback module to be
-%% used for exporting the documentation. See {@link
-%% //xmerl/xmerl:export_simple/3} for details.
-%% </dd>
-%% </dl>
-%%
-%% @see edoc:layout/2
-
--record(opts, {root, stylesheet, index_columns}).
-
-module(Element, Options) ->
- XML = layout_module(Element, init_opts(Element, Options)),
- Export = proplists:get_value(xml_export, Options,
- ?DEFAULT_XML_EXPORT),
- xmerl:export_simple([XML], Export, []).
-
-% Put layout options in a data structure for easier access.
-
-init_opts(Element, Options) ->
- R = #opts{root = get_attrval(root, Element),
- index_columns = proplists:get_value(index_columns,
- Options, 1)
- },
- case proplists:get_value(stylesheet, Options) of
- undefined ->
- S = edoc_lib:join_uri(R#opts.root, ?STYLESHEET),
- R#opts{stylesheet = S};
- "" ->
- R; % don't use any stylesheet
- S when is_list(S) ->
- R#opts{stylesheet = S};
- _ ->
- report("bad value for option `stylesheet'.", []),
- exit(error)
- end.
-
-
-%% =====================================================================
-%% XML-BASED LAYOUT ENGINE
-%% =====================================================================
-
-%% We assume that we have expanded XML data.
-
-%% <!ELEMENT module (moduleName, moduleFullName, behaviour*, description?,
-%% author*, version?, since?, copyright?, deprecated?,
-%% see*, reference*, typedecls?, functions)>
-%% <!ATTLIST module
-%% root CDATA #IMPLIED>
-%% <!ELEMENT moduleName (#PCDATA)>
-%% <!ELEMENT moduleFullName (#PCDATA)>
-%% <!ELEMENT behaviour (#PCDATA)>
-%% <!ATTLIST behaviour
-%% href CDATA #IMPLIED>
-%% <!ELEMENT description (briefDescription, fullDescription?)>
-%% <!ELEMENT briefDescription (#PCDATA)>
-%% <!ELEMENT fullDescription (#PCDATA)>
-%% <!ELEMENT author EMPTY>
-%% <!ATTLIST author
-%% name CDATA #REQUIRED
-%% email CDATA #IMPLIED
-%% website CDATA #IMPLIED>
-%% <!ELEMENT version (#PCDATA)>
-%% <!ELEMENT since (#PCDATA)>
-%% <!ELEMENT copyright (#PCDATA)>
-%% <!ELEMENT deprecated (description)>
-%% <!ELEMENT see (#PCDATA)>
-%% <!ATTLIST see
-%% name CDATA #REQUIRED
-%% href CDATA #IMPLIED>
-%% <!ELEMENT reference (#PCDATA)>
-%% <!ELEMENT typedecls (typedecl+)>
-%% <!ELEMENT functions (function+)>
-
-layout_module(#xmlElement{name = module, content = Es}=E, _Opts) ->
- Name = get_attrval(name, E),
- Desc = get_content(description, Es),
- ShortDesc = get_content(briefDescription, Desc),
- FullDesc = get_content(fullDescription, Desc),
- Functions = [E || E <- get_content(functions, Es)],
- SortedFs = lists:sort([{function_name(E), E} || E <- Functions]),
- Types = get_content(typedecls, Es),
- SortedTs = lists:sort([{type_name(E), E} || E <- Types]),
- Header = {header, [
- ?NL,{title, [Name]},
- ?NL,{prepared, [""]},
- ?NL,{responsible, [""]},
- ?NL,{docno, ["1"]},
- ?NL,{approved, [""]},
- ?NL,{checked, [""]},
- ?NL,{date, [""]},
- ?NL,{rev, ["A"]},
- ?NL,{file, [Name++".sgml"]}
- ]},
- Module = {module, [Name]},
- ModuleSummary = {modulesummary, ShortDesc},
- {Short,Long} = find_first_p(FullDesc,[]),
- Description = {description, [?NL,{p,Short}|Long]++[?NL|types(SortedTs)]},
- Funcs = functions(SortedFs),
- Authors = {authors, authors(Es)},
- See = sees1(Es),
- {erlref, [
- ?NL,Header,
- ?NL,Module,
- ?NL,ModuleSummary,
- ?NL,Description,
- ?NL,Funcs,
- ?NL,See,
- ?NL,Authors
- ]
- }.
-
-stylesheet(Opts) ->
- case Opts#opts.stylesheet of
- undefined ->
- [];
- CSS ->
- [{link, [{rel, "stylesheet"},
- {type, "text/css"},
- {href, CSS}], []},
- ?NL]
- end.
-
-% doc_index(FullDesc, Functions, Types) ->
-% case doc_index_rows(FullDesc, Functions, Types) of
-% [] -> [];
-% Rs ->
-% [{ul, [{li, [{a, [{href, local_label(R)}], [T]}]}
-% || {T, R} <- Rs]}]
-% end.
-
-% doc_index_rows(FullDesc, Functions, Types) ->
-% (if FullDesc == [] -> [];
-% true -> [{?DESCRIPTION_TITLE, ?DESCRIPTION_LABEL}]
-% end
-% ++ if Types == [] -> [];
-% true -> [{?DATA_TYPES_TITLE, ?DATA_TYPES_LABEL}]
-% end
-% ++ if Functions == [] -> [];
-% true -> [{?FUNCTION_INDEX_TITLE, ?FUNCTION_INDEX_LABEL},
-% {?FUNCTIONS_TITLE, ?FUNCTIONS_LABEL}]
-% end).
-
-% function_index(Fs, Cols) ->
-% case function_index_rows(Fs, Cols, []) of
-% [] -> [];
-% Rows ->
-% [?NL,
-% {h2, [{a, [{name, ?FUNCTION_INDEX_LABEL}],
-% [?FUNCTION_INDEX_TITLE]}]},
-% ?NL,
-% {table, [{width, "100%"}, {border, 1}], Rows},
-% ?NL]
-% end.
-
-% function_index_rows(Fs, Cols, Title) ->
-% Rows = (length(Fs) + (Cols - 1)) div Cols,
-% (if Title == [] -> [];
-% true -> [{tr, [{th, [{colspan, Cols * 2}, {align, left}],
-% [Title]}]},
-% ?NL]
-% end
-% ++ lists:flatmap(fun index_row/1,
-% edoc_lib:transpose(edoc_lib:segment(Fs, Rows)))).
-
-% index_row(Fs) ->
-% [{tr, lists:flatmap(fun index_col/1, Fs)}, ?NL].
-
-% index_col({Name, F=#xmlElement{content = Es}}) ->
-% [{td, [{valign, "top"}], label_href([Name], F)},
-% {td, index_desc(Es)}].
-
-index_desc(Es) ->
- Desc = get_content(description, Es),
- case get_content(briefDescription, Desc) of
- [] ->
- equiv(Es); % no description at all if no equiv
- ShortDesc ->
- ShortDesc
- end.
-
-% label_href(Content, F) ->
-% case get_attrval(label, F) of
-% "" -> Content;
-% Ref -> [{a, [{href, local_label(Ref)}], Content}]
-% end.
-
-
-%% <!ELEMENT function (args, typespec?, equiv?, description?, since?,
-%% deprecated?, see*)>
-%% <!ATTLIST function
-%% name CDATA #REQUIRED
-%% arity CDATA #REQUIRED
-%% exported NMTOKEN(yes | no) #REQUIRED
-%% label CDATA #IMPLIED>
-%% <!ELEMENT args (arg*)>
-%% <!ELEMENT arg description?>
-%% <!ATTLIST arg name CDATA #REQUIRED>
-
-
-%% <!ELEMENT equiv (expr, see?)>
-%% <!ELEMENT expr (#PCDATA)>
-
-% functions(Fs) ->
-% Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs),
-% if Es == [] -> [];
-% true ->
-% [?NL,
-% {h2, [{a, [{name, ?FUNCTIONS_LABEL}], [?FUNCTIONS_TITLE]}]},
-% ?NL | Es]
-% end.
-
-functions(Fs) ->
- Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs),
- if Es == [] -> [];
- true ->
- {funcs, Es}
- end.
-
-% is_exported(E) ->
-% case get_attrval(exported, E) of
-% "yes" -> true;
-% _ -> false
-% end.
-
-% function(Name, E=#xmlElement{content = Es}) ->
-% ([?NL, {h3, label_anchor([Name], E)}, ?NL]
-% ++ case typespec(get_content(typespec, Es)) of
-% [] ->
-% signature(get_content(arguments, Es),
-% get_text(functionName, Es));
-% Spec -> Spec
-% end
-% ++ equiv(Es)
-% ++ deprecated(Es, "function")
-% ++ fulldesc(Es)
-% ++ since(Es)
-% ++ sees(Es)).
-
-function(_Name, E=#xmlElement{content = Es}) ->
- TypeSpec = get_content(typespec, Es),
- [?NL,{func, [ ?NL,
- {name,
-% case typespec(get_content(typespec, Es)) of
- case funcheader(TypeSpec) of
- [] ->
- signature(get_content(args, Es),
- get_attrval(name, E));
- Spec -> Spec
- end
- },
- ?NL,{fsummary, fsummary(Es)},
-% ?NL,{type, local_types(TypeSpec)},
- ?NL,local_types(TypeSpec),
- ?NL,{desc, label_anchor(E)++fulldesc(Es)++sees(Es)}
- ]}].
-
-fsummary([]) -> ["\s"];
-fsummary(Es) ->
- Desc = get_content(description, Es),
- case get_content(briefDescription, Desc) of
- [] ->
- fsummary_equiv(Es); % no description at all if no equiv
- ShortDesc ->
- ShortDesc
- end.
-
-
-fsummary_equiv(Es) ->
- case get_content(equiv, Es) of
- [] -> ["\s"];
- Es1 ->
- case get_content(expr, Es1) of
- [] -> ["\s"];
- [Expr] ->
- ["Equivalent to ", Expr, ".",?NL]
- end
- end.
-
-
-function_name(E) ->
- get_attrval(name, E) ++ "/" ++ get_attrval(arity, E).
-
-label_anchor(E) ->
- case get_attrval(label, E) of
- "" -> [];
- Ref -> [{marker, [{id, Ref}],[]},?NL]
- end.
-
-label_anchor(Content, E) ->
- case get_attrval(label, E) of
- "" -> Content;
- Ref -> {p,[{marker, [{id, Ref}],[]},
- {em, Content}]}
- end.
-
-%% <!ELEMENT args (arg*)>
-%% <!ELEMENT arg (argName, description?)>
-%% <!ELEMENT argName (#PCDATA)>
-
-%% This is currently only done for functions without type spec.
-
-signature(Es, Name) ->
-% [{tt, [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> term()", ?NL]}].
- [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> term()", ?NL].
-
-arg(#xmlElement{content = Es}) ->
- [get_text(argName, Es)].
-
-%% <!ELEMENT typespec (erlangName, type, localdef*)>
-
-% typespec([]) -> [];
-% typespec(Es) ->
-% [{p, ([{tt, ([t_name(get_elem(qualifiedName, Es))]
-% ++ t_type(get_content(type, Es)))}]
-% ++ local_defs(get_elem(definition, Es)))},
-% ?NL].
-
-funcheader([]) -> [];
-funcheader(Es) ->
- [t_name(get_elem(erlangName, Es))] ++ t_utype(get_elem(type, Es)).
-
-local_types([]) -> [];
-local_types(Es) ->
- local_defs2(get_elem(localdef, Es)).
-
-local_defs2([]) -> [];
-local_defs2(Es) ->
- {type,[?NL | [{v, localdef(E)} || E <- Es]]}.
-
-%% <!ELEMENT typedecl (typedef, description?)>
-%% <!ELEMENT typedef (erlangName, argtypes, type?, localdef*)>
-
-types([]) -> [];
-types(Ts) ->
- Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E) end, Ts),
- [?NL,
-% {h2, [{a, [{name, ?DATA_TYPES_LABEL}],
-% [?DATA_TYPES_TITLE]}]},
-% ?NL | Es]
- {p,[{marker, [{id, ?DATA_TYPES_LABEL}],[]},
- {em,[?DATA_TYPES_TITLE]}]},
- ?NL, {taglist,[?NL|Es]}].
-
-%%type(Name, E=#xmlElement{content = Es}) ->
-%% ([?NL, {h3, label_anchor([Name, "()"], E)}, ?NL]
-%% ++ [{p, typedef(get_content(typedef, Es))}, ?NL]
-%% ++ fulldesc(Es)).
-typedecl(_Name, #xmlElement{content = Es}) ->
- [{tag, typedef(get_content(typedef, Es))},?NL,{item,fulldesc(Es)},?NL].
-
-
-type_name(#xmlElement{content = Es}) ->
- t_name(get_elem(erlangName, get_content(typedef, Es))).
-
-typedef(Es) ->
- Name = ([t_name(get_elem(erlangName, Es)), "("]
- ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])),
- (case get_elem(type, Es) of
- [] -> [{b, ["abstract datatype"]}, ": ", {tt, Name}];
- Type ->
- [{tt, Name ++ [" = "] ++ t_utype(Type)}]
- end
- ++ local_defs(get_elem(localdef, Es))).
-
-local_defs([]) -> [];
-local_defs(Es) ->
- [?NL, {ul, [{li, [{tt, localdef(E)}]} || E <- Es]}].
-
-localdef(E = #xmlElement{content = Es}) ->
- (case get_elem(typevar, Es) of
- [] ->
- label_anchor(t_abstype(get_content(abstype, Es)), E);
- [V] ->
- t_var(V)
- end
- ++ [" = "] ++ t_utype(get_elem(type, Es))).
-
-fulldesc(Es) ->
- case get_content(fullDescription, get_content(description, Es)) of
-% [] -> [?NL];
- [] -> index_desc(Es);
-% Desc -> [{p, Desc}, ?NL]
- Desc ->
- {Short,Long} = find_first_p(Desc,[]),
- [?NL,{p,Short}|Long] ++[?NL]
- end.
-
-find_first_p([#xmlElement{name=p}|_]=Long,Short) ->
- {lists:reverse(Short),Long};
-find_first_p([H|T],Short) ->
- find_first_p(T,[H|Short]);
-find_first_p([],Short) ->
- {lists:reverse(Short),[]}.
-
-
-sees1(Es) ->
- case get_elem(see, Es) of
- [] -> [];
- Es1 ->
- {section,[{title,["See also"]},{p,seq(fun see/1, Es1, [])}]}
- end.
-
-sees(Es) ->
- case get_elem(see, Es) of
- [] -> [];
- Es1 ->
- [{p, [{em, ["See also:"]}, " "] ++ seq(fun see/1, Es1, ["."])},
- ?NL]
- end.
-
-see(E=#xmlElement{content = Es}) ->
- see(E,Es).
-
-see(E, Es) ->
- case get_attrval(href, E) of
- "" -> Es;
- Ref ->
- case lists:reverse(Ref) of
- "lmgs.ppa_"++Ppa ->
- App = lists:reverse(Ppa),
- [{seealso, [{marker, App++"_app"}], [App]},"(6)"];
- "lmgs."++Dom ->
- Mod = lists:reverse(Dom),
- [{seealso, [{marker, Mod}], [Mod]},"(3)"];
- _ ->
- [{seealso, [{marker, Ref}], Es}]
- end
- end.
-
-equiv(Es) ->
- case get_content(equiv, Es) of
- [] -> ["\s"];
- Es1 ->
- case get_content(expr, Es1) of
- [] -> [];
- [Expr] ->
-% Expr1 = {tt, [Expr]},
-% Expr1 = {c, [Expr]},
- Expr1 = [Expr],
- Expr2 = case get_elem(see, Es1) of
- [] ->
- {c,Expr1};
- [E=#xmlElement{}] ->
-% see(E,Expr1)
- case get_attrval(href, E) of
- "" ->
- {c,Expr1};
- Ref ->
- {seealso, [{marker, Ref}], Expr1}
- end
- end,
- [{p, ["Equivalent to ", Expr2, "."]}, ?NL]
- end
- end.
-
-% replace_minus_with_percent([$-|T]) ->
-% [$%|T];
-% replace_minus_with_percent([H|T]) ->
-% [H|replace_minus_with_percent(T)].
-
-copyright(Es) ->
- case get_content(copyright, Es) of
- [] -> [];
- Es1 ->
- [{p, ["Copyright \251 " | Es1]}, ?NL]
- end.
-
-version(Es) ->
- case get_content(version, Es) of
- [] -> [];
- Es1 ->
- [{p, [{b, ["Version:"]}, " " | Es1]}, ?NL]
- end.
-
-since(Es) ->
- case get_content(since, Es) of
- [] -> [];
- Es1 ->
- [{p, [{b, ["Introduced in:"]}, " " | Es1]}, ?NL]
- end.
-
-deprecated(Es, S) ->
- Es1 = get_content(description, get_content(deprecated, Es)),
- case get_content(fullDescription, Es1) of
- [] -> [];
- Es2 ->
- [{p, [{b, ["This " ++ S ++ " is deprecated:"]}, " " | Es2]},
- ?NL]
- end.
-
-% behaviours(Es) ->
-% case get_elem(behaviour, Es) of
-% [] -> [];
-% Es1 ->
-% [{p, [{b, ["Behaviour:"]}, " "] ++ seq(fun behaviour/1, Es1, ["."])},
-% ?NL]
-% end.
-
-% behaviour(E=#xmlElement{content = Es}) ->
-% case get_attrval(href, E) of
-% "" -> [{tt, Es}];
-% Ref -> [{a, [{href, Ref}], [{tt, Es}]}]
-% end.
-
-authors(Es) ->
- case get_elem(author, Es) of
- [] -> [?NL,{aname,["\s"]},?NL,{email,["\s"]}];
- Es1 -> [?NL|seq(fun author/1, Es1, [])]
-%
-% [{p, [{b, ["Authors:"]}, " "] ++ seq(fun author/1, Es1, ["."])},
-% ?NL]
- end.
-
-
-%% <!ATTLIST author
-%% name CDATA #REQUIRED
-%% email CDATA #IMPLIED
-%% website CDATA #IMPLIED>
-
-author(E=#xmlElement{}) ->
- Name = case get_attrval(name, E) of
- [] -> "\s";
- N -> N
- end,
- Mail = case get_attrval(email, E) of
- [] -> "\s";
- M -> M
- end,
- [?NL,{aname,[Name]},?NL,{email,[Mail]}].
-
-% author(E=#xmlElement{}) ->
-% Name = get_attrval(name, E),
-% Mail = get_attrval(email, E),
-% URI = get_attrval(website, E),
-% (if Name == Mail ->
-% [{a, [{href, "mailto:" ++ Mail}],[{tt, [Mail]}]}];
-% true ->
-% if Mail == "" -> [Name];
-% true -> [Name, " (", {a, [{href, "mailto:" ++ Mail}],
-% [{tt, [Mail]}]}, ")"]
-% end
-% end
-% ++ if URI == "" -> [];
-% true -> [" [", {em, ["web site:"]}, " ",
-% {tt, [{a, [{href, URI}], [URI]}]}, "]"]
-% end).
-
-references(Es) ->
- case get_elem(reference, Es) of
- [] -> [];
- Es1 ->
- [{p, [{b, ["References"]},
- {ul, [{li, C} || #xmlElement{content = C} <- Es1]}]},
- ?NL]
- end.
-
-t_name([E]) ->
- N = get_attrval(name, E),
- case get_attrval(module, E) of
- "" -> N;
- M ->
- S = M ++ ":" ++ N,
- case get_attrval(app, E) of
- "" -> S;
- A -> "//" ++ A ++ "/" ++ S
- end
- end.
-
-t_utype([E]) ->
- t_utype_elem(E).
-
-t_utype_elem(E=#xmlElement{content = Es}) ->
- case get_attrval(name, E) of
- "" -> t_type(Es);
- Name ->
- T = t_type(Es),
- case T of
- [Name] -> T; % avoid generating "Foo::Foo"
- T -> [Name] ++ ["::"] ++ T
- end
- end.
-
-t_type([E=#xmlElement{name = typevar}]) ->
- t_var(E);
-t_type([E=#xmlElement{name = atom}]) ->
- t_atom(E);
-t_type([E=#xmlElement{name = integer}]) ->
- t_integer(E);
-t_type([E=#xmlElement{name = float}]) ->
- t_float(E);
-t_type([#xmlElement{name = nil}]) ->
- t_nil();
-t_type([#xmlElement{name = list, content = Es}]) ->
- t_list(Es);
-t_type([#xmlElement{name = tuple, content = Es}]) ->
- t_tuple(Es);
-t_type([#xmlElement{name = 'fun', content = Es}]) ->
- t_fun(Es);
-t_type([E = #xmlElement{name = abstype, content = Es}]) ->
- T = t_abstype(Es),
-% see(E,T);
- case get_attrval(href, E) of
- "" -> T;
- % Ref -> [{seealso, [{marker, Ref}], T}]
- _Ref -> T
- end;
-t_type([#xmlElement{name = union, content = Es}]) ->
- t_union(Es).
-
-t_var(E) ->
- [get_attrval(name, E)].
-
-
-t_atom(E) ->
- [get_attrval(value, E)].
-
-t_integer(E) ->
- [get_attrval(value, E)].
-
-t_float(E) ->
- [get_attrval(value, E)].
-
-t_nil() ->
- ["[]"].
-
-t_list(Es) ->
- ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"].
-
-t_tuple(Es) ->
- ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
-
-t_fun(Es) ->
- ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
- [") -> "] ++ t_utype(get_elem(type, Es))).
-
-t_abstype(Es) ->
-% ([t_name(get_elem(qualifiedName, Es)), "("]
-% ++ seq(fun t_type_elem/1, get_elem(type, Es), [")"])).
- case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of
- {Mod,Type} ->
- [Type, "("] ++
- seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) ++
- [" (see module ", Mod, ")"];
- Type ->
- [Type, "("] ++
- seq(fun t_utype_elem/1, get_elem(type, Es), [")"])
- end.
-
-%% Split at one colon, but not at two (or more)
-split_at_colon([$:,$:|_]=Rest,Acc) ->
- lists:reverse(Acc)++Rest;
-split_at_colon([$:|Type],Acc) ->
- {lists:reverse(Acc),Type};
-split_at_colon([Char|Rest],Acc) ->
- split_at_colon(Rest,[Char|Acc]);
-split_at_colon([],Acc) ->
- lists:reverse(Acc).
-
-% t_par(Es) ->
-% T = t_type(get_content(type, Es)),
-% case get_elem(variable, Es) of
-% [] -> T;
-% [V0] -> case t_variable(V0) of
-% T -> T;
-% V -> V ++ ["::"] ++ T
-% end
-% end.
-
-% t_par_elem(#xmlElement{content = Es}) -> t_par(Es).
-
-t_union(Es) ->
- seq(fun t_utype_elem/1, Es, " | ", []).
-
-seq(F, Es) ->
- seq(F, Es, []).
-
-seq(F, Es, Tail) ->
- seq(F, Es, ", ", Tail).
-
-seq(F, [E], _Sep, Tail) ->
- F(E) ++ Tail;
-seq(F, [E | Es], Sep, Tail) ->
- F(E) ++ [Sep] ++ seq(F, Es, Sep, Tail);
-seq(_F, [], _Sep, Tail) ->
- Tail.
-
-get_elem(Name, [#xmlElement{name = Name} = E | Es]) ->
- [E | get_elem(Name, Es)];
-get_elem(Name, [_ | Es]) ->
- get_elem(Name, Es);
-get_elem(_, []) ->
- [].
-
-get_attr(Name, [#xmlAttribute{name = Name} = A | As]) ->
- [A | get_attr(Name, As)];
-get_attr(Name, [_ | As]) ->
- get_attr(Name, As);
-get_attr(_, []) ->
- [].
-
-get_attrval(Name, #xmlElement{attributes = As}) ->
- case get_attr(Name, As) of
- [#xmlAttribute{value = V}] ->
- V;
- [] -> ""
- end.
-
-get_content(Name, Es) ->
- case get_elem(Name, Es) of
- [#xmlElement{content = Es1}] ->
- Es1;
- [] -> []
- end.
-
-get_text(Name, Es) ->
- case get_content(Name, Es) of
- [#xmlText{value = Text}] ->
- Text;
- [] -> ""
- end.
-
-% local_label(R) ->
-% "#" ++ R.
-
-xml(Title, CSS, Body) ->
- {html, [?NL,
- {head, [?NL,
- {title, [Title]},
- ?NL] ++ CSS},
- ?NL,
- {body, [{bgcolor, "white"}], Body},
- ?NL]
- }.
-
-%% ---------------------------------------------------------------------
-
- type(E) ->
- type(E, []).
-
-% type(E, Ds) ->
-% xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds),
-% ?HTML_EXPORT).
- type(E, Ds) ->
- xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds),
- ?SGML_EXPORT).
-
-overview(E=#xmlElement{name = overview, content = Es}, Options) ->
- Opts = init_opts(E, Options),
- Title = get_text(title, Es),
- Desc = get_content(description, Es),
-% ShortDesc = get_content(briefDescription, Desc),
- FullDesc = get_content(fullDescription, Desc),
- Body = ([?NL, {h1, [Title]}, ?NL]
-% ++ ShortDesc
- ++ copyright(Es)
- ++ version(Es)
- ++ since(Es)
- ++ deprecated(Es, "application")
- ++ authors(Es)
- ++ references(Es)
- ++ sees(Es)
- ++ FullDesc),
- XML = xml(Title, stylesheet(Opts), Body),
- xmerl:export_simple([XML], ?SGML_EXPORT, []).
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index 065944ccef..2f6d469536 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.9.1
+EDOC_VSN = 0.9.2
diff --git a/lib/eldap/doc/src/Makefile b/lib/eldap/doc/src/Makefile
index ac869e446f..aff1da4a9a 100644
--- a/lib/eldap/doc/src/Makefile
+++ b/lib/eldap/doc/src/Makefile
@@ -37,7 +37,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = eldap.xml
-XML_PART_FILES = release_notes.xml usersguide.xml
+XML_PART_FILES = usersguide.xml
XML_CHAPTER_FILES = notes.xml
BOOK_FILES = book.xml
diff --git a/lib/eldap/doc/src/fascicules.xml b/lib/eldap/doc/src/fascicules.xml
deleted file mode 100644
index cbc266cd30..0000000000
--- a/lib/eldap/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="usersguide" href="usersguide_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="release_notes" href="release_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/eldap/doc/src/note.gif b/lib/eldap/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/eldap/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/eldap/doc/src/notes.xml b/lib/eldap/doc/src/notes.xml
index 7aad745f67..8b066671ee 100644
--- a/lib/eldap/doc/src/notes.xml
+++ b/lib/eldap/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the Eldap application.</p>
+<section><title>Eldap 1.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eldap 1.2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/eldap/doc/src/warning.gif b/lib/eldap/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/eldap/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk
index 721387d97d..1636b6bb6d 100644
--- a/lib/eldap/vsn.mk
+++ b/lib/eldap/vsn.mk
@@ -1 +1 @@
-ELDAP_VSN = 1.2.2
+ELDAP_VSN = 1.2.3
diff --git a/lib/erl_docgen/doc/src/fasc_dtds.xml b/lib/erl_docgen/doc/src/fasc_dtds.xml
deleted file mode 100644
index 390ae6c5d1..0000000000
--- a/lib/erl_docgen/doc/src/fasc_dtds.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2007</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Fascicules DTDs</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- <file>fasc_dtds.xml</file>
- </header>
-
- <section>
- <title>The fascicules DTD</title>
-
- <p>The <c>fascicules</c> DTD is a special kind of DTD which can be
- used to specify the different parts of the documentation, and
- which one of those should be shown as default.</p>
-
- <p>Example:</p>
-
- <pre><![CDATA[
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
-</fascicules>
- ]]></pre>
-
- <p>In the example, it is specified that the documentation for this
- application consists of three parts: User's Guide, where
- the "cover page" (with the two frames) is located in
- <c>part_frame.html</c>, Reference Manual with the cover page
- <c>ref_man_frame.html</c> and Release Notes with the cover page
- <c>part_notes_frame.html</c>.</p>
-
- <p>As a result, at the top of the left frame in the generated HTML
- documentation, there will be corresponding links to User's Guide,
- Reference Manual and Release Notes.</p>
-
- <p>The attribute <c>entry="yes"</c> specifies that it is
- the Reference Manual which should be shown as default. This means
- that when generating the HTML files, <c>application_frame.html</c>
- will be copied to <c>index.html</c>.</p>
-
- <note>
- <p>DocBuilder assumes that the XML file written according to
- the <c>fascicules</c> DTD is called <c>fascicules.xml</c>.</p>
- </note>
-
- <p>This file is optional. If it does not exist, there are no links
- to other parts of the documentation (as they are not known) in
- the left frame, and no <c>index.html</c> is created.</p>
- </section>
-
- <section>
- <marker id="fasciculesTAG"></marker>
- <title>&lt;fascicules&gt;</title>
-
- <p>Top level tag for the <c>fascicules</c> DTD.</p>
-
- <p>Contains one or more
- <seealso marker="#fasciculeTAG">&lt;fascicule&gt;</seealso>.</p>
- </section>
-
- <section>
- <marker id="fasciculeTAG"></marker>
- <title>&lt;fascicule&gt;</title>
-
- <p>Specifies properties for one "part" of the documentation for an
- application.</p>
-
- <p>Contains plain text, the name of this part.</p>
-
- <p>The <c>file</c> attribute should specify the file name for
- the corresponding <c>part</c> or <c>application</c>, without
- the <c>.xml</c> extension.</p>
-
- <p>The <c>href</c> attribute should specify the file name for
- the corresponding HTML cover page file, without the <c>.html</c>
- extension.</p>
-
- <p>The optional <c>entry="yes"|"no"</c> attribute specifies if
- the HTML cover page should be copied to <c>index.html</c> or
- not. Default is <c>"no"</c>.</p>
- </section>
-</chapter>
-
diff --git a/lib/erl_docgen/doc/src/fascicules.xml b/lib/erl_docgen/doc/src/fascicules.xml
deleted file mode 100644
index 154c8a3b6d..0000000000
--- a/lib/erl_docgen/doc/src/fascicules.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
-</fascicules>
-
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index 59c65665d4..2652b4b0c8 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -31,7 +31,26 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.7.1</title>
+ <section><title>Erl_Docgen 0.7.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> The style for code, warning and note tags in the pdf
+ have been changed so they look like the html version.
+ <br/> The spacing around code blocks have been changed
+ for both html and pdf so it's the same regardless if the
+ user have a newline after the start tag (or before the
+ end tag) or not. </p>
+ <p>
+ Own Id: OTP-14674</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.7.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/erl_docgen/priv/xsl/db_funcs.xsl b/lib/erl_docgen/priv/xsl/db_funcs.xsl
new file mode 100644
index 0000000000..8178ce44fb
--- /dev/null
+++ b/lib/erl_docgen/priv/xsl/db_funcs.xsl
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ #
+ # %CopyrightBegin%
+ #
+ # Copyright Ericsson AB 2009-2017. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #
+ # %CopyrightEnd%
+
+ -->
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:erl="http://erlang.org"
+ xmlns:func="http://exslt.org/functions"
+ extension-element-prefixes="func"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
+
+ <!-- Used from code template to trim the newline/cr after the tag
+ and spaces/tabs between them
+ -->
+ <xsl:variable name="newlinechars" select="'&#10;&#13;'" />
+ <xsl:variable name="spacechars" select="'&#09; '" />
+
+ <func:function name="erl:code_trim">
+ <xsl:param name="string" />
+
+ <xsl:variable name="leftresult" select="erl:code_ltrim($string, $string)"/>
+ <xsl:variable name="result" select="erl:code_rtrim($leftresult, $leftresult)"/>
+
+ <func:result select="$result"/>
+ </func:function>
+
+ <func:function name="erl:code_rtrim">
+ <xsl:param name="string" />
+ <xsl:param name="origstring" />
+
+ <xsl:variable name="length" select="string-length($string)" />
+
+ <xsl:variable name="result">
+ <xsl:if test="$length &gt; 0">
+ <xsl:choose>
+ <xsl:when test="contains($spacechars, substring($string, $length, 1))">
+ <xsl:value-of select="erl:code_rtrim(substring($string, 1, $length - 1), $origstring)" />
+ </xsl:when>
+ <xsl:when test="contains($newlinechars, substring($string, $length, 1))">
+ <xsl:value-of select="erl:code_rtrim_1(substring($string, 1, $length - 1))" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$origstring" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:variable>
+
+ <func:result select="$result" />
+ </func:function>
+
+ <func:function name="erl:code_rtrim_1">
+ <xsl:param name="string" />
+
+ <xsl:variable name="length" select="string-length($string)" />
+
+ <xsl:variable name="result">
+ <xsl:if test="$length &gt; 0">
+ <xsl:choose>
+ <xsl:when test="contains($newlinechars, substring($string, $length, 1))">
+ <xsl:value-of select="erl:code_rtrim_1(substring($string, 1, $length - 1))" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="erl:code_rtrim($string, $string)" />
+ <!--xsl:value-of select="$string" /-->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:variable>
+
+ <func:result select="$result" />
+ </func:function>
+
+ <func:function name="erl:code_ltrim">
+ <xsl:param name="string" />
+ <xsl:param name="origstring" />
+
+ <xsl:variable name="result">
+ <xsl:if test="string-length($string) &gt; 0">
+ <xsl:choose>
+ <xsl:when test="contains($spacechars, substring($string, 1, 1))">
+ <xsl:value-of select="erl:code_ltrim(substring($string, 2), $origstring)" />
+ </xsl:when>
+ <xsl:when test="contains($newlinechars, substring($string, 1, 1))">
+ <xsl:value-of select="erl:code_ltrim_1(substring($string, 2))" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$origstring" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:variable>
+
+ <func:result select="$result" />
+ </func:function>
+
+ <func:function name="erl:code_ltrim_1">
+ <xsl:param name="string" />
+
+ <xsl:variable name="result">
+ <xsl:if test="string-length($string) &gt; 0">
+ <xsl:choose>
+ <xsl:when test="contains($newlinechars, substring($string, 1, 1))">
+ <xsl:value-of select="erl:code_ltrim_1(substring($string, 2))" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="erl:code_ltrim($string, $string)" />
+ <!--xsl:value-of select="$string" /-->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:variable>
+
+ <func:result select="$result" />
+ </func:function>
+
+</xsl:stylesheet>
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index a5e277aece..75614392fb 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -30,6 +30,7 @@
xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
<xsl:include href="db_html_params.xsl"/>
+ <xsl:include href="db_funcs.xsl"/>
<func:function name="erl:flip_first_char">
<xsl:param name="in"/>
@@ -1132,7 +1133,14 @@
<xsl:variable name="codenum">
<xsl:number level="any" from="chapter" count="code"/>
</xsl:variable>
- <div class="example"><pre><xsl:apply-templates/></pre></div>
+ <xsl:choose>
+ <xsl:when test="not(descendant::anno)">
+ <div class="example"><pre><xsl:value-of select="erl:code_trim(text())"/></pre></div>
+ </xsl:when>
+ <xsl:otherwise>
+ <div class="example"><pre><xsl:apply-templates/></pre></div>
+ </xsl:otherwise>
+ </xsl:choose>
</xsl:template>
<!-- Pre -->
diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index 99263847fb..46de66bcd8 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl
@@ -23,12 +23,16 @@
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
- extension-element-prefixes="exsl"
- xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ xmlns:func="http://exslt.org/functions"
+ xmlns:erl="http://erlang.org"
+ extension-element-prefixes="exsl func"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
<xsl:output method="xml" indent="yes"/>
<xsl:include href="db_pdf_params.xsl"/>
+ <xsl:include href="db_funcs.xsl"/>
<!-- Start of Dialyzer type/spec tags.
See also the templates matching "name" and "seealso" as well as
@@ -687,7 +691,7 @@
<fo:block xsl:use-attribute-sets="cover.inner.copyrightnotice">
<xsl:value-of select="/book/header/legalnotice"/>
- <!--
+ <!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -744,12 +748,12 @@
<fo:bookmark internal-destination="{generate-id(header/title)}"
starting-state="hide">
<fo:bookmark-title><xsl:value-of select="header/title"/></fo:bookmark-title>
-
+
<xsl:call-template name="bookmarks2">
<xsl:with-param name="entries"
select="chapter[header/title]"/>
</xsl:call-template>
-
+
</fo:bookmark>
</xsl:for-each>
</xsl:if>
@@ -1122,52 +1126,60 @@
<!-- Note -->
<xsl:template match="note">
<xsl:param name="partnum"/>
- <fo:block xsl:use-attribute-sets="note">
- <fo:block xsl:use-attribute-sets="note-warning-title">
- <xsl:text>Note:</xsl:text>
- </fo:block>
- <xsl:apply-templates>
- <xsl:with-param name="partnum" select="$partnum"/>
- </xsl:apply-templates>
+ <fo:block xsl:use-attribute-sets="note-warning">
+ <fo:block xsl:use-attribute-sets="note-title">
+ <xsl:text>Note:</xsl:text>
+ </fo:block>
+ <fo:block xsl:use-attribute-sets="note-warning-content">
+ <xsl:apply-templates>
+ <xsl:with-param name="partnum" select="$partnum"/>
+ </xsl:apply-templates>
+ </fo:block>
</fo:block>
</xsl:template>
<!-- Warning -->
<xsl:template match="warning">
<xsl:param name="partnum"/>
- <fo:block xsl:use-attribute-sets="warning">
- <fo:block xsl:use-attribute-sets="note-warning-title">
- <xsl:text>Warning:</xsl:text>
- </fo:block>
- <xsl:apply-templates>
- <xsl:with-param name="partnum" select="$partnum"/>
- </xsl:apply-templates>
+ <fo:block xsl:use-attribute-sets="note-warning">
+ <fo:block xsl:use-attribute-sets="warning-title">
+ <xsl:text>Warning:</xsl:text>
+ </fo:block>
+ <fo:block xsl:use-attribute-sets="note-warning-content">
+ <xsl:apply-templates>
+ <xsl:with-param name="partnum" select="$partnum"/>
+ </xsl:apply-templates>
+ </fo:block>
</fo:block>
</xsl:template>
<!-- Do -->
<xsl:template match="do">
<xsl:param name="partnum"/>
- <fo:block xsl:use-attribute-sets="do">
- <fo:block xsl:use-attribute-sets="note-warning-title">
- <xsl:text>Do:</xsl:text>
- </fo:block>
- <xsl:apply-templates>
- <xsl:with-param name="partnum" select="$partnum"/>
- </xsl:apply-templates>
+ <fo:block xsl:use-attribute-sets="note-warning">
+ <fo:block xsl:use-attribute-sets="note-title">
+ <xsl:text>Do:</xsl:text>
+ </fo:block>
+ <fo:block xsl:use-attribute-sets="note-warning-content">
+ <xsl:apply-templates>
+ <xsl:with-param name="partnum" select="$partnum"/>
+ </xsl:apply-templates>
+ </fo:block>
</fo:block>
</xsl:template>
<!-- Dont -->
<xsl:template match="dont">
<xsl:param name="partnum"/>
- <fo:block xsl:use-attribute-sets="dont">
- <fo:block xsl:use-attribute-sets="note-warning-title">
- <xsl:text>Don't:</xsl:text>
- </fo:block>
- <xsl:apply-templates>
- <xsl:with-param name="partnum" select="$partnum"/>
- </xsl:apply-templates>
+ <fo:block xsl:use-attribute-sets="note-warning">
+ <fo:block xsl:use-attribute-sets="warning-title">
+ <xsl:text>Don't:</xsl:text>
+ </fo:block>
+ <fo:block xsl:use-attribute-sets="note-warning-content">
+ <xsl:apply-templates>
+ <xsl:with-param name="partnum" select="$partnum"/>
+ </xsl:apply-templates>
+ </fo:block>
</fo:block>
</xsl:template>
@@ -1226,7 +1238,14 @@
</xsl:variable>
<fo:block xsl:use-attribute-sets="code">
- <xsl:apply-templates select="text()"/>
+ <xsl:choose>
+ <xsl:when test="not(descendant::anno)">
+ <xsl:value-of select="erl:code_trim(text())"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
</fo:block>
<xsl:if test="@caption">
diff --git a/lib/erl_docgen/priv/xsl/db_pdf_params.xsl b/lib/erl_docgen/priv/xsl/db_pdf_params.xsl
index d9a150d2d9..99da29c2ac 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf_params.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf_params.xsl
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
+<!--
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2016. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
# limitations under the License.
#
# %CopyrightEnd%
-
+
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
@@ -45,7 +45,7 @@
<xsl:param name="page-width">210mm</xsl:param>
<!-- Paper size: US Letter (279x216 mm) -->
- <!--
+ <!--
<xsl:param name="page-height">11in</xsl:param>
<xsl:param name="page-width">8.5in</xsl:param>
-->
@@ -248,86 +248,82 @@
</xsl:attribute-set>
<xsl:attribute-set name="code">
- <xsl:attribute name="background-color">#e0e0ff</xsl:attribute>
+ <xsl:attribute name="background-color">#f1f3f5</xsl:attribute>
+ <xsl:attribute name="border-style">solid</xsl:attribute>
+ <xsl:attribute name="border-color">#dee2e6</xsl:attribute><!-- dee2e6-->
+ <xsl:attribute name="border-width">0.3mm</xsl:attribute>
<xsl:attribute name="font-family">DejaVuSansMono, monospace</xsl:attribute>
<xsl:attribute name="font-size">0.8em</xsl:attribute>
- <xsl:attribute name="keep-together.within-page">auto</xsl:attribute>
+ <xsl:attribute name="keep-together.within-page">3</xsl:attribute>
<xsl:attribute name="linefeed-treatment">preserve</xsl:attribute>
- <xsl:attribute name="padding-before">0em</xsl:attribute>
- <xsl:attribute name="padding-after">1em</xsl:attribute>
- <xsl:attribute name="space-after">1em</xsl:attribute>
- <xsl:attribute name="space-before">2em</xsl:attribute>
- <xsl:attribute name="margin-left">0.5em</xsl:attribute>
- <xsl:attribute name="margin-right">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-before">1.5mm</xsl:attribute>
+ <xsl:attribute name="padding-after">1mm</xsl:attribute>
+ <xsl:attribute name="padding-left">1mm</xsl:attribute>
+ <xsl:attribute name="padding-right">1mm</xsl:attribute>
+ <xsl:attribute name="margin-left">1mm</xsl:attribute>
+ <xsl:attribute name="margin-right">1mm</xsl:attribute>
<xsl:attribute name="white-space-collapse">false</xsl:attribute>
<xsl:attribute name="white-space-treatment">preserve</xsl:attribute>
<xsl:attribute name="wrap-option">no-wrap</xsl:attribute>
+ <xsl:attribute name="space-after">0.5em</xsl:attribute>
+ <xsl:attribute name="space-before">0.5em</xsl:attribute>
</xsl:attribute-set>
-
-
<xsl:attribute-set name="toc.level1">
<xsl:attribute name="space-before">1em</xsl:attribute>
- </xsl:attribute-set>
-
-<xsl:attribute-set name="note">
- <xsl:attribute name="background-color">#d0fed0</xsl:attribute>
- <xsl:attribute name="space-after">1em</xsl:attribute>
- <xsl:attribute name="space-before">2em</xsl:attribute>
- <xsl:attribute name="text-align">justify</xsl:attribute>
- <xsl:attribute name="padding-before">1em</xsl:attribute>
- <xsl:attribute name="padding-after">0.3em</xsl:attribute>
- <xsl:attribute name="padding-left">0.5em</xsl:attribute>
- <xsl:attribute name="padding-right">0.5em</xsl:attribute>
- <xsl:attribute name="margin-left">0.5em</xsl:attribute>
- <xsl:attribute name="margin-right">0.5em</xsl:attribute>
- <xsl:attribute name="keep-together.within-page">always</xsl:attribute>
</xsl:attribute-set>
-<xsl:attribute-set name="warning">
- <xsl:attribute name="background-color">#ffd6d6</xsl:attribute>
- <xsl:attribute name="space-after">1em</xsl:attribute>
- <xsl:attribute name="space-before">2em</xsl:attribute>
- <xsl:attribute name="text-align">justify</xsl:attribute>
- <xsl:attribute name="padding-before">1em</xsl:attribute>
- <xsl:attribute name="padding-after">0.3em</xsl:attribute>
- <xsl:attribute name="padding-left">0.5em</xsl:attribute>
- <xsl:attribute name="padding-right">0.5em</xsl:attribute>
- <xsl:attribute name="margin-left">0.5em</xsl:attribute>
- <xsl:attribute name="margin-right">0.5em</xsl:attribute>
- <xsl:attribute name="keep-together.within-page">always</xsl:attribute>
+ <xsl:attribute-set name="note-title">
+ <xsl:attribute name="space-before">0.5em</xsl:attribute>
+ <xsl:attribute name="border-style">solid</xsl:attribute>
+ <xsl:attribute name="border-bottom-width">0mm</xsl:attribute>
+ <xsl:attribute name="border-color">#495057</xsl:attribute>
+ <xsl:attribute name="background-color">#2b8a3e</xsl:attribute>
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ <xsl:attribute name="color">#fefefe</xsl:attribute>
+ <xsl:attribute name="padding-before">1mm</xsl:attribute>
+ <xsl:attribute name="padding-after">0.5mm</xsl:attribute>
+ <xsl:attribute name="padding-left">1mm</xsl:attribute>
+ <xsl:attribute name="padding-right">1mm</xsl:attribute>
+ <xsl:attribute name="margin-left">1mm</xsl:attribute>
+ <xsl:attribute name="margin-right">1mm</xsl:attribute>
+ <xsl:attribute name="font-size">1.33em</xsl:attribute>
</xsl:attribute-set>
-<xsl:attribute-set name="do">
- <xsl:attribute name="background-color">#d0fed0</xsl:attribute>
- <xsl:attribute name="space-after">1em</xsl:attribute>
- <xsl:attribute name="space-before">2em</xsl:attribute>
- <xsl:attribute name="text-align">justify</xsl:attribute>
- <xsl:attribute name="padding-before">1em</xsl:attribute>
- <xsl:attribute name="padding-after">0.3em</xsl:attribute>
- <xsl:attribute name="padding-left">0.5em</xsl:attribute>
- <xsl:attribute name="padding-right">0.5em</xsl:attribute>
- <xsl:attribute name="margin-left">0.5em</xsl:attribute>
- <xsl:attribute name="margin-right">0.5em</xsl:attribute>
+ <xsl:attribute-set name="note-warning">
<xsl:attribute name="keep-together.within-page">always</xsl:attribute>
</xsl:attribute-set>
-<xsl:attribute-set name="dont">
- <xsl:attribute name="background-color">#ffd6d6</xsl:attribute>
- <xsl:attribute name="space-after">1em</xsl:attribute>
- <xsl:attribute name="space-before">2em</xsl:attribute>
- <xsl:attribute name="text-align">justify</xsl:attribute>
- <xsl:attribute name="padding-before">1em</xsl:attribute>
- <xsl:attribute name="padding-after">0.3em</xsl:attribute>
- <xsl:attribute name="padding-left">0.5em</xsl:attribute>
- <xsl:attribute name="padding-right">0.5em</xsl:attribute>
- <xsl:attribute name="margin-left">0.5em</xsl:attribute>
- <xsl:attribute name="margin-right">0.5em</xsl:attribute>
- <xsl:attribute name="keep-together.within-page">always</xsl:attribute>
+ <xsl:attribute-set name="warning-title">
+ <xsl:attribute name="space-before">0.5em</xsl:attribute>
+ <xsl:attribute name="border-style">solid</xsl:attribute>
+ <xsl:attribute name="border-bottom-width">0mm</xsl:attribute>
+ <xsl:attribute name="border-color">#495057</xsl:attribute>
+ <xsl:attribute name="background-color">#c92a2a</xsl:attribute>
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ <xsl:attribute name="color">#fefefe</xsl:attribute>
+ <xsl:attribute name="padding-before">1mm</xsl:attribute>
+ <xsl:attribute name="padding-after">0.5mm</xsl:attribute>
+ <xsl:attribute name="padding-left">1mm</xsl:attribute>
+ <xsl:attribute name="padding-right">1mm</xsl:attribute>
+ <xsl:attribute name="margin-left">1mm</xsl:attribute>
+ <xsl:attribute name="margin-right">1mm</xsl:attribute>
+ <xsl:attribute name="font-size">1.33em</xsl:attribute>
</xsl:attribute-set>
- <xsl:attribute-set name="note-warning-title">
- <xsl:attribute name="font-size">1.33em</xsl:attribute>
+ <xsl:attribute-set name="note-warning-content">
+ <xsl:attribute name="space-after">0.5em</xsl:attribute>
+ <xsl:attribute name="border-style">solid</xsl:attribute>
+ <xsl:attribute name="border-top-width">0mm</xsl:attribute>
+ <xsl:attribute name="border-color">#495057</xsl:attribute>
+ <xsl:attribute name="background-color">#f8f9fa</xsl:attribute>
+ <xsl:attribute name="text-align">justify</xsl:attribute>
+ <xsl:attribute name="padding-before">1mm</xsl:attribute>
+ <xsl:attribute name="padding-after">0.5mm</xsl:attribute>
+ <xsl:attribute name="padding-left">1mm</xsl:attribute>
+ <xsl:attribute name="padding-right">1mm</xsl:attribute>
+ <xsl:attribute name="margin-left">1mm</xsl:attribute>
+ <xsl:attribute name="margin-right">1mm</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="module-header">
@@ -354,7 +350,7 @@
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
<xsl:attribute name="space-after">0.25em</xsl:attribute>
<!-- xsl:attribute name="space-before">1.5em</xsl:attribute -->
- </xsl:attribute-set>
+ </xsl:attribute-set>
<xsl:attribute-set name="type-listblock">
<xsl:attribute name="provisional-distance-between-starts">1.8em</xsl:attribute>
diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index 7cdbb502d9..91422c8910 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -489,6 +489,8 @@ otp_xmlify_a_href("#"++_ = Marker, Es0) -> % <seealso marker="#what">
{Marker, Es0};
otp_xmlify_a_href("http:"++_ = URL, Es0) -> % external URL
{URL, Es0};
+otp_xmlify_a_href("https:"++_ = URL, Es0) -> % external URL
+ {URL, Es0};
otp_xmlify_a_href("OTPROOT"++AppRef, Es0) -> % <.. marker="App:FileRef
[AppS, "doc", FileRef1] = split(AppRef, "/"),
FileRef = AppS++":"++otp_xmlify_a_fileref(FileRef1, AppS),
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index 17a7c483f4..95b2329ac5 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1 +1 @@
-ERL_DOCGEN_VSN = 0.7.1
+ERL_DOCGEN_VSN = 0.7.2
diff --git a/lib/erl_interface/doc/src/Makefile b/lib/erl_interface/doc/src/Makefile
index 204a6051b2..8ef7e9648c 100644
--- a/lib/erl_interface/doc/src/Makefile
+++ b/lib/erl_interface/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1998-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1998-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
include $(ERL_TOP)/make/target.mk
@@ -36,7 +36,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
-XML_REF1_FILES = erl_call.xml
+XML_REF1_FILES = erl_call.xml
XML_REF3_FILES = erl_connect.xml \
erl_error.xml \
erl_eterm.xml \
@@ -46,16 +46,14 @@ XML_REF3_FILES = erl_connect.xml \
erl_global.xml \
ei.xml \
ei_connect.xml \
- registry.xml
+ registry.xml
BOOK_FILES = book.xml
-XML_APPLICATION_FILES = ref_man.xml
+XML_APPLICATION_FILES = ref_man.xml
#ref_man_ei.xml ref_man_erl_interface.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml \
- part_notes_history.xml
-XML_CHAPTER_FILES = ei_users_guide.xml notes.xml notes_history.xml
+ part.xml
+XML_CHAPTER_FILES = ei_users_guide.xml notes.xml
XML_FILES = $(XML_REF1_FILES) $(XML_REF3_FILES) $(BOOK_FILES) \
$(XML_APPLICATION_FILES) $(XML_PART_FILES) $(XML_CHAPTER_FILES)
@@ -66,7 +64,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
INFO_FILE = ../../info
-GIF_FILES =
+GIF_FILES =
MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1)
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
@@ -76,9 +74,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -98,7 +96,7 @@ man: $(MAN1_FILES) $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs clean_tex:
rm -rf $(HTMLDIR)/*
@@ -110,7 +108,7 @@ clean clean_docs clean_tex:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -127,4 +125,3 @@ release_docs_spec: docs
release_spec:
-
diff --git a/lib/erl_interface/doc/src/note.gif b/lib/erl_interface/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/erl_interface/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index ec20f3c67f..641a3de13f 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.10</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/doc/src/part_notes.xml b/lib/erl_interface/doc/src/part_notes.xml
deleted file mode 100644
index facdf821ee..0000000000
--- a/lib/erl_interface/doc/src/part_notes.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Erl_Interface Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>Erl_Interface</em> is a C interface library for communication
- with Erlang.</p>
- <p>For information about older versions, see
- <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/erl_interface/doc/src/part_notes_history.xml b/lib/erl_interface/doc/src/part_notes_history.xml
deleted file mode 100644
index 401fea4dd4..0000000000
--- a/lib/erl_interface/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2006</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Erl_Interface Release Notes History</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>Erl_Interface</em> is a C interface library for communication
- with Erlang.</p>
- </description>
- <xi:include href="notes_history.xml"/>
-</part>
-
diff --git a/lib/erl_interface/doc/src/warning.gif b/lib/erl_interface/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/erl_interface/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/erl_interface/src/Makefile b/lib/erl_interface/src/Makefile
index 31f34d4bba..135522397b 100644
--- a/lib/erl_interface/src/Makefile
+++ b/lib/erl_interface/src/Makefile
@@ -29,5 +29,5 @@ include $(ERL_TOP)/make/target.mk
debug opt shared purify quantify purecov gcov:
$(make_verbose)$(MAKE) -f $(TARGET)/Makefile TYPE=$@
-clean depend docs release release_docs tests release_tests check:
+clean depend docs release release_docs tests release_tests check xmllint:
$(make_verbose)$(MAKE) -f $(TARGET)/Makefile $@
diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in
index 4f393e952c..69b5b6003d 100644
--- a/lib/erl_interface/src/Makefile.in
+++ b/lib/erl_interface/src/Makefile.in
@@ -854,3 +854,5 @@ endif
release_docs:
release_tests:
+
+xmllint:
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index 01fcee86dd..d76d110afd 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1,2 +1,2 @@
-EI_VSN = 3.10
+EI_VSN = 3.10.1
ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/eunit/doc/src/Makefile b/lib/eunit/doc/src/Makefile
index f1491eb873..610e575af6 100644
--- a/lib/eunit/doc/src/Makefile
+++ b/lib/eunit/doc/src/Makefile
@@ -1,5 +1,5 @@
#<copyright>
-# <year>2004-2007</year>
+# <year>2004-2017</year>
# <holder>Ericsson AB, All Rights Reserved</holder>
#</copyright>
#<legalnotice>
@@ -51,11 +51,10 @@ EUNIT_MODULES = \
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = $(EUNIT_MODULES:=.xml)
+XML_REF3_FILES = $(EUNIT_MODULES:=.xml)
XML_PART_FILES = \
- part.xml \
- part_notes.xml
+ part.xml
XML_CHAPTER_FILES = \
chapter.xml
@@ -63,7 +62,7 @@ XML_CHAPTER_FILES = \
XML_NOTES_FILES = \
notes.xml
-HTML_EXAMPLE_FILES =
+HTML_EXAMPLE_FILES =
HTML_STYLESHEET_FILES = \
../stylesheet.css
@@ -99,10 +98,10 @@ TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -123,8 +122,8 @@ man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-$(XML_REF3_FILES):
- escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(EUNIT_VSN) -i $(EUNIT_INC_DIR) $(EUNIT_DIR)/$(@:%.xml=%.erl)
+$(XML_REF3_FILES):
+ escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(EUNIT_VSN) -i $(EUNIT_INC_DIR) $(EUNIT_DIR)/$(@:%.xml=%.erl)
$(XML_CHAPTER_FILES):
escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(EUNIT_VSN) -chapter ../overview.edoc
@@ -142,19 +141,19 @@ info:
xml: $(XML_REF3_FILES) $(XML_CHAPTER_FILES)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -169,6 +168,3 @@ release_docs_spec: docs
release_spec:
-
-
-
diff --git a/lib/eunit/doc/src/fascicules.xml b/lib/eunit/doc/src/fascicules.xml
deleted file mode 100644
index 217228785c..0000000000
--- a/lib/eunit/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index 7133befe37..b38cb2e70e 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -33,6 +33,21 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.3.4</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/eunit/doc/src/part_notes.xml b/lib/eunit/doc/src/part_notes.xml
deleted file mode 100644
index 7db65083e0..0000000000
--- a/lib/eunit/doc/src/part_notes.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2008</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>EUnit Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date>2008-10-29</date>
- <rev></rev>
-
- </header>
- <description>
- <p>The <em>EUnit</em> application
- contains modules with support for unit testing</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 25bb0dec17..2ed9eaac16 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.3.4
+EUNIT_VSN = 2.3.5
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index a3a936322a..518f67ee1b 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -1903,7 +1903,8 @@ infinity_div(Number1, Number2) when is_integer(Number1), is_integer(Number2) ->
infinity_bsl(pos_inf, _) -> pos_inf;
infinity_bsl(neg_inf, _) -> neg_inf;
-infinity_bsl(Number, pos_inf) when is_integer(Number), Number >= 0 -> pos_inf;
+infinity_bsl(0, pos_inf) -> 0;
+infinity_bsl(Number, pos_inf) when is_integer(Number), Number > 0 -> pos_inf;
infinity_bsl(Number, pos_inf) when is_integer(Number) -> neg_inf;
infinity_bsl(Number, neg_inf) when is_integer(Number), Number >= 0 -> 0;
infinity_bsl(Number, neg_inf) when is_integer(Number) -> -1;
@@ -1992,9 +1993,11 @@ arith_abs(X1, Opaques) ->
case infinity_geq(Min1, 0) of
true -> {Min1, Max1};
false ->
+ NegMin1 = infinity_inv(Min1),
+ NegMax1 = infinity_inv(Max1),
case infinity_geq(Max1, 0) of
- true -> {0, infinity_inv(Min1)};
- false -> {infinity_inv(Max1), infinity_inv(Min1)}
+ true -> {0, max(NegMin1, Max1)};
+ false -> {NegMax1, NegMin1}
end
end,
t_from_range(NewMin, NewMax)
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index abb6c259f6..2b290b2f23 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -1877,6 +1877,7 @@ t_map_put(KV, Map, Opaques) ->
%% Key and Value are *not* unopaqued, but the map is
map_put(_, ?none, _) -> ?none;
+map_put(_, ?unit, _) -> ?none;
map_put({Key, Value}, ?map(Pairs,DefK,DefV), Opaques) ->
case t_is_none_or_unit(Key) orelse t_is_none_or_unit(Value) of
true -> ?none;
@@ -1902,6 +1903,7 @@ t_map_update(KV, Map) ->
-spec t_map_update({erl_type(), erl_type()}, erl_type(), opaques()) -> erl_type().
t_map_update(_, ?none, _) -> ?none;
+t_map_update(_, ?unit, _) -> ?none;
t_map_update(KV={Key, _}, M, Opaques) ->
case t_is_subtype(t_atom('true'), t_map_is_key(Key, M, Opaques)) of
false -> ?none;
@@ -1922,6 +1924,7 @@ t_map_get(Key, Map, Opaques) ->
end).
map_get(_, ?none) -> ?none;
+map_get(_, ?unit) -> ?none;
map_get(Key, ?map(Pairs, DefK, DefV)) ->
DefRes =
case t_do_overlap(DefK, Key) of
@@ -1957,6 +1960,7 @@ t_map_is_key(Key, Map, Opaques) ->
end).
map_is_key(_, ?none) -> ?none;
+map_is_key(_, ?unit) -> ?none;
map_is_key(Key, ?map(Pairs, DefK, _DefV)) ->
case is_singleton_type(Key) of
true ->
@@ -2347,6 +2351,8 @@ t_from_range(X, Y) ->
-else.
+t_from_range(pos_inf, pos_inf) -> ?integer_pos;
+t_from_range(neg_inf, neg_inf) -> ?integer_neg;
t_from_range(neg_inf, pos_inf) -> t_integer();
t_from_range(neg_inf, Y) when is_integer(Y), Y < 0 -> ?integer_neg;
t_from_range(neg_inf, Y) when is_integer(Y), Y >= 0 -> t_integer();
@@ -2379,6 +2385,8 @@ t_from_range(pos_inf, neg_inf) -> t_none().
-spec t_from_range_unsafe(rng_elem(), rng_elem()) -> erl_type().
+t_from_range_unsafe(pos_inf, pos_inf) -> ?integer_pos;
+t_from_range_unsafe(neg_inf, neg_inf) -> ?integer_neg;
t_from_range_unsafe(neg_inf, pos_inf) -> t_integer();
t_from_range_unsafe(neg_inf, Y) -> ?int_range(neg_inf, Y);
t_from_range_unsafe(X, pos_inf) -> ?int_range(X, pos_inf);
diff --git a/lib/hipe/doc/src/Makefile b/lib/hipe/doc/src/Makefile
index a5edb80381..1c774d3357 100644
--- a/lib/hipe/doc/src/Makefile
+++ b/lib/hipe/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2016. All Rights Reserved.
+# Copyright Ericsson AB 2006-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -36,9 +36,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES =
+XML_REF3_FILES =
-XML_PART_FILES = part_notes.xml
+XML_PART_FILES = hipe_app.xml
XML_CHAPTER_FILES = notes.xml
BOOK_FILES = book.xml
@@ -47,7 +47,7 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-GIF_FILES =
+GIF_FILES =
# ----------------------------------------------------
@@ -68,9 +68,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -90,20 +90,20 @@ man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
distclean: clean
realclean: clean
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/hipe/doc/src/fascicules.xml b/lib/hipe/doc/src/fascicules.xml
deleted file mode 100644
index b15610fa8b..0000000000
--- a/lib/hipe/doc/src/fascicules.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="yes">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/hipe/doc/src/hipe_app.xml b/lib/hipe/doc/src/hipe_app.xml
index e489d155c3..9299c6d73f 100644
--- a/lib/hipe/doc/src/hipe_app.xml
+++ b/lib/hipe/doc/src/hipe_app.xml
@@ -47,6 +47,72 @@
Details on HiPE compiler options are given by <c>hipe:help_options()</c>.</p>
</description>
<section>
+ <title>Feature Limitations</title>
+ <p>
+ The HiPE compiler is in general compliant with the normal BEAM compiler,
+ with respect to semantic behavior. There are however features in the BEAM compiler
+ and the runtime system that have limited or no support for HiPE compiled modules.
+ </p>
+ <taglist>
+ <tag>Stack traces</tag>
+ <item><p>Stack traces returned from <seealso marker="erts:erlang#get_stacktrace/0">
+ <c>erlang:get_stacktrace/0</c></seealso> or as part of <c>'EXIT'</c> terms
+ can look incomplete if HiPE compiled functions are involved. Typically a stack trace
+ will contain only BEAM compiled functions or only HiPE compiled functions, depending
+ on where the exception was raised.</p>
+ <p>Source code line numbers in stack traces are also not supported by HiPE compiled functions.</p>
+ </item>
+
+ <tag>Tracing</tag>
+ <item><p>Erlang call trace is not supported by HiPE. Calling
+ <seealso marker="erts:erlang#trace_pattern/3"><c>erlang:trace_pattern({M,F,A}, ...)</c></seealso>
+ does not have any effect on HiPE compiled modules.</p>
+ </item>
+
+ <tag>NIFs</tag>
+ <item><p>Modules compiled with HiPE can not call <seealso marker="erts:erlang#load_nif-2">
+ <c>erlang:load_nif/2</c></seealso> to load NIFs.</p>
+ </item>
+
+ <tag>-on_load</tag>
+ <item><p>Modules compiled with HiPE can not use
+ <seealso marker="doc/reference_manual:code_loading#on_load"><c>-on_load()</c></seealso>
+ directives.</p>
+ </item>
+ </taglist>
+
+ </section>
+ <section>
+ <title>Performance Limitations</title>
+ <p>
+ The HiPE compiler does in general produce faster code than the
+ BEAM compiler. There are however some situation when HiPE
+ compiled code will perform worse than BEAM code.
+ </p>
+ <taglist>
+ <tag>Mode switches</tag>
+ <item><p>Every time a process changes from executing code in a
+ HiPE compiled module to a BEAM compiled module (or vice versa),
+ it will do a mode switch. This involves a certain amount of
+ CPU overhead which can have a negative net impact if the
+ process is switching back and forth without getting enough done in
+ each mode.</p>
+ </item>
+
+ <tag>Optimization for <c>receive</c> with unique references</tag>
+ <item><p>The BEAM compiler can do an optimization when
+ a <c>receive</c> statement is <em>only</em> waiting for messages
+ containing a reference created before the receive. All messages
+ that existed in the queue when the reference was created will be
+ bypassed, as they cannot possibly contain the reference. HiPE
+ does not implement this optimization.</p>
+ <p>An example of this is when
+ <c>gen_server:call()</c> waits for the reply message.</p>
+ </item>
+
+ </taglist>
+ </section>
+ <section>
<title>SEE ALSO</title>
<p>
<seealso marker="stdlib:c">c(3)</seealso>,
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index eadaee50e2..bad0c254ce 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -31,6 +31,39 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.17</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug for hipe compiled code using
+ <c>&lt;&lt;X/utf32&gt;&gt;</c> binary construction that
+ could cause faulty result or even VM crash.</p>
+ <p>
+ On architectures other than x86_64, code need to be
+ recompiled to benefit from this fix.</p>
+ <p>
+ Own Id: OTP-14740</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added documentation about limitations of hipe compared to
+ beam compiled code.</p>
+ <p>
+ Own Id: OTP-14767</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.16.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/hipe/doc/src/part_notes.xml b/lib/hipe/doc/src/part_notes.xml
deleted file mode 100644
index 828c304fb5..0000000000
--- a/lib/hipe/doc/src/part_notes.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2006</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>HiPE Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>HiPE</em> - High Performance Erlang.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/hipe/llvm/hipe_llvm.erl b/lib/hipe/llvm/hipe_llvm.erl
index 641d3fda0a..e04b171194 100644
--- a/lib/hipe/llvm/hipe_llvm.erl
+++ b/lib/hipe/llvm/hipe_llvm.erl
@@ -934,7 +934,7 @@ pp_ins(Dev, Ver, I) ->
end,
case call_is_tail(I) of
true -> write(Dev, "tail ");
- false -> ok
+ false -> write(Dev, "notail ")
end,
write(Dev, ["call ", call_cconv(I), " "]),
pp_options(Dev, call_ret_attrs(I)),
diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src
index 5b2280594f..eef4b9a34f 100644
--- a/lib/hipe/main/hipe.app.src
+++ b/lib/hipe/main/hipe.app.src
@@ -236,4 +236,4 @@
{applications, [kernel,stdlib]},
{env, []},
{runtime_dependencies, ["syntax_tools-1.6.14","stdlib-3.4","kernel-5.3",
- "erts-9.0","compiler-5.0"]}]}.
+ "erts-9.3","compiler-5.0"]}]}.
diff --git a/lib/hipe/rtl/hipe_rtl_binary_construct.erl b/lib/hipe/rtl/hipe_rtl_binary_construct.erl
index 52ea5db382..ec7044a2b9 100644
--- a/lib/hipe/rtl/hipe_rtl_binary_construct.erl
+++ b/lib/hipe/rtl/hipe_rtl_binary_construct.erl
@@ -168,9 +168,13 @@ gen_rtl(BsOP, Dst, Args, TrueLblName, FalseLblName, SystemLimitLblName, ConstTab
bs_put_utf8 ->
[_Src, _Base, _Offset] = Args,
- NewDsts = get_real(Dst),
- [hipe_rtl:mk_call(NewDsts, bs_put_utf8, Args,
- TrueLblName, FalseLblName, not_remote)];
+ [NewOffs] = get_real(Dst),
+ RetLbl = hipe_rtl:mk_new_label(),
+ [hipe_rtl:mk_call([NewOffs], bs_put_utf8, Args,
+ hipe_rtl:label_name(RetLbl), [], not_remote),
+ RetLbl,
+ hipe_rtl:mk_branch(NewOffs, ne, hipe_rtl:mk_imm(0),
+ TrueLblName, FalseLblName, 0.99)];
bs_utf16_size ->
case Dst of
@@ -195,8 +199,13 @@ gen_rtl(BsOP, Dst, Args, TrueLblName, FalseLblName, SystemLimitLblName, ConstTab
bs_validate_unicode ->
[_Arg] = Args,
- [hipe_rtl:mk_call([], bs_validate_unicode, Args,
- TrueLblName, FalseLblName, not_remote)];
+ [IsUnicode] = create_regs(1),
+ RetLbl = hipe_rtl:mk_new_label(),
+ [hipe_rtl:mk_call([IsUnicode], is_unicode, Args,
+ hipe_rtl:label_name(RetLbl), [], not_remote),
+ RetLbl,
+ hipe_rtl:mk_branch(IsUnicode, ne, hipe_rtl:mk_imm(0),
+ TrueLblName, FalseLblName, 0.99)];
bs_final ->
Zero = hipe_rtl:mk_imm(0),
diff --git a/lib/hipe/test/bs_SUITE_data/bs_construct.erl b/lib/hipe/test/bs_SUITE_data/bs_construct.erl
index b9e7d93570..aa85626857 100644
--- a/lib/hipe/test/bs_SUITE_data/bs_construct.erl
+++ b/lib/hipe/test/bs_SUITE_data/bs_construct.erl
@@ -279,13 +279,22 @@ bad_floats() ->
%% (incorrectly) signed.
huge_binaries() ->
- AlmostIllegal = id(<<0:(id((1 bsl 32)-8))>>),
case erlang:system_info(wordsize) of
- 4 -> huge_binaries_32(AlmostIllegal);
+ 4 ->
+ Old = erts_debug:set_internal_state(available_internal_state, true),
+ case erts_debug:set_internal_state(binary, (1 bsl 29)-1) of
+ false ->
+ io:format("\nNot enough memory to create 512Mb binary\n",[]);
+ Bin->
+ huge_binaries_32(Bin)
+ end,
+ erts_debug:set_internal_state(available_internal_state, Old);
+
8 -> ok
end,
garbage_collect(),
id(<<0:(id((1 bsl 31)-1))>>),
+ garbage_collect(),
id(<<0:(id((1 bsl 30)-1))>>),
garbage_collect(),
ok.
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index f88d9147b1..508ec00548 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.16.1
+HIPE_VSN = 3.17
diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile
index 19f12ac6b9..118f42b605 100644
--- a/lib/ic/doc/src/Makefile
+++ b/lib/ic/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2016. All Rights Reserved.
+# Copyright Ericsson AB 1998-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -49,8 +49,7 @@ XML_REF3_FILES = ic.xml \
ic_clib.xml \
ic_c_protocol.xml
-XML_PART_FILES = part.xml \
- part_notes.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = \
ch_introduction.xml \
@@ -68,13 +67,9 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-GIF_FILES = \
- book.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif
+GIF_FILES =
# ----------------------------------------------------
@@ -82,7 +77,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -161,12 +156,12 @@ JAVADOC_GENERATED_FILES = $(JAVADOC_PACK_HTML_FILES) $(JAVADOC_INDEX_HTML_FILES)
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
CLASSPATH = $(JAVA_SRC_ROOT):$(JAVA_INCL_ROOT)
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
JAVADOCFLAGS = \
-classpath $(CLASSPATH) \
-d ../doc/html/java \
@@ -197,7 +192,7 @@ clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
$(JAVADOC_GENERATED_FILES): JAVADOC-GENERATED
@@ -212,12 +207,12 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/ic/doc/src/book.gif b/lib/ic/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/ic/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/ic/doc/src/fascicules.xml b/lib/ic/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/ic/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/ic/doc/src/ic.gif b/lib/ic/doc/src/ic.gif
deleted file mode 100644
index d78cf7d8ed..0000000000
--- a/lib/ic/doc/src/ic.gif
+++ /dev/null
Binary files differ
diff --git a/lib/ic/doc/src/notes.gif b/lib/ic/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/ic/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index ea8bf758cf..fc68ec386c 100644
--- a/lib/ic/doc/src/notes.xml
+++ b/lib/ic/doc/src/notes.xml
@@ -31,7 +31,22 @@
<file>notes.xml</file>
</header>
- <section><title>IC 4.4.2</title>
+ <section><title>IC 4.4.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>IC 4.4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ic/doc/src/part_notes.xml b/lib/ic/doc/src/part_notes.xml
deleted file mode 100644
index 305b2c558d..0000000000
--- a/lib/ic/doc/src/part_notes.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1998</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Idl Compiler Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date>1998-05-06</date>
- <rev>2.1</rev>
- </header>
- <description>
- <p>The IDL
- Compiler Application is an Erlang implementation of a compiler for the IDL language.
- </p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/ic/doc/src/ref_man.gif b/lib/ic/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/ic/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/ic/doc/src/summary.html.src b/lib/ic/doc/src/summary.html.src
deleted file mode 100644
index cb92e51791..0000000000
--- a/lib/ic/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-IDL compiler
diff --git a/lib/ic/doc/src/user_guide.gif b/lib/ic/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/ic/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
index b3a18e03d4..446b46ad82 100644
--- a/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
@@ -1365,8 +1365,8 @@ static int cmp_strRec(m_strRec *b1, m_strRec *b2)
return 0;
if (!cmp_str(b1->str6,b2->str6))
return 0;
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
if (b1->str7[i][j] != b2->str7[i][j])
return 0;
for (j = 0; j < 3; j++)
@@ -1579,8 +1579,8 @@ static void print_strRec(m_strRec* sr)
fprintf(stdout, "\nboolean bb : %d\n",sr->bb);
fprintf(stdout, "string str4 : %s\n",sr->str4);
fprintf(stdout, "str7[2][3] :\n");
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
fprintf(stdout, "str7[%d][%d]: %ld\n", i, j, sr->str7[i][j]);
fprintf(stdout, "str5._length : %ld\n",sr->str5._length);
for (j = 0; j < sr->str5._length; j++)
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
index 40c7328f03..d6a78d2481 100644
--- a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
@@ -1369,8 +1369,8 @@ static int cmp_strRec(m_strRec *b1, m_strRec *b2)
return 0;
if (!cmp_str(b1->str6,b2->str6))
return 0;
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
if (b1->str7[i][j] != b2->str7[i][j])
return 0;
for (j = 0; j < 3; j++)
@@ -1583,8 +1583,8 @@ static void print_strRec(m_strRec* sr)
fprintf(stdout, "\nboolean bb : %d\n",sr->bb);
fprintf(stdout, "string str4 : %s\n",sr->str4);
fprintf(stdout, "str7[2][3] :\n");
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
fprintf(stdout, "str7[%d][%d]: %ld\n", i, j, sr->str7[i][j]);
fprintf(stdout, "str5._length : %ld\n",sr->str5._length);
for (j = 0; j < sr->str5._length; j++)
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
index 33cfe71322..17ef21f4f4 100644
--- a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
@@ -1369,8 +1369,8 @@ static int cmp_strRec(m_strRec *b1, m_strRec *b2)
return 0;
if (!cmp_str(b1->str6,b2->str6))
return 0;
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
if (b1->str7[i][j] != b2->str7[i][j])
return 0;
for (j = 0; j < 3; j++)
@@ -1583,8 +1583,8 @@ static void print_strRec(m_strRec* sr)
fprintf(stdout, "\nboolean bb : %d\n",sr->bb);
fprintf(stdout, "string str4 : %s\n",sr->str4);
fprintf(stdout, "str7[2][3] :\n");
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
fprintf(stdout, "str7[%d][%d]: %ld\n", i, j, sr->str7[i][j]);
fprintf(stdout, "str5._length : %ld\n",sr->str5._length);
for (j = 0; j < sr->str5._length; j++)
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index f0e5e7c266..b9f1ef7f20 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.4.2
+IC_VSN = 4.4.3
diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile
index cb71fbeb9c..cbfa5c9e30 100644
--- a/lib/inets/doc/src/Makefile
+++ b/lib/inets/doc/src/Makefile
@@ -39,6 +39,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
XML_APPLICATION_FILES = ref_man.xml
XML_CHAPTER_FILES = \
+ introduction.xml \
inets_services.xml \
http_client.xml \
http_server.xml \
@@ -58,7 +59,7 @@ XML_REF3_FILES = \
mod_alias.xml \
mod_auth.xml \
mod_esi.xml \
- mod_security.xml
+ mod_security.xml
XML_PART_FILES = \
part.xml
@@ -83,7 +84,7 @@ HTML_FILES = \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
@@ -95,10 +96,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -119,25 +120,25 @@ html: gifs $(HTML_REF_MAN_FILE)
clean clean_docs: clean_html clean_man clean_pdf
rm -f errs core *~
-man: $(MAN3_FILES)
+man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
-clean_pdf:
+clean_pdf:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
-clean_html:
+clean_html:
rm -rf $(TOP_HTML_FILES) $(HTMLDIR)/*
clean_man:
- rm -f $(MAN3_FILES)
+ rm -f $(MAN3_FILES)
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/inets/doc/src/book.gif b/lib/inets/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/inets/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/doc/src/fascicules.xml b/lib/inets/doc/src/fascicules.xml
deleted file mode 100644
index c075478967..0000000000
--- a/lib/inets/doc/src/fascicules.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
-
diff --git a/lib/inets/doc/src/inets.gif b/lib/inets/doc/src/inets.gif
deleted file mode 100644
index 64968ae68a..0000000000
--- a/lib/inets/doc/src/inets.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/doc/src/min_head.gif b/lib/inets/doc/src/min_head.gif
deleted file mode 100644
index 67948a6378..0000000000
--- a/lib/inets/doc/src/min_head.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml
index a8393c9248..d024c8afa8 100644
--- a/lib/inets/doc/src/mod_esi.xml
+++ b/lib/inets/doc/src/mod_esi.xml
@@ -142,7 +142,7 @@
to the client and <c>SessionID</c> is an identifier that shall by used when
calling this function, do not assume anything about
the datatype. This function may be called
- several times to chunk the the respons data. Notice that the
+ several times to chunk the response data. Notice that the
first chunk of data sent to the client must at least contain
all HTTP header fields that the response will generate. If the
first chunk does not contain the <em>end of HTTP header</em>,
diff --git a/lib/inets/doc/src/note.gif b/lib/inets/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/inets/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/doc/src/notes.gif b/lib/inets/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/inets/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 07e29b5542..70b2811c0e 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,37 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.4.4</title>
+ <section><title>Inets 6.4.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ CGI environment variable CONTENT_LENGTH shall be a string</p>
+ <p>
+ Own Id: OTP-14679</p>
+ </item>
+ <item>
+ <p>
+ In relaxed mode disregard Content-Length header if there
+ is also a Transfer-Encoding header.</p>
+ <p>
+ Own Id: OTP-14727</p>
+ </item>
+ <item>
+ <p>
+ Eliminated race condition, that could cause http request
+ to sporadically fail to complete successfully, when
+ keep-alive connections are used.</p>
+ <p>
+ Own Id: OTP-14783</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.4.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/doc/src/part_notes.xml b/lib/inets/doc/src/part_notes.xml
deleted file mode 100644
index d10c829f4a..0000000000
--- a/lib/inets/doc/src/part_notes.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2002</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Inets</title>
- <prepared>Micael Karlberg</prepared>
- <docno></docno>
- <date>2002-02-28</date>
- <rev>3.0</rev>
- <file>part_notes.sgml</file>
- </header>
- <description>
- <p>A set of services such as a Web server and a ftp client etc. </p>
- <p>For information about older versions see
- <url href="part_notes_history_frame.html">release notes history</url>.</p>
- </description>
- <xi:include file="notes.xml"/>
-</part>
-
-
diff --git a/lib/inets/doc/src/part_notes_history.xml b/lib/inets/doc/src/part_notes_history.xml
deleted file mode 100644
index 66e3307f69..0000000000
--- a/lib/inets/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Inets</title>
- <prepared>Micael Karlberg</prepared>
- <docno></docno>
- <date>2002-02-28</date>
- <rev>3.0</rev>
- <file>part_notes.sgml</file>
- </header>
- <include file="notes_history"></include>
-</part>
-
-
diff --git a/lib/inets/doc/src/ref_man.gif b/lib/inets/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/inets/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/doc/src/summary.html.src b/lib/inets/doc/src/summary.html.src
deleted file mode 100644
index 17637a0787..0000000000
--- a/lib/inets/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-A set of services such as a web server and a ftp client etc \ No newline at end of file
diff --git a/lib/inets/doc/src/user_guide.gif b/lib/inets/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/inets/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/doc/src/warning.gif b/lib/inets/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/inets/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index bd1d2e833a..a4e406b807 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -109,7 +109,7 @@ start_link(Parent, Request, Options, ProfileName) ->
%% to be called by the httpc manager process.
%%--------------------------------------------------------------------
send(Request, Pid) ->
- call(Request, Pid, 5000).
+ call(Request, Pid).
%%--------------------------------------------------------------------
@@ -711,13 +711,17 @@ do_handle_info({'EXIT', _, _}, State = #state{request = undefined}) ->
%% can retry requests in the pipeline.
do_handle_info({'EXIT', _, _}, State) ->
{noreply, State#state{status = close}}.
-
call(Msg, Pid) ->
- call(Msg, Pid, infinity).
-
-call(Msg, Pid, Timeout) ->
- gen_server:call(Pid, Msg, Timeout).
+ try gen_server:call(Pid, Msg, infinity)
+ catch
+ exit:{noproc, _} ->
+ {error, closed};
+ exit:{normal, _} ->
+ {error, closed};
+ exit:{{shutdown, _},_} ->
+ {error, closed}
+ end.
cast(Msg, Pid) ->
gen_server:cast(Pid, Msg).
@@ -736,7 +740,7 @@ maybe_send_answer(Request, Answer, State) ->
answer_request(Request, Answer, State).
deliver_answer(#request{from = From} = Request)
- when is_pid(From) ->
+ when From =/= answer_sent ->
Response = httpc_response:error(Request, socket_closed_remotely),
httpc_response:send(From, Response);
deliver_answer(_Request) ->
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index a63864493f..ffdf1603b3 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -849,11 +849,11 @@ pipeline_or_keep_alive(#request{id = Id,
from = From} = Request,
HandlerPid,
#state{handler_db = HandlerDb} = State) ->
- case (catch httpc_handler:send(Request, HandlerPid)) of
+ case httpc_handler:send(Request, HandlerPid) of
ok ->
HandlerInfo = {Id, HandlerPid, From},
ets:insert(HandlerDb, HandlerInfo);
- _ -> % timeout pipelining failed
+ {error, closed} -> % timeout pipelining failed
start_handler(Request, State)
end.
diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl
index b3b11b74ab..91638f5d2e 100644
--- a/lib/inets/src/http_client/httpc_response.erl
+++ b/lib/inets/src/http_client/httpc_response.erl
@@ -269,7 +269,7 @@ parse_headers(<<?LF,?LF,Body/binary>>, Header, Headers,
MaxHeaderSize, Result, Relaxed);
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers,
- MaxHeaderSize, Result, _) ->
+ MaxHeaderSize, Result, Relaxed) ->
HTTPHeaders = [lists:reverse(Header) | Headers],
Length = lists:foldl(fun(H, Acc) -> length(H) + Acc end,
0, HTTPHeaders),
@@ -277,8 +277,42 @@ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers,
true ->
ResponseHeaderRcord =
http_response:headers(HTTPHeaders, #http_response_h{}),
- {ok, list_to_tuple(
- lists:reverse([Body, ResponseHeaderRcord | Result]))};
+
+ %% RFC7230, Section 3.3.3
+ %% If a message is received with both a Transfer-Encoding and a
+ %% Content-Length header field, the Transfer-Encoding overrides the
+ %% Content-Length. Such a message might indicate an attempt to
+ %% perform request smuggling (Section 9.5) or response splitting
+ %% (Section 9.4) and ought to be handled as an error. A sender MUST
+ %% remove the received Content-Length field prior to forwarding such
+ %% a message downstream.
+ case ResponseHeaderRcord#http_response_h.'transfer-encoding' of
+ undefined ->
+ {ok, list_to_tuple(
+ lists:reverse([Body, ResponseHeaderRcord | Result]))};
+ Value ->
+ TransferEncoding = string:lowercase(Value),
+ ContentLength = ResponseHeaderRcord#http_response_h.'content-length',
+ if
+ %% Respond without error but remove Content-Length field in relaxed mode
+ (Relaxed =:= true)
+ andalso (TransferEncoding =:= "chunked")
+ andalso (ContentLength =/= "-1") ->
+ ResponseHeaderRcordFixed =
+ ResponseHeaderRcord#http_response_h{'content-length' = "-1"},
+ {ok, list_to_tuple(
+ lists:reverse([Body, ResponseHeaderRcordFixed | Result]))};
+ %% Respond with error in default (not relaxed) mode
+ (Relaxed =:= false)
+ andalso (TransferEncoding =:= "chunked")
+ andalso (ContentLength =/= "-1") ->
+ throw({error, {headers_conflict, {'content-length',
+ 'transfer-encoding'}}});
+ true ->
+ {ok, list_to_tuple(
+ lists:reverse([Body, ResponseHeaderRcord | Result]))}
+ end
+ end;
false ->
throw({error, {header_too_long, MaxHeaderSize,
MaxHeaderSize-Length}})
diff --git a/lib/inets/src/http_server/httpd_script_env.erl b/lib/inets/src/http_server/httpd_script_env.erl
index 055f08fdb0..d7c92c59ef 100644
--- a/lib/inets/src/http_server/httpd_script_env.erl
+++ b/lib/inets/src/http_server/httpd_script_env.erl
@@ -166,9 +166,9 @@ create_script_elements(cgi, path_info, PathInfo, ModData) ->
[{"PATH_INFO", PathInfo},
{"PATH_TRANSLATED", PathTranslated}];
create_script_elements(esi, entity_body, Body, _) ->
- [{content_length, httpd_util:flatlength(Body)}];
+ [{content_length, integer_to_list(httpd_util:flatlength(Body))}];
create_script_elements(cgi, entity_body, Body, _) ->
- [{"CONTENT_LENGTH", httpd_util:flatlength(Body)}];
+ [{"CONTENT_LENGTH", integer_to_list(httpd_util:flatlength(Body))}];
create_script_elements(_, _, _, _) ->
[].
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index fdf4cc6e07..e24a4a8694 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,13 +18,13 @@
%% %CopyrightEnd%
{"%VSN%",
[
- {<<"6.4.3">>, [{load_module, httpd_esi,
+ {<<"6.4.5">>, [{load_module, httpc_handler,
soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
- {<<"6.4.3">>, [{load_module, httpd_esi,
+ {<<"6.4.5">>, [{load_module, httpc_handler,
soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index ffc512050a..99a7e6a9db 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -150,6 +150,7 @@ INETS_ROOT = ../../inets
MODULES = \
inets_test_lib \
erl_make_certs \
+ make_certs \
ftp_SUITE \
ftp_format_SUITE \
http_format_SUITE \
@@ -157,10 +158,10 @@ MODULES = \
httpc_cookie_SUITE \
httpc_proxy_SUITE \
httpd_SUITE \
- old_httpd_SUITE \
+ httpd_bench_SUITE \
+ http_test_lib \
httpd_basic_SUITE \
httpd_mod \
- httpd_block \
httpd_load \
httpd_time_test \
httpd_1_1 \
@@ -189,7 +190,7 @@ SOURCE = $(ERL_FILES) $(HRL_FILES)
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-INETS_SPECS = inets.spec
+INETS_SPECS = inets.spec inets_bench.spec
COVER_FILE = inets.cover
INETS_FILES = inets.config $(INETS_SPECS)
@@ -200,8 +201,10 @@ INETS_FILES = inets.config $(INETS_SPECS)
# inets_ftp_suite \
# inets_tftp_suite
+
INETS_DATADIRS = inets_SUITE_data inets_socketwrap_SUITE_data
-HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data httpd_basic_SUITE_data old_httpd_SUITE_data
+HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data httpd_basic_SUITE_data old_httpd_SUITE_data httpd_bench_SUITE_data
+
HTTPC_DATADIRS = httpc_SUITE_data httpc_proxy_SUITE_data
FTP_DATADIRS = ftp_SUITE_data
diff --git a/lib/inets/test/http_test_lib.erl b/lib/inets/test/http_test_lib.erl
new file mode 100644
index 0000000000..38e9e4976e
--- /dev/null
+++ b/lib/inets/test/http_test_lib.erl
@@ -0,0 +1,180 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(http_test_lib).
+
+-include_lib("common_test/include/ct.hrl").
+-include("inets_test_lib.hrl").
+-include("http_internal.hrl").
+-include("httpc_internal.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+dummy_server(SocketType, Inet, Extra) ->
+ dummy_server(self(), SocketType, Inet, Extra).
+
+dummy_server(Caller, SocketType, Inet, Extra) ->
+ Args = [Caller, SocketType, Inet, Extra],
+ Pid = spawn(?MODULE, dummy_server_init, Args),
+ receive
+ {port, Port} ->
+ {Pid, Port}
+ end.
+
+dummy_server_init(Caller, ip_comm, Inet, Extra) ->
+ ContentCb = proplists:get_value(content_cb, Extra),
+ BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}, {nodelay, true}],
+ Conf = proplists:get_value(conf, Extra),
+ {ok, ListenSocket} = gen_tcp:listen(0, [Inet | BaseOpts]),
+ {ok, Port} = inet:port(ListenSocket),
+ Caller ! {port, Port},
+ dummy_ipcomm_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]},
+ [], ContentCb, Conf, ListenSocket);
+
+dummy_server_init(Caller, ssl, Inet, Extra) ->
+ ContentCb = proplists:get_value(content_cb, Extra),
+ SSLOptions = proplists:get_value(ssl, Extra),
+ Conf = proplists:get_value(conf, Extra),
+ BaseOpts = [binary, {reuseaddr,true}, {active, false}, {nodelay, true} |
+ SSLOptions],
+ dummy_ssl_server_init(Caller, BaseOpts, Inet, ContentCb, Conf).
+
+dummy_ssl_server_init(Caller, BaseOpts, Inet, ContentCb, Conf) ->
+ {ok, ListenSocket} = ssl:listen(0, [Inet | BaseOpts]),
+ {ok, {_, Port}} = ssl:sockname(ListenSocket),
+ Caller ! {port, Port},
+ dummy_ssl_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]},
+ [], ContentCb, Conf, ListenSocket).
+
+dummy_ipcomm_server_loop(MFA, Handlers, ContentCb, Conf, ListenSocket) ->
+ receive
+ stop ->
+ lists:foreach(fun(Handler) -> Handler ! stop end, Handlers);
+ {stop, From} ->
+ Stopper = fun(Handler) -> Handler ! stop end,
+ lists:foreach(Stopper, Handlers),
+ From ! {stopped, self()}
+ after 0 ->
+ {ok, Socket} = gen_tcp:accept(ListenSocket),
+ HandlerPid = dummy_request_handler(MFA, Socket, ContentCb, Conf),
+ gen_tcp:controlling_process(Socket, HandlerPid),
+ HandlerPid ! ipcomm_controller,
+ dummy_ipcomm_server_loop(MFA, [HandlerPid | Handlers],
+ ContentCb, Conf, ListenSocket)
+ end.
+
+dummy_ssl_server_loop(MFA, Handlers, ContentCb, Conf, ListenSocket) ->
+ receive
+ stop ->
+ lists:foreach(fun(Handler) -> Handler ! stop end, Handlers);
+ {stop, From} ->
+ Stopper = fun(Handler) -> Handler ! stop end,
+ lists:foreach(Stopper, Handlers),
+ From ! {stopped, self()}
+ after 0 ->
+ {ok, Socket} = ssl:transport_accept(ListenSocket),
+ HandlerPid = dummy_request_handler(MFA, Socket, ContentCb, Conf),
+ ssl:controlling_process(Socket, HandlerPid),
+ HandlerPid ! ssl_controller,
+ dummy_ssl_server_loop(MFA, [HandlerPid | Handlers],
+ ContentCb, Conf, ListenSocket)
+ end.
+
+dummy_request_handler(MFA, Socket, ContentCb, Conf) ->
+ spawn(?MODULE, dummy_request_handler_init, [MFA, Socket, ContentCb, Conf]).
+
+dummy_request_handler_init(MFA, Socket, ContentCb, Conf) ->
+ SockType =
+ receive
+ ipcomm_controller ->
+ inet:setopts(Socket, [{active, true}]),
+ ip_comm;
+ ssl_controller ->
+ ok = ssl:ssl_accept(Socket, infinity),
+ ssl:setopts(Socket, [{active, true}]),
+ ssl
+ end,
+ dummy_request_handler_loop(MFA, SockType, Socket, ContentCb, Conf).
+
+dummy_request_handler_loop({Module, Function, Args}, SockType, Socket, ContentCb, Conf) ->
+ receive
+ {Proto, _, Data} when (Proto =:= tcp) orelse (Proto =:= ssl) ->
+ case handle_request(Module, Function, [Data | Args], Socket, ContentCb, Conf) of
+ stop when Proto =:= tcp ->
+ gen_tcp:close(Socket);
+ stop when Proto =:= ssl ->
+ ssl:close(Socket);
+ NewMFA ->
+ dummy_request_handler_loop(NewMFA, SockType, Socket, ContentCb, Conf)
+ end;
+ stop when SockType =:= ip_comm ->
+ gen_tcp:close(Socket);
+ stop when SockType =:= ssl ->
+ ssl:close(Socket)
+ end.
+
+handle_request(Module, Function, Args, Socket, ContentCb, Conf) ->
+ case Module:Function(Args) of
+ {ok, Result} ->
+ case ContentCb:handle_http_msg(Result, Socket, Conf) of
+ stop ->
+ stop;
+ <<>> ->
+ {httpd_request, parse, [[{max_uri,?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]};
+ Data ->
+ handle_request(httpd_request, parse,
+ [Data, [{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]], Socket, ContentCb, Conf)
+ end;
+ NewMFA ->
+ NewMFA
+ end.
+
+%% Perform a synchronous stop
+dummy_server_stop(Pid) ->
+ Pid ! {stop, self()},
+ receive
+ {stopped, Pid} ->
+ ok
+ end.
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index e6dcd2285f..f7e5c11272 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -42,13 +42,15 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds, 30}}
].
all() ->
[
{group, http},
{group, sim_http},
+ {group, http_internal},
{group, https},
{group, sim_https},
{group, misc}
@@ -57,7 +59,8 @@ all() ->
groups() ->
[
{http, [], real_requests()},
- {sim_http, [], only_simulated()},
+ {sim_http, [], only_simulated() ++ [process_leak_on_keepalive]},
+ {http_internal, [], real_requests_esi()},
{https, [], real_requests()},
{sim_https, [], only_simulated()},
{misc, [], misc()}
@@ -92,6 +95,9 @@ real_requests()->
invalid_body
].
+real_requests_esi() ->
+ [slow_connection].
+
only_simulated() ->
[
cookie,
@@ -114,10 +120,10 @@ only_simulated() ->
invalid_chunk_size,
headers_dummy,
headers_with_obs_fold,
+ headers_conflict_chunked_with_length,
empty_response_header,
remote_socket_close,
remote_socket_close_async,
- process_leak_on_keepalive,
transfer_encoding,
transfer_encoding_identity,
redirect_loop,
@@ -129,7 +135,8 @@ only_simulated() ->
port_in_host_header,
redirect_port_in_host_header,
relaxed,
- multipart_chunks
+ multipart_chunks,
+ stream_fun_server_close
].
misc() ->
@@ -142,7 +149,6 @@ misc() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- ct:timetrap({seconds, 30}),
PrivDir = proplists:get_value(priv_dir, Config),
DataDir = proplists:get_value(data_dir, Config),
inets_test_lib:start_apps([inets]),
@@ -169,7 +175,6 @@ init_per_group(Group, Config0) when Group =:= sim_https; Group =:= https->
catch crypto:stop(),
try crypto:start() of
ok ->
- ct:timetrap({seconds, 30}),
start_apps(Group),
do_init_per_group(Group, Config0)
catch
@@ -746,7 +751,7 @@ empty_body() ->
empty_body(Config) when is_list(Config) ->
URL = url(group_name(Config), "/empty.html", Config),
{ok, {{_,200,_}, [_ | _], []}} =
- httpc:request(get, {URL, []}, [{timeout, 500}], []).
+ httpc:request(get, {URL, []}, [], []).
%%-------------------------------------------------------------------------
@@ -978,7 +983,6 @@ headers_dummy(Config) when is_list(Config) ->
{"If-Range", "Sat, 29 Oct 1994 19:43:31 GMT"},
{"If-Match", "*"},
{"Content-Type", "text/plain"},
- {"Content-Encoding", "chunked"},
{"Content-Length", "6"},
{"Content-Language", "en"},
{"Content-Location", "http://www.foobar.se"},
@@ -1004,6 +1008,18 @@ headers_with_obs_fold(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
+headers_conflict_chunked_with_length(doc) ->
+ ["Test the code for handling headers with both Transfer-Encoding"
+ "and Content-Length which must receive error in default (not relaxed) mode"
+ "and must receive successful response in relaxed mode"];
+headers_conflict_chunked_with_length(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/headers_conflict_chunked_with_length.html", Config), []},
+ {error, {could_not_parse_as_http, _}} = httpc:request(get, Request, [{relaxed, false}], []),
+ {ok,{{_,200,_},_,_}} = httpc:request(get, Request, [{relaxed, true}], []),
+ ok.
+
+%%-------------------------------------------------------------------------
+
invalid_headers(Config) ->
Request = {url(group_name(Config), "/dummy.html", Config), [{"cookie", undefined}]},
{error, _} = httpc:request(get, Request, [], []).
@@ -1179,6 +1195,40 @@ wait_for_whole_response(Config) when is_list(Config) ->
ReqSeqNumServer ! shutdown.
%%--------------------------------------------------------------------
+stream_fun_server_close() ->
+ [{doc, "Test that an error msg is received when using a receiver fun as stream target"}].
+stream_fun_server_close(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/delay_close.html", Config), []},
+ Self = self(),
+ Fun = fun(X) -> Self ! X end,
+ {ok, RequestId} = httpc:request(get, Request, [], [{sync, false}, {receiver, Fun}]),
+ receive
+ {RequestId, {error, Reason}} ->
+ ct:pal("Close ~p", [Reason]),
+ ok
+ after 13000 ->
+ ct:fail(did_not_receive_close)
+ end.
+
+%%--------------------------------------------------------------------
+slow_connection() ->
+ [{doc, "Test that a request on a slow keep-alive connection won't crash the httpc_manager"}].
+slow_connection(Config) when is_list(Config) ->
+ BodyFun = fun(0) -> eof;
+ (LenLeft) -> timer:sleep(1000),
+ {ok, lists:duplicate(10, "1"), LenLeft - 10}
+ end,
+ Request = {url(group_name(Config), "/httpc_SUITE:esi_post", Config),
+ [{"content-length", "100"}],
+ "text/plain",
+ {BodyFun, 100}},
+ {ok, _} = httpc:request(post, Request, [], []),
+ %% Second request causes a crash if gen_server timeout is not set to infinity
+ %% in httpc_handler.
+ {ok, _} = httpc:request(post, Request, [], []).
+
+
+%%--------------------------------------------------------------------
%% Internal Functions ------------------------------------------------
%%--------------------------------------------------------------------
stream(ReceiverPid, Receiver, Config) ->
@@ -1271,6 +1321,8 @@ url(https, End, Config) ->
?TLS_URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End;
url(sim_http, End, Config) ->
url(http, End, Config);
+url(http_internal, End, Config) ->
+ url(http, End, Config);
url(sim_https, End, Config) ->
url(https, End, Config).
url(http, UserInfo, End, Config) ->
@@ -1291,13 +1343,13 @@ group_name(Config) ->
server_start(sim_http, _) ->
Inet = inet_version(),
ok = httpc:set_options([{ipfamily, Inet}]),
- {_Pid, Port} = dummy_server(Inet),
+ {_Pid, Port} = http_test_lib:dummy_server(ip_comm, Inet, [{content_cb, ?MODULE}]),
Port;
server_start(sim_https, SslConfig) ->
Inet = inet_version(),
ok = httpc:set_options([{ipfamily, Inet}]),
- {_Pid, Port} = dummy_server(ssl, Inet, SslConfig),
+ {_Pid, Port} = http_test_lib:dummy_server(ssl, Inet, [{ssl, SslConfig}, {content_cb, ?MODULE}]),
Port;
server_start(_, HttpdConfig) ->
@@ -1317,7 +1369,17 @@ server_config(http, Config) ->
{mime_type, "text/plain"},
{script_alias, {"/cgi-bin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}}
];
-
+server_config(http_internal, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ [{port, 0},
+ {server_name,"httpc_test"},
+ {server_root, ServerRoot},
+ {document_root, proplists:get_value(doc_root, Config)},
+ {bind_address, any},
+ {ipfamily, inet_version()},
+ {mime_type, "text/plain"},
+ {erl_script_alias, {"", [httpc_SUITE]}}
+ ];
server_config(https, Config) ->
[{socket_type, {essl, ssl_config(Config)}} | server_config(http, Config)];
server_config(sim_https, Config) ->
@@ -1325,6 +1387,9 @@ server_config(sim_https, Config) ->
server_config(_, _) ->
[].
+esi_post(Sid, _Env, _Input) ->
+ mod_esi:deliver(Sid, ["OK"]).
+
start_apps(https) ->
inets_test_lib:start_apps([crypto, public_key, ssl]);
start_apps(sim_https) ->
@@ -1401,13 +1466,7 @@ receive_replys([ID|IDs]) ->
ct:pal({recived_canceld_id, Other})
end.
-%% Perform a synchronous stop
-dummy_server_stop(Pid) ->
- Pid ! {stop, self()},
- receive
- {stopped, Pid} ->
- ok
- end.
+
inet_version() ->
inet. %% Just run inet for now
@@ -1535,7 +1594,7 @@ dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) ->
handle_request(Module, Function, Args, Socket) ->
case Module:Function(Args) of
{ok, Result} ->
- case handle_http_msg(Result, Socket) of
+ case handle_http_msg(Result, Socket, []) of
stop ->
stop;
<<>> ->
@@ -1560,8 +1619,7 @@ handle_request(Module, Function, Args, Socket) ->
NewMFA
end.
-handle_http_msg({Method, RelUri, _, {_, Headers}, Body}, Socket) ->
-
+handle_http_msg({Method, RelUri, _, {_, Headers}, Body}, Socket, _) ->
ct:print("Request: ~p ~p", [Method, RelUri]),
NextRequest =
@@ -1853,7 +1911,6 @@ handle_uri(_,"/dummy_headers.html",_,_,Socket,_) ->
%% user to evaluate. This is not a valid response
%% it only tests that the header handling code works.
Head = "HTTP/1.1 200 ok\r\n" ++
- "Content-Length:32\r\n" ++
"Pragma:1#no-cache\r\n" ++
"Via:1.0 fred, 1.1 nowhere.com (Apache/1.1)\r\n" ++
"Warning:1#pseudonym foobar\r\n" ++
@@ -1883,6 +1940,15 @@ handle_uri(_,"/obs_folded_headers.html",_,_,_,_) ->
" b\r\n\r\n"
"Hello";
+handle_uri(_,"/headers_conflict_chunked_with_length.html",_,_,Socket,_) ->
+ Head = "HTTP/1.1 200 ok\r\n"
+ "Content-Length:32\r\n"
+ "Transfer-Encoding:Chunked\r\n\r\n",
+ send(Socket, Head),
+ send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ send(Socket, http_chunk:encode("obar</BODY></HTML>")),
+ http_chunk:encode_last();
+
handle_uri(_,"/capital_transfer_encoding.html",_,_,Socket,_) ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Transfer-Encoding:Chunked\r\n\r\n",
@@ -2030,6 +2096,9 @@ handle_uri(_,"/multipart_chunks.html",_,_,Socket,_) ->
send(Socket, Head),
send_multipart_chunks(Socket),
http_chunk:encode_last();
+handle_uri(_,"/delay_close.html",_,_,Socket,_) ->
+ ct:sleep(10000),
+ close(Socket);
handle_uri("HEAD",_,_,_,_,_) ->
"HTTP/1.1 200 ok\r\n" ++
"Content-Length:0\r\n\r\n";
diff --git a/lib/inets/test/httpd_bench_SUITE.erl b/lib/inets/test/httpd_bench_SUITE.erl
new file mode 100644
index 0000000000..9d8cbf9ae2
--- /dev/null
+++ b/lib/inets/test/httpd_bench_SUITE.erl
@@ -0,0 +1,846 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+%%
+-module(httpd_bench_SUITE).
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+-include_lib("public_key/include/public_key.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-define(remote_host, "NETMARKS_REMOTE_HOST").
+-define(LF, [10]).
+-define(CR, [13]).
+-define(CRLF, ?CR ++ ?LF).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
+
+all() ->
+ [
+ {group, http_dummy},
+ {group, http_inets},
+ {group, http_nginx},
+ {group, https_inets},
+ {group, https_dummy},
+ {group, https_nginx},
+ {group, http_dummy_keep_alive},
+ {group, http_inets_keep_alive},
+ {group, http_nginx_keep_alive},
+ {group, https_inets_keep_alive},
+ {group, https_dummy_keep_alive},
+ {group, https_nginx_keep_alive}
+ ].
+
+groups() ->
+ [
+ {http_dummy, [], client_tests()},
+ {http_inets, [], client_tests()},
+ {http_nginx, [], client_tests()},
+ {https_dummy, [], client_tests()},
+ {https_inets, [], client_tests()},
+ {https_nginx, [], client_tests()},
+ {http_dummy_keep_alive, [], client_tests()},
+ {http_inets_keep_alive, [], client_tests()},
+ {http_nginx_keep_alive, [], client_tests()},
+ {https_dummy_keep_alive, [], client_tests()},
+ {https_inets_keep_alive, [], client_tests()},
+ {https_nginx_keep_alive, [], client_tests()}
+ ].
+
+
+client_tests() ->
+ [wget_small,
+ erl_dummy_small,
+ httpc_small,
+ wget_big,
+ erl_dummy_big,
+ httpc_big
+ ].
+
+init_per_suite(Config) ->
+ try
+ {Node, Host} = setup(Config, node()),
+ init_ssl(Config),
+ [{iter, 10}, {server_node, Node}, {server_host, Host} | Config]
+ catch _:_ ->
+ {skipped, "Benchmark machines only"}
+ end.
+
+end_per_suite(_Config) ->
+ [application:stop(App) || App <- [asn1, crypto, public_key, ssl, inets]].
+
+init_per_group(Group, Config) when Group == http_dummy_keep_alive;
+ Group == https_dummy_keep_alive;
+ Group == http_inets_keep_alive;
+ Group == https_inets_keep_alive;
+ Group == http_nginx_keep_alive;
+ Group == https_nginx_keep_alive ->
+ Version = http_version(Group),
+ start_web_server(Group,
+ [{keep_alive, true},
+ {reuse_sessions, false},
+ {http_version, Version},
+ {http_opts,[{version, Version}]},
+ {http_headers, [{"connection", "keep-alive"}]},
+ {httpc_opts, [{keep_alive_timeout, 1500},
+ {max_keep_alive_length, ?config(iter, Config)}]}
+ | Config]);
+init_per_group(Group, Config) when Group == http_dummy;
+ Group == https_dummy;
+ Group == http_inets;
+ Group == https_inets;
+ Group == http_nginx;
+ Group == https_nginx ->
+ Version = http_version(Group),
+ start_web_server(Group,
+ [{keep_alive, false},
+ {reuse_sessions, false},
+ {http_version, Version},
+ {http_headers, [{"connection", "close"}]},
+ {http_opts,[{version, Version}]},
+ {httpc_opts, [{keep_alive_timeout, 0}, {max_keep_alive_length, 0}]}
+ | Config]);
+
+
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(Group, Config) ->
+ stop_web_server(Group, Config).
+
+init_per_testcase(TestCase, Config) when TestCase == httpc_small;
+ TestCase == httpc_big
+ ->
+ Opts = ?config(httpc_opts, Config),
+ inets:start(httpc, [{profile, TestCase}, {socket_opts, [{nodelay, true}]}]),
+ httpc:set_options(Opts, TestCase),
+ [{profile, TestCase} | proplists:delete(profile, Config)];
+
+init_per_testcase(_, Config) ->
+ Config.
+end_per_testcase(TestCase, _Config) when TestCase == httpc_small;
+ TestCase == httpc_big ->
+ ok = inets:stop(httpc, TestCase);
+end_per_testcase(_TestCase, Config) ->
+ Config.
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+erl_dummy_small(Config) when is_list(Config) ->
+ {ok, Result} = run_test(httpd_lib_client, "1k_file", Config),
+ notify(Result, Config, "erl_1k_file").
+
+erl_dummy_big(Config) when is_list(Config) ->
+ {ok, Result} = run_test(httpd_lib_client, "1M_file", Config),
+ notify(Result, Config, "erl_1M_file").
+
+wget_small(Config) when is_list(Config) ->
+ {ok, Result} = run_test(wget_client, "1k_file", Config),
+ notify(Result, Config, "wget_1k_file").
+
+wget_big(Config) when is_list(Config) ->
+ {ok, Result} = run_test(wget_client, "1M_file", Config),
+ notify(Result, Config, "wget_1M_file").
+
+httpc_small(Config) when is_list(Config) ->
+ {ok, Result} = run_test(httpc_client, "1k_file", Config),
+ notify(Result, Config, "httpc_1k_file").
+
+httpc_big(Config) when is_list(Config) ->
+ {ok, Result} = run_test(httpc_client, "1M_file", Config),
+ notify(Result, Config, "httpc_1M_file").
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Report benchmark results ------------------------------------------------
+%%--------------------------------------------------------------------
+
+notify({TestPerSec, _MBps}, Config, Suffix) ->
+ Name = lists:concat([?config(protocol,Config), " ",
+ server_name(Config, [dummy_pid, httpd_pid, nginx_port]),
+ "", Suffix]),
+ ct:comment("~p tps", [TestPerSec]),
+ ct_event:notify(#event{name = benchmark_data,
+ data=[{value, TestPerSec},
+ {suite, ?MODULE},
+ {name, Name}]}),
+ ok.
+%%--------------------------------------------------------------------
+%% Setup erlang nodes ------------------------------------------------
+%%--------------------------------------------------------------------
+
+server_name(Config, [Server | Rest]) ->
+ case proplists:get_value(Server, Config) of
+ undefined ->
+ server_name(Config, Rest);
+ _ ->
+ server_name(Server)
+ end.
+
+server_name(httpd_pid) ->
+ "inets";
+server_name(nginx_port) ->
+ "nginx";
+server_name(dummy_pid) ->
+ "erlang".
+
+setup(_Config, nonode@nohost) ->
+ exit(dist_not_enabled);
+setup(_Config, _LocalNode) ->
+ Host = case os:getenv(?remote_host) of
+ false ->
+ {ok, This} = inet:gethostname(),
+ This;
+ RemHost ->
+ RemHost
+ end,
+ Node = list_to_atom("inets_perf_server@" ++ Host),
+ SlaveArgs = case init:get_argument(pa) of
+ {ok, PaPaths} ->
+ lists:append([" -pa " ++ P || [P] <- PaPaths]);
+ _ -> []
+ end,
+ Prog =
+ case os:find_executable("erl") of
+ false -> "erl";
+ P -> P
+ end,
+ case net_adm:ping(Node) of
+ pong -> ok;
+ pang ->
+ {ok, Node} = slave:start(Host, inets_perf_server, SlaveArgs, no_link, Prog)
+ end,
+ Path = code:get_path(),
+ true = rpc:call(Node, code, set_path, [Path]),
+ [ensure_started(Node, App) || App <- [asn1, crypto, public_key, ssl, inets]],
+ [ensure_started(node(), App) || App <- [asn1, crypto, public_key, ssl, inets]],
+ (Node =:= node()) andalso restrict_schedulers(client),
+ {Node, Host}.
+
+ensure_started(Node, App) ->
+ ok = rpc:call(Node, application, ensure_started, [App]).
+
+
+restrict_schedulers(Type) ->
+ %% We expect this to run on 8 core machine
+ Extra0 = 1,
+ Extra = if (Type =:= server) -> -Extra0; true -> Extra0 end,
+ Scheds = erlang:system_info(schedulers),
+ erlang:system_flag(schedulers_online, (Scheds div 2) + Extra).
+
+%%--------------------------------------------------------------------
+%% Setup TLS input files ------------------------------------------------
+%%--------------------------------------------------------------------
+
+init_ssl(Config) ->
+ DDir = ?config(data_dir, Config),
+ PDir = ?config(priv_dir, Config),
+ {ok, _} = make_certs:all(DDir,
+ PDir).
+cert_opts(Config) ->
+ ClientCaCertFile = filename:join([?config(priv_dir, Config),
+ "client", "cacerts.pem"]),
+ ClientCertFile = filename:join([?config(priv_dir, Config),
+ "client", "cert.pem"]),
+ ServerCaCertFile = filename:join([?config(priv_dir, Config),
+ "server", "cacerts.pem"]),
+ ServerCertFile = filename:join([?config(priv_dir, Config),
+ "server", "cert.pem"]),
+ ServerKeyFile = filename:join([?config(priv_dir, Config),
+ "server", "key.pem"]),
+ ClientKeyFile = filename:join([?config(priv_dir, Config),
+ "client", "key.pem"]),
+ [{server_verification_opts, [{reuseaddr, true},
+ {cacertfile, ServerCaCertFile},
+ {ciphers, ["ECDHE-RSA-AES256-GCM-SHA384"]},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
+ {client_verification_opts, [
+ %%{verify, verify_peer},
+ {cacertfile, ClientCaCertFile},
+ {certfile, ClientCertFile},
+ {keyfile, ClientKeyFile}]}].
+
+%%--------------------------------------------------------------------
+%% Run clients ------------------------------------------------
+%%--------------------------------------------------------------------
+
+run_test(Client, File, Config) ->
+ Parent = self(),
+ Pid = spawn(fun() ->
+ receive
+ go ->
+ Parent ! {self(),
+ do_runs(Client, [{file, File} | Config])}
+ end
+ end),
+ Pid ! go,
+ receive
+ {Pid,{{tps, Tps}, {mbps, MBps}}} ->
+ ct:pal("Tps: ~p Bps~p", [Tps, MBps]),
+ {ok, {Tps, MBps}}
+ end.
+
+do_runs(Client, Config) ->
+ N = ?config(iter, Config),
+ DataDir = ?config(data_dir, Config),
+ File = ?config(file, Config),
+ Name = filename:join(DataDir, File),
+ Args = ?MODULE:Client(Config),
+ ?MODULE:Client({init, Args}),
+ Run =
+ fun() ->
+ ok = ?MODULE:Client(Args, N)
+ end,
+ {ok, Info} = file:read_file_info(Name, []),
+ Length = Info#file_info.size,
+ {TimeInMicro, _} = timer:tc(Run),
+ ReqPerSecond = (1000000 * N) div TimeInMicro,
+ BytesPerSecond = (1000000 * N * Length) div TimeInMicro,
+ {{tps, ReqPerSecond}, {mbps, BytesPerSecond}}.
+
+
+httpc_client({init, [_, Profile, URL, Headers, HTTPOpts]}) ->
+ %% Make sure pipelining feature will kick in when appropriate.
+ {ok, {{_ ,200, "OK"}, _,_}} = httpc:request(get,{URL, Headers}, HTTPOpts,
+ [{body_format, binary},
+ {socket_opts, [{nodelay, true}]}], Profile),
+ ct:sleep(1000);
+httpc_client(Config) ->
+ File = ?config(file, Config),
+ Protocol = ?config(protocol, Config),
+ Profile = ?config(profile, Config),
+ URL = (?config(urlfun,Config))(File),
+ Headers = ?config(http_headers, Config),
+ HTTPOpts = ?config(http_opts, Config),
+ [Protocol, Profile, URL, Headers, HTTPOpts].
+httpc_client(_,0) ->
+ ok;
+httpc_client([Protocol, Profile, URL, Headers, HTTPOpts], N) ->
+ {ok, {{_ ,200,"OK"}, _,_}} = httpc:request(get,{URL, Headers}, HTTPOpts, [{body_format, binary},
+ {socket_opts, [{nodelay, true}]}], Profile),
+ httpc_client([Protocol, Profile, URL, Headers, HTTPOpts], N-1).
+
+httpd_lib_client({init, [_, Type, Version, Request, Host, Port, Opts]}) ->
+ ok = httpd_test_lib:verify_request(Type, Host,
+ Port,
+ Opts, node(),
+ Request,
+ [{statuscode, 200},
+ {version, Version}], infinity),
+ ct:sleep(1000);
+httpd_lib_client(Config) ->
+ File = ?config(file, Config),
+ KeepAlive = ?config(keep_alive, Config),
+ Host = ?config(server_host, Config),
+ Port = ?config(port, Config),
+ ReuseSession = ?config(reuse_sessions, Config),
+ {Type, Opts} =
+ case ?config(protocol, Config) of
+ "http" ->
+ {ip_comm, [{active, true}, {mode, binary},{nodelay, true}]};
+ "https" ->
+ SSLOpts = proplists:get_value(client_verification_opts, cert_opts(Config)),
+ {ssl, [{active, true}, {mode, binary}, {nodelay, true},
+ {reuse_sessions, ReuseSession} | SSLOpts]}
+
+ end,
+ Version = ?config(http_version, Config),
+ Request = case KeepAlive of
+ true ->
+ http_request("GET /" ++ File ++ " ", Version, Host, {"connection:keep-alive\r\n", ""});
+ false ->
+ http_request("GET /" ++ File ++ " ", Version, Host)
+ end,
+
+ Args = [KeepAlive, Type, Version, Request, Host, Port, Opts],
+ httpd_lib_client(Args, 1),
+ Args.
+
+httpd_lib_client(_, 0) ->
+ ok;
+httpd_lib_client([true, Type, Version, Request, Host, Port, Opts], N) ->
+ ok = httpd_test_lib:verify_request_N(Type, Host,
+ Port,
+ Opts, node(),
+ Request,
+ [{statuscode, 200},
+ {version, Version}], infinity, N);
+httpd_lib_client([false, Type, Version, Request, Host, Port, Opts] = List, N) ->
+ ok = httpd_test_lib:verify_request(Type, Host,
+ Port,
+ Opts, node(),
+ Request,
+ [{statuscode, 200},
+ {version, Version}], infinity),
+ httpd_lib_client(List, N-1).
+
+wget_client({init,_}) ->
+ ok;
+wget_client(Config) ->
+ File = ?config(file, Config),
+ URL = (?config(urlfun,Config))(File),
+ KeepAlive = ?config(keep_alive, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Protocol = ?config(protocol, Config),
+ Iter = ?config(iter, Config),
+ FileName = filename:join(PrivDir, "wget_req"),
+ ProtocolOpts = case Protocol of
+ "http" ->
+ [];
+ "https" ->
+ proplists:get_value(client_verification_opts, cert_opts(Config))
+ end,
+ wget_req_file(FileName,URL,Iter),
+ [KeepAlive, FileName, URL, Protocol, ProtocolOpts, Iter].
+wget_client([KeepAlive, WgetFile, _URL, Protocol, ProtocolOpts, _], _) ->
+ process_flag(trap_exit, true),
+ Cmd = wget_N(KeepAlive, WgetFile, Protocol, ProtocolOpts),
+ %%ct:pal("Wget cmd: ~p", [Cmd]),
+ Port = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ wait_for_wget(Port).
+
+
+%%--------------------------------------------------------------------
+%% Start/stop servers ------------------------------------------------
+%%--------------------------------------------------------------------
+start_web_server(Group, Config) when Group == http_dummy;
+ Group == http_dummy_keep_alive ->
+ start_dummy("http", Config);
+
+start_web_server(Group, Config) when Group == https_dummy;
+ Group == https_dummy_keep_alive ->
+ start_dummy("https", Config);
+
+start_web_server(Group, Config) when Group == http_inets;
+ Group == http_inets_keep_alive ->
+ start_inets("http", [], Config);
+
+start_web_server(Group, Config) when Group == https_inets;
+ Group == https_inets_keep_alive ->
+ Opts = proplists:get_value(server_verification_opts, cert_opts(Config)),
+ ReuseSessions = ?config(reuse_sessions, Config),
+ SSLConfHttpd = [{socket_type, {essl,
+ [{nodelay, true}, {reuse_sessions, ReuseSessions} | Opts]}}],
+ start_inets("https", SSLConfHttpd, Config);
+
+start_web_server(Group, Config) when Group == http_nginx;
+ Group == http_nginx_keep_alive ->
+ case os:find_executable("nginx") of
+ false ->
+ {skip, "nginx not found"};
+ _ ->
+ start_nginx("http", Config)
+ end;
+
+start_web_server(Group, Config) when Group == https_nginx;
+ Group == https_nginx_keep_alive ->
+ case os:find_executable("nginx") of
+ false ->
+ {skip, "nginx not found"};
+ _ ->
+ start_nginx("https", cert_opts(Config) ++ Config)
+ end.
+
+start_inets(Protocol, ConfHttpd, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ DataDir = ?config(data_dir, Config),
+ Node = ?config(server_node, Config),
+ Host = ?config(server_host, Config),
+ HTTPVersion = ?config(http_version, Config),
+ Conf = [httpd, [{port,0},
+ {http_version, HTTPVersion},
+ {ipfamily, inet},
+ {server_name, "inets_test"},
+ {server_root, PrivDir},
+ {document_root, DataDir},
+ {keep_alive, ?config(keep_alive, Config)},
+ {keep_alive_timeout, 360}
+ | ConfHttpd]],
+ {ok, Pid} = rpc:call(Node, inets, start, Conf),
+ Port = proplists:get_value(port, rpc:call(Node, httpd, info, [Pid])),
+ F = fun(File) ->
+ lists:concat([Protocol,"://",Host,":",Port,"/",File])
+ end,
+ [{httpd_pid,Pid},{urlfun,F},{protocol,Protocol},{port,Port} | Config].
+
+start_dummy("http"= Protocol, Config) ->
+ HTTPVersion = ?config(http_version, Config),
+ Node = ?config(server_node, Config),
+ %%DataDir= ?config(data_dir, Config),
+ Host = ?config(server_host, Config),
+ Conf = [
+ %%{big, filename:join(DataDir, "1M_file")},
+ %%{small, filename:join(DataDir, "1k_file")},
+ {big, {gen, crypto:rand_bytes(1000000)}},
+ {small, {gen, crypto:rand_bytes(1000)}},
+ {http_version, HTTPVersion},
+ {keep_alive, ?config(keep_alive, Config)}
+ ],
+ {Pid, Port} = rpc:call(Node, http_test_lib, dummy_server, [ip_comm, inet, [{content_cb, ?MODULE}, {conf, Conf}]]),
+ F = fun(File) ->
+ lists:concat([Protocol,"://",Host,":",Port,"/",File])
+ end,
+ [{dummy_pid,Pid},{urlfun,F},{protocol, Protocol},{port,Port} | Config];
+
+start_dummy("https" = Protocol, Config) ->
+ HTTPVersion = ?config(http_version, Config),
+ Node = ?config(server_node, Config),
+ %% DataDir= ?config(data_dir, Config),
+ Host = ?config(server_host, Config),
+ SSLOpts = proplists:get_value(server_verification_opts, cert_opts(Config)),
+ Opts = [{active, true}, {nodelay, true}, {reuseaddr, true} | SSLOpts],
+ Conf = [%%{big, filename:join(DataDir, "1M_file")},
+ %%{small, filename:join(DataDir, "1k_file")},
+ {big, {gen, crypto:rand_bytes(1000000)}},
+ {small, {gen, crypto:rand_bytes(1000)}},
+ {http_version, HTTPVersion},
+ {keep_alive, ?config(keep_alive, Config)}
+ ],
+ {Pid, Port} = rpc:call(Node, http_test_lib, dummy_server,
+ [ssl, inet, [{ssl, Opts}, {content_cb, ?MODULE}, {conf, Conf}]]),
+ F = fun(File) ->
+ lists:concat([Protocol,"://",Host,":",Port,"/",File])
+ end,
+ [{dummy_pid,Pid},{urlfun,F},{protocol,Protocol},{port,Port} | Config].
+
+start_nginx(Protocol, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ DataDir = ?config(data_dir, Config),
+ Host = ?config(server_host, Config),
+ Port = inet_port(node()),
+
+ ConfFile = filename:join(PrivDir, "nginx.conf"),
+ nginx_conf(ConfFile, [{port, Port}, {protocol, Protocol} | Config]),
+ Cmd = "nginx -c " ++ ConfFile,
+ NginxPort = open_port({spawn, Cmd}, [{cd, DataDir}, stderr_to_stdout]),
+
+ F = fun(File) ->
+ lists:concat([Protocol,"://",Host,":",Port,"/",File])
+ end,
+
+ wait_for_nginx_up(Host, Port),
+
+ [{port, Port},{nginx_port, NginxPort},{urlfun,F},{protocol, Protocol} | Config ].
+
+stop_nginx(Config)->
+ PrivDir = ?config(priv_dir, Config),
+ {ok, Bin} = file:read_file(filename:join(PrivDir, "nginx.pid")),
+ Pid = string:strip(binary_to_list(Bin), right, $\n),
+ Cmd = "kill " ++ Pid,
+ os:cmd(Cmd).
+
+stop_web_server(Group, Config) when Group == http_inets;
+ Group == http_inets_keep_alive;
+ Group == https_inets;
+ Group == https_inets_keep_alive ->
+ ServerNode = ?config(server_node, Config),
+ rpc:call(ServerNode, inets, stop, [httpd, ?config(httpd_pid, Config)]);
+stop_web_server(Group, Config) when Group == http_dummy;
+ Group == http_dummy_keep_alive;
+ Group == https_dummy;
+ Group == https_dummy_keep_alive ->
+ stop_dummy_server(Config);
+stop_web_server(Group, Config) when Group == http_nginx;
+ Group == http_nginx_keep_alive;
+ Group == https_nginx;
+ Group == https_nginx_keep_alive ->
+ stop_nginx(Config).
+
+stop_dummy_server(Config) ->
+ case ?config(dummy_pid, Config) of
+ Pid when is_pid(Pid) ->
+ exit(Pid, kill);
+ _ ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+%% Misc ------------------------------------------------
+%%--------------------------------------------------------------------
+http_request(Request, "HTTP/1.1" = Version, Host, {Headers, Body}) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n" ++ Headers ++ "\r\n" ++ Body;
+http_request(Request, Version, _, {Headers, Body}) ->
+ Request ++ Version ++ "\r\n" ++ Headers ++ "\r\n" ++ Body.
+
+http_request(Request, "HTTP/1.1" = Version, Host) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n";
+http_request(Request, Version, _) ->
+ Request ++ Version ++ "\r\n\r\n".
+
+http_version(_) ->
+ "HTTP/1.1".
+
+inet_port(Node) ->
+ {Port, Socket} = do_inet_port(Node),
+ rpc:call(Node, gen_tcp, close, [Socket]),
+ Port.
+
+do_inet_port(Node) ->
+ {ok, Socket} = rpc:call(Node, gen_tcp, listen, [0, [{reuseaddr, true}]]),
+ {ok, Port} = rpc:call(Node, inet, port, [Socket]),
+ {Port, Socket}.
+
+%%--------------------------------------------------------------------
+%% Dummy server callbacks ------------------------------------------------
+%%--------------------------------------------------------------------
+
+handle_request(CB, S, "/1M_file" ++ _, Opts) ->
+ Name = proplists:get_value(big, Opts),
+ KeepAlive = proplists:get_value(keep_alive, Opts),
+ do_handle_request(CB, S, Name, Opts, KeepAlive);
+handle_request(CB, S, "/1k_file" ++ _, Opts) ->
+ Name = proplists:get_value(small, Opts),
+ KeepAlive = proplists:get_value(keep_alive, Opts),
+ do_handle_request(CB, S, Name, Opts, KeepAlive).
+
+do_handle_request(CB, S, Name, Opts, KeepAlive) when is_list(Name) ->
+ Version = proplists:get_value(http_version, Opts),
+ {ok, Fdesc} = file:open(Name, [read, binary]),
+ {ok, Info} = file:read_file_info(Name, []),
+ Length = Info#file_info.size,
+ Response = response_status_line_and_headers(Version, "Content-Length:"
+ ++ integer_to_list(Length) ++ ?CRLF, keep_alive(KeepAlive)),
+ CB:send(S, Response),
+ send_file(CB, S, Fdesc);
+do_handle_request(CB, S, {gen, Data}, Opts, KeepAlive) ->
+ Version = proplists:get_value(http_version, Opts),
+ Length = size(Data),
+ Response = response_status_line_and_headers(Version, "Content-Length:"
+ ++ integer_to_list(Length) ++ ?CRLF, keep_alive(KeepAlive)),
+ CB:send(S, Response),
+ send_file(CB, S, {gen, Data}).
+
+send_file(CB, S, {gen, Data}) ->
+ CB:send(S, Data);
+ %% ChunkSize = 64*1024,
+ %% case size(Data) of
+ %% N when N > ChunkSize ->
+ %% <<Chunk:N/binary, Rest/binary>> = Data,
+ %% %%{Chunk, Rest} = lists:split(N, Data),
+ %% CB:send(S, Chunk),
+ %% send_file(CB, S, {gen, Rest});
+ %% _ ->
+ %% CB:send(S, Data)
+ %% end;
+
+send_file(CB, S, FileDesc) ->
+ case file:read(FileDesc, 64*1024) of
+ {ok, Chunk} ->
+ CB:send(S, Chunk),
+ send_file(CB, S, FileDesc);
+ eof ->
+ file:close(FileDesc),
+ ok
+ end.
+
+response_status_line_and_headers(Version, Headers, ConnectionHeader) ->
+ StatusLine = [Version, " ", "200 OK", ?CRLF],
+ [StatusLine, Headers, ConnectionHeader, ?CRLF].
+
+keep_alive(true)->
+ "Connection:keep-alive\r\n";
+keep_alive(false) ->
+ "Connection:close\r\n".
+
+handle_http_msg({_Method, RelUri, _, {_, _Headers}, _Body}, Socket, Conf) ->
+ handle_request(connect_cb(Socket), Socket, RelUri, Conf),
+ case proplists:get_value(keep_alive, Conf) of
+ true ->
+ <<>>;
+ false ->
+ stop
+ end.
+
+connect_cb({sslsocket, _, _}) ->
+ ssl;
+connect_cb(_) ->
+ gen_tcp.
+
+%%--------------------------------------------------------------------
+%% Setup wget ------------------------------------------------
+%%--------------------------------------------------------------------
+wget_req_file(FileName, Url, Iter) ->
+ {ok, File} = file:open(FileName, [write]),
+ write_urls(File, Url, Iter).
+
+write_urls(File, Url, 1) ->
+ file:write(File, Url),
+ file:close(File);
+write_urls(File, Url, N) ->
+ file:write(File, Url),
+ file:write(File, "\n"),
+ write_urls(File, Url, N-1).
+
+wait_for_wget(Port) ->
+ receive
+ {Port, {data, _Data}} when is_port(Port) ->
+ wait_for_wget(Port);
+ {Port, closed} ->
+ ok;
+ {'EXIT', Port, _Reason} ->
+ ok
+ end.
+
+wget_N(KeepAlive, WegetFile, "http", _ProtocolOpts) ->
+ "wget -i " ++ WegetFile ++ " " ++ wget_keep_alive(KeepAlive) ++
+ " --no-cache --timeout=120" ;
+wget_N(KeepAlive, WegetFile, "https", ProtocolOpts) ->
+
+ "wget -i " ++ WegetFile ++ " " ++ wget_keep_alive(KeepAlive)
+ ++ wget_cert(ProtocolOpts) ++ wget_key(ProtocolOpts)
+ ++ wget_cacert(ProtocolOpts) ++
+ " --no-cache --timeout=120".
+
+wget(KeepAlive, URL, "http", _ProtocolOpts) ->
+ "wget " ++ URL ++ " " ++ wget_keep_alive(KeepAlive) ++
+ " --no-cache --timeout=120" ;
+wget(KeepAlive, URL, "https", ProtocolOpts) ->
+
+ "wget " ++ URL ++ " " ++ wget_keep_alive(KeepAlive)
+ ++ wget_cert(ProtocolOpts) ++ wget_key(ProtocolOpts)
+ ++ wget_cacert(ProtocolOpts) ++
+ " --no-cache --timeout=120".
+
+wget_keep_alive(true)->
+ "";
+wget_keep_alive(false) ->
+ "--no-http-keep-alive ".
+
+wget_cacert(ProtocolOpts) ->
+ "--ca-certificate=" ++ proplists:get_value(cacertfile, ProtocolOpts) ++ " ".
+
+wget_cert(ProtocolOpts) ->
+ "--certificate=" ++ proplists:get_value(certfile, ProtocolOpts) ++ " ".
+
+wget_key(ProtocolOpts) ->
+ "--private-key=" ++ proplists:get_value(keyfile, ProtocolOpts) ++ " ".
+
+%%--------------------------------------------------------------------
+%% Setup nginx ------------------------------------------------
+%%--------------------------------------------------------------------
+nginx_conf(ConfFile, Config)->
+ Protocol = ?config(protocol, Config),
+ file:write_file(ConfFile,
+ [format_nginx_conf(nginx_global(Config)),
+ format_nginx_conf(nginx_events(Config)),
+ format_nginx_conf(nginx_http(Protocol, Config))]).
+
+format_nginx_conf(Directives) ->
+ lists:map(fun({Key, Value}) ->
+ io_lib:format("~s ~s;\n", [Key, Value]);
+ (Str) ->
+ Str
+ end, Directives).
+
+
+nginx_global(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ [{"pid", filename:join(PrivDir, "nginx.pid")},
+ {"error_log", filename:join(PrivDir, "nginx.pid")},
+ {"worker_processes", "1"}].
+
+nginx_events(_Config) ->
+ ["events {\n",
+ {"worker_connections", "1024"},
+ "\n}"
+ ].
+
+nginx_http("http", Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ DataDir = ?config(data_dir, Config),
+ Port = ?config(port, Config),
+ ["http {\n" |
+ nginx_defaults(PrivDir) ++
+ [" server {",
+ {root, DataDir},
+ {listen, integer_to_list(Port)},
+ " location / {\n try_files $uri $uri/ /index.html;\n}"
+ "}\n", "}\n"
+ ]
+ ];
+
+nginx_http("https", Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ DataDir = ?config(data_dir, Config),
+ Port = ?config(port, Config),
+ SSLOpts = ?config(server_verification_opts, Config),
+ Ciphers = proplists:get_value(ciphers, SSLOpts),
+ ReuseSession = ?config(reuse_sessions, Config),
+ ["http {" |
+ nginx_defaults(PrivDir) ++
+ [" server {",
+ {"root", DataDir},
+ {"listen", integer_to_list(Port) ++ " ssl"},
+ {"ssl_certificate", ?config(certfile, SSLOpts)},
+ {"ssl_certificate_key", ?config(keyfile, SSLOpts)},
+ {"ssl_protocols", "TLSv1 TLSv1.1 TLSv1.2"},
+ {"ssl_ciphers", Ciphers},
+ {"ssl_session_cache", nginx_reuse_session(ReuseSession)},
+ " location / {\n try_files $uri $uri/ /index.html;\n}"
+ "}\n", "}\n"
+ ]
+ ].
+
+nginx_defaults(PrivDir) ->
+ [
+ %% Set temp and cache file options that will otherwise default to
+ %% restricted locations accessible only to root.
+ {"client_body_temp_path", filename:join(PrivDir, "client_body")},
+ {"fastcgi_temp_path", filename:join(PrivDir, "fastcgi_temp")},
+ {"proxy_temp_path", filename:join(PrivDir, "proxy_temp")},
+ {"scgi_temp_path", filename:join(PrivDir, "scgi_temp")},
+ {"uwsgi_temp_path", filename:join(PrivDir, "uwsgi_temp_path")},
+ {"access_log", filename:join(PrivDir, "access.log")},
+ {"error_log", filename:join(PrivDir, "error.log")},
+ %% Standard options
+ {"sendfile", "on"},
+ {"tcp_nopush", "on"},
+ {"tcp_nodelay", "on"},
+ {"keepalive_timeout", "360"},
+ {"types_hash_max_size", "2048"},
+ {"include", "/etc/nginx/mime.types"},
+ {"default_type", "application/octet-stream"}
+ ].
+
+nginx_reuse_session(true) ->
+ "on";
+nginx_reuse_session(false) ->
+ "off".
+
+wait_for_nginx_up(Host, Port) ->
+ case gen_tcp:connect(Host, Port, []) of
+ {ok, Socket} ->
+ gen_tcp:close(Socket);
+ _ ->
+ ct:sleep(100),
+ wait_for_nginx_up(Host, Port)
+ end.
+
diff --git a/lib/inets/test/httpd_bench_SUITE_data/1M_file b/lib/inets/test/httpd_bench_SUITE_data/1M_file
new file mode 100644
index 0000000000..557989144e
--- /dev/null
+++ b/lib/inets/test/httpd_bench_SUITE_data/1M_file
Binary files differ
diff --git a/lib/inets/test/httpd_bench_SUITE_data/1k_file b/lib/inets/test/httpd_bench_SUITE_data/1k_file
new file mode 100644
index 0000000000..cade172d80
--- /dev/null
+++ b/lib/inets/test/httpd_bench_SUITE_data/1k_file
Binary files differ
diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl
deleted file mode 100644
index 45547e6d4e..0000000000
--- a/lib/inets/test/httpd_block.erl
+++ /dev/null
@@ -1,372 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
--module(httpd_block).
-
--include_lib("common_test/include/ct.hrl").
-
-%% General testcases bodies called from httpd_SUITE
--export([block_disturbing_idle/4, block_non_disturbing_idle/4,
- block_503/4, block_disturbing_active/4,
- block_non_disturbing_active/4,
- block_disturbing_active_timeout_not_released/4,
- block_disturbing_active_timeout_released/4,
- block_non_disturbing_active_timeout_not_released/4,
- block_non_disturbing_active_timeout_released/4,
- disturbing_blocker_dies/4,
- non_disturbing_blocker_dies/4, restart_no_block/4,
- restart_disturbing_block/4, restart_non_disturbing_block/4
- ]).
-
-%% Help functions
--export([httpd_block/3, httpd_block/4, httpd_unblock/2, httpd_restart/2]).
--export([do_block_server/4, do_block_nd_server/5, do_long_poll/6]).
-
--define(report(Label, Content),
- inets:report_event(20, Label, test_case,
- [{module, ?MODULE}, {line, ?LINE} | Content])).
-
-
-%%-------------------------------------------------------------------------
-%% Test cases starts here.
-%%-------------------------------------------------------------------------
-block_disturbing_idle(_Type, Port, Host, Node) ->
- io:format("block_disturbing_idle -> entry~n", []),
- validate_admin_state(Node, Host, Port, unblocked),
- block_server(Node, Host, Port),
- validate_admin_state(Node, Host, Port, blocked),
- unblock_server(Node, Host, Port),
- validate_admin_state(Node, Host, Port, unblocked),
- io:format("block_disturbing_idle -> done~n", []),
- ok.
-
-%%--------------------------------------------------------------------
-block_non_disturbing_idle(_Type, Port, Host, Node) ->
- unblocked = get_admin_state(Node, Host, Port),
- block_nd_server(Node, Host, Port),
- blocked = get_admin_state(Node, Host, Port),
- unblock_server(Node, Host, Port),
- unblocked = get_admin_state(Node, Host, Port),
- ok.
-
-%%--------------------------------------------------------------------
-block_503(Type, Port, Host, Node) ->
- Req = "GET / HTTP/1.0\r\ndummy-host.ericsson.se:\r\n\r\n",
- unblocked = get_admin_state(Node, Host, Port),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node, Req,
- [{statuscode, 200},
- {version, "HTTP/1.0"}]),
- ok = block_server(Node, Host, Port),
- blocked = get_admin_state(Node, Host, Port),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node, Req,
- [{statuscode, 503},
- {version, "HTTP/1.0"}]),
- ok = unblock_server(Node, Host, Port),
- unblocked = get_admin_state(Node, Host, Port),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node, Req,
- [{statuscode, 200},
- {version, "HTTP/1.0"}]).
-
-%%--------------------------------------------------------------------
-block_disturbing_active(Type, Port, Host, Node) ->
- process_flag(trap_exit, true),
- Pid = long_poll(Type, Host, Port, Node, 200, 60000),
- ct:sleep(15000),
- block_server(Node, Host, Port),
- await_suite_failed_process_exit(Pid, "poller", 60000,
- connection_closed),
- blocked = get_admin_state(Node, Host, Port),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-block_non_disturbing_active(Type, Port, Host, Node) ->
- process_flag(trap_exit, true),
- Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- ct:sleep(15000),
- ok = block_nd_server(Node, Host, Port),
- await_normal_process_exit(Poller, "poller", 60000),
- blocked = get_admin_state(Node, Host, Port),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-block_disturbing_active_timeout_not_released(Type, Port, Host, Node) ->
- process_flag(trap_exit, true),
- Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- ct:sleep(15000),
- ok = httpd_block(undefined, Port, disturbing, 50000),
- await_normal_process_exit(Poller, "poller", 30000),
- blocked = get_admin_state(Node, Host, Port),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-block_disturbing_active_timeout_released(Type, Port, Host, Node) ->
- process_flag(trap_exit, true),
- Poller = long_poll(Type, Host, Port, Node, 200, 40000),
- ct:sleep(5000),
- ok = httpd_block(undefined, Port, disturbing, 10000),
- await_suite_failed_process_exit(Poller, "poller", 40000,
- connection_closed),
- blocked = get_admin_state(Node, Host, Port),
- process_flag(trap_exit, false),
- ok.
-%%--------------------------------------------------------------------
-block_non_disturbing_active_timeout_not_released(Type, Port, Host, Node) ->
- process_flag(trap_exit, true),
- Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- ct:sleep(5000),
- ok = block_nd_server(Node, Host, Port, 40000),
- await_normal_process_exit(Poller, "poller", 60000),
- blocked = get_admin_state(Node, Host, Port),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-block_non_disturbing_active_timeout_released(Type, Port, Host, Node) ->
- process_flag(trap_exit, true),
- Poller = long_poll(Type, Host, Port, Node, 200, 45000),
- ct:sleep(5000),
- Blocker = blocker_nd(Node, Host, Port ,10000, {error,timeout}),
- await_normal_process_exit(Blocker, "blocker", 15000),
- await_normal_process_exit(Poller, "poller", 50000),
- unblocked = get_admin_state(Node, Host, Port),
- process_flag(trap_exit, false),
- ok.
-%%--------------------------------------------------------------------
-disturbing_blocker_dies(Type, Port, Host, Node) ->
- process_flag(trap_exit, true),
- Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- ct:sleep(5000),
- Blocker = blocker(Node, Host, Port, 10000),
- ct:sleep(5000),
- exit(Blocker,simulate_blocker_crash),
- await_normal_process_exit(Poller, "poller", 60000),
- unblocked = get_admin_state(Node, Host, Port),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-non_disturbing_blocker_dies(Type, Port, Host, Node) ->
- process_flag(trap_exit, true),
- Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- ct:sleep(5000),
- Blocker = blocker_nd(Node, Host, Port, 10000, ok),
- ct:sleep(5000),
- exit(Blocker, simulate_blocker_crash),
- await_normal_process_exit(Poller, "poller", 60000),
- unblocked = get_admin_state(Node, Host, Port),
- process_flag(trap_exit, false),
- ok.
-%%--------------------------------------------------------------------
-restart_no_block(_, Port, Host, Node) ->
- {error,_Reason} = restart_server(Node, Host, Port).
-
-%%--------------------------------------------------------------------
-restart_disturbing_block(_, Port, Host, Node) ->
- ?report("restart_disturbing_block - get_admin_state (unblocked)", []),
- unblocked = get_admin_state(Node, Host, Port),
- ?report("restart_disturbing_block - block_server", []),
- ok = block_server(Node, Host, Port),
- ?report("restart_disturbing_block - restart_server", []),
- ok = restart_server(Node, Host, Port),
- ?report("restart_disturbing_block - unblock_server", []),
- ok = unblock_server(Node, Host, Port),
- ?report("restart_disturbing_block - get_admin_state (unblocked)", []),
- unblocked = get_admin_state(Node, Host, Port).
-
-%%--------------------------------------------------------------------
-restart_non_disturbing_block(_, Port, Host, Node) ->
- ?report("restart_non_disturbing_block - get_admin_state (unblocked)", []),
- unblocked = get_admin_state(Node, Host, Port),
- ?report("restart_non_disturbing_block - block_nd_server", []),
- ok = block_nd_server(Node, Host, Port),
- ?report("restart_non_disturbing_block - restart_server", []),
- ok = restart_server(Node, Host, Port),
- ?report("restart_non_disturbing_block - unblock_server", []),
- ok = unblock_server(Node, Host, Port),
- ?report("restart_non_disturbing_block - get_admin_state (unblocked)", []),
- unblocked = get_admin_state(Node, Host, Port).
-
-%%--------------------------------------------------------------------
-%% Internal functions
-%%--------------------------------------------------------------------
-blocker(Node, Host, Port, Timeout) ->
- spawn_link(?MODULE, do_block_server,[Node, Host, Port,Timeout]).
-
-do_block_server(Node, Host, Port, Timeout) ->
- ok = block_server(Node, Host, Port, Timeout),
- exit(normal).
-
-blocker_nd(Node, Host, Port, Timeout, Reply) ->
- spawn_link(?MODULE, do_block_nd_server,
- [Node, Host, Port, Timeout, Reply]).
-
-do_block_nd_server(Node, Host, Port, Timeout, Reply) ->
- Reply = block_nd_server(Node, Host, Port, Timeout),
- exit(normal).
-
-restart_server(Node, _Host, Port) ->
- Addr = undefined,
- rpc:call(Node, ?MODULE, httpd_restart, [Addr, Port]).
-
-
-block_server(Node, _Host, Port) ->
- io:format("block_server -> entry~n", []),
- Addr = undefined,
- rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, disturbing]).
-
-
-block_server(Node, _Host, Port, Timeout) ->
- Addr = undefined,
- rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, disturbing, Timeout]).
-
-
-block_nd_server(Node, _Host, Port) ->
- Addr = undefined,
- rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, non_disturbing]).
-
-block_nd_server(Node, _Host, Port, Timeout) ->
- Addr = undefined,
- rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, non_disturbing, Timeout]).
-
-unblock_server(Node, _Host, Port) ->
- io:format("~p:~p:block_server -> entry~n", [node(),self()]),
- Addr = undefined,
- rpc:call(Node, ?MODULE, httpd_unblock, [Addr, Port]).
-
-
-httpd_block(Addr, Port, Mode) ->
- io:format("~p:~p:httpd_block -> entry~n", [node(),self()]),
- Name = make_name(Addr, Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- httpd_manager:block(Pid, Mode);
- _ ->
- {error, not_started}
- end.
-
-httpd_block(Addr, Port, Mode, Timeout) ->
- Name = make_name(Addr, Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- httpd_manager:block(Pid, Mode, Timeout);
- _ ->
- {error, not_started}
- end.
-
-httpd_unblock(Addr, Port) ->
- io:format("~p:~p:httpd_unblock -> entry~n", [node(),self()]),
- Name = make_name(Addr, Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- httpd_manager:unblock(Pid);
- _ ->
- {error, not_started}
- end.
-
-httpd_restart(Addr, Port) ->
- Name = make_name(Addr, Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- httpd_manager:reload(Pid, undefined);
- _ ->
- {error, not_started}
- end.
-
-make_name(Addr, Port) ->
- httpd_util:make_name("httpd", Addr, Port, default).
-
-get_admin_state(_, _Host, Port) ->
- Name = make_name(undefined, Port),
- {status, _, _, StatusInfo} = sys:get_status(whereis(Name)),
- [_, _,_, _, Prop] = StatusInfo,
- State = state(Prop),
- element(6, State).
-
-validate_admin_state(Node, Host, Port, Expect) ->
- io:format("try validating server admin state: ~p~n", [Expect]),
- case get_admin_state(Node, Host, Port) of
- Expect ->
- ok;
- Unexpected ->
- io:format("failed validating server admin state: ~p~n",
- [Unexpected]),
- exit({unexpected_admin_state, Unexpected, Expect})
- end.
-
-
-await_normal_process_exit(Pid, Name, Timeout) ->
- receive
- {'EXIT', Pid, normal} ->
- ok;
- {'EXIT', Pid, Reason} ->
- Err =
- lists:flatten(
- io_lib:format("expected normal exit, "
- "unexpected exit of ~s process: ~p",
- [Name, Reason])),
- ct:fail(Err)
- after Timeout ->
- ct:fail("timeout while waiting for " ++ Name)
- end.
-
-
-await_suite_failed_process_exit(Pid, Name, Timeout, Why) ->
- receive
- {'EXIT', Pid, {test_failed, Why}} ->
- ok;
- {'EXIT', Pid, Reason} ->
- Err =
- lists:flatten(
- io_lib:format("expected connection_closed, "
- "unexpected exit of ~s process: ~p",
- [Name, Reason])),
- ct:fail(Err)
- after Timeout ->
- ct:fail("timeout while waiting for " ++ Name)
- end.
-
-long_poll(Type, Host, Port, Node, StatusCode, Timeout) ->
- spawn_link(?MODULE, do_long_poll, [Type, Host, Port, Node,
- StatusCode, Timeout]).
-
-do_long_poll(Type, Host, Port, Node, StatusCode, Timeout) ->
- Mod = "httpd_example",
- Func = "delay",
- Req = lists:flatten(io_lib:format("GET /eval?" ++ Mod ++ ":" ++ Func ++
- "(~p) HTTP/1.0\r\n\r\n",[30000])),
- case httpd_test_lib:verify_request(Type, Host, Port, Node, Req,
- [{statuscode, StatusCode},
- {version, "HTTP/1.0"}], Timeout) of
- ok ->
- exit(normal);
- Reason ->
- exit({test_failed, Reason})
- end.
-
-
-state([{data,[{"State", State}]} | _]) ->
- State;
-state([{data,[{"StateData", State}]} | _]) ->
- State;
-state([_ | Rest]) ->
- state(Rest).
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index 1cecd2642c..b6525037b2 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -23,7 +23,8 @@
-include("inets_test_lib.hrl").
%% Poll functions
--export([verify_request/6, verify_request/7, verify_request/8, is_expect/1]).
+-export([verify_request/6, verify_request/7, verify_request/8, is_expect/1,
+ verify_request_N/9]).
-record(state, {request, % string()
socket, % socket()
@@ -109,9 +110,9 @@ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, Ti
{error, Reason};
NewState ->
ValidateResult =
- validate(RequestStr, NewState, Options, Node, Port),
+ validate(RequestStr, NewState, Options, Node, Port),
inets_test_lib:close(SocketType, Socket),
- ValidateResult
+ ValidateResult
end;
ConnectError ->
@@ -126,6 +127,46 @@ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, Ti
{args, [SocketType, Host, Port, TranspOpts]}]})
end.
+verify_request_N(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut, N) ->
+ State = #state{},
+ try inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts) of
+ {ok, Socket} ->
+ request_N(SocketType, Socket, RequestStr, Options, TimeOut, Node, Port, State, N);
+ ConnectError ->
+ ct:fail({connect_error, ConnectError,
+ [SocketType, Host, Port, TranspOpts]})
+ catch
+ T:E ->
+ ct:fail({connect_failure,
+ [{type, T},
+ {error, E},
+ {stacktrace, erlang:get_stacktrace()},
+ {args, [SocketType, Host, Port, TranspOpts]}]})
+ end.
+
+request_N(SocketType, Socket, RequestStr, Options, TimeOut, Node, Port, State, 0) ->
+ ok = inets_test_lib:send(SocketType, Socket, RequestStr),
+ case request(State#state{request = RequestStr,
+ socket = Socket}, TimeOut) of
+ {error, Reason} ->
+ {error, Reason};
+ NewState ->
+ ValidateResult =
+ validate(RequestStr, NewState, Options, Node, Port),
+ inets_test_lib:close(SocketType, Socket),
+ ValidateResult
+ end;
+request_N(SocketType, Socket, RequestStr, Options, TimeOut, Node, Port, State, N) ->
+ ok = inets_test_lib:send(SocketType, Socket, RequestStr),
+ case request(State#state{request = RequestStr,
+ socket = Socket}, TimeOut) of
+ {error, Reason} ->
+ {error, Reason};
+ _NewState ->
+ request_N(SocketType, Socket, RequestStr, Options, TimeOut, Node, Port,
+ #state{}, N-1)
+ end.
+
request(#state{mfa = {Module, Function, Args},
request = RequestStr, socket = Socket} = State, TimeOut) ->
@@ -160,13 +201,35 @@ request(#state{mfa = {Module, Function, Args},
{ssl_closed, Socket} ->
exit({test_failed, connection_closed});
{ssl_error, Socket, Reason} ->
- ct:fail({ssl_error, Reason})
+ ct:fail({ssl_error, Reason});
+ {Socket, {data, Data}} when is_port(Socket) ->
+ case Module:Function([list_to_binary(Data) | Args]) of
+ {ok, Parsed} ->
+ port_handle_http_msg(Parsed, State);
+ {_, whole_body, _} when HeadRequest =:= "HEAD" ->
+ State#state{body = <<>>};
+ NewMFA ->
+ request(State#state{mfa = NewMFA}, TimeOut)
+ end;
+ {Socket, closed} when Function =:= whole_body ->
+ State#state{body = hd(Args)};
+ {Socket, closed} ->
+ exit({test_failed, connection_closed})
after TimeOut ->
ct:pal("~p ~w[~w]request -> timeout"
- "~n", [self(), ?MODULE, ?LINE]),
+ "~p~n", [self(), ?MODULE, ?LINE, Args]),
ct:fail(connection_timed_out)
end.
+
+port_handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body}, State) ->
+ State#state{status_line = {Version,
+ StatusCode,
+ ReasonPharse},
+ headers = Headers,
+ body = Body}.
+
+
handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body},
State = #state{request = RequestStr}) ->
case is_expect(RequestStr) of
diff --git a/lib/inets/test/inets.spec b/lib/inets/test/inets.spec
index ed102f8219..6cb3d6526c 100644
--- a/lib/inets/test/inets.spec
+++ b/lib/inets/test/inets.spec
@@ -1 +1,3 @@
-{suites,"../inets_test",all}.
+{suites,"../inets_test", all}.
+{skip_suites, "../inets_test", [httpd_bench_SUITE],
+ "Benchmarks run separately"}.
diff --git a/lib/inets/test/inets_bench.spec b/lib/inets/test/inets_bench.spec
new file mode 100644
index 0000000000..19136e691b
--- /dev/null
+++ b/lib/inets/test/inets_bench.spec
@@ -0,0 +1 @@
+{suites,"../inets_test",[httpd_bench_SUITE]}.
diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl
index f1185f7574..2529cc5f9b 100644
--- a/lib/inets/test/inets_test_lib.erl
+++ b/lib/inets/test/inets_test_lib.erl
@@ -463,8 +463,9 @@ connect_bin(essl, Host, Port, Opts0) ->
connect(ssl, Host, Port, Opts);
connect_bin(ip_comm, Host, Port, Opts0) ->
Opts = [binary, {packet, 0} | Opts0],
- connect(ip_comm, Host, Port, Opts).
-
+ connect(ip_comm, Host, Port, Opts);
+connect_bin(Type, Host, Port, Opts) ->
+ connect(Type, Host, Port, Opts).
connect_byte(SockType, Host, Port) ->
connect_byte(SockType, Host, Port, []).
@@ -477,27 +478,40 @@ connect_byte(essl, Host, Port, Opts0) ->
connect(ssl, Host, Port, Opts);
connect_byte(ip_comm, Host, Port, Opts0) ->
Opts = [{packet,0} | Opts0],
- connect(ip_comm, Host, Port, Opts).
+ connect(ip_comm, Host, Port, Opts);
+connect_byte(Type, Host, Port, Opts) ->
+ connect(Type, Host, Port, Opts).
connect(ip_comm, Host, Port, Opts) ->
gen_tcp:connect(Host, Port, Opts);
connect(ssl, Host, Port, Opts) ->
- ssl:connect(Host, Port, Opts).
+ ssl:connect(Host, Port, Opts);
+connect(openssl_port, Host, Port, Opts) ->
+ CaCertFile = proplists:get_value(cacertfile, Opts),
+ Cmd = "openssl s_client -quiet -port " ++ integer_to_list(Port) ++ " -host " ++ Host
+ ++ " -CAfile " ++ CaCertFile,
+ ct:log("openssl cmd: ~p~n", [Cmd]),
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ read_junk(OpensslPort),
+ {ok, OpensslPort}.
send(ssl, Socket, Data) ->
ssl:send(Socket, Data);
send(essl, Socket, Data) ->
ssl:send(Socket, Data);
send(ip_comm,Socket,Data) ->
- gen_tcp:send(Socket,Data).
-
-
+ gen_tcp:send(Socket,Data);
+send(openssl_port, Port, Data) ->
+ true = port_command(Port, Data),
+ ok.
close(ssl,Socket) ->
catch ssl:close(Socket);
close(essl,Socket) ->
catch ssl:close(Socket);
close(ip_comm,Socket) ->
- catch gen_tcp:close(Socket).
+ catch gen_tcp:close(Socket);
+close(openssl_port, Port) ->
+ exit(Port, normal).
hours(N) -> trunc(N * 1000 * 60 * 60).
@@ -572,3 +586,11 @@ do_inet_port(Node) ->
{ok, Socket} = rpc:call(Node, gen_tcp, listen, [0, [{reuseaddr, true}]]),
{ok, Port} = rpc:call(Node, inet, port, [Socket]),
{Port, Socket}.
+
+read_junk(OpensslPort) ->
+ receive
+ {OpensslPort, _} ->
+ read_junk(OpensslPort)
+ after 500 ->
+ ok
+ end.
diff --git a/lib/inets/test/make_certs.erl b/lib/inets/test/make_certs.erl
new file mode 100644
index 0000000000..7215a59823
--- /dev/null
+++ b/lib/inets/test/make_certs.erl
@@ -0,0 +1,530 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(make_certs).
+-compile([export_all]).
+
+%-export([all/1, all/2, rootCA/2, intermediateCA/3, endusers/3, enduser/3, revoke/3, gencrl/2, verify/3]).
+
+-record(config, {commonName,
+ organizationalUnitName = "Erlang OTP",
+ organizationName = "Ericsson AB",
+ localityName = "Stockholm",
+ countryName = "SE",
+ emailAddress = "[email protected]",
+ default_bits = 2048,
+ v2_crls = true,
+ ecc_certs = false,
+ issuing_distribution_point = false,
+ crl_port = 8000,
+ openssl_cmd = "openssl"}).
+
+
+default_config() ->
+ #config{}.
+
+make_config(Args) ->
+ make_config(Args, #config{}).
+
+make_config([], C) ->
+ C;
+make_config([{organizationalUnitName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{organizationalUnitName = Name});
+make_config([{organizationName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{organizationName = Name});
+make_config([{localityName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{localityName = Name});
+make_config([{countryName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{countryName = Name});
+make_config([{emailAddress, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{emailAddress = Name});
+make_config([{default_bits, Bits}|T], C) when is_integer(Bits) ->
+ make_config(T, C#config{default_bits = Bits});
+make_config([{v2_crls, Bool}|T], C) when is_boolean(Bool) ->
+ make_config(T, C#config{v2_crls = Bool});
+make_config([{crl_port, Port}|T], C) when is_integer(Port) ->
+ make_config(T, C#config{crl_port = Port});
+make_config([{ecc_certs, Bool}|T], C) when is_boolean(Bool) ->
+ make_config(T, C#config{ecc_certs = Bool});
+make_config([{issuing_distribution_point, Bool}|T], C) when is_boolean(Bool) ->
+ make_config(T, C#config{issuing_distribution_point = Bool});
+make_config([{openssl_cmd, Cmd}|T], C) when is_list(Cmd) ->
+ make_config(T, C#config{openssl_cmd = Cmd}).
+
+
+all([DataDir, PrivDir]) ->
+ all(DataDir, PrivDir).
+
+all(DataDir, PrivDir) ->
+ all(DataDir, PrivDir, #config{}).
+
+all(DataDir, PrivDir, C) when is_list(C) ->
+ all(DataDir, PrivDir, make_config(C));
+all(DataDir, PrivDir, C = #config{}) ->
+ ok = filelib:ensure_dir(filename:join(PrivDir, "erlangCA")),
+ create_rnd(DataDir, PrivDir), % For all requests
+ rootCA(PrivDir, "erlangCA", C),
+ intermediateCA(PrivDir, "otpCA", "erlangCA", C),
+ endusers(PrivDir, "otpCA", ["client", "server", "revoked", "a.server", "b.server"], C),
+ endusers(PrivDir, "erlangCA", ["localhost"], C),
+ %% Create keycert files
+ SDir = filename:join([PrivDir, "server"]),
+ SC = filename:join([SDir, "cert.pem"]),
+ SK = filename:join([SDir, "key.pem"]),
+ SKC = filename:join([SDir, "keycert.pem"]),
+ append_files([SK, SC], SKC),
+ CDir = filename:join([PrivDir, "client"]),
+ CC = filename:join([CDir, "cert.pem"]),
+ CK = filename:join([CDir, "key.pem"]),
+ CKC = filename:join([CDir, "keycert.pem"]),
+ append_files([CK, CC], CKC),
+ RDir = filename:join([PrivDir, "revoked"]),
+ RC = filename:join([RDir, "cert.pem"]),
+ RK = filename:join([RDir, "key.pem"]),
+ RKC = filename:join([RDir, "keycert.pem"]),
+ revoke(PrivDir, "otpCA", "revoked", C),
+ append_files([RK, RC], RKC),
+ remove_rnd(PrivDir),
+ {ok, C}.
+
+append_files(FileNames, ResultFileName) ->
+ {ok, ResultFile} = file:open(ResultFileName, [write]),
+ do_append_files(FileNames, ResultFile).
+
+do_append_files([], RF) ->
+ ok = file:close(RF);
+do_append_files([F|Fs], RF) ->
+ {ok, Data} = file:read_file(F),
+ ok = file:write(RF, Data),
+ do_append_files(Fs, RF).
+
+rootCA(Root, Name, C) ->
+ create_ca_dir(Root, Name, ca_cnf(C#config{commonName = Name})),
+ create_self_signed_cert(Root, Name, req_cnf(C#config{commonName = Name}), C),
+ file:copy(filename:join([Root, Name, "cert.pem"]), filename:join([Root, Name, "cacerts.pem"])),
+ gencrl(Root, Name, C).
+
+intermediateCA(Root, CA, ParentCA, C) ->
+ create_ca_dir(Root, CA, ca_cnf(C#config{commonName = CA})),
+ CARoot = filename:join([Root, CA]),
+ CnfFile = filename:join([CARoot, "req.cnf"]),
+ file:write_file(CnfFile, req_cnf(C#config{commonName = CA})),
+ KeyFile = filename:join([CARoot, "private", "key.pem"]),
+ ReqFile = filename:join([CARoot, "req.pem"]),
+ create_req(Root, CnfFile, KeyFile, ReqFile, C),
+ CertFile = filename:join([CARoot, "cert.pem"]),
+ sign_req(Root, ParentCA, "ca_cert", ReqFile, CertFile, C),
+ CACertsFile = filename:join(CARoot, "cacerts.pem"),
+ file:copy(filename:join([Root, ParentCA, "cacerts.pem"]), CACertsFile),
+ %% append this CA's cert to the cacerts file
+ {ok, Bin} = file:read_file(CertFile),
+ {ok, FD} = file:open(CACertsFile, [append]),
+ file:write(FD, ["\n", Bin]),
+ file:close(FD),
+ gencrl(Root, CA, C).
+
+endusers(Root, CA, Users, C) ->
+ [enduser(Root, CA, User, C) || User <- Users].
+
+enduser(Root, CA, User, C) ->
+ UsrRoot = filename:join([Root, User]),
+ file:make_dir(UsrRoot),
+ CnfFile = filename:join([UsrRoot, "req.cnf"]),
+ file:write_file(CnfFile, req_cnf(C#config{commonName = User})),
+ KeyFile = filename:join([UsrRoot, "key.pem"]),
+ ReqFile = filename:join([UsrRoot, "req.pem"]),
+ create_req(Root, CnfFile, KeyFile, ReqFile, C),
+ %create_req(Root, CnfFile, KeyFile, ReqFile),
+ CertFileAllUsage = filename:join([UsrRoot, "cert.pem"]),
+ sign_req(Root, CA, "user_cert", ReqFile, CertFileAllUsage, C),
+ CertFileDigitalSigOnly = filename:join([UsrRoot, "digital_signature_only_cert.pem"]),
+ sign_req(Root, CA, "user_cert_digital_signature_only", ReqFile, CertFileDigitalSigOnly, C),
+ CACertsFile = filename:join(UsrRoot, "cacerts.pem"),
+ file:copy(filename:join([Root, CA, "cacerts.pem"]), CACertsFile),
+ ok.
+
+revoke(Root, CA, User, C) ->
+ UsrCert = filename:join([Root, User, "cert.pem"]),
+ CACnfFile = filename:join([Root, CA, "ca.cnf"]),
+ Cmd = [C#config.openssl_cmd, " ca"
+ " -revoke ", UsrCert,
+ [" -crl_reason keyCompromise" || C#config.v2_crls ],
+ " -config ", CACnfFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env),
+ gencrl(Root, CA, C).
+
+gencrl(Root, CA, C) ->
+ CACnfFile = filename:join([Root, CA, "ca.cnf"]),
+ CACRLFile = filename:join([Root, CA, "crl.pem"]),
+ Cmd = [C#config.openssl_cmd, " ca"
+ " -gencrl ",
+ " -crlhours 24",
+ " -out ", CACRLFile,
+ " -config ", CACnfFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
+
+verify(Root, CA, User, C) ->
+ CAFile = filename:join([Root, User, "cacerts.pem"]),
+ CACRLFile = filename:join([Root, CA, "crl.pem"]),
+ CertFile = filename:join([Root, User, "cert.pem"]),
+ Cmd = [C#config.openssl_cmd, " verify"
+ " -CAfile ", CAFile,
+ " -CRLfile ", CACRLFile, %% this is undocumented, but seems to work
+ " -crl_check ",
+ CertFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ try cmd(Cmd, Env) catch
+ exit:{eval_cmd, _, _} ->
+ invalid
+ end.
+
+create_self_signed_cert(Root, CAName, Cnf, C = #config{ecc_certs = true}) ->
+ CARoot = filename:join([Root, CAName]),
+ CnfFile = filename:join([CARoot, "req.cnf"]),
+ file:write_file(CnfFile, Cnf),
+ KeyFile = filename:join([CARoot, "private", "key.pem"]),
+ CertFile = filename:join([CARoot, "cert.pem"]),
+ Cmd = [C#config.openssl_cmd, " ecparam"
+ " -out ", KeyFile,
+ " -name secp521r1 ",
+ %" -name sect283k1 ",
+ " -genkey "],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env),
+
+ Cmd2 = [C#config.openssl_cmd, " req"
+ " -new"
+ " -x509"
+ " -config ", CnfFile,
+ " -key ", KeyFile,
+ " -outform PEM ",
+ " -out ", CertFile],
+ cmd(Cmd2, Env);
+create_self_signed_cert(Root, CAName, Cnf, C) ->
+ CARoot = filename:join([Root, CAName]),
+ CnfFile = filename:join([CARoot, "req.cnf"]),
+ file:write_file(CnfFile, Cnf),
+ KeyFile = filename:join([CARoot, "private", "key.pem"]),
+ CertFile = filename:join([CARoot, "cert.pem"]),
+ Cmd = [C#config.openssl_cmd, " req"
+ " -new"
+ " -x509"
+ " -config ", CnfFile,
+ " -keyout ", KeyFile,
+ " -outform PEM",
+ " -out ", CertFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
+
+
+create_ca_dir(Root, CAName, Cnf) ->
+ CARoot = filename:join([Root, CAName]),
+ ok = filelib:ensure_dir(CARoot),
+ file:make_dir(CARoot),
+ create_dirs(CARoot, ["certs", "crl", "newcerts", "private"]),
+ create_rnd(Root, filename:join([CAName, "private"])),
+ create_files(CARoot, [{"serial", "01\n"},
+ {"crlnumber", "01"},
+ {"index.txt", ""},
+ {"ca.cnf", Cnf}]).
+
+create_req(Root, CnfFile, KeyFile, ReqFile, C = #config{ecc_certs = true}) ->
+ Cmd = [C#config.openssl_cmd, " ecparam"
+ " -out ", KeyFile,
+ " -name secp521r1 ",
+ %" -name sect283k1 ",
+ " -genkey "],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env),
+ Cmd2 = [C#config.openssl_cmd, " req"
+ " -new ",
+ " -key ", KeyFile,
+ " -outform PEM ",
+ " -out ", ReqFile,
+ " -config ", CnfFile],
+ cmd(Cmd2, Env);
+ %fix_key_file(KeyFile).
+create_req(Root, CnfFile, KeyFile, ReqFile, C) ->
+ Cmd = [C#config.openssl_cmd, " req"
+ " -new"
+ " -config ", CnfFile,
+ " -outform PEM ",
+ " -keyout ", KeyFile,
+ " -out ", ReqFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
+ %fix_key_file(KeyFile).
+
+
+sign_req(Root, CA, CertType, ReqFile, CertFile, C) ->
+ CACnfFile = filename:join([Root, CA, "ca.cnf"]),
+ Cmd = [C#config.openssl_cmd, " ca"
+ " -batch"
+ " -notext"
+ " -config ", CACnfFile,
+ " -extensions ", CertType,
+ " -in ", ReqFile,
+ " -out ", CertFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
+
+%%
+%% Misc
+%%
+
+create_dirs(Root, Dirs) ->
+ lists:foreach(fun(Dir) ->
+ file:make_dir(filename:join([Root, Dir])) end,
+ Dirs).
+
+create_files(Root, NameContents) ->
+ lists:foreach(
+ fun({Name, Contents}) ->
+ file:write_file(filename:join([Root, Name]), Contents) end,
+ NameContents).
+
+create_rnd(FromDir, ToDir) ->
+ From = filename:join([FromDir, "RAND"]),
+ To = filename:join([ToDir, "RAND"]),
+ file:copy(From, To).
+
+remove_rnd(Dir) ->
+ File = filename:join([Dir, "RAND"]),
+ file:delete(File).
+
+cmd(Cmd, Env) ->
+ FCmd = lists:flatten(Cmd),
+ Port = open_port({spawn, FCmd}, [stream, eof, exit_status, stderr_to_stdout,
+ {env, Env}]),
+ eval_cmd(Port, FCmd).
+
+eval_cmd(Port, Cmd) ->
+ receive
+ {Port, {data, _}} ->
+ eval_cmd(Port, Cmd);
+ {Port, eof} ->
+ ok
+ end,
+ receive
+ {Port, {exit_status, 0}} ->
+ ok;
+ {Port, {exit_status, Status}} ->
+ exit({eval_cmd, Cmd, Status})
+ after 0 ->
+ ok
+ end.
+
+%%
+%% Contents of configuration files
+%%
+
+req_cnf(C) ->
+ ["# Purpose: Configuration for requests (end users and CAs)."
+ "\n"
+ "ROOTDIR = $ENV::ROOTDIR\n"
+ "\n"
+
+ "[req]\n"
+ "input_password = secret\n"
+ "output_password = secret\n"
+ "default_bits = ", integer_to_list(C#config.default_bits), "\n"
+ "RANDFILE = $ROOTDIR/RAND\n"
+ "encrypt_key = no\n"
+ "default_md = md5\n"
+ "#string_mask = pkix\n"
+ "x509_extensions = ca_ext\n"
+ "prompt = no\n"
+ "distinguished_name= name\n"
+ "\n"
+
+ "[name]\n"
+ "commonName = ", C#config.commonName, "\n"
+ "organizationalUnitName = ", C#config.organizationalUnitName, "\n"
+ "organizationName = ", C#config.organizationName, "\n"
+ "localityName = ", C#config.localityName, "\n"
+ "countryName = ", C#config.countryName, "\n"
+ "emailAddress = ", C#config.emailAddress, "\n"
+ "\n"
+
+ "[ca_ext]\n"
+ "basicConstraints = critical, CA:true\n"
+ "keyUsage = cRLSign, keyCertSign\n"
+ "subjectKeyIdentifier = hash\n"
+ "subjectAltName = email:copy\n"].
+
+ca_cnf(C = #config{issuing_distribution_point = true}) ->
+ ["# Purpose: Configuration for CAs.\n"
+ "\n"
+ "ROOTDIR = $ENV::ROOTDIR\n"
+ "default_ca = ca\n"
+ "\n"
+
+ "[ca]\n"
+ "dir = $ROOTDIR/", C#config.commonName, "\n"
+ "certs = $dir/certs\n"
+ "crl_dir = $dir/crl\n"
+ "database = $dir/index.txt\n"
+ "new_certs_dir = $dir/newcerts\n"
+ "certificate = $dir/cert.pem\n"
+ "serial = $dir/serial\n"
+ "crl = $dir/crl.pem\n",
+ ["crlnumber = $dir/crlnumber\n" || C#config.v2_crls],
+ "private_key = $dir/private/key.pem\n"
+ "RANDFILE = $dir/private/RAND\n"
+ "\n"
+ "x509_extensions = user_cert\n",
+ ["crl_extensions = crl_ext\n" || C#config.v2_crls],
+ "unique_subject = no\n"
+ "default_days = 3600\n"
+ "default_md = md5\n"
+ "preserve = no\n"
+ "policy = policy_match\n"
+ "\n"
+
+ "[policy_match]\n"
+ "commonName = supplied\n"
+ "organizationalUnitName = optional\n"
+ "organizationName = match\n"
+ "countryName = match\n"
+ "localityName = match\n"
+ "emailAddress = supplied\n"
+ "\n"
+
+ "[crl_ext]\n"
+ "authorityKeyIdentifier=keyid:always,issuer:always\n",
+ ["issuingDistributionPoint=critical, @idpsec\n" || C#config.issuing_distribution_point],
+
+ "[idpsec]\n"
+ "fullname=URI:http://localhost:8000/",C#config.commonName,"/crl.pem\n"
+
+ "[user_cert]\n"
+ "basicConstraints = CA:false\n"
+ "keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ "crlDistributionPoints=@crl_section\n"
+
+ "[crl_section]\n"
+ %% intentionally invalid
+ "URI.1=http://localhost/",C#config.commonName,"/crl.pem\n"
+ "URI.2=http://localhost:",integer_to_list(C#config.crl_port),"/",C#config.commonName,"/crl.pem\n"
+ "\n"
+
+ "[user_cert_digital_signature_only]\n"
+ "basicConstraints = CA:false\n"
+ "keyUsage = digitalSignature\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ "\n"
+
+ "[ca_cert]\n"
+ "basicConstraints = critical,CA:true\n"
+ "keyUsage = cRLSign, keyCertSign\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid:always,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ "crlDistributionPoints=@crl_section\n"
+ ];
+
+ca_cnf(C = #config{issuing_distribution_point = false}) ->
+ ["# Purpose: Configuration for CAs.\n"
+ "\n"
+ "ROOTDIR = $ENV::ROOTDIR\n"
+ "default_ca = ca\n"
+ "\n"
+
+ "[ca]\n"
+ "dir = $ROOTDIR/", C#config.commonName, "\n"
+ "certs = $dir/certs\n"
+ "crl_dir = $dir/crl\n"
+ "database = $dir/index.txt\n"
+ "new_certs_dir = $dir/newcerts\n"
+ "certificate = $dir/cert.pem\n"
+ "serial = $dir/serial\n"
+ "crl = $dir/crl.pem\n",
+ ["crlnumber = $dir/crlnumber\n" || C#config.v2_crls],
+ "private_key = $dir/private/key.pem\n"
+ "RANDFILE = $dir/private/RAND\n"
+ "\n"
+ "x509_extensions = user_cert\n",
+ ["crl_extensions = crl_ext\n" || C#config.v2_crls],
+ "unique_subject = no\n"
+ "default_days = 3600\n"
+ "default_md = md5\n"
+ "preserve = no\n"
+ "policy = policy_match\n"
+ "\n"
+
+ "[policy_match]\n"
+ "commonName = supplied\n"
+ "organizationalUnitName = optional\n"
+ "organizationName = match\n"
+ "countryName = match\n"
+ "localityName = match\n"
+ "emailAddress = supplied\n"
+ "\n"
+
+ "[crl_ext]\n"
+ "authorityKeyIdentifier=keyid:always,issuer:always\n",
+ %["issuingDistributionPoint=critical, @idpsec\n" || C#config.issuing_distribution_point],
+
+ %"[idpsec]\n"
+ %"fullname=URI:http://localhost:8000/",C#config.commonName,"/crl.pem\n"
+
+ "[user_cert]\n"
+ "basicConstraints = CA:false\n"
+ "keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ %"crlDistributionPoints=@crl_section\n"
+
+ %%"[crl_section]\n"
+ %% intentionally invalid
+ %%"URI.1=http://localhost/",C#config.commonName,"/crl.pem\n"
+ %%"URI.2=http://localhost:",integer_to_list(C#config.crl_port),"/",C#config.commonName,"/crl.pem\n"
+ %%"\n"
+
+ "[user_cert_digital_signature_only]\n"
+ "basicConstraints = CA:false\n"
+ "keyUsage = digitalSignature\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ "\n"
+
+ "[ca_cert]\n"
+ "basicConstraints = critical,CA:true\n"
+ "keyUsage = cRLSign, keyCertSign\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid:always,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ %"crlDistributionPoints=@crl_section\n"
+ ].
diff --git a/lib/inets/test/old_httpd_SUITE.erl b/lib/inets/test/old_httpd_SUITE.erl
deleted file mode 100644
index 172db53844..0000000000
--- a/lib/inets/test/old_httpd_SUITE.erl
+++ /dev/null
@@ -1,2347 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(old_httpd_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include("inets_test_lib.hrl").
-
--include_lib("kernel/include/file.hrl").
-
-%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
--export([init_per_testcase/2, end_per_testcase/2,
- init_per_suite/1, end_per_suite/1]).
-
-%% Core Server tests
--export([
- ip_mod_alias/1,
- ip_mod_actions/1,
- ip_mod_security/1,
- ip_mod_auth/1,
- ip_mod_auth_api/1,
- ip_mod_auth_mnesia_api/1,
- ip_mod_htaccess/1,
- ip_mod_cgi/1,
- ip_mod_esi/1,
- ip_mod_get/1,
- ip_mod_head/1,
- ip_mod_all/1,
- ip_load_light/1,
- ip_load_medium/1,
- ip_load_heavy/1,
- ip_dos_hostname/1,
- ip_time_test/1,
- ip_block_disturbing_idle/1,
- ip_block_non_disturbing_idle/1,
- ip_block_503/1,
- ip_block_disturbing_active/1,
- ip_block_non_disturbing_active/1,
- ip_block_disturbing_active_timeout_not_released/1,
- ip_block_disturbing_active_timeout_released/1,
- ip_block_non_disturbing_active_timeout_not_released/1,
- ip_block_non_disturbing_active_timeout_released/1,
- ip_block_disturbing_blocker_dies/1,
- ip_block_non_disturbing_blocker_dies/1,
- ip_restart_no_block/1,
- ip_restart_disturbing_block/1,
- ip_restart_non_disturbing_block/1
- ]).
-
--export([
- essl_mod_alias/1,
- essl_mod_actions/1,
- essl_mod_security/1,
- essl_mod_auth/1,
- essl_mod_auth_api/1,
- essl_mod_auth_mnesia_api/1,
- essl_mod_htaccess/1,
- essl_mod_cgi/1,
- essl_mod_esi/1,
- essl_mod_get/1,
- essl_mod_head/1,
- essl_mod_all/1,
- essl_load_light/1,
- essl_load_medium/1,
- essl_load_heavy/1,
- essl_dos_hostname/1,
- essl_time_test/1,
- essl_restart_no_block/1,
- essl_restart_disturbing_block/1,
- essl_restart_non_disturbing_block/1,
- essl_block_disturbing_idle/1,
- essl_block_non_disturbing_idle/1,
- essl_block_503/1,
- essl_block_disturbing_active/1,
- essl_block_non_disturbing_active/1,
- essl_block_disturbing_active_timeout_not_released/1,
- essl_block_disturbing_active_timeout_released/1,
- essl_block_non_disturbing_active_timeout_not_released/1,
- essl_block_non_disturbing_active_timeout_released/1,
- essl_block_disturbing_blocker_dies/1,
- essl_block_non_disturbing_blocker_dies/1
- ]).
-
-%%% HTTP 1.1 tests
--export([ip_host/1, ip_chunked/1, ip_expect/1, ip_range/1,
- ip_if_test/1, ip_http_trace/1, ip_http1_1_head/1,
- ip_mod_cgi_chunked_encoding_test/1]).
-
-%%% HTTP 1.0 tests
--export([ip_head_1_0/1, ip_get_1_0/1, ip_post_1_0/1]).
-
-%%% HTTP 0.9 tests
--export([ip_get_0_9/1]).
-
-%%% Ticket tests
--export([ticket_5775/1,ticket_5865/1,ticket_5913/1,ticket_6003/1,
- ticket_7304/1]).
-
-%%% IPv6 tests
--export([ipv6_hostname_ipcomm/0, ipv6_hostname_ipcomm/1,
- ipv6_address_ipcomm/0, ipv6_address_ipcomm/1,
- ipv6_hostname_essl/0, ipv6_hostname_essl/1,
- ipv6_address_essl/0, ipv6_address_essl/1]).
-
-%% Help functions
--export([cleanup_mnesia/0, setup_mnesia/0, setup_mnesia/1]).
-
--define(IP_PORT, 8898).
--define(SSL_PORT, 8899).
--define(MAX_HEADER_SIZE, 256).
--define(IPV6_LOCAL_HOST, "0:0:0:0:0:0:0:1").
-
-%% Minutes before failed auths timeout.
--define(FAIL_EXPIRE_TIME,1).
-
-%% Seconds before successful auths timeout.
--define(AUTH_TIMEOUT,5).
-
--record(httpd_user, {user_name, password, user_data}).
--record(httpd_group, {group_name, userlist}).
-
-
-%%--------------------------------------------------------------------
-%% all(Arg) -> [Doc] | [Case] | {skip, Comment}
-%% Arg - doc | suite
-%% Doc - string()
-%% Case - atom()
-%% Name of a test case function.
-%% Comment - string()
-%% Description: Returns documentation/test cases in this test suite
-%% or a skip tuple if the platform is not supported.
-%%--------------------------------------------------------------------
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [
- {group, ip},
- {group, ssl},
- %%{group, http_1_1_ip},
- %%{group, http_1_0_ip},
- %%{group, http_0_9_ip},
- %%{group, ipv6},
- {group, tickets}
- ].
-
-groups() ->
- [
- {ip, [],
- [
- %%ip_mod_alias,
- ip_mod_actions,
- %%ip_mod_security,
- %% ip_mod_auth,
- %% ip_mod_auth_api,
- ip_mod_auth_mnesia_api,
- %%ip_mod_htaccess,
- %%ip_mod_cgi,
- %%ip_mod_esi,
- %%ip_mod_get,
- %%ip_mod_head,
- %%ip_mod_all,
- %% ip_load_light,
- %% ip_load_medium,
- %% ip_load_heavy,
- %%ip_dos_hostname,
- ip_time_test,
- %% Only used through load_config
- %% but we still need these tests
- %% should be cleaned up and moved to new test suite
- %%ip_restart_no_block,
- %%ip_restart_disturbing_block,
- %%ip_restart_non_disturbing_block,
- %% Tested in inets_SUITE
- %%ip_block_disturbing_idle,
- %%ip_block_non_disturbing_idle,
- ip_block_503
- %% Tested in new httpd_SUITE
- %%ip_block_disturbing_active,
- %%ip_block_non_disturbing_active,
- %%ip_block_disturbing_blocker_dies,
- %%ip_block_non_disturbing_blocker_dies
- %% No longer relevant
- %%ip_block_disturbing_active_timeout_not_released,
- %%ip_block_disturbing_active_timeout_released,
- %%ip_block_non_disturbing_active_timeout_not_released,
- %%ip_block_non_disturbing_active_timeout_released,
- ]},
- {ssl, [], [{group, essl}]},
- {essl, [],
- [
- %%essl_mod_alias,
- essl_mod_actions,
- %% essl_mod_security,
- %% essl_mod_auth,
- %% essl_mod_auth_api,
- essl_mod_auth_mnesia_api,
- %%essl_mod_htaccess,
- %%essl_mod_cgi,
- %%essl_mod_esi,
- %%essl_mod_get,
- %%essl_mod_head,
- %% essl_mod_all,
- %% essl_load_light,
- %% essl_load_medium,
- %% essl_load_heavy,
- %%essl_dos_hostname,
- essl_time_test
- %% Replaced by load_config
- %% essl_restart_no_block,
- %% essl_restart_disturbing_block,
- %% essl_restart_non_disturbing_block,
- %% essl_block_disturbing_idle,
- %% essl_block_non_disturbing_idle, essl_block_503,
- %% essl_block_disturbing_active,
- %% essl_block_non_disturbing_active,
- %% essl_block_disturbing_active_timeout_not_released,
- %% essl_block_disturbing_active_timeout_released,
- %% essl_block_non_disturbing_active_timeout_not_released,
- %% essl_block_non_disturbing_active_timeout_released,
- %% essl_block_disturbing_blocker_dies,
- %% essl_block_non_disturbing_blocker_dies
- ]},
- %% {http_1_1_ip, [],
- %% [
- %% %%ip_host, ip_chunked, ip_expect,
- %% %%ip_range,
- %% %%ip_if_test
- %% %%ip_http_trace, ip_http1_1_head,
- %% %%ip_mod_cgi_chunked_encoding_test
- %% ]},
- %%{http_1_0_ip, [],
- %%[ip_head_1_0, ip_get_1_0, ip_post_1_0]},
- %%{http_0_9_ip, [], [ip_get_0_9]},
- %% {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm,
- %% ipv6_hostname_essl, ipv6_address_essl]},
- {tickets, [],
- [%%ticket_5775, ticket_5865,
- ticket_5913%%, ticket_6003,
- %%ticket_7304
- ]}].
-
-init_per_group(ipv6 = _GroupName, Config) ->
- case inets_test_lib:has_ipv6_support() of
- {ok, _} ->
- Config;
- _ ->
- {skip, "Host does not support IPv6"}
- end;
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- io:format(user, "init_per_suite -> entry with"
- "~n Config: ~p"
- "~n", [Config]),
-
- PrivDir = proplists:get_value(priv_dir, Config),
- SuiteTopDir = filename:join(PrivDir, ?MODULE),
- case file:make_dir(SuiteTopDir) of
- ok ->
- ok;
- {error, eexist} ->
- ok;
- Error ->
- throw({error, {failed_creating_suite_top_dir, Error}})
- end,
-
- [{has_ipv6_support, inets_test_lib:has_ipv6_support()},
- {suite_top_dir, SuiteTopDir},
- {node, node()},
- {host, inets_test_lib:hostname()},
- {address, getaddr()} | Config].
-
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-
-end_per_suite(_Config) ->
- %% SuiteTopDir = proplists:get_value(suite_top_dir, Config),
- %% inets_test_lib:del_dirs(SuiteTopDir),
- ok.
-
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(Case, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- NewConfig = init_per_testcase2(Case, Config),
- init_per_testcase3(Case, NewConfig).
-
-
-init_per_testcase2(Case, Config) ->
-
- %% tsp("init_per_testcase2 -> entry with"
- %% "~n Config: ~p", [Config]),
-
- IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
- IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf",
- SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
- SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf",
-
- DataDir = proplists:get_value(data_dir, Config),
- SuiteTopDir = proplists:get_value(suite_top_dir, Config),
-
- %% tsp("init_per_testcase2 -> "
- %% "~n SuiteDir: ~p"
- %% "~n DataDir: ~p", [SuiteTopDir, DataDir]),
-
- TcTopDir = filename:join(SuiteTopDir, Case),
- ?line ok = file:make_dir(TcTopDir),
-
- %% tsp("init_per_testcase2 -> "
- %% "~n TcTopDir: ~p", [TcTopDir]),
-
- DataSrc = filename:join([DataDir, "server_root"]),
- ServerRoot = filename:join([TcTopDir, "server_root"]),
-
- %% tsp("init_per_testcase2 -> "
- %% "~n DataSrc: ~p"
- %% "~n ServerRoot: ~p", [DataSrc, ServerRoot]),
-
- ok = file:make_dir(ServerRoot),
- ok = file:make_dir(filename:join([TcTopDir, "logs"])),
-
- NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config],
-
- %% tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"),
-
- inets_test_lib:copy_dirs(DataSrc, ServerRoot),
-
- %% tsp("init_per_testcase2 -> fix cgi"),
- EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]),
- {ok, FileInfo} = file:read_file_info(EnvCGI),
- ok = file:write_file_info(EnvCGI,
- FileInfo#file_info{mode = 8#00755}),
-
- EchoCGI = case test_server:os_type() of
- {win32, _} ->
- "cgi_echo.exe";
- _ ->
- "cgi_echo"
- end,
- CGIDir = filename:join([ServerRoot, "cgi-bin"]),
- inets_test_lib:copy_file(EchoCGI, DataDir, CGIDir),
- NewEchoCGI = filename:join([CGIDir, EchoCGI]),
- {ok, FileInfo1} = file:read_file_info(NewEchoCGI),
- ok = file:write_file_info(NewEchoCGI,
- FileInfo1#file_info{mode = 8#00755}),
-
- %% To be used by IP test cases
- %% tsp("init_per_testcase2 -> ip testcase setups"),
- create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
- normal_access, IpNormal),
- create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
- mod_htaccess, IpHtaccess),
-
- %% To be used by SSL test cases
- %% tsp("init_per_testcase2 -> ssl testcase setups"),
- SocketType =
- case atom_to_list(Case) of
- [X, $s, $s, $l | _] ->
- case X of
- $p -> ssl;
- $e -> essl
- end;
- _ ->
- ssl
- end,
-
- create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
- normal_access, SslNormal),
- create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
- mod_htaccess, SslHtaccess),
-
- %% To be used by IPv6 test cases. Case-clause is so that
- %% you can do ts:run(inets, httpd_SUITE, <test case>)
- %% for all cases except the ipv6 cases as they depend
- %% on 'test_host_ipv6_only' that will only be present
- %% when you run the whole test suite due to shortcomings
- %% of the test server.
-
- tsp("init_per_testcase2 -> maybe generate IPv6 config file(s)"),
- NewConfig2 =
- case atom_to_list(Case) of
- "ipv6_" ++ _ ->
- case (catch inets_test_lib:has_ipv6_support(NewConfig)) of
- {ok, IPv6Address0} ->
- {ok, Hostname} = inet:gethostname(),
- IPv6Address = http_transport:ipv6_name(IPv6Address0),
- create_ipv6_config([{port, ?IP_PORT},
- {sock_type, ip_comm},
- {ipv6_host, IPv6Address} |
- NewConfig],
- "ipv6_hostname_ipcomm.conf",
- Hostname),
- create_ipv6_config([{port, ?IP_PORT},
- {sock_type, ip_comm},
- {ipv6_host, IPv6Address} |
- NewConfig],
- "ipv6_address_ipcomm.conf",
- IPv6Address),
- create_ipv6_config([{port, ?SSL_PORT},
- {sock_type, essl},
- {ipv6_host, IPv6Address} |
- NewConfig],
- "ipv6_hostname_essl.conf",
- Hostname),
- create_ipv6_config([{port, ?SSL_PORT},
- {sock_type, essl},
- {ipv6_host, IPv6Address} |
- NewConfig],
- "ipv6_address_essl.conf",
- IPv6Address),
- [{ipv6_host, IPv6Address} | NewConfig];
- _ ->
- NewConfig
- end;
-
- _ ->
- NewConfig
- end,
-
- %% tsp("init_per_testcase2 -> done when"
- %% "~n NewConfig2: ~p", [NewConfig2]),
-
- NewConfig2.
-
-
-init_per_testcase3(Case, Config) ->
- tsp("init_per_testcase3(~w) -> entry with"
- "~n Config: ~p", [Case, Config]),
-
-
-%% %% Create a new fresh node to be used by the server in this test-case
-
-%% NodeName = list_to_atom(atom_to_list(Case) ++ "_httpd"),
-%% Node = inets_test_lib:start_node(NodeName),
-
- %% Clean up (we do not want this clean up in end_per_testcase
- %% if init_per_testcase crashes for some testcase it will
- %% have contaminated the environment and there will be no clean up.)
- %% This init can take a few different paths so that one crashes
- %% does not mean that all invocations will.
-
- application:unset_env(inets, services),
- application:stop(inets),
- application:stop(ssl),
- cleanup_mnesia(),
-
- %% Start initialization
- tsp("init_per_testcase3(~w) -> start init", [Case]),
-
- Dog = test_server:timetrap(inets_test_lib:minutes(10)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- TcTopDir = proplists:get_value(tc_top_dir, Config),
-
- CaseRest =
- case atom_to_list(Case) of
- "ip_mod_htaccess" ->
- inets_test_lib:start_http_server(
- filename:join(TcTopDir,
- integer_to_list(?IP_PORT) ++
- "htaccess.conf")),
- "mod_htaccess";
- "ip_" ++ Rest ->
- inets_test_lib:start_http_server(
- filename:join(TcTopDir,
- integer_to_list(?IP_PORT) ++ ".conf")),
- Rest;
- "ticket_5913" ->
- HttpdOptions =
- [{file,
- filename:join(TcTopDir,
- integer_to_list(?IP_PORT) ++ ".conf")},
- {accept_timeout,30000},
- {debug,[{exported_functions,
- [httpd_manager,httpd_request_handler]}]}],
- inets_test_lib:start_http_server(HttpdOptions);
- "ticket_"++Rest ->
- %% OTP-5913 use the new syntax of inets.config
- inets_test_lib:start_http_server([{file,
- filename:join(TcTopDir,
- integer_to_list(?IP_PORT) ++ ".conf")}]),
- Rest;
-
- [X, $s, $s, $l, $_, $m, $o, $d, $_, $h, $t, $a, $c, $c, $e, $s, $s] ->
- ?ENSURE_STARTED([crypto, public_key, ssl]),
- SslTag =
- case X of
- $p -> ssl; % Plain
- $e -> essl % Erlang based ssl
- end,
- case inets_test_lib:start_http_server_ssl(
- filename:join(TcTopDir,
- integer_to_list(?SSL_PORT) ++
- "htaccess.conf"), SslTag) of
- ok ->
- "mod_htaccess";
- Other ->
- error_logger:info_msg("Other: ~p~n", [Other]),
- {skip, "SSL does not seem to be supported"}
- end;
- [X, $s, $s, $l, $_ | Rest] ->
- ?ENSURE_STARTED([crypto, public_key, ssl]),
- SslTag =
- case X of
- $p -> ssl;
- $e -> essl
- end,
- case inets_test_lib:start_http_server_ssl(
- filename:join(TcTopDir,
- integer_to_list(?SSL_PORT) ++
- ".conf"), SslTag) of
- ok ->
- Rest;
- Other ->
- error_logger:info_msg("Other: ~p~n", [Other]),
- {skip, "SSL does not seem to be supported"}
- end;
- "ipv6_" ++ _ = TestCaseStr ->
- case inets_test_lib:has_ipv6_support() of
- {ok, _} ->
- inets_test_lib:start_http_server(
- filename:join(TcTopDir,
- TestCaseStr ++ ".conf"));
-
- _ ->
- {skip, "Host does not support IPv6"}
- end
- end,
-
- InitRes =
- case CaseRest of
- {skip, _} = Skip ->
- Skip;
- "mod_auth_" ++ _ ->
- start_mnesia(proplists:get_value(node, Config)),
- [{watchdog, Dog} | NewConfig];
- "mod_htaccess" ->
- ServerRoot = proplists:get_value(server_root, Config),
- Path = filename:join([ServerRoot, "htdocs"]),
- catch remove_htaccess(Path),
- create_htaccess_data(Path, proplists:get_value(address, Config)),
- [{watchdog, Dog} | NewConfig];
- "range" ->
- ServerRoot = proplists:get_value(server_root, Config),
- Path = filename:join([ServerRoot, "htdocs"]),
- create_range_data(Path),
- [{watchdog, Dog} | NewConfig];
- _ ->
- [{watchdog, Dog} | NewConfig]
- end,
-
- tsp("init_per_testcase3(~w) -> done when"
- "~n InitRes: ~p", [Case, InitRes]),
-
- InitRes.
-
-
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(Case, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- Dog = proplists:get_value(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- end_per_testcase2(Case, lists:keydelete(watchdog, 1, Config)),
- ok.
-
-end_per_testcase2(Case, Config) ->
- tsp("end_per_testcase2(~w) -> entry with"
- "~n Config: ~p", [Case, Config]),
- application:unset_env(inets, services),
- application:stop(inets),
- application:stop(ssl),
- application:stop(crypto), % used by the new ssl (essl test cases)
- cleanup_mnesia(),
- tsp("end_per_testcase2(~w) -> done", [Case]),
- ok.
-
-
-%%-------------------------------------------------------------------------
-%% Test cases starts here.
-%%-------------------------------------------------------------------------
-
-%%-------------------------------------------------------------------------
-ip_mod_alias(doc) ->
- ["Module test: mod_alias"];
-ip_mod_alias(suite) ->
- [];
-ip_mod_alias(Config) when is_list(Config) ->
- httpd_mod:alias(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_actions(doc) ->
- ["Module test: mod_actions"];
-ip_mod_actions(suite) ->
- [];
-ip_mod_actions(Config) when is_list(Config) ->
- httpd_mod:actions(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_security(doc) ->
- ["Module test: mod_security"];
-ip_mod_security(suite) ->
- [];
-ip_mod_security(Config) when is_list(Config) ->
- ServerRoot = proplists:get_value(server_root, Config),
- httpd_mod:security(ServerRoot, ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_auth(doc) ->
- ["Module test: mod_auth"];
-ip_mod_auth(suite) ->
- [];
-ip_mod_auth(Config) when is_list(Config) ->
- httpd_mod:auth(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_auth_api(doc) ->
- ["Module test: mod_auth_api"];
-ip_mod_auth_api(suite) ->
- [];
-ip_mod_auth_api(Config) when is_list(Config) ->
- ServerRoot = proplists:get_value(server_root, Config),
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- httpd_mod:auth_api(ServerRoot, "", ip_comm, ?IP_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "dets_", ip_comm, ?IP_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "mnesia_", ip_comm, ?IP_PORT, Host, Node),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_auth_mnesia_api(doc) ->
- ["Module test: mod_auth_mnesia_api"];
-ip_mod_auth_mnesia_api(suite) ->
- [];
-ip_mod_auth_mnesia_api(Config) when is_list(Config) ->
- httpd_mod:auth_mnesia_api(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_htaccess(doc) ->
- ["Module test: mod_htaccess"];
-ip_mod_htaccess(suite) ->
- [];
-ip_mod_htaccess(Config) when is_list(Config) ->
- httpd_mod:htaccess(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_cgi(doc) ->
- ["Module test: mod_cgi"];
-ip_mod_cgi(suite) ->
- [];
-ip_mod_cgi(Config) when is_list(Config) ->
- httpd_mod:cgi(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_esi(doc) ->
- ["Module test: mod_esi"];
-ip_mod_esi(suite) ->
- [];
-ip_mod_esi(Config) when is_list(Config) ->
- httpd_mod:esi(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_get(doc) ->
- ["Module test: mod_get"];
-ip_mod_get(suite) ->
- [];
-ip_mod_get(Config) when is_list(Config) ->
- httpd_mod:get(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_head(doc) ->
- ["Module test: mod_head"];
-ip_mod_head(suite) ->
- [];
-ip_mod_head(Config) when is_list(Config) ->
- httpd_mod:head(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_all(doc) ->
- ["All modules test"];
-ip_mod_all(suite) ->
- [];
-ip_mod_all(Config) when is_list(Config) ->
- httpd_mod:all(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_load_light(doc) ->
- ["Test light load"];
-ip_load_light(suite) ->
- [];
-ip_load_light(Config) when is_list(Config) ->
- httpd_load:load_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config),
- get_nof_clients(ip_comm, light)),
- ok.
-%%-------------------------------------------------------------------------
-ip_load_medium(doc) ->
- ["Test medium load"];
-ip_load_medium(suite) ->
- [];
-ip_load_medium(Config) when is_list(Config) ->
- httpd_load:load_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config),
- get_nof_clients(ip_comm, medium)),
- ok.
-%%-------------------------------------------------------------------------
-ip_load_heavy(doc) ->
- ["Test heavy load"];
-ip_load_heavy(suite) ->
- [];
-ip_load_heavy(Config) when is_list(Config) ->
- httpd_load:load_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config),
- get_nof_clients(ip_comm, heavy)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-ip_dos_hostname(doc) ->
- ["Denial Of Service (DOS) attack test case"];
-ip_dos_hostname(suite) ->
- [];
-ip_dos_hostname(Config) when is_list(Config) ->
- dos_hostname(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config), ?MAX_HEADER_SIZE),
- ok.
-
-
-%%-------------------------------------------------------------------------
-ip_time_test(doc) ->
- [""];
-ip_time_test(suite) ->
- [];
-ip_time_test(Config) when is_list(Config) ->
- httpd_time_test:t(ip_comm, proplists:get_value(host, Config), ?IP_PORT),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_block_503(doc) ->
- ["Check that you will receive status code 503 when the server"
- " is blocked and 200 when its not blocked."];
-ip_block_503(suite) ->
- [];
-ip_block_503(Config) when is_list(Config) ->
- httpd_block:block_503(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_block_disturbing_idle(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "distribing does not really make a difference in this case."];
-ip_block_disturbing_idle(suite) ->
- [];
-ip_block_disturbing_idle(Config) when is_list(Config) ->
- httpd_block:block_disturbing_idle(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_block_non_disturbing_idle(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "non distribing does not really make a difference in this case."];
-ip_block_non_disturbing_idle(suite) ->
- [];
-ip_block_non_disturbing_idle(Config) when is_list(Config) ->
- httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_block_disturbing_active(doc) ->
- ["Check that you can block/unblock an active server. The strategy "
- "distribing means ongoing requests should be terminated."];
-ip_block_disturbing_active(suite) ->
- [];
-ip_block_disturbing_active(Config) when is_list(Config) ->
- httpd_block:block_disturbing_active(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_block_non_disturbing_active(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "non distribing means the ongoing requests should be compleated."];
-ip_block_non_disturbing_active(suite) ->
- [];
-ip_block_non_disturbing_active(Config) when is_list(Config) ->
- httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_block_disturbing_active_timeout_not_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "distribing means ongoing requests should be compleated"
- "if the timeout does not occur."];
-ip_block_disturbing_active_timeout_not_released(suite) ->
- [];
-ip_block_disturbing_active_timeout_not_released(Config)
- when is_list(Config) ->
- httpd_block:block_disturbing_active_timeout_not_released(ip_comm,
- ?IP_PORT,
- proplists:get_value(host,
- Config),
- proplists:get_value(node,
- Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_block_disturbing_active_timeout_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "distribing means ongoing requests should be terminated when"
- "the timeout occurs."];
-ip_block_disturbing_active_timeout_released(suite) ->
- [];
-ip_block_disturbing_active_timeout_released(Config)
- when is_list(Config) ->
- httpd_block:block_disturbing_active_timeout_released(ip_comm,
- ?IP_PORT,
- proplists:get_value(host,
- Config),
- proplists:get_value(node,
- Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_block_non_disturbing_active_timeout_not_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "non non distribing means ongoing requests should be completed."];
-ip_block_non_disturbing_active_timeout_not_released(suite) ->
- [];
-ip_block_non_disturbing_active_timeout_not_released(Config)
- when is_list(Config) ->
- httpd_block:
- block_non_disturbing_active_timeout_not_released(ip_comm,
- ?IP_PORT,
- proplists:get_value(host,
- Config),
- proplists:get_value(node,
- Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_block_non_disturbing_active_timeout_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "non non distribing means ongoing requests should be completed. "
- "When the timeout occurs the block operation sohould be canceled." ];
-ip_block_non_disturbing_active_timeout_released(suite) ->
- [];
-ip_block_non_disturbing_active_timeout_released(Config)
- when is_list(Config) ->
- httpd_block:
- block_non_disturbing_active_timeout_released(ip_comm,
- ?IP_PORT,
- proplists:get_value(host,
- Config),
- proplists:get_value(node,
- Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_block_disturbing_blocker_dies(doc) ->
- [];
-ip_block_disturbing_blocker_dies(suite) ->
- [];
-ip_block_disturbing_blocker_dies(Config) when is_list(Config) ->
- httpd_block:disturbing_blocker_dies(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_block_non_disturbing_blocker_dies(doc) ->
- [];
-ip_block_non_disturbing_blocker_dies(suite) ->
- [];
-ip_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
- httpd_block:non_disturbing_blocker_dies(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_restart_no_block(doc) ->
- [""];
-ip_restart_no_block(suite) ->
- [];
-ip_restart_no_block(Config) when is_list(Config) ->
- httpd_block:restart_no_block(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_restart_disturbing_block(doc) ->
- [""];
-ip_restart_disturbing_block(suite) ->
- [];
-ip_restart_disturbing_block(Config) when is_list(Config) ->
- httpd_block:restart_disturbing_block(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_restart_non_disturbing_block(doc) ->
- [""];
-ip_restart_non_disturbing_block(suite) ->
- [];
-ip_restart_non_disturbing_block(Config) when is_list(Config) ->
- httpd_block:restart_non_disturbing_block(ip_comm, ?IP_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-
-essl_mod_alias(doc) ->
- ["Module test: mod_alias - using new of configure new SSL"];
-essl_mod_alias(suite) ->
- [];
-essl_mod_alias(Config) when is_list(Config) ->
- ssl_mod_alias(essl, Config).
-
-
-ssl_mod_alias(Tag, Config) ->
- httpd_mod:alias(Tag, ?SSL_PORT,
- proplists:get_value(host, Config), proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_actions(doc) ->
- ["Module test: mod_actions - using new of configure new SSL"];
-essl_mod_actions(suite) ->
- [];
-essl_mod_actions(Config) when is_list(Config) ->
- ssl_mod_actions(essl, Config).
-
-
-ssl_mod_actions(Tag, Config) ->
- httpd_mod:actions(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_security(doc) ->
- ["Module test: mod_security - using new of configure new SSL"];
-essl_mod_security(suite) ->
- [];
-essl_mod_security(Config) when is_list(Config) ->
- ssl_mod_security(essl, Config).
-
-ssl_mod_security(Tag, Config) ->
- ServerRoot = proplists:get_value(server_root, Config),
- httpd_mod:security(ServerRoot,
- Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_auth(doc) ->
- ["Module test: mod_auth - using new of configure new SSL"];
-essl_mod_auth(suite) ->
- [];
-essl_mod_auth(Config) when is_list(Config) ->
- ssl_mod_auth(essl, Config).
-
-ssl_mod_auth(Tag, Config) ->
- httpd_mod:auth(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_mod_auth_api(doc) ->
- ["Module test: mod_auth - using new of configure new SSL"];
-essl_mod_auth_api(suite) ->
- [];
-essl_mod_auth_api(Config) when is_list(Config) ->
- ssl_mod_auth_api(essl, Config).
-
-ssl_mod_auth_api(Tag, Config) ->
- ServerRoot = proplists:get_value(server_root, Config),
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- httpd_mod:auth_api(ServerRoot, "", Tag, ?SSL_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "dets_", Tag, ?SSL_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "mnesia_", Tag, ?SSL_PORT, Host, Node),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_mod_auth_mnesia_api(doc) ->
- ["Module test: mod_auth_mnesia_api - using new of configure new SSL"];
-essl_mod_auth_mnesia_api(suite) ->
- [];
-essl_mod_auth_mnesia_api(Config) when is_list(Config) ->
- ssl_mod_auth_mnesia_api(essl, Config).
-
-ssl_mod_auth_mnesia_api(Tag, Config) ->
- httpd_mod:auth_mnesia_api(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_htaccess(doc) ->
- ["Module test: mod_htaccess - using new of configure new SSL"];
-essl_mod_htaccess(suite) ->
- [];
-essl_mod_htaccess(Config) when is_list(Config) ->
- ssl_mod_htaccess(essl, Config).
-
-ssl_mod_htaccess(Tag, Config) ->
- httpd_mod:htaccess(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_cgi(doc) ->
- ["Module test: mod_cgi - using new of configure new SSL"];
-essl_mod_cgi(suite) ->
- [];
-essl_mod_cgi(Config) when is_list(Config) ->
- ssl_mod_cgi(essl, Config).
-
-ssl_mod_cgi(Tag, Config) ->
- httpd_mod:cgi(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_esi(doc) ->
- ["Module test: mod_esi - using new of configure new SSL"];
-essl_mod_esi(suite) ->
- [];
-essl_mod_esi(Config) when is_list(Config) ->
- ssl_mod_esi(essl, Config).
-
-ssl_mod_esi(Tag, Config) ->
- httpd_mod:esi(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_get(doc) ->
- ["Module test: mod_get - using new of configure new SSL"];
-essl_mod_get(suite) ->
- [];
-essl_mod_get(Config) when is_list(Config) ->
- ssl_mod_get(essl, Config).
-
-ssl_mod_get(Tag, Config) ->
- httpd_mod:get(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_head(doc) ->
- ["Module test: mod_head - using new of configure new SSL"];
-essl_mod_head(suite) ->
- [];
-essl_mod_head(Config) when is_list(Config) ->
- ssl_mod_head(essl, Config).
-
-ssl_mod_head(Tag, Config) ->
- httpd_mod:head(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_all(doc) ->
- ["All modules test - using new of configure new SSL"];
-essl_mod_all(suite) ->
- [];
-essl_mod_all(Config) when is_list(Config) ->
- ssl_mod_all(essl, Config).
-
-ssl_mod_all(Tag, Config) ->
- httpd_mod:all(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_load_light(doc) ->
- ["Test light load - using new of configure new SSL"];
-essl_load_light(suite) ->
- [];
-essl_load_light(Config) when is_list(Config) ->
- ssl_load_light(essl, Config).
-
-ssl_load_light(Tag, Config) ->
- httpd_load:load_test(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config),
- get_nof_clients(ssl, light)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_load_medium(doc) ->
- ["Test medium load - using new of configure new SSL"];
-essl_load_medium(suite) ->
- [];
-essl_load_medium(Config) when is_list(Config) ->
- ssl_load_medium(essl, Config).
-
-ssl_load_medium(Tag, Config) ->
- httpd_load:load_test(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config),
- get_nof_clients(ssl, medium)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_load_heavy(doc) ->
- ["Test heavy load - using new of configure new SSL"];
-essl_load_heavy(suite) ->
- [];
-essl_load_heavy(Config) when is_list(Config) ->
- ssl_load_heavy(essl, Config).
-
-ssl_load_heavy(Tag, Config) ->
- httpd_load:load_test(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config),
- get_nof_clients(ssl, heavy)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_dos_hostname(doc) ->
- ["Denial Of Service (DOS) attack test case - using new of configure new SSL"];
-essl_dos_hostname(suite) ->
- [];
-essl_dos_hostname(Config) when is_list(Config) ->
- ssl_dos_hostname(essl, Config).
-
-ssl_dos_hostname(Tag, Config) ->
- dos_hostname(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config),
- ?MAX_HEADER_SIZE),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_time_test(doc) ->
- ["using new of configure new SSL"];
-essl_time_test(suite) ->
- [];
-essl_time_test(Config) when is_list(Config) ->
- ssl_time_test(essl, Config).
-
-ssl_time_test(Tag, Config) when is_list(Config) ->
- httpd_time_test:t(Tag,
- proplists:get_value(host, Config),
- ?SSL_PORT),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_block_503(doc) ->
- ["Check that you will receive status code 503 when the server"
- " is blocked and 200 when its not blocked - using new of configure new SSL."];
-essl_block_503(suite) ->
- [];
-essl_block_503(Config) when is_list(Config) ->
- ssl_block_503(essl, Config).
-
-ssl_block_503(Tag, Config) ->
- httpd_block:block_503(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_disturbing_idle(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "distribing does not really make a difference in this case."
- "Using new of configure new SSL"];
-essl_block_disturbing_idle(suite) ->
- [];
-essl_block_disturbing_idle(Config) when is_list(Config) ->
- ssl_block_disturbing_idle(essl, Config).
-
-ssl_block_disturbing_idle(Tag, Config) ->
- httpd_block:block_disturbing_idle(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_non_disturbing_idle(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "non distribing does not really make a difference in this case."
- "Using new of configure new SSL"];
-essl_block_non_disturbing_idle(suite) ->
- [];
-essl_block_non_disturbing_idle(Config) when is_list(Config) ->
- ssl_block_non_disturbing_idle(essl, Config).
-
-ssl_block_non_disturbing_idle(Tag, Config) ->
- httpd_block:block_non_disturbing_idle(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_disturbing_active(doc) ->
- ["Check that you can block/unblock an active server. The strategy "
- "distribing means ongoing requests should be terminated."
- "Using new of configure new SSL"];
-essl_block_disturbing_active(suite) ->
- [];
-essl_block_disturbing_active(Config) when is_list(Config) ->
- ssl_block_disturbing_active(essl, Config).
-
-ssl_block_disturbing_active(Tag, Config) ->
- httpd_block:block_disturbing_active(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_non_disturbing_active(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "non distribing means the ongoing requests should be compleated."
- "Using new of configure new SSL"];
-essl_block_non_disturbing_active(suite) ->
- [];
-essl_block_non_disturbing_active(Config) when is_list(Config) ->
- ssl_block_non_disturbing_active(essl, Config).
-
-ssl_block_non_disturbing_active(Tag, Config) ->
- httpd_block:block_non_disturbing_idle(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_disturbing_active_timeout_not_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "distribing means ongoing requests should be compleated"
- "if the timeout does not occur."
- "Using new of configure new SSL"];
-essl_block_disturbing_active_timeout_not_released(suite) ->
- [];
-essl_block_disturbing_active_timeout_not_released(Config)
- when is_list(Config) ->
- ssl_block_disturbing_active_timeout_not_released(essl, Config).
-
-ssl_block_disturbing_active_timeout_not_released(Tag, Config) ->
- Port = ?SSL_PORT,
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- httpd_block:block_disturbing_active_timeout_not_released(Tag,
- Port, Host, Node),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_disturbing_active_timeout_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "distribing means ongoing requests should be terminated when"
- "the timeout occurs."
- "Using new of configure new SSL"];
-essl_block_disturbing_active_timeout_released(suite) ->
- [];
-essl_block_disturbing_active_timeout_released(Config)
- when is_list(Config) ->
- ssl_block_disturbing_active_timeout_released(essl, Config).
-
-ssl_block_disturbing_active_timeout_released(Tag, Config) ->
- Port = ?SSL_PORT,
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- httpd_block:block_disturbing_active_timeout_released(Tag,
- Port,
- Host,
- Node),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_non_disturbing_active_timeout_not_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "non non distribing means ongoing requests should be completed."
- "Using new of configure new SSL"];
-essl_block_non_disturbing_active_timeout_not_released(suite) ->
- [];
-essl_block_non_disturbing_active_timeout_not_released(Config)
- when is_list(Config) ->
- ssl_block_non_disturbing_active_timeout_not_released(essl, Config).
-
-ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) ->
- Port = ?SSL_PORT,
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- httpd_block:block_non_disturbing_active_timeout_not_released(Tag,
- Port,
- Host,
- Node),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_block_non_disturbing_active_timeout_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "non distribing means ongoing requests should be completed. "
- "When the timeout occurs the block operation sohould be canceled."
- "Using new of configure new SSL"];
-essl_block_non_disturbing_active_timeout_released(suite) ->
- [];
-essl_block_non_disturbing_active_timeout_released(Config)
- when is_list(Config) ->
- ssl_block_non_disturbing_active_timeout_released(essl, Config).
-
-ssl_block_non_disturbing_active_timeout_released(Tag, Config)
- when is_list(Config) ->
- Port = ?SSL_PORT,
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- httpd_block:block_non_disturbing_active_timeout_released(Tag,
- Port,
- Host,
- Node),
-
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_block_disturbing_blocker_dies(doc) ->
- ["using new of configure new SSL"];
-essl_block_disturbing_blocker_dies(suite) ->
- [];
-essl_block_disturbing_blocker_dies(Config) when is_list(Config) ->
- ssl_block_disturbing_blocker_dies(essl, Config).
-
-ssl_block_disturbing_blocker_dies(Tag, Config) ->
- httpd_block:disturbing_blocker_dies(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_non_disturbing_blocker_dies(doc) ->
- ["using new of configure new SSL"];
-essl_block_non_disturbing_blocker_dies(suite) ->
- [];
-essl_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
- ssl_block_non_disturbing_blocker_dies(essl, Config).
-
-ssl_block_non_disturbing_blocker_dies(Tag, Config) ->
- httpd_block:non_disturbing_blocker_dies(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_restart_no_block(doc) ->
- ["using new of configure new SSL"];
-essl_restart_no_block(suite) ->
- [];
-essl_restart_no_block(Config) when is_list(Config) ->
- ssl_restart_no_block(essl, Config).
-
-ssl_restart_no_block(Tag, Config) ->
- httpd_block:restart_no_block(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_restart_disturbing_block(doc) ->
- ["using new of configure new SSL"];
-essl_restart_disturbing_block(suite) ->
- [];
-essl_restart_disturbing_block(Config) when is_list(Config) ->
- ssl_restart_disturbing_block(essl, Config).
-
-ssl_restart_disturbing_block(Tag, Config) ->
- httpd_block:restart_disturbing_block(Tag, ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_restart_non_disturbing_block(doc) ->
- ["using new of configure new SSL"];
-essl_restart_non_disturbing_block(suite) ->
- [];
-essl_restart_non_disturbing_block(Config) when is_list(Config) ->
- ssl_restart_non_disturbing_block(essl, Config).
-
-ssl_restart_non_disturbing_block(Tag, Config) ->
- httpd_block:restart_non_disturbing_block(Tag,
- ?SSL_PORT,
- proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-ip_host(doc) ->
- ["Control that the server accepts/rejects requests with/ without host"];
-ip_host(suite)->
- [];
-ip_host(Config) when is_list(Config) ->
- httpd_1_1:host(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_chunked(doc) ->
- ["Control that the server accepts chunked requests"];
-ip_chunked(suite) ->
- [];
-ip_chunked(Config) when is_list(Config) ->
- httpd_1_1:chunked(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_expect(doc) ->
- ["Control that the server handles request with the expect header "
- "field appropiate"];
-ip_expect(suite)->
- [];
-ip_expect(Config) when is_list(Config) ->
- httpd_1_1:expect(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_range(doc) ->
- ["Control that the server can handle range requests to plain files"];
-ip_range(suite)->
- [];
-ip_range(Config) when is_list(Config) ->
- httpd_1_1:range(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_if_test(doc) ->
- ["Test that the if - request header fields is handled correclty"];
-ip_if_test(suite) ->
- [];
-ip_if_test(Config) when is_list(Config) ->
- ServerRoot = proplists:get_value(server_root, Config),
- DocRoot = filename:join([ServerRoot, "htdocs"]),
- httpd_1_1:if_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config), DocRoot),
- ok.
-%%-------------------------------------------------------------------------
-ip_http_trace(doc) ->
- ["Test the trace module "];
-ip_http_trace(suite) ->
- [];
-ip_http_trace(Config) when is_list(Config) ->
- httpd_1_1:http_trace(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_http1_1_head(doc) ->
- ["Test the trace module "];
-ip_http1_1_head(suite)->
- [];
-ip_http1_1_head(Config) when is_list(Config) ->
- httpd_1_1:head(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
- proplists:get_value(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_get_0_9(doc) ->
- ["Test simple HTTP/0.9 GET"];
-ip_get_0_9(suite)->
- [];
-ip_get_0_9(Config) when is_list(Config) ->
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
- "GET / \r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/0.9"} ]),
- %% Without space after uri
- ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
- "GET /\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/0.9"} ]),
- ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
- "GET / HTTP/0.9\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/0.9"}]),
-
- ok.
-%%-------------------------------------------------------------------------
-ip_head_1_0(doc) ->
- ["Test HTTP/1.0 HEAD"];
-ip_head_1_0(suite)->
- [];
-ip_head_1_0(Config) when is_list(Config) ->
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
- "HEAD / HTTP/1.0\r\n\r\n", [{statuscode, 200},
- {version, "HTTP/1.0"}]),
-
- ok.
-%%-------------------------------------------------------------------------
-ip_get_1_0(doc) ->
- ["Test HTTP/1.0 GET"];
-ip_get_1_0(suite)->
- [];
-ip_get_1_0(Config) when is_list(Config) ->
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
- "GET / HTTP/1.0\r\n\r\n", [{statuscode, 200},
- {version, "HTTP/1.0"}]),
-
- ok.
-%%-------------------------------------------------------------------------
-ip_post_1_0(doc) ->
- ["Test HTTP/1.0 POST"];
-ip_post_1_0(suite)->
- [];
-ip_post_1_0(Config) when is_list(Config) ->
- Host = proplists:get_value(host, Config),
- Node = proplists:get_value(node, Config),
- %% Test the post message formatin 1.0! Real post are testes elsewhere
- ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
- "POST / HTTP/1.0\r\n\r\n "
- "Content-Length:6 \r\n\r\nfoobar",
- [{statuscode, 500}, {version, "HTTP/1.0"}]),
-
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_cgi_chunked_encoding_test(doc) ->
- ["Test the trace module "];
-ip_mod_cgi_chunked_encoding_test(suite)->
- [];
-ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) ->
- Host = proplists:get_value(host, Config),
- Script =
- case test_server:os_type() of
- {win32, _} ->
- "/cgi-bin/printenv.bat";
- _ ->
- "/cgi-bin/printenv.sh"
- end,
- Requests =
- ["GET " ++ Script ++ " HTTP/1.1\r\nHost:"++ Host ++"\r\n\r\n",
- "GET /cgi-bin/erl/httpd_example/newformat HTTP/1.1\r\nHost:"
- ++ Host ++"\r\n\r\n"],
- httpd_1_1:mod_cgi_chunked_encoding_test(ip_comm, ?IP_PORT,
- Host,
- proplists:get_value(node, Config),
- Requests),
- ok.
-
-%-------------------------------------------------------------------------
-
-ipv6_hostname_ipcomm() ->
- [{require, ipv6_hosts}].
-ipv6_hostname_ipcomm(X) ->
- SocketType = ip_comm,
- Port = ?IP_PORT,
- ipv6_hostname(SocketType, Port, X).
-
-ipv6_hostname_essl() ->
- [{require, ipv6_hosts}].
-ipv6_hostname_essl(X) ->
- SocketType = essl,
- Port = ?SSL_PORT,
- ipv6_hostname(SocketType, Port, X).
-
-ipv6_hostname(_SocketType, _Port, doc) ->
- ["Test standard ipv6 address"];
-ipv6_hostname(_SocketType, _Port, suite)->
- [];
-ipv6_hostname(SocketType, Port, Config) when is_list(Config) ->
- tsp("ipv6_hostname -> entry with"
- "~n SocketType: ~p"
- "~n Port: ~p"
- "~n Config: ~p", [SocketType, Port, Config]),
- Host = proplists:get_value(host, Config),
- URI = "GET HTTP://" ++
- Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
- tsp("ipv6_hostname -> Host: ~p", [Host]),
- httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
- node(),
- URI,
- [{statuscode, 200}, {version, "HTTP/1.1"}]),
- ok.
-
-%%-------------------------------------------------------------------------
-
-ipv6_address_ipcomm() ->
- [{require, ipv6_hosts}].
-ipv6_address_ipcomm(X) ->
- SocketType = ip_comm,
- Port = ?IP_PORT,
- ipv6_address(SocketType, Port, X).
-
-ipv6_address_essl() ->
- [{require, ipv6_hosts}].
-ipv6_address_essl(X) ->
- SocketType = essl,
- Port = ?SSL_PORT,
- ipv6_address(SocketType, Port, X).
-
-ipv6_address(_SocketType, _Port, doc) ->
- ["Test standard ipv6 address"];
-ipv6_address(_SocketType, _Port, suite)->
- [];
-ipv6_address(SocketType, Port, Config) when is_list(Config) ->
- tsp("ipv6_address -> entry with"
- "~n SocketType: ~p"
- "~n Port: ~p"
- "~n Config: ~p", [SocketType, Port, Config]),
- Host = proplists:get_value(host, Config),
- tsp("ipv6_address -> Host: ~p", [Host]),
- URI = "GET HTTP://" ++
- Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
- httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
- node(),
- URI,
- [{statuscode, 200}, {version, "HTTP/1.1"}]),
- ok.
-
-
-%%--------------------------------------------------------------------
-ticket_5775(doc) ->
- ["Tests that content-length is correct"];
-ticket_5775(suite) ->
- [];
-ticket_5775(Config) ->
- ok=httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
- ?IP_PORT, proplists:get_value(node, Config),
- "GET /cgi-bin/erl/httpd_example:get_bin "
- "HTTP/1.0\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/1.0"}]),
- ok.
-ticket_5865(doc) ->
- ["Tests that a header without last-modified is handled"];
-ticket_5865(suite) ->
- [];
-ticket_5865(Config) ->
- ct:skip(as_of_r15_behaviour_of_calendar_has_changed),
- Host = proplists:get_value(host,Config),
- ServerRoot = proplists:get_value(server_root, Config),
- DocRoot = filename:join([ServerRoot, "htdocs"]),
- File = filename:join([DocRoot,"last_modified.html"]),
-
- Bad_mtime = case test_server:os_type() of
- {win32, _} ->
- {{1600,12,31},{23,59,59}};
- {unix, _} ->
- {{1969,12,31},{23,59,59}}
- end,
-
- {ok,FI}=file:read_file_info(File),
-
- case file:write_file_info(File,FI#file_info{mtime=Bad_mtime}) of
- ok ->
- ok = httpd_test_lib:verify_request(ip_comm, Host,
- ?IP_PORT, proplists:get_value(node, Config),
- "GET /last_modified.html"
- " HTTP/1.1\r\nHost:"
- ++Host++"\r\n\r\n",
- [{statuscode, 200},
- {no_header,
- "last-modified"}]),
- ok;
- {error, Reason} ->
- Fault =
- io_lib:format("Attempt to change the file info to set the"
- " preconditions of the test case failed ~p~n",
- [Reason]),
- {skip, Fault}
- end.
-
-ticket_5913(doc) ->
- ["Tests that a header without last-modified is handled"];
-ticket_5913(suite) -> [];
-ticket_5913(Config) ->
- ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
- ?IP_PORT, proplists:get_value(node, Config),
- "GET /cgi-bin/erl/httpd_example:get_bin "
- "HTTP/1.0\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/1.0"}]),
- ok.
-
-ticket_6003(doc) ->
- ["Tests that a URI with a bad hexadecimal code is handled"];
-ticket_6003(suite) -> [];
-ticket_6003(Config) ->
- ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
- ?IP_PORT, proplists:get_value(node, Config),
- "GET http://www.erlang.org/%skalle "
- "HTTP/1.0\r\n\r\n",
- [{statuscode, 400},
- {version, "HTTP/1.0"}]),
- ok.
-
-ticket_7304(doc) ->
- ["Tests missing CR in delimiter"];
-ticket_7304(suite) ->
- [];
-ticket_7304(Config) ->
- ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
- ?IP_PORT, proplists:get_value(node, Config),
- "GET / HTTP/1.0\r\n\n",
- [{statuscode, 200},
- {version, "HTTP/1.0"}]),
- ok.
-
-%%--------------------------------------------------------------------
-%% Internal functions
-%%--------------------------------------------------------------------
-dos_hostname(Type, Port, Host, Node, Max) ->
- H1 = {"", 200},
- H2 = {"dummy-host.ericsson.se", 200},
- TooLongHeader = lists:append(lists:duplicate(Max + 1, "a")),
- H3 = {TooLongHeader, 403},
- Hosts = [H1,H2,H3],
- dos_hostname_poll(Type, Host, Port, Node, Hosts).
-
-%% make_ipv6(T) when is_tuple(T) andalso (size(T) =:= 8) ->
-%% make_ipv6(tuple_to_list(T));
-
-%% make_ipv6([_, _, _, _, _, _, _, _] = IPV6) ->
-%% lists:flatten(io_lib:format("~s:~s:~s:~s:~s:~s:~s:~s", IPV6)).
-
-
-%%--------------------------------------------------------------------
-%% Other help functions
-create_config(Config, Access, FileName) ->
- ServerRoot = proplists:get_value(server_root, Config),
- TcTopDir = proplists:get_value(tc_top_dir, Config),
- Port = proplists:get_value(port, Config),
- Type = proplists:get_value(sock_type, Config),
- Host = proplists:get_value(host, Config),
- Mods = io_lib:format("~p", [httpd_mod]),
- Funcs = io_lib:format("~p", [ssl_password_cb]),
- MaxHdrSz = io_lib:format("~p", [256]),
- MaxHdrAct = io_lib:format("~p", [close]),
-
- io:format(user,
- "create_config -> "
- "~n ServerRoot: ~p"
- "~n TcTopDir: ~p"
- "~n Type: ~p"
- "~n Port: ~p"
- "~n Host: ~p"
- "~n", [ServerRoot, TcTopDir, Type, Port, Host]),
-
- SSL =
- if
- (Type =:= ssl) orelse
- (Type =:= essl) ->
- [cline(["SSLCertificateFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLCertificateKeyFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLCACertificateFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLPasswordCallbackModule ", Mods]),
- cline(["SSLPasswordCallbackFunction ", Funcs]),
- cline(["SSLVerifyClient 0"]),
- cline(["SSLVerifyDepth 1"])];
- true ->
- []
- end,
- ModOrder =
- case Access of
- mod_htaccess ->
- "Modules mod_alias mod_htaccess mod_auth "
- "mod_security "
- "mod_responsecontrol mod_trace mod_esi "
- "mod_actions mod_cgi mod_dir "
- "mod_range mod_get "
- "mod_head mod_log mod_disk_log";
- _ ->
- "Modules mod_alias mod_auth mod_security "
- "mod_responsecontrol mod_trace mod_esi "
- "mod_actions mod_cgi mod_dir "
- "mod_range mod_get "
- "mod_head mod_log mod_disk_log"
- end,
-
- %% The test suite currently does not handle an explicit BindAddress.
- %% They assume any has been used, that is Addr is always set to undefined!
-
- %% {ok, Hostname} = inet:gethostname(),
- %% {ok, Addr} = inet:getaddr(Hostname, inet6),
- %% AddrStr = make_ipv6(Addr),
- %% BindAddress = lists:flatten(io_lib:format("~s|inet6", [AddrStr])),
-
- BindAddress = "*|inet",
- %% BindAddress = "*",
-
- HttpConfig = [
- cline(["Port ", integer_to_list(Port)]),
- cline(["ServerName ", Host]),
- cline(["SocketType ", atom_to_list(Type)]),
- cline([ModOrder]),
- %% cline(["LogFormat ", "erlang"]),
- cline(["ServerAdmin [email protected]"]),
- cline(["BindAddress ", BindAddress]),
- cline(["ServerRoot ", ServerRoot]),
- cline(["ErrorLog ", TcTopDir,
- "/logs/error_log_", integer_to_list(Port)]),
- cline(["TransferLog ", TcTopDir,
- "/logs/access_log_", integer_to_list(Port)]),
- cline(["SecurityLog ", TcTopDir,
- "/logs/security_log_", integer_to_list(Port)]),
- cline(["ErrorDiskLog ", TcTopDir,
- "/logs/error_disk_log_", integer_to_list(Port)]),
- cline(["ErrorDiskLogSize ", "190000 ", "11"]),
- cline(["TransferDiskLog ", TcTopDir,
- "/logs/access_disk_log_", integer_to_list(Port)]),
- cline(["TransferDiskLogSize ", "200000 ", "10"]),
- cline(["SecurityDiskLog ", TcTopDir,
- "/logs/security_disk_log_", integer_to_list(Port)]),
- cline(["SecurityDiskLogSize ", "210000 ", "9"]),
- cline(["MaxClients 10"]),
- cline(["MaxHeaderSize ", MaxHdrSz]),
- cline(["MaxHeaderAction ", MaxHdrAct]),
- cline(["DocumentRoot ",
- filename:join(ServerRoot, "htdocs")]),
- cline(["DirectoryIndex ", "index.html ", "welcome.html"]),
- cline(["DefaultType ", "text/plain"]),
- SSL,
- mod_alias_config(ServerRoot),
-
- config_directory(filename:join([ServerRoot,"htdocs",
- "open"]),
- "Open Area",
- filename:join(ServerRoot, "auth/passwd"),
- filename:join(ServerRoot, "auth/group"),
- plain,
- "user one Aladdin",
- filename:join(ServerRoot, "security_data")),
- config_directory(filename:join([ServerRoot,"htdocs",
- "secret"]),
- "Secret Area",
- filename:join(ServerRoot, "auth/passwd"),
- filename:join(ServerRoot, "auth/group"),
- plain,
- "group group1 group2",
- filename:join(ServerRoot, "security_data")),
- config_directory(filename:join([ServerRoot,"htdocs",
- "secret",
- "top_secret"]),
- "Top Secret Area",
- filename:join(ServerRoot, "auth/passwd"),
- filename:join(ServerRoot, "auth/group"),
- plain,
- "group group3",
- filename:join(ServerRoot, "security_data")),
-
- config_directory(filename:join([ServerRoot,"htdocs",
- "dets_open"]),
- "Dets Open Area",
- filename:join(ServerRoot, "passwd"),
- filename:join(ServerRoot, "group"),
- dets,
- "user one Aladdin",
- filename:join(ServerRoot, "security_data")),
- config_directory(filename:join([ServerRoot,"htdocs",
- "dets_secret"]),
- "Dets Secret Area",
- filename:join(ServerRoot, "passwd"),
- filename:join(ServerRoot, "group"),
- dets,
- "group group1 group2",
- filename:join(ServerRoot, "security_data")),
- config_directory(filename:join([ServerRoot,"htdocs",
- "dets_secret",
- "top_secret"]),
- "Dets Top Secret Area",
- filename:join(ServerRoot, "passwd"),
- filename:join(ServerRoot, "group"),
- dets,
- "group group3",
- filename:join(ServerRoot, "security_data")),
-
- config_directory(filename:join([ServerRoot,"htdocs",
- "mnesia_open"]),
- "Mnesia Open Area",
- false,
- false,
- mnesia,
- "user one Aladdin",
- filename:join(ServerRoot, "security_data")),
- config_directory(filename:join([ServerRoot,"htdocs",
- "mnesia_secret"]),
- "Mnesia Secret Area",
- false,
- false,
- mnesia,
- "group group1 group2",
- filename:join(ServerRoot, "security_data")),
- config_directory(filename:join(
- [ServerRoot, "htdocs", "mnesia_secret",
- "top_secret"]),
- "Mnesia Top Secret Area",
- false,
- false,
- mnesia,
- "group group3",
- filename:join(ServerRoot, "security_data"))
- ],
- ConfigFile = filename:join([TcTopDir, FileName]),
- {ok, Fd} = file:open(ConfigFile, [write]),
- ok = file:write(Fd, lists:flatten(HttpConfig)),
- ok = file:close(Fd).
-
-config_directory(Dir, AuthName, AuthUserFile, AuthGroupFile, AuthDBType,
- Require, SF) ->
- file:delete(SF),
- [
- cline(["<Directory ", Dir, ">"]),
- cline(["SecurityDataFile ", SF]),
- cline(["SecurityMaxRetries 3"]),
- cline(["SecurityFailExpireTime ", integer_to_list(?FAIL_EXPIRE_TIME)]),
- cline(["SecurityBlockTime 1"]),
- cline(["SecurityAuthTimeout ", integer_to_list(?AUTH_TIMEOUT)]),
- cline(["SecurityCallbackModule ", "httpd_mod"]),
- cline_if_set("AuthUserFile", AuthUserFile),
- cline_if_set("AuthGroupFile", AuthGroupFile),
- cline_if_set("AuthName", AuthName),
- cline_if_set("AuthDBType", AuthDBType),
- cline(["require ", Require]),
- cline(["</Directory>\r\n"])
- ].
-
-mod_alias_config(Root) ->
- [
- cline(["Alias /icons/ ", filename:join(Root,"icons"), "/"]),
- cline(["Alias /pics/ ", filename:join(Root, "icons"), "/"]),
- cline(["ScriptAlias /cgi-bin/ ", filename:join(Root, "cgi-bin"), "/"]),
- cline(["ScriptAlias /htbin/ ", filename:join(Root, "cgi-bin"), "/"]),
- cline(["ErlScriptAlias /cgi-bin/erl httpd_example io"]),
- cline(["EvalScriptAlias /eval httpd_example io"])
- ].
-
-cline(List) ->
- lists:flatten([List, "\r\n"]).
-
-cline_if_set(_, false) ->
- [];
-cline_if_set(Name, Var) when is_list(Var) ->
- cline([Name, " ", Var]);
-cline_if_set(Name, Var) when is_atom(Var) ->
- cline([Name, " ", atom_to_list(Var)]).
-
-getaddr() ->
- {ok,HostName} = inet:gethostname(),
- {ok,{A1,A2,A3,A4}} = inet:getaddr(HostName,inet),
- lists:flatten(io_lib:format("~p.~p.~p.~p",[A1,A2,A3,A4])).
-
-start_mnesia(Node) ->
- case rpc:call(Node, ?MODULE, cleanup_mnesia, []) of
- ok ->
- ok;
- Other ->
- tsf({failed_to_cleanup_mnesia, Other})
- end,
- case rpc:call(Node, ?MODULE, setup_mnesia, []) of
- {atomic, ok} ->
- ok;
- Other2 ->
- tsf({failed_to_setup_mnesia, Other2})
- end,
- ok.
-
-setup_mnesia() ->
- setup_mnesia([node()]).
-
-setup_mnesia(Nodes) ->
- ok = mnesia:create_schema(Nodes),
- ok = mnesia:start(),
- {atomic, ok} = mnesia:create_table(httpd_user,
- [{attributes,
- record_info(fields, httpd_user)},
- {disc_copies,Nodes}, {type, set}]),
- {atomic, ok} = mnesia:create_table(httpd_group,
- [{attributes,
- record_info(fields,
- httpd_group)},
- {disc_copies,Nodes}, {type,bag}]).
-
-cleanup_mnesia() ->
- mnesia:start(),
- mnesia:delete_table(httpd_user),
- mnesia:delete_table(httpd_group),
- stopped = mnesia:stop(),
- mnesia:delete_schema([node()]),
- ok.
-
-create_htaccess_data(Path, IpAddress)->
- create_htaccess_dirs(Path),
-
- create_html_file(filename:join([Path,"ht/open/dummy.html"])),
- create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])),
- create_html_file(filename:join([Path,"ht/secret/dummy.html"])),
- create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
-
- create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]),
- Path, "user one Aladdin"),
- create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]),
- Path, "group group1 group2"),
- create_htaccess_file(filename:join([Path,
- "ht/secret/top_secret/.htaccess"]),
- Path, "user four"),
- create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]),
- Path, nouser, IpAddress),
-
- create_user_group_file(filename:join([Path,"ht","users.file"]),
- "one:OnePassword\ntwo:TwoPassword\nthree:"
- "ThreePassword\nfour:FourPassword\nAladdin:"
- "AladdinPassword"),
- create_user_group_file(filename:join([Path,"ht","groups.file"]),
- "group1: two one\ngroup2: two three").
-
-create_html_file(PathAndFileName)->
- file:write_file(PathAndFileName,list_to_binary(
- "<html><head><title>test</title></head>
- <body>testar</body></html>")).
-
-create_htaccess_file(PathAndFileName, BaseDir, RequireData)->
- file:write_file(PathAndFileName,
- list_to_binary(
- "AuthUserFile "++ BaseDir ++
- "/ht/users.file\nAuthGroupFile "++ BaseDir
- ++ "/ht/groups.file\nAuthName Test\nAuthType"
- " Basic\n<Limit>\nrequire " ++ RequireData ++
- "\n</Limit>")).
-
-create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
- file:write_file(PathAndFileName,list_to_binary(
- "AuthUserFile "++ BaseDir ++
- "/ht/users.file\nAuthGroupFile " ++
- BaseDir ++ "/ht/groups.file\nAuthName"
- " Test\nAuthType"
- " Basic\n<Limit GET>\n\tallow from " ++
- format_ip(IpAddress,
- string:rchr(IpAddress,$.)) ++
- "\n</Limit>")).
-
-create_user_group_file(PathAndFileName, Data)->
- file:write_file(PathAndFileName, list_to_binary(Data)).
-
-create_htaccess_dirs(Path)->
- ok = file:make_dir(filename:join([Path,"ht"])),
- ok = file:make_dir(filename:join([Path,"ht/open"])),
- ok = file:make_dir(filename:join([Path,"ht/blocknet"])),
- ok = file:make_dir(filename:join([Path,"ht/secret"])),
- ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])).
-
-remove_htaccess_dirs(Path)->
- file:del_dir(filename:join([Path,"ht/secret/top_secret"])),
- file:del_dir(filename:join([Path,"ht/secret"])),
- file:del_dir(filename:join([Path,"ht/blocknet"])),
- file:del_dir(filename:join([Path,"ht/open"])),
- file:del_dir(filename:join([Path,"ht"])).
-
-format_ip(IpAddress,Pos)when Pos > 0->
- case lists:nth(Pos,IpAddress) of
- $.->
- case lists:nth(Pos-2,IpAddress) of
- $.->
- format_ip(IpAddress,Pos-3);
- _->
- lists:sublist(IpAddress,Pos-2) ++ "."
- end;
- _ ->
- format_ip(IpAddress,Pos-1)
- end;
-
-format_ip(IpAddress, _Pos)->
- "1" ++ IpAddress.
-
-remove_htaccess(Path)->
- file:delete(filename:join([Path,"ht/open/dummy.html"])),
- file:delete(filename:join([Path,"ht/secret/dummy.html"])),
- file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
- file:delete(filename:join([Path,"ht/blocknet/dummy.html"])),
- file:delete(filename:join([Path,"ht/blocknet/.htaccess"])),
- file:delete(filename:join([Path,"ht/open/.htaccess"])),
- file:delete(filename:join([Path,"ht/secret/.htaccess"])),
- file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])),
- file:delete(filename:join([Path,"ht","users.file"])),
- file:delete(filename:join([Path,"ht","groups.file"])),
- remove_htaccess_dirs(Path).
-
-
-dos_hostname_poll(Type, Host, Port, Node, Hosts) ->
- [dos_hostname_poll1(Type, Host, Port, Node, Host1, Code)
- || {Host1,Code} <- Hosts].
-
-dos_hostname_poll1(Type, Host, Port, Node, Host1, Code) ->
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- dos_hostname_request(Host1),
- [{statuscode, Code},
- {version, "HTTP/1.0"}]).
-
-dos_hostname_request(Host) ->
- "GET / HTTP/1.0\r\n" ++ Host ++ "\r\n\r\n".
-
-get_nof_clients(Mode, Load) ->
- get_nof_clients(test_server:os_type(), Mode, Load).
-
-get_nof_clients(_, ip_comm, light) -> 5;
-get_nof_clients(_, ssl, light) -> 2;
-get_nof_clients(_, ip_comm, medium) -> 10;
-get_nof_clients(_, ssl, medium) -> 4;
-get_nof_clients(_, ip_comm, heavy) -> 20;
-get_nof_clients(_, ssl, heavy) -> 6.
-
-%% Make a file 100 bytes long containing 012...9*10
-create_range_data(Path) ->
- PathAndFileName=filename:join([Path,"range.txt"]),
- file:write_file(PathAndFileName,list_to_binary(["12345678901234567890",
- "12345678901234567890",
- "12345678901234567890",
- "12345678901234567890",
- "12345678901234567890"])).
-
-create_ipv6_config(Config, FileName, Ipv6Address) ->
- ServerRoot = proplists:get_value(server_root, Config),
- TcTopDir = proplists:get_value(tc_top_dir, Config),
- Port = proplists:get_value(port, Config),
- SockType = proplists:get_value(sock_type, Config),
- Mods = io_lib:format("~p", [httpd_mod]),
- Funcs = io_lib:format("~p", [ssl_password_cb]),
- Host = proplists:get_value(ipv6_host, Config),
-
- MaxHdrSz = io_lib:format("~p", [256]),
- MaxHdrAct = io_lib:format("~p", [close]),
-
- Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi"
- " mod_dir mod_get mod_head"
- " mod_log mod_disk_log mod_trace",
-
- SSL =
- if
- (SockType =:= ssl) orelse
- (SockType =:= essl) ->
- [cline(["SSLCertificateFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLCertificateKeyFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLCACertificateFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLPasswordCallbackModule ", Mods]),
- cline(["SSLPasswordCallbackFunction ", Funcs]),
- cline(["SSLVerifyClient 0"]),
- cline(["SSLVerifyDepth 1"])];
- true ->
- []
- end,
-
- BindAddress = "[" ++ Ipv6Address ++"]|inet6",
-
- HttpConfig =
- [cline(["BindAddress ", BindAddress]),
- cline(["Port ", integer_to_list(Port)]),
- cline(["ServerName ", Host]),
- cline(["SocketType ", atom_to_list(SockType)]),
- cline([Mod_order]),
- cline(["ServerRoot ", ServerRoot]),
- cline(["DocumentRoot ", filename:join(ServerRoot, "htdocs")]),
- cline(["MaxHeaderSize ",MaxHdrSz]),
- cline(["MaxHeaderAction ",MaxHdrAct]),
- cline(["DirectoryIndex ", "index.html "]),
- cline(["DefaultType ", "text/plain"]),
- SSL],
- ConfigFile = filename:join([TcTopDir,FileName]),
- {ok, Fd} = file:open(ConfigFile, [write]),
- ok = file:write(Fd, lists:flatten(HttpConfig)),
- ok = file:close(Fd).
-
-
-tsp(F) ->
- inets_test_lib:tsp("[~w]" ++ F, [?MODULE]).
-tsp(F, A) ->
- inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]).
-
-tsf(Reason) ->
- inets_test_lib:tsf(Reason).
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/Makefile.src b/lib/inets/test/old_httpd_SUITE_data/Makefile.src
deleted file mode 100644
index b0fdb43d8d..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/Makefile.src
+++ /dev/null
@@ -1,14 +0,0 @@
-CC = @CC@
-LD = @LD@
-CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
-CROSSLDFLAGS = @CROSSLDFLAGS@
-
-PROGS = cgi_echo@exe@
-
-all: $(PROGS)
-
-cgi_echo@exe@: cgi_echo@obj@
- $(LD) $(CROSSLDFLAGS) -o cgi_echo cgi_echo@obj@ @LIBS@
-
-cgi_echo@obj@: cgi_echo.c
- $(CC) -c -o cgi_echo@obj@ $(CFLAGS) cgi_echo.c
diff --git a/lib/inets/test/old_httpd_SUITE_data/cgi_echo.c b/lib/inets/test/old_httpd_SUITE_data/cgi_echo.c
deleted file mode 100644
index 580f860e96..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/cgi_echo.c
+++ /dev/null
@@ -1,97 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-
-#if defined __WIN32__
-#include <windows.h>
-#include <fcntl.h>
-#endif
-
-static int read_exact(char *buffer, int len);
-static int write_exact(char *buffer, int len);
-
-int main(void)
-{
- char msg[100];
- int msg_len;
-#ifdef __WIN32__
- _setmode(_fileno( stdin), _O_BINARY);
- _setmode(_fileno( stdout), _O_BINARY);
-#endif
- msg_len = read_exact(msg, 100);
-
- write_exact("Content-type: text/plain\r\n\r\n", 28);
- write_exact(msg, msg_len);
- exit(EXIT_SUCCESS);
-}
-
-
-/* read from stdin */
-#ifdef __WIN32__
-static int read_exact(char *buffer, int len)
-{
- HANDLE standard_input = GetStdHandle(STD_INPUT_HANDLE);
-
- unsigned read_result;
- unsigned sofar = 0;
-
- if (!len) { /* Happens for "empty packages */
- return 0;
- }
- for (;;) {
- if (!ReadFile(standard_input, buffer + sofar,
- len - sofar, &read_result, NULL)) {
- return -1; /* EOF */
- }
- if (!read_result) {
- return -2; /* Interrupted while reading? */
- }
- sofar += read_result;
- if (sofar == len) {
- return len;
- }
- }
-}
-#else
-static int read_exact(char *buffer, int len) {
- int i, got = 0;
-
- do {
- if ((i = read(0, buffer + got, len - got)) <= 0)
- return(i);
- got += i;
- } while (got < len);
- return len;
-
-}
-#endif
-
-/* write to stdout */
-#ifdef __WIN32__
- static int write_exact(char *buffer, int len)
- {
- HANDLE standard_output = GetStdHandle(STD_OUTPUT_HANDLE);
- unsigned written;
-
- if (!WriteFile(standard_output, buffer, len, &written, NULL)) {
- return -1; /* Broken Pipe */
- }
- if (written < ((unsigned) len)) {
- /* This should not happen, standard output is not blocking? */
- return -2;
- }
-
- return (int) written;
-}
-
-#else
- static int write_exact(char *buffer, int len) {
- int i, wrote = 0;
-
- do {
- if ((i = write(1, buffer + wrote, len - wrote)) <= 0)
- return i;
- wrote += i;
- } while (wrote < len);
- return len;
- }
-#endif
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/Makefile b/lib/inets/test/old_httpd_SUITE_data/server_root/Makefile
deleted file mode 100644
index ed4d63a3bb..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/Makefile
+++ /dev/null
@@ -1,210 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-#
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include ../../vsn.mk
-VSN=$(INETS_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN)
-
-# ----------------------------------------------------
-# Target Specs
-# ----------------------------------------------------
-MODULE=
-
-AUTH_FILES = auth/group \
- auth/passwd
-CGI_FILES = cgi-bin/printenv.sh
-CONF_FILES = conf/8080.conf \
- conf/8888.conf \
- conf/httpd.conf \
- conf/ssl.conf \
- conf/mime.types
-OPEN_FILES = htdocs/open/dummy.html
-MNESIA_OPEN_FILES = htdocs/mnesia_open/dummy.html
-MISC_FILES = htdocs/misc/friedrich.html \
- htdocs/misc/oech.html
-SECRET_FILES = htdocs/secret/dummy.html
-MNESIA_SECRET_FILES = htdocs/mnesia_secret/dummy.html
-HTDOCS_FILES = htdocs/index.html \
- htdocs/config.shtml \
- htdocs/echo.shtml \
- htdocs/exec.shtml \
- htdocs/flastmod.shtml \
- htdocs/fsize.shtml \
- htdocs/include.shtml
-ICON_FILES = icons/README \
- icons/a.gif \
- icons/alert.black.gif \
- icons/alert.red.gif \
- icons/apache_pb.gif \
- icons/back.gif \
- icons/ball.gray.gif \
- icons/ball.red.gif \
- icons/binary.gif \
- icons/binhex.gif \
- icons/blank.gif \
- icons/bomb.gif \
- icons/box1.gif \
- icons/box2.gif \
- icons/broken.gif \
- icons/burst.gif \
- icons/button1.gif \
- icons/button10.gif \
- icons/button2.gif \
- icons/button3.gif \
- icons/button4.gif \
- icons/button5.gif \
- icons/button6.gif \
- icons/button7.gif \
- icons/button8.gif \
- icons/button9.gif \
- icons/buttonl.gif \
- icons/buttonr.gif \
- icons/c.gif \
- icons/comp.blue.gif \
- icons/comp.gray.gif \
- icons/compressed.gif \
- icons/continued.gif \
- icons/dir.gif \
- icons/down.gif \
- icons/dvi.gif \
- icons/f.gif \
- icons/folder.gif \
- icons/folder.open.gif \
- icons/folder.sec.gif \
- icons/forward.gif \
- icons/generic.gif \
- icons/generic.red.gif \
- icons/generic.sec.gif \
- icons/hand.right.gif \
- icons/hand.up.gif \
- icons/htdig.gif \
- icons/icon.sheet.gif \
- icons/image1.gif \
- icons/image2.gif \
- icons/image3.gif \
- icons/index.gif \
- icons/layout.gif \
- icons/left.gif \
- icons/link.gif \
- icons/movie.gif \
- icons/p.gif \
- icons/patch.gif \
- icons/pdf.gif \
- icons/pie0.gif \
- icons/pie1.gif \
- icons/pie2.gif \
- icons/pie3.gif \
- icons/pie4.gif \
- icons/pie5.gif \
- icons/pie6.gif \
- icons/pie7.gif \
- icons/pie8.gif \
- icons/portal.gif \
- icons/poweredby.gif \
- icons/ps.gif \
- icons/quill.gif \
- icons/right.gif \
- icons/screw1.gif \
- icons/screw2.gif \
- icons/script.gif \
- icons/sound1.gif \
- icons/sound2.gif \
- icons/sphere1.gif \
- icons/sphere2.gif \
- icons/star.gif \
- icons/star_blank.gif \
- icons/tar.gif \
- icons/tex.gif \
- icons/text.gif \
- icons/transfer.gif \
- icons/unknown.gif \
- icons/up.gif \
- icons/uu.gif \
- icons/uuencoded.gif \
- icons/world1.gif \
- icons/world2.gif
-
-SSL_FILES = ssl/ssl_client.pem \
- ssl/ssl_server.pem
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-ERL_COMPILE_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-debug opt:
-
-clean:
-
-docs:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth
- $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin
- $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf
- $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open
- $(INSTALL_DATA) $(OPEN_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/open
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
- $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc
- $(INSTALL_DATA) $(MISC_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/misc
- $(INSTALL_DIR) \
- $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret
- $(INSTALL_DIR) \
- $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret
- $(INSTALL_DATA) $(SECRET_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/secret
- $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs
- $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons
- $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl
- $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs
-
-release_docs_spec:
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/auth/group b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/group
deleted file mode 100644
index b3da0ccbd3..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/auth/group
+++ /dev/null
@@ -1,3 +0,0 @@
-group1: one two
-group2: two three
-group3: three Aladdin
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd
deleted file mode 100644
index 8c980ff547..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd
+++ /dev/null
@@ -1,4 +0,0 @@
-one:onePassword
-two:twoPassword
-three:threePassword
-Aladdin:AladdinPassword
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat b/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat
deleted file mode 100644
index 25a49a1536..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-echo tomrad > c:\cygwin\tmp\hej
-echo Content-type: text/html
-echo.
-echo ^<HTML^> ^<HEAD^> ^<TITLE^>OS Environment^</TITLE^> ^</HEAD^> ^<BODY^>^<PRE^>
-set
-echo ^</PRE^>^</BODY^>^</HTML^>
-
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh b/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh
deleted file mode 100755
index de81de9bde..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-echo "Content-type: text/html"
-echo ""
-echo "<HTML> <HEAD> <TITLE>OS Environment</TITLE> </HEAD> <BODY><PRE>"
-env
-echo "</PRE></BODY></HTML>" \ No newline at end of file
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf
deleted file mode 100644
index 7b1b4a15b2..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf
+++ /dev/null
@@ -1,79 +0,0 @@
-Port 8080
-#ServerName your.server.net
-SocketType ip_comm
-Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
-ServerAdmin [email protected]
-ServerRoot /var/tmp/server_root
-ErrorLog logs/error_log_8080
-TransferLog logs/access_log_8080
-SecurityLog logs/security_log_8080
-ErrorDiskLog logs/error_disk_log_8080
-ErrorDiskLogSize 200000 10
-TransferDiskLog logs/access_disk_log_8080
-TransferDiskLogSize 200000 10
-SecurityDiskLog logs/security_disk_log
-SecurityDiskLogSize 200000 10
-MaxClients 50
-#KeepAlive 5
-#KeepAliveTimeout 10
-DocumentRoot /var/tmp/server_root/htdocs
-DirectoryIndex index.html welcome.html
-DefaultType text/plain
-Alias /icons/ /var/tmp/server_root/icons/
-Alias /pics/ /var/tmp/server_root/icons/
-ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/
-ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/
-ErlScriptAlias /cgi-bin/erl httpd_example io
-EvalScriptAlias /eval httpd_example io
-#Script HEAD /cgi-bin/printenv.sh
-#Action image/gif /cgi-bin/printenv.sh
-
-<Directory /var/tmp/server_root/htdocs/open>
-AuthDBType plain
-AuthName Open Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require user one Aladdin
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/secret>
-AuthDBType plain
-AuthName Secret Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require group group1 group2
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/secret/top_secret>
-AuthDBType plain
-AuthName Top Secret Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require group group3
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_open>
-AuthDBType mnesia
-AuthName Open Area
-require user one Aladdin
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_secret>
-AuthDBType mnesia
-AuthName Secret Area
-require group group1 group2
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret>
-AuthDBType mnesia
-AuthName Top Secret Area
-require group group3
-allow from 130.100.34 130.100.35
-deny from 100.234.22.12 194.100.34.1 130.100.34.25
-SecurityDataFile logs/security_data
-SecurityMaxRetries 3
-SecurityBlockTime 10
-SecurityFailExpireTime 1
-SecurityAuthTimeout 1
-SecurityCallbackModule security_callback
-</Directory>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf
deleted file mode 100644
index 042779fcd0..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf
+++ /dev/null
@@ -1,63 +0,0 @@
-Port 8888
-#ServerName your.server.net
-SocketType ip_comm
-Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
-ServerAdmin [email protected]
-ServerRoot /var/tmp/server_root
-ErrorLog logs/error_log_8888
-TransferLog logs/access_log_8888
-ErrorDiskLog logs/error_disk_log_8888
-ErrorDiskLogSize 200000 10
-TransferDiskLog logs/access_disk_log_8888
-TransferDiskLogSize 200000 10
-MaxClients 150
-DocumentRoot /var/tmp/server_root/htdocs
-DirectoryIndex index.html welcome.html
-DefaultType text/plain
-Alias /icons/ /var/tmp/server_root/icons/
-Alias /pics/ /var/tmp/server_root/icons/
-ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/
-ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/
-ErlScriptAlias /cgi-bin/erl httpd_example io
-EvalScriptAlias /eval httpd_example io
-#Script HEAD /cgi-bin/printenv.sh
-#Action image/gif /cgi-bin/printenv.sh
-
-<Directory /var/tmp/server_root/htdocs/open>
-AuthName Open Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require user one Aladdin
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/secret>
-AuthName Secret Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require group group1 group2
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/secret/top_secret>
-AuthName Top Secret Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require group group3
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_open>
-AuthName Open Area
-AuthMnesiaDB On
-require user one Aladdin
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_secret>
-AuthName Secret Area
-AuthMnesiaDB On
-require group group1 group2
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret>
-AuthName Top Secret Area
-AuthMnesiaDB On
-require group group3
-</Directory>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf
deleted file mode 100644
index 3add93cd73..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf
+++ /dev/null
@@ -1,269 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2017. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-#
-
-# Port: The port the standalone listens to. For ports < 1023, you will
-# need httpd to be run as root initially.
-
-Port 8888
-
-# BindAddress: This directive is used to tell the server which IP address
-# to listen to. It can either contain "*", an IP address, or a fully
-# qualified Internet domain name.
-#
-# It is also possible to specify the ip-family with the directive.
-# There ar three possible value: inet, inet6 and inet6fb4
-# inet: Use IpFamily inet when retreiving the address and
-# fail if that does not work.
-# inet6: Use IpFamily inet6 when retreiving the address and
-# fail if that does not work.
-# inet6fb4: First IpFamily inet6 is tried and if that does not work,
-# inet is used as fallback.
-# Default value for ip-family is inet6fb4
-#
-# The syntax is: <address>[|<ip-family>]
-#
-#BindAddress *
-#BindAddress *|inet
-
-
-# ServerName allows you to set a host name which is sent back to clients for
-# your server if it's different than the one the program would get (i.e. use
-# "www" instead of the host's real name).
-#
-# Note: You cannot just invent host names and hope they work. The name you
-# define here must be a valid DNS name for your host. If you don't understand
-# this, ask your network administrator.
-
-#ServerName your.server.net
-
-# SocketType is either ip_comm, sockets or ssl.
-
-SocketType ip_comm
-
-# Modules: Server run-time plug-in modules written using the Erlang
-# Web Server API (EWSAPI). The server API make it easy to add functionality
-# to the server. Read more about EWSAPI in the Reference Manual.
-# WARNING! Do not tamper with this directive unless you are familiar with
-# EWSAPI.
-
-Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_dir mod_get mod_log mod_disk_log
-
-# ServerAdmin: Your address, where problems with the server should be
-# e-mailed.
-
-ServerAdmin [email protected]
-
-# ServerRoot: The directory the server's config, error, and log files
-# are kept in
-
-ServerRoot /var/tmp/server_root
-
-# ErrorLog: The location of the error log file. If this does not start
-# with /, ServerRoot is prepended to it.
-
-ErrorLog logs/error_log
-
-# TransferLog: The location of the transfer log file. If this does not
-# start with /, ServerRoot is prepended to it.
-
-TransferLog logs/access_log
-
-# SecurityLog: The location of the security log file (mod_security required)
-#
-SecurityLog logs/security_log
-
-# ErrorDiskLog: The location of the error log file. If this does not
-# start with /, ServerRoot is prepended to it. This log file is managed
-# with the disk_log module [See disk_log(3)]. The ErrorDiskLogSize directive
-# takes two argument, i.e. MaxBytes and MaxFiles. The wrap log writes at most
-# MaxBytes bytes on each file, and it uses MaxFiles files before it wraps, and
-# truncates the first file.
-
-ErrorDiskLog logs/error_disk_log
-ErrorDiskLogSize 200000 10
-
-# TransferDiskLog: The location of the transfer log file. If this does not
-# start with /, ServerRoot is prepended to it. This log file is managed
-# with the disk_log module [See disk_log(3)]. The TransferDiskLogSize directive
-# takes two argument, i.e. MaxBytes and MaxFiles. The wrap log writes at most
-# MaxBytes bytes on each file, and it uses MaxFiles files before it wraps, and
-# truncates the first file.
-
-TransferDiskLog logs/access_disk_log
-TransferDiskLogSize 200000 10
-
-# SecurityDiskLog: The location of the security log file. If this does not
-# start with /, ServerRoot is prepended to it. This log file is managed
-# with the disk_log module [See disk_log(3)]. The SecurityDiskLogSize directive
-# takes two argument, i.e. MaxBytes and MaxFiles. The wrap log writes at most
-# MaxBytes bytes on each file, and it uses MaxFiles files before it wraps, and
-# truncates the first file.
-
-SecurityDiskLog logs/security_disk_log
-SecurityDiskLogSize 200000 10
-
-# Limit on total number of servers running, i.e., limit on the number
-# of clients who can simultaneously connect --- if this limit is ever
-# reached, clients will be LOCKED OUT, so it should NOT BE SET TOO LOW.
-# It is intended mainly as a brake to keep a runaway server from taking
-# the server with it as it spirals down...
-
-MaxClients 50
-
-# KeepAlive set the flag for persistent connections. For persistent connections
-# set KeepAlive to on. To use One request per connection set the flag to off
-# Note: The value has changed since previous version of INETS.
-KeepAlive on
-
-# KeepAliveTimeout sets the number of seconds before a persistent connection
-# times out and closes.
-KeepAliveTimeout 10
-
-# MaxKeepAliveRequests sets the number of seconds before a persistent connection
-# times out and closes.
-MaxKeepAliveRequests 10
-
-
-
-# DocumentRoot: The directory out of which you will serve your
-# documents. By default, all requests are taken from this directory, but
-# symbolic links and aliases may be used to point to other locations.
-
-DocumentRoot /var/tmp/server_root/htdocs
-
-# DirectoryIndex: Name of the file or files to use as a pre-written HTML
-# directory index. Separate multiple entries with spaces.
-
-DirectoryIndex index.html welcome.html
-
-# DefaultType is the default MIME type for documents which the server
-# cannot find the type of from filename extensions.
-
-DefaultType text/plain
-
-# Aliases: Add here as many aliases as you need (with no limit). The format is
-# Alias fakename realname
-
-Alias /icons/ /var/tmp/server_root/icons/
-Alias /pics/ /var/tmp/server_root/icons/
-
-# ScriptAlias: This controls which directories contain server scripts.
-# Format: ScriptAlias fakename realname
-
-ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/
-ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/
-
-# This directive adds an action, which will activate cgi-script when a
-# file is requested using the method of method, which can be one of
-# GET, POST and HEAD. It sends the URL and file path of the requested
-# document using the standard CGI PATH_INFO and PATH_TRANSLATED
-# environment variables.
-
-#Script HEAD /cgi-bin/printenv.sh
-
-# This directive adds an action, which will activate cgi-script when a
-# file of content type mime-type is requested. It sends the URL and
-# file path of the requested document using the standard CGI PATH_INFO
-# and PATH_TRANSLATED environment variables.
-
-#Action image/gif /cgi-bin/printenv.sh
-
-# ErlScriptAlias: This specifies how "Erl" server scripts are called.
-# Format: ErlScriptAlias fakename realname allowed_modules
-
-ErlScriptAlias /down/erl httpd_example io
-
-# EvalScriptAlias: This specifies how "Eval" server scripts are called.
-# Format: EvalScriptAlias fakename realname allowed_modules
-
-EvalScriptAlias /eval httpd_example io
-
-# Point SSLCertificateFile at a PEM encoded certificate.
-
-SSLCertificateFile /var/tmp/server_root/ssl/ssl_server.pem
-
-# If the key is not combined with the certificate, use this directive to
-# point at the key file.
-
-SSLCertificateKeyFile /var/tmp/server_root/ssl/ssl_server.pem
-
-# Set SSLVerifyClient to:
-# 0 if no certicate is required
-# 1 if the client may present a valid certificate
-# 2 if the client must present a valid certificate
-# 3 if the client may present a valid certificate but it is not required to
-# have a valid CA
-
-SSLVerifyClient 0
-
-# Each directory to which INETS has access, can be configured with respect
-# to which services and features are allowed and/or disabled in that
-# directory (and its subdirectories).
-
-<Directory /var/tmp/server_root/htdocs/open>
-AuthDBType plain
-AuthName Open Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require user one Aladdin
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/secret>
-AuthDBType plain
-AuthName Secret Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require group group1 group2
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/secret/top_secret>
-AuthDBType plain
-AuthName Top Secret Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require group group3
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_open>
-AuthDBType mnesia
-AuthName Open Area
-require user one Aladdin
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_secret>
-AuthDBType mnesia
-AuthName Secret Area
-require group group1 group2
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret>
-AuthDBType mnesia
-AuthName Top Secret Area
-require group group3
-allow from 130.100.34 130.100.35
-deny from 100.234.22.12 194.100.34.1 130.100.34.25
-SecurityDataFile logs/security_data
-SecurityMaxRetries 3
-SecurityBlockTime 10
-SecurityFailExpireTime 1
-SecurityAuthTimeout 1
-SecurityCallbackModule security_callback
-</Directory>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types
deleted file mode 100644
index d2f81e4e5e..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types
+++ /dev/null
@@ -1,465 +0,0 @@
-# This is a comment. I love comments.
-
-# MIME type Extension
-application/EDI-Consent
-application/EDI-X12
-application/EDIFACT
-application/activemessage
-application/andrew-inset ez
-application/applefile
-application/atomicmail
-application/batch-SMTP
-application/beep+xml
-application/cals-1840
-application/commonground
-application/cybercash
-application/dca-rft
-application/dec-dx
-application/dvcs
-application/eshop
-application/http
-application/hyperstudio
-application/iges
-application/index
-application/index.cmd
-application/index.obj
-application/index.response
-application/index.vnd
-application/iotp
-application/ipp
-application/isup
-application/font-tdpfr
-application/mac-binhex40 hqx
-application/mac-compactpro cpt
-application/macwriteii
-application/marc
-application/mathematica
-application/mathematica-old
-application/msword doc
-application/news-message-id
-application/news-transmission
-application/ocsp-request
-application/ocsp-response
-application/octet-stream bin dms lha lzh exe class so dll
-application/oda oda
-application/parityfec
-application/pdf pdf
-application/pgp-encrypted
-application/pgp-keys
-application/pgp-signature
-application/pkcs10
-application/pkcs7-mime
-application/pkcs7-signature
-application/pkix-cert
-application/pkix-crl
-application/pkixcmp
-application/postscript ai eps ps
-application/prs.alvestrand.titrax-sheet
-application/prs.cww
-application/prs.nprend
-application/qsig
-application/remote-printing
-application/riscos
-application/rtf
-application/sdp
-application/set-payment
-application/set-payment-initiation
-application/set-registration
-application/set-registration-initiation
-application/sgml
-application/sgml-open-catalog
-application/sieve
-application/slate
-application/smil smi smil
-application/timestamp-query
-application/timestamp-reply
-application/vemmi
-application/vnd.3M.Post-it-Notes
-application/vnd.FloGraphIt
-application/vnd.accpac.simply.aso
-application/vnd.accpac.simply.imp
-application/vnd.acucobol
-application/vnd.aether.imp
-application/vnd.anser-web-certificate-issue-initiation
-application/vnd.anser-web-funds-transfer-initiation
-application/vnd.audiograph
-application/vnd.businessobjects
-application/vnd.bmi
-application/vnd.canon-cpdl
-application/vnd.canon-lips
-application/vnd.claymore
-application/vnd.commerce-battelle
-application/vnd.commonspace
-application/vnd.comsocaller
-application/vnd.contact.cmsg
-application/vnd.cosmocaller
-application/vnd.cups-postscript
-application/vnd.cups-raster
-application/vnd.cups-raw
-application/vnd.ctc-posml
-application/vnd.cybank
-application/vnd.dna
-application/vnd.dpgraph
-application/vnd.dxr
-application/vnd.ecdis-update
-application/vnd.ecowin.chart
-application/vnd.ecowin.filerequest
-application/vnd.ecowin.fileupdate
-application/vnd.ecowin.series
-application/vnd.ecowin.seriesrequest
-application/vnd.ecowin.seriesupdate
-application/vnd.enliven
-application/vnd.epson.esf
-application/vnd.epson.msf
-application/vnd.epson.quickanime
-application/vnd.epson.salt
-application/vnd.epson.ssf
-application/vnd.ericsson.quickcall
-application/vnd.eudora.data
-application/vnd.fdf
-application/vnd.ffsns
-application/vnd.framemaker
-application/vnd.fsc.weblaunch
-application/vnd.fujitsu.oasys
-application/vnd.fujitsu.oasys2
-application/vnd.fujitsu.oasys3
-application/vnd.fujitsu.oasysgp
-application/vnd.fujitsu.oasysprs
-application/vnd.fujixerox.ddd
-application/vnd.fujixerox.docuworks
-application/vnd.fujixerox.docuworks.binder
-application/vnd.fut-misnet
-application/vnd.grafeq
-application/vnd.groove-account
-application/vnd.groove-identity-message
-application/vnd.groove-injector
-application/vnd.groove-tool-message
-application/vnd.groove-tool-template
-application/vnd.groove-vcard
-application/vnd.hhe.lesson-player
-application/vnd.hp-HPGL
-application/vnd.hp-PCL
-application/vnd.hp-PCLXL
-application/vnd.hp-hpid
-application/vnd.hp-hps
-application/vnd.httphone
-application/vnd.hzn-3d-crossword
-application/vnd.ibm.afplinedata
-application/vnd.ibm.MiniPay
-application/vnd.ibm.modcap
-application/vnd.informix-visionary
-application/vnd.intercon.formnet
-application/vnd.intertrust.digibox
-application/vnd.intertrust.nncp
-application/vnd.intu.qbo
-application/vnd.intu.qfx
-application/vnd.irepository.package+xml
-application/vnd.is-xpr
-application/vnd.japannet-directory-service
-application/vnd.japannet-jpnstore-wakeup
-application/vnd.japannet-payment-wakeup
-application/vnd.japannet-registration
-application/vnd.japannet-registration-wakeup
-application/vnd.japannet-setstore-wakeup
-application/vnd.japannet-verification
-application/vnd.japannet-verification-wakeup
-application/vnd.koan
-application/vnd.lotus-1-2-3
-application/vnd.lotus-approach
-application/vnd.lotus-freelance
-application/vnd.lotus-notes
-application/vnd.lotus-organizer
-application/vnd.lotus-screencam
-application/vnd.lotus-wordpro
-application/vnd.mcd
-application/vnd.mediastation.cdkey
-application/vnd.meridian-slingshot
-application/vnd.mif mif
-application/vnd.minisoft-hp3000-save
-application/vnd.mitsubishi.misty-guard.trustweb
-application/vnd.mobius.daf
-application/vnd.mobius.dis
-application/vnd.mobius.msl
-application/vnd.mobius.plc
-application/vnd.mobius.txf
-application/vnd.motorola.flexsuite
-application/vnd.motorola.flexsuite.adsi
-application/vnd.motorola.flexsuite.fis
-application/vnd.motorola.flexsuite.gotap
-application/vnd.motorola.flexsuite.kmr
-application/vnd.motorola.flexsuite.ttc
-application/vnd.motorola.flexsuite.wem
-application/vnd.mozilla.xul+xml
-application/vnd.ms-artgalry
-application/vnd.ms-asf
-application/vnd.ms-excel xls
-application/vnd.ms-lrm
-application/vnd.ms-powerpoint ppt
-application/vnd.ms-project
-application/vnd.ms-tnef
-application/vnd.ms-works
-application/vnd.mseq
-application/vnd.msign
-application/vnd.music-niff
-application/vnd.musician
-application/vnd.netfpx
-application/vnd.noblenet-directory
-application/vnd.noblenet-sealer
-application/vnd.noblenet-web
-application/vnd.novadigm.EDM
-application/vnd.novadigm.EDX
-application/vnd.novadigm.EXT
-application/vnd.osa.netdeploy
-application/vnd.palm
-application/vnd.pg.format
-application/vnd.pg.osasli
-application/vnd.powerbuilder6
-application/vnd.powerbuilder6-s
-application/vnd.powerbuilder7
-application/vnd.powerbuilder7-s
-application/vnd.powerbuilder75
-application/vnd.powerbuilder75-s
-application/vnd.previewsystems.box
-application/vnd.publishare-delta-tree
-application/vnd.pvi.ptid1
-application/vnd.pwg-xhtml-print+xml
-application/vnd.rapid
-application/vnd.s3sms
-application/vnd.seemail
-application/vnd.shana.informed.formdata
-application/vnd.shana.informed.formtemplate
-application/vnd.shana.informed.interchange
-application/vnd.shana.informed.package
-application/vnd.sss-cod
-application/vnd.sss-dtf
-application/vnd.sss-ntf
-application/vnd.street-stream
-application/vnd.svd
-application/vnd.swiftview-ics
-application/vnd.triscape.mxs
-application/vnd.trueapp
-application/vnd.truedoc
-application/vnd.tve-trigger
-application/vnd.ufdl
-application/vnd.uplanet.alert
-application/vnd.uplanet.alert-wbxml
-application/vnd.uplanet.bearer-choice-wbxml
-application/vnd.uplanet.bearer-choice
-application/vnd.uplanet.cacheop
-application/vnd.uplanet.cacheop-wbxml
-application/vnd.uplanet.channel
-application/vnd.uplanet.channel-wbxml
-application/vnd.uplanet.list
-application/vnd.uplanet.list-wbxml
-application/vnd.uplanet.listcmd
-application/vnd.uplanet.listcmd-wbxml
-application/vnd.uplanet.signal
-application/vnd.vcx
-application/vnd.vectorworks
-application/vnd.vidsoft.vidconference
-application/vnd.visio
-application/vnd.vividence.scriptfile
-application/vnd.wap.sic
-application/vnd.wap.slc
-application/vnd.wap.wbxml wbxml
-application/vnd.wap.wmlc wmlc
-application/vnd.wap.wmlscriptc wmlsc
-application/vnd.webturbo
-application/vnd.wrq-hp3000-labelled
-application/vnd.wt.stf
-application/vnd.xara
-application/vnd.xfdl
-application/vnd.yellowriver-custom-menu
-application/whoispp-query
-application/whoispp-response
-application/wita
-application/wordperfect5.1
-application/x-bcpio bcpio
-application/x-cdlink vcd
-application/x-chess-pgn pgn
-application/x-compress
-application/x-cpio cpio
-application/x-csh csh
-application/x-director dcr dir dxr
-application/x-dvi dvi
-application/x-futuresplash spl
-application/x-gtar gtar
-application/x-gzip
-application/x-hdf hdf
-application/x-javascript js
-application/x-koan skp skd skt skm
-application/x-latex latex
-application/x-netcdf nc cdf
-application/x-sh sh
-application/x-shar shar
-application/x-shockwave-flash swf
-application/x-stuffit sit
-application/x-sv4cpio sv4cpio
-application/x-sv4crc sv4crc
-application/x-tar tar
-application/x-tcl tcl
-application/x-tex tex
-application/x-texinfo texinfo texi
-application/x-troff t tr roff
-application/x-troff-man man
-application/x-troff-me me
-application/x-troff-ms ms
-application/x-ustar ustar
-application/x-wais-source src
-application/x400-bp
-application/xml
-application/xml-dtd
-application/xml-external-parsed-entity
-application/zip zip
-audio/32kadpcm
-audio/basic au snd
-audio/g.722.1
-audio/l16
-audio/midi mid midi kar
-audio/mp4a-latm
-audio/mpa-robust
-audio/mpeg mpga mp2 mp3
-audio/parityfec
-audio/prs.sid
-audio/telephone-event
-audio/tone
-audio/vnd.cisco.nse
-audio/vnd.cns.anp1
-audio/vnd.cns.inf1
-audio/vnd.digital-winds
-audio/vnd.everad.plj
-audio/vnd.lucent.voice
-audio/vnd.nortel.vbk
-audio/vnd.nuera.ecelp4800
-audio/vnd.nuera.ecelp7470
-audio/vnd.nuera.ecelp9600
-audio/vnd.octel.sbc
-audio/vnd.qcelp
-audio/vnd.rhetorex.32kadpcm
-audio/vnd.vmx.cvsd
-audio/x-aiff aif aiff aifc
-audio/x-mpegurl m3u
-audio/x-pn-realaudio ram rm
-audio/x-pn-realaudio-plugin rpm
-audio/x-realaudio ra
-audio/x-wav wav
-chemical/x-pdb pdb
-chemical/x-xyz xyz
-image/bmp bmp
-image/cgm
-image/g3fax
-image/gif gif
-image/ief ief
-image/jpeg jpeg jpg jpe
-image/naplps
-image/png png
-image/prs.btif
-image/prs.pti
-image/tiff tiff tif
-image/vnd.cns.inf2
-image/vnd.dwg
-image/vnd.dxf
-image/vnd.fastbidsheet
-image/vnd.fpx
-image/vnd.fst
-image/vnd.fujixerox.edmics-mmr
-image/vnd.fujixerox.edmics-rlc
-image/vnd.mix
-image/vnd.net-fpx
-image/vnd.svf
-image/vnd.wap.wbmp wbmp
-image/vnd.xiff
-image/x-cmu-raster ras
-image/x-portable-anymap pnm
-image/x-portable-bitmap pbm
-image/x-portable-graymap pgm
-image/x-portable-pixmap ppm
-image/x-rgb rgb
-image/x-xbitmap xbm
-image/x-xpixmap xpm
-image/x-xwindowdump xwd
-message/delivery-status
-message/disposition-notification
-message/external-body
-message/http
-message/news
-message/partial
-message/rfc822
-message/s-http
-model/iges igs iges
-model/mesh msh mesh silo
-model/vnd.dwf
-model/vnd.flatland.3dml
-model/vnd.gdl
-model/vnd.gs-gdl
-model/vnd.gtw
-model/vnd.mts
-model/vnd.vtu
-model/vrml wrl vrml
-multipart/alternative
-multipart/appledouble
-multipart/byteranges
-multipart/digest
-multipart/encrypted
-multipart/form-data
-multipart/header-set
-multipart/mixed
-multipart/parallel
-multipart/related
-multipart/report
-multipart/signed
-multipart/voice-message
-text/calendar
-text/css css
-text/directory
-text/enriched
-text/html html htm
-text/parityfec
-text/plain asc txt
-text/prs.lines.tag
-text/rfc822-headers
-text/richtext rtx
-text/rtf rtf
-text/sgml sgml sgm
-text/tab-separated-values tsv
-text/t140
-text/uri-list
-text/vnd.DMClientScript
-text/vnd.IPTC.NITF
-text/vnd.IPTC.NewsML
-text/vnd.abc
-text/vnd.curl
-text/vnd.flatland.3dml
-text/vnd.fly
-text/vnd.fmi.flexstor
-text/vnd.in3d.3dml
-text/vnd.in3d.spot
-text/vnd.latex-z
-text/vnd.motorola.reflex
-text/vnd.ms-mediapackage
-text/vnd.wap.si
-text/vnd.wap.sl
-text/vnd.wap.wml wml
-text/vnd.wap.wmlscript wmls
-text/x-setext etx
-text/x-server-parsed-html shtml
-text/xml xml xsl
-text/xml-external-parsed-entity
-video/mp4v-es
-video/mpeg mpeg mpg mpe
-video/parityfec
-video/pointer
-video/quicktime qt mov
-video/vnd.fvt
-video/vnd.motorola.video
-video/vnd.motorola.videop
-video/vnd.mpegurl mxu
-video/vnd.mts
-video/vnd.nokia.interleaved-multimedia
-video/vnd.vivo
-video/x-msvideo avi
-video/x-sgi-movie movie
-x-conference/x-cooltalk ice
-
-
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf
deleted file mode 100644
index de49ceafd0..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf
+++ /dev/null
@@ -1,66 +0,0 @@
-Port 8088
-#ServerName your.server.net
-SocketType ssl
-Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
-ServerAdmin [email protected]
-ServerRoot /var/tmp/server_root
-ErrorLog logs/error_log_8088
-TransferLog logs/access_log_8088
-ErrorDiskLog logs/error_disk_log_8088
-ErrorDiskLogSize 200000 10
-TransferDiskLog logs/access_disk_log_8088
-TransferDiskLogSize 200000 10
-MaxClients 150
-DocumentRoot /var/tmp/server_root/htdocs
-DirectoryIndex index.html welcome.html
-DefaultType text/plain
-Alias /icons/ /var/tmp/server_root/icons/
-Alias /pics/ /var/tmp/server_root/icons/
-ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/
-ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/
-ErlScriptAlias /cgi-bin/erl httpd_example io
-EvalScriptAlias /eval httpd_example io
-SSLCertificateFile /var/tmp/server_root/ssl/ssl_server.pem
-SSLCertificateKeyFile /var/tmp/server_root/ssl/ssl_server.pem
-SSLVerifyClient 0
-#Script HEAD /cgi-bin/printenv.sh
-#Action image/gif /cgi-bin/printenv.sh
-
-<Directory /var/tmp/server_root/htdocs/open>
-AuthName Open Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require user one Aladdin
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/secret>
-AuthName Secret Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require group group1 group2
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/secret/top_secret>
-AuthName Top Secret Area
-AuthUserFile /var/tmp/server_root/auth/passwd
-AuthGroupFile /var/tmp/server_root/auth/group
-require group group3
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_open>
-AuthName Open Area
-AuthMnesiaDB On
-require user one Aladdin
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_secret>
-AuthName Secret Area
-AuthMnesiaDB On
-require group group1 group2
-</Directory>
-
-<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret>
-AuthName Top Secret Area
-AuthMnesiaDB On
-require group group3
-</Directory>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml
deleted file mode 100644
index 107e3ff610..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml
+++ /dev/null
@@ -1,70 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/ssi.html (17-Apr-1997)</TITLE>
-</HEAD>
-<BODY>
-<H1>/ssi.html</H1>
-
-<!-- ************* CONFIG ************* -->
-
-<!--#config timefmt="%a %b %e %T %Z %Y" sizefmt="abbrev"-->
-<!--#config errmsg="[an especially ugly error occurred while processing this directive]"-->
-
-<!-- ************* INCLUDE ************* -->
-
-<P>Include /misc/friedrich.html:
-<!--#include virtual="/misc/friedrich.html"-->
-<P>Include /misc/not_defined.html: <!--#include virtual="/misc/not_defined.html"-->
-<P>Include misc/friedrich.html:
-<!--#include file="misc/friedrich.html"-->
-<P>Include not_defined.html: <!--#include file="not_defined.html"-->
-
-<P><HR>
-
-<!-- ************* ECHO ************* -->
-
-<P>DOCUMENT_NAME: <!--#echo var="DOCUMENT_NAME"-->
-<P>DOCUMENT_URI: <!--#echo var="DOCUMENT_URI"-->
-<P>QUERY_STRING_UNESCAPED: <!--#echo var="QUERY_STRING_UNESCAPED"-->
-<P>DATE_LOCAL: <!--#echo var="DATE_LOCAL"-->
-<P>DATE_GMT: <!--#echo var="DATE_GMT"-->
-<P>LAST_MODIFIED: <!--#echo var="LAST_MODIFIED"-->
-<P>NOT_DEFINED: <!--#echo var="NOT_DEFINED"-->
-
-<P><HR>
-
-<!-- ************* FSIZE ************* -->
-
-<P>Size of index.html: <!--#fsize file="index.html"-->
-<P>Size of not_defined.html: <!--#fsize file="not_defined.html"-->
-<!--#config sizefmt="bytes"-->
-<P>Size of /misc/friedrich.html: <!--#fsize virtual="/misc/friedrich.html"-->
-<P>Size of /misc/not_defined.html: <!--#fsize virtual="/misc/not_defined.html"-->
-
-<P><HR>
-
-<!-- ************* FLASTMOD ************* -->
-
-<P>Last modification of index.html: <!--#flastmod file="index.html"-->
-<P>Last modification of not_defined.html: <!--#flastmod file="not_defined.html"-->
-<P>Last modification of /misc/friedrich.html: <!--#flastmod virtual="/misc/friedrich.html"-->
-<P>Last modification of /misc/not_defined.html: <!--#flastmod virtual="/misc/not_defined.html"-->
-
-<!--#exec cmd="ls"-->
-<!--#exec cmd="printenv"-->
-<!--#exec cmd="sunemaja"-->
-
-<!--#exec cgi="/cgi-bin/printenv.sh"-->
-
-</BODY>
-</HTML>
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html
deleted file mode 100644
index a6e8a35a04..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/open/dummy.html (17-Apr-1997)</TITLE>
-<!-- Created by: Joakim Greben�, 17-Apr-1997 -->
-<!-- Changed by: Joakim Greben�, 17-Apr-1997 -->
-</HEAD>
-<BODY>
-<H1>/open/dummy.html</H1>
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html
deleted file mode 100644
index 016b04e540..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/secret/dummy.html (17-Apr-1997)</TITLE>
-<!-- Created by: Joakim Greben�, 17-Apr-1997 -->
-<!-- Changed by: Joakim Greben�, 17-Apr-1997 -->
-</HEAD>
-<BODY>
-<H1>/secret/dummy.html</H1>
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html
deleted file mode 100644
index 34db3d5d1a..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/secret/top_secret/index.html (04-Feb-1998)</TITLE>
-<!-- Created by: Mattias Nilsson, 04-Feb-1998 -->
-</HEAD>
-<BODY>
-<H1>/secret/top_secret/index.html</H1>
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml
deleted file mode 100644
index 141db5be59..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml
+++ /dev/null
@@ -1,35 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/echo.shtml</TITLE>
-</HEAD>
-<BODY>
-<H1>/echo.shtml</H1>
-
-<P>DOCUMENT_NAME: <!--#echo var="DOCUMENT_NAME"-->
-
-<P>DOCUMENT_URI: <!--#echo var="DOCUMENT_URI"-->
-
-<P>QUERY_STRING_UNESCAPED: <!--#echo var="QUERY_STRING_UNESCAPED"-->
-
-<P>DATE_LOCAL: <!--#echo var="DATE_LOCAL"-->
-
-<P>DATE_GMT: <!--#echo var="DATE_GMT"-->
-
-<P>LAST_MODIFIED: <!--#echo var="LAST_MODIFIED"-->
-
-<P>NOT_DEFINED: <!--#echo var="NOT_DEFINED"-->
-
-<P>[<A HREF="ssi.html">Back</A>]
-
-</BODY>
-</HTML>
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml
deleted file mode 100644
index 97333da898..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml
+++ /dev/null
@@ -1,30 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/exec.shtml</TITLE>
-</HEAD>
-<BODY>
-<H1>/exec.shtml</H1>
-<PRE>
-<!--#exec cmd="ls"-->
-<HR>
-<!--#exec cmd="printenv"-->
-<HR>
-<!--#exec cmd="sunemaja"-->
-<HR>
-<!--#exec cgi="/cgi-bin/printenv.sh"-->
-</PRE>
-
-<P>[<A HREF="ssi.html">Back</A>]
-
-</BODY>
-</HTML>
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml
deleted file mode 100644
index d54c36fe50..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml
+++ /dev/null
@@ -1,29 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/flastmod.shtml</TITLE>
-</HEAD>
-<BODY>
-<H1>/flastmod.shtml</H1>
-
-<P>Last modification of index.html: <!--#flastmod file="index.html"-->
-
-<P>Last modification of not_defined.html: <!--#flastmod file="not_defined.html"-->
-
-<P>Last modification of /misc/friedrich.html: <!--#flastmod virtual="/misc/friedrich.html"-->
-
-<P>Last modification of /misc/not_defined.html: <!--#flastmod virtual="/misc/not_defined.html"-->
-
-<P>[<A HREF="ssi.html">Back</A>]
-
-</BODY>
-</HTML>
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml
deleted file mode 100644
index 570ee9cf6d..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml
+++ /dev/null
@@ -1,29 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/fsize.shtml</TITLE>
-</HEAD>
-<BODY>
-<H1>/fsize.shtml</H1>
-
-<P>Size of index.html: <!--#fsize file="index.html"-->
-
-<P>Size of not_defined.html: <!--#fsize file="not_defined.html"-->
-
-<P>Size of /misc/friedrich.html: <!--#fsize virtual="/misc/friedrich.html"-->
-
-<P>Size of /misc/not_defined.html: <!--#fsize virtual="/misc/not_defined.html"-->
-
-<P>[<A HREF="ssi.html">Back</A>]
-
-</BODY>
-</HTML>
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml
deleted file mode 100644
index 529aad0437..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml
+++ /dev/null
@@ -1,33 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/include.shtml</TITLE>
-</HEAD>
-<BODY>
-<H1>/include.shtml</H1>
-
-<P>Include /misc/friedrich.html:
-<!--#include virtual="/misc/friedrich.html"-->
-
-<P>Include /misc/not_defined.html:
-<!--#include virtual="/misc/not_defined.html"-->
-
-<P>Include misc/friedrich.html:
-<!--#include file="misc/friedrich.html"-->
-
-<P>Include not_defined.html:
-<!--#include file="not_defined.html"-->
-
-<P>[<A HREF="ssi.html">Back</A>]
-
-</BODY>
-</HTML>
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html
deleted file mode 100644
index cfdc9f9ab7..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/index.html</TITLE>
-</HEAD>
-<BODY>
-<H1>/index.html</H1>
-
-<STRONG>Server-Side Include (SSI) commands:</STRONG><BR>
-<A HREF="config.shtml">config</A><BR>
-<A HREF="echo.shtml">echo</A><BR>
-<A HREF="exec.shtml">exec</A><BR>
-<A HREF="flastmod.shtml">flastmod</A><BR>
-<A HREF="fsize.shtml">fsize</A><BR>
-<A HREF="include.shtml">include</A><BR>
-
-<BR>
-<BR>
-
-<STRONG>ESI callback:</STRING><BR>
-<A HREF="cgi-bin/erl/httpd_example/get">cgi-bin/erl/httpd_example/get</A><BR>
-<A HREF="cgi-bin/erl/httpd_example/yahoo">cgi-bin/erl/httpd_example/yahoo</A><BR>
-<A HREF="cgi-bin/erl/httpd_example/test1">cgi-bin/erl/httpd_example/test1</A><BR>
-
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html
deleted file mode 100644
index 65c1790813..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/last_modified.html</TITLE>
-</HEAD>
-<BODY>
-<H1>/last_modified.html</H1>
-
-<P>This document is only used for test of illegal last-modified date.</P>
-
-
-</BODY>
-</HTML>
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html
deleted file mode 100644
index d7953d5df4..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<P><CITE>
-Talking much about oneself can also be a means to conceal oneself.<BR>
--- Friedrich Nietzsche
-</CITE>
-
-<P>Nested Include:
-<!--#include file="misc/oech.html"-->
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html
deleted file mode 100644
index 506064bf04..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<P><CITE>
-What excuses stand in your way? How can you eliminate them?<BR>
--- Roger von Oech
-</CITE>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html
deleted file mode 100644
index 8c17451f91..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html
+++ /dev/null
@@ -1 +0,0 @@
-<HTML></HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html
deleted file mode 100644
index a6e8a35a04..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/open/dummy.html (17-Apr-1997)</TITLE>
-<!-- Created by: Joakim Greben�, 17-Apr-1997 -->
-<!-- Changed by: Joakim Greben�, 17-Apr-1997 -->
-</HEAD>
-<BODY>
-<H1>/open/dummy.html</H1>
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html
deleted file mode 100644
index 016b04e540..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/secret/dummy.html (17-Apr-1997)</TITLE>
-<!-- Created by: Joakim Greben�, 17-Apr-1997 -->
-<!-- Changed by: Joakim Greben�, 17-Apr-1997 -->
-</HEAD>
-<BODY>
-<H1>/secret/dummy.html</H1>
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html
deleted file mode 100644
index 2d17e8b596..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/mnesia_secret/top_secret/index.html (04-Feb-1998)</TITLE>
-<!-- Created by: Mattias Nilsson, 04-Feb-1998 -->
-</HEAD>
-<BODY>
-<H1>/mnesia_secret/top_secret/index.html</H1>
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html
deleted file mode 100644
index a6e8a35a04..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/open/dummy.html (17-Apr-1997)</TITLE>
-<!-- Created by: Joakim Greben�, 17-Apr-1997 -->
-<!-- Changed by: Joakim Greben�, 17-Apr-1997 -->
-</HEAD>
-<BODY>
-<H1>/open/dummy.html</H1>
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html
deleted file mode 100644
index 016b04e540..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/secret/dummy.html (17-Apr-1997)</TITLE>
-<!-- Created by: Joakim Greben�, 17-Apr-1997 -->
-<!-- Changed by: Joakim Greben�, 17-Apr-1997 -->
-</HEAD>
-<BODY>
-<H1>/secret/dummy.html</H1>
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html
deleted file mode 100644
index 34db3d5d1a..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<HTML>
-<HEAD>
-<TITLE>/secret/top_secret/index.html (04-Feb-1998)</TITLE>
-<!-- Created by: Mattias Nilsson, 04-Feb-1998 -->
-</HEAD>
-<BODY>
-<H1>/secret/top_secret/index.html</H1>
-</BODY>
-</HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/README b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/README
deleted file mode 100644
index a1fc5a5a9c..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/README
+++ /dev/null
@@ -1,161 +0,0 @@
-Public Domain Icons
-
- These icons were originally made for Mosaic for X and have been
- included in the NCSA httpd and Apache server distributions in the
- past. They are in the public domain and may be freely included in any
- application. The originals were done by Kevin Hughes ([email protected]).
-
- Many thanks to Andy Polyakov for tuning the icon colors and adding a
- few new images. If you'd like to contribute additions or ideas to
- this set, please let me know.
-
- The distribution site for these icons is at:
-
- http://www.eit.com/goodies/www.icons/
-
- Kevin Hughes
- September 11, 1995
-
-
-Suggested Uses
-
-The following are a few suggestions, to serve as a starting point for ideas.
-Please feel free to tweak and rename the icons as you like.
-
- a.gif
- This might be used to represent PostScript or text layout
- languages.
-
- alert.black.gif, alert.red.gif
- These can be used to highlight any important items, such as a
- README file in a directory.
-
- back.gif, forward.gif
- These can be used as links to go to previous and next areas.
-
- ball.gray.gif, ball.red.gif
- These might be used as bullets.
-
- binary.gif
- This can be used to represent binary files.
-
- binhex.gif
- This can represent BinHex-encoded data.
-
- blank.gif
- This can be used as a placeholder or a spacing element.
-
- bomb.gif
- This can be used to repreesnt core files.
-
- box1.gif, box2.gif
- These icons can be used to represent generic 3D applications and
- related files.
-
- broken.gif
- This can represent corrupted data.
-
- burst.gif
- This can call attention to new and important items.
-
- c.gif
- This might represent C source code.
-
- comp.blue.gif, comp.red.gif
- These little computer icons can stand for telnet or FTP
- sessions.
-
- compressed.gif
- This may represent compressed data.
-
- continued.gif
- This can be a link to a continued listing of a directory.
-
- down.gif, up.gif, left.gif, right.gif
- These can be used to scroll up, down, left and right in a
- listing or may be used to denote items in an outline.
-
- dvi.gif
- This can represent DVI files.
-
- f.gif
- This might represent FORTRAN or Forth source code.
-
- folder.gif, folder.open.gif, folder.sec.gif
- The folder can represent directories. There is also a version
- that can represent secure directories or directories that cannot
- be viewed.
-
- generic.gif, generic.sec.gif, generic.red.gif
- These can represent generic files, secure files, and important
- files, respectively.
-
- hand.right.gif, hand.up.gif
- These can point out important items (pun intended).
-
- image1.gif, image2.gif, image3.gif
- These can represent image formats of various types.
-
- index.gif
- This might represent a WAIS index or search facility.
-
- layout.gif
- This might represent files and formats that contain graphics as
- well as text layout, such as HTML and PDF files.
-
- link.gif
- This might represent files that are symbolic links.
-
- movie.gif
- This can represent various movie formats.
-
- p.gif
- This may stand for Perl or Python source code.
-
- pie0.gif ... pie8.gif
- These icons can be used in applications where a list of
- documents is returned from a search. The little pie chart images
- can denote how relevant the documents may be to your search
- query.
-
- patch.gif
- This may stand for patches and diff files.
-
- portal.gif
- This might be a link to an online service or a 3D world.
-
- ps.gif, quill.gif
- These may represent PostScript files.
-
- screw1.gif, screw2.gif
- These may represent CAD or engineering data and formats.
-
- script.gif
- This can represent any of various interpreted languages, such as
- Perl, python, TCL, and shell scripts, as well as server
- configuration files.
-
- sound1.gif, sound2.gif
- These can represent sound files.
-
- sphere1.gif, sphere2.gif
- These can represent 3D worlds or rendering applications and
- formats.
-
- tex.gif
- This can represent TeX files.
-
- text.gif
- This can represent generic (plain) text files.
-
- transfer.gif
- This can represent FTP transfers or uploads/downloads.
-
- unknown.gif
- This may represent a file of an unknown type.
-
- uuencoded.gif
- This can stand for uuencoded data.
-
- world1.gif, world2.gif
- These can represent 3D worlds or other 3D formats.
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif
deleted file mode 100644
index bb23d971f4..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif
deleted file mode 100644
index eaecd2172a..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif
deleted file mode 100644
index a423894043..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif
deleted file mode 100644
index 3a1c139fc4..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif
deleted file mode 100644
index a694ae1ec3..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif
deleted file mode 100644
index eb84268c4c..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif
deleted file mode 100644
index a8425cb574..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif
deleted file mode 100644
index 9a15cbae04..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif
deleted file mode 100644
index 62d0363108..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif
deleted file mode 100644
index 0ccf01e198..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif
deleted file mode 100644
index 270fdb1c06..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif
deleted file mode 100644
index 65dcd002ea..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif
deleted file mode 100644
index c43bc4faec..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif
deleted file mode 100644
index 9f8cbe9f76..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif
deleted file mode 100644
index fbdcf575f7..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif
deleted file mode 100644
index eb97cb7333..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif
deleted file mode 100644
index fe0c97998c..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif
deleted file mode 100644
index 7698455bf9..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif
deleted file mode 100644
index a8b8319232..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif
deleted file mode 100644
index 0fd15a0d7f..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif
deleted file mode 100644
index 64241e5c5d..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif
deleted file mode 100644
index 867cfd1212..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif
deleted file mode 100644
index b3f5fb248f..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif
deleted file mode 100644
index 7a308be8f6..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif
deleted file mode 100644
index 9acba576c0..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif
deleted file mode 100644
index 3883088e7a..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif
deleted file mode 100644
index c4dc3887db..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif
deleted file mode 100644
index 7555b6c164..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif
deleted file mode 100644
index f8d76a8c23..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif
deleted file mode 100644
index 7664cd0364..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif
deleted file mode 100644
index 39e732739f..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif
deleted file mode 100644
index b0ffb7e0cc..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif
deleted file mode 100644
index 48264601ae..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif
deleted file mode 100644
index a354c871cd..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif
deleted file mode 100644
index 791be33105..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif
deleted file mode 100644
index fbe353c282..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif
deleted file mode 100644
index 48264601ae..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif
deleted file mode 100644
index 30979cb528..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif
deleted file mode 100644
index 75332d9e59..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif
deleted file mode 100644
index b2959b4c85..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif
deleted file mode 100644
index de60b2940f..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif
deleted file mode 100644
index 94743981d9..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif
deleted file mode 100644
index 88d5240c3c..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif
deleted file mode 100644
index 5cdbc7206d..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif
deleted file mode 100644
index 85a5d68317..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif
deleted file mode 100644
index 35443fb63a..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif
deleted file mode 100644
index ad1686e448..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif
deleted file mode 100644
index 01e442bfa9..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif
deleted file mode 100644
index 751faeea36..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif
deleted file mode 100644
index 4f30484ff6..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif
deleted file mode 100644
index 162478fb3a..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif
deleted file mode 100644
index c96338a152..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif
deleted file mode 100644
index 279e6710d4..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif
deleted file mode 100644
index c5b6889a76..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif
deleted file mode 100644
index 0035183774..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif
deleted file mode 100644
index 7b917b4e91..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif
deleted file mode 100644
index 39bc90e795..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif
deleted file mode 100644
index c88fd777c4..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif
deleted file mode 100644
index 6f7a0ae7a7..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif
deleted file mode 100644
index 03aa6be71e..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif
deleted file mode 100644
index b04c5e0908..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif
deleted file mode 100644
index 4db9d023ed..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif
deleted file mode 100644
index 93471fdd88..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif
deleted file mode 100644
index 57aee93f07..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif
deleted file mode 100644
index 0dc327b569..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif
deleted file mode 100644
index 8661337f06..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif
deleted file mode 100644
index 59ddb34ce0..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif
deleted file mode 100644
index 0e6e506e00..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif
deleted file mode 100644
index d324ab80ea..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif
deleted file mode 100644
index 0f565bc1db..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif
deleted file mode 100644
index 818a5cdc7e..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif
deleted file mode 100644
index b256e5f75f..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif
deleted file mode 100644
index af6ba2b097..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif
deleted file mode 100644
index 06dccb3e44..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif
deleted file mode 100644
index d8a853bc58..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif
deleted file mode 100644
index 8efb49f55d..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif
deleted file mode 100644
index 48e6a7fb2f..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif
deleted file mode 100644
index 7067070da2..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif
deleted file mode 100644
index a9e462a377..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif
deleted file mode 100644
index 4cfe0a5e0f..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif
deleted file mode 100644
index a0c83cb85b..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif
deleted file mode 100644
index 617e779efa..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif
deleted file mode 100644
index 45e43233b8..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif
deleted file mode 100644
index 4c623909fb..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif
deleted file mode 100644
index 33697dbb66..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif
deleted file mode 100644
index 32b1ea23fb..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif
deleted file mode 100644
index 6d6d6d1ebf..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif
deleted file mode 100644
index 4387d529f6..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif
deleted file mode 100644
index 4387d529f6..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif
deleted file mode 100644
index 05b4ec2058..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gif
deleted file mode 100644
index e3203f7a88..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip b/lib/inets/test/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip
deleted file mode 100644
index 8d1c8b69c3..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem
deleted file mode 100644
index 427447958d..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n
-0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr
-3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB
-AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB
-I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna
-QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f
-eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws
-/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI
-ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV
-LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj
-40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6
-UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80
-t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J
------END RSA PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT
-BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE
-BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD
-VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw
-MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl
-cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD
-VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB
-AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV
-wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53
-h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID
-AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A
-PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD
-4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ
-S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5
------END CERTIFICATE-----
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem
deleted file mode 100644
index 4aac86db49..0000000000
--- a/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6
-7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt
-NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB
-AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm
-FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo
-T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd
-VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb
-Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7
-5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX
-BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm
-zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa
-GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z
-h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV
------END RSA PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT
-BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE
-BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD
-VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw
-MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl
-cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD
-VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB
-AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9
-Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ
-4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID
-AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk
-QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L
-psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg
-/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M
------END CERTIFICATE-----
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 560d524bac..dccdbfa94a 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.4.4
+INETS_VSN = 6.4.6
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index 508c8e01b5..37de0a35c5 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
+# Copyright Ericsson AB 2000-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -43,25 +43,17 @@ XML_APP_FILES = ref_man.xml
XML_REF3_FILES = jinterface.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml \
- part_notes_history.xml
-
+ part.xml
XML_CHAPTER_FILES = \
notes.xml \
- notes_history.xml \
jinterface_users_guide.xml
BOOK_FILES = book.xml
-XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-
-GIF_FILES = \
- notes.gif \
- ref_man.gif \
- user_guide.gif
+XML_FILES = $(BOOK_FILES) $(XML_APP_FILES) $(XML_REF3_FILES) \
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+GIF_FILES =
#------------------------------------------------------
@@ -92,7 +84,7 @@ JAVA_DOC_FILES = \
serialized-form.html \
package-list \
stylesheet.css \
- help-doc.html
+ help-doc.html
INFO_FILE = ../../info
JAVA_EXTRA_FILES = $(JAVA_DOC_FILES:%=$(HTMLDIR)/java/%)
@@ -110,17 +102,16 @@ JAVA_GEN_FILES = \
HTML_FILES = \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-TOP_HTML_FILES = $(INDEX_TARGET)
+TOP_HTML_FILES = $(INDEX_TARGET)
INDEX_FILE = index.html
-INDEX_SRC = $(INDEX_FILE).src
INDEX_TARGET = $(DOCDIR)/$(INDEX_FILE)
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -146,18 +137,15 @@ jdoc:$(JAVA_SRC_FILES)
(cd ../../java_src;$(JAVADOC) -sourcepath . -d $(JAVADOC_DEST) \
-windowtitle $(JAVADOC_TITLE) $(JAVADOC_PKGS))
-man:
+man:
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-#$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk
-# sed -e 's;%VSN%;$(VSN);' $< > $@
-
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/jinterface/doc/src/fascicules.xml b/lib/jinterface/doc/src/fascicules.xml
deleted file mode 100644
index 4f04be0515..0000000000
--- a/lib/jinterface/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="yes">
- User Guide
- </fascicule>
- <fascicule file="" href="java/index.html" entry="no">
- Java API
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/jinterface/doc/src/index.html.src b/lib/jinterface/doc/src/index.html.src
deleted file mode 100644
index 9276c5f89a..0000000000
--- a/lib/jinterface/doc/src/index.html.src
+++ /dev/null
@@ -1,99 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<!-- This file is obsolete -->
-<HTML>
-<!--
- %CopyrightBegin%
-
- Copyright Ericsson AB 2000-2016. All Rights Reserved.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- %CopyrightEnd%
--->
-<HEAD>
-<TITLE>Jinterface %VSN%</TITLE>
-</HEAD>
-
-<BODY BGCOLOR="#FFFFFF">
-
-<CENTER>
-<A HREF="http://www.erlang.se/"><IMG ALT="Erlang/OTP" BORDER=0 SRC="html/min_head.gif"></A><BR>
-
-<FONT SIZE="-1">
-[<A HREF="../../../doc/index.html">Up</A> |
-<A HREF="http://www.erlang.se/">Erlang/OTP</A>]
-</FONT><BR>
-
-<P><FONT SIZE="+3">Jinterface </FONT><BR>
-Version %VSN%
-</CENTER>
-
-<P><TABLE>
-<TR>
-<TD>
-</TD>
-
-<TD>
-<STRONG>Jinterface</STRONG> contains functions,
-which help you integrate programs written in Java and Erlang.
-</TD>
-</TR>
-</TABLE>
-
-<P><CENTER>
-<TABLE CELLPADDING=15>
-<TR>
-
-
-<TD ALIGN=CENTER>
-<A HREF="html/part_frame.html">
-<IMG ALT="User Guide" BORDER=0 SRC="html/user_guide.gif"></A><BR>
-<FONT SIZE="-1">
-<A HREF="html/part_frame.html">User Guide</A>
-</FONT>
-</TD>
-
-
-<TD ALIGN=CENTER>
-<A HREF="html/index.html"><IMG ALT="Java API" BORDER=0 SRC="html/ref_man.gif"></A><BR>
-<FONT SIZE="-1">
-<A HREF="html/index.html">Java API</A>
-</FONT>
-</TD>
-
-
-
-<TD ALIGN=CENTER>
-<A HREF="html/part_notes_frame.html"><IMG ALT="Release Notes" BORDER=0 SRC="html/notes.gif"></A><BR>
-<FONT SIZE="-1">
-<A HREF="html/part_notes_frame.html">Release Notes</A>
-</FONT>
-</TD>
-
-</TR>
-</TABLE>
-</CENTER>
-
-<P><CENTER>
-<HR>
-<FONT SIZE="-1">
-Copyright &copy; 1991-2001
-<A HREF="http://www.erlang.se/">Ericsson Utvecklings AB</A>
-</FONT>
-</CENTER>
-</BODY>
-</HTML>
-
-
-
-
diff --git a/lib/jinterface/doc/src/notes.gif b/lib/jinterface/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/jinterface/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index b44a04d7cd..346d467c2d 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.8.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.8</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/jinterface/doc/src/part_notes.xml b/lib/jinterface/doc/src/part_notes.xml
deleted file mode 100644
index de6dd7d162..0000000000
--- a/lib/jinterface/doc/src/part_notes.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2000</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Jinterface Release Notes</title>
- <prepared>Gordon Beaton</prepared>
- <docno></docno>
- <date>1999-10-25</date>
- <rev>A</rev>
- </header>
- <description>
- <p>The <em>Jinterface</em> application is a Java-Erlang
- communication tool package.</p>
- <p>For information about older versions, see
- <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/jinterface/doc/src/part_notes_history.xml b/lib/jinterface/doc/src/part_notes_history.xml
deleted file mode 100644
index 94bb996db5..0000000000
--- a/lib/jinterface/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2006</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Jinterface Release Notes History</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>Jinterface</em> application is a Java-Erlang
- communication tool package.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/jinterface/doc/src/ref_man.gif b/lib/jinterface/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/jinterface/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/jinterface/doc/src/summary.html.src b/lib/jinterface/doc/src/summary.html.src
deleted file mode 100644
index beb3305d58..0000000000
--- a/lib/jinterface/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-Low level interface to Java. \ No newline at end of file
diff --git a/lib/jinterface/doc/src/user_guide.gif b/lib/jinterface/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/jinterface/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
index e55cfa62ea..001acfdd2e 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
@@ -130,7 +130,6 @@ release_spec: opt
release_docs_spec:
-
-
+xmllint:
# ----------------------------------------------------
diff --git a/lib/jinterface/test/jinterface_SUITE.erl b/lib/jinterface/test/jinterface_SUITE.erl
index 73851f47e0..8c6a6368a9 100644
--- a/lib/jinterface/test/jinterface_SUITE.erl
+++ b/lib/jinterface/test/jinterface_SUITE.erl
@@ -176,11 +176,29 @@ init_per_suite(Config) when is_list(Config) ->
{error,bad_name} -> false;
P -> filelib:is_dir(P) end of
true ->
- jitu:init_all(Config);
+ case hostname_resolves() of
+ true ->
+ jitu:init_all(Config);
+ Skip ->
+ Skip
+ end;
false ->
{skip,"No jinterface application"}
end.
+%% Check if inet:gethostname() can be resolved by
+%% the native resolver. If it can, we know that
+%% jinterface name resolution works. If it cannot
+%% jinterface tests will fail.
+hostname_resolves() ->
+ {ok, HN} = inet:gethostname(),
+ case inet_gethost_native:gethostbyname(HN) of
+ {ok, _} ->
+ true;
+ _ ->
+ {skip, "Cannot resolve short hostname, add " ++ HN ++ " to /etc/hosts"}
+ end.
+
end_per_suite(Config) when is_list(Config) ->
jitu:finish_all(Config).
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index 373e2dab22..0a8a1190ec 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.8
+JINTERFACE_VSN = 1.8.1
diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile
index 8976a3b800..0759f362d4 100644
--- a/lib/kernel/doc/src/Makefile
+++ b/lib/kernel/doc/src/Makefile
@@ -70,8 +70,8 @@ XML_REF4_FILES = app.xml config.xml
XML_REF6_FILES = kernel_app.xml
-XML_PART_FILES = part_notes.xml part_notes_history.xml
-XML_CHAPTER_FILES = notes.xml notes_history.xml
+XML_PART_FILES =
+XML_CHAPTER_FILES = notes.xml
BOOK_FILES = book.xml
@@ -100,9 +100,9 @@ SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
TOP_SPECS_FILE = specs.xml
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
SPECS_ESRC = ../../src
@@ -125,7 +125,7 @@ html: gifs $(HTML_REF_MAN_FILE)
man: $(MAN3_FILES) $(MAN4_FILES) $(MAN6_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
@@ -134,7 +134,7 @@ clean clean_docs:
rm -f $(MAN6DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f $(SPECDIR)/*
- rm -f errs core *~
+ rm -f errs core *~
$(SPECDIR)/specs_erl_prim_loader_stub.xml:
escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
@@ -151,7 +151,7 @@ $(SPECDIR)/specs_zlib_stub.xml:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -169,4 +169,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
release_spec:
-
diff --git a/lib/kernel/doc/src/fascicules.xml b/lib/kernel/doc/src/fascicules.xml
deleted file mode 100644
index fadd37eefb..0000000000
--- a/lib/kernel/doc/src/fascicules.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index 070782e1f3..e6104b0c76 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -51,6 +51,7 @@ server() ->
{ok, Sock} = gen_tcp:accept(LSock),
{ok, Bin} = do_recv(Sock, []),
ok = gen_tcp:close(Sock),
+ ok = gen_tcp:close(LSock),
Bin.
do_recv(Sock, Bs) ->
@@ -309,9 +310,9 @@ do_recv(Sock, Bs) ->
<seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>.
</p></item>
</taglist>
- <p>The returned socket <c><anno>ListenSocket</anno></c> can only be
- used in calls to
- <seealso marker="#accept/1"><c>accept/1,2</c></seealso>.</p>
+ <p>The returned socket <c><anno>ListenSocket</anno></c> should be used
+ in calls to <seealso marker="#accept/1"><c>accept/1,2</c></seealso> to
+ accept incoming connection requests.</p>
<note>
<p>The default values for options specified to <c>listen</c> can
be affected by the Kernel configuration parameter
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index e5ac031539..0762cebc94 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>1996</year><year>2017</year>
+ <year>1996</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -469,8 +469,12 @@ MaxT = TickTime + TickTime / 4</code>
<item><c>ObjSuffix = string()</c></item>
<item><c>SrcSuffix = string()</c></item>
</list>
- <p>Specifies a list of rules for use by <c>filelib:find_file/2</c> and
- <c>filelib:find_source/2</c>. If this is set to some other value
+ <p>Specifies a list of rules for use by
+ <seealso marker="stdlib:filelib#find_file/2">
+ <c>filelib:find_file/2</c></seealso>
+ <seealso marker="stdlib:filelib#find_source/2">
+ <c>filelib:find_source/2</c></seealso>
+ If this is set to some other value
than the empty list, it replaces the default rules. Rules can be
simple pairs of directory suffixes, such as <c>{"ebin",
"src"}</c>, which are used by <c>filelib:find_file/2</c>, or
@@ -478,6 +482,16 @@ MaxT = TickTime + TickTime / 4</code>
file name extensions, for example <c>[{".beam", ".erl", [{"ebin",
"src"}]}</c>, which are used by <c>filelib:find_source/2</c>. Both
kinds of rules can be mixed in the list.</p>
+ <p>The interpretation of <c>ObjDirSuffix</c> and <c>SrcDirSuffix</c>
+ is as follows: if the end of the directory name where an
+ object is located matches <c>ObjDirSuffix</c>, then the
+ name created by replacing <c>ObjDirSuffix</c> with
+ <c>SrcDirSuffix</c> is expanded by calling
+ <seealso marker="stdlib:filelib#wildcard/1">
+ <c>filelib:wildcard/1</c></seealso>, and the first regular
+ file found among the matches is the source file.
+ </p>
+
</item>
</taglist>
</section>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index a5316dd476..65fe9b9c07 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,41 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add <c>os:cmd/2</c> that takes an options map as the
+ second argument.</p>
+ <p>
+ Add <c>max_size</c> as an option to <c>os:cmd/2</c> that
+ control the maximum size of the result that
+ <c>os:cmd/2</c> will return.</p>
+ <p>
+ Own Id: OTP-14823</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Refactored an internal API.</p>
+ <p>
+ Own Id: OTP-14784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 5.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml
index 0e9add4161..7ce2f54542 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -38,17 +38,35 @@
most platforms.</p>
</description>
+ <datatypes>
+ <datatype>
+ <name name="os_command"/>
+ </datatype>
+ <datatype>
+ <name name="os_command_opts"/>
+ <desc>
+ <p>Options for <seealso marker="#cmd/2"><c>os:cmd/2</c></seealso></p>
+ <taglist>
+ <tag><c>max_size</c></tag>
+ <item>
+ <p>The maximum size of the data returned by the <c>os:cmd</c> call.
+ See the <seealso marker="#cmd/2"><c>os:cmd/2</c></seealso>
+ documentation for more details.</p>
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
+ </datatypes>
+
<funcs>
<func>
<name name="cmd" arity="1"/>
+ <name name="cmd" arity="2"/>
<fsummary>Execute a command in a shell of the target OS.</fsummary>
<desc>
<p>Executes <c><anno>Command</anno></c> in a command shell of the
- target OS,
- captures the standard output of the command, and returns this
- result as a string. This function is a replacement of
- the previous function <c>unix:cmd/1</c>; they are equivalent on a
- Unix platform.</p>
+ target OS, captures the standard output of the command,
+ and returns this result as a string.</p>
<p><em>Examples:</em></p>
<code type="none">
LsOut = os:cmd("ls"), % on unix platform
@@ -57,6 +75,21 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
called from another program (for example, <c>os:cmd/1</c>)
can differ, compared with the standard output of the command
when called directly from an OS command shell.</p>
+ <p><c>os:cmd/2</c> was added in kernel-5.5 (OTP-20.2.1). It makes it
+ possible to pass an options map as the second argument in order to
+ control the behaviour of <c>os:cmd</c>. The possible options are:
+ </p>
+ <taglist>
+ <tag><c>max_size</c></tag>
+ <item>
+ <p>The maximum size of the data returned by the <c>os:cmd</c> call.
+ This option is a safety feature that should be used when the command
+ executed can return a very large, possibly infinite, result.</p>
+ <code type="none">
+> os:cmd("cat /dev/zero", #{ max_size => 20 }).
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]</code>
+ </item>
+ </taglist>
</desc>
</func>
diff --git a/lib/kernel/doc/src/part_notes.xml b/lib/kernel/doc/src/part_notes.xml
deleted file mode 100644
index 5e849039ee..0000000000
--- a/lib/kernel/doc/src/part_notes.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Kernel Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>Kernel</em> application has all the code necessary to run
- the Erlang runtime system itself; File servers and code servers
- etc.</p>
- <p>For information about older versions, see
- <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/kernel/doc/src/part_notes_history.xml b/lib/kernel/doc/src/part_notes_history.xml
deleted file mode 100644
index 0cf7b793da..0000000000
--- a/lib/kernel/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2006</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>Kernel Release Notes History</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>Kernel</em> application has all the code necessary to run
- the Erlang runtime system itself; File servers and code servers
- etc.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/kernel/doc/src/user_guide.gif b/lib/kernel/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/kernel/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 418b0c50e1..f5a890cb95 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -340,8 +340,7 @@ handle_call(all_loaded, _From, S) ->
{reply,all_loaded(Db),S};
handle_call({get_object_code,Mod}, _From, St) when is_atom(Mod) ->
- Path = St#state.path,
- case mod_to_bin(Path, Mod) of
+ case get_object_code(St, Mod) of
{_,Bin,FName} -> {reply,{Mod,Bin,FName},St};
Error -> {reply,Error,St}
end;
@@ -1182,19 +1181,28 @@ load_file(Mod, From, St0) ->
end,
handle_pending_on_load(Action, Mod, From, St0).
-load_file_1(Mod, From, #state{path=Path}=St) ->
- case mod_to_bin(Path, Mod) of
+load_file_1(Mod, From, St) ->
+ case get_object_code(St, Mod) of
error ->
{reply,{error,nofile},St};
{Mod,Binary,File} ->
try_load_module_1(File, Mod, Binary, From, St)
end.
-mod_to_bin([Dir|Tail], Mod) ->
- File = filename:append(Dir, atom_to_list(Mod) ++ objfile_extension()),
+get_object_code(#state{path=Path}, Mod) when is_atom(Mod) ->
+ ModStr = atom_to_list(Mod),
+ case erl_prim_loader:is_basename(ModStr) of
+ true ->
+ mod_to_bin(Path, Mod, ModStr ++ objfile_extension());
+ false ->
+ error
+ end.
+
+mod_to_bin([Dir|Tail], Mod, ModFile) ->
+ File = filename:append(Dir, ModFile),
case erl_prim_loader:get_file(File) of
error ->
- mod_to_bin(Tail, Mod);
+ mod_to_bin(Tail, Mod, ModFile);
{ok,Bin,_} ->
case filename:pathtype(File) of
absolute ->
@@ -1203,10 +1211,9 @@ mod_to_bin([Dir|Tail], Mod) ->
{Mod,Bin,absname(File)}
end
end;
-mod_to_bin([], Mod) ->
+mod_to_bin([], Mod, ModFile) ->
%% At last, try also erl_prim_loader's own method
- File = to_list(Mod) ++ objfile_extension(),
- case erl_prim_loader:get_file(File) of
+ case erl_prim_loader:get_file(ModFile) of
error ->
error; % No more alternatives !
{ok,Bin,FName} ->
diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl
index 70cbf1c87c..99ea8dc384 100644
--- a/lib/kernel/src/disk_log.erl
+++ b/lib/kernel/src/disk_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -266,7 +266,7 @@ inc_wrap_file(Log) ->
Size :: dlog_size(),
Reason :: no_such_log | nonode | {read_only_mode, Log}
| {blocked_log, Log}
- | {new_size_too_small, CurrentSize :: pos_integer()}
+ | {new_size_too_small, Log, CurrentSize :: pos_integer()}
| {badarg, size}
| {file_error, file:filename(), file_error()}.
change_size(Log, NewSize) ->
diff --git a/lib/kernel/src/erl_boot_server.erl b/lib/kernel/src/erl_boot_server.erl
index ac81cc9689..2578b74428 100644
--- a/lib/kernel/src/erl_boot_server.erl
+++ b/lib/kernel/src/erl_boot_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -58,13 +58,11 @@
-define(single_addr_mask, {255, 255, 255, 255}).
--type ip4_address() :: {0..255,0..255,0..255,0..255}.
-
--spec start(Slaves) -> {'ok', Pid} | {'error', What} when
+-spec start(Slaves) -> {'ok', Pid} | {'error', Reason} when
Slaves :: [Host],
- Host :: atom(),
+ Host :: inet:ip_address() | inet:hostname(),
Pid :: pid(),
- What :: any().
+ Reason :: {'badarg', Slaves}.
start(Slaves) ->
case check_arg(Slaves) of
@@ -74,11 +72,11 @@ start(Slaves) ->
{error, {badarg, Slaves}}
end.
--spec start_link(Slaves) -> {'ok', Pid} | {'error', What} when
+-spec start_link(Slaves) -> {'ok', Pid} | {'error', Reason} when
Slaves :: [Host],
- Host :: atom(),
+ Host :: inet:ip_address() | inet:hostname(),
Pid :: pid(),
- What :: any().
+ Reason :: {'badarg', Slaves}.
start_link(Slaves) ->
case check_arg(Slaves) of
@@ -104,10 +102,10 @@ check_arg([], Result) ->
check_arg(_, _Result) ->
error.
--spec add_slave(Slave) -> 'ok' | {'error', What} when
+-spec add_slave(Slave) -> 'ok' | {'error', Reason} when
Slave :: Host,
- Host :: atom(),
- What :: any().
+ Host :: inet:ip_address() | inet:hostname(),
+ Reason :: {'badarg', Slave}.
add_slave(Slave) ->
case inet:getaddr(Slave, inet) of
@@ -117,10 +115,10 @@ add_slave(Slave) ->
{error, {badarg, Slave}}
end.
--spec delete_slave(Slave) -> 'ok' | {'error', What} when
+-spec delete_slave(Slave) -> 'ok' | {'error', Reason} when
Slave :: Host,
- Host :: atom(),
- What :: any().
+ Host :: inet:ip_address() | inet:hostname(),
+ Reason :: {'badarg', Slave}.
delete_slave(Slave) ->
case inet:getaddr(Slave, inet) of
@@ -130,7 +128,7 @@ delete_slave(Slave) ->
{error, {badarg, Slave}}
end.
--spec add_subnet(Mask :: ip4_address(), Addr :: ip4_address()) ->
+-spec add_subnet(Netmask :: inet:ip_address(), Addr :: inet:ip_address()) ->
'ok' | {'error', any()}.
add_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) ->
@@ -141,14 +139,15 @@ add_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) ->
{error, empty_subnet}
end.
--spec delete_subnet(Mask :: ip4_address(), Addr :: ip4_address()) -> 'ok'.
+-spec delete_subnet(Netmask :: inet:ip_address(),
+ Addr :: inet:ip_address()) -> 'ok'.
delete_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) ->
gen_server:call(boot_server, {delete, {Mask, Addr}}).
-spec which_slaves() -> Slaves when
- Slaves :: [Host],
- Host :: atom().
+ Slaves :: [Slave],
+ Slave :: {Netmask :: inet:ip_address(), Address :: inet:ip_address()}.
which_slaves() ->
gen_server:call(boot_server, which).
diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index 480db6814e..9662f8fa90 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -21,7 +21,7 @@
%% Low-level debugging support. EXPERIMENTAL!
--export([size/1,df/1,df/2,df/3,ic/1]).
+-export([size/1,df/1,df/2,df/3,df/4,ic/1]).
%% This module contains the following *experimental* BIFs:
%% disassemble/1
@@ -347,31 +347,39 @@ is_term_seen(_, []) -> false.
-spec df(module()) -> df_ret().
df(Mod) when is_atom(Mod) ->
+ df(lists:concat([Mod, ".dis"]), Mod).
+
+-spec df(module(), atom()) -> df_ret();
+ (file:io_device() | file:filename(), module()) -> df_ret().
+
+df(Mod, Func) when is_atom(Mod), is_atom(Func) ->
+ df(lists:concat([Mod, "_", Func, ".dis"]), Mod, Func);
+df(Name, Mod) when is_atom(Mod) ->
try Mod:module_info(functions) of
Fs0 when is_list(Fs0) ->
- Name = lists:concat([Mod, ".dis"]),
Fs = [{Mod,Func,Arity} || {Func,Arity} <- Fs0],
dff(Name, Fs)
catch _:_ -> {undef,Mod}
end.
--spec df(module(), atom()) -> df_ret().
-df(Mod, Func) when is_atom(Mod), is_atom(Func) ->
+-spec df(module(), atom(), arity()) -> df_ret();
+ (file:io_device() | file:filename(), module(), atom()) -> df_ret().
+
+df(Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arity) ->
+ df(lists:concat([Mod, "_", Func, "_", Arity, ".dis"]), Mod, Func, Arity);
+df(Name, Mod, Func) when is_atom(Mod), is_atom(Func) ->
try Mod:module_info(functions) of
Fs0 when is_list(Fs0) ->
- Name = lists:concat([Mod, "_", Func, ".dis"]),
Fs = [{Mod,Func1,Arity} || {Func1,Arity} <- Fs0, Func1 =:= Func],
dff(Name, Fs)
catch _:_ -> {undef,Mod}
end.
--spec df(module(), atom(), arity()) -> df_ret().
-
-df(Mod, Func, Arity) when is_atom(Mod), is_atom(Func) ->
+-spec df(file:io_device() | file:filename(), module(), atom(), arity()) -> df_ret().
+df(Name, Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arity) ->
try Mod:module_info(functions) of
Fs0 when is_list(Fs0) ->
- Name = lists:concat([Mod, "_", Func, "_", Arity, ".dis"]),
Fs = [{Mod,Func1,Arity1} || {Func1,Arity1} <- Fs0,
Func1 =:= Func, Arity1 =:= Arity],
dff(Name, Fs)
diff --git a/lib/kernel/src/group_history.erl b/lib/kernel/src/group_history.erl
index 91f3663cc5..9745848992 100644
--- a/lib/kernel/src/group_history.erl
+++ b/lib/kernel/src/group_history.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017. All Rights Reserved.
+%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -260,7 +260,7 @@ resize_log(Name, _OldSize, NewSize) ->
ok ->
show('$#erlang-history-resize-result',
"ok~n", []);
- {error, {new_size_too_small, _}} ->
+ {error, {new_size_too_small, _, _}} ->
show('$#erlang-history-resize-result',
"failed (new size is too small)~n", []),
disable_history();
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index f4c7c277ed..f8199fcf71 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -236,9 +236,10 @@ load_common(Mod, Bin, Beam, Architecture) ->
lists:foreach(fun({FE, DestAddress}) ->
hipe_bifs:set_native_address_in_fe(FE, DestAddress)
end, erase(closures_to_patch)),
- ok = hipe_bifs:commit_patch_load(LoaderState),
set_beam_call_traps(FunDefs),
- ok;
+ export_funs(FunDefs),
+ ok = hipe_bifs:commit_patch_load(LoaderState),
+ ok;
BeamBinary when is_binary(BeamBinary) ->
%% Find all closures in the code.
[] = erase(closures_to_patch), %Clean up, assertion.
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 2a88cc7e26..0eca6fef03 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -120,6 +120,6 @@
{applications, []},
{env, [{error_logger, tty}]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-9.1", "stdlib-3.4", "sasl-3.0"]}
+ {runtime_dependencies, ["erts-9.3", "stdlib-3.4", "sasl-3.0"]}
]
}.
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index f1ef70a373..4ee497bbbd 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -18,7 +18,9 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-19.*, OTP-20.0
+ [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*, OTP-20.0
+ {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-20.1+
%% Down to - max one major revision back
- [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-19.*, OTP-20.0
+ [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*, OTP-20.0
+ {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-20.1+
}.
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index 0250783632..a4b4f798f9 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -21,7 +21,7 @@
%% Provides a common operating system interface.
--export([type/0, version/0, cmd/1, find_executable/1, find_executable/2]).
+-export([type/0, version/0, cmd/1, cmd/2, find_executable/1, find_executable/2]).
-include("file.hrl").
@@ -32,6 +32,11 @@
putenv/2, set_signal/2, system_time/0, system_time/1,
timestamp/0, unsetenv/1]).
+-type os_command() :: atom() | io_lib:chars().
+-type os_command_opts() :: #{ max_size => non_neg_integer() | infinity }.
+
+-export_type([os_command/0, os_command_opts/0]).
+
-spec getenv() -> [string()].
getenv() -> erlang:nif_error(undef).
@@ -232,15 +237,21 @@ extensions() ->
%% Executes the given command in the default shell for the operating system.
-spec cmd(Command) -> string() when
- Command :: atom() | io_lib:chars().
+ Command :: os_command().
cmd(Cmd) ->
+ cmd(Cmd, #{ }).
+
+-spec cmd(Command, Options) -> string() when
+ Command :: os_command(),
+ Options :: os_command_opts().
+cmd(Cmd, Opts) ->
validate(Cmd),
{SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd),
Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout,
stream, in, hide | SpawnOpts]),
MonRef = erlang:monitor(port, Port),
true = port_command(Port, SpawnInput),
- Bytes = get_data(Port, MonRef, Eot, []),
+ Bytes = get_data(Port, MonRef, Eot, [], 0, maps:get(max_size, Opts, infinity)),
demonitor(MonRef, [flush]),
String = unicode:characters_to_list(Bytes),
if %% Convert to unicode list if possible otherwise return bytes
@@ -291,12 +302,13 @@ validate1([List|Rest]) when is_list(List) ->
validate1([]) ->
ok.
-get_data(Port, MonRef, Eot, Sofar) ->
+get_data(Port, MonRef, Eot, Sofar, Size, Max) ->
receive
{Port, {data, Bytes}} ->
- case eot(Bytes, Eot) of
+ case eot(Bytes, Eot, Size, Max) of
more ->
- get_data(Port, MonRef, Eot, [Sofar,Bytes]);
+ get_data(Port, MonRef, Eot, [Sofar, Bytes],
+ Size + byte_size(Bytes), Max);
Last ->
catch port_close(Port),
flush_until_down(Port, MonRef),
@@ -307,13 +319,16 @@ get_data(Port, MonRef, Eot, Sofar) ->
iolist_to_binary(Sofar)
end.
-eot(_Bs, <<>>) ->
+eot(Bs, <<>>, Size, Max) when Size + byte_size(Bs) < Max ->
more;
-eot(Bs, Eot) ->
+eot(Bs, <<>>, Size, Max) ->
+ binary:part(Bs, {0, Max - Size});
+eot(Bs, Eot, Size, Max) ->
case binary:match(Bs, Eot) of
- nomatch -> more;
- {Pos, _} ->
- binary:part(Bs,{0, Pos})
+ {Pos, _} when Size + Pos < Max ->
+ binary:part(Bs,{0, Pos});
+ _ ->
+ eot(Bs, <<>>, Size, Max)
end.
%% When port_close returns we know that all the
diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl
index a5cc7b0ec1..872e63ab53 100644
--- a/lib/kernel/src/user.erl
+++ b/lib/kernel/src/user.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -398,7 +398,7 @@ get_line(Prompt, Port, Q, Acc, Enc) ->
get_line_bytes(Prompt, Port, Q, Acc, Bytes, Enc);
{Port, eof} ->
put(eof, true),
- {ok, eof, []};
+ {ok, eof, queue:new()};
{io_request,From,ReplyAs,{get_geometry,_}=Req} when is_pid(From) ->
do_io_request(Req, From, ReplyAs, Port,
queue:new()),
@@ -615,7 +615,7 @@ get_chars(Prompt, M, F, Xa, Port, Q, State, Enc) ->
get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc);
{Port, eof} ->
put(eof, true),
- {ok, eof, []};
+ {ok, eof, queue:new()};
%%{io_request,From,ReplyAs,Request} when is_pid(From) ->
%% get_chars_req(Prompt, M, F, Xa, Port, queue:new(), State,
%% Request, From, ReplyAs);
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 612f77149d..569753155f 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -1562,16 +1562,7 @@ on_load_trace_on_load(Config) ->
erlang:trace_pattern(on_load, false, []),
erlang:trace(self(), false, [call]),
- %% WE GET TRACES FOR CALLS TO UNDEFINED FUNCTIONS ???
- %% Remove filter when that is fixed.
- Ms = lists:filter(fun({trace,Papa,call,
- {error_handler,undefined_function,
- [on_load_update_code,_,_]}})
- -> false;
- (_) -> true
- end,
- flush()),
-
+ Ms = flush(),
[{trace, Papa, call, {on_load_update_code, a, []}},
{trace, Papa, call, {on_load_update_code, b, [99]}},
{trace, Papa, call, {on_load_update_code, c, []}}] = Ms,
diff --git a/lib/kernel/test/code_SUITE_data/upgrade_client.erl b/lib/kernel/test/code_SUITE_data/upgrade_client.erl
index faa18e1410..1c3c2def53 100644
--- a/lib/kernel/test/code_SUITE_data/upgrade_client.erl
+++ b/lib/kernel/test/code_SUITE_data/upgrade_client.erl
@@ -341,6 +341,7 @@ check_tracing_loop(N, MsgList) ->
stop_tracing(Tracer) ->
+ erlang:trace_pattern({error_handler,undefined_function,3}, false, [global]),
erlang:trace(self(), false, [call]),
Tracer ! die_please,
receive
diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl
index fe2fc778f2..12e2521939 100644
--- a/lib/kernel/test/disk_log_SUITE.erl
+++ b/lib/kernel/test/disk_log_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index bbfaa9d147..e34b4d77d2 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -95,7 +95,11 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
+init_per_testcase(TC, Config) when TC == hostnames;
+ TC == nodenames ->
+ file:make_dir("hostnames_nodedir"),
+ file:write_file("hostnames_nodedir/ignore_core_files",""),
+ Config;
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Config.
@@ -251,7 +255,7 @@ test_node(Name, Illigal) ->
end,
net_kernel:monitor_nodes(true),
BinCommand = unicode:characters_to_binary(Command, utf8),
- Prt = open_port({spawn, BinCommand}, [stream]),
+ Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]),
Node = list_to_atom(Name),
receive
{nodeup, Node} ->
diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl
index 0a7f73c344..0e7b7adc47 100644
--- a/lib/kernel/test/global_SUITE.erl
+++ b/lib/kernel/test/global_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -3470,8 +3470,8 @@ start_procs(Parent, N1, N2, N3, Config) ->
Pid6 = rpc:call(N3, ?MODULE, start_proc3, [test4]),
assert_pid(Pid6),
yes = global:register_name(test1, Pid3),
- yes = global:register_name(test2, Pid4, {global, notify_all_name}),
- yes = global:register_name(test3, Pid5, {global, random_notify_name}),
+ yes = global:register_name(test2, Pid4, fun global:notify_all_name/3),
+ yes = global:register_name(test3, Pid5, fun global:random_notify_name/3),
Resolve = fun(Name, Pid1, Pid2) ->
Parent ! {resolve_called, Name, node()},
{Min, Max} = minmax(Pid1, Pid2),
@@ -3546,7 +3546,7 @@ start_proc_basic(Name) ->
end.
init_proc_basic(Parent, Name) ->
- X = global:register_name(Name, self(), {?MODULE, fix_basic_name}),
+ X = global:register_name(Name, self(), fun ?MODULE:fix_basic_name/3),
Parent ! {self(),X},
loop().
@@ -3791,15 +3791,6 @@ stop() ->
test_server:stop_node(Node)
end, nodes()).
-dbg_logs(Name) -> dbg_logs(Name, ?NODES).
-
-dbg_logs(Name, Nodes) ->
- lists:foreach(fun(N) ->
- F = lists:concat([Name, ".log.", N, ".txt"]),
- ok = sys:log_to_file({global_name_server, N}, F)
- end, Nodes).
-
-
%% Tests that locally loaded nodes do not loose contact with other nodes.
global_lost_nodes(Config) when is_list(Config) ->
Timeout = 60,
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 3b502be8b8..ba0d075ef2 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -1083,11 +1083,9 @@ ifaddrs([{If,Opts}|IOs]) ->
#ifopts{flags=F} = Ifopts = check_ifopts(Opts, #ifopts{name=If}),
case F of
{flags,Flags} ->
- case lists:member(up, Flags) of
- true ->
- Ifopts#ifopts.addrs;
- false ->
- []
+ case lists:member(running, Flags) of
+ true -> Ifopts#ifopts.addrs;
+ false -> []
end ++ ifaddrs(IOs);
undefined ->
ifaddrs(IOs)
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 53a9e168ef..a0bcde68db 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -25,7 +25,8 @@
-export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1,
find_executable/1, unix_comment_in_command/1, deep_list_command/1,
large_output_command/1, background_command/0, background_command/1,
- message_leak/1, close_stdin/0, close_stdin/1, perf_counter_api/1]).
+ message_leak/1, close_stdin/0, close_stdin/1, max_size_command/1,
+ perf_counter_api/1]).
-include_lib("common_test/include/ct.hrl").
@@ -37,7 +38,7 @@ all() ->
[space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command,
find_executable, unix_comment_in_command, deep_list_command,
large_output_command, background_command, message_leak,
- close_stdin, perf_counter_api].
+ close_stdin, max_size_command, perf_counter_api].
groups() ->
[].
@@ -312,6 +313,19 @@ close_stdin(Config) ->
"-1" = os:cmd(Fds).
+max_size_command(_Config) ->
+
+ Res20 = os:cmd("cat /dev/zero", #{ max_size => 20 }),
+ 20 = length(Res20),
+
+ Res0 = os:cmd("cat /dev/zero", #{ max_size => 0 }),
+ 0 = length(Res0),
+
+ Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }),
+ 32768 = length(Res32768),
+
+ ResHello = string:trim(os:cmd("echo hello", #{ max_size => 20 })),
+ 5 = length(ResHello).
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl
index 2f4330c217..e88d42788f 100644
--- a/lib/kernel/test/prim_file_SUITE.erl
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -2108,12 +2108,25 @@ free_memory() ->
{value, {buffered_memory, Buffed}} -> Buffed;
false -> 0
end),
- TotFree div (1024*1024)
+ usable_mem(TotFree) div (1024*1024)
catch
error : undef ->
ct:fail({"os_mon not built"})
end.
+usable_mem(Memory) ->
+ case test_server:is_valgrind() of
+ true ->
+ %% Valgrind uses extra memory for the V- and A-bits.
+ %% http://valgrind.org/docs/manual/mc-manual.html#mc-manual.value
+ %% Docs says it uses "compression to represent the V bits compactly"
+ %% but let's be conservative and cut usable memory in half.
+ Memory div 2;
+ false ->
+ Memory
+ end.
+
+
%%%-----------------------------------------------------------------
%%% Utilities
rm_rf(Mod,Dir) ->
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index cef54dd41a..91261e1d55 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.4
+KERNEL_VSN = 5.4.2
diff --git a/lib/megaco/doc/src/Makefile b/lib/megaco/doc/src/Makefile
index 8b9ce31d0e..43704cddf4 100644
--- a/lib/megaco/doc/src/Makefile
+++ b/lib/megaco/doc/src/Makefile
@@ -1,7 +1,7 @@
-#
+#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
+# Copyright Ericsson AB 2000-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -43,7 +43,7 @@ include files.mk
# ----------------------------------------------------
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
INTERNAL_HTML_FILES = $(TECHNICAL_DESCR_FILES:%.xml=$(HTMLDIR)/%.html)
@@ -70,10 +70,6 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-INDEX_FILE = index.html
-INDEX_SRC = $(INDEX_FILE).src
-INDEX_TARGET = $(DOCDIR)/$(INDEX_FILE)
-
STANDARD_DIR = ../standard
STANDARDS = $(STANDARD_DIR)/rfc3525.txt \
$(STANDARD_DIR)/rfc4234.txt \
@@ -81,10 +77,10 @@ STANDARDS = $(STANDARD_DIR)/rfc3525.txt \
$(STANDARD_DIR)/implementors_guide_v10-13.pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
@@ -101,7 +97,7 @@ $(HTMLDIR)/%.png: %.png
docs: pdf html man
-ldocs: local_docs $(INDEX_TARGET)
+ldocs: local_docs
$(TOP_PDF_FILE): $(XML_FILES)
@@ -121,22 +117,14 @@ clean_html:
imgs: $(IMG_FILES:%=$(HTMLDIR)/%)
-man: $(MAN3_FILES)
-
-$(INDEX_TARGET): $(INDEX_SRC) $(APP_FILE)
- sed -e 's/%VSN%/$(VSN)/' \
- -e 's/%ERLANG_SITE%/www\.erlang\.se\//' \
- -e 's/%UP_ONE_LEVEL%/..\/..\/..\/doc\/index.html/' \
- -e 's/%OFF_PRINT%/pdf\/megaco-$(VSN).pdf/' $< > $@
+man: $(MAN3_FILES)
-debug opt:
+debug opt:
info:
@echo "->Makefile<-"
@echo ""
- @echo "INDEX_FILE = $(INDEX_FILE)"
- @echo "INDEX_SRC = $(INDEX_SRC)"
- @echo "INDEX_TARGET = $(INDEX_TARGET)"
+ @echo "HTML_REF_MAN_FILE = $(HTML_REF_MAN_FILE)"
@echo ""
@echo "XML_APPLICATION_FILES = $(XML_APPLICATION_FILES)"
@echo "XML_PART_FILES = $(XML_PART_FILES)"
@@ -158,7 +146,7 @@ info:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -193,4 +181,3 @@ $(HTMLDIR)/megaco_transport_mechanisms.html: megaco_transport_mechanisms.xml
$(HTMLDIR)/megaco_transport.html: megaco_transport.xml
$(HTMLDIR)/megaco_udp.html: megaco_udp.xml
$(HTMLDIR)/megaco_user.html: megaco_user.xml
-
diff --git a/lib/megaco/doc/src/book.gif b/lib/megaco/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/megaco/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/megaco/doc/src/book.xml b/lib/megaco/doc/src/book.xml
index 47ad9b6a5e..9c304d6cae 100644
--- a/lib/megaco/doc/src/book.xml
+++ b/lib/megaco/doc/src/book.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>Megaco/H.248</title>
@@ -46,4 +46,3 @@
</releasenotes>
<index></index>
</book>
-
diff --git a/lib/megaco/doc/src/fascicules.xml b/lib/megaco/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/megaco/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/megaco/doc/src/files.mk b/lib/megaco/doc/src/files.mk
index 5e7d849ab6..e40889c3fb 100644
--- a/lib/megaco/doc/src/files.mk
+++ b/lib/megaco/doc/src/files.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2016. All Rights Reserved.
+# Copyright Ericsson AB 2001-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -36,9 +36,7 @@ XML_REF3_FILES = \
megaco_udp.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml \
- part_notes_history.xml
+ part.xml
XML_EXTRA_FILES = \
notes_history.xml
@@ -61,11 +59,6 @@ IMG_FILES = \
single_node_config.gif \
distr_node_config.gif \
megaco_sys_arch.gif \
- user_guide.gif \
- note.gif \
- notes.gif \
- ref_man.gif \
- book.gif \
MG-startup_flow_noMID.gif \
MGC_startup_call_flow.gif \
MG_startup_call_flow.gif \
diff --git a/lib/megaco/doc/src/index.html.src b/lib/megaco/doc/src/index.html.src
deleted file mode 100644
index b3ff9c3ae7..0000000000
--- a/lib/megaco/doc/src/index.html.src
+++ /dev/null
@@ -1,113 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<!-- This file is obsolete -->
-<HTML>
-<!--
- ``Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson Utvecklings AB.
- Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
- AB. All Rights Reserved.''
-
- $Id$
--->
-<HEAD>
-<TITLE>Megaco %VSN%</TITLE>
-</HEAD>
-
-<BODY BGCOLOR="#FFFFFF">
-
-<CENTER>
-<A HREF="http://%ERLANG_SITE%">
- <IMG ALT="Erlang/OTP" BORDER=0 SRC="html/min_head.gif">
-</A><BR>
-
-<FONT SIZE="-1">
-[<A HREF="%UP_ONE_LEVEL%">Up</A> |
-<A HREF="http://%ERLANG_SITE%">Erlang/OTP</A>]
-</FONT><BR>
-
-<P><FONT SIZE="+3">Megaco</FONT><BR>
-Version %VSN%
-</CENTER>
-
-<P><TABLE>
-<TR>
-<TD>
-<!-- IMG ALIGN=LEFT ALT="MEGACO" SRC="html/megaco.gif" -->
-</TD>
-
-<TD>
- <p>The <STRONG>Megaco</STRONG> application is a framework
-for building applications on top of the Megaco/H.248 protocol.</p>
-</TD>
-</TR>
-</TABLE>
-
-<P><CENTER>
-<TABLE CELLPADDING=15>
-<TR>
-<TD ALIGN=CENTER>
-<A HREF="html/users_guide.html">
- <IMG ALT="User's Guide" BORDER=0 SRC="html/user_guide.gif">
-</A>
-<BR>
-<FONT SIZE="-1">
-<A HREF="html/users_guide.html">User's Guide</A>
-</FONT>
-</TD>
-
-<TD ALIGN=CENTER>
-<A HREF="html/index.html">
- <IMG ALT="Reference Manual" BORDER=0 SRC="html/ref_man.gif">
-</A>
-<BR>
-<FONT SIZE="-1">
-<A HREF="html/index.html">Reference Manual</A>
-</FONT>
-</TD>
-
-</TR>
-
-<TR>
-<TD ALIGN=CENTER>
-<A HREF="html/release_notes.html">
- <IMG ALT="Release Notes" BORDER=0 SRC="html/notes.gif">
-</A>
-<BR>
-<FONT SIZE="-1">
-<A HREF="html/release_notes.html">Release Notes</A>
-</FONT>
-</TD>
-
-<TD ALIGN=CENTER>
-<A HREF="%OFF_PRINT%">
- <IMG ALT="Off-Print" BORDER=0 SRC="html/book.gif">
-</A>
-<BR>
-<FONT SIZE="-1">
-<A HREF="%OFF_PRINT%">Off-Print</A>
-</FONT>
-</TD>
-</TR>
-</TABLE>
-</CENTER>
-
-<P><CENTER>
-<HR>
-<FONT SIZE="-1">
-Copyright &copy; 1991-2001
-<A HREF="http://www.erlang.se/">Ericsson Utvecklings AB</A>
-</FONT>
-</CENTER>
-</BODY>
-</HTML>
diff --git a/lib/megaco/doc/src/note.gif b/lib/megaco/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/megaco/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/megaco/doc/src/notes.gif b/lib/megaco/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/megaco/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index 068389c0c2..54e048a172 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,9 +19,9 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
-
+
<title>Megaco Release Notes</title>
<prepared>Lars Thors&eacute;n, H&aring;kan Mattsson, Micael Karlberg</prepared>
<docno></docno>
@@ -35,9 +35,24 @@
bugfixes for every release of Megaco. Each release of Megaco
thus constitutes one section in this document. The title of each
section is the version number of Megaco.</p>
-
-
- <section><title>Megaco 3.18.2</title>
+
+
+ <section><title>Megaco 3.18.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Megaco 3.18.2</title>
<section><title>Improvements and New Features</title>
<list>
@@ -142,7 +157,7 @@
</item>
</list>
</section>
-
+
</section>
<section><title>Megaco 3.17.0.2</title>
@@ -158,7 +173,7 @@
</item>
</list>
</section>
-
+
</section>
<section><title>Megaco 3.17.0.1</title>
@@ -190,7 +205,7 @@
<list type="bulleted">
<item>
<p>Allow whitespaces in installation path. </p>
- <p>It is now possible to give configure and make an
+ <p>It is now possible to give configure and make an
installation/release path with whitespaces in it. </p>
<p>Own Id: OTP-10107</p>
</item>
@@ -202,24 +217,24 @@
<section>
<title>Fixed bugs and malfunctions</title>
-
+
<!--
<p>-</p>
-->
-
+
<list type="bulleted">
<item>
- <p>Buffer overrun error while flex scanner processing
+ <p>Buffer overrun error while flex scanner processing
property parm groups. </p>
- <p>This error occured only for large messages if a
- buffer realloc was needed while processing the
+ <p>This error occured only for large messages if a
+ buffer realloc was needed while processing the
property parm groups. </p>
<p>Own Id: OTP-10998</p>
<p>Aux Id: Seq 12263</p>
</item>
-
+
</list>
-
+
</section>
<section>
@@ -229,10 +244,10 @@
<!--
<list type="bulleted">
<item>
- <p>A number of binary encoding alternatives has been removed.
+ <p>A number of binary encoding alternatives has been removed.
The binary encoding option <c>driver</c> has been removed
- since this (the use of the asn1 linked in driver) is
- now default and there is now way to <em>not</em> use it.
+ since this (the use of the asn1 linked in driver) is
+ now default and there is now way to <em>not</em> use it.
See <seealso marker="megaco_encode#binary_config">configuration of binary encoding</seealso> for more info. </p>
</item>
@@ -270,19 +285,19 @@
<section>
<title>Fixed bugs and malfunctions</title>
-
+
<p>-</p>
-
+
<!--
<list type="bulleted">
<item>
<p>Fixing miscellaneous things detected by dialyzer. </p>
<p>Own Id: OTP-9075</p>
</item>
-
+
</list>
-->
-
+
</section>
<section>
@@ -293,10 +308,10 @@
<list type="bulleted">
<item>
- <p>A number of binary encoding alternatives has been removed.
+ <p>A number of binary encoding alternatives has been removed.
The binary encoding option <c>driver</c> has been removed
- since this (the use of the asn1 linked in driver) is
- now default and there is now way to <em>not</em> use it.
+ since this (the use of the asn1 linked in driver) is
+ now default and there is now way to <em>not</em> use it.
See <seealso marker="megaco_encode#binary_config">configuration of binary encoding</seealso> for more info. </p>
</item>
@@ -322,7 +337,7 @@
<list type="bulleted">
<item>
<p>Allow whitespaces in installation path. </p>
- <p>It is now possible to give configure and make an
+ <p>It is now possible to give configure and make an
installation/release path with whitespaces in it. </p>
<p>Own Id: OTP-10107</p>
</item>
@@ -332,8 +347,8 @@
</item>
<item>
- <p>Removed use of deprecated system flag,
- <c>global_haeps_size</c>, in the measurement tool
+ <p>Removed use of deprecated system flag,
+ <c>global_haeps_size</c>, in the measurement tool
<c>mstone1</c>. </p>
</item>
@@ -343,19 +358,19 @@
<section>
<title>Fixed bugs and malfunctions</title>
-
+
<p>-</p>
-
+
<!--
<list type="bulleted">
<item>
<p>Fixing miscellaneous things detected by dialyzer. </p>
<p>Own Id: OTP-9075</p>
</item>
-
+
</list>
-->
-
+
</section>
<section>
@@ -365,8 +380,8 @@
<!--
<list type="bulleted">
<item>
- <p>Due to the change in the flex driver API,
- we may no longer be able to build and/or use
+ <p>Due to the change in the flex driver API,
+ we may no longer be able to build and/or use
the flex driver without reentrant support. </p>
<p>Own Id: OTP-9795</p>
</item>
@@ -400,8 +415,8 @@
</item>
<item>
- <p>Removed use of deprecated system flag,
- <c>scheduler_bind_type</c>, in the measurement tool
+ <p>Removed use of deprecated system flag,
+ <c>scheduler_bind_type</c>, in the measurement tool
<c>mstone1</c>. </p>
<p>Own Id: OTP-9949</p>
</item>
@@ -412,19 +427,19 @@
<section>
<title>Fixed bugs and malfunctions</title>
-
+
<p>-</p>
-
+
<!--
<list type="bulleted">
<item>
<p>Fixing miscellaneous things detected by dialyzer. </p>
<p>Own Id: OTP-9075</p>
</item>
-
+
</list>
-->
-
+
</section>
<section>
@@ -434,8 +449,8 @@
<!--
<list type="bulleted">
<item>
- <p>Due to the change in the flex driver API,
- we may no longer be able to build and/or use
+ <p>Due to the change in the flex driver API,
+ we may no longer be able to build and/or use
the flex driver without reentrant support. </p>
<p>Own Id: OTP-9795</p>
</item>
@@ -467,8 +482,8 @@
</item>
<item>
- <p>ASN.1 no longer makes use of a driver to accelerate encode/decode,
- instead it uses NIFs. The encoding config option is <em>still</em>
+ <p>ASN.1 no longer makes use of a driver to accelerate encode/decode,
+ instead it uses NIFs. The encoding config option is <em>still</em>
the same, i.e. <c>driver</c>. </p>
<p>Own Id: OTP-9672</p>
</item>
@@ -491,19 +506,19 @@
<section>
<title>Fixed bugs and malfunctions</title>
-
+
<p>-</p>
-
+
<!--
<list type="bulleted">
<item>
<p>Fixing miscellaneous things detected by dialyzer. </p>
<p>Own Id: OTP-9075</p>
</item>
-
+
</list>
-->
-
+
</section>
<section>
@@ -513,8 +528,8 @@
<!--
<list type="bulleted">
<item>
- <p>Due to the change in the flex driver API,
- we may no longer be able to build and/or use
+ <p>Due to the change in the flex driver API,
+ we may no longer be able to build and/or use
the flex driver without reentrant support. </p>
<p>Own Id: OTP-9795</p>
</item>
@@ -551,19 +566,19 @@
<section>
<title>Fixed bugs and malfunctions</title>
-
+
<p>-</p>
-
+
<!--
<list type="bulleted">
<item>
<p>Fixing miscellaneous things detected by dialyzer. </p>
<p>Own Id: OTP-9075</p>
</item>
-
+
</list>
-->
-
+
</section>
</section> <!-- 3.15.1.1 -->
@@ -582,8 +597,8 @@
<!--
<list type="bulleted">
<item>
- <p>Updated the
- <seealso marker="megaco_performance">performance</seealso>
+ <p>Updated the
+ <seealso marker="megaco_performance">performance</seealso>
chapter. </p>
<p>Own Id: OTP-8696</p>
</item>
@@ -637,7 +652,7 @@
<!--
<list type="bulleted">
<item>
- <p>Eliminated a possible race condition while creating
+ <p>Eliminated a possible race condition while creating
pending counters. </p>
<p>Own Id: OTP-8634</p>
<p>Aux Id: Seq 11579</p>
@@ -666,8 +681,8 @@
<list type="bulleted">
<item>
- <p>Updated the
- <seealso marker="megaco_performance">performance</seealso>
+ <p>Updated the
+ <seealso marker="megaco_performance">performance</seealso>
chapter. </p>
<p>Own Id: OTP-8696</p>
</item>
@@ -694,14 +709,14 @@
<item>
<p>Fix shared libraries installation. </p>
- <p>The flex shared lib(s) were incorrectly installed as data
+ <p>The flex shared lib(s) were incorrectly installed as data
files. </p>
<p>Peter Lemenkov</p>
<p>Own Id: OTP-8627</p>
</item>
<item>
- <p>Eliminated a possible race condition while creating
+ <p>Eliminated a possible race condition while creating
pending counters. </p>
<p>Own Id: OTP-8634</p>
<p>Aux Id: Seq 11579</p>
@@ -757,14 +772,14 @@
<item>
<p>Fix shared libraries installation. </p>
- <p>The flex shared lib(s) were incorrectly installed as data
+ <p>The flex shared lib(s) were incorrectly installed as data
files. </p>
<p>Peter Lemenkov</p>
<p>Own Id: OTP-8627</p>
</item>
<item>
- <p>Eliminated a possible race condition while creating
+ <p>Eliminated a possible race condition while creating
pending counters. </p>
<p>Own Id: OTP-8634</p>
<p>Aux Id: Seq 11579</p>
@@ -792,18 +807,18 @@
<list type="bulleted">
<item>
- <p>Various changes to configure and makefile(s) to facilitate cross
+ <p>Various changes to configure and makefile(s) to facilitate cross
compilation (and other build system improvements). </p>
<p>Own Id: OTP-8323</p>
</item>
<item>
- <p>Added a help target in the test Makefile to explain
- the most useful make targets, used when testing the
+ <p>Added a help target in the test Makefile to explain
+ the most useful make targets, used when testing the
application using the test-server provided with megaco.</p>
<p>Own Id: OTP-8362</p>
</item>
-
+
<item>
<p>Adapted megaco_filter to the new internal format.</p>
<p>Own Id: OTP-8403</p>
@@ -820,16 +835,16 @@
<list type="bulleted">
<item>
- <p>Callbacks, when the callback module is unknown (undefined),
+ <p>Callbacks, when the callback module is unknown (undefined),
results in warning messages. </p>
<p>A race condition scenario. As part of a cancelation operation,
replies with waiting acknowledgements is cancelled. This includes
- informing the user (via a call to the handle_trans_ack callback
- function). It is possible that at this point the connection data
- has been removed, which makes it impossible for megaco to
- perform this operation, resulting in the warning message. The
- solution is to also store the callback module with the other
- reply information, to be used when cleaning up after a
+ informing the user (via a call to the handle_trans_ack callback
+ function). It is possible that at this point the connection data
+ has been removed, which makes it impossible for megaco to
+ perform this operation, resulting in the warning message. The
+ solution is to also store the callback module with the other
+ reply information, to be used when cleaning up after a
cancelation. </p>
<p>Own Id: OTP-8328</p>
<p>Aux Id: Seq 11384</p>
@@ -863,8 +878,8 @@
</item>
<item>
- <p>The documentation is now built with open source tools
- (<em>xsltproc</em> and <em>fop</em>) that exists on most
+ <p>The documentation is now built with open source tools
+ (<em>xsltproc</em> and <em>fop</em>) that exists on most
platforms. One visible change is that the frames are removed.</p>
<p>Own Id: OTP-8249</p>
</item>
@@ -910,11 +925,4 @@
</section>
</section> <!-- 3.13 -->
-
- <!-- section>
- <title>Release notes history</title>
- <p>For information about older versions see
- <url href="part_notes_history_frame.html">release notes history</url>.</p
- </section> -->
</chapter>
-
diff --git a/lib/megaco/doc/src/part_notes.xml b/lib/megaco/doc/src/part_notes.xml
deleted file mode 100644
index 7a838c5718..0000000000
--- a/lib/megaco/doc/src/part_notes.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2000</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Megaco/H.248 Release Notes</title>
- <prepared>Lars Thors&eacute;n, H&aring;kan Mattsson, Micael Karlberg</prepared>
- <docno></docno>
- <date>2007-06-15</date>
- <rev>%VSN%</rev>
- <file>part_notes.xml</file>
- </header>
- <description>
- <p>The Megaco application is a framework for building
- applications on top of the Megaco/H.248 protocol.</p>
- <p>For information about older versions see
- <url href="part_notes_history_frame.html">release notes history</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/megaco/doc/src/part_notes_history.xml b/lib/megaco/doc/src/part_notes_history.xml
deleted file mode 100644
index 0d8f1f2fb5..0000000000
--- a/lib/megaco/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2006</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Megaco Release Notes History</title>
- <prepared>Micael Karklberg</prepared>
- <responsible>OTP</responsible>
- <docno></docno>
- <approved>OTP</approved>
- <checked></checked>
- <date>2007-06-15</date>
- <rev>A</rev>
- <file>part_notes_history.xml</file>
- </header>
- <description>
- <p>The Megaco application is a framework for building
- applications on top of the Megaco/H.248 protocol.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/megaco/doc/src/ref_man.gif b/lib/megaco/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/megaco/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/megaco/doc/src/user_guide.gif b/lib/megaco/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/megaco/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/megaco/doc/src/warning.gif b/lib/megaco/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/megaco/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 9c6ba5bba0..a4f7de7f07 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.18.2
+MEGACO_VSN = 3.18.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/Makefile b/lib/mnesia/doc/src/Makefile
index 39f2b28637..82fcf66256 100644
--- a/lib/mnesia/doc/src/Makefile
+++ b/lib/mnesia/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1997-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
@@ -44,12 +44,11 @@ XML_REF3_FILES = \
mnesia_registry.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml \
- part_notes_history.xml
+ part.xml
XML_CHAPTER_FILES = \
Mnesia_chap1.xml \
+ Mnesia_overview.xml \
Mnesia_chap2.xml \
Mnesia_chap3.xml \
Mnesia_chap4.xml \
@@ -63,20 +62,12 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
-
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
GIF_FILES = \
- book.gif \
- company.gif \
- mnesia.gif \
- note.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif \
- warning.gif
+ company.gif
XML_HTML_FILES = \
notes_history.xml
@@ -89,7 +80,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -102,10 +93,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -134,11 +125,11 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/mnesia/doc/src/book.gif b/lib/mnesia/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/mnesia/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/mnesia/doc/src/fascicules.xml b/lib/mnesia/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/mnesia/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/mnesia/doc/src/mnesia.gif b/lib/mnesia/doc/src/mnesia.gif
deleted file mode 100644
index fbbabee5aa..0000000000
--- a/lib/mnesia/doc/src/mnesia.gif
+++ /dev/null
Binary files differ
diff --git a/lib/mnesia/doc/src/note.gif b/lib/mnesia/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/mnesia/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/mnesia/doc/src/notes.gif b/lib/mnesia/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/mnesia/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index e9243f7fc9..ba94e913f5 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -39,7 +39,49 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.15.1</title>
+ <section><title>Mnesia 4.15.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Removed a quadratic behavior in startup. This change
+ implies that backend plugins (if used) must be set when
+ the schema is created or via configuration parameters
+ before mnesia is started.</p>
+ <p>
+ Own Id: OTP-14829 Aux Id: ERIERL-84 </p>
+ </item>
+ <item>
+ <p>
+ Bad timing could crash mnesia after a checkpoint was
+ deactivated and reactivated with the same checkpoint name
+ on different tables.</p>
+ <p>
+ Own Id: OTP-14841 Aux Id: ERIERL-113 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.15.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix backup error handling, the real failure reason was
+ not returned.</p>
+ <p>
+ Own Id: OTP-14776 Aux Id: ERIERL-103 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.15.1</title>
<section><title>Improvements and New Features</title>
<list>
@@ -906,9 +948,9 @@
</item>
</list>
</section>
-
- </section>
-
+
+ </section>
+
<section><title>Mnesia 4.4.13</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -977,7 +1019,7 @@
</section>
<section><title>Mnesia 4.4.10</title>
-
+
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -1007,7 +1049,7 @@
</item>
</list>
</section>
-
+
</section>
@@ -1025,7 +1067,7 @@
</item>
</list>
</section>
-
+
</section>
<section><title>Mnesia 4.4.8</title>
@@ -1050,7 +1092,7 @@
</item>
</list>
</section>
-
+
</section>
<section><title>Mnesia 4.4.7</title>
@@ -1084,7 +1126,7 @@
</section>
- <section><title>Mnesia 4.4.6</title>
+ <section><title>Mnesia 4.4.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -1275,9 +1317,4 @@
</section>
</section>
- <!-- section>
- <title>Previous Notes</title>
- <p>For information about older versions see <url href="part_notes_history_frame.html">release notes history</url>.</p>
- </section -->
</chapter>
-
diff --git a/lib/mnesia/doc/src/part_notes.xml b/lib/mnesia/doc/src/part_notes.xml
deleted file mode 100644
index 1e4e4bc60a..0000000000
--- a/lib/mnesia/doc/src/part_notes.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1997</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>MNESIA Release Notes</title>
- <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
- <docno></docno>
- <date>1997-05-27</date>
- <rev>1.2</rev>
- <file>part_notes.xml</file>
- </header>
- <description>
- <p><em>Mnesia</em> is a Distributed DataBase Management
- System (DBMS), appropriate for telecommunications applications and other
- Erlang applications which require continuous operation and exhibit soft
- real-time properties. </p>
- <p>For information about older versions see
- <url href="part_notes_history_frame.html">release notes history</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/mnesia/doc/src/part_notes_history.xml b/lib/mnesia/doc/src/part_notes_history.xml
deleted file mode 100644
index a1c6f5aef0..0000000000
--- a/lib/mnesia/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2004</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>MNESIA Release Notes</title>
- <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
- <docno></docno>
- <date>1997-05-27</date>
- <rev>1.2</rev>
- <file>part_notes_history.sgml</file>
- </header>
- <description>
- <p><em>Mnesia</em> is a Distributed DataBase Management
- System (DBMS), appropriate for telecommunications applications and other
- Erlang applications which require continuous operation and exhibit soft
- real-time properties. </p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/mnesia/doc/src/ref_man.gif b/lib/mnesia/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/mnesia/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/mnesia/doc/src/summary.html.src b/lib/mnesia/doc/src/summary.html.src
deleted file mode 100644
index 2941a2f46a..0000000000
--- a/lib/mnesia/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-A heavy duty real-time distributed database \ No newline at end of file
diff --git a/lib/mnesia/doc/src/user_guide.gif b/lib/mnesia/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/mnesia/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/mnesia/doc/src/warning.gif b/lib/mnesia/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/mnesia/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index b68b2de028..1842769778 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -151,7 +151,8 @@
{'snmp', SnmpStruct::term()} |
{'storage_properties', [{Backend::module(), [BackendProp::_]}]} |
{'type', 'set' | 'ordered_set' | 'bag'} |
- {'local_content', boolean()}.
+ {'local_content', boolean()} |
+ {'user_properties', proplists:proplist()}.
-type t_result(Res) :: {'atomic', Res} | {'aborted', Reason::term()}.
-type activity() :: 'ets' | 'async_dirty' | 'sync_dirty' | 'transaction' | 'sync_transaction' |
diff --git a/lib/mnesia/src/mnesia_checkpoint.erl b/lib/mnesia/src/mnesia_checkpoint.erl
index 2ff77326a9..8112378ffd 100644
--- a/lib/mnesia/src/mnesia_checkpoint.erl
+++ b/lib/mnesia/src/mnesia_checkpoint.erl
@@ -857,9 +857,9 @@ retainer_loop(Cp = #checkpoint_args{is_activated=false, name=Name}) ->
retainer_loop(Cp = #checkpoint_args{name=Name}) ->
receive
{_From, {retain, Tid, Tab, Key, OldRecs}} ->
- R = val({Tab, {retainer, Name}}),
+ R = ?catch_val({Tab, {retainer, Name}}),
PendingTab = Cp#checkpoint_args.pending_tab,
- case R#retainer.really_retain of
+ case is_record(R, retainer) andalso R#retainer.really_retain of
true ->
Store = R#retainer.store,
try true = ets:member(PendingTab, Tid),
diff --git a/lib/mnesia/src/mnesia_log.erl b/lib/mnesia/src/mnesia_log.erl
index 55b1d6e419..a2de23a2a3 100644
--- a/lib/mnesia/src/mnesia_log.erl
+++ b/lib/mnesia/src/mnesia_log.erl
@@ -752,8 +752,8 @@ abort_write(B, What, Args, Reason) ->
Opaque = B#backup_args.opaque,
dbg_out("Failed to perform backup. M=~p:F=~tp:A=~tp -> ~tp~n",
[Mod, What, Args, Reason]),
- try apply(Mod, abort_write, [Opaque]) of
- {ok, _Res} -> throw({error, Reason})
+ try {ok, _Res} = apply(Mod, abort_write, [Opaque]) of
+ _ -> throw({error, Reason})
catch _:Other ->
error("Failed to abort backup. ~p:~tp~tp -> ~tp~n",
[Mod, abort_write, [Opaque], Other]),
diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl
index 83cc19c678..71952af31c 100644
--- a/lib/mnesia/src/mnesia_schema.erl
+++ b/lib/mnesia/src/mnesia_schema.erl
@@ -952,19 +952,9 @@ get_index_plugins() ->
get_schema_user_property(mnesia_index_plugins).
get_schema_user_property(Key) ->
- Tab = schema,
- %% Must work reliably both within transactions and outside of transactions
- Res = case get(mnesia_activity_state) of
- undefined ->
- dirty_read_table_property(Tab, Key);
- _ ->
- do_read_table_property(Tab, Key)
- end,
- case Res of
- undefined ->
- [];
- {_, Types} ->
- Types
+ case dirty_read_table_property(schema, Key) of
+ undefined -> [];
+ {_, Types} -> Types
end.
get_ext_types_disc() ->
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index a95f468ba2..45f811846d 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.15.1
+MNESIA_VSN = 4.15.3
diff --git a/lib/observer/doc/src/Makefile b/lib/observer/doc/src/Makefile
index b38278a156..11bfee1bdb 100644
--- a/lib/observer/doc/src/Makefile
+++ b/lib/observer/doc/src/Makefile
@@ -9,11 +9,11 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
# AB. All Rights Reserved.''
-#
+#
# $Id$
#
include $(ERL_TOP)/make/target.mk
@@ -45,17 +45,15 @@ XML_REF3_FILES = \
XML_REF6_FILES = observer_app.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml \
- part_notes_history.xml
+ part.xml
XML_CHAPTER_FILES = \
+ introduction_ug.xml \
crashdump_ug.xml \
etop_ug.xml \
observer_ug.xml \
ttb_ug.xml \
- notes.xml \
- notes_history.xml
+ notes.xml
BOOK_FILES = book.xml
@@ -69,9 +67,7 @@ ONLY_HTML_FILE =
GIF_FILES = \
et_processes.gif \
- et_modsprocs.gif \
- note.gif
-
+ et_modsprocs.gif
# ----------------------------------------------------
HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -88,9 +84,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -123,12 +119,12 @@ man: $(MAN1_FILES) $(MAN3_FILES) $(MAN6_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
@@ -148,4 +144,3 @@ release_docs_spec: docs
release_spec:
-
diff --git a/lib/observer/doc/src/fascicules.xml b/lib/observer/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/observer/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/observer/doc/src/note.gif b/lib/observer/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/observer/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index 05ea550964..96cd89b375 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -32,6 +32,73 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A bug introduced in OTP-20 would make Crashdump Viewer
+ crash when trying to expand an empty binary. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-14642</p>
+ </item>
+ <item>
+ <p>
+ If a match spec in the config file contained more than
+ one clause, observer would earlier crash when trying to
+ display it in the GUI. This is now corrected.</p>
+ <p>
+ Own Id: OTP-14643 Aux Id: ERL-489 </p>
+ </item>
+ <item>
+ <p>Writing of crash dumps is significantly faster.</p>
+ <p>Maps are now included in crash dumps.</p>
+ <p>Constants terms would only be shown in one process,
+ while other processes referencing the same constant term
+ would show a marker for incomplete heap. </p>
+ <p>
+ Own Id: OTP-14685 Aux Id: OTP-14611, OTP-14603, OTP-14595 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Binaries and some other data in crash dumps are now
+ encoded in base64 (instead of in hex), which will reduce
+ the size of crash dumps.</p>
+ <p>A few bugs in the handling of sub binaries in
+ <c>crashdump_viewer</c> have been fixed.</p>
+ <p>
+ Own Id: OTP-14686</p>
+ </item>
+ <item>
+ <p>
+ In order to allow future improvements, Crashdump Viewer
+ now checks the version tag of the crashdump to see that
+ it is a known format. If the crashdump version is newer
+ than Crashdump Viewer is prepared to read, then an
+ information dialog is displayed before Crashdump Viewer
+ terminates.</p>
+ <p>
+ If an incomplete process heap is discovered in a
+ crashdump, Crashdump Viewer will now display a warning
+ for this, similar to the warning displayed when a
+ crashdump is truncated. Incomplete heaps can occur if for
+ instance the literals are not included, which is the case
+ for all dumps prior to OTP-20.2.</p>
+ <p>
+ Own Id: OTP-14755</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 2.5</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/observer/doc/src/part_notes.xml b/lib/observer/doc/src/part_notes.xml
deleted file mode 100644
index ba15c39cda..0000000000
--- a/lib/observer/doc/src/part_notes.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Observer Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>OBSERVER</em> application contains tools for tracing
- and investigation of distributed systems.</p>
- <p>For information about older versions, see
- <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/observer/doc/src/part_notes_history.xml b/lib/observer/doc/src/part_notes_history.xml
deleted file mode 100644
index e60210924c..0000000000
--- a/lib/observer/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2006</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>Observer Release Notes History</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>OBSERVER</em> application contains tools for tracing
- and investigation of distributed systems.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/observer/include/etop.hrl b/lib/observer/include/etop.hrl
index 002937e522..f8d370450b 100644
--- a/lib/observer/include/etop.hrl
+++ b/lib/observer/include/etop.hrl
@@ -18,4 +18,4 @@
%% %CopyrightEnd%
%%
--include("../../runtime_tools/include/observer_backend.hrl").
+-include_lib("runtime_tools/include/observer_backend.hrl").
diff --git a/lib/observer/src/cdv_bin_cb.erl b/lib/observer/src/cdv_bin_cb.erl
index 5502869973..a4a542297c 100644
--- a/lib/observer/src/cdv_bin_cb.erl
+++ b/lib/observer/src/cdv_bin_cb.erl
@@ -71,6 +71,8 @@ hex_binary_fun(Bin) ->
plain_html(io_lib:format("~s",[S]))
end.
+format_hex(<<>>,_) ->
+ [];
format_hex(<<B1:4,B2:4>>,_) ->
[integer_to_list(B1,16),integer_to_list(B2,16)];
format_hex(<<B1:4,B2:4,Bin/binary>>,0) ->
diff --git a/lib/observer/src/cdv_detail_wx.erl b/lib/observer/src/cdv_detail_wx.erl
index f6d282638a..6c4739042b 100644
--- a/lib/observer/src/cdv_detail_wx.erl
+++ b/lib/observer/src/cdv_detail_wx.erl
@@ -48,6 +48,7 @@ init([Id, Data, ParentFrame, Callback, App, Parent]) ->
display_progress(ParentFrame,App),
case Callback:get_details(Id, Data) of
{ok,Details} ->
+ display_progress_pulse(Callback,Id),
init(Id,ParentFrame,Callback,App,Parent,Details);
{yes_no, Info, Fun} ->
destroy_progress(App),
@@ -69,8 +70,16 @@ display_progress(ParentFrame,cdv) ->
"Reading data");
display_progress(_,_) ->
ok.
+
+%% Display pulse while creating process detail page with much data
+display_progress_pulse(cdv_proc_cb,Pid) ->
+ observer_lib:report_progress({ok,"Displaying data for "++Pid}),
+ observer_lib:report_progress({ok,start_pulse});
+display_progress_pulse(_,_) ->
+ ok.
+
destroy_progress(cdv) ->
- observer_lib:destroy_progress_dialog();
+ observer_lib:sync_destroy_progress_dialog();
destroy_progress(_) ->
ok.
diff --git a/lib/observer/src/cdv_info_wx.erl b/lib/observer/src/cdv_info_wx.erl
index 7e416dd11a..07c28610e2 100644
--- a/lib/observer/src/cdv_info_wx.erl
+++ b/lib/observer/src/cdv_info_wx.erl
@@ -95,6 +95,10 @@ handle_cast(Msg, State) ->
io:format("~p~p: Unhandled cast ~tp~n",[?MODULE, ?LINE, Msg]),
{noreply, State}.
+handle_event(#wx{obj=MoreEntry,event=#wxMouse{type=left_down},userData={more,More}}, State) ->
+ observer_lib:add_scroll_entries(MoreEntry,More),
+ {noreply, State};
+
handle_event(#wx{event=#wxMouse{type=left_down},userData=Target}, State) ->
cdv_virtual_list_wx:start_detail_win(Target),
{noreply, State};
diff --git a/lib/observer/src/cdv_port_cb.erl b/lib/observer/src/cdv_port_cb.erl
index b5cbe8132d..6bb8f07a74 100644
--- a/lib/observer/src/cdv_port_cb.erl
+++ b/lib/observer/src/cdv_port_cb.erl
@@ -34,7 +34,8 @@
-define(COL_CONN, ?COL_ID+1).
-define(COL_NAME, ?COL_CONN+1).
-define(COL_CTRL, ?COL_NAME+1).
--define(COL_SLOT, ?COL_CTRL+1).
+-define(COL_QUEUE, ?COL_CTRL+1).
+-define(COL_SLOT, ?COL_QUEUE+1).
@@ -44,6 +45,7 @@ col_to_elem(?COL_ID) -> #port.id;
col_to_elem(?COL_CONN) -> #port.connected;
col_to_elem(?COL_NAME) -> #port.name;
col_to_elem(?COL_CTRL) -> #port.controls;
+col_to_elem(?COL_QUEUE) -> #port.queue;
col_to_elem(?COL_SLOT) -> #port.slot.
col_spec() ->
@@ -51,6 +53,7 @@ col_spec() ->
{"Connected", ?wxLIST_FORMAT_LEFT, 120},
{"Name", ?wxLIST_FORMAT_LEFT, 150},
{"Controls", ?wxLIST_FORMAT_LEFT, 200},
+ {"Queue", ?wxLIST_FORMAT_RIGHT, 100},
{"Slot", ?wxLIST_FORMAT_RIGHT, 50}].
get_info(_) ->
@@ -96,9 +99,17 @@ format(D) ->
info_fields() ->
[{"Overview",
[{"Name", name},
+ {"State", state},
+ {"Task Flags", task_flags},
{"Connected", {click,connected}},
{"Slot", slot},
- {"Controls", controls}]},
+ {"Controls", controls},
+ {"Input bytes", input},
+ {"Output bytes", output},
+ {"Queue bytes", queue},
+ {"Port data", port_data}]},
{scroll_boxes,
[{"Links",1,{click,links}},
- {"Monitors",1,{click,monitors}}]}].
+ {"Monitors",1,{click,monitors}},
+ {"Suspended",1,{click,suspended}}
+ ]}].
diff --git a/lib/observer/src/cdv_proc_cb.erl b/lib/observer/src/cdv_proc_cb.erl
index f10650bbb7..0ea23dd7cb 100644
--- a/lib/observer/src/cdv_proc_cb.erl
+++ b/lib/observer/src/cdv_proc_cb.erl
@@ -149,6 +149,10 @@ info_fields() ->
{"Old Heap", old_heap},
{"Heap Unused", heap_unused},
{"Old Heap Unused", old_heap_unused},
+ {"Binary vheap", bin_vheap},
+ {"Old Binary vheap", old_bin_vheap},
+ {"Binary vheap unused", bin_vheap_unused},
+ {"Old Binary vheap unused", old_bin_vheap_unused},
{"Number of Heap Fragements", num_heap_frag},
{"Heap Fragment Data",heap_frag_data},
{"New Heap Start", new_heap_start},
diff --git a/lib/observer/src/cdv_sched_cb.erl b/lib/observer/src/cdv_sched_cb.erl
index 192aaf31a7..d2696a276f 100644
--- a/lib/observer/src/cdv_sched_cb.erl
+++ b/lib/observer/src/cdv_sched_cb.erl
@@ -31,7 +31,8 @@
%% Columns
-define(COL_ID, 0).
--define(COL_PROC, ?COL_ID+1).
+-define(COL_TYPE, ?COL_ID+1).
+-define(COL_PROC, ?COL_TYPE+1).
-define(COL_PORT, ?COL_PROC+1).
-define(COL_RQL, ?COL_PORT+1).
-define(COL_PQL, ?COL_RQL+1).
@@ -39,6 +40,7 @@
%% Callbacks for cdv_virtual_list_wx
col_to_elem(id) -> col_to_elem(?COL_ID);
col_to_elem(?COL_ID) -> #sched.name;
+col_to_elem(?COL_TYPE) -> #sched.type;
col_to_elem(?COL_PROC) -> #sched.process;
col_to_elem(?COL_PORT) -> #sched.port;
col_to_elem(?COL_RQL) -> #sched.run_q;
@@ -46,6 +48,7 @@ col_to_elem(?COL_PQL) -> #sched.port_q.
col_spec() ->
[{"Id", ?wxLIST_FORMAT_RIGHT, 50},
+ {"Type", ?wxLIST_FORMAT_CENTER, 100},
{"Current Process", ?wxLIST_FORMAT_CENTER, 130},
{"Current Port", ?wxLIST_FORMAT_CENTER, 130},
{"Run Queue Length", ?wxLIST_FORMAT_RIGHT, 180},
@@ -73,7 +76,8 @@ detail_pages() ->
[{"Scheduler Information", fun init_gen_page/2}].
init_gen_page(Parent, Info0) ->
- Fields = info_fields(),
+ Type = proplists:get_value(type, Info0),
+ Fields = info_fields(Type),
Details = proplists:get_value(details, Info0),
Info = if is_map(Details) -> Info0 ++ maps:to_list(Details);
true -> Info0
@@ -81,15 +85,16 @@ init_gen_page(Parent, Info0) ->
cdv_info_wx:start_link(Parent,{Fields,Info,[]}).
%%% Internal
-info_fields() ->
+info_fields(Type) ->
[{"Scheduler Overview",
[{"Id", id},
+ {"Type", type},
{"Current Process",process},
{"Current Port", port},
{"Sleep Info Flags", sleep_info},
{"Sleep Aux Work", sleep_aux}
]},
- {"Run Queues",
+ {run_queues_header(Type),
[{"Flags", runq_flags},
{"Priority Max Length", runq_max},
{"Priority High Length", runq_high},
@@ -116,3 +121,8 @@ info_fields() ->
{" ", {currp_stack, 11}}
]}
].
+
+run_queues_header(normal) ->
+ "Run Queues";
+run_queues_header(DirtyX) ->
+ "Run Queues (common for all '" ++ atom_to_list(DirtyX) ++ "' schedulers)".
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 95e12887cd..d2a175d52d 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -26,10 +26,25 @@
%% Tables
%% ------
%% cdv_dump_index_table: This table holds all tags read from the
-%% crashdump. Each tag indicates where the information about a
-%% specific item starts. The table entry for a tag includes the start
-%% position for this item-information. In a crash dump file, all tags
-%% start with a "=" at the beginning of a line.
+%% crashdump, except the 'binary' tag. Each tag indicates where the
+%% information about a specific item starts. The table entry for a
+%% tag includes the start position for this item-information. In a
+%% crash dump file, all tags start with a "=" at the beginning of a
+%% line.
+%%
+%% cdv_binary_index_table: This table holds all 'binary' tags. The hex
+%% address for each binary is converted to its integer value before
+%% storing Address -> Start Position in this table. The hex value of
+%% the address is never used for lookup.
+%%
+%% cdv_reg_proc_table: This table holds mappings between pid and
+%% registered name. This is used for timers and monitors.
+%%
+%% cdv_heap_file_chars: For each 'proc_heap' and 'literals' tag, this
+%% table contains the number of characters to read from the crash dump
+%% file. This is used for giving an indication in percent of the
+%% progress when parsing this data.
+%%
%%
%% Process state
%% -------------
@@ -73,6 +88,9 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
+%% Test support
+-export([get_dump_versions/0]).
+
%% Debug support
-export([debug/1,stop_debug/0]).
@@ -87,7 +105,10 @@
% line_head/1 function can return
-define(not_available,"N/A").
-define(binary_size_progress_limit,10000).
+-define(max_dump_version,[0,5]).
+%% The value of the next define must be divisible by 4.
+-define(base64_chunk_size, (4*256)).
%% All possible tags - use macros in order to avoid misspelling in the code
-define(abort,abort).
@@ -95,6 +116,10 @@
-define(allocator,allocator).
-define(atoms,atoms).
-define(binary,binary).
+-define(dirty_cpu_scheduler,dirty_cpu_scheduler).
+-define(dirty_cpu_run_queue,dirty_cpu_run_queue).
+-define(dirty_io_scheduler,dirty_io_scheduler).
+-define(dirty_io_run_queue,dirty_io_run_queue).
-define(ende,ende).
-define(erl_crash_dump,erl_crash_dump).
-define(ets,ets).
@@ -104,6 +129,7 @@
-define(index_table,index_table).
-define(instr_data,instr_data).
-define(internal_ets,internal_ets).
+-define(literals,literals).
-define(loaded_modules,loaded_modules).
-define(memory,memory).
-define(memory_map,memory_map).
@@ -125,6 +151,7 @@
-record(state,{file,dump_vsn,wordsize=4,num_atoms="unknown"}).
+-record(dec_opts, {bin_addr_adj=0,base64=true}).
%%%-----------------------------------------------------------------
%%% Debugging
@@ -293,6 +320,11 @@ port(Id) ->
expand_binary(Pos) ->
call({expand_binary,Pos}).
+%%%-----------------------------------------------------------------
+%%% For testing only - called from crashdump_viewer_SUITE
+get_dump_versions() ->
+ call(get_dump_versions).
+
%%====================================================================
%% Server functions
%%====================================================================
@@ -342,10 +374,12 @@ handle_call(general_info,_From,State=#state{file=File}) ->
ets:insert(cdv_reg_proc_table,
{cdv_dump_node_name,GenInfo#general_info.node_name}),
{reply,{ok,GenInfo,TW},State#state{wordsize=WS, num_atoms=NumAtoms}};
-handle_call({expand_binary,{Offset,Size,Pos}},_From,State=#state{file=File}) ->
+handle_call({expand_binary,{Offset,Size,Pos}},_From,
+ #state{file=File,dump_vsn=DumpVsn}=State) ->
Fd = open(File),
pos_bof(Fd,Pos),
- {Bin,_Line} = get_binary(Offset,Size,bytes(Fd)),
+ DecodeOpts = get_decode_opts(DumpVsn),
+ {Bin,_Line} = get_binary(Offset,Size,bytes(Fd),DecodeOpts),
close(Fd),
{reply,{ok,Bin},State};
handle_call(procs_summary,_From,State=#state{file=File,wordsize=WS}) ->
@@ -418,9 +452,11 @@ handle_call(loaded_mods,_From,State=#state{file=File}) ->
TW = truncated_warning([?mod]),
{_CC,_OC,Mods} = loaded_mods(File),
{reply,{ok,Mods,TW},State};
-handle_call({loaded_mod_details,Mod},_From,State=#state{file=File}) ->
+handle_call({loaded_mod_details,Mod},_From,
+ #state{dump_vsn=DumpVsn,file=File}=State) ->
TW = truncated_warning([{?mod,Mod}]),
- ModInfo = get_loaded_mod_details(File,Mod),
+ DecodeOpts = get_decode_opts(DumpVsn),
+ ModInfo = get_loaded_mod_details(File,Mod,DecodeOpts),
{reply,{ok,ModInfo,TW},State};
handle_call(funs,_From,State=#state{file=File}) ->
TW = truncated_warning([?fu]),
@@ -454,8 +490,9 @@ handle_call(index_tables,_From,State=#state{file=File}) ->
handle_call(schedulers,_From,State=#state{file=File}) ->
Schedulers=schedulers(File),
TW = truncated_warning([?scheduler]),
- {reply,{ok,Schedulers,TW},State}.
-
+ {reply,{ok,Schedulers,TW},State};
+handle_call(get_dump_versions,_From,State=#state{dump_vsn=DumpVsn}) ->
+ {reply,{ok,{?max_dump_version,DumpVsn}},State}.
%%--------------------------------------------------------------------
@@ -780,11 +817,12 @@ parse_vsn_str(Str,WS) ->
%%%-----------------------------------------------------------------
-%%% Traverse crash dump and insert index in table for each heading
-%%%
-%%% Progress is reported during the time and MUST be checked with
-%%% crashdump_viewer:get_progress/0 until it returns {ok,done}.
+%%% Traverse crash dump and insert index in table for each heading.
+%%% Progress is reported during the time.
do_read_file(File) ->
+ erase(?literals), %Clear literal cache.
+ put(truncated,false), %Not truncated (yet).
+ erase(truncated_reason), %Not truncated (yet).
case file:read_file_info(File) of
{ok,#file_info{type=regular,
access=FileA,
@@ -796,17 +834,21 @@ do_read_file(File) ->
{Tag,Id,Rest,N1} = tag(Fd,TagAndRest,1),
case Tag of
?erl_crash_dump ->
- reset_tables(),
- insert_index(Tag,Id,N1+1),
- put_last_tag(Tag,""),
- DumpVsn = [list_to_integer(L) ||
- L<-string:tokens(Id,".")],
- AddrAdj = get_bin_addr_adj(DumpVsn),
- indexify(Fd,AddrAdj,Rest,N1),
- end_progress(),
- check_if_truncated(),
- close(Fd),
- {ok,DumpVsn};
+ case check_dump_version(Id) of
+ {ok,DumpVsn} ->
+ reset_tables(),
+ insert_index(Tag,Id,N1+1),
+ put_last_tag(Tag,""),
+ DecodeOpts = get_decode_opts(DumpVsn),
+ indexify(Fd,DecodeOpts,Rest,N1),
+ end_progress(),
+ check_if_truncated(),
+ close(Fd),
+ {ok,DumpVsn};
+ Error ->
+ close(Fd),
+ Error
+ end;
_Other ->
R = io_lib:format(
"~ts is not an Erlang crash dump~n",
@@ -834,7 +876,19 @@ do_read_file(File) ->
{error,R}
end.
-indexify(Fd,AddrAdj,Bin,N) ->
+check_dump_version(Vsn) ->
+ DumpVsn = [list_to_integer(L) || L<-string:tokens(Vsn,".")],
+ if DumpVsn > ?max_dump_version ->
+ Info =
+ "This Crashdump Viewer is too old for the given "
+ "Erlang crash dump. Please use a newer version of "
+ "Crashdump Viewer.",
+ {error,Info};
+ true ->
+ {ok,DumpVsn}
+ end.
+
+indexify(Fd,DecodeOpts,Bin,N) ->
case binary:match(Bin,<<"\n=">>) of
{Start,Len} ->
Pos = Start+Len,
@@ -847,7 +901,7 @@ indexify(Fd,AddrAdj,Bin,N) ->
%% order to minimize lookup time. Key is the
%% translated address.
{HexAddr,_} = get_hex(Id),
- Addr = HexAddr bor AddrAdj,
+ Addr = HexAddr bor DecodeOpts#dec_opts.bin_addr_adj,
insert_binary_index(Addr,NewPos);
_ ->
insert_index(Tag,Id,NewPos)
@@ -856,9 +910,22 @@ indexify(Fd,AddrAdj,Bin,N) ->
{?proc_heap,LastId} ->
[{_,LastPos}] = lookup_index(?proc_heap,LastId),
ets:insert(cdv_heap_file_chars,{LastId,N+Start+1-LastPos});
+ {?literals,[]} ->
+ case get(truncated_reason) of
+ undefined ->
+ [{_,LastPos}] = lookup_index(?literals,[]),
+ ets:insert(cdv_heap_file_chars,
+ {literals,N+Start+1-LastPos});
+ _ ->
+ %% Literals are truncated. Make sure we never
+ %% attempt to read in the literals. (Heaps that
+ %% references literals will show markers for
+ %% incomplete heaps, but will otherwise work.)
+ delete_index(?literals, [])
+ end;
_ -> ok
end,
- indexify(Fd,AddrAdj,Rest,N1);
+ indexify(Fd,DecodeOpts,Rest,N1);
nomatch ->
case progress_read(Fd) of
{ok,Chunk0} when is_binary(Chunk0) ->
@@ -869,7 +936,7 @@ indexify(Fd,AddrAdj,Bin,N) ->
_ ->
{Chunk0,N+byte_size(Bin)}
end,
- indexify(Fd,AddrAdj,Chunk,N1);
+ indexify(Fd,DecodeOpts,Chunk,N1);
eof ->
eof
end
@@ -908,6 +975,7 @@ check_if_truncated() ->
find_truncated_proc({Tag,_Id}) when Tag==?atoms;
Tag==?binary;
Tag==?instr_data;
+ Tag==?literals;
Tag==?memory_status;
Tag==?memory_map ->
put(truncated_proc,false);
@@ -1065,7 +1133,7 @@ get_proc_details(File,Pid,WS,DumpVsn) ->
{{Stack,MsgQ,Dict},TW} =
case truncated_warning([{?proc,Pid}]) of
[] ->
- {expand_memory(Fd,Pid,DumpVsn),[]};
+ expand_memory(Fd,Pid,DumpVsn);
TW0 ->
{{[],[],[]},TW0}
end,
@@ -1158,6 +1226,18 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) ->
"OldHeap unused" ->
Bytes = list_to_integer(bytes(Fd))*WS,
get_procinfo(Fd,Fun,Proc#proc{old_heap_unused=Bytes},WS);
+ "BinVHeap" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{bin_vheap=Bytes},WS);
+ "OldBinVHeap" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{old_bin_vheap=Bytes},WS);
+ "BinVHeap unused" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{bin_vheap_unused=Bytes},WS);
+ "OldBinVHeap unused" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{old_bin_vheap_unused=Bytes},WS);
"New heap start" ->
get_procinfo(Fd,Fun,Proc#proc{new_heap_start=bytes(Fd)},WS);
"New heap top" ->
@@ -1176,7 +1256,7 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) ->
"Last calls" ->
get_procinfo(Fd,Fun,Proc#proc{last_calls=get_last_calls(Fd)},WS);
"Link list" ->
- {Links,Monitors,MonitoredBy} = parse_link_list(bytes(Fd),[],[],[]),
+ {Links,Monitors,MonitoredBy} = get_link_list(Fd),
get_procinfo(Fd,Fun,Proc#proc{links=Links,
monitors=Monitors,
mon_by=MonitoredBy},WS);
@@ -1258,86 +1338,64 @@ get_last_calls(Fd,<<>>,Acc,Lines) ->
lists:reverse(Lines,[byte_list_to_string(lists:reverse(Acc))])
end.
-parse_link_list([SB|Str],Links,Monitors,MonitoredBy) when SB==$[; SB==$] ->
- parse_link_list(Str,Links,Monitors,MonitoredBy);
-parse_link_list("#Port"++_=Str,Links,Monitors,MonitoredBy) ->
- {Link,Rest} = parse_port(Str),
- parse_link_list(Rest,[Link|Links],Monitors,MonitoredBy);
-parse_link_list("<"++_=Str,Links,Monitors,MonitoredBy) ->
- {Link,Rest} = parse_pid(Str),
- parse_link_list(Rest,[Link|Links],Monitors,MonitoredBy);
-parse_link_list("{to,"++Str,Links,Monitors,MonitoredBy) ->
- {Mon,Rest} = parse_monitor(Str),
- parse_link_list(Rest,Links,[Mon|Monitors],MonitoredBy);
-parse_link_list("{from,"++Str,Links,Monitors,MonitoredBy) ->
- {Mon,Rest} = parse_monitor(Str),
- parse_link_list(Rest,Links,Monitors,[Mon|MonitoredBy]);
-parse_link_list(", "++Rest,Links,Monitors,MonitoredBy) ->
- parse_link_list(Rest,Links,Monitors,MonitoredBy);
-parse_link_list([],Links,Monitors,MonitoredBy) ->
- {lists:reverse(Links),lists:reverse(Monitors),lists:reverse(MonitoredBy)};
-parse_link_list(Unexpected,Links,Monitors,MonitoredBy) ->
- io:format("WARNING: found unexpected data in link list:~n~ts~n",[Unexpected]),
- parse_link_list([],Links,Monitors,MonitoredBy).
-
-
-parse_port(Str) ->
- {Port,Rest} = parse_link(Str,[]),
- {{Port,Port},Rest}.
-
-parse_pid(Str) ->
- {Pid,Rest} = parse_link(Str,[]),
- {{Pid,Pid},Rest}.
-
-parse_monitor("{"++Str) ->
- %% Named process
- {Name,Node,Rest1} = parse_name_node(Str,[]),
- Pid = get_pid_from_name(Name,Node),
- case parse_link(string:strip(Rest1,left,$,),[]) of
- {Ref,"}"++Rest2} ->
- %% Bug in break.c - prints an extra "}" for remote
- %% nodes... thus the strip
- {{Pid,"{"++Name++","++Node++"} ("++Ref++")"},
- string:strip(Rest2,left,$})};
- {Ref,[]} ->
- {{Pid,"{"++Name++","++Node++"} ("++Ref++")"},[]}
- end;
-parse_monitor(Str) ->
- case parse_link(Str,[]) of
- {Pid,","++Rest1} ->
- case parse_link(Rest1,[]) of
- {Ref,"}"++Rest2} ->
- {{Pid,Pid++" ("++Ref++")"},Rest2};
- {Ref,[]} ->
- {{Pid,Pid++" ("++Ref++")"},[]}
- end;
- {Pid,[]} ->
- {{Pid,Pid++" (unknown_ref)"},[]}
+get_link_list(Fd) ->
+ case get_chunk(Fd) of
+ {ok,<<"[",Bin/binary>>} ->
+ #{links:=Links,
+ mons:=Monitors,
+ mon_by:=MonitoredBy} =
+ get_link_list(Fd,Bin,#{links=>[],mons=>[],mon_by=>[]}),
+ {lists:reverse(Links),
+ lists:reverse(Monitors),
+ lists:reverse(MonitoredBy)};
+ eof ->
+ {[],[],[]}
+ end.
+
+get_link_list(Fd,<<NL:8,_/binary>>=Bin,Acc) when NL=:=$\r; NL=:=$\n->
+ skip(Fd,Bin),
+ Acc;
+get_link_list(Fd,Bin,Acc) ->
+ case binary:split(Bin,[<<", ">>,<<"]">>]) of
+ [Link,Rest] ->
+ get_link_list(Fd,Rest,get_link(Link,Acc));
+ [Incomplete] ->
+ case get_chunk(Fd) of
+ {ok,More} ->
+ get_link_list(Fd,<<Incomplete/binary,More/binary>>,Acc);
+ eof ->
+ Acc
+ end
end.
-parse_link(">"++Rest,Acc) ->
- {lists:reverse(Acc,">"),Rest};
-parse_link([H|T],Acc) ->
- parse_link(T,[H|Acc]);
-parse_link([],Acc) ->
- %% truncated
- {lists:reverse(Acc),[]}.
-
-parse_name_node(","++Rest,Name) ->
- parse_name_node(Rest,Name,[]);
-parse_name_node([H|T],Name) ->
- parse_name_node(T,[H|Name]);
-parse_name_node([],Name) ->
- %% truncated
- {lists:reverse(Name),[],[]}.
-
-parse_name_node("}"++Rest,Name,Node) ->
- {lists:reverse(Name),lists:reverse(Node),Rest};
-parse_name_node([H|T],Name,Node) ->
- parse_name_node(T,Name,[H|Node]);
-parse_name_node([],Name,Node) ->
- %% truncated
- {lists:reverse(Name),lists:reverse(Node),[]}.
+get_link(<<"#Port",_/binary>>=PortBin,#{links:=Links}=Acc) ->
+ PortStr = binary_to_list(PortBin),
+ Acc#{links=>[{PortStr,PortStr}|Links]};
+get_link(<<"<",_/binary>>=PidBin,#{links:=Links}=Acc) ->
+ PidStr = binary_to_list(PidBin),
+ Acc#{links=>[{PidStr,PidStr}|Links]};
+get_link(<<"{to,",Bin/binary>>,#{mons:=Monitors}=Acc) ->
+ Acc#{mons=>[parse_monitor(Bin)|Monitors]};
+get_link(<<"{from,",Bin/binary>>,#{mon_by:=MonitoredBy}=Acc) ->
+ Acc#{mon_by=>[parse_monitor(Bin)|MonitoredBy]};
+get_link(Unexpected,Acc) ->
+ io:format("WARNING: found unexpected data in link list:~n~ts~n",[Unexpected]),
+ Acc.
+
+parse_monitor(MonBin) ->
+ case binary:split(MonBin,[<<",">>,<<"{">>,<<"}">>],[global]) of
+ [PidBin,RefBin,<<>>] ->
+ PidStr = binary_to_list(PidBin),
+ RefStr = binary_to_list(RefBin),
+ {PidStr,PidStr++" ("++RefStr++")"};
+ [<<>>,NameBin,NodeBin,<<>>,RefBin,<<>>] ->
+ %% Named process
+ NameStr = binary_to_list(NameBin),
+ NodeStr = binary_to_list(NodeBin),
+ PidStr = get_pid_from_name(NameStr,NodeStr),
+ RefStr = binary_to_list(RefBin),
+ {PidStr,"{"++NameStr++","++NodeStr++"} ("++RefStr++")"}
+ end.
get_pid_from_name(Name,Node) ->
case ets:lookup(cdv_reg_proc_table,cdv_dump_node_name) of
@@ -1384,50 +1442,85 @@ maybe_other_node2(Channel) ->
expand_memory(Fd,Pid,DumpVsn) ->
- BinAddrAdj = get_bin_addr_adj(DumpVsn),
+ DecodeOpts = get_decode_opts(DumpVsn),
put(fd,Fd),
- Dict = read_heap(Fd,Pid,BinAddrAdj,gb_trees:empty()),
- Expanded = {read_stack_dump(Fd,Pid,BinAddrAdj,Dict),
- read_messages(Fd,Pid,BinAddrAdj,Dict),
- read_dictionary(Fd,Pid,BinAddrAdj,Dict)},
+ Dict0 = case get(?literals) of
+ undefined ->
+ Literals = read_literals(Fd,DecodeOpts),
+ put(?literals,Literals),
+ put(fd,Fd),
+ Literals;
+ Literals ->
+ Literals
+ end,
+ Dict = read_heap(Fd,Pid,DecodeOpts,Dict0),
+ Expanded = {read_stack_dump(Fd,Pid,DecodeOpts,Dict),
+ read_messages(Fd,Pid,DecodeOpts,Dict),
+ read_dictionary(Fd,Pid,DecodeOpts,Dict)},
erase(fd),
- Expanded.
-
-%%%-----------------------------------------------------------------
-%%% This is a workaround for a bug in dump versions prior to 0.3:
-%%% Addresses were truncated to 32 bits. This could cause binaries to
-%%% get the same address as heap terms in the dump. To work around it
-%%% we always store binaries on very high addresses in the gb_tree.
-get_bin_addr_adj(DumpVsn) when DumpVsn < [0,3] ->
- 16#f bsl 64;
-get_bin_addr_adj(_) ->
- 0.
+ IncompleteWarning =
+ case erase(incomplete_heap) of
+ undefined ->
+ [];
+ true ->
+ ["WARNING: This process has an incomplete heap. "
+ "Some information might be missing."]
+ end,
+ {Expanded,IncompleteWarning}.
+
+read_literals(Fd,DecodeOpts) ->
+ case lookup_index(?literals,[]) of
+ [{_,Start}] ->
+ [{_,Chars}] = ets:lookup(cdv_heap_file_chars,literals),
+ init_progress("Reading literals",Chars),
+ pos_bof(Fd,Start),
+ read_heap(DecodeOpts,gb_trees:empty());
+ [] ->
+ gb_trees:empty()
+ end.
+
+get_decode_opts(DumpVsn) ->
+ BinAddrAdj = if
+ DumpVsn < [0,3] ->
+ %% This is a workaround for a bug in dump
+ %% versions prior to 0.3: Addresses were
+ %% truncated to 32 bits. This could cause
+ %% binaries to get the same address as heap
+ %% terms in the dump. To work around it we
+ %% always store binaries on very high
+ %% addresses in the gb_tree.
+ 16#f bsl 64;
+ true ->
+ 0
+ end,
+ Base64 = DumpVsn >= [0,5],
+ #dec_opts{bin_addr_adj=BinAddrAdj,base64=Base64}.
%%%
%%% Read top level section.
%%%
-read_stack_dump(Fd,Pid,BinAddrAdj,Dict) ->
+read_stack_dump(Fd,Pid,DecodeOpts,Dict) ->
case lookup_index(?proc_stack,Pid) of
[{_,Start}] ->
pos_bof(Fd,Start),
- read_stack_dump1(Fd,BinAddrAdj,Dict,[]);
+ read_stack_dump1(Fd,DecodeOpts,Dict,[]);
[] ->
[]
end.
-read_stack_dump1(Fd,BinAddrAdj,Dict,Acc) ->
+read_stack_dump1(Fd,DecodeOpts,Dict,Acc) ->
%% This function is never called if the dump is truncated in {?proc_heap,Pid}
case bytes(Fd) of
"=" ++ _next_tag ->
lists:reverse(Acc);
Line ->
- Stack = parse_top(Line,BinAddrAdj,Dict),
- read_stack_dump1(Fd,BinAddrAdj,Dict,[Stack|Acc])
+ Stack = parse_top(Line,DecodeOpts,Dict),
+ read_stack_dump1(Fd,DecodeOpts,Dict,[Stack|Acc])
end.
-parse_top(Line0, BinAddrAdj, D) ->
+parse_top(Line0, DecodeOpts, D) ->
{Label,Line1} = get_label(Line0),
- {Term,Line,D} = parse_term(Line1, BinAddrAdj, D),
+ {Term,Line,D} = parse_term(Line1, DecodeOpts, D),
[] = skip_blanks(Line),
{Label,Term}.
@@ -1435,27 +1528,27 @@ parse_top(Line0, BinAddrAdj, D) ->
%%% Read message queue.
%%%
-read_messages(Fd,Pid,BinAddrAdj,Dict) ->
+read_messages(Fd,Pid,DecodeOpts,Dict) ->
case lookup_index(?proc_messages,Pid) of
[{_,Start}] ->
pos_bof(Fd,Start),
- read_messages1(Fd,BinAddrAdj,Dict,[]);
+ read_messages1(Fd,DecodeOpts,Dict,[]);
[] ->
[]
end.
-read_messages1(Fd,BinAddrAdj,Dict,Acc) ->
+read_messages1(Fd,DecodeOpts,Dict,Acc) ->
%% This function is never called if the dump is truncated in {?proc_heap,Pid}
case bytes(Fd) of
"=" ++ _next_tag ->
lists:reverse(Acc);
Line ->
- Msg = parse_message(Line,BinAddrAdj,Dict),
- read_messages1(Fd,BinAddrAdj,Dict,[Msg|Acc])
+ Msg = parse_message(Line,DecodeOpts,Dict),
+ read_messages1(Fd,DecodeOpts,Dict,[Msg|Acc])
end.
-parse_message(Line0, BinAddrAdj, D) ->
- {Msg,":"++Line1,_} = parse_term(Line0, BinAddrAdj, D),
- {Token,Line,_} = parse_term(Line1, BinAddrAdj, D),
+parse_message(Line0, DecodeOpts, D) ->
+ {Msg,":"++Line1,_} = parse_term(Line0, DecodeOpts, D),
+ {Token,Line,_} = parse_term(Line1, DecodeOpts, D),
[] = skip_blanks(Line),
{Msg,Token}.
@@ -1463,26 +1556,26 @@ parse_message(Line0, BinAddrAdj, D) ->
%%% Read process dictionary
%%%
-read_dictionary(Fd,Pid,BinAddrAdj,Dict) ->
+read_dictionary(Fd,Pid,DecodeOpts,Dict) ->
case lookup_index(?proc_dictionary,Pid) of
[{_,Start}] ->
pos_bof(Fd,Start),
- read_dictionary1(Fd,BinAddrAdj,Dict,[]);
+ read_dictionary1(Fd,DecodeOpts,Dict,[]);
[] ->
[]
end.
-read_dictionary1(Fd,BinAddrAdj,Dict,Acc) ->
+read_dictionary1(Fd,DecodeOpts,Dict,Acc) ->
%% This function is never called if the dump is truncated in {?proc_heap,Pid}
case bytes(Fd) of
"=" ++ _next_tag ->
lists:reverse(Acc);
Line ->
- Msg = parse_dictionary(Line,BinAddrAdj,Dict),
- read_dictionary1(Fd,BinAddrAdj,Dict,[Msg|Acc])
+ Msg = parse_dictionary(Line,DecodeOpts,Dict),
+ read_dictionary1(Fd,DecodeOpts,Dict,[Msg|Acc])
end.
-parse_dictionary(Line0, BinAddrAdj, D) ->
- {Entry,Line,_} = parse_term(Line0, BinAddrAdj, D),
+parse_dictionary(Line0, DecodeOpts, D) ->
+ {Entry,Line,_} = parse_term(Line0, DecodeOpts, D),
[] = skip_blanks(Line),
Entry.
@@ -1490,18 +1583,18 @@ parse_dictionary(Line0, BinAddrAdj, D) ->
%%% Read heap data.
%%%
-read_heap(Fd,Pid,BinAddrAdj,Dict0) ->
+read_heap(Fd,Pid,DecodeOpts,Dict0) ->
case lookup_index(?proc_heap,Pid) of
[{_,Pos}] ->
[{_,Chars}] = ets:lookup(cdv_heap_file_chars,Pid),
init_progress("Reading process heap",Chars),
pos_bof(Fd,Pos),
- read_heap(BinAddrAdj,Dict0);
+ read_heap(DecodeOpts,Dict0);
[] ->
Dict0
end.
-read_heap(BinAddrAdj,Dict0) ->
+read_heap(DecodeOpts,Dict0) ->
%% This function is never called if the dump is truncated in {?proc_heap,Pid}
case get(fd) of
end_of_heap ->
@@ -1515,14 +1608,14 @@ read_heap(BinAddrAdj,Dict0) ->
Dict0;
Line ->
update_progress(length(Line)+1),
- Dict = parse(Line,BinAddrAdj,Dict0),
- read_heap(BinAddrAdj,Dict)
+ Dict = parse(Line,DecodeOpts,Dict0),
+ read_heap(DecodeOpts,Dict)
end
end.
-parse(Line0, BinAddrAdj, Dict0) ->
+parse(Line0, DecodeOpts, Dict0) ->
{Addr,":"++Line1} = get_hex(Line0),
- {_Term,Line,Dict} = parse_heap_term(Line1, Addr, BinAddrAdj, Dict0),
+ {_Term,Line,Dict} = parse_heap_term(Line1, Addr, DecodeOpts, Dict0),
[] = skip_blanks(Line),
Dict.
@@ -1555,6 +1648,10 @@ port_to_tuple("#Port<"++Port) ->
get_portinfo(Fd,Port) ->
case line_head(Fd) of
+ "State" ->
+ get_portinfo(Fd,Port#port{state=bytes(Fd)});
+ "Task Flags" ->
+ get_portinfo(Fd,Port#port{task_flags=bytes(Fd)});
"Slot" ->
%% stored as integer so we can sort on it
get_portinfo(Fd,Port#port{slot=list_to_integer(bytes(Fd))});
@@ -1579,6 +1676,10 @@ get_portinfo(Fd,Port) ->
{Pid,Pid++" ("++Ref++")"}
end || Mon <- Monitors0],
get_portinfo(Fd,Port#port{monitors=Monitors});
+ "Suspended" ->
+ Pids = split_pid_list_no_space(bytes(Fd)),
+ Suspended = [{Pid,Pid} || Pid <- Pids],
+ get_portinfo(Fd,Port#port{suspended=Suspended});
"Port controls linked-in driver" ->
Str = lists:flatten(["Linked in driver: " | string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
@@ -1594,6 +1695,15 @@ get_portinfo(Fd,Port) ->
"Port is UNIX fd not opened by emulator" ->
Str = lists:flatten(["UNIX fd not opened by emulator: "| string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
+ "Input" ->
+ get_portinfo(Fd,Port#port{input=list_to_integer(bytes(Fd))});
+ "Output" ->
+ get_portinfo(Fd,Port#port{output=list_to_integer(bytes(Fd))});
+ "Queue" ->
+ get_portinfo(Fd,Port#port{queue=list_to_integer(bytes(Fd))});
+ "Port Data" ->
+ get_portinfo(Fd,Port#port{port_data=string(Fd)});
+
"=" ++ _next_tag ->
Port;
Other ->
@@ -1842,12 +1952,15 @@ get_nodeinfo(Fd,Nod) ->
%%-----------------------------------------------------------------
%% Page with details about one loaded modules
-get_loaded_mod_details(File,Mod) ->
+get_loaded_mod_details(File,Mod,DecodeOpts) ->
[{_,Start}] = lookup_index(?mod,Mod),
Fd = open(File),
pos_bof(Fd,Start),
InitLM = #loaded_mod{mod=Mod,old_size="No old code exists"},
- ModInfo = get_loaded_mod_info(Fd,InitLM,fun all_modinfo/3),
+ Fun = fun(F, LM, LineHead) ->
+ all_modinfo(F, LM, LineHead, DecodeOpts)
+ end,
+ ModInfo = get_loaded_mod_info(Fd,InitLM,Fun),
close(Fd),
ModInfo.
@@ -1905,59 +2018,48 @@ get_loaded_mod_info(Fd,LM,Fun) ->
main_modinfo(_Fd,LM,_LineHead) ->
LM.
-all_modinfo(Fd,LM,LineHead) ->
+all_modinfo(Fd,LM,LineHead,DecodeOpts) ->
case LineHead of
"Current attributes" ->
- Str = hex_to_str(bytes(Fd,"")),
+ Str = get_attribute(Fd, DecodeOpts),
LM#loaded_mod{current_attrib=Str};
"Current compilation info" ->
- Str = hex_to_str(bytes(Fd,"")),
+ Str = get_attribute(Fd, DecodeOpts),
LM#loaded_mod{current_comp_info=Str};
"Old attributes" ->
- Str = hex_to_str(bytes(Fd,"")),
+ Str = get_attribute(Fd, DecodeOpts),
LM#loaded_mod{old_attrib=Str};
"Old compilation info" ->
- Str = hex_to_str(bytes(Fd,"")),
+ Str = get_attribute(Fd, DecodeOpts),
LM#loaded_mod{old_comp_info=Str};
Other ->
unexpected(Fd,Other,"loaded modules info"),
LM
end.
-
-hex_to_str(Hex) ->
- Term = hex_to_term(Hex,[]),
+get_attribute(Fd, DecodeOpts) ->
+ Term = do_get_attribute(Fd, DecodeOpts),
io_lib:format("~tp~n",[Term]).
-hex_to_term([X,Y|Hex],Acc) ->
- MS = hex_to_dec([X]),
- LS = hex_to_dec([Y]),
- Z = 16*MS+LS,
- hex_to_term(Hex,[Z|Acc]);
-hex_to_term([],Acc) ->
- Bin = list_to_binary(lists:reverse(Acc)),
- case catch binary_to_term(Bin) of
- {'EXIT',_Reason} ->
- {"WARNING: The term is probably truncated!",
- "I can not do binary_to_term.",
- Bin};
- Term ->
- Term
- end;
-hex_to_term(Rest,Acc) ->
- {"WARNING: The term is probably truncated!",
- "I can not convert hex to term.",
- Rest,list_to_binary(lists:reverse(Acc))}.
-
-
-hex_to_dec("F") -> 15;
-hex_to_dec("E") -> 14;
-hex_to_dec("D") -> 13;
-hex_to_dec("C") -> 12;
-hex_to_dec("B") -> 11;
-hex_to_dec("A") -> 10;
-hex_to_dec(N) -> list_to_integer(N).
-
+do_get_attribute(Fd, DecodeOpts) ->
+ Bytes = bytes(Fd, ""),
+ try get_binary(Bytes, DecodeOpts) of
+ {Bin,_} ->
+ try binary_to_term(Bin) of
+ Term ->
+ Term
+ catch
+ _:_ ->
+ {"WARNING: The term is probably truncated!",
+ "I cannot do binary_to_term/1.",
+ Bin}
+ end
+ catch
+ _:_ ->
+ {"WARNING: The term is probably truncated!",
+ "I cannot convert to binary.",
+ Bytes}
+ end.
%%-----------------------------------------------------------------
%% Page with list of all funs
@@ -2432,73 +2534,142 @@ get_indextableinfo1(Fd,IndexTable) ->
%%-----------------------------------------------------------------
%% Page with scheduler table information
schedulers(File) ->
- case lookup_index(?scheduler) of
- [] ->
- [];
- Schedulers ->
- Fd = open(File),
- R = lists:map(fun({Name,Start}) ->
- get_schedulerinfo(Fd,Name,Start)
- end,
- Schedulers),
- close(Fd),
- R
- end.
+ Fd = open(File),
+
+ Schds0 = case lookup_index(?scheduler) of
+ [] ->
+ [];
+ Normals ->
+ [{Normals, #sched{type=normal}}]
+ end,
+ Schds1 = case lookup_index(?dirty_cpu_scheduler) of
+ [] ->
+ Schds0;
+ DirtyCpus ->
+ [{DirtyCpus, get_dirty_runqueue(Fd, ?dirty_cpu_run_queue)}
+ | Schds0]
+ end,
+ Schds2 = case lookup_index(?dirty_io_scheduler) of
+ [] ->
+ Schds1;
+ DirtyIos ->
+ [{DirtyIos, get_dirty_runqueue(Fd, ?dirty_io_run_queue)}
+ | Schds1]
+ end,
+
+ R = schedulers1(Fd, Schds2, []),
+ close(Fd),
+ R.
-get_schedulerinfo(Fd,Name,Start) ->
+schedulers1(_Fd, [], Acc) ->
+ Acc;
+schedulers1(Fd, [{Scheds,Sched0} | Tail], Acc0) ->
+ Acc1 = lists:foldl(fun({Name,Start}, AccIn) ->
+ [get_schedulerinfo(Fd,Name,Start,Sched0) | AccIn]
+ end,
+ Acc0,
+ Scheds),
+ schedulers1(Fd, Tail, Acc1).
+
+get_schedulerinfo(Fd,Name,Start,Sched0) ->
pos_bof(Fd,Start),
- get_schedulerinfo1(Fd,#sched{name=Name}).
+ get_schedulerinfo1(Fd,Sched0#sched{name=list_to_integer(Name)}).
-get_schedulerinfo1(Fd,Sched=#sched{details=Ds}) ->
+sched_type(?dirty_cpu_run_queue) -> dirty_cpu;
+sched_type(?dirty_io_run_queue) -> dirty_io.
+
+get_schedulerinfo1(Fd, Sched) ->
+ case get_schedulerinfo2(Fd, Sched) of
+ {more, Sched2} ->
+ get_schedulerinfo1(Fd, Sched2);
+ {done, Sched2} ->
+ Sched2
+ end.
+
+get_schedulerinfo2(Fd, Sched=#sched{details=Ds}) ->
case line_head(Fd) of
"Current Process" ->
- get_schedulerinfo1(Fd,Sched#sched{process=bytes(Fd, "None")});
+ {more, Sched#sched{process=bytes(Fd, "None")}};
"Current Port" ->
- get_schedulerinfo1(Fd,Sched#sched{port=bytes(Fd, "None")});
+ {more, Sched#sched{port=bytes(Fd, "None")}};
+
+ "Scheduler Sleep Info Flags" ->
+ {more, Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}}};
+ "Scheduler Sleep Info Aux Work" ->
+ {more, Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}}};
+
+ "Current Process State" ->
+ {more, Sched#sched{details=Ds#{currp_state=>bytes(Fd)}}};
+ "Current Process Internal State" ->
+ {more, Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}}};
+ "Current Process Program counter" ->
+ {more, Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}}};
+ "Current Process CP" ->
+ {more, Sched#sched{details=Ds#{currp_cp=>string(Fd)}}};
+ "Current Process Limited Stack Trace" ->
+ %% If there shall be last in scheduler information block
+ {done, Sched#sched{details=get_limited_stack(Fd, 0, Ds)}};
+
+ "=" ++ _next_tag ->
+ {done, Sched};
+
+ Other ->
+ case Sched#sched.type of
+ normal ->
+ get_runqueue_info2(Fd, Other, Sched);
+ _ ->
+ unexpected(Fd,Other,"dirty scheduler information"),
+ {done, Sched}
+ end
+ end.
+
+get_dirty_runqueue(Fd, Tag) ->
+ case lookup_index(Tag) of
+ [{_, Start}] ->
+ pos_bof(Fd,Start),
+ get_runqueue_info1(Fd,#sched{type=sched_type(Tag)});
+ [] ->
+ #sched{}
+ end.
+
+get_runqueue_info1(Fd, Sched) ->
+ case get_runqueue_info2(Fd, line_head(Fd), Sched) of
+ {more, Sched2} ->
+ get_runqueue_info1(Fd, Sched2);
+ {done, Sched2} ->
+ Sched2
+ end.
+
+get_runqueue_info2(Fd, LineHead, Sched=#sched{details=Ds}) ->
+ case LineHead of
"Run Queue Max Length" ->
RQMax = list_to_integer(bytes(Fd)),
RQ = RQMax + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}}};
"Run Queue High Length" ->
RQHigh = list_to_integer(bytes(Fd)),
RQ = RQHigh + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}}};
"Run Queue Normal Length" ->
RQNorm = list_to_integer(bytes(Fd)),
RQ = RQNorm + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}}};
"Run Queue Low Length" ->
RQLow = list_to_integer(bytes(Fd)),
RQ = RQLow + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}}};
"Run Queue Port Length" ->
RQ = list_to_integer(bytes(Fd)),
- get_schedulerinfo1(Fd,Sched#sched{port_q=RQ});
-
- "Scheduler Sleep Info Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}});
- "Scheduler Sleep Info Aux Work" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}});
+ {more, Sched#sched{port_q=RQ}};
"Run Queue Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}});
+ {more, Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}}};
- "Current Process State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_state=>bytes(Fd)}});
- "Current Process Internal State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}});
- "Current Process Program counter" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}});
- "Current Process CP" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_cp=>string(Fd)}});
- "Current Process Limited Stack Trace" ->
- %% If there shall be last in scheduler information block
- Sched#sched{details=get_limited_stack(Fd, 0, Ds)};
"=" ++ _next_tag ->
- Sched;
+ {done, Sched};
Other ->
unexpected(Fd,Other,"scheduler information"),
- Sched
+ {done, Sched}
end.
get_limited_stack(Fd, N, Ds) ->
@@ -2514,99 +2685,134 @@ get_limited_stack(Fd, N, Ds) ->
%%%-----------------------------------------------------------------
%%% Parse memory in crashdump version 0.1 and newer
%%%
-parse_heap_term([$l|Line0], Addr, BinAddrAdj, D0) -> %Cons cell.
- {H,"|"++Line1,D1} = parse_term(Line0, BinAddrAdj, D0),
- {T,Line,D2} = parse_term(Line1, BinAddrAdj, D1),
+parse_heap_term([$l|Line0], Addr, DecodeOpts, D0) -> %Cons cell.
+ {H,"|"++Line1,D1} = parse_term(Line0, DecodeOpts, D0),
+ {T,Line,D2} = parse_term(Line1, DecodeOpts, D1),
Term = [H|T],
D = gb_trees:insert(Addr, Term, D2),
{Term,Line,D};
-parse_heap_term([$t|Line0], Addr, BinAddrAdj, D) -> %Tuple
+parse_heap_term([$t|Line0], Addr, DecodeOpts, D) -> %Tuple
{N,":"++Line} = get_hex(Line0),
- parse_tuple(N, Line, Addr, BinAddrAdj, D, []);
-parse_heap_term([$F|Line0], Addr, _BinAddrAdj, D0) -> %Float
+ parse_tuple(N, Line, Addr, DecodeOpts, D, []);
+parse_heap_term([$F|Line0], Addr, _DecodeOpts, D0) -> %Float
{N,":"++Line1} = get_hex(Line0),
{Chars,Line} = get_chars(N, Line1),
Term = list_to_float(Chars),
D = gb_trees:insert(Addr, Term, D0),
{Term,Line,D};
-parse_heap_term("B16#"++Line0, Addr, _BinAddrAdj, D0) -> %Positive big number.
+parse_heap_term("B16#"++Line0, Addr, _DecodeOpts, D0) -> %Positive big number.
{Term,Line} = get_hex(Line0),
D = gb_trees:insert(Addr, Term, D0),
{Term,Line,D};
-parse_heap_term("B-16#"++Line0, Addr, _BinAddrAdj, D0) -> %Negative big number
+parse_heap_term("B-16#"++Line0, Addr, _DecodeOpts, D0) -> %Negative big number
{Term0,Line} = get_hex(Line0),
Term = -Term0,
D = gb_trees:insert(Addr, Term, D0),
{Term,Line,D};
-parse_heap_term("B"++Line0, Addr, _BinAddrAdj, D0) -> %Decimal big num
+parse_heap_term("B"++Line0, Addr, _DecodeOpts, D0) -> %Decimal big num
case string:to_integer(Line0) of
{Int,Line} when is_integer(Int) ->
D = gb_trees:insert(Addr, Int, D0),
{Int,Line,D}
end;
-parse_heap_term([$P|Line0], Addr, _BinAddrAdj, D0) -> % External Pid.
+parse_heap_term([$P|Line0], Addr, _DecodeOpts, D0) -> % External Pid.
{Pid0,Line} = get_id(Line0),
Pid = ['#CDVPid'|Pid0],
D = gb_trees:insert(Addr, Pid, D0),
{Pid,Line,D};
-parse_heap_term([$p|Line0], Addr, _BinAddrAdj, D0) -> % External Port.
+parse_heap_term([$p|Line0], Addr, _DecodeOpts, D0) -> % External Port.
{Port0,Line} = get_id(Line0),
Port = ['#CDVPort'|Port0],
D = gb_trees:insert(Addr, Port, D0),
{Port,Line,D};
-parse_heap_term("E"++Line0, Addr, _BinAddrAdj, D0) -> %Term encoded in external format.
- {Bin,Line} = get_binary(Line0),
+parse_heap_term("E"++Line0, Addr, DecodeOpts, D0) -> %Term encoded in external format.
+ {Bin,Line} = get_binary(Line0, DecodeOpts),
Term = binary_to_term(Bin),
D = gb_trees:insert(Addr, Term, D0),
{Term,Line,D};
-parse_heap_term("Yh"++Line0, Addr, _BinAddrAdj, D0) -> %Heap binary.
- {Term,Line} = get_binary(Line0),
+parse_heap_term("Yh"++Line0, Addr, DecodeOpts, D0) -> %Heap binary.
+ {Term,Line} = get_binary(Line0, DecodeOpts),
D = gb_trees:insert(Addr, Term, D0),
{Term,Line,D};
-parse_heap_term("Yc"++Line0, Addr, BinAddrAdj, D0) -> %Reference-counted binary.
+parse_heap_term("Yc"++Line0, Addr, DecodeOpts, D0) -> %Reference-counted binary.
{Binp0,":"++Line1} = get_hex(Line0),
{Offset,":"++Line2} = get_hex(Line1),
{Sz,Line} = get_hex(Line2),
- Binp = Binp0 bor BinAddrAdj,
- Term = case lookup_binary_index(Binp) of
- [{_,Start}] -> cdvbin(Offset,Sz,{'#CDVBin',Start});
- [] -> '#CDVNonexistingBinary'
- end,
- D = gb_trees:insert(Addr, Term, D0),
- {Term,Line,D};
-parse_heap_term("Ys"++Line0, Addr, BinAddrAdj, D0) -> %Sub binary.
+ Binp = Binp0 bor DecodeOpts#dec_opts.bin_addr_adj,
+ case lookup_binary_index(Binp) of
+ [{_,Start}] ->
+ SymbolicBin = {'#CDVBin',Start},
+ Term = cdvbin(Offset, Sz, SymbolicBin),
+ D1 = gb_trees:insert(Addr, Term, D0),
+ D = gb_trees:insert(Binp, SymbolicBin, D1),
+ {Term,Line,D};
+ [] ->
+ Term = '#CDVNonexistingBinary',
+ D1 = gb_trees:insert(Addr, Term, D0),
+ D = gb_trees:insert(Binp, Term, D1),
+ {Term,Line,D}
+ end;
+parse_heap_term("Ys"++Line0, Addr, DecodeOpts, D0) -> %Sub binary.
{Binp0,":"++Line1} = get_hex(Line0),
{Offset,":"++Line2} = get_hex(Line1),
- {Sz,Line} = get_hex(Line2),
- Binp = Binp0 bor BinAddrAdj,
- Term = case lookup_binary_index(Binp) of
- [{_,Start}] -> cdvbin(Offset,Sz,{'#CDVBin',Start});
- [] ->
- %% Might it be on the heap?
- case gb_trees:lookup(Binp, D0) of
- {value,Bin} -> cdvbin(Offset,Sz,Bin);
- none -> '#CDVNonexistingBinary'
- end
- end,
- D = gb_trees:insert(Addr, Term, D0),
- {Term,Line,D}.
-
+ {Sz,Line3} = get_hex(Line2),
+ {Term,Line,D1} = deref_bin(Binp0, Offset, Sz, Line3, DecodeOpts, D0),
+ D = gb_trees:insert(Addr, Term, D1),
+ {Term,Line,D};
+parse_heap_term("Mf"++Line0, Addr, DecodeOpts, D0) -> %Flatmap.
+ {Size,":"++Line1} = get_hex(Line0),
+ {Keys,":"++Line2,D1} = parse_term(Line1, DecodeOpts, D0),
+ {Values,Line,D2} = parse_tuple(Size, Line2, Addr,DecodeOpts, D1, []),
+ Pairs = zip_tuples(tuple_size(Keys), Keys, Values, []),
+ Map = maps:from_list(Pairs),
+ D = gb_trees:update(Addr, Map, D2),
+ {Map,Line,D};
+parse_heap_term("Mh"++Line0, Addr, DecodeOpts, D0) -> %Head node in a hashmap.
+ {MapSize,":"++Line1} = get_hex(Line0),
+ {N,":"++Line2} = get_hex(Line1),
+ {Nodes,Line,D1} = parse_tuple(N, Line2, Addr, DecodeOpts, D0, []),
+ Map = maps:from_list(flatten_hashmap_nodes(Nodes)),
+ MapSize = maps:size(Map), %Assertion.
+ D = gb_trees:update(Addr, Map, D1),
+ {Map,Line,D};
+parse_heap_term("Mn"++Line0, Addr, DecodeOpts, D) -> %Interior node in a hashmap.
+ {N,":"++Line} = get_hex(Line0),
+ parse_tuple(N, Line, Addr, DecodeOpts, D, []).
parse_tuple(0, Line, Addr, _, D0, Acc) ->
Tuple = list_to_tuple(lists:reverse(Acc)),
D = gb_trees:insert(Addr, Tuple, D0),
{Tuple,Line,D};
-parse_tuple(N, Line0, Addr, BinAddrAdj, D0, Acc) ->
- case parse_term(Line0, BinAddrAdj, D0) of
+parse_tuple(N, Line0, Addr, DecodeOpts, D0, Acc) ->
+ case parse_term(Line0, DecodeOpts, D0) of
{Term,[$,|Line],D} when N > 1 ->
- parse_tuple(N-1, Line, Addr, BinAddrAdj, D, [Term|Acc]);
+ parse_tuple(N-1, Line, Addr, DecodeOpts, D, [Term|Acc]);
{Term,Line,D}->
- parse_tuple(N-1, Line, Addr, BinAddrAdj, D, [Term|Acc])
+ parse_tuple(N-1, Line, Addr, DecodeOpts, D, [Term|Acc])
end.
-parse_term([$H|Line0], BinAddrAdj, D) -> %Pointer to heap term.
+zip_tuples(0, _T1, _T2, Acc) ->
+ Acc;
+zip_tuples(N, T1, T2, Acc) when N =< tuple_size(T1) ->
+ zip_tuples(N-1, T1, T2, [{element(N, T1),element(N, T2)}|Acc]).
+
+flatten_hashmap_nodes(Tuple) ->
+ flatten_hashmap_nodes_1(tuple_size(Tuple), Tuple, []).
+
+flatten_hashmap_nodes_1(0, _Tuple, Acc) ->
+ Acc;
+flatten_hashmap_nodes_1(N, Tuple0, Acc0) ->
+ case element(N, Tuple0) of
+ [K|V] ->
+ flatten_hashmap_nodes_1(N-1, Tuple0, [{K,V}|Acc0]);
+ Tuple when is_tuple(Tuple) ->
+ Acc = flatten_hashmap_nodes_1(N-1, Tuple0, Acc0),
+ flatten_hashmap_nodes_1(tuple_size(Tuple), Tuple, Acc)
+ end.
+
+parse_term([$H|Line0], DecodeOpts, D) -> %Pointer to heap term.
{Ptr,Line} = get_hex(Line0),
- deref_ptr(Ptr, Line, BinAddrAdj, D);
+ deref_ptr(Ptr, Line, DecodeOpts, D);
parse_term([$N|Line], _, D) -> %[] (nil).
{[],Line,D};
parse_term([$I|Line0], _, D) -> %Small.
@@ -2623,11 +2829,11 @@ parse_term([$p|Line0], _, D) -> %Port.
parse_term([$S|Str0], _, D) -> %Information string.
Str = lists:reverse(skip_blanks(lists:reverse(Str0))),
{Str,[],D};
-parse_term([$D|Line0], _, D) -> %DistExternal
+parse_term([$D|Line0], DecodeOpts, D) -> %DistExternal
try
{AttabSize,":"++Line1} = get_hex(Line0),
{Attab, "E"++Line2} = parse_atom_translation_table(AttabSize, Line1, []),
- {Bin,Line3} = get_binary(Line2),
+ {Bin,Line3} = get_binary(Line2, DecodeOpts),
{try
erts_debug:dist_ext_to_term(Attab, Bin)
catch
@@ -2660,26 +2866,55 @@ parse_atom_translation_table(0, Line0, As) ->
parse_atom_translation_table(N, Line0, As) ->
{A, Line1, _} = parse_atom(Line0, []),
parse_atom_translation_table(N-1, Line1, [A|As]).
-
-
-deref_ptr(Ptr, Line, BinAddrAdj, D0) ->
- case gb_trees:lookup(Ptr, D0) of
+
+deref_ptr(Ptr, Line, DecodeOpts, D) ->
+ Lookup = fun(D0) ->
+ gb_trees:lookup(Ptr, D0)
+ end,
+ do_deref_ptr(Lookup, Line, DecodeOpts, D).
+
+deref_bin(Binp0, Offset, Sz, Line, DecodeOpts, D) ->
+ Binp = Binp0 bor DecodeOpts#dec_opts.bin_addr_adj,
+ Lookup = fun(D0) ->
+ lookup_binary(Binp, Offset, Sz, D0)
+ end,
+ do_deref_ptr(Lookup, Line, DecodeOpts, D).
+
+lookup_binary(Binp, Offset, Sz, D) ->
+ case lookup_binary_index(Binp) of
+ [{_,Start}] ->
+ Term = cdvbin(Offset, Sz, {'#CDVBin',Start}),
+ {value,Term};
+ [] ->
+ case gb_trees:lookup(Binp, D) of
+ {value,<<_:Offset/bytes,Sub:Sz/bytes,_/bytes>>} ->
+ {value,Sub};
+ {value,SymbolicBin} ->
+ {value,cdvbin(Offset, Sz, SymbolicBin)};
+ none ->
+ none
+ end
+ end.
+
+do_deref_ptr(Lookup, Line, DecodeOpts, D0) ->
+ case Lookup(D0) of
{value,Term} ->
{Term,Line,D0};
none ->
case get(fd) of
end_of_heap ->
+ put(incomplete_heap,true),
{['#CDVIncompleteHeap'],Line,D0};
Fd ->
case bytes(Fd) of
"="++_ ->
put(fd, end_of_heap),
- deref_ptr(Ptr, Line, BinAddrAdj, D0);
+ do_deref_ptr(Lookup, Line, DecodeOpts, D0);
L ->
update_progress(length(L)+1),
- D = parse(L, BinAddrAdj, D0),
- deref_ptr(Ptr, Line, BinAddrAdj, D)
+ D = parse(L, DecodeOpts, D0),
+ do_deref_ptr(Lookup, Line, DecodeOpts, D)
end
end
end.
@@ -2742,36 +2977,80 @@ get_label([$:|Line], Acc) ->
get_label([H|T], Acc) ->
get_label(T, [H|Acc]).
-get_binary(Line0) ->
+get_binary(Line0,DecodeOpts) ->
case get_hex(Line0) of
{N,":"++Line} ->
- do_get_binary(N, Line, [], false);
+ get_binary_1(N, Line, DecodeOpts);
_ ->
{'#CDVTruncatedBinary',[]}
end.
-get_binary(Offset,Size,Line0) ->
+get_binary_1(N,Line,#dec_opts{base64=false}) ->
+ get_binary_hex(N, Line, [], false);
+get_binary_1(N,Line0,#dec_opts{base64=true}) ->
+ NumBytes = ((N+2) div 3) * 4,
+ {Base64,Line} = lists:split(NumBytes, Line0),
+ Bin = get_binary_base64(list_to_binary(Base64), <<>>, false),
+ {Bin,Line}.
+
+get_binary(Offset,Size,Line0,DecodeOpts) ->
case get_hex(Line0) of
{_N,":"++Line} ->
- Progress = Size>?binary_size_progress_limit,
- Progress andalso init_progress("Reading binary",Size),
- do_get_binary(Size, lists:sublist(Line,(Offset*2)+1,Size*2), [],
- Progress);
- _ ->
- {'#CDVTruncatedBinary',[]}
- end.
-
-do_get_binary(0, Line, Acc, Progress) ->
+ get_binary_1(Offset,Size,Line,DecodeOpts);
+ _ ->
+ {'#CDVTruncatedBinary',[]}
+ end.
+
+get_binary_1(Offset,Size,Line,#dec_opts{base64=false}) ->
+ Progress = Size > ?binary_size_progress_limit,
+ Progress andalso init_progress("Reading binary",Size),
+ get_binary_hex(Size, lists:sublist(Line,(Offset*2)+1,Size*2), [],
+ Progress);
+get_binary_1(StartOffset,Size,Line,#dec_opts{base64=true}) ->
+ Progress = Size > ?binary_size_progress_limit,
+ Progress andalso init_progress("Reading binary",Size),
+ EndOffset = StartOffset + Size,
+ StartByte = (StartOffset div 3) * 4,
+ EndByte = ((EndOffset + 2) div 3) * 4,
+ NumBytes = EndByte - StartByte,
+ case list_to_binary(Line) of
+ <<_:StartByte/bytes,Base64:NumBytes/bytes,_/bytes>> ->
+ Bin0 = get_binary_base64(Base64, <<>>, Progress),
+ Skip = StartOffset - (StartOffset div 3) * 3,
+ <<_:Skip/bytes,Bin:Size/bytes,_/bytes>> = Bin0,
+ {Bin,[]};
+ _ ->
+ {'#CDVTruncatedBinary',[]}
+ end.
+
+get_binary_hex(0, Line, Acc, Progress) ->
Progress andalso end_progress(),
{list_to_binary(lists:reverse(Acc)),Line};
-do_get_binary(N, [A,B|Line], Acc, Progress) ->
+get_binary_hex(N, [A,B|Line], Acc, Progress) ->
Byte = (get_hex_digit(A) bsl 4) bor get_hex_digit(B),
Progress andalso update_progress(),
- do_get_binary(N-1, Line, [Byte|Acc], Progress);
-do_get_binary(_N, [], _Acc, Progress) ->
+ get_binary_hex(N-1, Line, [Byte|Acc], Progress);
+get_binary_hex(_N, [], _Acc, Progress) ->
Progress andalso end_progress(),
{'#CDVTruncatedBinary',[]}.
+get_binary_base64(<<Chunk0:?base64_chunk_size/bytes,T/bytes>>,
+ Acc0, Progress) ->
+ Chunk = base64:decode(Chunk0),
+ Acc = <<Acc0/binary,Chunk/binary>>,
+ Progress andalso update_progress(?base64_chunk_size * 3 div 4),
+ get_binary_base64(T, Acc, Progress);
+get_binary_base64(Chunk0, Acc, Progress) ->
+ case Progress of
+ true ->
+ update_progress(?base64_chunk_size * 3 div 4),
+ end_progress();
+ false ->
+ ok
+ end,
+ Chunk = base64:decode(Chunk0),
+ <<Acc/binary,Chunk/binary>>.
+
cdvbin(Offset,Size,{'#CDVBin',Pos}) ->
['#CDVBin',Offset,Size,Pos];
cdvbin(Offset,Size,['#CDVBin',_,_,Pos]) ->
@@ -2792,6 +3071,10 @@ reset_tables() ->
insert_index(Tag,Id,Pos) ->
ets:insert(cdv_dump_index_table,{{Tag,Pos},Id}).
+delete_index(Tag,Id) ->
+ Ms = [{{{Tag,'$1'},Id},[],[true]}],
+ ets:select_delete(cdv_dump_index_table, Ms).
+
lookup_index({Tag,Id}) ->
lookup_index(Tag,Id);
lookup_index(Tag) ->
@@ -2808,6 +3091,7 @@ insert_binary_index(Addr,Pos) ->
lookup_binary_index(Addr) ->
ets:lookup(cdv_binary_index_table,Addr).
+
%%-----------------------------------------------------------------
%% Convert tags read from crashdump to atoms used as first part of key
%% in cdv_dump_index_table
@@ -2816,6 +3100,10 @@ tag_to_atom("allocated_areas") -> ?allocated_areas;
tag_to_atom("allocator") -> ?allocator;
tag_to_atom("atoms") -> ?atoms;
tag_to_atom("binary") -> ?binary;
+tag_to_atom("dirty_cpu_scheduler") -> ?dirty_cpu_scheduler;
+tag_to_atom("dirty_cpu_run_queue") -> ?dirty_cpu_run_queue;
+tag_to_atom("dirty_io_scheduler") -> ?dirty_io_scheduler;
+tag_to_atom("dirty_io_run_queue") -> ?dirty_io_run_queue;
tag_to_atom("end") -> ?ende;
tag_to_atom("erl_crash_dump") -> ?erl_crash_dump;
tag_to_atom("ets") -> ?ets;
@@ -2825,6 +3113,7 @@ tag_to_atom("hidden_node") -> ?hidden_node;
tag_to_atom("index_table") -> ?index_table;
tag_to_atom("instr_data") -> ?instr_data;
tag_to_atom("internal_ets") -> ?internal_ets;
+tag_to_atom("literals") -> ?literals;
tag_to_atom("loaded_modules") -> ?loaded_modules;
tag_to_atom("memory") -> ?memory;
tag_to_atom("mod") -> ?mod;
@@ -2848,8 +3137,10 @@ tag_to_atom(UnknownTag) ->
%%%-----------------------------------------------------------------
%%% Store last tag for use when truncated, and reason if aborted
put_last_tag(?abort,Reason) ->
- %% Don't overwrite the real last tag
- put(truncated_reason,Reason);
+ %% Don't overwrite the real last tag, and make sure to return
+ %% the previous last tag.
+ put(truncated_reason,Reason),
+ get(last_tag);
put_last_tag(Tag,Id) ->
put(last_tag,{Tag,Id}).
diff --git a/lib/observer/src/crashdump_viewer.hrl b/lib/observer/src/crashdump_viewer.hrl
index 6a93a089fd..252e19379d 100644
--- a/lib/observer/src/crashdump_viewer.hrl
+++ b/lib/observer/src/crashdump_viewer.hrl
@@ -80,6 +80,10 @@
old_heap,
heap_unused,
old_heap_unused,
+ bin_vheap,
+ old_bin_vheap,
+ bin_vheap_unused,
+ old_bin_vheap_unused,
new_heap_start,
new_heap_top,
stack_top,
@@ -95,19 +99,27 @@
-record(port,
{id,
+ state,
+ task_flags=0,
slot,
connected,
links,
name,
monitors,
- controls}).
+ suspended,
+ controls,
+ input,
+ output,
+ queue,
+ port_data}).
-record(sched,
{name,
+ type,
process,
port,
run_q=0,
- port_q=0,
+ port_q,
details=#{}
}).
diff --git a/lib/observer/src/observer_html_lib.erl b/lib/observer/src/observer_html_lib.erl
index a85808a472..22b4714d63 100644
--- a/lib/observer/src/observer_html_lib.erl
+++ b/lib/observer/src/observer_html_lib.erl
@@ -355,11 +355,11 @@ href_proc_bin(From, T, Acc, LTB) ->
PreviewStr
end
end;
- [PreviewIntStr,SizeStr,Md5] when From =:= obs ->
+ [PreviewIntStr,PreviewBitSizeStr,SizeStr,Md5] when From =:= obs ->
Size = list_to_integer(SizeStr),
PreviewInt = list_to_integer(PreviewIntStr),
- PrevSize = (trunc(math:log2(PreviewInt)/8)+1)*8,
- PreviewStr = preview_string(Size,<<PreviewInt:PrevSize>>),
+ PreviewBitSize = list_to_integer(PreviewBitSizeStr),
+ PreviewStr = preview_string(Size,<<PreviewInt:PreviewBitSize>>),
if LTB ->
href("TARGET=\"expanded\"",
["#OBSBinary?key1="++PreviewIntStr++
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 29f4f9fabc..0470e785d9 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -21,7 +21,8 @@
-export([get_wx_parent/1,
display_info_dialog/2, display_yes_no_dialog/1,
- display_progress_dialog/3, destroy_progress_dialog/0,
+ display_progress_dialog/3,
+ destroy_progress_dialog/0, sync_destroy_progress_dialog/0,
wait_for_progress/0, report_progress/1,
user_term/3, user_term_multiline/3,
interval_dialog/4, start_timer/1, start_timer/2, stop_timer/1, timer_config/1,
@@ -31,7 +32,8 @@
set_listctrl_col_size/2,
create_status_bar/1,
html_window/1, html_window/2,
- make_obsbin/2
+ make_obsbin/2,
+ add_scroll_entries/2
]).
-include_lib("wx/include/wx.hrl").
@@ -40,6 +42,8 @@
-define(SINGLE_LINE_STYLE, ?wxBORDER_NONE bor ?wxTE_READONLY bor ?wxTE_RICH2).
-define(MULTI_LINE_STYLE, ?SINGLE_LINE_STYLE bor ?wxTE_MULTILINE).
+-define(NUM_SCROLL_ITEMS,8).
+
-define(pulse_timeout,50).
get_wx_parent(Window) ->
@@ -397,17 +401,18 @@ get_box_info({Title, left, List}) -> {Title, ?wxALIGN_LEFT, List};
get_box_info({Title, right, List}) -> {Title, ?wxALIGN_RIGHT, List}.
add_box(Panel, OuterBox, Cursor, Title, Proportion, {Format, List}) ->
- Box = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, Title}]),
+ NumStr = " ("++integer_to_list(length(List))++")",
+ Box = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, Title ++ NumStr}]),
Scroll = wxScrolledWindow:new(Panel),
wxScrolledWindow:enableScrolling(Scroll,true,true),
wxScrolledWindow:setScrollbars(Scroll,1,1,0,0),
ScrollSizer = wxBoxSizer:new(?wxVERTICAL),
wxScrolledWindow:setSizer(Scroll, ScrollSizer),
wxWindow:setBackgroundStyle(Scroll, ?wxBG_STYLE_SYSTEM),
- add_entries(Format, List, Scroll, ScrollSizer, Cursor),
+ Entries = add_entries(Format, List, Scroll, ScrollSizer, Cursor),
wxSizer:add(Box,Scroll,[{proportion,1},{flag,?wxEXPAND}]),
wxSizer:add(OuterBox,Box,[{proportion,Proportion},{flag,?wxEXPAND}]),
- {Scroll,ScrollSizer,length(List)}.
+ {Scroll,ScrollSizer,length(Entries)}.
add_entries(click, List, Scroll, ScrollSizer, Cursor) ->
Add = fun(Link) ->
@@ -415,7 +420,20 @@ add_entries(click, List, Scroll, ScrollSizer, Cursor) ->
wxWindow:setBackgroundStyle(TC, ?wxBG_STYLE_SYSTEM),
wxSizer:add(ScrollSizer,TC, [{flag,?wxEXPAND}])
end,
- [Add(Link) || Link <- List];
+ if length(List) > ?NUM_SCROLL_ITEMS ->
+ {List1,Rest} = lists:split(?NUM_SCROLL_ITEMS,List),
+ LinkEntries = [Add(Link) || Link <- List1],
+ NStr = integer_to_list(length(Rest)),
+ TC = link_entry2(Scroll,
+ {{more,{Rest,Scroll,ScrollSizer}},"more..."},
+ Cursor,
+ "Click to see " ++ NStr ++ " more entries"),
+ wxWindow:setBackgroundStyle(TC, ?wxBG_STYLE_SYSTEM),
+ E = wxSizer:add(ScrollSizer,TC, [{flag,?wxEXPAND}]),
+ LinkEntries ++ [E];
+ true ->
+ [Add(Link) || Link <- List]
+ end;
add_entries(plain, List, Scroll, ScrollSizer, _) ->
Add = fun(String) ->
TC = wxStaticText:new(Scroll, ?wxID_ANY, String),
@@ -423,6 +441,23 @@ add_entries(plain, List, Scroll, ScrollSizer, _) ->
end,
[Add(String) || String <- List].
+add_scroll_entries(MoreEntry,{List, Scroll, ScrollSizer}) ->
+ wx:batch(
+ fun() ->
+ wxSizer:remove(ScrollSizer,?NUM_SCROLL_ITEMS),
+ wxStaticText:destroy(MoreEntry),
+ Cursor = wxCursor:new(?wxCURSOR_HAND),
+ Add = fun(Link) ->
+ TC = link_entry(Scroll, Link, Cursor),
+ wxWindow:setBackgroundStyle(TC, ?wxBG_STYLE_SYSTEM),
+ wxSizer:add(ScrollSizer,TC, [{flag,?wxEXPAND}])
+ end,
+ Entries = [Add(Link) || Link <- List],
+ wxCursor:destroy(Cursor),
+ wxSizer:layout(ScrollSizer),
+ wxSizer:setVirtualSizeHints(ScrollSizer,Scroll),
+ Entries
+ end).
create_box(_Panel, {scroll_boxes,[]}) ->
undefined;
@@ -449,7 +484,7 @@ create_box(Panel, {scroll_boxes,Data}) ->
{_,H} = wxWindow:getSize(Dummy),
wxTextCtrl:destroy(Dummy),
- MaxH = if MaxL > 8 -> 8*H;
+ MaxH = if MaxL > ?NUM_SCROLL_ITEMS -> ?NUM_SCROLL_ITEMS*H;
true -> MaxL*H
end,
[wxWindow:setMinSize(B,{0,MaxH}) || {B,_,_} <- Boxes],
@@ -504,20 +539,22 @@ create_box(Parent, Data) ->
link_entry(Panel, Link) ->
Cursor = wxCursor:new(?wxCURSOR_HAND),
- TC = link_entry2(Panel, to_link(Link), Cursor),
+ TC = link_entry(Panel, Link, Cursor),
wxCursor:destroy(Cursor),
TC.
link_entry(Panel, Link, Cursor) ->
- link_entry2(Panel, to_link(Link), Cursor).
+ link_entry2(Panel,to_link(Link),Cursor).
link_entry2(Panel,{Target,Str},Cursor) ->
+ link_entry2(Panel,{Target,Str},Cursor,"Click to see properties for " ++ Str).
+link_entry2(Panel,{Target,Str},Cursor,ToolTipText) ->
TC = wxStaticText:new(Panel, ?wxID_ANY, Str),
wxWindow:setForegroundColour(TC,?wxBLUE),
wxWindow:setCursor(TC, Cursor),
wxWindow:connect(TC, left_down, [{userData,Target}]),
wxWindow:connect(TC, enter_window),
wxWindow:connect(TC, leave_window),
- ToolTip = wxToolTip:new("Click to see properties for " ++ Str),
+ ToolTip = wxToolTip:new(ToolTipText),
wxWindow:setToolTip(TC, ToolTip),
TC.
@@ -708,6 +745,11 @@ wait_for_progress() ->
destroy_progress_dialog() ->
report_progress(finish).
+sync_destroy_progress_dialog() ->
+ Ref = erlang:monitor(process,?progress_handler),
+ destroy_progress_dialog(),
+ receive {'DOWN',Ref,process,_,_} -> ok end.
+
report_progress(Progress) ->
case whereis(?progress_handler) of
Pid when is_pid(Pid) ->
@@ -787,9 +829,8 @@ progress_dialog_new(Parent,Title,Str) ->
[{style,?wxDEFAULT_DIALOG_STYLE}]),
Panel = wxPanel:new(Dialog),
Sizer = wxBoxSizer:new(?wxVERTICAL),
- Message = wxStaticText:new(Panel, 1, Str),
- Gauge = wxGauge:new(Panel, 2, 100, [{size, {170, -1}},
- {style, ?wxGA_HORIZONTAL}]),
+ Message = wxStaticText:new(Panel, 1, Str,[{size,{220,-1}}]),
+ Gauge = wxGauge:new(Panel, 2, 100, [{style, ?wxGA_HORIZONTAL}]),
SizerFlags = ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT bor ?wxTOP,
wxSizer:add(Sizer, Message, [{flag,SizerFlags},{border,15}]),
wxSizer:add(Sizer, Gauge, [{flag, SizerFlags bor ?wxBOTTOM},{border,15}]),
@@ -810,7 +851,7 @@ progress_dialog_destroy({Dialog,_,_}) ->
make_obsbin(Bin,Tab) ->
Size = byte_size(Bin),
- Preview =
+ {Preview,PreviewBitSize} =
try
%% The binary might be a unicode string, in which case we
%% don't want to split it in the middle of a grapheme
@@ -819,14 +860,14 @@ make_obsbin(Bin,Tab) ->
PB1 = string:slice(Bin,0,PL1),
PS1 = byte_size(PB1) * 8,
<<P1:PS1>> = PB1,
- P1
+ {P1,PS1}
catch _:_ ->
%% Probably not a string, so just split anywhere
PS2 = min(Size, 10) * 8,
<<P2:PS2, _/binary>> = Bin,
- P2
+ {P2,PS2}
end,
Hash = erlang:phash2(Bin),
Key = {Preview, Size, Hash},
ets:insert(Tab, {Key,Bin}),
- ['#OBSBin',Preview,Size,Hash].
+ ['#OBSBin',Preview,PreviewBitSize,Size,Hash].
diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl
index 5908e99e36..f7ae07fb85 100644
--- a/lib/observer/src/observer_port_wx.erl
+++ b/lib/observer/src/observer_port_wx.erl
@@ -242,6 +242,10 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL},
Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
{noreply, State#state{timer=Timer}};
+handle_event(#wx{obj=MoreEntry,event=#wxMouse{type=left_down},userData={more,More}}, State) ->
+ observer_lib:add_scroll_entries(MoreEntry,More),
+ {noreply, State};
+
handle_event(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) ->
observer ! {open_link, TargetPid},
{noreply, State};
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index 2e5fe0bc1a..1c40afba46 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -27,7 +27,7 @@
handle_event/2, handle_cast/2]).
-include_lib("wx/include/wx.hrl").
--include("../include/etop.hrl").
+-include("etop.hrl").
-include("observer_defs.hrl").
-include("etop_defs.hrl").
@@ -572,7 +572,8 @@ change_accum(true, S0) ->
S0#holder{accum=true};
change_accum(false, S0=#holder{info=Info}) ->
self() ! refresh,
- S0#holder{accum=lists:sort(array:to_list(Info))}.
+ Accum = [{Pid, Reds} || #etop_proc_info{pid=Pid, reds=Reds} <- array:to_list(Info)],
+ S0#holder{accum=lists:sort(Accum)}.
handle_update_old(#etop_info{procinfo=ProcInfo0},
S0=#holder{parent=Parent, sort=Sort=#sort{sort_key=KeyField}}) ->
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index fb02ae2728..5bc17e2aee 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -120,6 +120,10 @@ handle_event(#wx{id=?REFRESH}, #state{frame=Frame, pid=Pid, pages=Pages, expand_
end,
{noreply, State};
+handle_event(#wx{obj=MoreEntry,event=#wxMouse{type=left_down},userData={more,More}}, State) ->
+ observer_lib:add_scroll_entries(MoreEntry,More),
+ {noreply, State};
+
handle_event(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) ->
observer ! {open_link, TargetPid},
{noreply, State};
@@ -253,8 +257,6 @@ init_stack_page(Parent, Pid) ->
[Pid, current_stacktrace])
of
{current_stacktrace,RawBt} ->
- observer_wx:try_rpc(node(Pid), erlang, process_info,
- [Pid, current_stacktrace]),
wxListCtrl:deleteAllItems(LCtrl),
wx:foldl(fun({M, F, A, Info}, Row) ->
_Item = wxListCtrl:insertItem(LCtrl, Row, ""),
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index 8127248262..2c3b46a3a1 100644
--- a/lib/observer/src/observer_trace_wx.erl
+++ b/lib/observer/src/observer_trace_wx.erl
@@ -1201,7 +1201,7 @@ make_ms(MS) ->
make_ms(Name,Term,FunStr).
make_ms(Name, Term, FunStr) ->
- #match_spec{name=Name, term=Term, str=io_lib:format("~tw", Term), func = FunStr}.
+ #match_spec{name=Name, term=Term, str=io_lib:format("~tw", [Term]), func = FunStr}.
parse_tp({tp, Mod, FAs}, State) ->
Patterns = [#tpattern{m=Mod,fa={F,A}, ms=make_ms(List)} ||
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index e16f3cab6b..2e387f7e74 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -38,13 +38,13 @@
-define(ID_SYSTEM_TABLES, 406).
-define(ID_TABLE_INFO, 407).
-define(ID_SHOW_TABLE, 408).
-
--record(opt, {type=ets,
- sys_hidden=true,
- unread_hidden=true,
- sort_key=2,
- sort_incr=true
- }).
+
+-record(opts, {type=ets,
+ sys_hidden=true,
+ unread_hidden=true}).
+
+-record(sort, {sort_incr=true,
+ sort_key=2}).
-record(state,
{
@@ -52,9 +52,9 @@
grid,
panel,
node=node(),
- opt=#opt{},
+ opts=#opts{},
+ holder,
selected,
- tabs,
timer
}).
@@ -64,8 +64,18 @@ start_link(Notebook, Parent, Config) ->
init([Notebook, Parent, Config]) ->
Panel = wxPanel:new(Notebook),
Sizer = wxBoxSizer:new(?wxVERTICAL),
- Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES,
- Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style}]),
+
+ Opts=#opts{type=maps:get(type, Config, ets),
+ sys_hidden=maps:get(sys_hidden, Config, true),
+ unread_hidden=maps:get(unread_hidden, Config, true)},
+
+ Style = ?wxLC_REPORT bor ?wxLC_VIRTUAL bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES,
+ Self = self(),
+ Attrs = observer_lib:create_attrs(),
+ Holder = spawn_link(fun() -> init_table_holder(Self, Attrs) end),
+ CBs = [{onGetItemText, fun(_, Item,Col) -> get_row(Holder, Item, Col) end},
+ {onGetItemAttr, fun(_, Item) -> get_attr(Holder, Item) end}],
+ Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style} | CBs]),
wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL},
{proportion, 1}, {border, 5}]),
wxWindow:setSizer(Panel, Sizer),
@@ -95,38 +105,26 @@ init([Notebook, Parent, Config]) ->
wxWindow:setFocus(Grid),
{Panel, #state{grid=Grid, parent=Parent, panel=Panel,
- timer=Config,
- opt=#opt{type=maps:get(type, Config, ets),
- sys_hidden=maps:get(sys_hidden, Config, true),
- unread_hidden=maps:get(unread_hidden, Config, true)}
- }}.
+ opts=Opts, timer=Config, holder=Holder}}.
handle_event(#wx{id=?ID_REFRESH},
- State = #state{node=Node, grid=Grid, opt=Opt}) ->
- Tables = get_tables(Node, Opt),
- {Tabs,Sel} = update_grid(Grid, sel(State), Opt, Tables),
- Sel =/= undefined andalso wxListCtrl:ensureVisible(Grid, Sel),
- {noreply, State#state{tabs=Tabs, selected=Sel}};
+ State = #state{holder=Holder, node=Node, opts=Opts}) ->
+ Tables = get_tables(Node, Opts),
+ Holder ! {refresh, Tables},
+ {noreply, State};
handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
- State = #state{node=Node, grid=Grid,
- opt=Opt0=#opt{sort_key=Key, sort_incr=Bool}}) ->
- Opt = case col2key(Col) of
- Key -> Opt0#opt{sort_incr=not Bool};
- NewKey -> Opt0#opt{sort_key=NewKey}
- end,
- Tables = get_tables(Node, Opt),
- {Tabs,Sel} = update_grid(Grid, sel(State), Opt, Tables),
- wxWindow:setFocus(Grid),
- {noreply, State#state{opt=Opt, tabs=Tabs, selected=Sel}};
+ State = #state{holder=Holder}) ->
+ Holder ! {sort, Col},
+ {noreply, State};
-handle_event(#wx{id=Id}, State = #state{node=Node, grid=Grid, opt=Opt0})
+handle_event(#wx{id=Id}, State = #state{node=Node, holder=Holder, grid=Grid, opts=Opt0})
when Id >= ?ID_ETS, Id =< ?ID_SYSTEM_TABLES ->
Opt = case Id of
- ?ID_ETS -> Opt0#opt{type=ets};
- ?ID_MNESIA -> Opt0#opt{type=mnesia};
- ?ID_UNREADABLE -> Opt0#opt{unread_hidden= not Opt0#opt.unread_hidden};
- ?ID_SYSTEM_TABLES -> Opt0#opt{sys_hidden= not Opt0#opt.sys_hidden}
+ ?ID_ETS -> Opt0#opts{type=ets};
+ ?ID_MNESIA -> Opt0#opts{type=mnesia};
+ ?ID_UNREADABLE -> Opt0#opts{unread_hidden= not Opt0#opts.unread_hidden};
+ ?ID_SYSTEM_TABLES -> Opt0#opts{sys_hidden= not Opt0#opts.sys_hidden}
end,
case get_tables2(Node, Opt) of
Error = {error, _} ->
@@ -135,9 +133,9 @@ handle_event(#wx{id=Id}, State = #state{node=Node, grid=Grid, opt=Opt0})
self() ! Error,
{noreply, State};
Tables ->
- {Tabs, Sel} = update_grid(Grid, sel(State), Opt, Tables),
+ Holder ! {refresh, Tables},
wxWindow:setFocus(Grid),
- {noreply, State#state{opt=Opt, tabs=Tabs, selected=Sel}}
+ {noreply, State#state{opts=Opt}}
end;
handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
@@ -146,19 +144,18 @@ handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
handle_event(#wx{event=#wxList{type=command_list_item_activated,
itemIndex=Index}},
- State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs}) ->
- Table = lists:nth(Index+1, Tabs),
- case Table#tab.protection of
- private ->
- self() ! {error, "Table has 'private' protection and can not be read"};
- _ ->
- observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}])
+ State=#state{holder=Holder, node=Node, opts=#opts{type=Type}, grid=Grid}) ->
+ case get_table(Holder, Index) of
+ #tab{protection=private} ->
+ self() ! {error, "Table has 'private' protection and can not be read"};
+ #tab{}=Table ->
+ observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}]);
+ _ -> ignore
end,
{noreply, State};
handle_event(#wx{event=#wxList{type=command_list_item_right_click}},
State=#state{panel=Panel}) ->
-
Menu = wxMenu:new(),
wxMenu:append(Menu, ?ID_TABLE_INFO, "Table info"),
wxMenu:append(Menu, ?ID_SHOW_TABLE, "Show Table Content"),
@@ -167,32 +164,33 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click}},
{noreply, State};
handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}},
- State) ->
+ State=#state{holder=Holder}) ->
+ Holder ! {selected, Index},
{noreply, State#state{selected=Index}};
handle_event(#wx{id=?ID_TABLE_INFO},
- State = #state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs, selected=Sel}) ->
+ State = #state{holder=Holder, grid=Grid, node=Node, opts=#opts{type=Type}, selected=Sel}) ->
case Sel of
undefined ->
{noreply, State};
R when is_integer(R) ->
- Table = lists:nth(Sel+1, Tabs),
+ Table = get_table(Holder, Sel),
display_table_info(Grid, Node, Type, Table),
{noreply, State}
end;
handle_event(#wx{id=?ID_SHOW_TABLE},
- State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs, selected=Sel}) ->
+ State=#state{holder=Holder, grid=Grid, node=Node, opts=#opts{type=Type}, selected=Sel}) ->
case Sel of
undefined ->
{noreply, State};
R when is_integer(R) ->
- Table = lists:nth(Sel+1, Tabs),
- case Table#tab.protection of
- private ->
+ case get_table(Holder, R) of
+ #tab{protection=private} ->
self() ! {error, "Table has 'private' protection and can not be read"};
- _ ->
- observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}])
+ #tab{}=Table ->
+ observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}]);
+ _ -> ignore
end,
{noreply, State}
end;
@@ -202,14 +200,14 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL},
Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
{noreply, State#state{timer=Timer}};
-handle_event(Event, _State) ->
- error({unhandled_event, Event}).
+handle_event(_Event, State) ->
+ {noreply, State}.
handle_sync_event(_Event, _Obj, _State) ->
ok.
-handle_call(get_config, _, #state{timer=Timer, opt=Opt}=State) ->
- #opt{type=Type, sys_hidden=Sys, unread_hidden=Unread} = Opt,
+handle_call(get_config, _, #state{timer=Timer, opts=Opt}=State) ->
+ #opts{type=Type, sys_hidden=Sys, unread_hidden=Unread} = Opt,
Conf0 = observer_lib:timer_config(Timer),
Conf = Conf0#{type=>Type, sys_hidden=>Sys, unread_hidden=>Unread},
{reply, Conf, State};
@@ -220,50 +218,68 @@ handle_call(Event, From, _State) ->
handle_cast(Event, _State) ->
error({unhandled_cast, Event}).
-handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
- tabs=OldTabs}) ->
- case get_tables(Node, Opt) of
- OldTabs ->
- %% no change
- {noreply, State};
- Tables ->
- {Tabs, Sel} = update_grid(Grid, sel(State), Opt, Tables),
- Sel =/= undefined andalso wxListCtrl:ensureVisible(Grid, Sel),
- {noreply, State#state{tabs=Tabs, selected=Sel}}
- end;
+handle_info(refresh_interval, State = #state{holder=Holder, node=Node, opts=Opt}) ->
+ Tables = get_tables(Node, Opt),
+ Holder ! {refresh, Tables},
+ {noreply, State};
-handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt0,
- timer=Timer0}) ->
- {Tables, Opt} = case Opt0#opt.type =:= mnesia andalso get_tables2(Node, Opt0) of
+handle_info({active, Node}, State = #state{parent=Parent, holder=Holder, grid=Grid,
+ opts=Opt0, timer=Timer0}) ->
+ {Tables, Opt} = case Opt0#opts.type =:= mnesia andalso get_tables2(Node, Opt0) of
Ts when is_list(Ts) ->
{Ts, Opt0};
_ -> % false or error getting mnesia tables
- Opt1 = Opt0#opt{type=ets},
+ Opt1 = Opt0#opts{type=ets},
{get_tables(Node, Opt1), Opt1}
end,
- {Tabs,Sel} = update_grid(Grid, sel(State), Opt, Tables),
+ Holder ! {refresh, Tables},
wxWindow:setFocus(Grid),
create_menus(Parent, Opt),
Timer = observer_lib:start_timer(Timer0, 10),
- {noreply, State#state{node=Node, tabs=Tabs, timer=Timer, opt=Opt, selected=Sel}};
+ {noreply, State#state{node=Node, timer=Timer, opts=Opt}};
handle_info(not_active, State = #state{timer = Timer0}) ->
Timer = observer_lib:stop_timer(Timer0),
{noreply, State#state{timer=Timer}};
-handle_info({error, Error}, #state{panel=Panel,opt=Opt}=State) ->
+handle_info({error, Error}, #state{panel=Panel,opts=Opt}=State) ->
Str = io_lib:format("ERROR: ~ts~n",[Error]),
observer_lib:display_info_dialog(Panel,Str),
- case Opt#opt.type of
+ case Opt#opts.type of
mnesia -> wxMenuBar:check(observer_wx:get_menubar(), ?ID_ETS, true);
_ -> ok
end,
- {noreply, State#state{opt=Opt#opt{type=ets}}};
+ {noreply, State#state{opts=Opt#opts{type=ets}}};
+
+handle_info({refresh, Min, Min}, State = #state{grid=Grid}) ->
+ wxListCtrl:setItemCount(Grid, Min+1),
+ wxListCtrl:refreshItem(Grid, Min), %% Avoid assert in wx below if Max is 0
+ observer_wx:set_status(io_lib:format("Tables: ~w", [Min+1])),
+ {noreply, State};
+handle_info({refresh, Min, Max}, State = #state{grid=Grid}) ->
+ wxListCtrl:setItemCount(Grid, Max+1),
+ Max > 0 andalso wxListCtrl:refreshItems(Grid, Min, Max),
+ observer_wx:set_status(io_lib:format("Tables: ~w", [Max+1])),
+ {noreply, State};
+
+handle_info({selected, New, Size}, #state{grid=Grid, selected=Old} = State) ->
+ if
+ is_integer(Old), Old < Size ->
+ wxListCtrl:setItemState(Grid, Old, 0, ?wxLIST_STATE_SELECTED);
+ true -> ignore
+ end,
+ if is_integer(New) ->
+ wxListCtrl:setItemState(Grid, New, 16#FFFF, ?wxLIST_STATE_SELECTED),
+ wxListCtrl:ensureVisible(Grid, New);
+ true -> ignore
+ end,
+ {noreply, State#state{selected=New}};
handle_info(_Event, State) ->
{noreply, State}.
-terminate(_Event, _State) ->
+terminate(_Event, #state{holder=Holder}) ->
+ Holder ! stop,
ok.
code_change(_, _, State) ->
@@ -271,7 +287,7 @@ code_change(_, _, State) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-create_menus(Parent, #opt{sys_hidden=Sys, unread_hidden=UnR, type=Type}) ->
+create_menus(Parent, #opts{sys_hidden=Sys, unread_hidden=UnR, type=Type}) ->
MenuEntries = [{"View",
[#create_menu{id = ?ID_TABLE_INFO, text = "Table information\tCtrl-I"},
separator,
@@ -298,7 +314,7 @@ get_tables(Node, Opts) ->
Res ->
Res
end.
-get_tables2(Node, #opt{type=Type, sys_hidden=Sys, unread_hidden=Unread}) ->
+get_tables2(Node, #opts{type=Type, sys_hidden=Sys, unread_hidden=Unread}) ->
Args = [Type, [{sys_hidden,Sys}, {unread_hidden,Unread}]],
case rpc:call(Node, observer_backend, get_table_list, Args) of
{badrpc, Error} ->
@@ -386,49 +402,134 @@ list_to_strings([A]) -> integer_to_list(A);
list_to_strings([A|B]) ->
integer_to_list(A) ++ " ," ++ list_to_strings(B).
-update_grid(Grid, Selected, Opt, Tables) ->
- wx:batch(fun() -> update_grid2(Grid, Selected, Opt, Tables) end).
-
-update_grid2(Grid, {SelName,SelId}, #opt{sort_key=Sort,sort_incr=Dir}, Tables) ->
- wxListCtrl:deleteAllItems(Grid),
- Update =
- fun(#tab{name = Name, id = Id, owner = Owner, size = Size, memory = Memory,
- protection = Protection, reg_name = RegName},
- {Row, Sel}) ->
- _Item = wxListCtrl:insertItem(Grid, Row, ""),
- if (Row rem 2) =:= 0 ->
- wxListCtrl:setItemBackgroundColour(Grid, Row, ?BG_EVEN);
- true -> ignore
- end,
- if Protection == private ->
- wxListCtrl:setItemTextColour(Grid, Row, {200,130,50});
- true -> ignore
- end,
-
- lists:foreach(fun({_, ignore}) -> ignore;
- ({Col, Val}) ->
- wxListCtrl:setItem(Grid, Row, Col, observer_lib:to_str(Val))
- end,
- [{0,Name}, {1,Size}, {2, Memory div 1024},
- {3,Owner}, {4,RegName}, {5,Id}]),
- if SelName =:= Name, SelId =:= Id ->
- wxListCtrl:setItemState(Grid, Row, 16#FFFF, ?wxLIST_STATE_SELECTED),
- {Row+1, Row};
- true ->
- wxListCtrl:setItemState(Grid, Row, 0, ?wxLIST_STATE_SELECTED),
- {Row+1, Sel}
- end
- end,
- ProcInfo = case Dir of
- false -> lists:reverse(lists:keysort(Sort, Tables));
- true -> lists:keysort(Sort, Tables)
- end,
- {_, Sel} = lists:foldl(Update, {0, undefined}, ProcInfo),
- {ProcInfo, Sel}.
-
-sel(#state{selected=Sel, tabs=Tabs}) ->
- try lists:nth(Sel+1, Tabs) of
- #tab{name=Name, id=Id} -> {Name, Id}
- catch _:_ ->
- {undefined, undefined}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Table holder needs to be in a separate process otherwise
+%% the callback get_row/3 may deadlock if the process do
+%% wx calls when callback is invoked.
+
+get_table(Table, Item) ->
+ get_row(Table, Item, all).
+
+get_row(Table, Item, Column) ->
+ Ref = erlang:monitor(process, Table),
+ Table ! {get_row, self(), Item, Column},
+ receive
+ {'DOWN', Ref, _, _, _} -> "";
+ {Table, Res} ->
+ erlang:demonitor(Ref),
+ Res
end.
+
+get_attr(Table, Item) ->
+ Ref = erlang:monitor(process, Table),
+ Table ! {get_attr, self(), Item},
+ receive
+ {'DOWN', Ref, _, _, _} -> wx:null();
+ {Table, Res} ->
+ erlang:demonitor(Ref),
+ Res
+ end.
+
+-record(holder, {node, parent, pid,
+ tabs=array:new(),
+ sort=#sort{},
+ attrs,
+ sel
+ }).
+
+init_table_holder(Parent, Attrs) ->
+ Parent ! refresh,
+ table_holder(#holder{node=node(), parent=Parent, attrs=Attrs}).
+
+table_holder(S0 = #holder{parent=Parent, tabs=Tabs0, sel=Sel0}) ->
+ receive
+ {get_attr, From, Row} ->
+ get_attr(From, Row, S0),
+ table_holder(S0);
+ {get_row, From, Row, Col} ->
+ get_row(From, Row, Col, Tabs0),
+ table_holder(S0);
+ {sort, Col} ->
+ STab = get_sel(Sel0, Tabs0),
+ Parent ! {refresh, 0, array:size(Tabs0)-1},
+ S1 = sort(col2key(Col), S0),
+ Sel = sel_idx(STab, S1#holder.tabs),
+ Parent ! {selected, Sel, array:size(Tabs0)},
+ table_holder(S1#holder{sel=Sel});
+ {refresh, Tabs1} ->
+ STab = get_sel(Sel0, Tabs0),
+ Tabs = case S0#holder.sort of
+ #sort{sort_incr=false, sort_key=Col} ->
+ array:from_list(lists:reverse(lists:keysort(Col, Tabs1)));
+ #sort{sort_key=Col} ->
+ array:from_list(lists:keysort(Col, Tabs1))
+ end,
+ Parent ! {refresh, 0, array:size(Tabs)-1},
+ Sel = sel_idx(STab, Tabs),
+ Parent ! {selected, Sel,array:size(Tabs)},
+ table_holder(S0#holder{tabs=Tabs, sel=Sel});
+ {selected, Sel} ->
+ table_holder(S0#holder{sel=Sel});
+ stop ->
+ ok;
+ What ->
+ io:format("Table holder got ~tp~n",[What]),
+ Parent ! {refresh, 0, array:size(Tabs0)-1},
+ table_holder(S0)
+ end.
+
+get_sel(undefined, _Tabs) ->
+ undefined;
+get_sel(Idx, Tabs) ->
+ array:get(Idx, Tabs).
+
+sel_idx(undefined, _Tabs) ->
+ undefined;
+sel_idx(Tab, Tabs) ->
+ Find = fun(Idx, C, Acc) -> C =:= Tab andalso throw({found, Idx}), Acc end,
+ try array:foldl(Find, undefined, Tabs)
+ catch {found, Idx} -> Idx
+ end.
+
+sort(Col, #holder{sort=#sort{sort_key=Col, sort_incr=Incr}=S, tabs=Table0}=H) ->
+ Table = lists:reverse(array:to_list(Table0)),
+ H#holder{sort=S#sort{sort_incr=(not Incr)},
+ tabs=array:from_list(Table)};
+sort(Col, #holder{sort=#sort{sort_incr=Incr}=S, tabs=Table0}=H) ->
+ Table = case Incr of
+ false -> lists:reverse(lists:keysort(Col, array:to_list(Table0)));
+ true -> lists:keysort(Col, array:to_list(Table0))
+ end,
+ H#holder{sort=S#sort{sort_key=Col},
+ tabs=array:from_list(Table)}.
+
+get_row(From, Row, Col, Table) ->
+ Object = array:get(Row, Table),
+ From ! {self(), get_col(Col, Object)}.
+
+get_col(all, Rec) ->
+ Rec;
+get_col(2, #tab{}=Rec) -> %% Memory in kB
+ observer_lib:to_str(element(#tab.memory, Rec) div 1024);
+get_col(Col, #tab{}=Rec) ->
+ case element(col2key(Col), Rec) of
+ ignore -> "";
+ Val -> observer_lib:to_str(Val)
+ end;
+get_col(_, _) ->
+ "".
+
+get_attr(From, Row, #holder{tabs=Tabs, attrs=Attrs}) ->
+ EvenOdd = case (Row rem 2) > 0 of
+ true -> Attrs#attrs.odd;
+ false -> Attrs#attrs.even
+ end,
+ What = try array:get(Row, Tabs) of
+ #tab{protection=private} ->
+ Attrs#attrs.deleted;
+ _ ->
+ EvenOdd
+ catch _ ->
+ EvenOdd
+ end,
+ From ! {self(), What}.
diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl
index f37d9057cb..b5e94a893a 100644
--- a/lib/observer/test/crashdump_helper.erl
+++ b/lib/observer/test/crashdump_helper.erl
@@ -19,7 +19,9 @@
%%
-module(crashdump_helper).
--export([n1_proc/2,remote_proc/2]).
+-export([n1_proc/2,remote_proc/2,
+ dump_maps/0,create_maps/0,
+ create_binaries/0,create_sub_binaries/1]).
-compile(r18).
-include_lib("common_test/include/ct.hrl").
@@ -60,7 +62,9 @@ n1_proc(Creator,_N2,Pid2,Port2,_L) ->
put(ref,Ref),
put(pid,Pid),
put(bin,Bin),
+ put(bins,create_binaries()),
put(sub_bin,SubBin),
+ put(sub_bins,create_sub_binaries(get(bins))),
put(bignum,83974938738373873),
put(neg_bignum,-38748762783736367),
put(ext_pid,Pid2),
@@ -79,6 +83,7 @@ n1_proc(Creator,_N2,Pid2,Port2,_L) ->
link(OtherPid), % own node
link(Pid2), % external node
erlang:monitor(process,OtherPid),
+ erlang:monitor(process,init), % named process
erlang:monitor(process,Pid2),
code:load_file(?MODULE),
@@ -92,3 +97,49 @@ remote_proc(P1,Creator) ->
Creator ! {self(),done},
receive after infinity -> ok end
end).
+
+create_binaries() ->
+ Sizes = lists:seq(60, 70) ++ lists:seq(120, 140),
+ [begin
+ <<H:16/unit:8>> = erlang:md5(<<Size:32>>),
+ Data = ((H bsl (8*150)) div (H+7919)),
+ <<Data:Size/unit:8>>
+ end || Size <- Sizes].
+
+create_sub_binaries(Bins) ->
+ [create_sub_binary(Bin, Start, LenSub) ||
+ Bin <- Bins,
+ Start <- [0,1,2,3,4,5,10,22],
+ LenSub <- [0,1,2,3,4,6,9]].
+
+create_sub_binary(Bin, Start, LenSub) ->
+ Len = byte_size(Bin) - LenSub - Start,
+ <<_:Start/bytes,Sub:Len/bytes,_/bytes>> = Bin,
+ Sub.
+
+%%%
+%%% Test dumping of maps. Dumping of maps only from OTP 20.2.
+%%%
+
+dump_maps() ->
+ Parent = self(),
+ F = fun() ->
+ register(aaaaaaaa_maps, self()),
+ put(maps, create_maps()),
+ Parent ! {self(),done},
+ receive _ -> ok end
+ end,
+ Pid = spawn_link(F),
+ receive
+ {Pid,done} ->
+ {ok,Pid}
+ end.
+
+create_maps() ->
+ Map0 = maps:from_list([{I,[I,I+1]} || I <- lists:seq(1, 40)]),
+ Map1 = maps:from_list([{I,{a,[I,I*I],{}}} || I <- lists:seq(1, 100)]),
+ Map2 = maps:from_list([{{I},(I*I) bsl 24} || I <- lists:seq(1, 10000)]),
+ Map3 = lists:foldl(fun(I, A) ->
+ A#{I=>I*I}
+ end, Map2, lists:seq(-10, 0)),
+ #{a=>Map0,b=>Map1,c=>Map2,d=>Map3,e=>#{}}.
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index f9ac884743..41ca3f3ce9 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -25,7 +25,7 @@
%% Test functions
-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
start_stop/1,load_file/1,not_found_items/1,
- non_existing/1,not_a_crashdump/1,old_crashdump/1]).
+ non_existing/1,not_a_crashdump/1,old_crashdump/1,new_crashdump/1]).
-export([init_per_suite/1, end_per_suite/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -83,6 +83,7 @@ all() ->
non_existing,
not_a_crashdump,
old_crashdump,
+ new_crashdump,
load_file,
not_found_items
].
@@ -212,6 +213,25 @@ not_a_crashdump(Config) when is_list(Config) ->
ok = crashdump_viewer:stop().
+%% Try to load a file with newer version than this crashdump viewer can handle
+new_crashdump(Config) ->
+ Dump = hd(?config(dumps,Config)),
+ ok = start_backend(Dump),
+ {ok,{MaxVsn,CurrentVsn}} = crashdump_viewer:get_dump_versions(),
+ if MaxVsn =/= CurrentVsn ->
+ ct:fail("Current dump version is not equal to cdv's max version");
+ true ->
+ ok
+ end,
+ ok = crashdump_viewer:stop(),
+ NewerVsn = lists:join($.,[integer_to_list(X+1) || X <- MaxVsn]),
+ PrivDir = ?config(priv_dir,Config),
+ NewDump = filename:join(PrivDir,"new_erl_crash.dump"),
+ ok = file:write_file(NewDump,"=erl_crash_dump:"++NewerVsn++"\n"),
+ {error, Reason} = start_backend(NewDump),
+ "This Crashdump Viewer is too old" ++_ = Reason,
+ ok = crashdump_viewer:stop().
+
%% Load files into the tool and view all pages
load_file(Config) when is_list(Config) ->
case ?t:is_debug() of
@@ -328,7 +348,7 @@ browse_file(File) ->
io:format(" info read",[]),
- lookat_all_pids(Procs),
+ lookat_all_pids(Procs,is_truncated(File),incomplete_allowed(File)),
io:format(" pids ok",[]),
lookat_all_ports(Ports),
io:format(" ports ok",[]),
@@ -339,6 +359,21 @@ browse_file(File) ->
Procs. % used as second arg to special/2
+is_truncated(File) ->
+ case filename:extension(File) of
+ ".trunc"++_ ->
+ true;
+ _ ->
+ false
+ end.
+
+incomplete_allowed(File) ->
+ %% Incomplete heap is allowed for native libs, since some literals
+ %% are not dumped - and for pre OTP-20 (really pre 20.2) releases,
+ %% since literals were not dumped at all then.
+ Rel = get_rel_from_dump_name(File),
+ Rel < 20 orelse test_server:is_native(lists).
+
special(File,Procs) ->
case filename:extension(File) of
".full_dist" ->
@@ -364,6 +399,14 @@ special(File,Procs) ->
crashdump_viewer:expand_binary({SOffset,SSize,SPos}),
io:format(" expand binary ok",[]),
+ Binaries = crashdump_helper:create_binaries(),
+ verify_binaries(Binaries, proplists:get_value(bins,Dict)),
+ io:format(" binaries ok",[]),
+
+ SubBinaries = crashdump_helper:create_sub_binaries(Binaries),
+ verify_binaries(SubBinaries, proplists:get_value(sub_bins,Dict)),
+ io:format(" sub binaries ok",[]),
+
#proc{last_calls=LastCalls} = ProcDetails,
true = length(LastCalls) =< 4,
@@ -416,13 +459,36 @@ special(File,Procs) ->
old_attrib=undefined,
old_comp_info=undefined}=Mod2,
ok;
+ ".trunc_mod" ->
+ ModName = atom_to_list(?helper_mod),
+ {ok,Mod=#loaded_mod{},[TW]} =
+ crashdump_viewer:loaded_mod_details(ModName),
+ "WARNING: The crash dump is truncated here."++_ = TW,
+ #loaded_mod{current_attrib=CA,current_comp_info=CCI,
+ old_attrib=OA,old_comp_info=OCI} = Mod,
+ case lists:all(fun(undefined) ->
+ true;
+ (S) when is_list(S) ->
+ io_lib:printable_unicode_list(lists:flatten(S));
+ (_) -> false
+ end,
+ [CA,CCI,OA,OCI]) of
+ true ->
+ ok;
+ false ->
+ ct:fail({should_be_printable_strings_or_undefined,
+ {CA,CCI,OA,OCI}})
+ end,
+ ok;
".trunc_bin1" ->
%% This is 'full_dist' truncated after the first
%% "=binary:"
%% i.e. no binary exist in the dump
[#proc{pid=Pid0}|_Rest] = lists:keysort(#proc.name,Procs),
Pid = pid_to_list(Pid0),
- {ok,ProcDetails=#proc{},[]} = crashdump_viewer:proc_details(Pid),
+ %%WarnIncompleteHeap = ["WARNING: This process has an incomplete heap. Some information might be missing."],
+ {ok,ProcDetails=#proc{},[]} =
+ crashdump_viewer:proc_details(Pid),
io:format(" process details ok",[]),
#proc{dict=Dict} = ProcDetails,
@@ -513,20 +579,56 @@ special(File,Procs) ->
io:format(" unicode table name ok",[]),
ok;
+ ".maps" ->
+ %% I registered a process as aaaaaaaa_maps in the map dump
+ %% to make sure it will be the first in the list when sorted
+ %% on names.
+ [#proc{pid=Pid0,name=Name}|_Rest] = lists:keysort(#proc.name,Procs),
+ "aaaaaaaa_maps" = Name,
+ Pid = pid_to_list(Pid0),
+ {ok,ProcDetails=#proc{},[]} = crashdump_viewer:proc_details(Pid),
+ io:format(" process details ok",[]),
+
+ #proc{dict=Dict} = ProcDetails,
+ %% io:format("~p\n", [Dict]),
+ Maps = crashdump_helper:create_maps(),
+ Maps = proplists:get_value(maps,Dict),
+ io:format(" maps ok",[]),
+ ok;
_ ->
ok
end,
ok.
+verify_binaries([H|T1], [H|T2]) ->
+ %% Heap binary.
+ verify_binaries(T1, T2);
+verify_binaries([Bin|T1], [['#CDVBin',Offset,Size,Pos]|T2]) ->
+ %% Refc binary.
+ {ok,<<Bin:Size/binary>>} = crashdump_viewer:expand_binary({Offset,Size,Pos}),
+ verify_binaries(T1, T2);
+verify_binaries([], []) ->
+ ok.
-lookat_all_pids([]) ->
+lookat_all_pids([],_,_) ->
ok;
-lookat_all_pids([#proc{pid=Pid0}|Procs]) ->
+lookat_all_pids([#proc{pid=Pid0}|Procs],TruncAllowed,IncompAllowed) ->
Pid = pid_to_list(Pid0),
- {ok,_ProcDetails=#proc{},_ProcTW} = crashdump_viewer:proc_details(Pid),
- {ok,_Ets,_EtsTW} = crashdump_viewer:ets_tables(Pid),
- {ok,_Timers,_TimersTW} = crashdump_viewer:timers(Pid),
- lookat_all_pids(Procs).
+ {ok,_ProcDetails=#proc{},ProcTW} = crashdump_viewer:proc_details(Pid),
+ {ok,_Ets,EtsTW} = crashdump_viewer:ets_tables(Pid),
+ {ok,_Timers,TimersTW} = crashdump_viewer:timers(Pid),
+ case {ProcTW,EtsTW,TimersTW} of
+ {[],[],[]} ->
+ ok;
+ {["WARNING: This process has an incomplete heap."++_],[],[]}
+ when IncompAllowed ->
+ ok; % native libs, literals might not be included in dump
+ _ when TruncAllowed ->
+ ok; % truncated dump
+ TWs ->
+ ct:fail({unexpected_warning,TWs})
+ end,
+ lookat_all_pids(Procs,TruncAllowed,IncompAllowed).
lookat_all_ports([]) ->
ok;
@@ -574,21 +676,35 @@ do_create_dumps(DataDir,Rel) ->
current ->
CD3 = dump_with_args(DataDir,Rel,"instr","+Mim true"),
CD4 = dump_with_strange_module_name(DataDir,Rel,"strangemodname"),
- Tmp = dump_with_args(DataDir,Rel,"trunc_bytes",""),
- {ok,#file_info{size=Max}} = file:read_file_info(Tmp),
- ok = file:delete(Tmp),
- Bytes = max(15,rand:uniform(Max)),
- CD5 = dump_with_args(DataDir,Rel,"trunc_bytes",
- "-env ERL_CRASH_DUMP_BYTES " ++
- integer_to_list(Bytes)),
+ CD5 = dump_with_size_limit_reached(DataDir,Rel,"trunc_bytes"),
CD6 = dump_with_unicode_atoms(DataDir,Rel,"unicode"),
- TruncatedDumps = truncate_dump(CD1),
- {[CD1,CD2,CD3,CD4,CD5,CD6|TruncatedDumps], DosDump};
+ CD7 = dump_with_maps(DataDir,Rel,"maps"),
+ TruncDumpMod = truncate_dump_mod(CD1),
+ TruncatedDumpsBinary = truncate_dump_binary(CD1),
+ {[CD1,CD2,CD3,CD4,CD5,CD6,CD7,TruncDumpMod|TruncatedDumpsBinary],
+ DosDump};
_ ->
{[CD1,CD2], DosDump}
end.
-truncate_dump(File) ->
+truncate_dump_mod(File) ->
+ {ok,Bin} = file:read_file(File),
+ ModNameBin = atom_to_binary(?helper_mod,latin1),
+ NewLine = case os:type() of
+ {win32,_} -> <<"\r\n">>;
+ _ -> <<"\n">>
+ end,
+ RE = <<NewLine/binary,"=mod:",ModNameBin/binary,
+ NewLine/binary,"Current size: [0-9]*",
+ NewLine/binary,"Current attributes: ...">>,
+ {match,[{Pos,Len}]} = re:run(Bin,RE),
+ Size = Pos + Len,
+ <<Truncated:Size/binary,_/binary>> = Bin,
+ DumpName = filename:rootname(File) ++ ".trunc_mod",
+ file:write_file(DumpName,Truncated),
+ DumpName.
+
+truncate_dump_binary(File) ->
{ok,Bin} = file:read_file(File),
BinTag = <<"\n=binary:">>,
Colon = <<":">>,
@@ -596,7 +712,10 @@ truncate_dump(File) ->
{win32,_} -> <<"\r\n">>;
_ -> <<"\n">>
end,
- [StartBin,AfterTag] = binary:split(Bin,BinTag),
+ %% Split after "our binary" created by crashdump_helper
+ %% (it may not be the first binary).
+ RE = <<"\n=binary:(?=[0-9A-Z]+",NewLine/binary,"FF:AQID)">>,
+ [StartBin,AfterTag] = re:split(Bin,RE,[{parts,2}]),
[AddrAndSize,BinaryAndRest] = binary:split(AfterTag,Colon),
[Binary,_Rest] = binary:split(BinaryAndRest,NewLine),
TruncSize = byte_size(Binary) - 2,
@@ -689,6 +808,28 @@ dump_with_strange_module_name(DataDir,Rel,DumpName) ->
?t:stop_node(n1),
CD.
+dump_with_size_limit_reached(DataDir,Rel,DumpName) ->
+ Tmp = dump_with_args(DataDir,Rel,DumpName,""),
+ {ok,#file_info{size=Max}} = file:read_file_info(Tmp),
+ ok = file:delete(Tmp),
+ dump_with_size_limit_reached(DataDir,Rel,DumpName,Max).
+
+dump_with_size_limit_reached(DataDir,Rel,DumpName,Max) ->
+ Bytes = max(15,rand:uniform(Max)),
+ CD = dump_with_args(DataDir,Rel,DumpName,
+ "-env ERL_CRASH_DUMP_BYTES " ++
+ integer_to_list(Bytes)),
+ {ok,#file_info{size=Size}} = file:read_file_info(CD),
+ if Size =< Bytes ->
+ %% This means that the dump was actually smaller than the
+ %% randomly selected truncation size, so we'll just do it
+ %% again with a smaller number
+ ok = file:delete(CD),
+ dump_with_size_limit_reached(DataDir,Rel,DumpName,Size-3);
+ true ->
+ CD
+ end.
+
dump_with_unicode_atoms(DataDir,Rel,DumpName) ->
Opt = rel_opt(Rel),
Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"",
@@ -699,6 +840,16 @@ dump_with_unicode_atoms(DataDir,Rel,DumpName) ->
?t:stop_node(n1),
CD.
+dump_with_maps(DataDir,Rel,DumpName) ->
+ Opt = rel_opt(Rel),
+ Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"",
+ PzOpt = [{args,Pz}],
+ {ok,N1} = ?t:start_node(n1,peer,Opt ++ PzOpt),
+ {ok,_Pid} = rpc:call(N1,crashdump_helper,dump_maps,[]),
+ CD = dump(N1,DataDir,Rel,DumpName),
+ ?t:stop_node(n1),
+ CD.
+
dump(Node,DataDir,Rel,DumpName) ->
Crashdump = filename:join(DataDir, dump_prefix(Rel)++DumpName),
rpc:call(Node,os,putenv,["ERL_CRASH_DUMP",Crashdump]),
@@ -751,6 +902,11 @@ dump_prefix(current) ->
dump_prefix(Rel) ->
lists:concat(["r",Rel,"_dump."]).
+get_rel_from_dump_name(File) ->
+ Name = filename:basename(File),
+ ["r"++Rel|_] = string:split(Name,"_"),
+ list_to_integer(Rel).
+
compat_rel(current) ->
"";
compat_rel(Rel) ->
diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl
index 0db2c1ea77..fd4f93f662 100644
--- a/lib/observer/test/observer_SUITE.erl
+++ b/lib/observer/test/observer_SUITE.erl
@@ -113,7 +113,12 @@ appup_file(Config) when is_list(Config) ->
basic(suite) -> [];
basic(doc) -> [""];
basic(Config) when is_list(Config) ->
- timer:send_after(100, "foobar"), %% Otherwise the timer server gets added to procs
+ %% Start these before
+ wx:new(),
+ wx:destroy(),
+ timer:send_after(100, "foobar"),
+ {foo, node@machine} ! dummy_msg, %% start distribution stuff
+ %% Otherwise ever lasting servers gets added to procs
ProcsBefore = processes(),
ProcInfoBefore = [{P,process_info(P)} || P <- ProcsBefore],
NumProcsBefore = length(ProcsBefore),
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index 5f43198f85..fc1fca0925 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.5
+OBSERVER_VSN = 2.6
diff --git a/lib/odbc/doc/src/Makefile b/lib/odbc/doc/src/Makefile
index b66d00a88b..b3f93a7e9c 100644
--- a/lib/odbc/doc/src/Makefile
+++ b/lib/odbc/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2016. All Rights Reserved.
+# Copyright Ericsson AB 1999-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -38,11 +38,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = odbc.xml
+XML_REF3_FILES = odbc.xml
-XML_PART_FILES = part.xml \
- part_notes.xml \
- part_notes_history.xml
+XML_PART_FILES = part.xml
XML_HTML_FILES = \
notes_history.xml
@@ -52,21 +50,15 @@ XML_CHAPTER_FILES = \
getting_started.xml \
databases.xml \
error_handling.xml \
- notes.xml
+ notes.xml
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
GIF_FILES = \
- book.gif \
- odbc.gif \
- note.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif \
- odbc_app_arc.gif
+ odbc_app_arc.gif
# ----------------------------------------------------
@@ -87,10 +79,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -117,12 +109,12 @@ man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%) # We depend just to copy them to ../html
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/odbc/doc/src/book.gif b/lib/odbc/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/odbc/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/odbc/doc/src/fascicules.xml b/lib/odbc/doc/src/fascicules.xml
deleted file mode 100644
index 91251d4b20..0000000000
--- a/lib/odbc/doc/src/fascicules.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
-
diff --git a/lib/odbc/doc/src/note.gif b/lib/odbc/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/odbc/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/odbc/doc/src/notes.gif b/lib/odbc/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/odbc/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml
index cc25a21c74..2aa55ca99c 100644
--- a/lib/odbc/doc/src/notes.xml
+++ b/lib/odbc/doc/src/notes.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -21,7 +21,7 @@
limitations under the License.
</legalnotice>
-
+
<title>ODBC Release Notes</title>
<prepared>otp_appnotes</prepared>
<docno>nil</docno>
@@ -32,7 +32,22 @@
<p>This document describes the changes made to the odbc application.
</p>
- <section><title>ODBC 2.12</title>
+ <section><title>ODBC 2.12.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.12</title>
<section><title>Improvements and New Features</title>
<list>
@@ -568,9 +583,9 @@
</section>
-
+
<section><title>ODBC 2.10.7</title>
-
+
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -583,7 +598,7 @@
</list>
</section>
-
+
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -720,7 +735,7 @@
</section>
<section><title>ODBC 2.10.5</title>
-
+
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -732,12 +747,12 @@
</item>
</list>
</section>
-
+
</section>
<section><title>ODBC 2.10.4</title>
-
+
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -756,7 +771,7 @@
</item>
</list>
</section>
-
+
</section>
<section><title>ODBC 2.10.3</title>
@@ -787,7 +802,7 @@
</section>
<section><title>ODBC 2.10.2</title>
-
+
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -802,9 +817,9 @@
</section>
</section>
-
+
<section><title>ODBC 2.10.1</title>
-
+
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -852,7 +867,7 @@
</section>
<section><title>ODBC 2.10</title>
-
+
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -865,11 +880,11 @@
</item>
</list>
</section>
-
+
</section>
<section><title>ODBC 2.0.9</title>
-
+
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -897,9 +912,9 @@
</item>
</list>
</section>
-
+
</section>
-
+
<section>
<title>ODBC 2.0.8</title>
@@ -1142,9 +1157,5 @@
</item>
</list>
</section>
- <!-- p>For information about older versions see
- <url href="part_notes_history_frame.html">release notes history</url>.</p -->
</section>
</chapter>
-
-
diff --git a/lib/odbc/doc/src/odbc.gif b/lib/odbc/doc/src/odbc.gif
deleted file mode 100644
index fbbabee5aa..0000000000
--- a/lib/odbc/doc/src/odbc.gif
+++ /dev/null
Binary files differ
diff --git a/lib/odbc/doc/src/odbc_index.gif b/lib/odbc/doc/src/odbc_index.gif
deleted file mode 100644
index fbbabee5aa..0000000000
--- a/lib/odbc/doc/src/odbc_index.gif
+++ /dev/null
Binary files differ
diff --git a/lib/odbc/doc/src/part_notes.xml b/lib/odbc/doc/src/part_notes.xml
deleted file mode 100644
index 8f1dbd3fbc..0000000000
--- a/lib/odbc/doc/src/part_notes.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>ODBC Release Notes</title>
- <prepared>Ingela Anderton Andin</prepared>
- <docno></docno>
- <date>2004-09-07</date>
- <rev></rev>
- <file>part_notes.sgml</file>
- </header>
- <description>
- <p>An interface to relational SQL-databases built on ODBC (Open
- Database Connectivity). </p>
- <p>For information about older versions see
- <url href="part_notes_history_frame.html">release notes history</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
-
diff --git a/lib/odbc/doc/src/part_notes_history.xml b/lib/odbc/doc/src/part_notes_history.xml
deleted file mode 100644
index 6f855f6ed1..0000000000
--- a/lib/odbc/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Odbc</title>
- <prepared>Ingela Anderton Andin</prepared>
- <docno></docno>
- <date>2004-09-30</date>
- <rev></rev>
- <file>part_notes.sgml</file>
- </header>
- <include file="notes_history"></include>
-</part>
-
-
diff --git a/lib/odbc/doc/src/ref_man.gif b/lib/odbc/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/odbc/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/odbc/doc/src/user_guide.gif b/lib/odbc/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/odbc/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/odbc/doc/src/warning.gif b/lib/odbc/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/odbc/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index 2e313570e1..3f7677a71d 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.12
+ODBC_VSN = 2.12.1
diff --git a/lib/orber/doc/src/CosNaming.xml b/lib/orber/doc/src/CosNaming.xml
index d69b604f2f..251e721df1 100644
--- a/lib/orber/doc/src/CosNaming.xml
+++ b/lib/orber/doc/src/CosNaming.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -55,8 +55,8 @@ struct Binding {
typedef sequence <Binding> BindingList;
]]></code>
- <p>To get access to the record definitions for the structs use:
- <c>-include_lib("orber/COSS/CosNaming.hrl").</c>.</p>
+ <p>To get access to the record definitions for the structs use:</p>
+ <code>-include_lib("orber/COSS/CosNaming.hrl").</code>
<p>Names are not an ORB object but the can be structured in components as seen by
the definition above. There are no requirements on names so the service can support
many different conventions and standards.</p>
diff --git a/lib/orber/doc/src/CosNaming_NamingContext.xml b/lib/orber/doc/src/CosNaming_NamingContext.xml
index 96a6367cbb..4c83e6a240 100644
--- a/lib/orber/doc/src/CosNaming_NamingContext.xml
+++ b/lib/orber/doc/src/CosNaming_NamingContext.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -37,61 +37,54 @@
naming contexts. Name context may be named in other contexts and cycles are
permitted.</p>
<p>The type <c>NameComponent</c> used below is defined as:</p>
- <code type="none">
- -record('CosNaming_NameComponent', {id, kind=""}).
- </code>
+ <code type="erl">-record('CosNaming_NameComponent', {id, kind=""}).</code>
<p>where <c>id</c> and <c>kind</c> are strings. </p>
<p>The type <c>Binding</c> used below is defined as:</p>
- <code type="none">
- -record('CosNaming_Binding', {binding_name, binding_type}).
- </code>
+ <code type="erl">-record('CosNaming_Binding', {binding_name, binding_type}).</code>
<p>where <c>binding_name</c> is a Name and <c>binding_type</c> is an enum which
has the values <c>nobject</c> and <c>ncontext</c>.</p>
<p>Both these records are defined in the file <c>CosNaming.hrl</c> and it
is included with:</p>
- <code type="none">
- -include_lib("orber/COSS/CosNaming/CosNaming.hrl").
- </code>
+ <code type="erl">-include_lib("orber/COSS/CosNaming/CosNaming.hrl").</code>
<p>There are a number of exceptions that can be returned from functions in this
interface.</p>
<list type="bulleted">
<item>
<p>NotFound is defined as </p>
- <code type="none">
--record('CosNaming_NamingContext_NotFound',
- {rest_of_name, why}). </code>
+ <code type="erl">
+-record('CosNaming_NamingContext_NotFound',
+ {rest_of_name, why}).</code>
</item>
<item>
<p>CannotProceed is defined as </p>
- <code type="none">
--record('CosNaming_NamingContext_CannotProceed',
- {rest_of_name, cxt}). </code>
+ <code type="erl">
+-record('CosNaming_NamingContext_CannotProceed',
+ {rest_of_name, cxt}).
+ </code>
</item>
<item>
<p>InvalidName is defined as </p>
- <code type="none">
--record('CosNaming_NamingContext_InvalidName', {}). </code>
+ <code type="erl">
+-record('CosNaming_NamingContext_InvalidName', {}).
+ </code>
</item>
<item>
<p>NotFound is defined as </p>
- <code type="none">
--record('CosNaming_NamingContext_NotFound', {}). </code>
+ <code type="erl">-record('CosNaming_NamingContext_NotFound', {}).</code>
</item>
<item>
<p>AlreadyBound is defined as </p>
- <code type="none">
--record('CosNaming_NamingContext_AlreadyBound', {}). </code>
+ <code type="erl">-record('CosNaming_NamingContext_AlreadyBound', {}).</code>
</item>
<item>
<p>NotEmpty is defined as </p>
- <code type="none">
--record('CosNaming_NamingContext_NotEmpty', {). </code>
+ <code type="erl">-record('CosNaming_NamingContext_NotEmpty', {}).</code>
</item>
</list>
<p>These exceptions are defined in the file <c>CosNaming_NamingContext.hrl</c> and it
is included with:</p>
- <code type="none">
- -include_lib("orber/COSS/CosNaming/CosNaming_NamingContext.hrl").
+ <code type="erl">
+-include_lib("orber/COSS/CosNaming/CosNaming_NamingContext.hrl").
</code>
</description>
<funcs>
diff --git a/lib/orber/doc/src/CosNaming_NamingContextExt.xml b/lib/orber/doc/src/CosNaming_NamingContextExt.xml
index a571b97ccb..2af3deadda 100644
--- a/lib/orber/doc/src/CosNaming_NamingContextExt.xml
+++ b/lib/orber/doc/src/CosNaming_NamingContextExt.xml
@@ -4,8 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year>
- <year>2016</year>
+ <year>2000</year><year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -36,8 +35,8 @@
<description>
<p>To get access to the record definitions for the structures use: <br></br>
</p>
- <code type="none">
- -include_lib("orber/COSS/CosNaming/CosNaming.hrl").
+ <code type="erl">
+-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
</code>
<p>This module also exports the functions described in:</p>
<list type="bulleted">
diff --git a/lib/orber/doc/src/Makefile b/lib/orber/doc/src/Makefile
index ecb0206443..c77345f12b 100644
--- a/lib/orber/doc/src/Makefile
+++ b/lib/orber/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
+# Copyright Ericsson AB 1997-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -57,8 +57,7 @@ XML_REF3_FILES = \
orber_acl.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml
+ part.xml
XML_CHAPTER_FILES = \
ch_contents.xml \
@@ -79,15 +78,11 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-TECHNICAL_DESCR_FILES =
+TECHNICAL_DESCR_FILES =
GIF_FILES = \
- book.gif \
- notes.gif \
- ref_man.gif \
- user_guide.gif \
name.gif \
orbs.gif \
theORB.gif \
@@ -114,7 +109,7 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html)
@@ -126,10 +121,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -157,11 +152,11 @@ man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/orber/doc/src/any.xml b/lib/orber/doc/src/any.xml
index f51712c97e..c94a2132d8 100644
--- a/lib/orber/doc/src/any.xml
+++ b/lib/orber/doc/src/any.xml
@@ -4,8 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1998</year>
- <year>2016</year>
+ <year>1998</year><year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -41,9 +40,7 @@
<p>The type <c>TC</c> used below describes an IDL type and is a tuple according
to the to the Erlang language mapping.</p>
<p>The type <c>Any</c> used below is defined as:</p>
- <code type="none">
- -record(any, {typecode, value}).
- </code>
+ <code type="erl">-record(any, {typecode, value}).</code>
<p>where <c>typecode</c> is a TC tuple and <c>value</c> is an Erlang term of
the type defined by the typecode field.</p>
</description>
diff --git a/lib/orber/doc/src/book.gif b/lib/orber/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/orber/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/orber/doc/src/ch_debugging.xml b/lib/orber/doc/src/ch_debugging.xml
index a036cf5231..debac4313e 100644
--- a/lib/orber/doc/src/ch_debugging.xml
+++ b/lib/orber/doc/src/ch_debugging.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2016</year>
+ <year>2001</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -121,7 +121,7 @@ Result.......: {'EXCEPTION',{'MARSHAL',[],102,'COMPLETED_NO'}}
uses the <c>error_logger</c> module to generate the logs. If the traffic
is intense you probably want to write the reports to a log-file.
This is done by, for example, invoking:</p>
- <code type="none">
+ <code type="erl">
erl> error_logger:tty(false).
erl> error_logger:logfile({open, "/tmp/IIOPTrace"}).
</code>
diff --git a/lib/orber/doc/src/ch_exceptions.xml b/lib/orber/doc/src/ch_exceptions.xml
index 52735dc394..17657d0d4a 100644
--- a/lib/orber/doc/src/ch_exceptions.xml
+++ b/lib/orber/doc/src/ch_exceptions.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2016</year>
+ <year>2001</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -192,7 +192,7 @@ module MyModule {
<title>Throwing Exceptions</title>
<p>To be able to raise <c>MyException</c> or <c>MyExceptionMsg</c> exceptions,
the generated <c>MyModule.hrl</c> must be included, and typical usage is:</p>
- <code type="none">
+ <code type="erl">
-module('MyModule_MyInterface_impl').
-include("MyModule.hrl").
diff --git a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml
index a0feda3f84..eaa88f24f1 100644
--- a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml
+++ b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -819,7 +819,7 @@ module x {
<p><term id="Type Codes"><termdef>Type codes give a complete description of the type including all its components and structure.</termdef></term>are, for example, used in <seealso marker="any">Any</seealso> values.
Hence, we can encapsulate the <c>employee</c> struct in an <c>any</c>
type by:</p>
- <code type="none">
+ <code type="erl">
%% Erlang code
....
AnEmployee = #'DB_employee'{'No' = 1,
@@ -962,7 +962,7 @@ R1 = m_i:foo(Obj, 55),
representation of the IDL-type <c>void</c>, must be returned by
<c>baz</c> and <c>'_set_RWAttribute'</c>.
These operations can be implemented in the call-back module as:</p>
- <code type="none">
+ <code type="erl">
'_set_RWAttribute'(State, Long) ->
{reply, ok, State}.
@@ -1011,7 +1011,7 @@ $> erlc +"{be,erl_template}" DB.idl
<p>We begin with implementing the <c>DB_Access_impl.erl</c> module, which,
if we used <c>erl_template</c>, will look like the following. All we need
to do is to add the logic to the <c>logon</c> operation.</p>
- <code type="none"><![CDATA[
+ <code type="erl"><![CDATA[
%%----------------------------------------------------------------------
%% <LICENSE>
%%
@@ -1154,7 +1154,7 @@ $ <input>erlc *.erl</input>
<seealso marker="ch_exceptions">Exceptions</seealso> chapter.
In the following example, only the implementation of the API functions
are shown:</p>
- <code type="none">
+ <code type="erl">
%%======================================================================
%% API Functions
%%======================================================================
diff --git a/lib/orber/doc/src/ch_install.xml b/lib/orber/doc/src/ch_install.xml
index 9bc974225d..65faa91ccf 100644
--- a/lib/orber/doc/src/ch_install.xml
+++ b/lib/orber/doc/src/ch_install.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -106,7 +106,7 @@
necessary to start the Erlang distribution (i.e. using <c>-name/-sname</c>).</p>
<p>If we use <c>ram_copies</c> there is no need for creating a disk based
schema. Simply use:</p>
- <code type="none">
+ <code type="erl">
erl> mnesia:start().
erl> corba:orb_init([{domain, "MyRAMSingleNodeORB"}]).
erl> orber:install([node()], [{ifr_storage_type, ram_copies}]).
@@ -115,7 +115,7 @@ erl> orber:start().
<p>If you installation requires <c>disc_copies</c> you must begin with
creating a Mnesia schema. Otherwise, the installation is similar
to a RAM installation:</p>
- <code type="none">
+ <code type="erl">
erl> mnesia:create_schema([node()]).
erl> mnesia:start().
erl> corba:orb_init([{domain, "MyDiskSingleNodeORB"}]).
@@ -137,7 +137,7 @@ erl> orber:start().
<title>Install RAM Based Multi Node Orber</title>
<p>Within a domain Orber uses the Erlang distribution protocol. Hence, you
<em>must</em> start it first by, for example, using:</p>
- <code type="none">
+ <code type="erl">
hostA> erl -sname nodeA
</code>
<p>In this example, we assume that we want to use two nodes; <c>nodeA</c> and
@@ -146,7 +146,7 @@ hostA> erl -sname nodeA
parameter <c>extra_db_nodes</c> or use <c>mnesia:change_config/2</c>. To
begin with, Mnesia must be started on all nodes before we can install
Orber:</p>
- <code type="none">
+ <code type="erl">
nodeA@hostA> mnesia:start().
nodeA@hostA> mnesia:change_config(extra_db_nodes,
[nodeA@hostA, nodeB@hostB]).
@@ -154,7 +154,7 @@ nodeA@hostA> mnesia:change_config(extra_db_nodes,
<p>After that the above have been repeated on <c>nodeB</c> we must
first make sure that both nodes will use the same domain name, then
we can install Orber:</p>
- <code type="none">
+ <code type="erl">
nodeA@hostA> corba:orb_init([{domain, "MyRAMMultiNodeORB"}]).
nodeA@hostA> orber:install([nodeA@hostA, nodeB@hostB],
[{ifr_storage_type, ram_copies}]).
@@ -162,7 +162,7 @@ nodeA@hostA> orber:start().
</code>
<p>Note that you can only invoke <c>orber:install/1/2</c> on one of the
nodes. Now we can start Orber on the other node:</p>
- <code type="none">
+ <code type="erl">
nodeB@hostB> corba:orb_init([{domain, "MyRAMMultiNodeORB"}]).
nodeB@hostB> orber:start().
</code>
@@ -173,7 +173,7 @@ nodeB@hostB> orber:start().
<p>As for RAM based multi-node Orber installations, the Erlang distribution
must be started (e.g. erl -sname nodeA). The major difference is that
when it is disk based a Mnesia schema must be created:</p>
- <code type="none">
+ <code type="erl">
nodeA@hostA> mnesia:create_schema([nodeA@hostA, nodeB@hostB]).
nodeA@hostA> mnesia:start().
</code>
@@ -183,7 +183,7 @@ nodeA@hostA> mnesia:start().
<c>mnesia:start()</c>) on <c>nodeB</c>.</p>
<p>After Mnesia have been started on all nodes, you must confirm that all
nodes have the same domain name, then Orber is ready to be installed:</p>
- <code type="none">
+ <code type="erl">
nodeA@hostA> corba:orb_init([{domain, "MyDiskMultiNodeORB"}]).
nodeA@hostA> orber:install([nodeA@hostA, nodeB@hostB],
[{ifr_storage_type, disc_copies}]).
@@ -191,7 +191,7 @@ nodeA@hostA> orber:start().
</code>
<p>Note that you can only invoke <c>orber:install/1/2</c> on one of the
nodes. Now we can start Orber on the other node:</p>
- <code type="none">
+ <code type="erl">
nodeB@hostB> corba:orb_init([{domain, "MyDiskMultiNodeORB"}]).
nodeB@hostB> orber:start().
</code>
@@ -918,7 +918,7 @@ TCP Firewall With NAT</icaption>
verify whether access would be granted or not. For example, if Orber would
be started with the ACL <c>[{tcp_out, "10.1.1.1/8#4001/5001"}]</c>, then
<c>orber_acl:match/2</c> would behave as follows:</p>
- <code type="none">
+ <code type="erl">
erl> orber_acl:match({11,1,1,1}, tcp_out).
false
@@ -967,7 +967,7 @@ erl> orber_acl:match({10,1,1,1}, tcp_out, true).
the configuration of the underlying system.</p>
<p>Adding the interface context, for generated stubs/skeletons, is done in the
following way:</p>
- <code type="none">
+ <code type="erl">
Ctx = #'IOP_ServiceContext'{context_id = ?ORBER_GENERIC_CTX_ID,
context_data = {interface, "10.0.0.1"}},
'CosNaming_NamingContext':resolve(NS, [{context, [Ctx]}], Name),
diff --git a/lib/orber/doc/src/ch_interceptors.xml b/lib/orber/doc/src/ch_interceptors.xml
index 392fe7de82..4a9f8e69ca 100644
--- a/lib/orber/doc/src/ch_interceptors.xml
+++ b/lib/orber/doc/src/ch_interceptors.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2016</year>
+ <year>2001</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -121,7 +121,7 @@ The Invocation Order of Interceptor Functions.</icaption>
we store which objects the clients are allowed to invoke operations on
and <c>ChecksumModule</c> determines which module we should use to handle
the checksums. </p>
- <code type="none">
+ <code type="erl">
new_in_connection(Arg, Host, Port) ->
%% Since we only use one interceptor we do not care about the
%% input Arg since it is set do undefined by Orber.
@@ -141,7 +141,7 @@ new_in_connection(Arg, Host, Port) ->
<p>When a new request comes in the first interceptor function to be invoked is
<c>in_request_encoded</c>. We will remove the checksum from the coded
request body in the following way:</p>
- <code type="none">
+ <code type="erl">
in_request_encoded({ObjTable, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) ->
NewBin = ChecksumModule:remove_checksum(Bin),
{NewBin, Extra}.
@@ -154,7 +154,7 @@ in_request_encoded({ObjTable, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) ->
good throughput.</p>
<p>If we want to we can restrict any clients to only use a subset of operations
exported by a server:</p>
- <code type="none">
+ <code type="erl">
in_request({ObjTable, ChecksumModule}, ObjKey, Ctx, Op, Params, Extra) ->
case ets:lookup(ObjTable, {ObjKey, Op}) of
[] ->
@@ -166,13 +166,13 @@ in_request({ObjTable, ChecksumModule}, ObjKey, Ctx, Op, Params, Extra) ->
<p>At this point Orber are now ready to invoke the operation on the target
object. Since we do not care about what the reply is the <c>out_reply</c>
function do nothing, i.e.:</p>
- <code type="none">
+ <code type="erl">
out_reply(_, _, _, _, Reply, Extra) ->
{Reply, Extra}.
</code>
<p>If the client side ORB expects a checksum to be added to the reply we
add it by using:</p>
- <code type="none">
+ <code type="erl">
out_reply_encoded({ObjTable, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) ->
NewBin = ChecksumModule:add_checksum(Bin),
{NewBin, Extra}.
@@ -183,8 +183,7 @@ out_reply_encoded({ObjTable, ChecksumModule}, ObjKey, Ctx, Op, Bin, Extra) ->
</warning>
<p>For outgoing requests the principle is the same. Hence, it is not further
described here. The complete interceptor module would look like:</p>
- <code type="none">
-
+ <code type="erl">
-module(myInterceptor).
%% Interceptor functions.
diff --git a/lib/orber/doc/src/ch_naming_service.xml b/lib/orber/doc/src/ch_naming_service.xml
index bcbab2a597..991402ae86 100644
--- a/lib/orber/doc/src/ch_naming_service.xml
+++ b/lib/orber/doc/src/ch_naming_service.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -116,7 +116,7 @@ Figure 1: Contextual object relationships using the Naming Service.</icaption>
<title>Fetch Initial Reference to the Naming Service</title>
<p>In order to use the naming service you have to fetch an
initial reference to it. This is done with:</p>
- <code type="none">
+ <code type="erl">
NS = corba:resolve_initial_references("NameService").
</code>
<note>
@@ -128,14 +128,14 @@ NS = corba:resolve_initial_references("NameService").
<title>Creating a Naming Context</title>
<p>There are two functions for creating a naming context.
The first function, which only creates a naming context object is:</p>
- <code type="none">
+ <code type="erl">
NC = 'CosNaming_NamingContext':new_context(NS).
</code>
<p>The other function creates a naming context and binds it to a name in
an already existing naming context (the initial context in this
example):
</p>
- <code type="none">
+ <code type="erl">
NC = 'CosNaming_NamingContext':bind_new_context(NS, lname:new(["new"])).
</code>
</section>
@@ -150,19 +150,19 @@ NC = 'CosNaming_NamingContext':bind_new_context(NS, lname:new(["new"])).
<list type="ordered">
<item>
<p>Use the naming library functions to create a name</p>
- <code type="none">
+ <code type="erl">
Name = lname:new(["object"]).
</code>
</item>
<item>
<p>Use CosNaming::NamingContext::bind() to bind a name to an object</p>
- <code type="none">
+ <code type="erl">
'CosNaming_NamingContext':bind(Sc, Name, Object).
</code>
</item>
<item>
<p>Use CosNaming::NamingContext::unbind() to remove the NameBinding from an object</p>
- <code type="none">
+ <code type="erl">
'CosNaming_NamingContext':unbind(Sc, Name).
</code>
</item>
@@ -180,19 +180,19 @@ Name = lname:new(["object"]).
<list type="ordered">
<item>
<p>Use the naming library functions to create a name path:</p>
- <code type="none">
+ <code type="erl">
Name = lname:new(["workgroup", "services"]).
</code>
</item>
<item>
<p>Use CosNaming::NamingContext::resolve() to to resolve the name to an object</p>
- <code type="none">
+ <code type="erl">
Sc = 'CosNaming_NamingContext':resolve(NS, Name).
</code>
</item>
</list>
<p>An alternative is to use:</p>
- <code type="none">
+ <code type="erl">
Sc = corba:string_to_object("corbaname:rir:/NameService#workgroup/services/").
</code>
<p>The <c>corbaname</c> schema is described further in the Interoperable
@@ -205,7 +205,7 @@ Sc = corba:string_to_object("corbaname:rir:/NameService#workgroup/services/").
<item>
<p>Use CosNaming::NamingContext::list() to list all the bindings in a context</p>
<p>The following code retrieves and lists up to 10 bindings from a context.</p>
- <code type="none">
+ <code type="erl">
{BList, BIterator} = 'CosNaming_NamingContext':list(Sc, 10).
lists:foreach(fun({{Id, Kind},BindingType}) -> case BindingType of
@@ -229,8 +229,8 @@ lists:foreach(fun({{Id, Kind},BindingType}) -> case BindingType of
<em>must be removed</em> otherwise dangling processes will occur.
Use <c>CosNaming::BindingIterator::destroy()</c> to remove it.</p>
</warning>
- <code type="none">
- 'CosNaming_NamingContext':destroy(BIterator).
+ <code type="erl">
+'CosNaming_NamingContext':destroy(BIterator).
</code>
</section>
@@ -241,7 +241,7 @@ lists:foreach(fun({{Id, Kind},BindingType}) -> case BindingType of
<list type="ordered">
<item>
<p>Use CosNaming::NamingContext::destroy() to remove a NamingContext</p>
- <code type="none">
+ <code type="erl">
'CosNaming_NamingContext':destroy(Sc).
</code>
</item>
@@ -318,13 +318,13 @@ lists:foreach(fun({{Id, Kind},BindingType}) -> case BindingType of
listed below, they should be associated with. The <c>NameService</c>
key may <em>not</em> be changed in Orber. If you want to add one of the
reserved keys as an initial service, simply use:</p>
- <code type="none">
+ <code type="erl">
1> Factory = cosNotificationApp:start_global_factory().
2> corba:add_initial_service("NotificationService", Factory).
</code>
<p>This object can then be easily resolved by any other ORB, supporting
the Interoperable Naming Service, by using:</p>
- <code type="none">
+ <code type="erl">
3> NF = corba:string_to_object("corbaloc::[email protected]:4001/NotificationService").
</code>
<table>
@@ -438,13 +438,13 @@ lists:foreach(fun({{Id, Kind},BindingType}) -> case BindingType of
<tcaption>Stringified Name representation</tcaption>
</table>
<p>After creating a stringified Name we can either use:</p>
- <code type="none">
+ <code type="erl">
NameStr = "org.erlang",
NS = corba:resolve_initial_references("NameService"),
Obj = 'CosNaming_NamingContextExt':resolve_str(NS, NameStr),
</code>
<p>or concatenate the Name String using:</p>
- <code type="none">
+ <code type="erl">
NameStr = "Swedish/Soccer/Champions",
Address = "corbaname:iiop:[email protected]:2000/NameService",
NS = corba:resolve_initial_references("NameService"),
diff --git a/lib/orber/doc/src/ch_orberweb.xml b/lib/orber/doc/src/ch_orberweb.xml
index be1d7fb983..c9dcc382e6 100644
--- a/lib/orber/doc/src/ch_orberweb.xml
+++ b/lib/orber/doc/src/ch_orberweb.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2016</year>
+ <year>2001</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -208,8 +208,7 @@
<p>You may choose to start OrberWeb on node, on which Orber is running or not. But
the Erlang distribution must be started (e.g. by using -sname aNodeName). Now, all
you have to do is to invoke:</p>
- <code type="none">
-
+ <code type="none">
erl> webtool:start().
WebTool is available at http://localhost:8888/
Or http://127.0.0.1:8888/
diff --git a/lib/orber/doc/src/ch_stubs.xml b/lib/orber/doc/src/ch_stubs.xml
index 144191a66a..9290c127f9 100644
--- a/lib/orber/doc/src/ch_stubs.xml
+++ b/lib/orber/doc/src/ch_stubs.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2016</year>
+ <year>1999</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -158,7 +158,7 @@ up_to_date
<p>Arguments and Replies are determined by the IDL-code and, hence, not
further described here.</p>
</note>
- <code type="none">
+ <code type="erl">
%%%-----------------------------------------------------------
%%% File : Module_Interface_impl.erl
%%% Author :
diff --git a/lib/orber/doc/src/corba.xml b/lib/orber/doc/src/corba.xml
index d89f035dba..fbfb55f2f2 100644
--- a/lib/orber/doc/src/corba.xml
+++ b/lib/orber/doc/src/corba.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -370,12 +370,12 @@ Example:
<desc>
<p>This function takes a <c>corbaname</c>, <c>corbaloc</c> or an IOR on the
external string representation and returns the object reference.</p>
- <p>To lookup the NameService reference, simply use
- <c>"corbaloc:iiop:[email protected]:4001/NameService"</c></p>
- <p>We can also resolve an object from the NameService by using
- <c>"corbaname:iiop:[email protected]:4001/NameService#org/Erlang/MyObj"</c></p>
- <p>To lookup the NameService reference with an IPv6 address, simply use
- <c>"corbaloc:iiop:1.2@[FEC1:0:3:0:0312:44AF:FAB1:3D01]:4001/NameService"</c></p>
+ <p>To lookup the NameService reference, simply use:</p>
+ <code>corbaloc:iiop:[email protected]:4001/NameService</code>
+ <p>We can also resolve an object from the NameService by using:</p>
+ <code>corbaname:iiop:[email protected]:4001/NameService#org/Erlang/MyObj</code>
+ <p>To lookup the NameService reference with an IPv6 address, simply use:</p>
+ <code>corbaloc:iiop:1.2@[FEC1:0:3:0:0312:44AF:FAB1:3D01]:4001/NameService</code>
<p>For more information about <c>corbaname</c> and <c>corbaloc</c>, see
the User's Guide (Interoperable Naming Service).</p>
<p>The <em>configuration</em> context is used to override the global
diff --git a/lib/orber/doc/src/fascicules.xml b/lib/orber/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/orber/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/orber/doc/src/fixed.xml b/lib/orber/doc/src/fixed.xml
index a751476cf7..ef4d1bd604 100644
--- a/lib/orber/doc/src/fixed.xml
+++ b/lib/orber/doc/src/fixed.xml
@@ -4,8 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2002</year>
- <year>2016</year>
+ <year>2002</year><year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -38,8 +37,8 @@
<description>
<p>This module contains functions that gives an interface to the CORBA fixed type.</p>
<p>The type <c>Fixed</c> used below is defined as:</p>
- <code type="none">
- -record(fixed, {digits, scale, value}).
+ <code type="erl">
+-record(fixed, {digits, scale, value}).
</code>
<p>where <c>digits</c> is the total amount of digits it consists of and
<c>scale</c> is the number of fractional digits. The <c>value</c> field
diff --git a/lib/orber/doc/src/lname.xml b/lib/orber/doc/src/lname.xml
index 09d6859777..c0c9be1a85 100644
--- a/lib/orber/doc/src/lname.xml
+++ b/lib/orber/doc/src/lname.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -44,14 +44,14 @@
the Names are represented as standard Erlang lists and therefor will be removed
by the garbage collector when not in use.</p>
<p>The type <c>NameComponent</c> used below is defined as:</p>
- <code type="none">
- -record('CosNaming_NameComponent', {id, kind=""}).
+ <code type="erl">
+-record('CosNaming_NameComponent', {id, kind=""}).
</code>
<p><c>id</c> and <c>kind</c> are strings. </p>
<p>The record is defined in the file <c>CosNaming.hrl</c> and it
is included with:</p>
- <code type="none">
- -include_lib("orber/COSS/CosNaming/CosNaming.hrl").
+ <code type="erl">
+-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
</code>
</description>
<funcs>
diff --git a/lib/orber/doc/src/lname_component.xml b/lib/orber/doc/src/lname_component.xml
index 631e5d0244..8b8001c0fb 100644
--- a/lib/orber/doc/src/lname_component.xml
+++ b/lib/orber/doc/src/lname_component.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -44,14 +44,14 @@
function because the NameComponents are represented as Erlang records and
therefor will be removed by the garbage collector when not in use.</p>
<p>The type <c>NameComponent</c> used below is defined as:</p>
- <code type="none">
- -record('CosNaming_NameComponent', {id, kind=""}).
+ <code type="erl">
+-record('CosNaming_NameComponent', {id, kind=""}).
</code>
<p><c>id</c> and <c>kind</c> are strings. </p>
<p>The record is defined in the file <c>CosNaming.hrl</c> and it
is included with:</p>
- <code type="none">
- -include_lib("orber/COSS/CosNaming/CosNaming.hrl").
+ <code type="erl">
+-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
</code>
</description>
<funcs>
diff --git a/lib/orber/doc/src/notes.gif b/lib/orber/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/orber/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml
index 5a82270b28..35da4f73da 100644
--- a/lib/orber/doc/src/notes.xml
+++ b/lib/orber/doc/src/notes.xml
@@ -33,7 +33,28 @@
<file>notes.xml</file>
</header>
- <section><title>Orber 3.8.3</title>
+ <section><title>Orber 3.8.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ <item>
+ <p> Removed the man warnings by using the code tag
+ instead of c tag. </p>
+ <p>
+ Own Id: OTP-14673</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Orber 3.8.3</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/orber/doc/src/orber.gif b/lib/orber/doc/src/orber.gif
deleted file mode 100644
index d78cf7d8ed..0000000000
--- a/lib/orber/doc/src/orber.gif
+++ /dev/null
Binary files differ
diff --git a/lib/orber/doc/src/part_notes.xml b/lib/orber/doc/src/part_notes.xml
deleted file mode 100644
index 61d9d4c3b9..0000000000
--- a/lib/orber/doc/src/part_notes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1997</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Orber Release Notes</title>
- <prepared>Lars Thors&eacute;n, Peter Lundell</prepared>
- <docno></docno>
- <date>1999-04-20</date>
- <rev>2.0</rev>
- </header>
- <description>
- <p>The Orber Application is an Erlang implementation of a CORBA Object
- Request Broker.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/orber/doc/src/ref_man.gif b/lib/orber/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/orber/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/orber/doc/src/summary.html.src b/lib/orber/doc/src/summary.html.src
deleted file mode 100644
index f88ee7218f..0000000000
--- a/lib/orber/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-A CORBA Object Request Broker
diff --git a/lib/orber/doc/src/user_guide.gif b/lib/orber/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/orber/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk
index 595e686cb7..bfd3f283b5 100644
--- a/lib/orber/vsn.mk
+++ b/lib/orber/vsn.mk
@@ -1 +1 @@
-ORBER_VSN = 3.8.3
+ORBER_VSN = 3.8.4
diff --git a/lib/os_mon/doc/src/Makefile b/lib/os_mon/doc/src/Makefile
index 91c7ae7bc3..4aa8879a91 100644
--- a/lib/os_mon/doc/src/Makefile
+++ b/lib/os_mon/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1997-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
include $(ERL_TOP)/make/target.mk
@@ -43,16 +43,14 @@ XML_REF3_FILES = cpu_sup.xml \
os_sup.xml \
nteventlog.xml
-XML_REF6_FILES = os_mon_app.xml
+XML_REF6_FILES = os_mon_app.xml
-XML_PART_FILES = part_notes.xml
+XML_PART_FILES =
XML_CHAPTER_FILES = notes.xml
BOOK_FILES = book.xml
-GIF_FILES = \
- note.gif \
- warning.gif
+GIF_FILES =
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
@@ -74,9 +72,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -96,17 +94,17 @@ man: $(MAN3_FILES) $(MAN6_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -122,4 +120,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6"
release_spec:
-
diff --git a/lib/os_mon/doc/src/fascicules.xml b/lib/os_mon/doc/src/fascicules.xml
deleted file mode 100644
index fadd37eefb..0000000000
--- a/lib/os_mon/doc/src/fascicules.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/os_mon/doc/src/note.gif b/lib/os_mon/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/os_mon/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
index b29a64155e..cec0856a8b 100644
--- a/lib/os_mon/doc/src/notes.xml
+++ b/lib/os_mon/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the OS_Mon application.</p>
+<section><title>Os_Mon 2.4.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Os_Mon 2.4.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/os_mon/doc/src/part_notes.xml b/lib/os_mon/doc/src/part_notes.xml
deleted file mode 100644
index 364178acc3..0000000000
--- a/lib/os_mon/doc/src/part_notes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>OS_Mon Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The operating system monitor, OS_Mon, provides services for
- monitoring CPU load, disk usage, memory usage and OS messages.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/os_mon/doc/src/user_guide.gif b/lib/os_mon/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/os_mon/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/os_mon/doc/src/warning.gif b/lib/os_mon/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/os_mon/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index e4250f577b..eb4f13ea9e 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.4.3
+OS_MON_VSN = 2.4.4
diff --git a/lib/otp_mibs/doc/src/Makefile b/lib/otp_mibs/doc/src/Makefile
index 62698b9300..c65e2a8e3c 100644
--- a/lib/otp_mibs/doc/src/Makefile
+++ b/lib/otp_mibs/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2003-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2003-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
@@ -38,7 +38,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = otp_mib.xml
-XML_PART_FILES = part.xml part_notes.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = \
introduction.xml \
mibs.xml \
@@ -50,7 +50,7 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-GIF_FILES = note.gif
+GIF_FILES =
# ----------------------------------------------------
@@ -95,7 +95,7 @@ clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
@@ -113,4 +113,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
release_spec:
-
diff --git a/lib/otp_mibs/doc/src/fascicules.xml b/lib/otp_mibs/doc/src/fascicules.xml
deleted file mode 100644
index c075478967..0000000000
--- a/lib/otp_mibs/doc/src/fascicules.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
-
diff --git a/lib/otp_mibs/doc/src/note.gif b/lib/otp_mibs/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/otp_mibs/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/otp_mibs/doc/src/notes.xml b/lib/otp_mibs/doc/src/notes.xml
index dbd2f47ffb..c99148a904 100644
--- a/lib/otp_mibs/doc/src/notes.xml
+++ b/lib/otp_mibs/doc/src/notes.xml
@@ -32,6 +32,21 @@
<p>This document describes the changes made to the OTP_Mibs
application.</p>
+<section><title>Otp_Mibs 1.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Otp_Mibs 1.1.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/otp_mibs/doc/src/part_notes.xml b/lib/otp_mibs/doc/src/part_notes.xml
deleted file mode 100644
index 5c03d28720..0000000000
--- a/lib/otp_mibs/doc/src/part_notes.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>OTP_Mibs Release Notes</title>
- <prepared>Ingela Anderton Andin</prepared>
- <docno></docno>
- <date>2004-09-07</date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>OTP_Mibs</em> application provides an SNMP management
- information base for Erlang nodes.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
-
diff --git a/lib/otp_mibs/vsn.mk b/lib/otp_mibs/vsn.mk
index 7a793007ee..13406cb5b1 100644
--- a/lib/otp_mibs/vsn.mk
+++ b/lib/otp_mibs/vsn.mk
@@ -1,4 +1,4 @@
-OTP_MIBS_VSN = 1.1.1
+OTP_MIBS_VSN = 1.1.2
# Note: The branch 'otp_mibs' is defunct as of otp_mibs-1.0.4 and
# should NOT be used again.
diff --git a/lib/parsetools/doc/src/Makefile b/lib/parsetools/doc/src/Makefile
index 8b03ed0fc7..e4cd2c0a76 100644
--- a/lib/parsetools/doc/src/Makefile
+++ b/lib/parsetools/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1997-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
@@ -39,7 +39,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = yecc.xml leex.xml
-XML_PART_FILES = part_notes.xml
+XML_PART_FILES =
XML_CHAPTER_FILES = notes.xml
BOOK_FILES = book.xml
@@ -48,8 +48,7 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-GIF_FILES = \
- note.gif
+GIF_FILES =
XML_HTML_FILES = \
notes_history.xml
@@ -69,10 +68,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -98,11 +97,11 @@ man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -117,4 +116,3 @@ release_docs_spec: docs
release_spec:
-
diff --git a/lib/parsetools/doc/src/fascicules.xml b/lib/parsetools/doc/src/fascicules.xml
deleted file mode 100644
index fadd37eefb..0000000000
--- a/lib/parsetools/doc/src/fascicules.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/parsetools/doc/src/note.gif b/lib/parsetools/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/parsetools/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml
index 3fa7169f50..b3370a06ab 100644
--- a/lib/parsetools/doc/src/notes.xml
+++ b/lib/parsetools/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the Parsetools application.</p>
+<section><title>Parsetools 2.1.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Warnings about unused functions in <c>leexinc.hrl</c>
+ are suppressed. </p>
+ <p>
+ Own Id: OTP-14697</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Parsetools 2.1.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/parsetools/doc/src/part_notes.xml b/lib/parsetools/doc/src/part_notes.xml
deleted file mode 100644
index 5e0824e839..0000000000
--- a/lib/parsetools/doc/src/part_notes.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1997</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>PARSETOOLS Release Notes</title>
- <prepared>Carl Velin</prepared>
- <docno></docno>
- <date>1997-04-28</date>
- <rev>1.0</rev>
- <file>part_notes.sgml</file>
- </header>
- <description>
- <p>The <em>Parsetools</em> application contains utilities for
- parsing and scanning. Yecc is an <term id="LALR-1"></term>parser
- generator for Erlang, similar to yacc. Yecc takes a <term
- id="BNF"></term>grammar definition as input, and produces Erlang
- code for a parser as output. Leex is a regular expression based
- lexical analyzer generator for Erlang, similar to lex or flex.</p>
- <p>There are also release notes for
- <seealso marker="notes_history">older versions</seealso>.</p>
- </description>
- <xi:lude href="notes.xml"/>
-</part>
-
diff --git a/lib/parsetools/doc/src/user_guide.gif b/lib/parsetools/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/parsetools/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/parsetools/doc/src/warning.gif b/lib/parsetools/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/parsetools/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/parsetools/include/leexinc.hrl b/lib/parsetools/include/leexinc.hrl
index b4449607cb..2a74c252ff 100644
--- a/lib/parsetools/include/leexinc.hrl
+++ b/lib/parsetools/include/leexinc.hrl
@@ -272,6 +272,8 @@ skip_cont(Rest, Line, {skip_token,Push}, Error) ->
skip_cont(Rest, Line, {error,_S}, Error) ->
skip_tokens(yystate(), Rest, Line, Rest, 0, Line, Error, reject, 0).
+-compile({nowarn_unused_function, [yyrev/1, yyrev/2, yypre/2, yysuf/2]}).
+
yyrev(List) -> lists:reverse(List).
yyrev(List, Tail) -> lists:reverse(List, Tail).
yypre(List, N) -> lists:sublist(List, N).
@@ -282,6 +284,8 @@ yysuf(List, N) -> lists:nthtail(N, List).
%% Line has been updated with respect to newlines in the prefix of
%% Chars consisting of (TokenLength - AcceptLength) characters.
+-compile({nowarn_unused_function, adjust_line/4}).
+
adjust_line(N, N, _Cs, L) -> L;
adjust_line(T, A, [$\n|Cs], L) ->
adjust_line(T-1, A, Cs, L-1);
diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk
index 502ca00a47..b6d2ce0cd4 100644
--- a/lib/parsetools/vsn.mk
+++ b/lib/parsetools/vsn.mk
@@ -1 +1 @@
-PARSETOOLS_VSN = 2.1.5
+PARSETOOLS_VSN = 2.1.6
diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile
index 5bdc5d4159..f5157fe87a 100644
--- a/lib/public_key/doc/src/Makefile
+++ b/lib/public_key/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2016. All Rights Reserved.
+# Copyright Ericsson AB 2008-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = public_key.xml
XML_REF6_FILES = public_key_app.xml
-XML_PART_FILES = part.xml part_notes.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = \
introduction.xml \
public_key_records.xml \
@@ -50,9 +50,9 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_REF6_FILES) $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_REF6_FILES) $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-GIF_FILES = note.gif
+GIF_FILES =
# ----------------------------------------------------
diff --git a/lib/public_key/doc/src/fascicules.xml b/lib/public_key/doc/src/fascicules.xml
deleted file mode 100644
index 25e7008537..0000000000
--- a/lib/public_key/doc/src/fascicules.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="usersguide" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="release_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
-
diff --git a/lib/public_key/doc/src/note.gif b/lib/public_key/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/public_key/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index a4c0194328..11012ee9e5 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -35,6 +35,31 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 1.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a bug in <c>public_key:ssh_encode/2</c> that made
+ it possible to erroneously encode e.g. an RSA key with
+ another type e.g. ECDSA in the resulting binary.</p>
+ <p>
+ Own Id: OTP-14570 Aux Id: ERIERL-52, OTP-14676 </p>
+ </item>
+ <item>
+ <p>
+ Corrected handling of parameterized EC keys in
+ public_key:generate_key/1 so that it will work as
+ expected instead of causing a runtime error in crypto.</p>
+ <p>
+ Own Id: OTP-14620</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 1.5.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/public_key/doc/src/part_notes.xml b/lib/public_key/doc/src/part_notes.xml
deleted file mode 100644
index 17f06d14f5..0000000000
--- a/lib/public_key/doc/src/part_notes.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2008</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>public_key Release Notes</title>
- <prepared>Ingela Anderton Andin</prepared>
- <docno></docno>
- <date>2008-01-22</date>
- <rev></rev>
- </header>
- <description>
- <p></p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
-
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 3040f2db0d..dea35bc390 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -774,6 +774,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
<func>
<name>pkix_test_data(Options) -> Config </name>
+ <name>pkix_test_data([chain_opts()]) -> [conf_opt()]</name>
<fsummary>Creates certificate test data.</fsummary>
<type>
<v>Options = #{chain_type() := chain_opts()} </v>
@@ -781,30 +782,83 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
<v>chain_type() = server_chain | client_chain </v>
- <v>chain_opts() = #{chain_end() := [cert_opt()],
- intermediates => [[cert_opt()]]}</v>
- <d>A valid chain must have at least a ROOT and a peer cert</d>
-
- <v>chain_end() = root | peer </v>
-
+ <v>chain_opts() = #{root := [cert_opt()] | root_cert(),
+ peer := [cert_opt()],
+ intermediates => [[cert_opt()]]}</v>
+ <d>
+ A valid chain must have at least a ROOT and a peer cert.
+ The root cert can be given either as a cert pre-generated by
+ <seealso marker="#pkix_test_root_cert-2">
+ pkix_test_root_cert/2
+ </seealso>, or as root cert generation options.
+ </d>
+ <v>root_cert() = #{cert := der_encoded(), key := Key}</v>
+ <d>
+ A root certificate generated by
+ <seealso marker="#pkix_test_root_cert-2">
+ pkix_test_root_cert/2
+ </seealso>.
+ </d>
<v>cert_opt() = {Key, Value}</v>
<d>For available options see <seealso marker="#cert_opt"> cert_opt()</seealso> below.</d>
<v>Config = #{server_config := [conf_opt()],
client_config := [conf_opt()]}</v>
- <v>conf_opt() = {cert, der_encoded()} | {key, der_encoded()} |{cacerts, [der_encoded()]}</v>
- <d>This is a subset of the type <seealso marker="ssl:ssl#type-ssloption"> ssl:ssl_option()</seealso> </d>
+ <v>conf_opt() = {cert, der_encoded()} | {key, PrivateKey} |{cacerts, [der_encoded()]}</v>
+ <d>
+ This is a subset of the type
+ <seealso marker="ssl:ssl#type-ssloption"> ssl:ssl_option()</seealso>.
+ <c>PrivateKey</c> is what
+ <seealso marker="#generate_key-1">generate_key/1</seealso>
+ returns.
+ </d>
</type>
<desc>
- <p>Creates certificate test data to facilitate automated testing
- of applications using X509-certificates often through
- SSL/TLS. The test data can be used when you have control
- over both the client and the server in a test scenario.
+ <p>
+ Creates certificate configuration(s) consisting of certificate
+ and its private key plus CA certificate bundle, for a client
+ and a server, intended to facilitate automated testing
+ of applications using X509-certificates,
+ often through SSL/TLS. The test data can be used
+ when you have control over both the client and the server
+ in a test scenario.
+ </p>
+ <p>
+ When this function is called with a map containing
+ client and server chain specifications;
+ it generates both a client and a server certificate chain
+ where the <c>cacerts</c>
+ returned for the server contains the root cert the server
+ should trust and the intermediate certificates the server
+ should present to connecting clients.
+ The root cert the server should trust is the one used
+ as root of the client certificate chain.
+ Vice versa applies to the <c>cacerts</c> returned for the client.
+ The root cert(s) can either be pre-generated with
+ <seealso marker="#pkix_test_root_cert-2">
+ pkix_test_root_cert/2
+ </seealso>, or if options are specified; it is (they are)
+ generated.
+ </p>
+ <p>
+ When this function is called with a list of certificate options;
+ it generates a configuration with just one node certificate
+ where <c>cacerts</c> contains the root cert
+ and the intermediate certs that should be presented to a peer.
+ In this case the same root cert must be used for all peers.
+ This is useful in for example an Erlang distributed cluster
+ where any node, towards another node, acts either
+ as a server or as a client depending on who connects to whom.
+ The generated certificate contains a subject altname,
+ which is not needed in a client certificate,
+ but makes the certificate useful for both roles.
+ </p>
+ <p>
+ The <marker id="cert_opt"/><c>cert_opt()</c>
+ type consists of the following options:
</p>
-
- <p> The <marker id="cert_opt"/> cert_opt() type consists of the following options: </p>
<taglist>
<tag> {digest, digest_type()}</tag>
<item><p>Hash algorithm to be used for
@@ -851,6 +905,36 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
</desc>
</func>
+ <func>
+ <name>pkix_test_root_cert(Name, Options) -> RootCert</name>
+ <fsummary>Generates a test data root cert.</fsummary>
+ <type>
+ <v>Name = string()</v>
+ <d>The root certificate name.</d>
+ <v>Options = [cert_opt()]</v>
+ <d>
+ For available options see
+ <seealso marker="#cert_opt">cert_opt()</seealso>
+ under
+ <seealso marker="#pkix_test_data-1">pkix_test_data/1</seealso>.
+ </d>
+ <v>RootCert = #{cert := der_encoded(), key := Key}</v>
+ <d>
+ A root certificate and key. The <c>Key</c> is generated by
+ <seealso marker="#generate_key-1">generate_key/1</seealso>.
+ </d>
+ </type>
+ <desc>
+ <p>
+ Generates a root certificate that can be used
+ in multiple calls to
+ <seealso marker="#pkix_test_data-1">pkix_test_data/1</seealso>
+ when you want the same root certificate for
+ several generated certificates.
+ </p>
+ </desc>
+ </func>
+
<func>
<name>pkix_verify(Cert, Key) -> boolean()</name>
<fsummary>Verifies PKIX x.509 certificate signature.</fsummary>
@@ -883,8 +967,8 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
</type>
<desc>
<p>This function checks that the <i>Presented Identifier</i> (e.g hostname) in a peer certificate
- conforms with the Expected Identifier that the client wants to connect to.
- This functions is intended to be added as an extra client check to the peer certificate when performing
+ is in agreement with the <i>Reference Identifier</i> that the client expects to be connected to.
+ The function is intended to be added as an extra client check of the peer certificate when performing
<seealso marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso>
</p>
<p>See <url href="https://tools.ietf.org/html/rfc6125">RFC 6125</url>
@@ -897,7 +981,8 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
<p>The <c>{OtherRefId,term()}</c> is defined by the user and is passed to the <c>match_fun</c>, if defined.
If that term is a binary, it will be converted to a string.
</p>
- <p>The <c>ip</c> takes a 4-tuple or a
+ <p>The <c>ip</c> Reference ID takes an <seealso marker="inet:inet#type-ip_address">inet:ip_address()</seealso>
+ or an ip address in string format (E.g "10.0.1.1" or "1234::5678:9012") as second element.
</p>
</desc>
</func>
diff --git a/lib/public_key/doc/src/public_key_records.xml b/lib/public_key/doc/src/public_key_records.xml
index 739310c88b..9ebdbb244d 100644
--- a/lib/public_key/doc/src/public_key_records.xml
+++ b/lib/public_key/doc/src/public_key_records.xml
@@ -70,10 +70,10 @@
<p><c>| {dNSName, string()}</c></p>
<p><c>| {x400Address, string()}</c></p>
<p><c>| {directoryName, {rdnSequence, [#AttributeTypeAndValue'{}]}}</c></p>
- <p><c>| {eidPartyName, special_string()}</c></p>
- <p><c>| {eidPartyName, special_string(), special_string()}</c></p>
+ <p><c>| {ediPartyName, special_string()}</c></p>
+ <p><c>| {ediPartyName, special_string(), special_string()}</c></p>
<p><c>| {uniformResourceIdentifier, string()}</c></p>
- <p><c>| {ipAddress, string()}</c></p>
+ <p><c>| {iPAddress, string()}</c></p>
<p><c>| {registeredId, oid()}</c></p>
<p><c>| {otherName, term()}</c></p>
</item>
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index 76fd0f8133..c433a96585 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -33,11 +33,12 @@
is_fixed_dh_cert/1, verify_data/1, verify_fun/4,
select_extension/2, match_name/3,
extensions_list/1, cert_auth_key_id/1, time_str_2_gregorian_sec/1,
- gen_test_certs/1]).
+ gen_test_certs/1, root_cert/2]).
-define(NULL, 0).
--export_type([chain_opts/0, test_config/0]).
+-export_type([cert_opt/0, chain_opts/0, conf_opt/0,
+ test_config/0, test_root_cert/0]).
-type cert_opt() :: {digest, public_key:digest_type()} |
{key, public_key:key_params() | public_key:private_key()} |
@@ -46,9 +47,12 @@
-type chain_end() :: root | peer.
-type chain_opts() :: #{chain_end() := [cert_opt()], intermediates => [[cert_opt()]]}.
-type conf_opt() :: {cert, public_key:der_encoded()} |
- {key, public_key:der_encoded()} |
+ {key, public_key:private_key()} |
{cacerts, [public_key:der_encoded()]}.
--type test_config() :: #{server_config := [conf_opt()], client_config := [conf_opt()]}.
+-type test_config() ::
+ #{server_config := [conf_opt()], client_config := [conf_opt()]}.
+-type test_root_cert() ::
+ #{cert := binary(), key := public_key:private_key()}.
%%====================================================================
%% Internal application APIu
%%====================================================================
@@ -430,31 +434,94 @@ match_name(Fun, Name, PermittedName, [Head | Tail]) ->
false ->
match_name(Fun, Name, Head, Tail)
end.
+
%%%
--spec gen_test_certs(#{server_chain:= chain_opts(), client_chain:= chain_opts()}) -> test_config().
-
-%% Generates server and and client configuration for testing
+-spec gen_test_certs(#{server_chain:= chain_opts(),
+ client_chain:= chain_opts()} |
+ chain_opts()) ->
+ test_config() |
+ [conf_opt()].
+%%
+%% Generates server and and client configuration for testing
%% purposes. All certificate options have default values
-gen_test_certs(#{client_chain := #{root := ClientRootConf,
- intermediates := ClientCAs,
- peer := ClientPeer},
- server_chain :=
- #{root := ServerRootConf,
- intermediates := ServerCAs,
- peer := ServerPeer}}) ->
- SRootKey = gen_key(proplists:get_value(key, ServerRootConf, default_key_gen())),
- CRootKey = gen_key(proplists:get_value(key, ClientRootConf, default_key_gen())),
- ServerRoot = root_cert("server", SRootKey, ClientRootConf),
- ClientRoot = root_cert("client", CRootKey, ServerRootConf),
-
- [{ServerDERCert, ServerDERKey} | ServerCAsKeys] = config(server, ServerRoot,
- SRootKey, lists:reverse([ServerPeer | lists:reverse(ServerCAs)])),
- [{ClientDERCert, ClientDERKey} | ClientCAsKeys] = config(client, ClientRoot,
- CRootKey, lists:reverse([ClientPeer | lists:reverse(ClientCAs)])),
- ServerDERCA = ca_config(ClientRoot, ServerCAsKeys),
- ClientDERCA = ca_config(ServerRoot, ClientCAsKeys),
- #{server_config => [{cert, ServerDERCert}, {key, ServerDERKey}, {cacerts, ServerDERCA}],
- client_config => [{cert, ClientDERCert}, {key, ClientDERKey}, {cacerts, ClientDERCA}]}.
+gen_test_certs(
+ #{client_chain :=
+ #{root := ClientRoot,
+ intermediates := ClientCAs,
+ peer := ClientPeer},
+ server_chain :=
+ #{root := ServerRoot,
+ intermediates := ServerCAs,
+ peer := ServerPeer}}) ->
+ #{cert := ServerRootCert, key := ServerRootKey} =
+ case ServerRoot of
+ #{} ->
+ ServerRoot;
+ ServerRootConf when is_list(ServerRootConf) ->
+ root_cert("SERVER ROOT CA", ServerRootConf)
+ end,
+ #{cert := ClientRootCert, key := ClientRootKey} =
+ case ClientRoot of
+ #{} ->
+ ClientRoot;
+ ClientRootConf when is_list(ClientRootConf) ->
+ root_cert("CLIENT ROOT CA", ClientRootConf)
+ end,
+ [{ServerDERCert, ServerDERKey} | ServerCAsKeys] =
+ config(
+ server, ServerRootCert, ServerRootKey,
+ lists:reverse([ServerPeer | lists:reverse(ServerCAs)])),
+ [{ClientDERCert, ClientDERKey} | ClientCAsKeys] =
+ config(
+ client, ClientRootCert, ClientRootKey,
+ lists:reverse([ClientPeer | lists:reverse(ClientCAs)])),
+ ServerDERCA = ca_config(ClientRootCert, ServerCAsKeys),
+ ClientDERCA = ca_config(ServerRootCert, ClientCAsKeys),
+ #{server_config =>
+ [{cert, ServerDERCert}, {key, ServerDERKey},
+ {cacerts, ServerDERCA}],
+ client_config =>
+ [{cert, ClientDERCert}, {key, ClientDERKey},
+ {cacerts, ClientDERCA}]};
+%%
+%% Generates a node configuration for testing purposes,
+%% when using the node server cert also for the client.
+%% All certificate options have default values
+gen_test_certs(
+ #{root := Root, intermediates := CAs, peer := Peer}) ->
+ #{cert := RootCert, key := RootKey} =
+ case Root of
+ #{} ->
+ Root;
+ RootConf when is_list(RootConf) ->
+ root_cert("SERVER ROOT CA", RootConf)
+ end,
+ [{DERCert, DERKey} | CAsKeys] =
+ config(
+ server, RootCert, RootKey,
+ lists:reverse([Peer | lists:reverse(CAs)])),
+ DERCAs = ca_config(RootCert, CAsKeys),
+ [{cert, DERCert}, {key, DERKey}, {cacerts, DERCAs}].
+
+%%%
+-spec root_cert(string(), [cert_opt()]) -> test_root_cert().
+%%
+%% Generate a self-signed root cert
+root_cert(Name, Opts) ->
+ PrivKey = gen_key(proplists:get_value(key, Opts, default_key_gen())),
+ TBS = cert_template(),
+ Issuer = subject("root", Name),
+ OTPTBS =
+ TBS#'OTPTBSCertificate'{
+ signature = sign_algorithm(PrivKey, Opts),
+ issuer = Issuer,
+ validity = validity(Opts),
+ subject = Issuer,
+ subjectPublicKeyInfo = public_key(PrivKey),
+ extensions = extensions(undefined, ca, Opts)
+ },
+ #{cert => public_key:pkix_sign(OTPTBS, PrivKey),
+ key => PrivKey}.
%%--------------------------------------------------------------------
%%% Internal functions
@@ -1103,7 +1170,7 @@ missing_basic_constraints(OtpCert, SelfSigned, ValidationState, VerifyFun, UserS
UserState}
end.
- gen_key(KeyGen) ->
+gen_key(KeyGen) ->
case is_key(KeyGen) of
true ->
KeyGen;
@@ -1120,28 +1187,14 @@ is_key(#'ECPrivateKey'{}) ->
is_key(_) ->
false.
-root_cert(Role, PrivKey, Opts) ->
- TBS = cert_template(),
- Issuer = issuer("root", Role, " ROOT CA"),
- OTPTBS = TBS#'OTPTBSCertificate'{
- signature = sign_algorithm(PrivKey, Opts),
- issuer = Issuer,
- validity = validity(Opts),
- subject = Issuer,
- subjectPublicKeyInfo = public_key(PrivKey),
- extensions = extensions(Role, ca, Opts)
- },
- public_key:pkix_sign(OTPTBS, PrivKey).
cert_template() ->
#'OTPTBSCertificate'{
version = v3,
- serialNumber = trunc(rand:uniform()*100000000)*10000 + 1,
+ serialNumber = erlang:unique_integer([positive, monotonic]),
issuerUniqueID = asn1_NOVALUE,
subjectUniqueID = asn1_NOVALUE
}.
-issuer(Contact, Role, Name) ->
- subject(Contact, Role ++ Name).
subject(Contact, Name) ->
Opts = [{email, Contact ++ "@example.org"},
@@ -1176,9 +1229,11 @@ validity(Opts) ->
DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1),
DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
{DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
- Format = fun({Y,M,D}) ->
- lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D]))
- end,
+ Format =
+ fun({Y,M,D}) ->
+ lists:flatten(
+ io_lib:format("~4..0w~2..0w~2..0w000000Z",[Y,M,D]))
+ end,
#'Validity'{notBefore={generalTime, Format(DefFrom)},
notAfter ={generalTime, Format(DefTo)}}.
@@ -1240,7 +1295,6 @@ cert(Role, #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = Iss
subject = subject(Contact, atom_to_list(Role) ++ Name),
subjectPublicKeyInfo = public_key(Key),
extensions = extensions(Role, Type, Opts)
-
},
public_key:pkix_sign(OTPTBS, PrivKey).
@@ -1297,7 +1351,7 @@ add_default_extensions(server, peer, Exts) ->
],
add_default_extensions(Default, Exts);
-add_default_extensions(_, peer, Exts) ->
+add_default_extensions(client, peer, Exts) ->
Exts.
add_default_extensions(Defaults0, Exts) ->
diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl
index 0243bcaa82..e89e16f120 100644
--- a/lib/public_key/src/pubkey_pbe.erl
+++ b/lib/public_key/src/pubkey_pbe.erl
@@ -222,7 +222,8 @@ pbe_pad(Data, {#'PBEParameter'{}, _}) ->
pbe_pad(Data, #'PBES2-params'{}) ->
pbe_pad(Data);
pbe_pad(Data, _) ->
- Data.
+pbe_pad(Data).%% Data.
+
pbe_pad(Data) ->
N = 8 - (erlang:byte_size(Data) rem 8),
diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl
index 75c1880655..a7d018e440 100644
--- a/lib/public_key/src/pubkey_ssh.erl
+++ b/lib/public_key/src/pubkey_ssh.erl
@@ -29,7 +29,15 @@
]).
-define(UINT32(X), X:32/unsigned-big-integer).
--define(STRING(X), ?UINT32((size(X))), (X)/binary).
+-define(STRING(X), ?UINT32((byte_size(X))), (X)/binary).
+
+-define(DEC_BIN(X,Len), ?UINT32(Len), X:Len/binary ).
+-define(DEC_MPINT(I,Len), ?DEC_INT(I,Len) ).
+-define(DEC_INT(I,Len), ?UINT32(Len), I:Len/big-signed-integer-unit:8 ).
+
+-define(Empint(X), (mpint(X))/binary ).
+-define(Estring(X), (string(X))/binary ).
+
%% Max encoded line length is 72, but conformance examples use 68
%% Comment from rfc 4716: "The following are some examples of public
@@ -47,12 +55,12 @@
%% Description: Decodes a ssh file-binary.
%%--------------------------------------------------------------------
decode(Bin, public_key)->
- case binary:match(Bin, begin_marker()) of
- nomatch ->
- openssh_decode(Bin, openssh_public_key);
- _ ->
- rfc4716_decode(Bin)
- end;
+ PKtype =
+ case binary:match(Bin, begin_marker()) of
+ nomatch -> openssh_public_key;
+ _ -> rfc4716_public_key
+ end,
+ decode(Bin, PKtype);
decode(Bin, rfc4716_public_key) ->
rfc4716_decode(Bin);
decode(Bin, ssh2_pubkey) ->
@@ -164,26 +172,8 @@ join_entry([Line | Lines], Entry) ->
join_entry(Lines, [Line | Entry]).
-rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
- ?UINT32(SizeE), E:SizeE/binary,
- ?UINT32(SizeN), N:SizeN/binary>>) when Type == <<"ssh-rsa">> ->
- #'RSAPublicKey'{modulus = erlint(SizeN, N),
- publicExponent = erlint(SizeE, E)};
-
-rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
- ?UINT32(SizeP), P:SizeP/binary,
- ?UINT32(SizeQ), Q:SizeQ/binary,
- ?UINT32(SizeG), G:SizeG/binary,
- ?UINT32(SizeY), Y:SizeY/binary>>) when Type == <<"ssh-dss">> ->
- {erlint(SizeY, Y),
- #'Dss-Parms'{p = erlint(SizeP, P),
- q = erlint(SizeQ, Q),
- g = erlint(SizeG, G)}};
-rfc4716_pubkey_decode(<<?UINT32(Len), ECDSA_SHA2_etc:Len/binary,
- ?UINT32(SizeId), Id:SizeId/binary,
- ?UINT32(SizeQ), Q:SizeQ/binary>>) ->
- <<"ecdsa-sha2-", Id/binary>> = ECDSA_SHA2_etc,
- {#'ECPoint'{point = Q}, {namedCurve,public_key:ssh_curvename2oid(Id)}}.
+rfc4716_pubkey_decode(BinKey) -> ssh2_pubkey_decode(BinKey).
+
openssh_decode(Bin, FileType) ->
Lines = binary:split(Bin, <<"\n">>, [global]),
@@ -267,18 +257,14 @@ decode_comment(Comment) ->
openssh_pubkey_decode(Type, Base64Enc) ->
try
- ssh2_pubkey_decode(Type, base64:mime_decode(Base64Enc))
+ <<?DEC_BIN(Type,_TL), Bin/binary>> = base64:mime_decode(Base64Enc),
+ ssh2_pubkey_decode(Type, Bin)
catch
_:_ ->
{Type, base64:mime_decode(Base64Enc)}
end.
-erlint(MPIntSize, MPIntValue) ->
- Bits= MPIntSize * 8,
- <<Integer:Bits/integer>> = MPIntValue,
- Integer.
-
ssh1_rsa_pubkey_decode(MBin, EBin) ->
#'RSAPublicKey'{modulus = integer_decode(MBin),
publicExponent = integer_decode(EBin)}.
@@ -411,71 +397,37 @@ comma_list_encode([Option | Rest], Acc) ->
ssh2_pubkey_encode(#'RSAPublicKey'{modulus = N, publicExponent = E}) ->
- ssh2_pubkey_encode({#'RSAPublicKey'{modulus = N, publicExponent = E}, 'ssh-rsa'});
-
-ssh2_pubkey_encode({Key, 'rsa-sha2-256'}) -> ssh2_pubkey_encode({Key, 'ssh-rsa'});
-ssh2_pubkey_encode({Key, 'rsa-sha2-512'}) -> ssh2_pubkey_encode({Key, 'ssh-rsa'});
-ssh2_pubkey_encode({#'RSAPublicKey'{modulus = N, publicExponent = E}, SignAlg}) ->
- SignAlgName = list_to_binary(atom_to_list(SignAlg)),
- StrLen = size(SignAlgName),
- EBin = mpint(E),
- NBin = mpint(N),
- <<?UINT32(StrLen), SignAlgName:StrLen/binary,
- EBin/binary,
- NBin/binary>>;
-ssh2_pubkey_encode({{_,#'Dss-Parms'{}}=Key, _}) ->
- ssh2_pubkey_encode(Key);
+ <<?STRING(<<"ssh-rsa">>), ?Empint(E), ?Empint(N)>>;
ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) ->
- TypeStr = <<"ssh-dss">>,
- StrLen = size(TypeStr),
- PBin = mpint(P),
- QBin = mpint(Q),
- GBin = mpint(G),
- YBin = mpint(Y),
- <<?UINT32(StrLen), TypeStr:StrLen/binary,
- PBin/binary,
- QBin/binary,
- GBin/binary,
- YBin/binary>>;
-ssh2_pubkey_encode({{#'ECPoint'{},_}=Key, _}) ->
- ssh2_pubkey_encode(Key);
+ <<?STRING(<<"ssh-dss">>), ?Empint(P), ?Empint(Q), ?Empint(G), ?Empint(Y)>>;
ssh2_pubkey_encode(Key={#'ECPoint'{point = Q}, {namedCurve,OID}}) ->
- TypeStr = key_type(Key),
- StrLen = size(TypeStr),
- IdB = public_key:oid2ssh_curvename(OID),
- <<?UINT32(StrLen), TypeStr:StrLen/binary,
- (string(IdB))/binary,
- (string(Q))/binary>>.
+ Curve = public_key:oid2ssh_curvename(OID),
+ <<?STRING(key_type(Key)), ?Estring(Curve), ?Estring(Q)>>.
-ssh2_pubkey_decode(Bin = <<?UINT32(Len), Type:Len/binary, _/binary>>) ->
+ssh2_pubkey_decode(<<?DEC_BIN(Type,_TL), Bin/binary>>) ->
ssh2_pubkey_decode(Type, Bin).
-ssh2_pubkey_decode(<<"rsa-sha2-256">>, Bin) -> ssh2_pubkey_decode(<<"ssh-rsa">>, Bin);
-ssh2_pubkey_decode(<<"rsa-sha2-512">>, Bin) -> ssh2_pubkey_decode(<<"ssh-rsa">>, Bin);
+%% ssh2_pubkey_decode(<<"rsa-sha2-256">>, Bin) -> ssh2_pubkey_decode(<<"ssh-rsa">>, Bin);
+%% ssh2_pubkey_decode(<<"rsa-sha2-512">>, Bin) -> ssh2_pubkey_decode(<<"ssh-rsa">>, Bin);
ssh2_pubkey_decode(<<"ssh-rsa">>,
- <<?UINT32(Len), _:Len/binary,
- ?UINT32(SizeE), E:SizeE/binary,
- ?UINT32(SizeN), N:SizeN/binary>>) ->
- #'RSAPublicKey'{modulus = erlint(SizeN, N),
- publicExponent = erlint(SizeE, E)};
+ <<?DEC_INT(E, _EL),
+ ?DEC_INT(N, _NL)>>) ->
+ #'RSAPublicKey'{modulus = N,
+ publicExponent = E};
ssh2_pubkey_decode(<<"ssh-dss">>,
- <<?UINT32(Len), _:Len/binary,
- ?UINT32(SizeP), P:SizeP/binary,
- ?UINT32(SizeQ), Q:SizeQ/binary,
- ?UINT32(SizeG), G:SizeG/binary,
- ?UINT32(SizeY), Y:SizeY/binary>>) ->
- {erlint(SizeY, Y),
- #'Dss-Parms'{p = erlint(SizeP, P),
- q = erlint(SizeQ, Q),
- g = erlint(SizeG, G)}};
+ <<?DEC_INT(P, _PL),
+ ?DEC_INT(Q, _QL),
+ ?DEC_INT(G, _GL),
+ ?DEC_INT(Y, _YL)>>) ->
+ {Y, #'Dss-Parms'{p = P,
+ q = Q,
+ g = G}};
ssh2_pubkey_decode(<<"ecdsa-sha2-",Id/binary>>,
- <<?UINT32(Len), ECDSA_SHA2_etc:Len/binary,
- ?UINT32(SizeId), Id:SizeId/binary,
- ?UINT32(SizeQ), Q:SizeQ/binary>>) ->
- <<"ecdsa-sha2-", Id/binary>> = ECDSA_SHA2_etc,
+ <<?DEC_BIN(Id, _IL),
+ ?DEC_BIN(Q, _QL)>>) ->
{#'ECPoint'{point = Q}, {namedCurve,public_key:ssh_curvename2oid(Id)}}.
@@ -575,17 +527,16 @@ mpint(X) -> mpint_pos(X).
mpint_neg(X) ->
Bin = int_to_bin_neg(X, []),
- Sz = byte_size(Bin),
- <<?UINT32(Sz), Bin/binary>>.
+ <<?STRING(Bin)>>.
mpint_pos(X) ->
Bin = int_to_bin_pos(X, []),
<<MSB,_/binary>> = Bin,
- Sz = byte_size(Bin),
if MSB band 16#80 == 16#80 ->
- <<?UINT32((Sz+1)), 0, Bin/binary>>;
+ B = << 0, Bin/binary>>,
+ <<?STRING(B)>>;
true ->
- <<?UINT32(Sz), Bin/binary>>
+ <<?STRING(Bin)>>
end.
int_to_bin_pos(0,Ds=[_|_]) ->
@@ -602,7 +553,8 @@ int_to_bin_neg(X,Ds) ->
string(X) when is_binary(X) ->
<< ?STRING(X) >>;
string(X) ->
- << ?STRING(list_to_binary(X)) >>.
+ B = list_to_binary(X),
+ << ?STRING(B) >>.
is_ssh_curvename(Id) -> try public_key:ssh_curvename2oid(Id) of _ -> true
catch _:_ -> false
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 31cb1fff3c..034126655c 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -59,7 +59,8 @@
pkix_crl_verify/2,
pkix_crl_issuer/1,
short_name_hash/1,
- pkix_test_data/1
+ pkix_test_data/1,
+ pkix_test_root_cert/2
]).
-export_type([public_key/0, private_key/0, pem_entry/0,
@@ -942,7 +943,6 @@ ssh_decode(SshBin, Type) when is_binary(SshBin),
%%--------------------------------------------------------------------
-spec ssh_encode([{public_key(), Attributes::list()}], ssh_file()) -> binary()
; (public_key(), ssh2_pubkey) -> binary()
- ; ({public_key(),atom()}, ssh2_pubkey) -> binary()
.
%%
%% Description: Encodes a list of ssh file entries (public keys and
@@ -1034,10 +1034,12 @@ short_name_hash({rdnSequence, _Attributes} = Name) ->
%%--------------------------------------------------------------------
--spec pkix_test_data(#{chain_type() := pubkey_cert:chain_opts()}) ->
- pubkey_cert:test_config().
+-spec pkix_test_data(#{chain_type() := pubkey_cert:chain_opts()} |
+ pubkey_cert:chain_opts()) ->
+ pubkey_cert:test_config() |
+ [pubkey_cert:conf_opt()].
-%% Description: Generates OpenSSL-style hash of a name.
+%% Description: Generates cert(s) and ssl configuration
%%--------------------------------------------------------------------
pkix_test_data(#{client_chain := ClientChain0,
@@ -1046,7 +1048,21 @@ pkix_test_data(#{client_chain := ClientChain0,
ClientChain = maps:merge(Default, ClientChain0),
ServerChain = maps:merge(Default, ServerChain0),
pubkey_cert:gen_test_certs(#{client_chain => ClientChain,
- server_chain => ServerChain}).
+ server_chain => ServerChain});
+pkix_test_data(#{} = Chain) ->
+ Default = #{intermediates => []},
+ pubkey_cert:gen_test_certs(maps:merge(Default, Chain)).
+
+%%--------------------------------------------------------------------
+-spec pkix_test_root_cert(
+ Name :: string(), Opts :: [pubkey_cert:cert_opt()]) ->
+ pubkey_cert:test_root_cert().
+
+%% Description: Generates a root cert suitable for pkix_test_data/1
+%%--------------------------------------------------------------------
+
+pkix_test_root_cert(Name, Opts) ->
+ pubkey_cert:root_cert(Name, Opts).
%%--------------------------------------------------------------------
%%% Internal functions
@@ -1324,9 +1340,9 @@ ec_normalize_params(#'ECParameters'{} = ECParams) ->
ec_normalize_params(Other) -> Other.
-spec ec_curve_spec(ecpk_parameters_api()) -> term().
-ec_curve_spec( #'ECParameters'{fieldID = FieldId, curve = PCurve, base = Base, order = Order, cofactor = CoFactor }) ->
- Field = {pubkey_cert_records:supportedCurvesTypes(FieldId#'FieldID'.fieldType),
- FieldId#'FieldID'.parameters},
+ec_curve_spec( #'ECParameters'{fieldID = #'FieldID'{fieldType = Type,
+ parameters = Params}, curve = PCurve, base = Base, order = Order, cofactor = CoFactor }) ->
+ Field = format_field(pubkey_cert_records:supportedCurvesTypes(Type), Params),
Curve = {PCurve#'Curve'.a, PCurve#'Curve'.b, none},
{Field, Curve, Base, Order, CoFactor};
ec_curve_spec({ecParameters, ECParams}) ->
@@ -1336,6 +1352,26 @@ ec_curve_spec({namedCurve, OID}) when is_tuple(OID), is_integer(element(1,OID))
ec_curve_spec({namedCurve, Name}) when is_atom(Name) ->
crypto:ec_curve(Name).
+format_field(characteristic_two_field = Type, Params0) ->
+ #'Characteristic-two'{
+ m = M,
+ basis = BasisOid,
+ parameters = Params} = der_decode('Characteristic-two', Params0),
+ {Type, M, field_param_decode(BasisOid, Params)};
+format_field(prime_field, Params0) ->
+ Prime = der_decode('Prime-p', Params0),
+ {prime_field, Prime}.
+
+field_param_decode(?'ppBasis', Params) ->
+ #'Pentanomial'{k1 = K1, k2 = K2, k3 = K3} =
+ der_decode('Pentanomial', Params),
+ {ppbasis, K1, K2, K3};
+field_param_decode(?'tpBasis', Params) ->
+ K = der_decode('Trinomial', Params),
+ {tpbasis, K};
+field_param_decode(?'gnBasis', _) ->
+ onbasis.
+
-spec ec_key({PubKey::term(), PrivateKey::term()}, Params::ecpk_parameters()) -> #'ECPrivateKey'{}.
ec_key({PubKey, PrivateKey}, Params) ->
#'ECPrivateKey'{version = 1,
@@ -1529,6 +1565,8 @@ verify_hostname_match_loop(Refs, Pres, MatchFun, FailCB, Cert) ->
Refs).
+to_lower_ascii({ip,_}=X) -> X;
+to_lower_ascii({iPAddress,_}=X) -> X;
to_lower_ascii(S) when is_list(S) -> lists:map(fun to_lower_ascii/1, S);
to_lower_ascii({T,S}) -> {T, to_lower_ascii(S)};
to_lower_ascii(C) when $A =< C,C =< $Z -> C + ($a-$A);
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 0077c7908c..449d1fc040 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -64,7 +64,9 @@ all() ->
groups() ->
[{pem_decode_encode, [], [dsa_pem, rsa_pem, ec_pem, encrypted_pem,
dh_pem, cert_pem, pkcs7_pem, pkcs10_pem, ec_pem2,
- ec_pem_encode_generated]},
+ ec_pem_encode_generated,
+ gen_ec_param_prime_field, gen_ec_param_char_2_field
+ ]},
{ssh_public_key_decode_encode, [],
[ssh_rsa_public_key, ssh_dsa_public_key, ssh_ecdsa_public_key,
ssh_rfc4716_rsa_comment, ssh_rfc4716_dsa_comment,
@@ -102,8 +104,15 @@ init_per_testcase(pkix_test_data_all_default, Config) ->
[] ->
{skip, missing_ecc_support};
_ ->
- init_common_per_testcase(Config)
+ init_common_per_testcase(Config)
end;
+
+init_per_testcase(gen_ec_param_prime_field=TC, Config) ->
+ init_per_testcase_gen_ec_param(TC, secp521r1, Config);
+
+init_per_testcase(gen_ec_param_char_2_field=TC, Config) ->
+ init_per_testcase_gen_ec_param(TC, sect571r1, Config);
+
init_per_testcase(TestCase, Config) ->
case TestCase of
ssh_hostkey_fingerprint_md5_implicit -> init_fingerprint_testcase([md5], Config);
@@ -991,7 +1000,7 @@ pkix_verify_hostname_options(Config) ->
%% openssl req -x509 -nodes -newkey rsa:1024 -keyout /dev/null -extensions SAN -config public_key_SUITE_data/verify_hostname_ip.conf 2>/dev/null > public_key_SUITE_data/pkix_verify_hostname_subjAltName_IP.pem
%%
%% Subject: C=SE, CN=example.com
-%% Subject Alternative Name: DNS:1.2.3.4, DNS: abcd:ef::1, IP:5.6.7.8, URI:https://10.11.12.13
+%% Subject Alternative Name: DNS:1.2.3.4, DNS: abcd:ef::1, IP:10.67.16.75, URI:https://10.11.12.13
pkix_verify_hostname_subjAltName_IP(Config) ->
DataDir = proplists:get_value(data_dir, Config),
@@ -1000,7 +1009,7 @@ pkix_verify_hostname_subjAltName_IP(Config) ->
%% Print the tests that a matchfun has to handle
catch public_key:pkix_verify_hostname(Cert, [{some_tag,"some.domain"},
- {ip, {5,6,7,8}}
+ {ip, {10,67,16,75}}
],
[{match_fun,
fun(Ref,Pres) ->
@@ -1012,14 +1021,14 @@ pkix_verify_hostname_subjAltName_IP(Config) ->
true = public_key:pkix_verify_hostname(Cert, [{uri_id,"https://10.11.12.13"}]),
true = public_key:pkix_verify_hostname(Cert, [{dns_id,"1.2.3.4"}]),
true = public_key:pkix_verify_hostname(Cert, [{dns_id,<<"1.2.3.4">>}]),
- false = public_key:pkix_verify_hostname(Cert, [{dns_id,"5.6.7.8"}]),
+ false = public_key:pkix_verify_hostname(Cert, [{dns_id,"10.67.16.75"}]),
true = public_key:pkix_verify_hostname(Cert, [{ip, "aBcD:ef:0::0:1"}]),
true = public_key:pkix_verify_hostname(Cert, [{ip, {16#abcd,16#ef,0,0,0,0,0,1}}]),
- true = public_key:pkix_verify_hostname(Cert, [{ip, "5.6.7.8"}]),
- true = public_key:pkix_verify_hostname(Cert, [{ip, <<"5.6.7.8">>}]),
- true = public_key:pkix_verify_hostname(Cert, [{ip, {5,6,7,8}}]).
-
-
+ true = public_key:pkix_verify_hostname(Cert, [{ip, "10.67.16.75"}]),
+ true = public_key:pkix_verify_hostname(Cert, [{ip, <<"10.67.16.75">>}]),
+ true = public_key:pkix_verify_hostname(Cert, [{ip, {10,67,16,75}}]),
+ false = public_key:pkix_verify_hostname(Cert, [{ip, {1,2,3,4}}]),
+ false = public_key:pkix_verify_hostname(Cert, [{ip, {10,11,12,13}}]).
%%--------------------------------------------------------------------
pkix_iso_rsa_oid() ->
[{doc, "Test workaround for supporting certs that use ISO oids"
@@ -1205,6 +1214,19 @@ short_crl_issuer_hash(Config) when is_list(Config) ->
CrlIssuerHash = public_key:short_name_hash(Issuer).
+%%--------------------------------------------------------------------
+gen_ec_param_prime_field() ->
+ [{doc, "Generate key with EC prime_field parameters"}].
+gen_ec_param_prime_field(Config) when is_list(Config) ->
+ Datadir = proplists:get_value(data_dir, Config),
+ do_gen_ec_param(filename:join(Datadir, "ec_key_param0.pem")).
+
+%%--------------------------------------------------------------------
+gen_ec_param_char_2_field() ->
+ [{doc, "Generate key with EC characteristic_two_field parameters"}].
+gen_ec_param_char_2_field(Config) when is_list(Config) ->
+ Datadir = proplists:get_value(data_dir, Config),
+ do_gen_ec_param(filename:join(Datadir, "ec_key_param1.pem")).
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
@@ -1277,6 +1299,43 @@ strip_superfluous_newlines(Bin) ->
Str = string:strip(binary_to_list(Bin), right, 10),
re:replace(Str,"\n\n","\n", [{return,list}, global]).
+do_gen_ec_param(File) ->
+ {ok, KeyPem} = file:read_file(File),
+ Entries = public_key:pem_decode(KeyPem),
+ [ParamInfo] = [Entry || Entry={'EcpkParameters', _, not_encrypted} <- Entries],
+ {ecParameters, Params} = public_key:pem_entry_decode(ParamInfo),
+ Key = public_key:generate_key(Params),
+ case check_entry_type(Key, 'ECPrivateKey') of
+ true ->
+ ok;
+ false ->
+ ct:fail({key_gen_fail, File})
+ end.
+
+init_per_testcase_gen_ec_param(TC, Curve, Config) ->
+ case crypto:ec_curves() of
+ [] ->
+ {skip, missing_ec_support};
+ Curves ->
+ case lists:member(Curve, Curves)
+ andalso crypto_supported_curve(Curve, Curves)
+ of
+ true ->
+ init_common_per_testcase(Config);
+ false ->
+ {skip, {missing_ec_support, Curve}}
+ end
+ end.
+
+
+crypto_supported_curve(Curve, Curves) ->
+ try crypto:generate_key(ecdh, Curve) of
+ {error,_} -> false; % Just in case crypto is changed in the future...
+ _-> true
+ catch
+ _:_-> false
+ end.
+
incorrect_countryname_pkix_cert() ->
<<48,130,5,186,48,130,4,162,160,3,2,1,2,2,7,7,250,61,63,6,140,137,48,13,6,9,42, 134,72,134,247,13,1,1,5,5,0,48,129,220,49,11,48,9,6,3,85,4,6,19,2,85,83,49, 16,48,14,6,3,85,4,8,19,7,65,114,105,122,111,110,97,49,19,48,17,6,3,85,4,7,19, 10,83,99,111,116,116,115,100,97,108,101,49,37,48,35,6,3,85,4,10,19,28,83,116, 97,114,102,105,101,108,100,32,84,101,99,104,110,111,108,111,103,105,101,115, 44,32,73,110,99,46,49,57,48,55,6,3,85,4,11,19,48,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 49,49,48,47,6,3,85,4,3,19,40,83,116,97,114,102,105,101,108,100,32,83,101,99, 117,114,101,32,67,101,114,116,105,102,105,99,97,116,105,111,110,32,65,117, 116,104,111,114,105,116,121,49,17,48,15,6,3,85,4,5,19,8,49,48,54,56,56,52,51, 53,48,30,23,13,49,48,49,48,50,51,48,49,51,50,48,53,90,23,13,49,50,49,48,50, 51,48,49,51,50,48,53,90,48,122,49,11,48,9,6,3,85,4,6,12,2,85,83,49,11,48,9,6, 3,85,4,8,12,2,65,90,49,19,48,17,6,3,85,4,7,12,10,83,99,111,116,116,115,100, 97,108,101,49,38,48,36,6,3,85,4,10,12,29,83,112,101,99,105,97,108,32,68,111, 109,97,105,110,32,83,101,114,118,105,99,101,115,44,32,73,110,99,46,49,33,48, 31,6,3,85,4,3,12,24,42,46,108,111,103,105,110,46,115,101,99,117,114,101,115, 101,114,118,101,114,46,110,101,116,48,130,1,34,48,13,6,9,42,134,72,134,247, 13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,185,136,240,80,141,36,124, 245,182,130,73,19,188,74,166,117,72,228,185,209,43,129,244,40,44,193,231,11, 209,12,234,88,43,142,1,162,48,122,17,95,230,105,171,131,12,147,46,204,36,80, 250,171,33,253,35,62,83,22,71,212,186,141,14,198,89,89,121,204,224,122,246, 127,110,188,229,162,67,95,6,74,231,127,99,131,7,240,85,102,203,251,50,58,58, 104,245,103,181,183,134,32,203,121,232,54,32,188,139,136,112,166,126,14,91, 223,153,172,164,14,61,38,163,208,215,186,210,136,213,143,70,147,173,109,217, 250,169,108,31,211,104,238,103,93,182,59,165,43,196,189,218,241,30,148,240, 109,90,69,176,194,52,116,173,151,135,239,10,209,179,129,192,102,75,11,25,168, 223,32,174,84,223,134,70,167,55,172,143,27,130,123,226,226,7,34,142,166,39, 48,246,96,231,150,84,220,106,133,193,55,95,159,227,24,249,64,36,1,142,171,16, 202,55,126,7,156,15,194,22,116,53,113,174,104,239,203,120,45,131,57,87,84, 163,184,27,83,57,199,91,200,34,43,98,61,180,144,76,65,170,177,2,3,1,0,1,163, 130,1,224,48,130,1,220,48,15,6,3,85,29,19,1,1,255,4,5,48,3,1,1,0,48,29,6,3, 85,29,37,4,22,48,20,6,8,43,6,1,5,5,7,3,1,6,8,43,6,1,5,5,7,3,2,48,14,6,3,85, 29,15,1,1,255,4,4,3,2,5,160,48,56,6,3,85,29,31,4,49,48,47,48,45,160,43,160, 41,134,39,104,116,116,112,58,47,47,99,114,108,46,115,116,97,114,102,105,101, 108,100,116,101,99,104,46,99,111,109,47,115,102,115,50,45,48,46,99,114,108, 48,83,6,3,85,29,32,4,76,48,74,48,72,6,11,96,134,72,1,134,253,110,1,7,23,2,48, 57,48,55,6,8,43,6,1,5,5,7,2,1,22,43,104,116,116,112,115,58,47,47,99,101,114, 116,115,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99,111,109, 47,114,101,112,111,115,105,116,111,114,121,47,48,129,141,6,8,43,6,1,5,5,7,1, 1,4,129,128,48,126,48,42,6,8,43,6,1,5,5,7,48,1,134,30,104,116,116,112,58,47, 47,111,99,115,112,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99, 111,109,47,48,80,6,8,43,6,1,5,5,7,48,2,134,68,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 47,115,102,95,105,110,116,101,114,109,101,100,105,97,116,101,46,99,114,116, 48,31,6,3,85,29,35,4,24,48,22,128,20,73,75,82,39,209,27,188,242,161,33,106, 98,123,81,66,122,138,215,213,86,48,59,6,3,85,29,17,4,52,48,50,130,24,42,46, 108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118,101,114,46,110, 101,116,130,22,108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118, 101,114,46,110,101,116,48,29,6,3,85,29,14,4,22,4,20,138,233,191,208,157,203, 249,85,242,239,20,195,48,10,148,49,144,101,255,116,48,13,6,9,42,134,72,134, 247,13,1,1,5,5,0,3,130,1,1,0,82,31,121,162,49,50,143,26,167,202,143,61,71, 189,201,199,57,81,122,116,90,192,88,24,102,194,174,48,157,74,27,87,210,223, 253,93,3,91,150,109,120,1,110,27,11,200,198,141,222,246,14,200,71,105,41,138, 13,114,122,106,63,17,197,181,234,121,61,89,74,65,41,231,248,219,129,83,176, 219,55,107,55,211,112,98,38,49,69,77,96,221,108,123,152,12,210,159,157,141, 43,226,55,187,129,3,82,49,136,66,81,196,91,234,196,10,82,48,6,80,163,83,71, 127,102,177,93,209,129,26,104,2,84,24,255,248,161,3,244,169,234,92,122,110, 43,4,17,113,185,235,108,219,210,236,132,216,177,227,17,169,58,162,159,182, 162,93,160,229,200,9,163,229,110,121,240,168,232,14,91,214,188,196,109,210, 164,222,0,109,139,132,113,91,16,118,173,178,176,80,132,34,41,199,51,206,250, 224,132,60,115,192,94,107,163,219,212,226,225,65,169,148,108,213,46,174,173, 103,110,189,229,166,149,254,31,51,44,144,108,187,182,11,251,201,206,86,138, 208,59,51,86,132,235,81,225,88,34,190,8,184>>.
diff --git a/lib/public_key/test/public_key_SUITE_data/ec_key_param0.pem b/lib/public_key/test/public_key_SUITE_data/ec_key_param0.pem
new file mode 100644
index 0000000000..679b08f1a2
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ec_key_param0.pem
@@ -0,0 +1,28 @@
+-----BEGIN EC PARAMETERS-----
+MIIBwgIBATBNBgcqhkjOPQEBAkIB////////////////////////////////////
+//////////////////////////////////////////////////8wgZ4EQgH/////
+////////////////////////////////////////////////////////////////
+/////////////////ARBUZU+uWGOHJofkpohoLaFQO6i2nJbmbMV87i0iZGO8Qnh
+Vhk5Uex+k3sWUsC9O7G/BzVz34g9LDTx70Uf1GtQPwADFQDQnogAKRy4U5bMZxc5
+MoSqoNpkugSBhQQAxoWOBrcEBOnNnj7LZiOVtEKcZIE5BT+1Ifgor2BrTT26oUte
+d+/nWSj+HcEnov+o3jNIs8GFakKb+X5+McLlvWYBGDkpaniaO8AEXIpftCx9G9mY
+9URJV5tEaBevvRcnPmYsl+5ymV70JkDFULkBP60HYTU8cIaicsJAiL6Udp/RZlAC
+QgH///////////////////////////////////////////pRhoeDvy+Wa3/MAUj3
+CaXQO7XJuImcR667b7cekThkCQIBAQ==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIICnAIBAQRBP6XqBV/b2Q49a8RUvrJBwotzY+IErK5FkzgkExqJuyzXXMM3jMtd
+M1vlEF46OCjbldw7NaYITW1mpwz6z2xtqyagggHGMIIBwgIBATBNBgcqhkjOPQEB
+AkIB////////////////////////////////////////////////////////////
+//////////////////////////8wgZ4EQgH/////////////////////////////
+/////////////////////////////////////////////////////////ARBUZU+
+uWGOHJofkpohoLaFQO6i2nJbmbMV87i0iZGO8QnhVhk5Uex+k3sWUsC9O7G/BzVz
+34g9LDTx70Uf1GtQPwADFQDQnogAKRy4U5bMZxc5MoSqoNpkugSBhQQAxoWOBrcE
+BOnNnj7LZiOVtEKcZIE5BT+1Ifgor2BrTT26oUted+/nWSj+HcEnov+o3jNIs8GF
+akKb+X5+McLlvWYBGDkpaniaO8AEXIpftCx9G9mY9URJV5tEaBevvRcnPmYsl+5y
+mV70JkDFULkBP60HYTU8cIaicsJAiL6Udp/RZlACQgH/////////////////////
+//////////////////////pRhoeDvy+Wa3/MAUj3CaXQO7XJuImcR667b7cekThk
+CQIBAaGBiQOBhgAEAVtCjzs+HP67ZZheraLGJPY+iIJHwDYWeCyzn2J4/fOv5CTo
+x0+1QcjrECh4V4F6jfqwW/oQaG9KKyEWhvvWQ0yVAeBfE+89DhYB3h2kyelqRcwZ
+XUuB7n7TxijJiHJXq9b+u+sh0qH9ya6nbWjHk79V37pgQOegHjnRc1gIFz6IfRUa
+-----END EC PRIVATE KEY-----
diff --git a/lib/public_key/test/public_key_SUITE_data/ec_key_param1.pem b/lib/public_key/test/public_key_SUITE_data/ec_key_param1.pem
new file mode 100644
index 0000000000..67095d8dc5
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ec_key_param1.pem
@@ -0,0 +1,25 @@
+-----BEGIN EC PARAMETERS-----
+MIIBcQIBATAlBgcqhkjOPQECMBoCAgI7BgkqhkjOPQECAwMwCQIBAgIBBQIBCjBk
+BAEBBEgC9A5+IiHyld4pcRe389YvXGqX/8uM7/HNa6jOSpoYrYT/q72O+lkzK+et
+Z1ambilK/RhaeP8SqlIOTec5usoMf/7/fylVcnoDFQAqoFj3Og4zq0hrD2EEEMU6
+fxMjEASBkQQDAwAdNLhWKWwWwNQNPNd1CpPR0pVfqAql9A/I23sqvb3lOVD0wNKT
+zdcRo1tn+xSZrmADhhTxOUq/o7TIUNkn4ed2nI7sLRkDe/JzQtpjm23M//63PWnX
+jGwnpgCcu8oZgPhTOSHopoRCPkO6sIpXYpGvj0YbsqizUx0vBIXBmxbi8VFuI908
+GkgnrxuKwVsCSAP//////////////////////////////////////////////+Zh
+zhj/VZhzCAWbGGgjhR7H3ZyhFh3pPVF01m6Dgum7L+hORwIBAg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIICXgIBAQRIAwaM3VblR1nZJseRVx4qJBQLLulB7uMF9KE+hMPlzZjj/f3KbcK4
+dfhxYFf4gqWNJW3fzFMEYF7JpzDjuFTmfX5jjO5f8cw9oIIBdTCCAXECAQEwJQYH
+KoZIzj0BAjAaAgICOwYJKoZIzj0BAgMDMAkCAQICAQUCAQowZAQBAQRIAvQOfiIh
+8pXeKXEXt/PWL1xql//LjO/xzWuozkqaGK2E/6u9jvpZMyvnrWdWpm4pSv0YWnj/
+EqpSDk3nObrKDH/+/38pVXJ6AxUAKqBY9zoOM6tIaw9hBBDFOn8TIxAEgZEEAwMA
+HTS4VilsFsDUDTzXdQqT0dKVX6gKpfQPyNt7Kr295TlQ9MDSk83XEaNbZ/sUma5g
+A4YU8TlKv6O0yFDZJ+HndpyO7C0ZA3vyc0LaY5ttzP/+tz1p14xsJ6YAnLvKGYD4
+Uzkh6KaEQj5DurCKV2KRr49GG7Kos1MdLwSFwZsW4vFRbiPdPBpIJ68bisFbAkgD
+///////////////////////////////////////////////mYc4Y/1WYcwgFmxho
+I4Uex92coRYd6T1RdNZug4Lpuy/oTkcCAQKhgZUDgZIABALOIIukF443IxnNZx1Q
+4HKDifQ9Lj3Rh+inIKczXLLJNEGCJ0wwG3d4v/fOxzdepZcnYviFAijQQA/iYWsA
+/Zet+5B4yxKISQcHei+PXkqwAAwEqq6D4hO2orlNOO430rgLoA0MNFc7I1THanOp
+q4RhYp8qnCEjM7nfQ4R0F+hPgZK1VPojkrGolaY/cNT/oA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName_IP.pem b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName_IP.pem
index f9ffb257b5..97d12cdadf 100644
--- a/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName_IP.pem
+++ b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName_IP.pem
@@ -1,13 +1,13 @@
-----BEGIN CERTIFICATE-----
-MIIB/zCCAWigAwIBAgIJAMoSejmTjwAGMA0GCSqGSIb3DQEBCwUAMB8xCzAJBgNV
-BAYTAlNFMRAwDgYDVQQDEwc1LjYuNy44MB4XDTE3MDkyODE0MDAxNVoXDTE3MTAy
-ODE0MDAxNVowHzELMAkGA1UEBhMCU0UxEDAOBgNVBAMTBzUuNi43LjgwgZ8wDQYJ
-KoZIhvcNAQEBBQADgY0AMIGJAoGBAMUPU89KwVbTCDkyxQSz3wprMbZTLe35K6jm
-Q7oY1rJyVXjsFHwZrFqqNMScEyX40rJhczQ2Z9etEX6qYLbdb/DZeFcKo14fR583
-QMFZC+qqpLWHdvjaQN0KwD99VFeZIGpRgywG8SR+BXZjDHUkGsMrikAEJtf0Tgih
-IPyiFtiJAgMBAAGjQzBBMD8GA1UdEQQ4MDaCBzEuMi4zLjSHBAUGBwiHEKvNAO8A
-AAAAAAAAAAAAAAGGE2h0dHBzOi8vMTAuMTEuMTIuMTMwDQYJKoZIhvcNAQELBQAD
-gYEAtWVeQaRFZ0kH/pzSWMSsOCUrjbwlWRwDNbagNKoM6nCRv0QQ59fG6XrVZwR3
-c0s5arlMh3U2+bjKE+Iq9+b/lN1lGzf8iaAqBNa7KptwTSUEY3TiNG5X0zlSXKTI
-3z7AaUEtghL9ImCPj5V3tVksqWd7U0zLmeeLZnM+wGAL9Hc=
+MIICBzCCAXCgAwIBAgIJAJgbo5FL73LuMA0GCSqGSIb3DQEBCwUAMCMxCzAJBgNV
+BAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNvbTAeFw0xNzEwMTExMDM0NDJaFw0x
+NzExMTAxMDM0NDJaMCMxCzAJBgNVBAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNv
+bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5muN8NIRHuqXgtAFpaJ4EPnd
+SD+hnzMiiWQ9qAsS8P4xFsl5aNH74BTgst6Rcq33qAw+4BtKFXMt7JbWMuZklFV3
+fzRSx099MVJSH3f2LDMNLfyDiSJnhBEv1rLPaosi91ZLvI5LiGTxzRLi3qftZBft
+Ryw1OempB4chLcBy2rsCAwEAAaNDMEEwPwYDVR0RBDgwNoIHMS4yLjMuNIcECkMQ
+S4cQq80A7wAAAAAAAAAAAAAAAYYTaHR0cHM6Ly8xMC4xMS4xMi4xMzANBgkqhkiG
+9w0BAQsFAAOBgQDMn8aqs/5FkkWhspvN2n+D2l87M+33a5My54ZVZhayZ/KRmhCN
+Gix/BiVYJ3UlmWmGcnQXb3MLt/LQHaD3S2whDaLN3xJ8BbnX7A4ZTybitdyeFhDw
+K3iDVUM3bSsBJ4EcBPWIMnow3ALP5HlGRMlH/87Qt+uVPXuwNh9pmyIhRQ==
-----END CERTIFICATE-----
diff --git a/lib/public_key/test/public_key_SUITE_data/verify_hostname_ip.conf b/lib/public_key/test/public_key_SUITE_data/verify_hostname_ip.conf
index 0a738f2586..798592e4f6 100644
--- a/lib/public_key/test/public_key_SUITE_data/verify_hostname_ip.conf
+++ b/lib/public_key/test/public_key_SUITE_data/verify_hostname_ip.conf
@@ -5,14 +5,13 @@ distinguished_name = DN
[DN]
C=SE
CN=example.com
-CN=5.6.7.8
[SAN]
subjectAltName = @alt_names
[alt_names]
DNS = 1.2.3.4
-IP.1 = 5.6.7.8
+IP.1 = 10.67.16.75
IP.2 = abcd:ef::1
URI = https://10.11.12.13
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index c01d8820f2..99a0cc087e 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 1.5.1
+PUBLIC_KEY_VSN = 1.5.2
diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile
index 5ce40bb995..ec19a4ce59 100644
--- a/lib/runtime_tools/doc/src/Makefile
+++ b/lib/runtime_tools/doc/src/Makefile
@@ -1,8 +1,8 @@
#
# %CopyrightBegin%
-#
+#
# Copyright Ericsson AB 1999-2016. All Rights Reserved.
-#
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,7 +14,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
include $(ERL_TOP)/make/target.mk
@@ -44,8 +44,8 @@ XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml system_information.xml msacc.xml
XML_REF6_FILES = runtime_tools_app.xml
-XML_PART_FILES = part_notes.xml part_notes_history.xml part.xml
-XML_CHAPTER_FILES = notes.xml notes_history.xml LTTng.xml
+XML_PART_FILES = part.xml
+XML_CHAPTER_FILES = notes.xml LTTng.xml
GENERATED_XML_FILES = DTRACE.xml SYSTEMTAP.xml
@@ -54,7 +54,8 @@ BOOK_FILES = book.xml
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) \
- $(XML_REF6_FILES) $(XML_APPLICATION_FILES)
+ $(XML_REF6_FILES) $(XML_APPLICATION_FILES) \
+ $(GENERATED_XML_FILES)
GIF_FILES =
@@ -77,10 +78,10 @@ SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
TOP_SPECS_FILE = specs.xml
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
SPECS_ESRC = ../../src
@@ -89,7 +90,6 @@ SPECS_FLAGS = -I../../include -I../../../kernel/src
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-$(XML_FILES): $(GENERATED_XML_FILES)
%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml
$(ERL_TOP)/make/emd2exml $< $@
@@ -109,18 +109,18 @@ man: $(MAN3_FILES) $(MAN6_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(MAN6DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -136,4 +136,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
release_spec:
-
diff --git a/lib/runtime_tools/doc/src/fascicules.xml b/lib/runtime_tools/doc/src/fascicules.xml
deleted file mode 100644
index 1a0bd6ec97..0000000000
--- a/lib/runtime_tools/doc/src/fascicules.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="refman" href="refman_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/runtime_tools/doc/src/note.gif b/lib/runtime_tools/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/runtime_tools/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 8b4d437c26..74300ba3fc 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -32,6 +32,38 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.12.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ New family of <c>erts_alloc</c> strategies: Age Order
+ First Fit. Similar to "address order", but instead the
+ oldest possible carrier is always chosen for allocation.</p>
+ <p>
+ Own Id: OTP-14917 Aux Id: ERIERL-88 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Runtime_Tools 1.12.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.12.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/runtime_tools/doc/src/part_notes.xml b/lib/runtime_tools/doc/src/part_notes.xml
deleted file mode 100644
index cabf3e39da..0000000000
--- a/lib/runtime_tools/doc/src/part_notes.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Runtime_Tools Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>Runtime_Tools</em> provides low footprint tracing/debugging
- tools suitable for inclusion in a production system.</p>
- <p>For information about older versions, see
- <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/runtime_tools/doc/src/part_notes_history.xml b/lib/runtime_tools/doc/src/part_notes_history.xml
deleted file mode 100644
index dd1991f23a..0000000000
--- a/lib/runtime_tools/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2006</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>Runtime_Tools Release Notes History</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>Runtime_Tools</em> provides low footprint tracing/debugging
- tools suitable for inclusion in a production system.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/runtime_tools/doc/src/warning.gif b/lib/runtime_tools/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/runtime_tools/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/runtime_tools/src/erts_alloc_config.erl b/lib/runtime_tools/src/erts_alloc_config.erl
index 514530332c..4b028681a0 100644
--- a/lib/runtime_tools/src/erts_alloc_config.erl
+++ b/lib/runtime_tools/src/erts_alloc_config.erl
@@ -265,7 +265,13 @@ strategy_str(aoff) ->
strategy_str(aoffcbf) ->
"Address order first fit carrier best fit";
strategy_str(aoffcaobf) ->
- "Address order first fit carrier adress order best fit".
+ "Address order first fit carrier adress order best fit";
+strategy_str(ageffcaoff) ->
+ "Age order first fit carrier address order first fit";
+strategy_str(ageffcbf) ->
+ "Age order first fit carrier best fit";
+strategy_str(ageffcaobf) ->
+ "Age order first fit carrier adress order best fit".
default_acul(A, S) ->
case carrier_migration_support(S) of
diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl
index df25297eb9..3772fcd2f9 100644
--- a/lib/runtime_tools/src/system_information.erl
+++ b/lib/runtime_tools/src/system_information.erl
@@ -75,43 +75,37 @@ load_report(file, File) -> load_report(data, from_file(File));
load_report(data, Report) ->
ok = start_internal(), gen_server:call(?SERVER, {load_report, Report}, infinity).
-report() -> [
- {init_arguments, init:get_arguments()},
- {code_paths, code:get_path()},
- {code, code()},
- {system_info, erlang_system_info()},
- {erts_compile_info, erlang:system_info(compile_info)},
- {beam_dynamic_libraries, get_dynamic_libraries()},
- {environment_erts, os_getenv_erts_specific()},
- {environment, [split_env(Env) || Env <- os:getenv()]},
- {sanity_check, sanity_check()}
- ].
+report() ->
+ %% This is ugly but beats having to maintain two distinct implementations,
+ %% and we don't really care about memory use since it's internal and
+ %% undocumented.
+ {ok, Fd} = file:open([], [ram, read, write]),
+ to_fd(Fd),
+ {ok, _} = file:position(Fd, bof),
+ from_fd(Fd).
-spec to_file(FileName) -> ok | {error, Reason} when
FileName :: file:name_all(),
Reason :: file:posix() | badarg | terminated | system_limit.
to_file(File) ->
- file:write_file(File, iolist_to_binary([
- io_lib:format("{system_information_version, ~p}.~n", [
- ?REPORT_FILE_VSN
- ]),
- io_lib:format("{system_information, ~p}.~n", [
- report()
- ])
- ])).
+ case file:open(File, [raw, write, binary, delayed_write]) of
+ {ok, Fd} ->
+ try
+ to_fd(Fd)
+ after
+ file:close(Fd)
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
from_file(File) ->
- case file:consult(File) of
- {ok, Data} ->
- case get_value([system_information_version], Data) of
- ?REPORT_FILE_VSN ->
- get_value([system_information], Data);
- Vsn ->
- erlang:error({unknown_version, Vsn})
- end;
- _ ->
- erlang:error(bad_report_file)
+ {ok, Fd} = file:open(File, [raw, read]),
+ try
+ from_fd(Fd)
+ after
+ file:close(Fd)
end.
applications() -> applications([]).
@@ -457,61 +451,151 @@ split_env([$=|Vs], Key) -> {lists:reverse(Key), Vs};
split_env([I|Vs], Key) -> split_env(Vs, [I|Key]);
split_env([], KV) -> lists:reverse(KV). % should not happen.
-%% get applications
+from_fd(Fd) ->
+ try
+ [{system_information_version, "1.0"},
+ {system_information, Data}] = consult_fd(Fd),
+ Data
+ catch
+ _:_ -> erlang:error(bad_report_file)
+ end.
-code() ->
- % order is important
- get_code_from_paths(code:get_path()).
+consult_fd(Fd) ->
+ consult_fd_1(Fd, [], {ok, []}).
+consult_fd_1(Fd, Cont0, ReadResult) ->
+ Data =
+ case ReadResult of
+ {ok, Characters} -> Characters;
+ eof -> eof
+ end,
+ case erl_scan:tokens(Cont0, Data, 1) of
+ {done, {ok, Tokens, _}, Next} ->
+ {ok, Term} = erl_parse:parse_term(Tokens),
+ [Term | consult_fd_1(Fd, [], {ok, Next})];
+ {more, Cont} ->
+ consult_fd_1(Fd, Cont, file:read(Fd, 1 bsl 20));
+ {done, {eof, _}, eof} -> []
+ end.
-get_code_from_paths([]) -> [];
-get_code_from_paths([Path|Paths]) ->
- case is_application_path(Path) of
- true ->
- [{application, get_application_from_path(Path)}|get_code_from_paths(Paths)];
- false ->
- [{code, [
- {path, Path},
- {modules, get_modules_from_path(Path)}
- ]}|get_code_from_paths(Paths)]
+%%
+%% Dumps a system_information tuple to the given Fd, writing the term in chunks
+%% to avoid eating too much memory on large systems.
+%%
+
+to_fd(Fd) ->
+ EmitChunk =
+ fun(Format, Args) ->
+ ok = file:write(Fd, io_lib:format(Format, Args))
+ end,
+
+ EmitChunk("{system_information_version, ~w}.~n"
+ "{system_information,["
+ "{init_arguments,~w},"
+ "{code_paths,~w},",
+ [?REPORT_FILE_VSN,
+ init:get_arguments(),
+ code:get_path()]),
+
+ emit_code_info(EmitChunk),
+
+ EmitChunk( "," %% Note the leading comma!
+ "{system_info,~w},"
+ "{erts_compile_info,~w},"
+ "{beam_dynamic_libraries,~w},"
+ "{environment_erts,~w},"
+ "{environment,~w},"
+ "{sanity_check,~w}"
+ "]}.~n",
+ [erlang_system_info(),
+ erlang:system_info(compile_info),
+ get_dynamic_libraries(),
+ os_getenv_erts_specific(),
+ [split_env(Env) || Env <- os:getenv()],
+ sanity_check()]).
+
+%% Emits all modules/applications in the *code path order*
+emit_code_info(EmitChunk) ->
+ EmitChunk("{code, [", []),
+ comma_separated_foreach(EmitChunk,
+ fun(Path) ->
+ case is_application_path(Path) of
+ true -> emit_application_info(EmitChunk, Path);
+ false -> emit_code_path_info(EmitChunk, Path)
+ end
+ end, code:get_path()),
+ EmitChunk("]}", []).
+
+emit_application_info(EmitChunk, Path) ->
+ [Appfile|_] = filelib:wildcard(filename:join(Path, "*.app")),
+ case file:consult(Appfile) of
+ {ok, [{application, App, Info}]} ->
+ RtDeps = proplists:get_value(runtime_dependencies, Info, []),
+ Description = proplists:get_value(description, Info, []),
+ Version = proplists:get_value(vsn, Info, []),
+
+ EmitChunk("{application, {~w,["
+ "{description,~w},"
+ "{vsn,~w},"
+ "{path,~w},"
+ "{runtime_dependencies,~w},",
+ [App, Description, Version, Path, RtDeps]),
+ emit_module_info_from_path(EmitChunk, Path),
+ EmitChunk("]}}", [])
end.
+emit_code_path_info(EmitChunk, Path) ->
+ EmitChunk("{code, ["
+ "{path, ~w},", [Path]),
+ emit_module_info_from_path(EmitChunk, Path),
+ EmitChunk("]}", []).
+
+emit_module_info_from_path(EmitChunk, Path) ->
+ BeamFiles = filelib:wildcard(filename:join(Path, "*.beam")),
+
+ EmitChunk("{modules, [", []),
+ comma_separated_foreach(EmitChunk,
+ fun(Beam) ->
+ emit_module_info(EmitChunk, Beam)
+ end, BeamFiles),
+ EmitChunk("]}", []).
+
+emit_module_info(EmitChunk, Beam) ->
+ %% FIXME: The next three calls load *all* significant chunks onto the heap,
+ %% which may cause us to run out of memory if there's a huge module in the
+ %% code path.
+ {ok,{Mod, Md5}} = beam_lib:md5(Beam),
+
+ CompilerVersion = get_compiler_version(Beam),
+ Native = beam_is_native_compiled(Beam),
+
+ Loaded = case code:is_loaded(Mod) of
+ false -> false;
+ _ -> true
+ end,
+
+ EmitChunk("{~w,["
+ "{loaded,~w},"
+ "{native,~w},"
+ "{compiler,~w},"
+ "{md5,~w}"
+ "]}",
+ [Mod, Loaded, Native, CompilerVersion, hexstring(Md5)]).
+
+comma_separated_foreach(_EmitChunk, _Fun, []) ->
+ ok;
+comma_separated_foreach(_EmitChunk, Fun, [H]) ->
+ Fun(H);
+comma_separated_foreach(EmitChunk, Fun, [H | T]) ->
+ Fun(H),
+ EmitChunk(",", []),
+ comma_separated_foreach(EmitChunk, Fun, T).
+
is_application_path(Path) ->
case filelib:wildcard(filename:join(Path, "*.app")) of
[] -> false;
_ -> true
end.
-get_application_from_path(Path) ->
- [Appfile|_] = filelib:wildcard(filename:join(Path, "*.app")),
- case file:consult(Appfile) of
- {ok, [{application, App, Info}]} ->
- {App, [
- {description, proplists:get_value(description, Info, [])},
- {vsn, proplists:get_value(vsn, Info, [])},
- {path, Path},
- {runtime_dependencies,
- proplists:get_value(runtime_dependencies, Info, [])},
- {modules, get_modules_from_path(Path)}
- ]}
- end.
-
-get_modules_from_path(Path) ->
- [
- begin
- {ok,{Mod, Md5}} = beam_lib:md5(Beam),
- Loaded = case code:is_loaded(Mod) of
- false -> false;
- _ -> true
- end,
- {Mod, [
- {loaded, Loaded},
- {native, beam_is_native_compiled(Beam)},
- {compiler, get_compiler_version(Beam)},
- {md5, hexstring(Md5)}
- ]}
- end || Beam <- filelib:wildcard(filename:join(Path, "*.beam"))
- ].
-
hexstring(Bin) when is_binary(Bin) ->
lists:flatten([io_lib:format("~2.16.0b", [V]) || <<V>> <= Bin]).
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index d8a4ede136..0dc6a48570 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.12.2
+RUNTIME_TOOLS_VSN = 1.12.4
diff --git a/lib/sasl/doc/src/Makefile b/lib/sasl/doc/src/Makefile
index a66b1f8bcb..baf563ca62 100644
--- a/lib/sasl/doc/src/Makefile
+++ b/lib/sasl/doc/src/Makefile
@@ -9,11 +9,11 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
# AB. All Rights Reserved.''
-#
+#
# $Id$
#
include $(ERL_TOP)/make/target.mk
@@ -44,17 +44,14 @@ XML_REF4_FILES = appup.xml rel.xml relup.xml script.xml
XML_REF6_FILES = sasl_app.xml
-XML_PART_FILES = part.xml part_notes.xml part_notes_history.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = sasl_intro.xml \
error_logging.xml \
- notes.xml \
- notes_history.xml
+ notes.xml
BOOK_FILES = book.xml
-GIF_FILES = \
- note.gif \
- warning.gif
+GIF_FILES =
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
@@ -78,14 +75,14 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
+$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
docs: pdf html man
@@ -100,7 +97,7 @@ man: $(MAN3_FILES) $(MAN4_FILES) $(MAN6_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%) # We depend just to copy them to ../html
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
@@ -108,11 +105,11 @@ clean clean_docs:
rm -f $(MAN4DIR)/*
rm -f $(MAN6DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -130,4 +127,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
release_spec:
-
diff --git a/lib/sasl/doc/src/fascicules.xml b/lib/sasl/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/sasl/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/sasl/doc/src/note.gif b/lib/sasl/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/sasl/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index b144122c4b..e532c3cd6f 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -31,6 +31,26 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 3.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The Report Browser, rb, could earlier not handle reports
+ that were not lists, for example generated by
+ <c>error_logger:info_report({some, tuple})</c>. This term
+ is allowed as input to error_logger, but rb would state
+ that "A report on bad form was encountered". This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-13906 Aux Id: ERL-261 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 3.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/sasl/doc/src/part_notes.xml b/lib/sasl/doc/src/part_notes.xml
deleted file mode 100644
index 8a32deefd9..0000000000
--- a/lib/sasl/doc/src/part_notes.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>SASL Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The System Architecture Support Libraries, <em>SASL</em>,
- provides support for alarm and release handling etc.</p>
- <p>For information about older versions, see
- <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/sasl/doc/src/part_notes_history.xml b/lib/sasl/doc/src/part_notes_history.xml
deleted file mode 100644
index abdf09d845..0000000000
--- a/lib/sasl/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2006</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>SASL Release Notes History</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The System Architecture Support Libraries, <em>SASL</em>,
- provides support for alarm and release handling etc.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/sasl/doc/src/warning.gif b/lib/sasl/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/sasl/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/sasl/src/format_lib_supp.erl b/lib/sasl/src/format_lib_supp.erl
index 80dcdc91da..00ce1b4e33 100644
--- a/lib/sasl/src/format_lib_supp.erl
+++ b/lib/sasl/src/format_lib_supp.erl
@@ -86,8 +86,10 @@ print_data(Device, Line, [{Key, Value}|T]) ->
print_data(Device, Line, [Value|T]) ->
Modifier = misc_supp:modifier(Device),
io:format(Device, "~"++Modifier++"p~n", [Value]),
- print_data(Device, Line, T).
-
+ print_data(Device, Line, T);
+print_data(Device, _Line, Value) ->
+ Modifier = misc_supp:modifier(Device),
+ io:format(Device, "~"++Modifier++"p~n", [Value]).
print_items(Device, Line, {Name, Items}) ->
print_items(Device, Line, Name, Items).
diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl
index 426dedbab5..4ba2540545 100644
--- a/lib/sasl/test/rb_SUITE.erl
+++ b/lib/sasl/test/rb_SUITE.erl
@@ -36,6 +36,7 @@ no_group_cases() ->
groups() ->
[{running_error_logger,[shuffle],[show,
+ show_other,
list,
rescan,
start_stop_log,
@@ -165,6 +166,23 @@ show(Config) ->
ok.
+show_other(Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
+
+ %% Insert some reports in the error log and start rb
+ error_logger:info_report([rb_test_term_in_list]),
+ error_logger:info_report(rb_test_term_no_list),
+ ok = start_rb(OutFile),
+
+ %% Show by type and check content
+ [{_,I1},{_,I2}] = check_report(fun() -> rb:show(info_report) end,OutFile),
+
+ true = contains(I1,"rb_test_term_no_list"),
+ true = contains(I2,"rb_test_term_in_list"),
+
+ ok.
+
list(Config) ->
PrivDir = ?config(priv_dir,Config),
OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index e980a42688..2488197ec5 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 3.1
+SASL_VSN = 3.1.1
diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile
index 9ea5dba0c5..3ebee792f9 100644
--- a/lib/snmp/doc/src/Makefile
+++ b/lib/snmp/doc/src/Makefile
@@ -1,9 +1,9 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1997-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -15,7 +15,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
include $(ERL_TOP)/make/target.mk
@@ -66,7 +66,6 @@ HTML_REF6_FILES = $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html)
HTML_CHAP_FILES = $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
EXTRA_FILES = \
- summary.html.src \
$(DEFAULT_HTML_FILES) \
$(HTML_REF1_FILES) \
$(HTML_REF3_FILES) \
@@ -85,18 +84,14 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-INDEX_FILE = index.html
-INDEX_SRC = $(INDEX_FILE).src
-INDEX_TARGET = $(DOCDIR)/$(INDEX_FILE)
-
GIF_TARGETS = $(GIF_FILES:%=$(HTMLDIR)/%)
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -106,21 +101,17 @@ $(HTMLDIR)/%.gif: %.gif # Copy them to ../html
docs: pdf html man
-ldocs: local_docs $(INDEX_TARGET)
+ldocs: local_docs
$(TOP_PDF_FILE): $(XML_FILES)
pdf: $(TOP_PDF_FILE)
html: gifs $(HTML_REF_MAN_FILE)
-html2: html $(INDEX_TARGET)
clean clean_docs: clean_html clean_man clean_pdf
rm -f errs core *~
-$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk # Create top make file
- $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ # inserting version number
-
man: man1 man3 man6 man7
man1: $(MAN1_FILES)
@@ -133,7 +124,7 @@ man7: $(MAN7_FILES)
gifs: $(GIF_TARGETS)
-debug opt:
+debug opt:
clean_pdf:
@echo "cleaning pdf:"
@@ -149,7 +140,6 @@ clean_man:
clean_html:
@echo "cleaning html:"
rm -rf $(HTMLDIR)/*
- rm -f $(INDEX_TARGET)
$(MAN7DIR)/%.7: $(MIBSDIR)/%.mib
@echo "processing $*"
@@ -162,7 +152,7 @@ $(MAN7DIR)/%.7: $(MIBSDIR)/%.mib
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
$(MAN1DIR)/snmpc.1: snmpc_cmd.xml
date=`date +"%B %e %Y"`; \
@@ -194,7 +184,7 @@ info: info_xml info_man info_html
@echo "MAN6DIR: $(MAN6DIR)"
@echo "MAN7DIR: $(MAN7DIR)"
-info_man:
+info_man:
@echo "man files:"
@echo "MAN1_FILES = $(MAN1_FILES)"
@echo "MAN3_FILES = $(MAN3_FILES)"
@@ -203,7 +193,7 @@ info_man:
@echo ""
@echo "MIB_FILES = $(MIB_FILES)"
-info_xml:
+info_xml:
@echo "xml files:"
# @echo "XML_REF1_FILES = $(XML_REF1_FILES)"
@echo "XML_REF3_FILES = $(XML_REF3_FILES)"
@@ -221,9 +211,7 @@ info_xml:
info_html:
@echo "html files:"
@echo "DOCDIR = $(DOCDIR)"
- @echo "INDEX_FILE = $(INDEX_FILE)"
- @echo "INDEX_SRC = $(INDEX_SRC)"
- @echo "INDEX_TARGET = $(INDEX_TARGET)"
+ @echo "HTML_REF_MAN_FILE = $(HTML_REF_MAN_FILE)"
@echo ""
@echo "HTMLDIR = $(HTMLDIR)"
@echo "HTML_APP_FILES = $(HTML_APP_FILES)"
diff --git a/lib/snmp/doc/src/book.gif b/lib/snmp/doc/src/book.gif
deleted file mode 100644
index 94b3868792..0000000000
--- a/lib/snmp/doc/src/book.gif
+++ /dev/null
Binary files differ
diff --git a/lib/snmp/doc/src/fascicules.xml b/lib/snmp/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/snmp/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/snmp/doc/src/files.mk b/lib/snmp/doc/src/files.mk
index 5aeae19105..f364cb6fa5 100644
--- a/lib/snmp/doc/src/files.mk
+++ b/lib/snmp/doc/src/files.mk
@@ -1,9 +1,9 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2001-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2001-2017. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -15,7 +15,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
XML_APPLICATION_FILES = \
@@ -81,9 +81,7 @@ XML_REF3_FILES = \
XML_REF6_FILES = snmp_app.xml
XML_PART_FILES = \
- part.xml \
- part_notes.xml \
- part_notes_history.xml
+ part.xml
XML_CHAPTER_FILES = \
snmp_intro.xml \
@@ -115,23 +113,16 @@ XML_FILES = $(BOOK_FILES) \
$(XML_REF6_FILES) \
$(XML_APPLICATION_FILES)
-GIF_FILES = book.gif \
+GIF_FILES = \
getnext1.gif \
getnext2.gif \
getnext3.gif \
getnext4.gif \
snmp_agent_netif_1.gif \
snmp_manager_netif_1.gif \
- min_head.gif \
- note.gif \
- notes.gif \
- ref_man.gif \
snmp-um-1-image-1.gif \
snmp-um-1-image-2.gif \
snmp-um-1-image-3.gif \
- snmp.gif \
- user_guide.gif \
- warning.gif \
MIB_mechanism.gif
PS_FILES = getnext1.ps \
diff --git a/lib/snmp/doc/src/index.html.src b/lib/snmp/doc/src/index.html.src
deleted file mode 100644
index e1b6be4d1f..0000000000
--- a/lib/snmp/doc/src/index.html.src
+++ /dev/null
@@ -1,99 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<!-- This file is obsolete -->
-<HTML>
-<!--
- ``Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson Utvecklings AB.
- Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
- AB. All Rights Reserved.''
-
- $Id$
--->
-<HEAD>
-<TITLE>SNMP %VSN%</TITLE>
-</HEAD>
-
-<BODY BGCOLOR="#FFFFFF">
-
-<CENTER>
-<A HREF="http://www.erlang.se/"><IMG ALT="Erlang/OTP" BORDER=0 SRC="html/min_head.gif"></A><BR>
-
-<FONT SIZE="-1">
-[<A HREF="../../../doc/index.html">Up</A> |
-<A HREF="http://www.erlang.se/">Erlang/OTP</A>]
-</FONT><BR>
-
-<P><FONT SIZE="+3">
-SNMP
-</FONT><BR>
-Version %VSN%
-</CENTER>
-
-<P><TABLE>
-<TR>
-<TD>
-<IMG ALIGN=LEFT ALT="SNMP" SRC="html/snmp.gif">
-</TD>
-
-<TD>
-<P>A bilingual Simple Network Management Protocol application,
- featuring an Extensible Agent, a simple manager, a MIB compiler
- and facilities for implementing SNMP MIBs etc.
-</TD>
-</TR>
-</TABLE>
-
-<P><CENTER>
-<TABLE CELLPADDING=15>
-<TR>
-<TD ALIGN=CENTER>
-<A HREF="html/users_guide.html"><IMG ALT="User's Guide" BORDER=0 SRC="html/user_guide.gif"></A><BR>
-<FONT SIZE="-1">
-<A HREF="html/users_guide.html">User's Guide</A>
-</FONT>
-</TD>
-
-<TD ALIGN=CENTER>
-<A HREF="html/index.html"><IMG ALT="Reference Manual" BORDER=0 SRC="html/ref_man.gif"></A><BR>
-<FONT SIZE="-1">
-<A HREF="html/index.html">Reference Manual</A>
-</FONT>
-</TD>
-
-<TD ALIGN=CENTER>
-<A HREF="html/release_notes.html"><IMG ALT="Release Notes" BORDER=0 SRC="html/notes.gif"></A><BR>
-<FONT SIZE="-1">
-<A HREF="html/release_notes.html">Release Notes</A>
-</FONT>
-</TD>
-
-<TD ALIGN=CENTER>
-<A HREF="pdf/snmp-%VSN%.pdf"><IMG ALT="Off-Print" BORDER=0 SRC="html/book.gif"></A><BR>
-<FONT SIZE="-1">
-<A HREF="pdf/snmp-%VSN%.pdf">Off-Print</A>
-</FONT>
-</TD>
-</TR>
-</TABLE>
-</CENTER>
-
-<P><CENTER>
-<HR>
-<FONT SIZE="-1">
-Copyright &copy; 1991-2001
-<A HREF="http://www.erlang.se/">Ericsson Utvecklings AB</A>
-</FONT>
-</CENTER>
-</BODY>
-</HTML>
diff --git a/lib/snmp/doc/src/min_head.gif b/lib/snmp/doc/src/min_head.gif
deleted file mode 100644
index 67948a6378..0000000000
--- a/lib/snmp/doc/src/min_head.gif
+++ /dev/null
Binary files differ
diff --git a/lib/snmp/doc/src/note.gif b/lib/snmp/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/snmp/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/snmp/doc/src/notes.gif b/lib/snmp/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/snmp/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 5cd20bad87..1b5f94ed07 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>SNMP Release Notes</title>
@@ -34,7 +34,22 @@
</header>
- <section><title>SNMP 5.2.8</title>
+ <section><title>SNMP 5.2.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SNMP 5.2.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -253,8 +268,8 @@
<list type="bulleted">
<item>
<p>[compiler] Refinement of type Opaque was not allowed. </p>
- <p>MIB constructs such as '<c>SYNTAX Opaque (SIZE(0..65535))</c>'
- was previously not allowed,
+ <p>MIB constructs such as '<c>SYNTAX Opaque (SIZE(0..65535))</c>'
+ was previously not allowed,
see the standard <c>ALARM-MIB</c> for eaxmple. </p>
<p>Own Id: OTP-12066</p>
<p>Aux Id: Seq 12669</p>
@@ -271,8 +286,8 @@
<!--
<list type="bulleted">
<item>
- <p>[agent]
- see <seealso marker="snmpa#load_mibs">load_mibs</seealso> and
+ <p>[agent]
+ see <seealso marker="snmpa#load_mibs">load_mibs</seealso> and
<seealso marker="snmpa#unload_mibs">unload_mibs</seealso>. </p>
<p>Own Id: OTP-11216</p>
</item>
@@ -403,18 +418,18 @@
<!--
<list type="bulleted">
<item>
- <p>Wrong block cypher type used for AES ('aes_cbf128'
- instead of 'aes_cfb128') when performing AES block
- encrypt/decrypt which breaks SNMP usmAesCfb128Protocol
+ <p>Wrong block cypher type used for AES ('aes_cbf128'
+ instead of 'aes_cfb128') when performing AES block
+ encrypt/decrypt which breaks SNMP usmAesCfb128Protocol
in agent and manager. </p>
<p>Own Id: OTP-11412</p>
</item>
<item>
- <p>[manager] When performing the AES encryption, invalid values for
+ <p>[manager] When performing the AES encryption, invalid values for
the EngineBoots and EngineTime was used. </p>
- <p>The values of the local agent was used, which would have produced
- "some" values if an agent was actually running.
+ <p>The values of the local agent was used, which would have produced
+ "some" values if an agent was actually running.
If not it would have caused a crash. </p>
<p>Own Id: OTP-11413</p>
</item>
@@ -458,16 +473,16 @@
<list type="bulleted">
<item>
<p>[agent] Enable SNMP to create missing database directories. </p>
- <p>Add
+ <p>Add
<seealso marker="snmp_app#db_init_error">
- {db_init_error, create_db_and_dir}</seealso> option to SNMP
- <seealso marker="snmp_app#manager_opts_and_types">manager</seealso>
+ {db_init_error, create_db_and_dir}</seealso> option to SNMP
+ <seealso marker="snmp_app#manager_opts_and_types">manager</seealso>
and
- <seealso marker="snmp_app#agent_opts_and_types">agent</seealso>.
+ <seealso marker="snmp_app#agent_opts_and_types">agent</seealso>.
This allows them to create any missing parent directories for
- <c>db_dir</c>, rather than treating any missing directories
+ <c>db_dir</c>, rather than treating any missing directories
as a fatal error.
- The default for <c>db_init_error</c>, which is <c>terminate</c>,
+ The default for <c>db_init_error</c>, which is <c>terminate</c>,
is unchanged. </p>
<p>Steve Vinoski</p>
<p>Own Id: OTP-11352</p>
@@ -477,7 +492,7 @@
<p>[manager] Improved handling of unexpected return values from
<seealso marker="snmpm_user">snmpm_user</seealso>
callback functions. </p>
- <p>Violations of the documented API (crashes or invalid return
+ <p>Violations of the documented API (crashes or invalid return
values) will now result in an error message. </p>
<p>Own Id: OTP-11307</p>
</item>
@@ -485,16 +500,16 @@
<item>
<p>Add (atl) log conversion block option. </p>
<p>It is now possible to request that the Audit Trail Log should
- be blocked during conversion (<c>log_to_txt</c> or <c>log_to_io</c>).
- This could be usefull when coverting an entire large log (when
+ be blocked during conversion (<c>log_to_txt</c> or <c>log_to_io</c>).
+ This could be usefull when coverting an entire large log (when
there is a chance it may otherwise wrap during conversion). </p>
- <p>See
- agent
+ <p>See
+ agent
<seealso marker="snmpa#log_to_txt">log_to_txt</seealso> and
- <seealso marker="snmpa#log_to_io">log_to_io</seealso> and also
- manager
+ <seealso marker="snmpa#log_to_io">log_to_io</seealso> and also
+ manager
<seealso marker="snmpm#log_to_txt">log_to_txt</seealso> and
- <seealso marker="snmpm#log_to_io">log_to_io</seealso>
+ <seealso marker="snmpm#log_to_io">log_to_io</seealso>
for details. </p>
<p>Own Id: OTP-11396</p>
<p>Own Id: seq12433</p>
@@ -503,9 +518,9 @@
<item>
<p>When converting an Audit Trail Log to text, a corrupt
log entry could cause the entire conversion to fail. </p>
- <p>Also, for a log with sequence numbers, failing to
+ <p>Also, for a log with sequence numbers, failing to
decode a log entry would cause the conversion to fail
- (not because of the failed decode, but because of the
+ (not because of the failed decode, but because of the
failure to write the error message). </p>
<p>Own Id: OTP-111453</p>
<p>Aux Id: Seq 12459</p>
@@ -523,18 +538,18 @@
<list type="bulleted">
<item>
- <p>Wrong block cypher type used for AES ('aes_cbf128'
- instead of 'aes_cfb128') when performing AES block
- encrypt/decrypt which breaks SNMP usmAesCfb128Protocol
+ <p>Wrong block cypher type used for AES ('aes_cbf128'
+ instead of 'aes_cfb128') when performing AES block
+ encrypt/decrypt which breaks SNMP usmAesCfb128Protocol
in agent and manager. </p>
<p>Own Id: OTP-11412</p>
</item>
<item>
- <p>[manager] When performing the AES encryption, invalid values for
+ <p>[manager] When performing the AES encryption, invalid values for
the EngineBoots and EngineTime was used. </p>
- <p>The values of the local agent was used, which would have produced
- "some" values if an agent was actually running.
+ <p>The values of the local agent was used, which would have produced
+ "some" values if an agent was actually running.
If not it would have caused a crash. </p>
<p>Own Id: OTP-11413</p>
</item>
@@ -576,14 +591,14 @@
<list type="bulleted">
<item>
- <p>[agent] Improved documentation for the functions for
- loading and unloading mibs,
- see <seealso marker="snmpa#load_mibs">load_mibs</seealso> and
- <seealso marker="snmpa#unload_mibs">unload_mibs</seealso> for
+ <p>[agent] Improved documentation for the functions for
+ loading and unloading mibs,
+ see <seealso marker="snmpa#load_mibs">load_mibs</seealso> and
+ <seealso marker="snmpa#unload_mibs">unload_mibs</seealso> for
more info. </p>
- <p>Also added new functions for loading and unloading a single mib,
- see <seealso marker="snmpa#load_mib">load_mib</seealso> and
- <seealso marker="snmpa#unload_mib">unload_mib</seealso> for
+ <p>Also added new functions for loading and unloading a single mib,
+ see <seealso marker="snmpa#load_mib">load_mib</seealso> and
+ <seealso marker="snmpa#unload_mib">unload_mib</seealso> for
more info. </p>
<p>Own Id: OTP-11216</p>
</item>
@@ -599,8 +614,8 @@
<!--
<list type="bulleted">
<item>
- <p>[agent]
- see <seealso marker="snmpa#load_mibs">load_mibs</seealso> and
+ <p>[agent]
+ see <seealso marker="snmpa#load_mibs">load_mibs</seealso> and
<seealso marker="snmpa#unload_mibs">unload_mibs</seealso>. </p>
<p>Own Id: OTP-11216</p>
</item>
@@ -626,7 +641,7 @@
</list>
-->
</section>
-
+
</section> <!-- 4.24.2 -->
@@ -659,29 +674,29 @@
<list type="bulleted">
<item>
- <p>[agent] Reading the value of the vacmViewTreeFamilyMask returns
+ <p>[agent] Reading the value of the vacmViewTreeFamilyMask returns
it in the wrong (internal bitlist) format. </p>
<p>The vacmViewTreeFamilyMask is defined as a bit string in the MIB
- (OCTET STRING). Internally a bitlist (list of 1's and 0's,
+ (OCTET STRING). Internally a bitlist (list of 1's and 0's,
see <seealso marker="snmp_agent_config_files#vacm">vacm config file</seealso>
- for more info) is used.
+ for more info) is used.
However, the MIB implementation assumed the latter, effectively
rendering all attempts to read/set masks via SNMP unsuccessful. </p>
- <p>Since the mask is used in hot paths (e.g. access permission checks
- for each SNMP operation, the bitlist representation of the mask has
- benefits (e.g. faster processing). Reading/writing the view mask
- objects is less time-critical. Therefore, to fix the issue, convert
- between the bitlist (internal) representation and bitstring
+ <p>Since the mask is used in hot paths (e.g. access permission checks
+ for each SNMP operation, the bitlist representation of the mask has
+ benefits (e.g. faster processing). Reading/writing the view mask
+ objects is less time-critical. Therefore, to fix the issue, convert
+ between the bitlist (internal) representation and bitstring
(external) when the vacmViewTreeFamilyMask objects are accessed. </p>
- <p>Also, the check of the vacm config file was invalid with
- regard to the mask value. It was assumed to be a proper oid, which
+ <p>Also, the check of the vacm config file was invalid with
+ regard to the mask value. It was assumed to be a proper oid, which
is not strictly the case (see bitlist above). </p>
<p>Own Id: OTP-11177</p>
<p>Stefan Zegenhagen</p>
</item>
<item>
- <p>[agent] The counter increment function in the local-db was
+ <p>[agent] The counter increment function in the local-db was
incorrect. It did not handle counter wrap correctly. </p>
<p>Own Id: OTP-11192</p>
</item>
@@ -728,26 +743,26 @@
</item>
<item>
- <p>[agent] Introduced a documented behaviour for the mib-server
- <seealso marker="snmpa_mib_data">mib-data backend</seealso>.
+ <p>[agent] Introduced a documented behaviour for the mib-server
+ <seealso marker="snmpa_mib_data">mib-data backend</seealso>.
At present only the default module (<c>snmpa_mib_data_tttn</c>) is
provided. </p>
- <p>A config option for the (agent)
- <seealso marker="snmp_config#agent_mib_server">mib-servers</seealso>
- mib-data backend module has been added to the agent config options,
+ <p>A config option for the (agent)
+ <seealso marker="snmp_config#agent_mib_server">mib-servers</seealso>
+ mib-data backend module has been added to the agent config options,
<seealso marker="snmp_config#agent_ms_data_module">data_module</seealso>. </p>
<p>Own Id: OTP-11101</p>
</item>
<item>
- <p>[agent] Introduced a documented behaviour for the
- <seealso marker="snmpa_mib_storage">mib storage</seealso>.
- At present there are three simple modules
- (<c>snmpa_mib_storage_ets</c>, <c>snmpa_mib_storage_dets</c> and
+ <p>[agent] Introduced a documented behaviour for the
+ <seealso marker="snmpa_mib_storage">mib storage</seealso>.
+ At present there are three simple modules
+ (<c>snmpa_mib_storage_ets</c>, <c>snmpa_mib_storage_dets</c> and
<c>snmpa_mib_storage_mnesia</c>) implementing this behaviour,
provided with the app. </p>
- <p>A config option for the (agent)
- <seealso marker="snmp_config#agent_mib_storage">mib storage</seealso>
+ <p>A config option for the (agent)
+ <seealso marker="snmp_config#agent_mib_storage">mib storage</seealso>
has been added to the agent config options. </p>
<p>Own Id: OTP-11107</p>
</item>
@@ -805,14 +820,14 @@
<list type="bulleted">
<item>
<p>[agent] Errors in <c>vacmAccessTable</c> RowStatus handling.
- There are problems with the handling of vacmAccessTableStatus
+ There are problems with the handling of vacmAccessTableStatus
that cause some SNMP test suites to report errors.
- Most notably, erroneous set operations frequently cause "genErr"
- errors to be returned. These "genErr" errors are usually caused
- by badmatch exceptions coming from
- <c>{ok, Row} = snmpa_vacm:get_row(RowIndex)</c>
+ Most notably, erroneous set operations frequently cause "genErr"
+ errors to be returned. These "genErr" errors are usually caused
+ by badmatch exceptions coming from
+ <c>{ok, Row} = snmpa_vacm:get_row(RowIndex)</c>
if the row does not exist. </p>
- <p>The semantics of the RowStatus handling in that table has
+ <p>The semantics of the RowStatus handling in that table has
been adjusted to be compliant with the RowStatus
textual description of SNPMv2-TC MIB. </p>
<p>Stefan Zegenhagen</p>
@@ -836,7 +851,7 @@
</item>
<item>
- <p>[compiler] The MIB compiler could not handle a table index
+ <p>[compiler] The MIB compiler could not handle a table index
that was defined later in the MIB. </p>
<p>Own Id: OTP-10808</p>
</item>
@@ -869,7 +884,7 @@
<title>SNMP Development Toolkit 4.23</title>
<!--
<p>Version 4.23 supports code replacement in runtime from/to
- version 4.22.1,
+ version 4.22.1,
4.22, 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and 4.21. </p>
-->
@@ -881,14 +896,14 @@
<list type="bulleted">
<item>
- <p>[manager] Polish return values of snmpm_user_default according
+ <p>[manager] Polish return values of snmpm_user_default according
to snmpm_user doc.</p>
<p>Luca Favatella</p>
<p>Own Id: OTP-10671</p>
</item>
<item>
- <p>[agent] Remove runtime warning in snmpa_agent because of
+ <p>[agent] Remove runtime warning in snmpa_agent because of
tuple fun usage. </p>
<p>Luca Favatella</p>
<p>Own Id: OTP-10672</p>
@@ -910,10 +925,10 @@
<!--
<list type="bulleted">
<item>
- <p>[agent] Simultaneous
- <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso>
+ <p>[agent] Simultaneous
+ <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso>
calls can interfere.
- The master agent did not check if a backup was already in
+ The master agent did not check if a backup was already in
progress when a backup request was accepted. </p>
<p>Own Id: OTP-9884</p>
<p>Aux Id: Seq 11995</p>
@@ -946,7 +961,7 @@
<section>
<title>SNMP Development Toolkit 4.22.1</title>
<p>Version 4.22.1 supports code replacement in runtime from/to
- version 4.22, 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and
+ version 4.22, 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and
4.21. </p>
<section>
@@ -958,15 +973,15 @@
<list type="bulleted">
<item>
<p>[agent] Sematic fixes to SNMP-USER-BASED-SM-MIB.
- The semantics allow the <c>usmUserAuthKeyChange</c> and
- <c>usmUserPrivKeyChange</c> objects to be written to in the
- same set requests that also creates and clones the user.
- This was not possible beforehand, causing test tools checking
+ The semantics allow the <c>usmUserAuthKeyChange</c> and
+ <c>usmUserPrivKeyChange</c> objects to be written to in the
+ same set requests that also creates and clones the user.
+ This was not possible beforehand, causing test tools checking
semantic SNMPv3 behaviour to fail on a lot of test cases. </p>
- <p>Furthermore, once the user has been cloned by writing to an
- instance of <c>usmUserCloneFrom</c>, further set-operations to
- the same object will not return an error, but be no-ops.
- Especially, it must be avoided to copy security parameters
+ <p>Furthermore, once the user has been cloned by writing to an
+ instance of <c>usmUserCloneFrom</c>, further set-operations to
+ the same object will not return an error, but be no-ops.
+ Especially, it must be avoided to copy security parameters
again (possibly even from a different user). </p>
<p>Stefan Zegenhagen</p>
<p>Own Id: OTP-10166</p>
@@ -974,14 +989,14 @@
<item>
<p>[agent] Errors in <c>vacmAccessTable</c> RowStatus handling.
- There are problems with the handling of vacmAccessTableStatus
+ There are problems with the handling of vacmAccessTableStatus
that cause some SNMP test suites to report errors.
- Most notably, erroneous set operations frequently cause "genErr"
- errors to be returned. These "genErr" errors are usually caused
- by badmatch exceptions coming from
- <c>{ok, Row} = snmpa_vacm:get_row(RowIndex)</c>
+ Most notably, erroneous set operations frequently cause "genErr"
+ errors to be returned. These "genErr" errors are usually caused
+ by badmatch exceptions coming from
+ <c>{ok, Row} = snmpa_vacm:get_row(RowIndex)</c>
if the row does not exist. </p>
- <p>The semantics of the RowStatus handling in that table has
+ <p>The semantics of the RowStatus handling in that table has
been adjusted to be compliant with the RowStatus
textual description of SNPMv2-TC MIB. </p>
<p>Stefan Zegenhagen</p>
@@ -1000,24 +1015,24 @@
<list type="bulleted">
<item>
<p>[agent] Fix walk over vacmAccessTable.
- Fix the get_next implementation of vacmAccessTable to
+ Fix the get_next implementation of vacmAccessTable to
return all table entries. </p>
- <p>The get_next implementation of vacmAccessTable did not return
- all available table data. Instead, it only returned the first
+ <p>The get_next implementation of vacmAccessTable did not return
+ all available table data. Instead, it only returned the first
column for each row, and all columns for the last row available. </p>
<p>Stefan Zegenhagen</p>
<p>Own Id: OTP-10165</p>
</item>
<item>
- <p>[manager]
- <seealso marker="snmpm#log_to_io">snmpm:log_to_io/6</seealso>
+ <p>[manager]
+ <seealso marker="snmpm#log_to_io">snmpm:log_to_io/6</seealso>
did not use the LogName argument. </p>
<p>Own Id: OTP-10066</p>
</item>
<item>
- <p>Incorrect TimeTicks decode. Also bad handling of
+ <p>Incorrect TimeTicks decode. Also bad handling of
invalid encode (value outside of value range) for both
<c>TimeTicks</c> and <c>Unsigned32</c>. </p>
<p>Own Id: OTP-10132</p>
@@ -1048,38 +1063,38 @@
<list type="bulleted">
<item>
- <p>[compiler] The table information the MIB compiler provides with
- augmented tables has been extended with <c>nbr_of_cols</c>,
+ <p>[compiler] The table information the MIB compiler provides with
+ augmented tables has been extended with <c>nbr_of_cols</c>,
<c>first_accessible</c> and <c>not_accessible</c>. </p>
<p>Own Id: OTP-9969</p>
</item>
<item>
- <p>Added the <c>log_to_io</c> audit-trail-log converter function
- to the api modules of both the
- <seealso marker="snmpm#log_to_io">manager</seealso>
- and
+ <p>Added the <c>log_to_io</c> audit-trail-log converter function
+ to the api modules of both the
+ <seealso marker="snmpm#log_to_io">manager</seealso>
+ and
<seealso marker="snmpa#log_to_io">agent</seealso>. </p>
<p>Own Id: OTP-9940</p>
</item>
<item>
- <p>[manager] Introduced a new transport module,
- <c>snmpm_net_if_mt</c>,
- which handles all incomming and outgoing
+ <p>[manager] Introduced a new transport module,
+ <c>snmpm_net_if_mt</c>,
+ which handles all incomming and outgoing
traffic in newly created processes. The message/request is
processed and then the process exits. </p>
<p>Own Id: OTP-9876</p>
</item>
<item>
- <p>[agent] Documenting previously existing but undocumented function,
+ <p>[agent] Documenting previously existing but undocumented function,
<seealso marker="snmp_generic#get_table_info">snmp_generic:get_table_info/2</seealso>. </p>
<p>Own Id: OTP-9942</p>
</item>
<item>
- <p>[agent] Improve error handling while reading agent config files.
+ <p>[agent] Improve error handling while reading agent config files.
Some files contain mandatory information and is therefor themself
mandatory. </p>
<p>Own Id: OTP-9943</p>
@@ -1095,10 +1110,10 @@
<!--
<list type="bulleted">
<item>
- <p>[agent] Simultaneous
- <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso>
+ <p>[agent] Simultaneous
+ <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso>
calls can interfere.
- The master agent did not check if a backup was already in
+ The master agent did not check if a backup was already in
progress when a backup request was accepted. </p>
<p>Own Id: OTP-9884</p>
<p>Aux Id: Seq 11995</p>
@@ -1120,7 +1135,7 @@
<section>
<title>SNMP Development Toolkit 4.21.7</title>
<p>Version 4.21.7 supports code replacement in runtime from/to
- version 4.21.6, 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1, 4.21, 4.20.1 and
+ version 4.21.6, 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1, 4.21, 4.20.1 and
4.20. </p>
<section>
@@ -1130,13 +1145,13 @@
<!--
<list type="bulleted">
<item>
- <p>[agent] DoS attack using GET-BULK with large value of
+ <p>[agent] DoS attack using GET-BULK with large value of
MaxRepetitions.
- A preventive method has been implementing by simply
- limit the number of varbinds that can be included in
- a Get-BULK response message. This is specified by the
- new config option,
- <seealso marker="snmp_app#agent_gb_max_vbs">gb_max_vbs</seealso>.
+ A preventive method has been implementing by simply
+ limit the number of varbinds that can be included in
+ a Get-BULK response message. This is specified by the
+ new config option,
+ <seealso marker="snmp_app#agent_gb_max_vbs">gb_max_vbs</seealso>.
</p>
<p>Own Id: OTP-9700</p>
</item>
@@ -1154,10 +1169,10 @@
<list type="bulleted">
<item>
- <p>[agent] Simultaneous
- <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso>
+ <p>[agent] Simultaneous
+ <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso>
calls can interfere.
- The master agent did not check if a backup was already in
+ The master agent did not check if a backup was already in
progress when a backup request was accepted. </p>
<p>Own Id: OTP-9884</p>
<p>Aux Id: Seq 11995</p>
@@ -1178,7 +1193,7 @@
<section>
<title>SNMP Development Toolkit 4.21.6</title>
<p>Version 4.21.6 supports code replacement in runtime from/to
- version 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1, 4.21, 4.20.1 and
+ version 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1, 4.21, 4.20.1 and
4.20. </p>
<section>
@@ -1189,13 +1204,13 @@
<list type="bulleted">
<item>
- <p>[agent] DoS attack using GET-BULK with large value of
+ <p>[agent] DoS attack using GET-BULK with large value of
MaxRepetitions.
- A preventive method has been implementing by simply
- limit the number of varbinds that can be included in
- a Get-BULK response message. This is specified by the
- new config option,
- <seealso marker="snmp_app#agent_gb_max_vbs">gb_max_vbs</seealso>.
+ A preventive method has been implementing by simply
+ limit the number of varbinds that can be included in
+ a Get-BULK response message. This is specified by the
+ new config option,
+ <seealso marker="snmp_app#agent_gb_max_vbs">gb_max_vbs</seealso>.
</p>
<p>Own Id: OTP-9700</p>
</item>
@@ -1212,11 +1227,11 @@
<list type="bulleted">
<item>
- <p>[agent] Mib server cache gclimit update function incorrectly calls
- age update function.
- The gclimit update function,
- <seealso marker="snmpa#update_mibs_cache_gclimit">update_mibs_cache_gclimit/1</seealso>,
- <em>incorrectly</em> called the age update function,
+ <p>[agent] Mib server cache gclimit update function incorrectly calls
+ age update function.
+ The gclimit update function,
+ <seealso marker="snmpa#update_mibs_cache_gclimit">update_mibs_cache_gclimit/1</seealso>,
+ <em>incorrectly</em> called the age update function,
<seealso marker="snmpa#update_mibs_cache_age">update_mibs_cache_age/2</seealso>. </p>
<p>Johan Claesson</p>
<p>Own Id: OTP-9868</p>
@@ -1258,18 +1273,18 @@
<section>
<title>Fixed Bugs and Malfunctions</title>
<!--
- <p>-</p>
+ <p>-</p>
-->
<list type="bulleted">
<item>
- <p>[agent] Repeated vacm table dumping fails due to file name
- conflict. When dumping the vacm table to disk, a temoporary
- file with a fixed name was used. If the table dumping
- (snmpa_vacm:dump_table/0) was initiated from several different
- processes in rapid succesion, the dumping could fail because the
- different processes was simultaniously trying to write to the
- same file. This problem has been eliminated by creating a unique
+ <p>[agent] Repeated vacm table dumping fails due to file name
+ conflict. When dumping the vacm table to disk, a temoporary
+ file with a fixed name was used. If the table dumping
+ (snmpa_vacm:dump_table/0) was initiated from several different
+ processes in rapid succesion, the dumping could fail because the
+ different processes was simultaniously trying to write to the
+ same file. This problem has been eliminated by creating a unique
name for the temporary file. </p>
<p>Own Id: OTP-9851</p>
<p>Aux Id: Seq 11980</p>
@@ -1310,7 +1325,7 @@
<!--
<list type="bulleted">
<item>
- <p>[compiler] Improved version info printout from the
+ <p>[compiler] Improved version info printout from the
<seealso marker="snmpc(command)#">MIB compiler frontend escript</seealso>. </p>
<p>Own Id: OTP-9618</p>
</item>
@@ -1323,7 +1338,7 @@
<section>
<title>Fixed Bugs and Malfunctions</title>
<!--
- <p>-</p>
+ <p>-</p>
-->
<list type="bulleted">
@@ -1367,7 +1382,7 @@
<list type="bulleted">
<item>
- <p>[compiler] Improved version info printout from the
+ <p>[compiler] Improved version info printout from the
<seealso marker="snmpc(command)#">MIB compiler frontend escript</seealso>. </p>
<p>Own Id: OTP-9618</p>
</item>
@@ -1379,27 +1394,27 @@
<section>
<title>Fixed Bugs and Malfunctions</title>
<!--
- <p>-</p>
+ <p>-</p>
-->
<list type="bulleted">
<item>
- <p>[agent] Version 4.20 introduced a change that broke trap
- sending from subagents. Due to a bug in the test code,
+ <p>[agent] Version 4.20 introduced a change that broke trap
+ sending from subagents. Due to a bug in the test code,
this was not discovered, until that bug was fixed. </p>
<p>Own Id: OTP-9745</p>
</item>
<item>
- <p>[agent] When sending an error message (reply) regarding
+ <p>[agent] When sending an error message (reply) regarding
<c>snmpUnknownPDUHandlers</c>, the agent used the wrong OID. </p>
<p>Own Id: OTP-9747</p>
</item>
<item>
- <p>[compiler] Fix the <c>--warnings/--W</c> option parsing in the
+ <p>[compiler] Fix the <c>--warnings/--W</c> option parsing in the
<seealso marker="snmpc(command)#option_warnings">snmpc</seealso>
- wrapper (e)script.
+ wrapper (e)script.
The short warning option was incorrectly <c>--w</c>, instead
of as documented <c>--W</c>. This has now been corrected. </p>
<p>*** POTENTIAL INCOMPATIBILITY ***</p>
@@ -1419,7 +1434,7 @@
<list type="bulleted">
<item>
- <p>[compiler] The short warning option has been changed from
+ <p>[compiler] The short warning option has been changed from
<c>--w</c> to <c>--W</c> to comply with the documentation. </p>
<p>Tuncer Ayaz</p>
<p>Own Id: OTP-9718</p>
@@ -1443,7 +1458,7 @@
<!--
<list type="bulleted">
<item>
- <p>Bad note store GC timer deactivation.
+ <p>Bad note store GC timer deactivation.
Wrong field in the state record was set (timeout instead active). </p>
<p>Stefan Grundmann</p>
<p>Own Id: OTP-9690</p>
@@ -1457,12 +1472,12 @@
<section>
<title>Fixed Bugs and Malfunctions</title>
<!--
- <p>-</p>
+ <p>-</p>
-->
<list type="bulleted">
<item>
- <p>Bad note store GC timer deactivation.
+ <p>Bad note store GC timer deactivation.
Wrong field in the state record was set (timeout instead active). </p>
<p>Stefan Grundmann</p>
<p>Own Id: OTP-9690</p>
@@ -1492,13 +1507,13 @@
-->
<list type="bulleted">
<item>
- <p>[compiler] Used wrong variable name (for
- warnings-as-errors variable), which caused the
+ <p>[compiler] Used wrong variable name (for
+ warnings-as-errors variable), which caused the
compiler to crash when using the snmpc (e)script. </p>
- <p>Also added the option
+ <p>Also added the option
<seealso marker="snmpc(command)#option_werror">--Werror</seealso>
- for the SNMP MIB compiler (escript) frontend (to mimic
- <seealso marker="erts:erlc">erlc</seealso>),
+ for the SNMP MIB compiler (escript) frontend (to mimic
+ <seealso marker="erts:erlc">erlc</seealso>),
which specifies whether warnings should be treated as errors. </p>
<p>Own Id: OTP-9447</p>
</item>
@@ -1513,12 +1528,12 @@
<section>
<title>Fixed Bugs and Malfunctions</title>
- <p>-</p>
+ <p>-</p>
<!--
<list type="bulleted">
<item>
- <p>The snmp config tool could not handle (manager) audit trail config
+ <p>The snmp config tool could not handle (manager) audit trail config
because the option seqno was not handled. </p>
<p>Own Id: OTP-9354</p>
</item>
@@ -1548,15 +1563,15 @@
-->
<list type="bulleted">
<item>
- <p>[manager] There was no way to specify transport domain.
+ <p>[manager] There was no way to specify transport domain.
The transport domains was assumed to be IPv4 (transportDomainUdpIpv4).
- This has now been changed so that it can also be IPv6
- (transportDomainUdpIpv6).
- To facilitate this, the transport domain, <c>tdomain</c>,
- is now a (new) valid option when
+ This has now been changed so that it can also be IPv6
+ (transportDomainUdpIpv6).
+ To facilitate this, the transport domain, <c>tdomain</c>,
+ is now a (new) valid option when
<seealso marker="snmpm#register_agent">registering</seealso>
- a new agent (and
- <seealso marker="snmpm#update_agent_info">updating</seealso>
+ a new agent (and
+ <seealso marker="snmpm#update_agent_info">updating</seealso>
agent info). </p>
<p>This also mean that the transport behaviour has changed. </p>
<p>Own Id: OTP-9305</p>
@@ -1564,10 +1579,10 @@
</item>
<item>
- <p>[compiler] Added the option
- <seealso marker="snmpc#compile">warnings_as_errors</seealso>
- (for the SNMP MIB compiler (escript) frontend, the option
- <seealso marker="snmpc(command)#option_wae">--wae</seealso> is used)
+ <p>[compiler] Added the option
+ <seealso marker="snmpc#compile">warnings_as_errors</seealso>
+ (for the SNMP MIB compiler (escript) frontend, the option
+ <seealso marker="snmpc(command)#option_wae">--wae</seealso> is used)
which specifies whether warnings should be treated as errors. </p>
<p>Tuncer Ayaz</p>
<p>Own Id: OTP-9437</p>
@@ -1579,12 +1594,12 @@
<section>
<title>Fixed Bugs and Malfunctions</title>
<!--
- <p>-</p>
+ <p>-</p>
-->
<list type="bulleted">
<item>
- <p>The snmp config tool could not handle (manager) audit trail config
+ <p>The snmp config tool could not handle (manager) audit trail config
because the option seqno was not handled. </p>
<p>Own Id: OTP-9354</p>
</item>
@@ -1638,11 +1653,11 @@
<section>
<title>Fixed Bugs and Malfunctions</title>
<!--
- <p>-</p>
+ <p>-</p>
-->
<list type="bulleted">
<item>
- <p>[agent] Did not handle transport domains properly in some cases,
+ <p>[agent] Did not handle transport domains properly in some cases,
for instance trap sending. </p>
<p>Own Id: OTP-9400</p>
</item>
@@ -1679,9 +1694,9 @@
<list type="bulleted">
<item>
<p>[agent] Added support for sending traps to IPv6 targets. </p>
- <p>See the
- <seealso marker="snmp_agent_config_files#target_addr">target address config file</seealso>,
- the <seealso marker="snmpa_conf#target_addr_entry">target_addr_entry/11</seealso> function or
+ <p>See the
+ <seealso marker="snmp_agent_config_files#target_addr">target address config file</seealso>,
+ the <seealso marker="snmpa_conf#target_addr_entry">target_addr_entry/11</seealso> function or
<seealso marker="snmp_target_mib#add_addr">add_addr/11</seealso> for more info. </p>
<p>Own Id: OTP-9088</p>
<p>Aux Id: Seq 11790</p>
@@ -1690,7 +1705,7 @@
<item>
<p>[agent] To be able to handle multiple engine-id(s) when
- sending trap(s), the function
+ sending trap(s), the function
<seealso marker="snmp_community_mib#add_community">
add_community/6</seealso> has been added. </p>
<p>Own Id: OTP-9119</p>
@@ -1700,14 +1715,14 @@
<item>
<p>[manager] The API for snmp requests has been augmented to
allow the caller to override some configuration. </p>
- <p>This has been done by introducing a new set of API functions, see
- <seealso marker="snmpm#sync_get2">sync_get2/3,4</seealso>,
- <seealso marker="snmpm#async_get2">async_get2/3,4</seealso>,
- <seealso marker="snmpm#sync_get_next2">sync_get_next2/3,4</seealso>,
- <seealso marker="snmpm#async_get_next2">async_get_next2/3,4</seealso>,
- <seealso marker="snmpm#sync_get_bulk2">sync_get_bulk2/5,6</seealso>,
- <seealso marker="snmpm#async_get_bulk2">async_get_bulk2/5,6</seealso>,
- <seealso marker="snmpm#sync_set2">sync_set2/3,4</seealso> and
+ <p>This has been done by introducing a new set of API functions, see
+ <seealso marker="snmpm#sync_get2">sync_get2/3,4</seealso>,
+ <seealso marker="snmpm#async_get2">async_get2/3,4</seealso>,
+ <seealso marker="snmpm#sync_get_next2">sync_get_next2/3,4</seealso>,
+ <seealso marker="snmpm#async_get_next2">async_get_next2/3,4</seealso>,
+ <seealso marker="snmpm#sync_get_bulk2">sync_get_bulk2/5,6</seealso>,
+ <seealso marker="snmpm#async_get_bulk2">async_get_bulk2/5,6</seealso>,
+ <seealso marker="snmpm#sync_set2">sync_set2/3,4</seealso> and
<seealso marker="snmpm#async_set2">async_set2/3,4</seealso>
for more info. </p>
<p>Own Id: OTP-9162</p>
@@ -1715,8 +1730,8 @@
<item>
<p>[manager] The old API functions (for get and set
- requests:
- snmpm:g/3,4,5,6,7, snmpm:ag/3,4,5,6,7,
+ requests:
+ snmpm:g/3,4,5,6,7, snmpm:ag/3,4,5,6,7,
snmpm:gn/3,4,5,6,7, snmpm:agn/3,4,5,6,7,
snmpm:s/3,4,5,6,7, snmpm:s/3,4,5,6,7,
snmpm:gb/5,6,7,8,9 and snmpm:agb/5,6,7,8,9)
@@ -1728,12 +1743,12 @@
<item>
<p>[agent] Pass extra info through the agent to the net-if
process when sending notifications. </p>
- <p>See
+ <p>See
<seealso marker="snmpa#send_notification2">
- snmpa:send_notification2/3</seealso> for more info.
- See also the incomming net-if messages when sending a
- <seealso marker="snmp_agent_netif#im_send_pdu">trap</seealso>
- (send_pdu message) and
+ snmpa:send_notification2/3</seealso> for more info.
+ See also the incomming net-if messages when sending a
+ <seealso marker="snmp_agent_netif#im_send_pdu">trap</seealso>
+ (send_pdu message) and
<seealso marker="snmp_agent_netif#im_send_pdu_req">
notification</seealso> (send_pdu_req message). </p>
<p>Own Id: OTP-9183</p>
@@ -1751,15 +1766,15 @@
<section>
<title>Fixed Bugs and Malfunctions</title>
<!--
- <p>-</p>
+ <p>-</p>
-->
<list type="bulleted">
<item>
<p>Fixed endode/decode of values of type <c>Counter32</c>. </p>
- <p>This type (<c>Counter32</c>) is an unsigned integer 32,
- but is actually encoded as an signed integer 32.
- The encode/decode functions however, treated it as if it was
+ <p>This type (<c>Counter32</c>) is an unsigned integer 32,
+ but is actually encoded as an signed integer 32.
+ The encode/decode functions however, treated it as if it was
encodeded as an unsigned integer 32. </p>
<p>Own Id: OTP-9022</p>
</item>
@@ -1774,12 +1789,4 @@
</section>
</section> <!-- 4.20 -->
-
-
- <!-- section>
- <title>Release notes history</title>
- <p>For information about older versions see
- <url href="part_notes_history_frame.html">release notes history</url>.</p>
- </section -->
</chapter>
-
diff --git a/lib/snmp/doc/src/part_notes.xml b/lib/snmp/doc/src/part_notes.xml
deleted file mode 100644
index d149044169..0000000000
--- a/lib/snmp/doc/src/part_notes.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1997</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>SNMP Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- <file>part_notes.xml</file>
- </header>
- <description>
- <p>A multilingual Simple Network Management Protocol application,
- featuring an Extensible Agent, a simple manager and a MIB
- compiler and facilities for implementing SNMP MIBs etc.</p>
- <p>For information about older versions see
- <url href="part_notes_history_frame.html">release notes history</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/snmp/doc/src/part_notes_history.xml b/lib/snmp/doc/src/part_notes_history.xml
deleted file mode 100644
index aa5276dc94..0000000000
--- a/lib/snmp/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>SNMP Release Notes History</title>
- <prepared></prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date></date>
- <rev></rev>
- <file>part_notes_history.xml</file>
- </header>
- <description>
- <p>A multilingual Simple Network Management Protocol application,
- featuring an Extensible Agent, a simple manager and a MIB
- compiler and facilities for implementing SNMP MIBs etc.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/snmp/doc/src/ref_man.gif b/lib/snmp/doc/src/ref_man.gif
deleted file mode 100644
index b13c4efd53..0000000000
--- a/lib/snmp/doc/src/ref_man.gif
+++ /dev/null
Binary files differ
diff --git a/lib/snmp/doc/src/snmp.gif b/lib/snmp/doc/src/snmp.gif
deleted file mode 100644
index d9985f990b..0000000000
--- a/lib/snmp/doc/src/snmp.gif
+++ /dev/null
Binary files differ
diff --git a/lib/snmp/doc/src/snmp_impl_example_agent.xml b/lib/snmp/doc/src/snmp_impl_example_agent.xml
index a86006a0a7..e576fa51f3 100644
--- a/lib/snmp/doc/src/snmp_impl_example_agent.xml
+++ b/lib/snmp/doc/src/snmp_impl_example_agent.xml
@@ -47,6 +47,7 @@
EX1-MIB DEFINITIONS ::= BEGIN
IMPORTS
+ experimental FROM RFC1155-SMI
RowStatus FROM STANDARD-MIB
DisplayString FROM RFC1213-MIB
OBJECT-TYPE FROM RFC-1212
@@ -81,7 +82,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
FriendsEntry ::=
SEQUENCE {
- fIndex
+ fIndex
INTEGER,
fName
DisplayString,
@@ -105,6 +106,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"Name of friend"
::= { friendsEntry 2 }
+
fAddress OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
ACCESS read-write
@@ -112,6 +114,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"Address of friend"
::= { friendsEntry 3 }
+
fStatus OBJECT-TYPE
SYNTAX RowStatus
ACCESS read-write
@@ -119,12 +122,13 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"The status of this conceptual row."
::= { friendsEntry 4 }
+
fTrap TRAP-TYPE
ENTERPRISE example1
VARIABLES { myName, fIndex }
DESCRIPTION
- "This trap is sent when something happens to
- the friend specified by fIndex."
+ "This trap is sent when something happens to
+ the friend specified by fIndex."
::= 1
END
</code>
diff --git a/lib/snmp/doc/src/summary.html.src b/lib/snmp/doc/src/summary.html.src
deleted file mode 100644
index 9bad4adbeb..0000000000
--- a/lib/snmp/doc/src/summary.html.src
+++ /dev/null
@@ -1 +0,0 @@
-Simple Network Management Protocol (SNMP) support including a MIB compiler, a simple SNMP manager and tools for creating SNMP agents
diff --git a/lib/snmp/doc/src/user_guide.gif b/lib/snmp/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/snmp/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/snmp/doc/src/warning.gif b/lib/snmp/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/snmp/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 3c1a6f2afd..2ed2c4580c 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -605,7 +605,12 @@ init_per_group(multiple_reqs_3 = GroupName, Config) ->
init_per_group(test_multi_threaded = GroupName, Config) ->
init_mt(snmp_test_lib:init_group_top_dir(GroupName, Config));
init_per_group(test_v3 = GroupName, Config) ->
- init_v3(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ case snmp_test_lib:crypto_start() of
+ ok ->
+ init_v3(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ _ ->
+ {skip, "Crypto did not start"}
+ end;
init_per_group(test_v1_v2 = GroupName, Config) ->
init_v1_v2(snmp_test_lib:init_group_top_dir(GroupName, Config));
init_per_group(test_v2 = GroupName, Config) ->
@@ -631,11 +636,26 @@ init_per_group(mib_storage_varm_dets = GroupName, Config) ->
init_varm_mib_storage_dets(
snmp_test_lib:init_group_top_dir(GroupName, Config));
init_per_group(mib_storage_size_check_mnesia = GroupName, Config) ->
- init_size_check_msm(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ case snmp_test_lib:crypto_start() of
+ ok ->
+ init_size_check_msm(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ _ ->
+ {skip, "Crypto did not start"}
+ end;
init_per_group(mib_storage_size_check_dets = GroupName, Config) ->
- init_size_check_msd(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ case snmp_test_lib:crypto_start() of
+ ok ->
+ init_size_check_msm(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ _ ->
+ {skip, "Crypto did not start"}
+ end;
init_per_group(mib_storage_size_check_ets = GroupName, Config) ->
- init_size_check_mse(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ case snmp_test_lib:crypto_start() of
+ ok ->
+ init_size_check_msm(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ _ ->
+ {skip, "Crypto did not start"}
+ end;
init_per_group(mib_storage_mnesia = GroupName, Config) ->
init_mib_storage_mnesia(snmp_test_lib:init_group_top_dir(GroupName,
Config));
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index 4bfeb0f8d1..6ced55f0cc 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -156,16 +156,25 @@ init_per_suite(Config0) when is_list(Config0) ->
?DBG("init_per_suite -> entry with"
"~n Config0: ~p", [Config0]),
- Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0),
- Config2 = snmp_test_lib:fix_data_dir(Config1),
-
- %% Mib-dirs
- %% data_dir is trashed by the test-server / common-test
- %% so there is no point in fixing it...
- MibDir = snmp_test_lib:lookup(data_dir, Config2),
- StdMibDir = filename:join([code:priv_dir(snmp), "mibs"]),
-
- [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2].
+ %% Preferably this test SUITE should be divided into groups
+ %% so that if crypto does not work only v3 tests that
+ %% need crypto will be skipped, but as this is only a
+ %% problem with one legacy test machine, we will procrastinate
+ %% until we have a more important reason to fix this.
+ case snmp_test_lib:crypto_start() of
+ ok ->
+ Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0),
+ Config2 = snmp_test_lib:fix_data_dir(Config1),
+ %% Mib-dirs
+ %% data_dir is trashed by the test-server / common-test
+ %% so there is no point in fixing it...
+ MibDir = snmp_test_lib:lookup(data_dir, Config2),
+ StdMibDir = filename:join([code:priv_dir(snmp), "mibs"]),
+
+ [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2];
+ _ ->
+ {skip, "Crypto did not start"}
+ end.
end_per_suite(Config) when is_list(Config) ->
diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
index 24c14d86ea..6a3466b6e4 100644
--- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
+++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
@@ -88,8 +88,17 @@ groups() ->
].
init_per_suite(Config) ->
- [{agent_port, ?AGENT_PORT}, {manager_port, ?MANAGER_PORT} | Config].
-
+ case re:run(os:cmd("snmpd -v"),"NET-SNMP", [{capture, first}]) of
+ nomatch ->
+ {skip, "snmpd is NOT NET-SNMP"};
+ {match, _} ->
+ case re:run(os:cmd("snmpd -v"),"5.4|5.6.2.1", [{capture, first}]) of
+ nomatch ->
+ [{agent_port, ?AGENT_PORT}, {manager_port, ?MANAGER_PORT} | Config];
+ {match, _} ->
+ {skip, "buggy snmpd"}
+ end
+ end.
end_per_suite(_Config) ->
ok.
@@ -322,7 +331,7 @@ snmpget(Oid, Transport, Config) ->
Args =
["-c", "public", net_snmp_version(Versions),
- "-m", "",
+ "-m", ":",
"-Cf",
net_snmp_addr_str(Transport),
oid_str(Oid)],
@@ -353,11 +362,13 @@ start_snmpd(Community, SysDescr, Config) ->
["--rocommunity"++domain_suffix(Domain)++"="
++Community++" "++inet_parse:ntoa(Ip)
|| {Domain, {Ip, _}} <- Targets],
+
SnmpdArgs =
- ["-f", "-r", %"-Dverbose",
- "-c", filename:join(DataDir, "snmpd.conf"),
- "-C", "-Lo",
- "-m", "",
+ ["-f", "-r", %"-Dverbose",
+ "-c", filename:join(DataDir, "snmpd.conf"),
+ "-C",
+ "-Lo",
+ "-m", ":",
"--sysDescr="++SysDescr,
"--agentXSocket=tcp:localhost:"++integer_to_list(Port)]
++ CommunityArgs
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index ef48608bda..c195f9f5d9 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 5.2.8
+SNMP_VSN = 5.2.9
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile
index adbda5a030..f54f5e0708 100644
--- a/lib/ssh/doc/src/Makefile
+++ b/lib/ssh/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2016. All Rights Reserved.
+# Copyright Ericsson AB 2004-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -48,18 +48,18 @@ XML_REF3_FILES = ssh.xml \
XML_REF6_FILES = ssh_app.xml
-XML_PART_FILES = part_notes.xml \
+XML_PART_FILES = \
usersguide.xml
XML_CHAPTER_FILES = notes.xml \
introduction.xml \
- ssh_protocol.xml \
using_ssh.xml \
configure_algos.xml
+# ssh_protocol.xml \
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES)\
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
IMAGE_FILES = SSH_protocols.png
@@ -85,10 +85,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -116,12 +116,12 @@ clean clean_docs:
man: $(MAN3_FILES) $(MAN6_FILES)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/ssh/doc/src/fascicules.xml b/lib/ssh/doc/src/fascicules.xml
deleted file mode 100644
index 7e99398c16..0000000000
--- a/lib/ssh/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="usersguide" href="usersguide_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 216f7b1bff..df2e04c92a 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,100 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.6.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Adjusted supervisor timeouts</p>
+ <p>
+ Own Id: OTP-14907</p>
+ </item>
+ <item>
+ <p>
+ Remove ERROR messages for slow process exits</p>
+ <p>
+ Own Id: OTP-14930</p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add option <c>save_accepted_host</c> to
+ <c>ssh:connection</c>. This option, if set to false,
+ inhibits saving host keys to e.g the file
+ <c>known_hosts</c>.</p>
+ <p>
+ Own Id: OTP-14935</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.6.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix problem with OpenSSH 7.2 (and later) clients that has
+ used sha1 instead of sha2 for rsa-sha-256/512 user's
+ public keys.</p>
+ <p>
+ Own Id: OTP-14827 Aux Id: ERL-531 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.6.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Passphrase option for ecdsa public keys was missing.</p>
+ <p>
+ Own Id: OTP-14602</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The host and user public key handling is hardened so that
+ a faulty plugin can't deliver a key of wrong type.</p>
+ <p>
+ Better checks in the server of the available hostkey's
+ types at start and at each accept.</p>
+ <p>
+ Better checks in the client of the available user public
+ key types at connect.</p>
+ <p>
+ Own Id: OTP-14676 Aux Id: ERIERL-52, OTP-14570 </p>
+ </item>
+ <item>
+ <p>
+ SSH can now fetch the host key from the private keys
+ stored in an Engine. See the crypto application for
+ details about Engines.</p>
+ <p>
+ Own Id: OTP-14757</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.6.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -57,7 +151,6 @@
</section>
<section><title>Ssh 4.6.1</title>
-
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -312,6 +405,22 @@
</section>
+
+<section><title>Ssh 4.4.2.2</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Default exec is disabled when a user-defined shell is
+ enabled.</p>
+ <p>
+ Own Id: OTP-14881</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+
<section><title>Ssh 4.4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/part_notes.xml b/lib/ssh/doc/src/part_notes.xml
deleted file mode 100644
index aaca8ca9f2..0000000000
--- a/lib/ssh/doc/src/part_notes.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>SSH Release Notes</title>
- <prepared>Jakob Cederlund</prepared>
- <docno></docno>
- <date></date>
- <rev>%VSN%</rev>
- <file>part_notes.sgml</file>
- </header>
- <description>
- <p>This document describes the changes made to the SSH application.
- </p>
- </description>
- <xi:include file="notes.xml"/>
-</part>
-
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index d9516fff12..acf94ff6af 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -178,6 +178,12 @@
supplied with this option.
</p>
</item>
+ <tag><c><![CDATA[{ecdsa_pass_phrase, string()}]]></c></tag>
+ <item>
+ <p>If the user ECDSA key is protected by a passphrase, it can be
+ supplied with this option.
+ </p>
+ </item>
<tag>
<c><![CDATA[{silently_accept_hosts, boolean()}]]></c> <br/>
<c><![CDATA[{silently_accept_hosts, CallbackFun}]]></c> <br/>
@@ -221,6 +227,18 @@
</item>
</list>
</item>
+
+ <tag><c><![CDATA[{save_accepted_host, boolean()}]]></c></tag>
+ <item>
+ <p>If <c>true</c>, the client saves an accepted host key to avoid the
+ accept question the next time the same host is connected. If the option
+ <c>key_cb</c> is not present, the key is saved in the file "known_hosts".
+ </p>
+ <p>If <c>false</c>, the key is not saved and the key will still be unknown
+ at the next access of the same host.
+ </p>
+ </item>
+
<tag><c><![CDATA[{user_interaction, boolean()}]]></c></tag>
<item>
<p>If <c>false</c>, disables the client to connect to the server
diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml
index a1cd9d4b02..98a1676ca4 100644
--- a/lib/ssh/doc/src/ssh_client_key_api.xml
+++ b/lib/ssh/doc/src/ssh_client_key_api.xml
@@ -56,11 +56,17 @@
<tag><c>string() =</c></tag>
<item><p><c>[byte()]</c></p></item>
<tag><c>public_key() =</c></tag>
- <item><p><c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
+ <item><p><c>#'RSAPublicKey'{}
+ | {integer(),#'Dss-Parms'{}}
+ | {#'ECPoint'{},{namedCurve,Curve::string()}}</c></p></item>
<tag><c>private_key() =</c></tag>
- <item><p><c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
+ <item><p><c>#'RSAPrivateKey'{}
+ | #'DSAPrivateKey'{}
+ | #'ECPrivateKey'{}</c></p></item>
<tag><c>public_key_algorithm() =</c></tag>
- <item><p><c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
+ <item><p><c>'ssh-rsa' | 'ssh-dss'
+ | 'rsa-sha2-256' | 'rsa-sha2-384' | 'rsa-sha2-512'
+ | 'ecdsa-sha2-nistp256' | 'ecdsa-sha2-nistp384' | 'ecdsa-sha2-nistp521' </c></p></item>
</taglist>
</section>
@@ -73,10 +79,11 @@
<d>Description of the host that owns the <c>PublicKey</c>.</d>
<v>Key = public_key()</v>
- <d>Normally an RSA or DSA public key, but handling of other public keys can be added.</d>
+ <d>Normally an RSA, DSA or ECDSA public key, but handling of other public keys can be added.</d>
<v>ConnectOptions = proplists:proplist()</v>
- <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso></d>
+ <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
<v>Reason = term().</v>
</type>
<desc>
@@ -89,17 +96,17 @@
<fsummary>Checks if a host key is trusted.</fsummary>
<type>
<v>Key = public_key() </v>
- <d>Normally an RSA or DSA public key, but handling of other public keys can be added.</d>
+ <d>Normally an RSA, DSA or ECDSA public key, but handling of other public keys can be added.</d>
<v>Host = string()</v>
<d>Description of the host.</d>
<v>Algorithm = public_key_algorithm()</v>
- <d>Host key algorithm. Is to support <c>'ssh-rsa'| 'ssh-dss'</c>, but more algorithms
- can be handled.</d>
+ <d>Host key algorithm.</d>
<v>ConnectOptions = proplists:proplist() </v>
- <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>.</d>
+ <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
<v>Result = boolean()</v>
</type>
@@ -110,15 +117,15 @@
<func>
<name>Module:user_key(Algorithm, ConnectOptions) ->
- {ok, PrivateKey} | {error, Reason}</name>
+ {ok, PrivateKey} | {error, Reason}</name>
<fsummary>Fetches the users <em>public key</em> matching the <c>Algorithm</c>.</fsummary>
<type>
<v>Algorithm = public_key_algorithm()</v>
- <d>Host key algorithm. Is to support <c>'ssh-rsa'| 'ssh-dss'</c> but more algorithms
- can be handled.</d>
+ <d>Host key algorithm.</d>
<v>ConnectOptions = proplists:proplist()</v>
- <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso></d>
+ <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
<v>PrivateKey = private_key()</v>
<d>Private key of the user matching the <c>Algorithm</c>.</d>
diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml
index a0694ca8d9..c6808b95d1 100644
--- a/lib/ssh/doc/src/ssh_server_key_api.xml
+++ b/lib/ssh/doc/src/ssh_server_key_api.xml
@@ -57,11 +57,17 @@
<tag><c>string() =</c></tag>
<item><p><c>[byte()]</c></p></item>
<tag><c>public_key() =</c></tag>
- <item><p><c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
+ <item><p><c>#'RSAPublicKey'{}
+ | {integer(),#'Dss-Parms'{}}
+ | {#'ECPoint'{},{namedCurve,Curve::string()}}</c></p></item>
<tag><c>private_key() =</c></tag>
- <item><p><c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
+ <item><p><c>#'RSAPrivateKey'{}
+ | #'DSAPrivateKey'{}
+ | #'ECPrivateKey'{}</c></p></item>
<tag><c>public_key_algorithm() =</c></tag>
- <item><p><c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
+ <item><p><c>'ssh-rsa' | 'ssh-dss'
+ | 'rsa-sha2-256' | 'rsa-sha2-384' | 'rsa-sha2-512'
+ | 'ecdsa-sha2-nistp256' | 'ecdsa-sha2-nistp384' | 'ecdsa-sha2-nistp521' </c></p></item>
</taglist>
</section>
@@ -72,12 +78,13 @@
<fsummary>Fetches the host’s private key.</fsummary>
<type>
<v>Algorithm = public_key_algorithm()</v>
- <d>Host key algorithm. Is to support <c>'ssh-rsa' | 'ssh-dss'</c>, but more algorithms
- can be handled.</d>
+ <d>Host key algorithm.</d>
<v>DaemonOptions = proplists:proplist()</v>
- <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>.</d>
- <v>Key = private_key()</v>
- <d>Private key of the host matching the <c>Algorithm</c>.</d>
+ <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
+ <v>Key = private_key() | crypto:engine_key_ref()</v>
+ <d>Private key of the host matching the <c>Algorithm</c>.
+ It may be a reference to a 'ssh-rsa', rsa-sha2-* or 'ssh-dss' (NOT ecdsa) key stored in a loaded Engine.</d>
<v>Reason = term()</v>
</type>
<desc>
@@ -90,11 +97,12 @@
<fsummary>Checks if the user key is authorized.</fsummary>
<type>
<v>Key = public_key()</v>
- <d>Normally an RSA or DSA public key, but handling of other public keys can be added</d>
+ <d>Normally an RSA, DSA or ECDSA public key, but handling of other public keys can be added</d>
<v>User = string()</v>
<d>User owning the public key.</d>
<v>DaemonOptions = proplists:proplist()</v>
- <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>.</d>
+ <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
<v>Result = boolean()</v>
</type>
<desc>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index ed7fbf9cf3..129426a6d5 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -464,11 +464,16 @@
<v>FileInfo = record()</v>
</type>
<desc>
- <p>Returns a <c><![CDATA[file_info]]></c> record from the file specified by
+ <p>Returns a <c><![CDATA[file_info]]></c> record from the file system object specified by
<c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>. See
<seealso marker="kernel:file#read_file_info-2">file:read_file_info/2</seealso>
for information about the record.
</p>
+ <p>
+ Depending on the underlying OS:es links might be followed and info on the final file, directory
+ etc is returned. See <seealso marker="#read_link_info-2">ssh_sftp::read_link_info/2</seealso>
+ on how to get information on links instead.
+ </p>
</desc>
</func>
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 974292fde1..4a22322333 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -42,10 +42,10 @@
{env, []},
{mod, {ssh_app, []}},
{runtime_dependencies, [
- "crypto-3.7.3",
+ "crypto-4.2",
"erts-6.0",
"kernel-3.0",
- "public_key-1.4",
+ "public_key-1.5.2",
"stdlib-3.3"
]}]}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 1a5d48baca..032d87bdad 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -188,6 +188,7 @@ daemon(Port) ->
daemon(Socket, UserOptions) when is_port(Socket) ->
try
#{} = Options = ssh_options:handle_options(server, UserOptions),
+
case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
ok ->
{ok, {IP,Port}} = inet:sockname(Socket),
@@ -461,6 +462,9 @@ open_listen_socket(_Host0, Port0, Options0) ->
%%%----------------------------------------------------------------
finalize_start(Host, Port, Profile, Options0, F) ->
try
+ %% throws error:Error if no usable hostkey is found
+ ssh_connection_handler:available_hkey_algorithms(server, Options0),
+
sshd_sup:start_child(Host, Port, Profile, Options0)
of
{error, {already_started, _}} ->
@@ -470,6 +474,8 @@ finalize_start(Host, Port, Profile, Options0, F) ->
Result = {ok,_} ->
F(Options0, Result)
catch
+ error:{shutdown,Err} ->
+ {error,Err};
exit:{noproc, _} ->
{error, ssh_not_started}
end.
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index d6d412db43..4711f54fb5 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -35,6 +35,8 @@
-define(DEFAULT_TRANSPORT, {tcp, gen_tcp, tcp_closed} ).
+-define(DEFAULT_SHELL, {shell, start, []} ).
+
-define(MAX_RND_PADDING_LEN, 15).
-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
@@ -63,8 +65,8 @@
-define(uint16(X), << ?UINT16(X) >> ).
-define(uint32(X), << ?UINT32(X) >> ).
-define(uint64(X), << ?UINT64(X) >> ).
--define(string(X), << ?STRING(list_to_binary(X)) >> ).
-define(string_utf8(X), << ?STRING(unicode:characters_to_binary(X)) >> ).
+-define(string(X), ?string_utf8(X)).
-define(binary(X), << ?STRING(X) >>).
%% Cipher details
diff --git a/lib/ssh/src/ssh_acceptor_sup.erl b/lib/ssh/src/ssh_acceptor_sup.erl
index a24664793b..fc564a359b 100644
--- a/lib/ssh/src/ssh_acceptor_sup.erl
+++ b/lib/ssh/src/ssh_acceptor_sup.erl
@@ -86,10 +86,7 @@ child_spec(Address, Port, Profile, Options) ->
Timeout = ?GET_INTERNAL_OPT(timeout, Options, ?DEFAULT_TIMEOUT),
#{id => id(Address, Port, Profile),
start => {ssh_acceptor, start_link, [Port, Address, Options, Timeout]},
- restart => transient,
- shutdown => 5500, %brutal_kill,
- type => worker,
- modules => [ssh_acceptor]
+ restart => transient % because a crashed listener could be replaced by a new one
}.
id(Address, Port, Profile) ->
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index ac64a7bf14..03d264745b 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -145,14 +145,17 @@ get_public_key(SigAlg, #ssh{opts = Opts}) ->
case KeyCb:user_key(KeyAlg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
{ok, PrivKey} ->
try
+ %% Check the key - the KeyCb may be a buggy plugin
+ true = ssh_transport:valid_key_sha_alg(PrivKey, KeyAlg),
Key = ssh_transport:extract_public_key(PrivKey),
public_key:ssh_encode(Key, ssh2_pubkey)
of
PubKeyBlob -> {ok,{PrivKey,PubKeyBlob}}
catch
_:_ ->
- not_ok
+ not_ok
end;
+
_Error ->
not_ok
end.
@@ -301,11 +304,10 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
SigWLen/binary>>
},
SessionId,
- #ssh{opts = Opts,
- userauth_supported_methods = Methods} = Ssh) ->
+ #ssh{userauth_supported_methods = Methods} = Ssh) ->
case verify_sig(SessionId, User, "ssh-connection",
- BAlg, KeyBlob, SigWLen, Opts) of
+ BAlg, KeyBlob, SigWLen, Ssh) of
true ->
{authorized, User,
ssh_transport:ssh_packet(
@@ -515,7 +517,7 @@ pre_verify_sig(User, KeyBlob, Opts) ->
false
end.
-verify_sig(SessionId, User, Service, AlgBin, KeyBlob, SigWLen, Opts) ->
+verify_sig(SessionId, User, Service, AlgBin, KeyBlob, SigWLen, #ssh{opts = Opts} = Ssh) ->
try
Alg = binary_to_list(AlgBin),
{KeyCb,KeyCbOpts} = ?GET_OPT(key_cb, Opts),
@@ -526,7 +528,7 @@ verify_sig(SessionId, User, Service, AlgBin, KeyBlob, SigWLen, Opts) ->
<<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen,
<<?UINT32(AlgLen), _Alg:AlgLen/binary,
?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig,
- ssh_transport:verify(PlainText, ssh_transport:sha(Alg), Sig, Key)
+ ssh_transport:verify(PlainText, ssh_transport:sha(Alg), Sig, Key, Ssh)
catch
_:_ ->
false
diff --git a/lib/ssh/src/ssh_channel_sup.erl b/lib/ssh/src/ssh_channel_sup.erl
index 6b01dc334d..8444533fd1 100644
--- a/lib/ssh/src/ssh_channel_sup.erl
+++ b/lib/ssh/src/ssh_channel_sup.erl
@@ -26,7 +26,7 @@
-behaviour(supervisor).
--export([start_link/1, start_child/2]).
+-export([start_link/1, start_child/5]).
%% Supervisor callback
-export([init/1]).
@@ -37,7 +37,14 @@
start_link(Args) ->
supervisor:start_link(?MODULE, [Args]).
-start_child(Sup, ChildSpec) ->
+start_child(Sup, Callback, Id, Args, Exec) ->
+ ChildSpec =
+ #{id => make_ref(),
+ start => {ssh_channel, start_link, [self(), Id, Callback, Args, Exec]},
+ restart => temporary,
+ type => worker,
+ modules => [ssh_channel]
+ },
supervisor:start_child(Sup, ChildSpec).
%%%=========================================================================
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 62854346b0..958c342f5f 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -127,7 +127,8 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
cm = ConnectionHandler}};
handle_ssh_msg({ssh_cm, ConnectionHandler,
- {exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined} = State) ->
+ {exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined,
+ shell=?DEFAULT_SHELL} = State) ->
{Reply, Status} = exec(Cmd),
write_chars(ConnectionHandler,
ChannelId, io_lib:format("~p\n", [Reply])),
@@ -136,6 +137,15 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
ssh_connection:exit_status(ConnectionHandler, ChannelId, Status),
ssh_connection:send_eof(ConnectionHandler, ChannelId),
{stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
+
+handle_ssh_msg({ssh_cm, ConnectionHandler,
+ {exec, ChannelId, WantReply, _Cmd}}, #state{exec = undefined} = State) ->
+ write_chars(ConnectionHandler, ChannelId, 1, "Prohibited.\n"),
+ ssh_connection:reply_request(ConnectionHandler, WantReply, success, ChannelId),
+ ssh_connection:exit_status(ConnectionHandler, ChannelId, 255),
+ ssh_connection:send_eof(ConnectionHandler, ChannelId),
+ {stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
+
handle_ssh_msg({ssh_cm, ConnectionHandler,
{exec, ChannelId, WantReply, Cmd}}, State) ->
NewState = start_shell(ConnectionHandler, Cmd, State),
@@ -453,11 +463,14 @@ move_cursor(From, To, #ssh_pty{width=Width, term=Type}) ->
%% %%% make sure that there is data to send
%% %%% before calling ssh_connection:send
write_chars(ConnectionHandler, ChannelId, Chars) ->
+ write_chars(ConnectionHandler, ChannelId, ?SSH_EXTENDED_DATA_DEFAULT, Chars).
+
+write_chars(ConnectionHandler, ChannelId, Type, Chars) ->
case has_chars(Chars) of
false -> ok;
true -> ssh_connection:send(ConnectionHandler,
ChannelId,
- ?SSH_EXTENDED_DATA_DEFAULT,
+ Type,
Chars)
end.
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 7e9ee78fd2..946ae2967b 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -812,22 +812,20 @@ start_channel(Cb, Id, Args, SubSysSup, Opts) ->
start_channel(Cb, Id, Args, SubSysSup, undefined, Opts).
start_channel(Cb, Id, Args, SubSysSup, Exec, Opts) ->
- ChildSpec = child_spec(Cb, Id, Args, Exec),
ChannelSup = ssh_subsystem_sup:channel_supervisor(SubSysSup),
- assert_limit_num_channels_not_exceeded(ChannelSup, Opts),
- ssh_channel_sup:start_child(ChannelSup, ChildSpec).
+ case max_num_channels_not_exceeded(ChannelSup, Opts) of
+ true ->
+ ssh_channel_sup:start_child(ChannelSup, Cb, Id, Args, Exec);
+ false ->
+ throw(max_num_channels_exceeded)
+ end.
-assert_limit_num_channels_not_exceeded(ChannelSup, Opts) ->
+max_num_channels_not_exceeded(ChannelSup, Opts) ->
MaxNumChannels = ?GET_OPT(max_channels, Opts),
NumChannels = length([x || {_,_,worker,[ssh_channel]} <-
supervisor:which_children(ChannelSup)]),
- if
- %% Note that NumChannels is BEFORE starting a new one
- NumChannels < MaxNumChannels ->
- ok;
- true ->
- throw(max_num_channels_exceeded)
- end.
+ %% Note that NumChannels is BEFORE starting a new one
+ NumChannels < MaxNumChannels.
%%--------------------------------------------------------------------
%%% Internal functions
@@ -874,14 +872,6 @@ check_subsystem(SsName, Options) ->
Value
end.
-child_spec(Callback, Id, Args, Exec) ->
- Name = make_ref(),
- StartFunc = {ssh_channel, start_link, [self(), Id, Callback, Args, Exec]},
- Restart = temporary,
- Shutdown = 3600,
- Type = worker,
- {Name, StartFunc, Restart, Shutdown, Type, [ssh_channel]}.
-
start_cli(#connection{cli_spec = no_cli}, _) ->
{error, cli_disabled};
start_cli(#connection{options = Options,
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 54fce6bd99..c8ac3a9c04 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -46,6 +46,7 @@
%%% Internal application API
-export([start_connection/4,
+ available_hkey_algorithms/2,
open_channel/6,
request/6, request/7,
reply_request/3,
@@ -442,13 +443,12 @@ init_ssh_record(Role, Socket, Opts) ->
init_ssh_record(Role, Socket, PeerAddr, Opts).
init_ssh_record(Role, _Socket, PeerAddr, Opts) ->
- KeyCb = ?GET_OPT(key_cb, Opts),
AuthMethods = ?GET_OPT(auth_methods, Opts),
S0 = #ssh{role = Role,
- key_cb = KeyCb,
+ key_cb = ?GET_OPT(key_cb, Opts),
opts = Opts,
userauth_supported_methods = AuthMethods,
- available_host_keys = supported_host_keys(Role, KeyCb, Opts),
+ available_host_keys = available_hkey_algorithms(Role, Opts),
random_length_padding = ?GET_OPT(max_random_length_padding, Opts)
},
@@ -1460,13 +1460,12 @@ terminate(shutdown, StateName, State0) ->
State0),
finalize_termination(StateName, State);
-%% terminate({shutdown,Msg}, StateName, State0) when is_record(Msg,ssh_msg_disconnect)->
-%% State = send_msg(Msg, State0),
-%% finalize_termination(StateName, Msg, State);
-
terminate({shutdown,_R}, StateName, State) ->
finalize_termination(StateName, State);
+terminate(kill, StateName, State) ->
+ finalize_termination(StateName, State);
+
terminate(Reason, StateName, State0) ->
%% Others, e.g undef, {badmatch,_}
log_error(Reason),
@@ -1569,44 +1568,42 @@ peer_role(client) -> server;
peer_role(server) -> client.
%%--------------------------------------------------------------------
-supported_host_keys(client, _, Options) ->
- try
- find_sup_hkeys(Options)
- of
- [] ->
+available_hkey_algorithms(Role, Options) ->
+ KeyCb = ?GET_OPT(key_cb, Options),
+ case [A || A <- available_hkey_algos(Options),
+ (Role==client) orelse available_host_key(KeyCb, A, Options)
+ ] of
+
+ [] when Role==client ->
error({shutdown, "No public key algs"});
- Algs ->
- [atom_to_list(A) || A<-Algs]
- catch
- exit:Reason ->
- error({shutdown, Reason})
- end;
-supported_host_keys(server, KeyCb, Options) ->
- [atom_to_list(A) || A <- find_sup_hkeys(Options),
- available_host_key(KeyCb, A, Options)
- ].
+ [] when Role==server ->
+ error({shutdown, "No host key available"});
-find_sup_hkeys(Options) ->
- case proplists:get_value(public_key,
- ?GET_OPT(preferred_algorithms,Options)
- )
- of
- undefined ->
- ssh_transport:default_algorithms(public_key);
- L ->
- NonSupported = L--ssh_transport:supported_algorithms(public_key),
- L -- NonSupported
+ Algs ->
+ [atom_to_list(A) || A<-Algs]
end.
+available_hkey_algos(Options) ->
+ SupAlgos = ssh_transport:supported_algorithms(public_key),
+ HKeys = proplists:get_value(public_key,
+ ?GET_OPT(preferred_algorithms,Options)
+ ),
+ NonSupported = HKeys -- SupAlgos,
+ AvailableAndSupported = HKeys -- NonSupported,
+ AvailableAndSupported.
+
%% Alg :: atom()
available_host_key({KeyCb,KeyCbOpts}, Alg, Opts) ->
UserOpts = ?GET_OPT(user_options, Opts),
case KeyCb:host_key(Alg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
- {ok,_} -> true;
- _ -> false
+ {ok,Key} ->
+ %% Check the key - the KeyCb may be a buggy plugin
+ ssh_transport:valid_key_sha_alg(Key, Alg);
+ _ ->
+ false
end.
diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
index 60ee8b7c73..2e8450090a 100644
--- a/lib/ssh/src/ssh_connection_sup.erl
+++ b/lib/ssh/src/ssh_connection_sup.erl
@@ -52,10 +52,7 @@ init(_) ->
},
ChildSpecs = [#{id => undefined, % As simple_one_for_one is used.
start => {ssh_connection_handler, start_link, []},
- restart => temporary,
- shutdown => 4000,
- type => worker,
- modules => [ssh_connection_handler]
+ restart => temporary % because there is no way to restart a crashed connection
}
],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl
index 3f742ad9b6..eb2c2848f3 100644
--- a/lib/ssh/src/ssh_dbg.erl
+++ b/lib/ssh/src/ssh_dbg.erl
@@ -24,6 +24,8 @@
-export([messages/0, messages/1, messages/2, messages/3,
auth/0, auth/1, auth/2, auth/3,
+ algs/0, algs/1, algs/2, algs/3,
+ hostkey/0, hostkey/1, hostkey/2, hostkey/3,
stop/0
]).
@@ -46,6 +48,16 @@ auth(F) -> start(auth,F).
auth(F,X) -> start(auth,F,X).
auth(F,M,I) -> start(auth,F,M,I).
+algs() -> start(algs).
+algs(F) -> start(algs,F).
+algs(F,X) -> start(algs,F,X).
+algs(F,M,I) -> start(algs,F,M,I).
+
+hostkey() -> start(hostkey).
+hostkey(F) -> start(hostkey,F).
+hostkey(F,X) -> start(hostkey,F,X).
+hostkey(F,M,I) -> start(hostkey,F,M,I).
+
stop() -> dbg:stop().
%%%----------------------------------------------------------------
@@ -71,23 +83,49 @@ fmt_fun(F) -> fun(Fmt,Args,Data) -> F(Fmt,Args), Data end.
id_fun() -> fun(X) -> X end.
%%%----------------------------------------------------------------
-dbg_ssh(msg) ->
- dbg_ssh(auth),
- dbg:tp(ssh_message,encode,1, x),
- dbg:tp(ssh_message,decode,1, x),
- dbg:tpl(ssh_transport,select_algorithm,4, x),
- dbg:tp(ssh_transport,hello_version_msg,1, x),
- dbg:tp(ssh_transport,handle_hello_version,1, x),
- dbg:tpl(ssh_connection_handler,ext_info,2, x);
+dbg_ssh(What) ->
+ case [E || E <- lists:flatten(dbg_ssh0(What)),
+ element(1,E) =/= ok] of
+ [] -> ok;
+ Other -> Other
+ end.
+
+
+dbg_ssh0(auth) ->
+ [dbg:tp(ssh_transport,hello_version_msg,1, x),
+ dbg:tp(ssh_transport,handle_hello_version,1, x),
+ dbg:tp(ssh_message,encode,1, x),
+ dbg:tpl(ssh_transport,select_algorithm,4, x),
+ dbg:tpl(ssh_connection_handler,ext_info,2, x),
+ lists:map(fun(F) -> dbg:tp(ssh_auth, F, x) end,
+ [publickey_msg, password_msg, keyboard_interactive_msg])
+ ];
+
+dbg_ssh0(algs) ->
+ [dbg:tpl(ssh_transport,select_algorithm,4, x),
+ dbg:tpl(ssh_connection_handler,ext_info,2, x)
+ ];
+
+dbg_ssh0(hostkey) ->
+ [dbg:tpl(ssh_transport, verify_host_key, 4, x),
+ dbg:tp(ssh_transport, verify, 4, x),
+ dbg:tpl(ssh_transport, known_host_key, 3, x),
+%% dbg:tpl(ssh_transport, accepted_host, 4, x),
+ dbg:tpl(ssh_transport, add_host_key, 4, x),
+ dbg:tpl(ssh_transport, is_host_key, 5, x)
+ ];
+
+dbg_ssh0(msg) ->
+ [dbg_ssh0(hostkey),
+ dbg_ssh0(auth),
+ dbg:tp(ssh_message,encode,1, x),
+ dbg:tp(ssh_message,decode,1, x),
+ dbg:tpl(ssh_transport,select_algorithm,4, x),
+ dbg:tp(ssh_transport,hello_version_msg,1, x),
+ dbg:tp(ssh_transport,handle_hello_version,1, x),
+ dbg:tpl(ssh_connection_handler,ext_info,2, x)
+ ].
-dbg_ssh(auth) ->
- dbg:tp(ssh_transport,hello_version_msg,1, x),
- dbg:tp(ssh_transport,handle_hello_version,1, x),
- dbg:tp(ssh_message,encode,1, x),
- dbg:tpl(ssh_transport,select_algorithm,4, x),
- dbg:tpl(ssh_connection_handler,ext_info,2, x),
- lists:foreach(fun(F) -> dbg:tp(ssh_auth, F, x) end,
- [publickey_msg, password_msg, keyboard_interactive_msg]).
%%%================================================================
cond_start(Type, WriteFun, MangleArgFun, Init) ->
@@ -108,12 +146,31 @@ msg_formater(msg, {trace_ts,_Pid,return_from,{ssh_message,encode,1},_Res,_TS}, D
msg_formater(msg, {trace_ts,_Pid,call,{ssh_message,decode,_},_TS}, D) ->
D;
msg_formater(msg, {trace_ts,Pid,return_from,{ssh_message,decode,1},Msg,TS}, D) ->
- fmt("~n~s ~p RECV ~s~n", [ts(TS),Pid,wr_record(shrink_bin(Msg))], D);
-
-msg_formater(auth, {trace_ts,Pid,return_from,{ssh_message,decode,1},#ssh_msg_userauth_failure{authentications=As},TS}, D) ->
+ Extra =
+ case Msg of
+ #ssh_msg_userauth_info_request{data = D0} ->
+ try ssh_message:decode_keyboard_interactive_prompts(D0, [])
+ of
+ Acc ->
+ io_lib:format(" -- decoded data:~n", []) ++
+ element(1,
+ lists:mapfoldl(
+ fun({Prompt,Echo}, N) ->
+ {io_lib:format(" prompt[~p]: \"~s\" (echo=~p)~n",[N,Prompt,Echo]), N+1}
+ end, 1, Acc))
+ catch
+ _:_ ->
+ ""
+ end;
+ _ ->
+ ""
+ end,
+ fmt("~n~s ~p RECV ~s~s~n", [ts(TS),Pid,wr_record(shrink_bin(Msg)),Extra], D);
+
+msg_formater(_auth, {trace_ts,Pid,return_from,{ssh_message,decode,1},#ssh_msg_userauth_failure{authentications=As},TS}, D) ->
fmt("~n~s ~p Client login FAILURE. Try ~s~n", [ts(TS),Pid,As], D);
-msg_formater(auth, {trace_ts,Pid,return_from,{ssh_message,decode,1},#ssh_msg_userauth_success{},TS}, D) ->
+msg_formater(_auth, {trace_ts,Pid,return_from,{ssh_message,decode,1},#ssh_msg_userauth_success{},TS}, D) ->
fmt("~n~s ~p Client login SUCCESS~n", [ts(TS),Pid], D);
@@ -155,20 +212,61 @@ msg_formater(_, {trace_ts,Pid,return_from,{ssh_connection_handler,ext_info,2},St
D
end;
+msg_formater(_, {trace_ts,Pid,call, {ssh_transport,verify_host_key,[_Ssh,_PK,_Dgst,{AlgStr,_Sign}]},TS}, D) ->
+ fmt("~n~s ~p Client got a ~s hostkey. Will try to verify it~n", [ts(TS),Pid,AlgStr], D);
+msg_formater(_, {trace_ts,Pid,return_from, {ssh_transport,verify_host_key,4}, Result, TS}, D) ->
+ case Result of
+ ok -> fmt("~n~s ~p Hostkey verified.~n", [ts(TS),Pid], D);
+ {error,E} ->
+ fmt("~n~s ~p ***** Hostkey NOT verified: ~p ******!~n", [ts(TS),Pid,E], D);
+ _ -> fmt("~n~s ~p ***** Hostkey is NOT verified: ~p ******!~n", [ts(TS),Pid,Result], D)
+ end;
+
+msg_formater(_, {trace_ts,Pid,return_from, {ssh_transport,verify,4}, Result, TS}, D) ->
+ case Result of
+ true -> D;
+ _ -> fmt("~n~s ~p Couldn't verify the signature!~n", [ts(TS),Pid], D)
+ end;
+
+msg_formater(_, {trace_ts,_Pid,call, {ssh_transport,is_host_key,_}, _TS}, D) -> D;
+msg_formater(_, {trace_ts,Pid,return_from, {ssh_transport,is_host_key,5}, {CbMod,Result}, TS}, D) ->
+ case Result of
+ true -> fmt("~n~s ~p Hostkey found by ~p.~n", [ts(TS),Pid,CbMod], D);
+ _ -> fmt("~n~s ~p Hostkey NOT found by ~p.~n", [ts(TS),Pid,CbMod], D)
+ end;
+
+msg_formater(_, {trace_ts,_Pid,call, {ssh_transport,add_host_key,_}, _TS}, D) -> D;
+msg_formater(_, {trace_ts,Pid,return_from, {ssh_transport,add_host_key,4}, {CbMod,Result}, TS}, D) ->
+ case Result of
+ ok -> fmt("~n~s ~p New hostkey added by ~p.~n", [ts(TS),Pid,CbMod], D);
+ _ -> D
+ end;
+
+msg_formater(_, {trace_ts,_Pid,call,{ssh_transport,known_host_key,_},_TS}, D) -> D;
+msg_formater(_, {trace_ts,Pid,return_from, {ssh_transport,known_host_key,3}, Result, TS}, D) ->
+ case Result of
+ ok -> D;
+ {error,E} -> fmt("~n~s ~p Hostkey addition failed: ~p~n", [ts(TS),Pid,E], D);
+ _ -> fmt("~n~s ~p Hostkey addition: ~p~n", [ts(TS),Pid,Result], D)
+ end;
+
msg_formater(_, {trace_ts,Pid,call,{ssh_auth,publickey_msg,[[SigAlg,#ssh{user=User}]]},TS}, D) ->
- fmt("~n~s ~p Client will try to login user ~p with public key algorithm ~p~n", [ts(TS),Pid,User,SigAlg], D);
+ fmt("~n~s ~p Client will try to login user ~p with method: public key algorithm ~p~n", [ts(TS),Pid,User,SigAlg], D);
msg_formater(_, {trace_ts,Pid,return_from,{ssh_auth,publickey_msg,1},{not_ok,#ssh{user=User}},TS}, D) ->
- fmt("~s ~p User ~p can't login with that kind of public key~n", [ts(TS),Pid,User], D);
+ fmt("~s ~p User ~p can't use that kind of public key~n", [ts(TS),Pid,User], D);
+msg_formater(_, {trace_ts,_Pid,return_from,{ssh_auth,publickey_msg,1},_,_TS}, D) -> D;
msg_formater(_, {trace_ts,Pid,call,{ssh_auth,password_msg,[[#ssh{user=User}]]},TS}, D) ->
- fmt("~n~s ~p Client will try to login user ~p with password~n", [ts(TS),Pid,User], D);
+ fmt("~n~s ~p Client will try to login user ~p with method: password~n", [ts(TS),Pid,User], D);
msg_formater(_, {trace_ts,Pid,return_from,{ssh_auth,password_msg,1},{not_ok,#ssh{user=User}},TS}, D) ->
- fmt("~s ~p User ~p can't login with password~n", [ts(TS),Pid,User], D);
+ fmt("~s ~p User ~p can't use method password as login method~n", [ts(TS),Pid,User], D);
+msg_formater(_, {trace_ts,_Pid,return_from,{ssh_auth,password_msg,1},_Result,_TS}, D) -> D;
msg_formater(_, {trace_ts,Pid,call,{ssh_auth,keyboard_interactive_msg,[[#ssh{user=User}]]},TS}, D) ->
- fmt("~n~s ~p Client will try to login user ~p with password~n", [ts(TS),Pid,User], D);
+ fmt("~n~s ~p Client will try to login user ~p with method: keyboard-interactive~n", [ts(TS),Pid,User], D);
msg_formater(_, {trace_ts,Pid,return_from,{ssh_auth,keyboard_interactive_msg,1},{not_ok,#ssh{user=User}},TS}, D) ->
- fmt("~s ~p User ~p can't login with keyboard_interactive password~n", [ts(TS),Pid,User], D);
+ fmt("~s ~p User ~p can't use method keyboard-interactive as login method~n", [ts(TS),Pid,User], D);
+msg_formater(_, {trace_ts,_Pid,return_from,{ssh_auth,keyboard_interactive_msg,1},_Result,_TS}, D) -> D;
msg_formater(msg, {trace_ts,Pid,send,{tcp,Sock,Bytes},Pid,TS}, D) ->
fmt("~n~s ~p TCP SEND on ~p~n ~p~n", [ts(TS),Pid,Sock, shrink_bin(Bytes)], D);
@@ -187,26 +285,20 @@ msg_formater(msg, {trace_ts,Pid,'receive',ErlangMsg,TS}, D) ->
fmt("~n~s ~p ERL MSG RECEIVE~n ~p~n", [ts(TS),Pid,shrink_bin(ErlangMsg)], D);
-%% msg_formater(_, {trace_ts,_Pid,return_from,MFA,_Ret,_TS}=M, D) ->
-%% case lists:member(MFA, [{ssh_auth,keyboard_interactive_msg,1},
-%% {ssh_auth,password_msg,1},
-%% {ssh_auth,publickey_msg,1}]) of
-%% true ->
-%% D;
-%% false ->
-%% fmt("~nDBG ~n~p~n", [shrink_bin(M)], D)
-%% end;
-
-%% msg_formater(_, M, D) ->
-%% fmt("~nDBG ~n~p~n", [shrink_bin(M)], D).
-
-msg_formater(_, _, D) ->
- D.
+msg_formater(_, _M, D) ->
+ fmt("~nDBG other ~n~p~n", [shrink_bin(_M)], D),
+ D.
%%%----------------------------------------------------------------
-record(data, {writer,
+ initialized,
acc}).
+fmt(Fmt, Args, D=#data{initialized=false}) ->
+ fmt(Fmt, Args,
+ D#data{acc = (D#data.writer)("~s~n", [initial_info()], D#data.acc),
+ initialized = true}
+ );
fmt(Fmt, Args, D=#data{writer=Write, acc=Acc}) ->
D#data{acc = Write(Fmt,Args,Acc)}.
@@ -221,10 +313,47 @@ setup_tracer(Type, WriteFun, MangleArgFun, Init) ->
msg_formater(Type, MangleArgFun(Arg), D)
end,
InitialData = #data{writer = WriteFun,
+ initialized = false,
acc = Init},
{ok,_} = dbg:tracer(process, {Handler, InitialData}),
ok.
+
+initial_info() ->
+ Lines =
+ [ts(erlang:timestamp()),
+ "",
+ "SSH:"]
+ ++ as_list_of_lines(case application:get_key(ssh,vsn) of
+ {ok,Vsn} -> Vsn;
+ _ -> "(ssh not started)"
+ end)
+ ++ ["",
+ "Cryptolib:"]
+ ++ as_list_of_lines(crypto:info_lib())
+ ++ ["",
+ "Crypto app:"]
+ ++ as_list_of_lines(crypto:supports()),
+ W = max_len(Lines),
+ append_lines([line_of($*, W+4)]
+ ++ prepend_lines("* ", Lines)
+ ++ [line_of($-, W+4)],
+ io_lib:nl()
+ ).
+
+
+as_list_of_lines(Term) ->
+ prepend_lines(" ",
+ string:tokens(lists:flatten(io_lib:format("~p",[Term])),
+ io_lib:nl() % Get line endings in current OS
+ )
+ ).
+
+line_of(Char,W) -> lists:duplicate(W,Char).
+max_len(L) -> lists:max([length(S) || S<-L]).
+append_lines(L, X) -> [S++X || S<-L].
+prepend_lines(X, L) -> [X++S || S<-L].
+
%%%----------------------------------------------------------------
shrink_bin(B) when is_binary(B), size(B)>256 -> {'*** SHRINKED BIN',
size(B),
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index b1fc05ae33..eb06f05a4a 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -252,12 +252,12 @@ encode(#ssh_msg_kexdh_init{e = E}) ->
<<?Ebyte(?SSH_MSG_KEXDH_INIT), ?Empint(E)>>;
encode(#ssh_msg_kexdh_reply{
- public_host_key = Key,
+ public_host_key = {Key,SigAlg},
f = F,
h_sig = Signature
}) ->
EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
- EncSign = encode_signature(Key, Signature),
+ EncSign = encode_signature(Key, SigAlg, Signature),
<<?Ebyte(?SSH_MSG_KEXDH_REPLY), ?Ebinary(EncKey), ?Empint(F), ?Ebinary(EncSign)>>;
encode(#ssh_msg_kex_dh_gex_request{
@@ -278,20 +278,20 @@ encode(#ssh_msg_kex_dh_gex_init{e = Public}) ->
encode(#ssh_msg_kex_dh_gex_reply{
%% Will be private key encode_host_key extracts only the public part!
- public_host_key = Key,
+ public_host_key = {Key,SigAlg},
f = F,
h_sig = Signature
}) ->
EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
- EncSign = encode_signature(Key, Signature),
+ EncSign = encode_signature(Key, SigAlg, Signature),
<<?Ebyte(?SSH_MSG_KEX_DH_GEX_REPLY), ?Ebinary(EncKey), ?Empint(F), ?Ebinary(EncSign)>>;
encode(#ssh_msg_kex_ecdh_init{q_c = Q_c}) ->
<<?Ebyte(?SSH_MSG_KEX_ECDH_INIT), ?Empint(Q_c)>>;
-encode(#ssh_msg_kex_ecdh_reply{public_host_key = Key, q_s = Q_s, h_sig = Sign}) ->
+encode(#ssh_msg_kex_ecdh_reply{public_host_key = {Key,SigAlg}, q_s = Q_s, h_sig = Sign}) ->
EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
- EncSign = encode_signature(Key, Sign),
+ EncSign = encode_signature(Key, SigAlg, Sign),
<<?Ebyte(?SSH_MSG_KEX_ECDH_REPLY), ?Ebinary(EncKey), ?Empint(Q_s), ?Ebinary(EncSign)>>;
encode(#ssh_msg_ignore{data = Data}) ->
@@ -602,12 +602,12 @@ decode_signature(<<?DEC_BIN(Alg,__0), ?UINT32(_), Signature/binary>>) ->
{binary_to_list(Alg), Signature}.
-encode_signature({#'RSAPublicKey'{},Sign}, Signature) ->
- SignName = list_to_binary(atom_to_list(Sign)),
+encode_signature(#'RSAPublicKey'{}, SigAlg, Signature) ->
+ SignName = list_to_binary(atom_to_list(SigAlg)),
<<?Ebinary(SignName), ?Ebinary(Signature)>>;
-encode_signature({{_, #'Dss-Parms'{}},_}, Signature) ->
+encode_signature({_, #'Dss-Parms'{}}, _SigAlg, Signature) ->
<<?Ebinary(<<"ssh-dss">>), ?Ebinary(Signature)>>;
-encode_signature({{#'ECPoint'{}, {namedCurve,OID}},_}, Signature) ->
+encode_signature({#'ECPoint'{}, {namedCurve,OID}}, _SigAlg, Signature) ->
CurveName = public_key:oid2ssh_curvename(OID),
<<?Ebinary(<<"ecdsa-sha2-",CurveName/binary>>), ?Ebinary(Signature)>>.
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index 6939094401..1e10f72956 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -268,7 +268,7 @@ default(server) ->
},
{shell, def} =>
- #{default => {shell, start, []},
+ #{default => ?DEFAULT_SHELL,
chk => fun({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A);
(V) -> check_function1(V) orelse check_function2(V)
end,
@@ -421,6 +421,12 @@ default(client) ->
class => user_options
},
+ {ecdsa_pass_phrase, def} =>
+ #{default => undefined,
+ chk => fun check_string/1,
+ class => user_options
+ },
+
{silently_accept_hosts, def} =>
#{default => false,
chk => fun check_silently_accept_hosts/1,
@@ -433,6 +439,12 @@ default(client) ->
class => user_options
},
+ {save_accepted_host, def} =>
+ #{default => true,
+ chk => fun erlang:is_boolean/1,
+ class => user_options
+ },
+
{pref_public_key_algs, def} =>
#{default => ssh_transport:default_algorithms(public_key),
chk => fun check_pref_public_key_algs/1,
diff --git a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl
index 8db051095c..77da240a66 100644
--- a/lib/ssh/src/ssh_subsystem_sup.erl
+++ b/lib/ssh/src/ssh_subsystem_sup.erl
@@ -74,18 +74,14 @@ ssh_connection_child_spec(Role, Address, Port, _Profile, Options) ->
#{id => id(Role, ssh_connection_sup, Address, Port),
start => {ssh_connection_sup, start_link, [Options]},
restart => temporary,
- shutdown => 5000,
- type => supervisor,
- modules => [ssh_connection_sup]
+ type => supervisor
}.
ssh_channel_child_spec(Role, Address, Port, _Profile, Options) ->
#{id => id(Role, ssh_channel_sup, Address, Port),
start => {ssh_channel_sup, start_link, [Options]},
restart => temporary,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_channel_sup]
+ type => supervisor
}.
id(Role, Sup, Address, Port) ->
diff --git a/lib/ssh/src/ssh_sup.erl b/lib/ssh/src/ssh_sup.erl
index eaec7a54e4..8183016ba5 100644
--- a/lib/ssh/src/ssh_sup.erl
+++ b/lib/ssh/src/ssh_sup.erl
@@ -36,15 +36,14 @@ init(_) ->
intensity => 10,
period => 3600
},
- ChildSpecs = [#{id => Module,
- start => {Module, start_link, []},
- restart => permanent,
- shutdown => 4000, %brutal_kill,
- type => supervisor,
- modules => [Module]
+ ChildSpecs = [#{id => sshd_sup,
+ start => {sshd_sup, start_link, []},
+ type => supervisor
+ },
+ #{id => sshc_sup,
+ start => {sshc_sup, start_link, []},
+ type => supervisor
}
- || Module <- [sshd_sup,
- sshc_sup]
],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index e70abf59c2..17f990c5d8 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -63,9 +63,7 @@ init([Address, Port, Profile, Options]) ->
[#{id => id(ssh_acceptor_sup, Address, Port, Profile),
start => {ssh_acceptor_sup, start_link, [Address, Port, Profile, Options]},
restart => transient,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_acceptor_sup]
+ type => supervisor
}];
_ ->
[]
@@ -124,9 +122,8 @@ start_subsystem(SystemSup, Role, Address, Port, Profile, Options) ->
#{id => make_ref(),
start => {ssh_subsystem_sup, start_link, [Role, Address, Port, Profile, Options]},
restart => temporary,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_subsystem_sup]},
+ type => supervisor
+ },
supervisor:start_child(SystemSup, SubsystemSpec).
stop_subsystem(SystemSup, SubSys) ->
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 6d158537d4..975053d301 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -51,10 +51,10 @@
extract_public_key/1,
ssh_packet/2, pack/2,
valid_key_sha_alg/2,
- sha/1, sign/3, verify/4]).
+ sha/1, sign/3, verify/5]).
%%% For test suites
--export([pack/3]).
+-export([pack/3, adjust_algs_for_peer_version/2]).
-export([decompress/2, decrypt_blocks/3, is_valid_mac/3 ]). % FIXME: remove
-define(Estring(X), ?STRING((if is_binary(X) -> X;
@@ -426,7 +426,7 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E},
K = compute_key(dh, E, Private, [P,G]),
MyPrivHostKey = get_host_key(Ssh0, SignAlg),
MyPubHostKey = extract_public_key(MyPrivHostKey),
- H = kex_hash(Ssh0, MyPubHostKey, SignAlg, sha(Kex), {E,Public,K}),
+ H = kex_hash(Ssh0, MyPubHostKey, sha(Kex), {E,Public,K}),
H_SIG = sign(H, sha(SignAlg), MyPrivHostKey),
{SshPacket, Ssh1} =
ssh_packet(#ssh_msg_kexdh_reply{public_host_key = {MyPubHostKey,SignAlg},
@@ -451,13 +451,12 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = PeerPubHostKey,
f = F,
h_sig = H_SIG},
#ssh{keyex_key = {{Private, Public}, {G, P}},
- algorithms = #alg{kex=Kex,
- hkey=SignAlg}} = Ssh0) ->
+ algorithms = #alg{kex=Kex}} = Ssh0) ->
%% client
if
1=<F, F=<(P-1)->
K = compute_key(dh, F, Private, [P,G]),
- H = kex_hash(Ssh0, PeerPubHostKey, SignAlg, sha(Kex), {Public,F,K}),
+ H = kex_hash(Ssh0, PeerPubHostKey, sha(Kex), {Public,F,K}),
case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of
ok ->
{SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
@@ -590,7 +589,7 @@ handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E},
1<K, K<(P-1) ->
MyPrivHostKey = get_host_key(Ssh0, SignAlg),
MyPubHostKey = extract_public_key(MyPrivHostKey),
- H = kex_hash(Ssh0, MyPubHostKey, SignAlg, sha(Kex), {Min,NBits,Max,P,G,E,Public,K}),
+ H = kex_hash(Ssh0, MyPubHostKey, sha(Kex), {Min,NBits,Max,P,G,E,Public,K}),
H_SIG = sign(H, sha(SignAlg), MyPrivHostKey),
{SshPacket, Ssh} =
ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = {MyPubHostKey,SignAlg},
@@ -620,8 +619,7 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostK
h_sig = H_SIG},
#ssh{keyex_key = {{Private, Public}, {G, P}},
keyex_info = {Min, Max, NBits},
- algorithms = #alg{kex=Kex,
- hkey=SignAlg}} =
+ algorithms = #alg{kex=Kex}} =
Ssh0) ->
%% client
if
@@ -629,7 +627,7 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostK
K = compute_key(dh, F, Private, [P,G]),
if
1<K, K<(P-1) ->
- H = kex_hash(Ssh0, PeerPubHostKey, SignAlg, sha(Kex), {Min,NBits,Max,P,G,Public,F,K}),
+ H = kex_hash(Ssh0, PeerPubHostKey, sha(Kex), {Min,NBits,Max,P,G,Public,F,K}),
case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of
ok ->
{SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
@@ -676,7 +674,7 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic},
K ->
MyPrivHostKey = get_host_key(Ssh0, SignAlg),
MyPubHostKey = extract_public_key(MyPrivHostKey),
- H = kex_hash(Ssh0, MyPubHostKey, SignAlg, sha(Curve), {PeerPublic, MyPublic, K}),
+ H = kex_hash(Ssh0, MyPubHostKey, sha(Curve), {PeerPublic, MyPublic, K}),
H_SIG = sign(H, sha(SignAlg), MyPrivHostKey),
{SshPacket, Ssh1} =
ssh_packet(#ssh_msg_kex_ecdh_reply{public_host_key = {MyPubHostKey,SignAlg},
@@ -699,15 +697,15 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic},
handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey,
q_s = PeerPublic,
h_sig = H_SIG},
- #ssh{keyex_key = {{MyPublic,MyPrivate}, Curve},
- algorithms = #alg{hkey=SignAlg}} = Ssh0
+ #ssh{keyex_key = {{MyPublic,MyPrivate}, Curve}
+ } = Ssh0
) ->
%% at client
try
compute_key(ecdh, PeerPublic, MyPrivate, Curve)
of
K ->
- H = kex_hash(Ssh0, PeerPubHostKey, SignAlg, sha(Curve), {MyPublic,PeerPublic,K}),
+ H = kex_hash(Ssh0, PeerPubHostKey, sha(Curve), {MyPublic,PeerPublic,K}),
case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of
ok ->
{SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
@@ -797,8 +795,14 @@ get_host_key(SSH, SignAlg) ->
#ssh{key_cb = {KeyCb,KeyCbOpts}, opts = Opts} = SSH,
UserOpts = ?GET_OPT(user_options, Opts),
case KeyCb:host_key(SignAlg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
- {ok, PrivHostKey} -> PrivHostKey;
- Result -> exit({error, {Result, unsupported_key_type}})
+ {ok, PrivHostKey} ->
+ %% Check the key - the KeyCb may be a buggy plugin
+ case valid_key_sha_alg(PrivHostKey, SignAlg) of
+ true -> PrivHostKey;
+ false -> exit({error, bad_hostkey})
+ end;
+ Result ->
+ exit({error, {Result, unsupported_key_type}})
end.
extract_public_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}) ->
@@ -807,13 +811,21 @@ extract_public_key(#'DSAPrivateKey'{y = Y, p = P, q = Q, g = G}) ->
{Y, #'Dss-Parms'{p=P, q=Q, g=G}};
extract_public_key(#'ECPrivateKey'{parameters = {namedCurve,OID},
publicKey = Q}) ->
- {#'ECPoint'{point=Q}, {namedCurve,OID}}.
+ {#'ECPoint'{point=Q}, {namedCurve,OID}};
+extract_public_key(#{engine:=_, key_id:=_, algorithm:=Alg} = M) ->
+ case {Alg, crypto:privkey_to_pubkey(Alg, M)} of
+ {rsa, [E,N]} ->
+ #'RSAPublicKey'{modulus = N, publicExponent = E};
+ {dss, [P,Q,G,Y]} ->
+ {Y, #'Dss-Parms'{p=P, q=Q, g=G}}
+ end.
+
verify_host_key(#ssh{algorithms=Alg}=SSH, PublicKey, Digest, {AlgStr,Signature}) ->
case atom_to_list(Alg#alg.hkey) of
AlgStr ->
- case verify(Digest, sha(Alg#alg.hkey), Signature, PublicKey) of
+ case verify(Digest, sha(Alg#alg.hkey), Signature, PublicKey, SSH) of
false ->
{error, bad_signature};
true ->
@@ -824,6 +836,7 @@ verify_host_key(#ssh{algorithms=Alg}=SSH, PublicKey, Digest, {AlgStr,Signature})
end.
+%%% -> boolean() | {error,_}
accepted_host(Ssh, PeerName, Public, Opts) ->
case ?GET_OPT(silently_accept_hosts, Opts) of
@@ -845,11 +858,16 @@ accepted_host(Ssh, PeerName, Public, Opts) ->
%% Call-back alternatives: A user provided fun is called for the decision:
F when is_function(F,2) ->
- true == (catch F(PeerName, public_key:ssh_hostkey_fingerprint(Public)));
+ case catch F(PeerName, public_key:ssh_hostkey_fingerprint(Public)) of
+ true -> true;
+ _ -> {error, fingerprint_check_failed}
+ end;
{DigestAlg,F} when is_function(F,2) ->
- true == (catch F(PeerName, public_key:ssh_hostkey_fingerprint(DigestAlg,Public)))
-
+ case catch F(PeerName, public_key:ssh_hostkey_fingerprint(DigestAlg,Public)) of
+ true -> true;
+ _ -> {error, {fingerprint_check_failed,DigestAlg}}
+ end
end.
@@ -867,18 +885,30 @@ fmt_hostkey(X) -> X.
known_host_key(#ssh{opts = Opts, key_cb = {KeyCb,KeyCbOpts}, peer = {PeerName,_}} = Ssh,
Public, Alg) ->
UserOpts = ?GET_OPT(user_options, Opts),
- case KeyCb:is_host_key(Public, PeerName, Alg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
- true ->
+ case is_host_key(KeyCb, Public, PeerName, Alg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
+ {_,true} ->
ok;
- false ->
+ {_,false} ->
+ DoAdd = ?GET_OPT(save_accepted_host, Opts),
case accepted_host(Ssh, PeerName, Public, Opts) of
- true ->
- KeyCb:add_host_key(PeerName, Public, [{key_cb_private,KeyCbOpts}|UserOpts]);
+ true when DoAdd == true ->
+ {_,R} = add_host_key(KeyCb, PeerName, Public, [{key_cb_private,KeyCbOpts}|UserOpts]),
+ R;
+ true when DoAdd == false ->
+ ok;
false ->
- {error, rejected}
+ {error, rejected_by_user};
+ {error,E} ->
+ {error,E}
end
end.
+is_host_key(KeyCb, Public, PeerName, Alg, Data) ->
+ {KeyCb, KeyCb:is_host_key(Public, PeerName, Alg, Data)}.
+
+add_host_key(KeyCb, PeerName, Public, Data) ->
+ {KeyCb, KeyCb:add_host_key(PeerName, Public, Data)}.
+
%% Each of the algorithm strings MUST be a comma-separated list of
%% algorithm names (see ''Algorithm Naming'' in [SSH-ARCH]). Each
@@ -1242,10 +1272,12 @@ payload(<<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>) ->
<<Payload:PayloadLen/binary, _/binary>> = PayloadAndPadding,
Payload.
+sign(SigData, HashAlg, #{algorithm:=dss} = Key) ->
+ mk_dss_sig(crypto:sign(dss, HashAlg, SigData, Key));
+sign(SigData, HashAlg, #{algorithm:=SigAlg} = Key) ->
+ crypto:sign(SigAlg, HashAlg, SigData, Key);
sign(SigData, HashAlg, #'DSAPrivateKey'{} = Key) ->
- DerSignature = public_key:sign(SigData, HashAlg, Key),
- #'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature),
- <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>;
+ mk_dss_sig(public_key:sign(SigData, HashAlg, Key));
sign(SigData, HashAlg, Key = #'ECPrivateKey'{}) ->
DerEncodedSign = public_key:sign(SigData, HashAlg, Key),
#'ECDSA-Sig-Value'{r=R, s=S} = public_key:der_decode('ECDSA-Sig-Value', DerEncodedSign),
@@ -1253,7 +1285,13 @@ sign(SigData, HashAlg, Key = #'ECPrivateKey'{}) ->
sign(SigData, HashAlg, Key) ->
public_key:sign(SigData, HashAlg, Key).
-verify(PlainText, HashAlg, Sig, {_, #'Dss-Parms'{}} = Key) ->
+
+mk_dss_sig(DerSignature) ->
+ #'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature),
+ <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>.
+
+
+verify(PlainText, HashAlg, Sig, {_, #'Dss-Parms'{}} = Key, _) ->
case Sig of
<<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> ->
Signature = public_key:der_encode('Dss-Sig-Value', #'Dss-Sig-Value'{r = R, s = S}),
@@ -1261,7 +1299,7 @@ verify(PlainText, HashAlg, Sig, {_, #'Dss-Parms'{}} = Key) ->
_ ->
false
end;
-verify(PlainText, HashAlg, Sig, {#'ECPoint'{},_} = Key) ->
+verify(PlainText, HashAlg, Sig, {#'ECPoint'{},_} = Key, _) ->
case Sig of
<<?UINT32(Rlen),R:Rlen/big-signed-integer-unit:8,
?UINT32(Slen),S:Slen/big-signed-integer-unit:8>> ->
@@ -1271,7 +1309,15 @@ verify(PlainText, HashAlg, Sig, {#'ECPoint'{},_} = Key) ->
_ ->
false
end;
-verify(PlainText, HashAlg, Sig, Key) ->
+
+verify(PlainText, HashAlg, Sig, #'RSAPublicKey'{}=Key, #ssh{role = server,
+ c_version = "SSH-2.0-OpenSSH_7."++_})
+ when HashAlg == sha256; HashAlg == sha512 ->
+ %% Public key signing bug in in OpenSSH >= 7.2
+ public_key:verify(PlainText, HashAlg, Sig, Key)
+ orelse public_key:verify(PlainText, sha, Sig, Key);
+
+verify(PlainText, HashAlg, Sig, Key, _) ->
public_key:verify(PlainText, HashAlg, Sig, Key).
@@ -1779,11 +1825,11 @@ hash(K, H, Ki, N, HashAlg) ->
hash(K, H, <<Ki/binary, Kj/binary>>, N-128, HashAlg).
%%%----------------------------------------------------------------
-kex_hash(SSH, Key, SignAlg, HashAlg, Args) ->
- crypto:hash(HashAlg, kex_plaintext(SSH,Key,SignAlg,Args)).
+kex_hash(SSH, Key, HashAlg, Args) ->
+ crypto:hash(HashAlg, kex_plaintext(SSH,Key,Args)).
-kex_plaintext(SSH, Key, SignAlg, Args) ->
- EncodedKey = public_key:ssh_encode({Key,SignAlg}, ssh2_pubkey),
+kex_plaintext(SSH, Key, Args) ->
+ EncodedKey = public_key:ssh_encode(Key, ssh2_pubkey),
<<?Estring(SSH#ssh.c_version), ?Estring(SSH#ssh.s_version),
?Ebinary(SSH#ssh.c_keyinit), ?Ebinary(SSH#ssh.s_keyinit),
?Ebinary(EncodedKey),
@@ -1804,6 +1850,8 @@ kex_alg_dependent({Min, NBits, Max, Prime, Gen, E, F, K}) ->
%%%----------------------------------------------------------------
+valid_key_sha_alg(#{engine:=_, key_id:=_}, _Alg) -> true; % Engine key
+
valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-512') -> true;
valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-384') -> true;
valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-256') -> true;
@@ -1817,11 +1865,14 @@ valid_key_sha_alg(#'RSAPrivateKey'{}, 'ssh-rsa' ) -> true;
valid_key_sha_alg({_, #'Dss-Parms'{}}, 'ssh-dss') -> true;
valid_key_sha_alg(#'DSAPrivateKey'{}, 'ssh-dss') -> true;
-valid_key_sha_alg({#'ECPoint'{},{namedCurve,OID}}, Alg) -> sha(OID) == sha(Alg);
-valid_key_sha_alg(#'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) -> sha(OID) == sha(Alg);
+valid_key_sha_alg({#'ECPoint'{},{namedCurve,OID}}, Alg) -> valid_key_sha_alg_ec(OID, Alg);
+valid_key_sha_alg(#'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) -> valid_key_sha_alg_ec(OID, Alg);
valid_key_sha_alg(_, _) -> false.
-
+valid_key_sha_alg_ec(OID, Alg) ->
+ Curve = public_key:oid2ssh_curvename(OID),
+ Alg == list_to_atom("ecdsa-sha2-" ++ binary_to_list(Curve)).
+
public_algo(#'RSAPublicKey'{}) -> 'ssh-rsa'; % FIXME: Not right with draft-curdle-rsa-sha2
public_algo({_, #'Dss-Parms'{}}) -> 'ssh-dss';
diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl
index 133b2c6450..fd4d8a3c07 100644
--- a/lib/ssh/src/sshc_sup.erl
+++ b/lib/ssh/src/sshc_sup.erl
@@ -60,10 +60,7 @@ init(_) ->
},
ChildSpecs = [#{id => undefined, % As simple_one_for_one is used.
start => {ssh_connection_handler, start_link, []},
- restart => temporary,
- shutdown => 4000,
- type => worker,
- modules => [ssh_connection_handler]
+ restart => temporary % because there is no way to restart a crashed connection
}
],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/sshd_sup.erl b/lib/ssh/src/sshd_sup.erl
index c23e65d955..779a861a54 100644
--- a/lib/ssh/src/sshd_sup.erl
+++ b/lib/ssh/src/sshd_sup.erl
@@ -90,10 +90,8 @@ init(_) ->
child_spec(Address, Port, Profile, Options) ->
#{id => id(Address, Port, Profile),
start => {ssh_system_sup, start_link, [Address, Port, Profile, Options]},
- restart => temporary,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_system_sup]
+ restart => temporary,
+ type => supervisor
}.
id(Address, Port, Profile) ->
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 32e76cf077..21359a0386 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -37,8 +37,11 @@ MODULES= \
ssh_renegotiate_SUITE \
ssh_basic_SUITE \
ssh_bench_SUITE \
+ ssh_compat_SUITE \
ssh_connection_SUITE \
+ ssh_engine_SUITE \
ssh_protocol_SUITE \
+ ssh_property_test_SUITE \
ssh_sftp_SUITE \
ssh_sftpd_SUITE \
ssh_sftpd_erlclient_SUITE \
@@ -48,6 +51,7 @@ MODULES= \
ssh_test_lib \
ssh_key_cb \
ssh_key_cb_options \
+ ssh_key_cb_engine_keys \
ssh_trpt_test_lib \
ssh_echo_server \
ssh_bench_dev_null \
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
index c07140dc43..19e2754eba 100644
--- a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
+++ b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
@@ -57,9 +57,9 @@
%%% Properties:
-prop_seq(_Config) ->
+prop_seq(Config) ->
{ok,Pid} = ssh_eqc_event_handler:add_report_handler(),
- {_, _, Port} = init_daemon(),
+ {_, _, Port} = init_daemon(Config),
numtests(1000,
?FORALL(Delay, choose(0,100),%% Micro seconds
try
@@ -86,7 +86,8 @@ any_relevant_error_report(Pid) ->
end, Reports).
%%%================================================================
-init_daemon() ->
+init_daemon(Config) ->
ok = begin ssh:stop(), ssh:start() end,
- ssh_test_lib:daemon([]).
+ DataDir = proplists:get_value(data_dir, Config),
+ ssh_test_lib:daemon([{system_dir,DataDir}]).
diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl
index 98964a2c8a..de6e448ebd 100644
--- a/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ b/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -29,15 +29,13 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--define(TIMEOUT, 35000).
-
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap,{seconds,40}}].
+ {timetrap,{seconds,round(1.5*?TIMEOUT/1000)}}].
all() ->
%% [{group,kex},{group,cipher}... etc
@@ -90,7 +88,7 @@ init_per_suite(Config) ->
" -- Max num algorithms: ~p~n"
,[os:getenv("HOME"),
init:get_argument(home),
- os:cmd("ssh -V"),
+ ssh_test_lib:installed_ssh_version("TIMEOUT"),
ssh:default_algorithms(),
crypto:info_lib(),
ssh_test_lib:default_algorithms(sshc),
@@ -318,10 +316,10 @@ sshc_simple_exec_os_cmd(Config) ->
ok;
false ->
ct:log("Bad result: ~p~nExpected: ~p~nMangled result: ~p", [RawResult,Expect,Lines]),
- {fail, "Bad result"}
+ {fail, "Bad result (see log in testcase)"}
end
after ?TIMEOUT ->
- ct:fail("Did not receive answer")
+ ct:fail("Did not receive answer (timeout)")
end.
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 62e2a585e4..365f25fabb 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -46,6 +46,7 @@
exec_key_differs2/1,
exec_key_differs3/1,
exec_key_differs_fail/1,
+ fail_daemon_start/1,
idle_time_client/1,
idle_time_server/1,
inet6_option/1,
@@ -59,7 +60,7 @@
login_bad_pwd_no_retry5/1,
misc_ssh_options/1,
openssh_zlib_basic_test/1,
- packet_size_zero/1,
+ packet_size/1,
pass_phrase/1,
peername_sockname/1,
send/1,
@@ -99,14 +100,18 @@ all() ->
{group, ecdsa_sha2_nistp521_key},
{group, dsa_pass_key},
{group, rsa_pass_key},
+ {group, ecdsa_sha2_nistp256_pass_key},
+ {group, ecdsa_sha2_nistp384_pass_key},
+ {group, ecdsa_sha2_nistp521_pass_key},
{group, host_user_key_differs},
{group, key_cb},
{group, internal_error},
+ {group, rsa_host_key_is_actualy_ecdsa},
daemon_already_started,
double_close,
daemon_opt_fd,
multi_daemon_opt_fd,
- packet_size_zero,
+ packet_size,
ssh_info_print,
{group, login_bad_pwd_no_retry},
shell_exit_status
@@ -118,12 +123,16 @@ groups() ->
{ecdsa_sha2_nistp256_key, [], basic_tests()},
{ecdsa_sha2_nistp384_key, [], basic_tests()},
{ecdsa_sha2_nistp521_key, [], basic_tests()},
+ {rsa_host_key_is_actualy_ecdsa, [], [fail_daemon_start]},
{host_user_key_differs, [], [exec_key_differs1,
exec_key_differs2,
exec_key_differs3,
exec_key_differs_fail]},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
+ {ecdsa_sha2_nistp256_pass_key, [], [pass_phrase]},
+ {ecdsa_sha2_nistp384_pass_key, [], [pass_phrase]},
+ {ecdsa_sha2_nistp521_pass_key, [], [pass_phrase]},
{key_cb, [], [key_callback, key_callback_options]},
{internal_error, [], [internal_error]},
{login_bad_pwd_no_retry, [], [login_bad_pwd_no_retry1,
@@ -174,6 +183,31 @@ init_per_group(rsa_key, Config) ->
false ->
{skip, unsupported_pub_key}
end;
+init_per_group(rsa_host_key_is_actualy_ecdsa, Config) ->
+ case
+ lists:member('ssh-rsa',
+ ssh_transport:default_algorithms(public_key)) and
+ lists:member('ecdsa-sha2-nistp256',
+ ssh_transport:default_algorithms(public_key))
+ of
+ true ->
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ ssh_test_lib:setup_ecdsa("256", DataDir, PrivDir),
+ %% The following sets up bad rsa keys:
+ begin
+ UserDir = PrivDir,
+ System = filename:join(UserDir, "system"),
+ file:copy(filename:join(DataDir, "id_rsa"), filename:join(UserDir, "id_rsa")),
+ file:rename(filename:join(System, "ssh_host_ecdsa_key"), filename:join(System, "ssh_host_rsa_key")),
+ file:rename(filename:join(System, "ssh_host_ecdsa_key.pub"), filename:join(System, "ssh_host_rsa_key.pub")),
+ ssh_test_lib:setup_rsa_known_host(DataDir, UserDir),
+ ssh_test_lib:setup_rsa_auth_keys(DataDir, UserDir)
+ end,
+ Config;
+ false ->
+ {skip, unsupported_pub_key}
+ end;
init_per_group(ecdsa_sha2_nistp256_key, Config) ->
case lists:member('ecdsa-sha2-nistp256',
ssh_transport:default_algorithms(public_key)) of
@@ -229,6 +263,45 @@ init_per_group(dsa_pass_key, Config) ->
false ->
{skip, unsupported_pub_key}
end;
+init_per_group(ecdsa_sha2_nistp256_pass_key, Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ case lists:member('ecdsa-sha2-nistp256',
+ ssh_transport:default_algorithms(public_key))
+ andalso
+ ssh_test_lib:setup_ecdsa_pass_phrase("256", DataDir, PrivDir, "Password")
+ of
+ true ->
+ [{pass_phrase, {ecdsa_pass_phrase, "Password"}}| Config];
+ false ->
+ {skip, unsupported_pub_key}
+ end;
+init_per_group(ecdsa_sha2_nistp384_pass_key, Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ case lists:member('ecdsa-sha2-nistp384',
+ ssh_transport:default_algorithms(public_key))
+ andalso
+ ssh_test_lib:setup_ecdsa_pass_phrase("384", DataDir, PrivDir, "Password")
+ of
+ true ->
+ [{pass_phrase, {ecdsa_pass_phrase, "Password"}}| Config];
+ false ->
+ {skip, unsupported_pub_key}
+ end;
+init_per_group(ecdsa_sha2_nistp521_pass_key, Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ case lists:member('ecdsa-sha2-nistp521',
+ ssh_transport:default_algorithms(public_key))
+ andalso
+ ssh_test_lib:setup_ecdsa_pass_phrase("521", DataDir, PrivDir, "Password")
+ of
+ true ->
+ [{pass_phrase, {ecdsa_pass_phrase, "Password"}}| Config];
+ false ->
+ {skip, unsupported_pub_key}
+ end;
init_per_group(host_user_key_differs, Config) ->
Data = proplists:get_value(data_dir, Config),
Sys = filename:join(proplists:get_value(priv_dir, Config), system_rsa),
@@ -241,7 +314,7 @@ init_per_group(host_user_key_differs, Config) ->
file:copy(filename:join(Data, "ssh_host_rsa_key.pub"), filename:join(Sys, "ssh_host_rsa_key.pub")),
file:copy(filename:join(Data, "id_ecdsa256"), filename:join(Usr, "id_ecdsa")),
file:copy(filename:join(Data, "id_ecdsa256.pub"), filename:join(Usr, "id_ecdsa.pub")),
- ssh_test_lib:setup_ecdsa_auth_keys("256", Usr, SysUsr),
+ ssh_test_lib:setup_ecdsa_auth_keys("256", Data, SysUsr),
ssh_test_lib:setup_rsa_known_host(Sys, Usr),
Config;
init_per_group(key_cb, Config) ->
@@ -259,7 +332,8 @@ init_per_group(internal_error, Config) ->
DataDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
ssh_test_lib:setup_dsa(DataDir, PrivDir),
- file:delete(filename:join(PrivDir, "system/ssh_host_dsa_key")),
+ %% In the test case the key will be deleted after the daemon start:
+ %% ... file:delete(filename:join(PrivDir, "system/ssh_host_dsa_key")),
Config;
init_per_group(dir_options, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
@@ -306,6 +380,7 @@ init_per_group(dir_options, Config) ->
init_per_group(_, Config) ->
Config.
+
end_per_group(dsa_key, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
ssh_test_lib:clean_dsa(PrivDir),
@@ -689,11 +764,11 @@ cli(Config) when is_list(Config) ->
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
ssh_connection:shell(ConnectionRef, ChannelId),
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
receive
{ssh_cm, ConnectionRef,
{data,0,0, <<"\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n">>}} ->
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
end,
@@ -822,12 +897,17 @@ key_callback_options(Config) when is_list(Config) ->
%%% Test that client does not hang if disconnects due to internal error
internal_error(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
+ PrivDir = proplists:get_value(priv_dir, Config),
UserDir = proplists:get_value(priv_dir, Config),
+ SystemDir = filename:join(PrivDir, system),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
{failfun, fun ssh_test_lib:failfun/2}]),
+
+ %% Now provoke an error in the following connect:
+ file:delete(filename:join(PrivDir, "system/ssh_host_dsa_key")),
+
{error, Error} =
ssh:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
@@ -856,6 +936,17 @@ send(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+%%%
+fail_daemon_start(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
+ UserDir = proplists:get_value(priv_dir, Config),
+
+ {error,_} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]).
+
+%%--------------------------------------------------------------------
%%% Test ssh:connection_info([peername, sockname])
peername_sockname(Config) when is_list(Config) ->
process_flag(trap_exit, true),
@@ -1013,7 +1104,7 @@ multi_daemon_opt_fd(Config) ->
end || {S,Pid,C} <- Tests].
%%--------------------------------------------------------------------
-packet_size_zero(Config) ->
+packet_size(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
@@ -1028,21 +1119,31 @@ packet_size_zero(Config) ->
{user_interaction, false},
{user, "vego"},
{password, "morot"}]),
-
- {ok,Chan} = ssh_connection:session_channel(Conn, 1000, _MaxPacketSize=0, 60000),
- ok = ssh_connection:shell(Conn, Chan),
+ lists:foreach(
+ fun(MaxPacketSize) ->
+ ct:log("Try max_packet_size=~p",[MaxPacketSize]),
+ {ok,Ch} = ssh_connection:session_channel(Conn, 1000, MaxPacketSize, 60000),
+ ok = ssh_connection:shell(Conn, Ch),
+ rec(Server, Conn, Ch, MaxPacketSize)
+ end, [0, 1, 10, 25]),
ssh:close(Conn),
- ssh:stop_daemon(Server),
+ ssh:stop_daemon(Server).
+rec(Server, Conn, Ch, MaxSz) ->
receive
- {ssh_cm,Conn,{data,Chan,_Type,_Msg1}} = M ->
- ct:log("Got ~p",[M]),
- ct:fail(doesnt_obey_max_packet_size_0)
- after 5000 ->
- ok
- end.
-
+ {ssh_cm,Conn,{data,Ch,_,M}} when size(M) =< MaxSz ->
+ ct:log("~p: ~p",[MaxSz,M]),
+ rec(Server, Conn, Ch, MaxSz);
+ {ssh_cm,Conn,{data,Ch,_,_}} = M ->
+ ct:log("Max pkt size=~p. Got ~p",[MaxSz,M]),
+ ssh:close(Conn),
+ ssh:stop_daemon(Server),
+ ct:fail("Does not obey max_packet_size=~p",[MaxSz])
+ after
+ 2000 -> ok
+ end.
+
%%--------------------------------------------------------------------
shell_no_unicode(Config) ->
new_do_shell(proplists:get_value(io,Config),
@@ -1254,14 +1355,11 @@ shell_exit_status(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%% Due to timing the error message may or may not be delivered to
%% the "tcp-application" before the socket closed message is recived
-check_error("Invalid state") ->
- ok;
-check_error("Connection closed") ->
- ok;
-check_error("Selection of key exchange algorithm failed"++_) ->
- ok;
-check_error(Error) ->
- ct:fail(Error).
+check_error("Invalid state") -> ok;
+check_error("Connection closed") -> ok;
+check_error("Selection of key exchange algorithm failed"++_) -> ok;
+check_error("No host key available") -> ok;
+check_error(Error) -> ct:fail(Error).
basic_test(Config) ->
ClientOpts = proplists:get_value(client_opts, Config),
@@ -1403,7 +1501,7 @@ new_do_shell(IO, N, Ops=[{Order,Arg}|More]) ->
ct:fail("*** Expected ~p, but got ~p",[string:strip(ExpStr),RecStr])
end
after 30000 ->
- ct:log("Meassage queue of ~p:~n~p",
+ ct:log("Message queue of ~p:~n~p",
[self(), erlang:process_info(self(), messages)]),
case Order of
expect -> ct:fail("timeout, expected ~p",[string:strip(Arg)]);
diff --git a/lib/ssh/test/ssh_bench_SUITE.erl b/lib/ssh/test/ssh_bench_SUITE.erl
index 2c0cd8fc8e..b6c6147646 100644
--- a/lib/ssh/test/ssh_bench_SUITE.erl
+++ b/lib/ssh/test/ssh_bench_SUITE.erl
@@ -57,12 +57,19 @@ init_per_suite(Config) ->
ok ->
DataSize = 1000000,
SystemDir = proplists:get_value(data_dir, Config),
- Algs = insert_none(ssh:default_algorithms()),
+ Algs = ssh:default_algorithms(),
{_ServerPid, _Host, Port} =
ssh_test_lib:daemon([{system_dir, SystemDir},
{user_passwords, [{?UID,?PWD}]},
{failfun, fun ssh_test_lib:failfun/2},
{preferred_algorithms, Algs},
+ {modify_algorithms,[{prepend,[{cipher,[none]},
+ {mac,[none]}
+ ]},
+ {rm, [{cipher,['[email protected]',
+ ]}
+ ]},
{max_random_length_padding, 0},
{subsystems, [{"/dev/null", {ssh_bench_dev_null,[DataSize]}}]}
]),
@@ -175,11 +182,34 @@ gen_data(DataSz) ->
%% {suite, ?MODULE},
%% {name, mk_name(["Transfer 1M bytes ",Cipher,"/",Mac," [µs]"])}]);
connect_measure(Port, Cipher, Mac, Data, Options) ->
+ AES_GCM = {cipher,['[email protected]',
+
+ AlgOpt = case {Cipher,Mac} of
+ {none,none} ->
+ [{modify_algorithms,[{prepend, [{cipher,[Cipher]},
+ {mac,[Mac]}]},
+ {rm,[AES_GCM]}
+ ]}];
+ {none,_} ->
+ [{modify_algorithms,[{prepend, [{cipher,[Cipher]}]},
+ {rm,[AES_GCM]}
+ ]},
+ {preferred_algorithms, [{mac,[Mac]}]}];
+ {_,none} ->
+ [{modify_algorithms,[{prepend, [{mac,[Mac]}]},
+ {rm,[AES_GCM]}
+ ]},
+ {preferred_algorithms, [{cipher,[Cipher]}]}];
+ _ ->
+ [{preferred_algorithms, [{cipher,[Cipher]},
+ {mac,[Mac]}]},
+ {modify_algorithms, [{rm,[AES_GCM]}]}
+ ]
+ end,
Times =
[begin
- {ok,C} = ssh:connect("localhost", Port, [{preferred_algorithms, [{cipher,[Cipher]},
- {mac,[Mac]}]}
- |Options]),
+ {ok,C} = ssh:connect("localhost", Port, AlgOpt ++ Options),
{ok,Ch} = ssh_connection:session_channel(C, 10000),
success = ssh_connection:subsystem(C, Ch, "/dev/null", 10000),
{Time,ok} = timer:tc(?MODULE, send_wait_acc, [C, Ch, Data]),
@@ -205,16 +235,6 @@ send_wait_acc(C, Ch, Data) ->
%%%
%%%----------------------------------------------------------------
-insert_none(L) ->
- lists:foldl(fun insert_none/2, [], L).
-
-insert_none({T,L}, Acc) when T==cipher ;
- T==mac ->
- [{T, [{T1,L1++[none]} || {T1,L1} <- L]} | Acc];
-insert_none(_, Acc) ->
- Acc.
-
-%%%----------------------------------------------------------------
mk_name(Name) -> [char(C) || C <- lists:concat(Name)].
char($-) -> $_;
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
new file mode 100644
index 0000000000..f7eda1dc08
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -0,0 +1,1399 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssh_compat_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssh/src/ssh_transport.hrl"). % #ssh_msg_kexinit{}
+-include_lib("kernel/include/inet.hrl"). % #hostent{}
+-include_lib("kernel/include/file.hrl"). % #file_info{}
+-include("ssh_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-define(USER,"sshtester").
+-define(PASSWD, "foobar").
+-define(BAD_PASSWD, "NOT-"?PASSWD).
+-define(DOCKER_PFX, "ssh_compat_suite-ssh").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [%%{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,40}}].
+
+all() ->
+%% [check_docker_present] ++
+ [{group,G} || G <- ssh_image_versions()].
+
+groups() ->
+ [{otp_client, [], [login_otp_is_client,
+ all_algorithms_sftp_exec_reneg_otp_is_client,
+ send_recv_big_with_renegotiate_otp_is_client
+ ]},
+ {otp_server, [], [login_otp_is_server,
+ all_algorithms_sftp_exec_reneg_otp_is_server
+ ]} |
+ [{G, [], [{group,otp_client}, {group,otp_server}]} || G <- ssh_image_versions()]
+ ].
+
+
+ssh_image_versions() ->
+ try
+ %% Find all useful containers in such a way that undefined command, too low
+ %% priviliges, no containers and containers found give meaningful result:
+ L0 = ["REPOSITORY"++_|_] = string:tokens(os:cmd("docker images"), "\r\n"),
+ [["REPOSITORY","TAG"|_]|L1] = [string:tokens(E, " ") || E<-L0],
+ [list_to_atom(V) || [?DOCKER_PFX,V|_] <- L1]
+ of
+ Vs ->
+ lists:sort(Vs)
+ catch
+ error:{badmatch,_} ->
+ []
+ end.
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ ?CHECK_CRYPTO(
+ case os:find_executable("docker") of
+ false ->
+ {skip, "No docker"};
+ _ ->
+ ssh:start(),
+ ct:log("Crypto info: ~p",[crypto:info_lib()]),
+ Config
+ end).
+
+end_per_suite(Config) ->
+ %% Remove all containers that are not running:
+%%% os:cmd("docker rm $(docker ps -aq -f status=exited)"),
+ %% Remove dangling images:
+%%% os:cmd("docker rmi $(docker images -f dangling=true -q)"),
+ catch ssh:stop(),
+ Config.
+
+
+init_per_group(otp_server, Config) ->
+ case proplists:get_value(common_remote_client_algs, Config) of
+ undefined ->
+ SSHver = proplists:get_value(ssh_version, Config, ""),
+ {skip,"No "++SSHver++ " client found in docker"};
+ _ ->
+ Config
+ end;
+
+init_per_group(otp_client, Config) ->
+ Config;
+
+init_per_group(G, Config0) ->
+ case lists:member(G, ssh_image_versions()) of
+ true ->
+ %% This group is for one of the images
+ Vssh = atom_to_list(G),
+ Cmnt = io_lib:format("+++ ~s +++",[Vssh]),
+ ct:comment("~s",[Cmnt]),
+ try start_docker(G) of
+ {ok,ID} ->
+ ct:log("==> ~p started",[G]),
+ %% Find the algorithms that both client and server supports:
+ {IP,Port} = ip_port([{id,ID}]),
+ ct:log("Try contact ~p:~p",[IP,Port]),
+ Config1 = [{id,ID},
+ {ssh_version,Vssh}
+ | Config0],
+ try common_algs(Config1, IP, Port) of
+ {ok, ServerHello, RemoteServerCommon, ClientHello, RemoteClientCommon} ->
+ case chk_hellos([ServerHello,ClientHello], Cmnt) of
+ Cmnt ->
+ ok;
+ NewCmnt ->
+ ct:comment("~s",[NewCmnt])
+ end,
+ AuthMethods =
+ %% This should be obtained by quering the peer, but that
+ %% is a bit hard. It is possible with ssh_protocol_SUITE
+ %% techniques, but it can wait.
+ case Vssh of
+ "dropbear" ++ _ ->
+ [password, publickey];
+ _ ->
+ [password, 'keyboard-interactive', publickey]
+ end,
+ [{common_remote_server_algs,RemoteServerCommon},
+ {common_remote_client_algs,RemoteClientCommon},
+ {common_authmethods,AuthMethods}
+ |Config1];
+ Other ->
+ ct:log("Error in init_per_group: ~p",[Other]),
+ stop_docker(ID),
+ {fail, "Can't contact docker sshd"}
+ catch
+ Class:Exc ->
+ ST = erlang:get_stacktrace(),
+ ct:log("common_algs: ~p:~p~n~p",[Class,Exc,ST]),
+ stop_docker(ID),
+ {fail, "Failed during setup"}
+ end
+ catch
+ cant_start_docker ->
+ {skip, "Can't start docker"};
+
+ C:E ->
+ ST = erlang:get_stacktrace(),
+ ct:log("No ~p~n~p:~p~n~p",[G,C,E,ST]),
+ {skip, "Can't start docker"}
+ end;
+
+ false ->
+ Config0
+ end.
+
+end_per_group(G, Config) ->
+ case lists:member(G, ssh_image_versions()) of
+ true ->
+ catch stop_docker(proplists:get_value(id,Config));
+ false ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+check_docker_present(_Config) ->
+ ct:log("This testcase is just to show in Monitor that we have a test host with docker installed",[]),
+ {fail, "Test is OK: just showing docker is available"}.
+
+%%--------------------------------------------------------------------
+login_otp_is_client(Config) ->
+ {IP,Port} = ip_port(Config),
+ PublicKeyAlgs = [A || {public_key,A} <- proplists:get_value(common_remote_server_algs, Config)],
+ CommonAuths =
+ [{AuthMethod,Alg} || AuthMethod <- proplists:get_value(common_authmethods, Config),
+ Alg <- case AuthMethod of
+ publickey ->
+ PublicKeyAlgs;
+ _ ->
+ [' ']
+ end
+ ],
+
+ chk_all_algos(?FUNCTION_NAME, CommonAuths, Config,
+ fun(AuthMethod,Alg) ->
+ {Opts,Dir} =
+ case AuthMethod of
+ publickey ->
+ {[], setup_remote_auth_keys_and_local_priv(Alg, Config)};
+ _ ->
+ {[{password,?PASSWD}], new_dir(Config)}
+ end,
+ ssh:connect(IP, Port, [{auth_methods, atom_to_list(AuthMethod)},
+ {user,?USER},
+ {user_dir, Dir},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ | Opts
+ ])
+ end).
+
+
+%%--------------------------------------------------------------------
+login_otp_is_server(Config) ->
+ PublicKeyAlgs = [A || {public_key,A} <- proplists:get_value(common_remote_client_algs, Config)],
+ CommonAuths =
+ [{AuthMethod,Alg} || AuthMethod <- proplists:get_value(common_authmethods, Config),
+ Alg <- case AuthMethod of
+ publickey ->
+ PublicKeyAlgs;
+ _ ->
+ [' ']
+ end
+ ],
+ SysDir = setup_local_hostdir(hd(PublicKeyAlgs), Config),
+ chk_all_algos(?FUNCTION_NAME, CommonAuths, Config,
+ fun(AuthMethod,Alg) ->
+ {Opts,UsrDir} =
+ case AuthMethod of
+ publickey ->
+ {[{user_passwords, [{?USER,?BAD_PASSWD}]}],
+ setup_remote_priv_and_local_auth_keys(Alg, Config)
+ };
+ _ ->
+ {[{user_passwords, [{?USER,?PASSWD}]}],
+ new_dir(Config)
+ }
+ end,
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods, atom_to_list(AuthMethod)},
+ {system_dir, SysDir},
+ {user_dir, UsrDir},
+ {failfun, fun ssh_test_lib:failfun/2}
+ | Opts
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+3]).\r\n'",
+ [<<"Answer=4">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R
+ end).
+
+%%--------------------------------------------------------------------
+all_algorithms_sftp_exec_reneg_otp_is_client(Config) ->
+ CommonAlgs = proplists:get_value(common_remote_server_algs, Config),
+ {IP,Port} = ip_port(Config),
+ chk_all_algos(?FUNCTION_NAME, CommonAlgs, Config,
+ fun(Tag, Alg) ->
+ ConnRes =
+ ssh:connect(IP, Port,
+ [{user,?USER},
+ {password,?PASSWD},
+ {auth_methods, "password"},
+ {user_dir, new_dir(Config)},
+ {preferred_algorithms, [{Tag,[Alg]}]},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]) ,
+ test_erl_client_reneg(ConnRes, % Seems that max 10 channels may be open in sshd
+ [{exec,1},
+ {sftp,5},
+ {no_subsyst,1},
+ {setenv, 1},
+ {sftp_async,1}
+ ])
+ end).
+
+%%--------------------------------------------------------------------
+all_algorithms_sftp_exec_reneg_otp_is_server(Config) ->
+ CommonAlgs = proplists:get_value(common_remote_client_algs, Config),
+ UserDir = setup_remote_priv_and_local_auth_keys('ssh-rsa', Config),
+ chk_all_algos(?FUNCTION_NAME, CommonAlgs, Config,
+ fun(Tag,Alg) ->
+ HostKeyAlg = case Tag of
+ public_key -> Alg;
+ _ -> 'ssh-rsa'
+ end,
+ SftpRootDir = new_dir(Config),
+ %% ct:log("Rootdir = ~p",[SftpRootDir]),
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{preferred_algorithms, [{Tag,[Alg]}]},
+ {system_dir, setup_local_hostdir(HostKeyAlg, Config)},
+ {user_dir, UserDir},
+ {user_passwords, [{?USER,?PASSWD}]},
+ {failfun, fun ssh_test_lib:failfun/2},
+ {subsystems,
+ [ssh_sftpd:subsystem_spec([{cwd,SftpRootDir},
+ {root,SftpRootDir}]),
+ {"echo_10",{ssh_echo_server,[10,[{dbg,true}]]}}
+ ]}
+ ]),
+ R = do([fun() ->
+ exec_from_docker(Config, Host, HostPort,
+ "hi_there.\r\n",
+ [<<"hi_there">>],
+ "")
+ end,
+ fun() ->
+ sftp_tests_erl_server(Config, Host, HostPort, SftpRootDir, UserDir)
+ end
+ ]),
+ ssh:stop_daemon(Server),
+ R
+ end).
+
+%%--------------------------------------------------------------------
+send_recv_big_with_renegotiate_otp_is_client(Config) ->
+ %% Connect to the remote openssh server:
+ {IP,Port} = ip_port(Config),
+ {ok,C} = ssh:connect(IP, Port, [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, setup_remote_auth_keys_and_local_priv('ssh-rsa', Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+
+ %% Open a channel and exec the Linux 'cat' command at the openssh side.
+ %% This 'cat' will read stdin and write to stdout until an eof is read from stdin.
+ {ok, Ch1} = ssh_connection:session_channel(C, infinity),
+ success = ssh_connection:exec(C, Ch1, "cat", infinity),
+
+ %% Build big binary
+ HalfSizeBytes = 100*1000*1000,
+ Data = << <<X:32>> || X <- lists:seq(1, HalfSizeBytes div 4)>>,
+
+ %% Send the data. Must spawn a process to avoid deadlock. The client will block
+ %% until all is sent through the send window. But the server will stop receiveing
+ %% when the servers send-window towards the client is full.
+ %% Since the client can't receive before the server has received all but 655k from the client
+ %% ssh_connection:send/4 is blocking...
+ spawn_link(
+ fun() ->
+ ct:comment("Sending ~p Mbytes with renegotiation in the middle",[2*byte_size(Data)/1000000]),
+ %% ct:log("sending first ~p bytes",[byte_size(Data)]),
+ ok = ssh_connection:send(C, Ch1, Data, 10000),
+ %% ct:log("Init renegotiation test",[]),
+ Kex1 = renegotiate_test(init, C),
+ %% ct:log("sending next ~p bytes",[byte_size(Data)]),
+ ok = ssh_connection:send(C, Ch1, Data, 10000),
+ %% ct:log("Finnish renegotiation test",[]),
+ renegotiate_test(Kex1, C),
+ %% ct:log("sending eof",[]),
+ ok = ssh_connection:send_eof(C, Ch1)
+ %%, ct:log("READY, sent ~p bytes",[2*byte_size(Data)])
+ end),
+
+ {eof,ReceivedData} =
+ loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ %%ct:log("Get more ~p",[ ExpectedSize-byte_size(Acc) ]),
+ receive
+ {ssh_cm, C, {eof,Ch}} when Ch==Ch1 ->
+ %% ct:log("eof received",[]),
+ {eof,Acc};
+
+ {ssh_cm, C, {data,Ch,0,B}} when Ch==Ch1,
+ is_binary(B) ->
+ %% ct:log("(1) Received ~p bytes (total ~p), missing ~p bytes",
+ %% [byte_size(B),
+ %% byte_size(B)+byte_size(Acc),
+ %% 2*byte_size(Data)-(byte_size(B)+byte_size(Acc))]),
+ ssh_connection:adjust_window(C, Ch1, byte_size(B)),
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>),
+
+ ExpectedData = <<Data/binary, Data/binary>>,
+ case ReceivedData of
+ ExpectedData ->
+ %% ct:log("Correct data returned",[]),
+ %% receive close messages
+ loop_until(fun(Left) -> %% ct:log("Expect: ~p",[Left]),
+ Left == []
+ end,
+ fun([Next|Rest]) ->
+ receive
+ {ssh_cm,C,Next} -> Rest
+ end
+ end,
+ [%% Already received: {eof, Ch1},
+ {exit_status,Ch1,0},
+ {closed,Ch1}]
+ ),
+ ok;
+ _ when is_binary(ReceivedData) ->
+ ct:fail("~p bytes echoed but ~p expected", [byte_size(ReceivedData), 2*byte_size(Data)])
+ end.
+
+%%--------------------------------------------------------------------
+%% Utilities ---------------------------------------------------------
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%%
+%% A practical meta function
+%%
+loop_until(CondFun, DoFun, Acc) ->
+ case CondFun(Acc) of
+ true ->
+ Acc;
+ false ->
+ loop_until(CondFun, DoFun, DoFun(Acc))
+ end.
+
+%%--------------------------------------------------------------------
+%%
+%% Exec the Command in the docker. Add the arguments ExtraSshArg in the
+%% ssh command.
+%%
+%% If Expects is returned, then return 'ok', else return {fail,Msg}.
+%%
+exec_from_docker(Config, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)),
+ is_list(Config) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, new_dir(Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ R = exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg, Config),
+ ssh:close(C),
+ R.
+
+exec_from_docker(C, DestIP, DestPort, Command, Expects, ExtraSshArg, Config) when is_binary(hd(Expects)) ->
+ ExecCommand =
+ lists:concat(
+ ["sshpass -p ",?PASSWD," "
+ | case proplists:get_value(ssh_version,Config) of
+ "dropbear" ++ _ ->
+ ["dbclient -y -y -p ",DestPort," ",ExtraSshArg," ",iptoa(DestIP)," "];
+
+ _ -> %% OpenSSH or compatible
+ ["/buildroot/ssh/bin/ssh -o 'CheckHostIP=no' -o 'StrictHostKeyChecking=no' ",
+ ExtraSshArg," -p ",DestPort," ",iptoa(DestIP)," "]
+ end]) ++ Command,
+
+ case exec(C, ExecCommand) of
+ {ok,{ExitStatus,Result}} = R when ExitStatus == 0 ->
+ case binary:match(Result, Expects) of
+ nomatch ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Bad answer"};
+ _ ->
+ ok
+ end;
+ {ok,_} = R ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Exit status =/= 0"};
+ R ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Couldn't login to host"}
+ end.
+
+
+exec(C, Cmd) ->
+ %% ct:log("~s",[Cmd]),
+ {ok,Ch} = ssh_connection:session_channel(C, 10000),
+ success = ssh_connection:exec(C, Ch, Cmd, 10000),
+ result_of_exec(C, Ch).
+
+
+result_of_exec(C, Ch) ->
+ result_of_exec(C, Ch, undefined, <<>>).
+
+result_of_exec(C, Ch, ExitStatus, Acc) ->
+ receive
+ {ssh_cm,C,{closed,Ch}} ->
+ %%ct:log("CHAN ~p got *closed*",[Ch]),
+ {ok, {ExitStatus, Acc}};
+
+ {ssh_cm,C,{exit_status,Ch,ExStat}} when ExitStatus == undefined ->
+ %%ct:log("CHAN ~p got *exit status ~p*",[Ch,ExStat]),
+ result_of_exec(C, Ch, ExStat, Acc);
+
+ {ssh_cm,C,{data,Ch,_,Data}=_X} when ExitStatus == undefined ->
+ %%ct:log("CHAN ~p got ~p",[Ch,_X]),
+ result_of_exec(C, Ch, ExitStatus, <<Acc/binary, Data/binary>>);
+
+ _Other ->
+ %%ct:log("OTHER: ~p",[_Other]),
+ result_of_exec(C, Ch, ExitStatus, Acc)
+
+ after 5000 ->
+ ct:log("NO MORE, received so far:~n~s",[Acc]),
+ {error, timeout}
+ end.
+
+
+%%--------------------------------------------------------------------
+%%
+%% Loop through all {Tag,Alg} pairs in CommonAlgs, call DoTestFun(Tag,Alg) which
+%% returns one of {ok,C}, ok, or Other.
+%%
+%% The chk_all_algos returns 'ok' or {fail,FaledAlgosList}
+%%
+
+chk_all_algos(FunctionName, CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
+ ct:comment("~p algorithms",[length(CommonAlgs)]),
+ %% Check each algorithm
+ Failed =
+ lists:foldl(
+ fun({Tag,Alg}, FailedAlgos) ->
+ %% ct:log("Try ~p",[Alg]),
+ case DoTestFun(Tag,Alg) of
+ {ok,C} ->
+ ssh:close(C),
+ FailedAlgos;
+ ok ->
+ FailedAlgos;
+ Other ->
+ ct:log("FAILED! ~p ~p: ~p",[Tag,Alg,Other]),
+ [{Alg,Other}|FailedAlgos]
+ end
+ end, [], CommonAlgs),
+ ct:pal("~s", [format_result_table_use_all_algos(FunctionName, Config, CommonAlgs, Failed)]),
+ case Failed of
+ [] ->
+ ok;
+ _ ->
+ {fail, Failed}
+ end.
+
+
+
+%%%----------------------------------------------------------------
+%%%
+%%% Call all Funs as Fun() which returns 'ok', {ok,C} or Other.
+%%% do/1 returns 'ok' or the first encountered value that is not
+%%% successful.
+%%%
+
+do(Funs) ->
+ do(Funs, 1).
+
+do([Fun|Funs], N) ->
+ case Fun() of
+ ok ->
+ %% ct:log("Fun ~p ok",[N]),
+ do(Funs, N-1);
+ {ok,C} ->
+ %% ct:log("Fun ~p {ok,C}",[N]),
+ ssh:close(C),
+ do(Funs, N-1);
+ Other ->
+ ct:log("Fun ~p FAILED:~n~p",[N, Other]),
+ Other
+ end;
+
+do([], _) ->
+ %% ct:log("All Funs ok",[]),
+ ok.
+
+%%--------------------------------------------------------------------
+%%
+%% Functions to set up local and remote host's and user's keys and directories
+%%
+
+setup_local_hostdir(KeyAlg, Config) ->
+ setup_local_hostdir(KeyAlg, new_dir(Config), Config).
+setup_local_hostdir(KeyAlg, HostDir, Config) ->
+ {ok, {Priv,Publ}} = host_priv_pub_keys(Config, KeyAlg),
+ %% Local private and public key
+ DstFile = filename:join(HostDir, dst_filename(host,KeyAlg)),
+ ok = file:write_file(DstFile, Priv),
+ ok = file:write_file(DstFile++".pub", Publ),
+ HostDir.
+
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, UserDir, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, Config) ->
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config) ->
+ {ok, {Priv,Publ}} = user_priv_pub_keys(Config, KeyAlg),
+ %% Local private and public keys
+ DstFile = filename:join(UserDir, dst_filename(user,KeyAlg)),
+ ok = file:write_file(DstFile, Priv),
+ ok = file:write_file(DstFile++".pub", Publ),
+ %% Remote auth_methods with public key
+ {ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
+ {password, ?PASSWD },
+ {auth_methods, "password"},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ _ = ssh_sftp:make_dir(Ch, ".ssh"),
+ ok = ssh_sftp:write_file(Ch, ".ssh/authorized_keys", Publ),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh/authorized_keys", #file_info{mode=8#700}),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh", #file_info{mode=8#700}),
+ ok = ssh_sftp:stop_channel(Ch),
+ ok = ssh:close(Cc),
+ UserDir.
+
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, UserDir, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, Config) ->
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config) ->
+ {ok, {Priv,Publ}} = user_priv_pub_keys(Config, KeyAlg),
+ %% Local auth_methods with public key
+ AuthKeyFile = filename:join(UserDir, "authorized_keys"),
+ ok = file:write_file(AuthKeyFile, Publ),
+ %% Remote private and public key
+ {ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
+ {password, ?PASSWD },
+ {auth_methods, "password"},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ _ = ssh_sftp:make_dir(Ch, ".ssh"),
+ DstFile = filename:join(".ssh", dst_filename(user,KeyAlg)),
+ ok = ssh_sftp:write_file(Ch, DstFile, Priv),
+ ok = ssh_sftp:write_file_info(Ch, DstFile, #file_info{mode=8#700}),
+ ok = ssh_sftp:write_file(Ch, DstFile++".pub", Publ),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh", #file_info{mode=8#700}),
+ ok = ssh_sftp:stop_channel(Ch),
+ ok = ssh:close(Cc),
+ UserDir.
+
+user_priv_pub_keys(Config, KeyAlg) -> priv_pub_keys("users_keys", user, Config, KeyAlg).
+host_priv_pub_keys(Config, KeyAlg) -> priv_pub_keys("host_keys", host, Config, KeyAlg).
+
+priv_pub_keys(KeySubDir, Type, Config, KeyAlg) ->
+ KeyDir = filename:join(proplists:get_value(data_dir,Config), KeySubDir),
+ {ok,Priv} = file:read_file(filename:join(KeyDir,src_filename(Type,KeyAlg))),
+ {ok,Publ} = file:read_file(filename:join(KeyDir,src_filename(Type,KeyAlg)++".pub")),
+ {ok, {Priv,Publ}}.
+
+
+%%%---------------- The default filenames
+src_filename(user, 'ssh-rsa' ) -> "id_rsa";
+src_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
+src_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
+src_filename(user, 'ssh-dss' ) -> "id_dsa";
+src_filename(user, 'ecdsa-sha2-nistp256') -> "id_ecdsa256";
+src_filename(user, 'ecdsa-sha2-nistp384') -> "id_ecdsa384";
+src_filename(user, 'ecdsa-sha2-nistp521') -> "id_ecdsa521";
+src_filename(host, 'ssh-rsa' ) -> "ssh_host_rsa_key";
+src_filename(host, 'rsa-sha2-256' ) -> "ssh_host_rsa_key";
+src_filename(host, 'rsa-sha2-512' ) -> "ssh_host_rsa_key";
+src_filename(host, 'ssh-dss' ) -> "ssh_host_dsa_key";
+src_filename(host, 'ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key256";
+src_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key384";
+src_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key521".
+
+dst_filename(user, 'ssh-rsa' ) -> "id_rsa";
+dst_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
+dst_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
+dst_filename(user, 'ssh-dss' ) -> "id_dsa";
+dst_filename(user, 'ecdsa-sha2-nistp256') -> "id_ecdsa";
+dst_filename(user, 'ecdsa-sha2-nistp384') -> "id_ecdsa";
+dst_filename(user, 'ecdsa-sha2-nistp521') -> "id_ecdsa";
+dst_filename(host, 'ssh-rsa' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'rsa-sha2-256' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'rsa-sha2-512' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'ssh-dss' ) -> "ssh_host_dsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key".
+
+
+%%--------------------------------------------------------------------
+%%
+%% Format the result table for chk_all_algos/4
+%%
+format_result_table_use_all_algos(FunctionName, Config, CommonAlgs, Failed) ->
+ %% Write a nice table with the result
+ AlgHead = 'Algorithm',
+ AlgWidth = lists:max([length(atom_to_list(A)) || {_,A} <- CommonAlgs]),
+ {ResultTable,_} =
+ lists:mapfoldl(
+ fun({T,A}, Tprev) ->
+ Tag = case T of
+ Tprev -> "";
+ _ -> io_lib:format('~s~n',[T])
+ end,
+ {io_lib:format('~s ~*s ~s~n',
+ [Tag, -AlgWidth, A,
+ case proplists:get_value(A,Failed) of
+ undefined -> "(ok)";
+ Err -> io_lib:format("<<<< FAIL <<<< ~p",[Err])
+ end]),
+ T}
+ end, undefined, CommonAlgs),
+
+ Vssh = proplists:get_value(ssh_version,Config,""),
+ io_lib:format("~nResults of ~p, Peer version: ~s~n~n"
+ "Tag ~*s Result~n"
+ "=====~*..=s=======~n~s"
+ ,[FunctionName, Vssh,
+ -AlgWidth, AlgHead,
+ AlgWidth, "", ResultTable]).
+
+%%--------------------------------------------------------------------
+%%
+%% Docker handling: start_docker/1 and stop_docker/1
+%%
+start_docker(Ver) ->
+ Cmnd = lists:concat(["docker run -itd --rm -p 1234 ",?DOCKER_PFX,":",Ver]),
+ Id0 = os:cmd(Cmnd),
+ ct:log("Ver = ~p, Cmnd ~p~n-> ~p",[Ver,Cmnd,Id0]),
+ case is_docker_sha(Id0) of
+ true ->
+ Id = hd(string:tokens(Id0, "\n")),
+ IP = ip(Id),
+ Port = 1234,
+ {ok, {Ver,{IP,Port},Id}};
+ false ->
+ throw(cant_start_docker)
+ end.
+
+
+stop_docker({_Ver,_,Id}) ->
+ Cmnd = lists:concat(["docker kill ",Id]),
+ os:cmd(Cmnd).
+
+is_docker_sha(L) ->
+ lists:all(fun(C) when $a =< C,C =< $z -> true;
+ (C) when $0 =< C,C =< $9 -> true;
+ ($\n) -> true;
+ (_) -> false
+ end, L).
+
+%%--------------------------------------------------------------------
+%%
+%% Misc docker info functions
+
+ip_port(Config) ->
+ {_Ver,{IP,Port},_} = proplists:get_value(id,Config),
+ {IP,Port}.
+
+port_mapped_to(Id) ->
+ Cmnd = lists:concat(["docker ps --format \"{{.Ports}}\" --filter id=",Id]),
+ [_, PortStr | _] = string:tokens(os:cmd(Cmnd), ":->/"),
+ list_to_integer(PortStr).
+
+ip(Id) ->
+ Cmnd = lists:concat(["docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ",
+ Id]),
+ IPstr0 = os:cmd(Cmnd),
+ ct:log("Cmnd ~p~n-> ~p",[Cmnd,IPstr0]),
+ IPstr = hd(string:tokens(IPstr0, "\n")),
+ {ok,IP} = inet:parse_address(IPstr),
+ IP.
+
+%%--------------------------------------------------------------------
+%%
+%% Normalize the host returned from ssh_test_lib
+
+iptoa({0,0,0,0}) -> inet_parse:ntoa(host_ip());
+iptoa(IP) -> inet_parse:ntoa(IP).
+
+host_ip() ->
+ {ok,Name} = inet:gethostname(),
+ {ok,#hostent{h_addr_list = [IP|_]}} = inet_res:gethostbyname(Name),
+ IP.
+
+%%--------------------------------------------------------------------
+%%
+%% Create a new fresh directory or clear an existing one
+%%
+
+new_dir(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SubDirName = integer_to_list(erlang:system_time()),
+ Dir = filename:join(PrivDir, SubDirName),
+ case file:read_file_info(Dir) of
+ {error,enoent} ->
+ ok = file:make_dir(Dir),
+ Dir;
+ _ ->
+ timer:sleep(25),
+ new_dir(Config)
+ end.
+
+clear_dir(Dir) ->
+ delete_all_contents(Dir),
+ {ok,[]} = file:list_dir(Dir),
+ Dir.
+
+delete_all_contents(Dir) ->
+ {ok,Fs} = file:list_dir(Dir),
+ lists:map(fun(F0) ->
+ F = filename:join(Dir, F0),
+ case filelib:is_file(F) of
+ true ->
+ file:delete(F);
+ false ->
+ case filelib:is_dir(F) of
+ true ->
+ delete_all_contents(F),
+ file:del_dir(F);
+ false ->
+ ct:log("Neither file nor dir: ~p",[F])
+ end
+ end
+ end, Fs).
+
+%%--------------------------------------------------------------------
+%%
+%% Find the intersection of algoritms for otp ssh and the docker ssh.
+%% Returns {ok, ServerHello, Server, ClientHello, Client} where Server are the algorithms common
+%% with the docker server and analogous for Client.
+%%
+%% Client may be undefined if no usable client is found.
+%%
+%% Both Server and Client are lists of {Tag,AlgName}.
+%%
+
+common_algs(Config, IP, Port) ->
+ case remote_server_algs(IP, Port) of
+ {ok, {ServerHello, RemoteServerKexInit}} ->
+ RemoteServerAlgs = kexint_msg2default_algorithms(RemoteServerKexInit),
+ Server = find_common_algs(RemoteServerAlgs,
+ use_algorithms(ServerHello)),
+ ct:log("Remote server:~n~p~n~p",[ServerHello, RemoteServerAlgs]),
+ case remote_client_algs(Config) of
+ {ok,{ClientHello,RemoteClientKexInit}} ->
+ RemoteClientAlgs = kexint_msg2default_algorithms(RemoteClientKexInit),
+ Client = find_common_algs(RemoteClientAlgs,
+ use_algorithms(ClientHello)),
+ ct:log("Remote client:~n~p~n~p",[ClientHello, RemoteClientAlgs]),
+ {ok, ServerHello, Server, ClientHello, Client};
+ {error,_} =TO ->
+ ct:log("Remote client algs can't be found: ~p",[TO]),
+ {ok, ServerHello, Server, undefined, undefined};
+ Other ->
+ Other
+ end;
+ Other ->
+ Other
+ end.
+
+
+chk_hellos(Hs, Str) ->
+ lists:foldl(
+ fun(H, Acc) ->
+ try binary:split(H, <<"-">>, [global])
+ of
+ %% [<<"SSH">>,<<"2.0">>|_] ->
+ %% Acc;
+ [<<"SSH">>,OldVer = <<"1.",_/binary>>|_] ->
+ io_lib:format("~s, Old SSH ver ~s",[Acc,OldVer]);
+ _ ->
+ Acc
+ catch
+ _:_ ->
+ Acc
+ end
+ end, Str, Hs).
+
+
+find_common_algs(Remote, Local) ->
+ [{T,V} || {T,Vs} <- ssh_test_lib:extract_algos(
+ ssh_test_lib:intersection(Remote,
+ Local)),
+ V <- Vs].
+
+
+use_algorithms(RemoteHelloBin) ->
+ MyAlgos = ssh:chk_algos_opts(
+ [{modify_algorithms,
+ [{append,
+ [{kex,['diffie-hellman-group1-sha1']}
+ ]}
+ ]}
+ ]),
+ ssh_transport:adjust_algs_for_peer_version(binary_to_list(RemoteHelloBin)++"\r\n",
+ MyAlgos).
+
+kexint_msg2default_algorithms(#ssh_msg_kexinit{kex_algorithms = Kex,
+ server_host_key_algorithms = PubKey,
+ encryption_algorithms_client_to_server = CipherC2S,
+ encryption_algorithms_server_to_client = CipherS2C,
+ mac_algorithms_client_to_server = MacC2S,
+ mac_algorithms_server_to_client = MacS2C,
+ compression_algorithms_client_to_server = CompC2S,
+ compression_algorithms_server_to_client = CompS2C
+ }) ->
+ [{kex, ssh_test_lib:to_atoms(Kex)},
+ {public_key, ssh_test_lib:to_atoms(PubKey)},
+ {cipher, [{client2server,ssh_test_lib:to_atoms(CipherC2S)},
+ {server2client,ssh_test_lib:to_atoms(CipherS2C)}]},
+ {mac, [{client2server,ssh_test_lib:to_atoms(MacC2S)},
+ {server2client,ssh_test_lib:to_atoms(MacS2C)}]},
+ {compression, [{client2server,ssh_test_lib:to_atoms(CompC2S)},
+ {server2client,ssh_test_lib:to_atoms(CompS2C)}]}].
+
+
+%%--------------------------------------------------------------------
+%%
+%% Find the algorithms supported by the remote server
+%%
+%% Connect with tcp to the server, send a hello and read the returned
+%% server hello and kexinit message.
+%%
+remote_server_algs(IP, Port) ->
+ case try_gen_tcp_connect(IP, Port, 5) of
+ {ok,S} ->
+ ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
+ receive_hello(S);
+ {error,Error} ->
+ {error,Error}
+ end.
+
+try_gen_tcp_connect(IP, Port, N) when N>0 ->
+ case gen_tcp:connect(IP, Port, [binary]) of
+ {ok,S} ->
+ {ok,S};
+ {error,_Error} when N>1 ->
+ receive after 1000 -> ok end,
+ try_gen_tcp_connect(IP, Port, N-1);
+ {error,Error} ->
+ {error,Error}
+ end;
+try_gen_tcp_connect(_, _, _) ->
+ {error, "No contact"}.
+
+
+%%--------------------------------------------------------------------
+%%
+%% Find the algorithms supported by the remote client
+%%
+%% Set up a fake ssh server and make the remote client connect to it. Use
+%% hello message and the kexinit message.
+%%
+remote_client_algs(Config) ->
+ Parent = self(),
+ Ref = make_ref(),
+ spawn(
+ fun() ->
+ {ok,Sl} = gen_tcp:listen(0, [binary]),
+ {ok,{IP,Port}} = inet:sockname(Sl),
+ Parent ! {addr,Ref,IP,Port},
+ {ok,S} = gen_tcp:accept(Sl),
+ ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
+ Parent ! {Ref,receive_hello(S)}
+ end),
+ receive
+ {addr,Ref,IP,Port} ->
+ spawn(fun() ->
+ exec_from_docker(Config, IP, Port,
+ "howdy.\r\n",
+ [<<"howdy">>],
+ "")
+ end),
+ receive
+ {Ref, Result} ->
+ Result
+ after 5000 ->
+ {error, {timeout,2}}
+ end
+ after 5000 ->
+ {error, {timeout,1}}
+ end.
+
+
+%%% Receive a few packets from the remote server or client and find what is supported:
+
+receive_hello(S) ->
+ try
+ receive_hello(S, <<>>)
+ of
+ Result ->
+ Result
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {Class,Error,ST}}
+ end.
+
+
+receive_hello(S, Ack) ->
+ %% The Ack is to collect bytes until the full message is received
+ receive
+ {tcp, S, Bin0} when is_binary(Bin0) ->
+ case binary:split(<<Ack/binary, Bin0/binary>>, [<<"\r\n">>,<<"\r">>,<<"\n">>]) of
+ [Hello = <<"SSH-2.0-",_/binary>>, NextPacket] ->
+ %% ct:log("Got 2.0 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ {ok, {Hello, receive_kexinit(S, NextPacket)}};
+
+ [Hello = <<"SSH-1.99-",_/binary>>, NextPacket] ->
+ %% ct:log("Got 1.99 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ {ok, {Hello, receive_kexinit(S, NextPacket)}};
+
+ [Bin] when size(Bin) < 256 ->
+ %% ct:log("Got part of hello (~p chars):~n~s~n~s",[size(Bin),Bin,
+ %% [io_lib:format('~2.16.0b ',[C])
+ %% || C <- binary_to_list(Bin0)
+ %% ]
+ %% ]),
+ receive_hello(S, Bin0);
+
+ _ ->
+ ct:log("Bad hello string (line ~p, ~p chars):~n~s~n~s",[?LINE,size(Bin0),Bin0,
+ [io_lib:format('~2.16.0b ',[C])
+ || C <- binary_to_list(Bin0)
+ ]
+ ]),
+ ct:fail("Bad hello string received")
+ end;
+ Other ->
+ ct:log("Bad hello string (line ~p):~n~p",[?LINE,Other]),
+ ct:fail("Bad hello string received")
+
+ after 10000 ->
+ ct:log("Timeout waiting for hello!~n~s",[Ack]),
+ throw(timeout)
+ end.
+
+
+receive_kexinit(_S, <<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>)
+ when PacketLen < 5000, % heuristic max len to stop huge attempts if packet decodeing get out of sync
+ size(PayloadAndPadding) >= (PacketLen-1) % Need more bytes?
+ ->
+ ct:log("Has all ~p packet bytes",[PacketLen]),
+ PayloadLen = PacketLen - PaddingLen - 1,
+ <<Payload:PayloadLen/binary, _Padding:PaddingLen/binary>> = PayloadAndPadding,
+ ssh_message:decode(Payload);
+
+receive_kexinit(S, Ack) ->
+ ct:log("Has ~p bytes, need more",[size(Ack)]),
+ receive
+ {tcp, S, Bin0} when is_binary(Bin0) ->
+ receive_kexinit(S, <<Ack/binary, Bin0/binary>>);
+ Other ->
+ ct:log("Bad hello string (line ~p):~n~p",[?LINE,Other]),
+ ct:fail("Bad hello string received")
+
+ after 10000 ->
+ ct:log("Timeout waiting for kexinit!~n~s",[Ack]),
+ throw(timeout)
+ end.
+
+%%%----------------------------------------------------------------
+%%% Test of sftp from the OpenSSH client side
+%%%
+
+sftp_tests_erl_server(Config, ServerIP, ServerPort, ServerRootDir, UserDir) ->
+ try
+ Cmnds = prepare_local_directory(ServerRootDir),
+ call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir),
+ check_local_directory(ServerRootDir)
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {Class,Error,ST}}
+ end.
+
+
+prepare_local_directory(ServerRootDir) ->
+ file:write_file(filename:join(ServerRootDir,"tst1"),
+ <<"Some test text">>
+ ),
+ ["get tst1",
+ "put tst1 tst2",
+ "put tst1 tst3",
+ "rename tst1 ex_tst1",
+ "rm tst3",
+ "mkdir mydir",
+ "cd mydir",
+ "put tst1 file_1",
+ "put tst1 unreadable_file",
+ "chmod 222 unreadable_file",
+ "exit"].
+
+check_local_directory(ServerRootDir) ->
+ case lists:sort(ok(file:list_dir(ServerRootDir)) -- [".",".."]) of
+ ["ex_tst1","mydir","tst2"] ->
+ {ok,Expect} = file:read_file(filename:join(ServerRootDir,"ex_tst1")),
+ case file:read_file(filename:join(ServerRootDir,"tst2")) of
+ {ok,Expect} ->
+ case lists:sort(ok(file:list_dir(filename:join(ServerRootDir,"mydir"))) -- [".",".."]) of
+ ["file_1","unreadable_file"] ->
+ case file:read_file(filename:join([ServerRootDir,"mydir","file_1"])) of
+ {ok,Expect} ->
+ case file:read_file(filename:join([ServerRootDir,"mydir","unreadable_file"])) of
+ {error,_} ->
+ ok;
+ {ok,_} ->
+ {error, {could_read_unreadable,"mydir/unreadable_file"}}
+ end;
+ {ok,Other} ->
+ ct:log("file_1:~n~s~nExpected:~n~s",[Other,Expect]),
+ {error, {bad_contents_in_file,"mydir/file_1"}}
+ end;
+ Other ->
+ ct:log("Directory ~s~n~p",[filename:join(ServerRootDir,"mydir"),Other]),
+ {error,{bad_dir_contents,"mydir"}}
+ end;
+ {ok,Other} ->
+ ct:log("tst2:~n~s~nExpected:~n~s",[Other,Expect]),
+ {error, {bad_contents_in_file,"tst2"}}
+ end;
+ ["tst1"] ->
+ {error,{missing_file,"tst2"}};
+ Other ->
+ ct:log("Directory ~s~n~p",[ServerRootDir,Other]),
+ {error,{bad_dir_contents,"/"}}
+ end.
+
+call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, UserDir},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+
+ %% Make commands for "expect" in the docker:
+ PreExpectCmnds = ["spawn /buildroot/ssh/bin/sftp -oPort="++integer_to_list(ServerPort)++
+ " -oCheckHostIP=no -oStrictHostKeyChecking=no " ++
+ iptoa(ServerIP)++"\n"
+ ],
+ PostExpectCmnds= [],
+ ExpectCmnds =
+ PreExpectCmnds ++
+ ["expect \"sftp>\" {send \""++Cmnd++"\n\"}\n" || Cmnd <- Cmnds] ++
+ PostExpectCmnds,
+
+ %% Make an commands file in the docker
+ {ok,Ch} = ssh_sftp:start_channel(C, [{timeout,10000}]),
+ ok = ssh_sftp:write_file(Ch, "commands", erlang:iolist_to_binary(ExpectCmnds)),
+ ok = ssh_sftp:stop_channel(Ch),
+
+ %% Call expect in the docker
+ {ok, Ch1} = ssh_connection:session_channel(C, infinity),
+ Kex1 = renegotiate_test(init, C),
+ success = ssh_connection:exec(C, Ch1, "expect commands", infinity),
+
+ renegotiate_test(Kex1, C),
+ recv_log_msgs(C, Ch1),
+
+ %% Done.
+ ssh:close(C).
+
+recv_log_msgs(C, Ch) ->
+ receive
+ {ssh_cm,C,{closed,Ch}} ->
+ %% ct:log("Channel closed ~p",[{closed,1}]),
+ ok;
+ {ssh_cm,C,{data,Ch,1,Msg}} ->
+ ct:log("*** ERROR from docker:~n~s",[Msg]),
+ recv_log_msgs(C, Ch);
+ {ssh_cm,C,_Msg} ->
+ %% ct:log("Got ~p",[_Msg]),
+ recv_log_msgs(C, Ch)
+ end.
+
+%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
+%%%
+%%% Tests from the Erlang client side
+%%%
+%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
+test_erl_client_reneg({ok,C}, Spec) ->
+ %% Start the test processes on the connection C:
+ Parent = self(),
+ Pids = [spawn(
+ fun() ->
+ Parent ! {self(), TestType, Id, one_test_erl_client(TestType,Id,C)}
+ end
+ )
+ || {TestType,N} <- Spec,
+ Id <- lists:seq(1,N)],
+
+ Kex1 = renegotiate_test(init, C),
+
+ %% Collect the results:
+ case lists:filter(
+ fun(R) -> R=/=ok end,
+ [receive
+ {Pid,_TestType,_Id,ok} ->
+ %% ct:log("Test ~p:~p passed!", [_TestType,_Id]),
+ ok;
+ {Pid,TestType,Id,OtherResult} ->
+ ct:log("~p:~p ~p ~p~n~p",[?MODULE,?LINE,TestType,Id,OtherResult]),
+ {error,TestType,Id}
+ end || Pid <- Pids])
+ of
+ [] ->
+ renegotiate_test(Kex1, C),
+ {ok,C};
+ Other ->
+ renegotiate_test(Kex1, C),
+ Other
+ end;
+
+test_erl_client_reneg(Error, _) ->
+ Error.
+
+
+one_test_erl_client(exec, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ success = ssh_connection:exec(C, Ch, "echo Hi there", 5000),
+ case loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ receive
+ {ssh_cm, C, {eof,Ch}} ->
+ {eof,Acc};
+ {ssh_cm, C, {data,Ch,0,B}} when is_binary(B) ->
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>) of
+ {eof,<<"Hi there\n">>} ->
+ ok;
+ Other ->
+ ct:pal("exec Got other ~p", [Other]),
+ {error, {exec,Id,bad_msg,Other,undefined}}
+ end;
+
+one_test_erl_client(no_subsyst, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ case ssh_connection:subsystem(C, Ch, "foo", infinity) of
+ failure ->
+ ok;
+ Other ->
+ ct:pal("no_subsyst Got other ~p", [Other]),
+ {error, {no_subsyst,Id,bad_ret,Other,undefined}}
+ end;
+
+one_test_erl_client(setenv, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ Var = "ENV_TEST",
+ Value = lists:concat(["env_test_",Id,"_",erlang:system_time()]),
+ Env = case ssh_connection:setenv(C, Ch, Var, Value, infinity) of
+ success -> binary_to_list(Value++"\n");
+ failure -> <<"\n">>
+ end,
+ success = ssh_connection:exec(C, Ch, "echo $"++Var, 5000),
+ case loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ receive
+ {ssh_cm, C, {eof,Ch}} ->
+ {eof,Acc};
+ {ssh_cm, C, {data,Ch,0,B}} when is_binary(B) ->
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>) of
+ {eof,Env} ->
+ ok;
+ Other ->
+ ct:pal("setenv Got other ~p", [Other]),
+ {error, {setenv,Id,bad_msg,Other,undefined}}
+ end;
+
+one_test_erl_client(SFTP, Id, C) when SFTP==sftp ; SFTP==sftp_async ->
+ try
+ {ok,Ch} = ssh_sftp:start_channel(C, [{timeout,10000}]),
+ %% A new fresh name of a new file tree:
+ RootDir = lists:concat(["r_",Id,"_",erlang:system_time()]),
+ %% Check that it does not exist:
+ false = lists:member(RootDir, ok(ssh_sftp:list_dir(Ch, "."))),
+ %% Create it:
+ ok = ssh_sftp:make_dir(Ch, RootDir),
+ {ok, #file_info{type=directory, access=read_write}} = ssh_sftp:read_file_info(Ch, RootDir),
+ R = do_sftp_tests_erl_client(SFTP, C, Ch, Id, RootDir),
+ catch ssh_sftp:stop_channel(Ch),
+ R
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {SFTP,Id,Class,Error,ST}}
+ end.
+
+
+
+do_sftp_tests_erl_client(sftp_async, _C, Ch, _Id, RootDir) ->
+ FileName1 = "boring_name",
+ F1 = filename:join(RootDir, FileName1),
+ %% Open a new handle and start writing:
+ {ok,Handle1} = ssh_sftp:open(Ch, F1, [write,binary]),
+ {async,Aref1} = ssh_sftp:awrite(Ch, Handle1, <<0:250000/unsigned-unit:8>>),
+ wait_for_async_result(Aref1);
+
+do_sftp_tests_erl_client(sftp, _C, Ch, _Id, RootDir) ->
+ FileName0 = "f0",
+ F0 = filename:join(RootDir, FileName0),
+
+ %% Create and write a file:
+ ok = ssh_sftp:write_file(Ch,
+ F0 = filename:join(RootDir, FileName0),
+ Data0 = mkbin(1234,240)),
+ {ok,Data0} = ssh_sftp:read_file(Ch, F0),
+ {ok, #file_info{type=regular, access=read_write, size=1234}} = ssh_sftp:read_file_info(Ch, F0),
+
+ %% Re-write:
+ {ok,Handle0} = ssh_sftp:open(Ch, F0, [write,read,binary]),
+ ok = ssh_sftp:pwrite(Ch, Handle0, 16, Data0_1=mkbin(10,255)),
+
+ <<B1:16/binary, _:10/binary, B2:(1234-26)/binary>> = Data0,
+ FileContents = <<B1:16/binary, Data0_1:10/binary, B2:(1234-26)/binary>>,
+
+ <<_:1/binary, Part:25/binary, _/binary>> = FileContents,
+ {ok, Part} = ssh_sftp:pread(Ch, Handle0, 1, 25),
+
+ %% Check:
+ {ok, FileContents} = ssh_sftp:pread(Ch, Handle0, 0, 1234),
+ ok = ssh_sftp:close(Ch, Handle0),
+
+ %% Check in another way:
+ {ok, FileContents} = ssh_sftp:read_file(Ch, F0),
+
+ %% Remove write access rights and check that it can't be written:
+ ok = ssh_sftp:write_file_info(Ch, F0, #file_info{mode=8#400}), %read}),
+ {ok, #file_info{type=regular, access=read}} = ssh_sftp:read_file_info(Ch, F0),
+ {error,permission_denied} = ssh_sftp:write_file(Ch, F0, mkbin(10,14)),
+
+ %% Test deletion of file and dir:
+ [FileName0] = ok(ssh_sftp:list_dir(Ch, RootDir)) -- [".", ".."],
+ ok = ssh_sftp:delete(Ch, F0),
+ [] = ok(ssh_sftp:list_dir(Ch, RootDir)) -- [".", ".."],
+ ok = ssh_sftp:del_dir(Ch, RootDir),
+ false = lists:member(RootDir, ok(ssh_sftp:list_dir(Ch, "."))),
+ ok.
+
+
+wait_for_async_result(Aref) ->
+ receive
+ {async_reply, Aref, Result} ->
+ Result
+ after
+ 60000 ->
+ timeout
+ end.
+
+
+mkbin(Size, Byte) ->
+ list_to_binary(lists:duplicate(Size,Byte)).
+
+ok({ok,X}) -> X.
+
+%%%----------------------------------------------------------------
+renegotiate_test(init, ConnectionRef) ->
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+ ssh_connection_handler:renegotiate(ConnectionRef),
+ %%ct:log("Renegotiate test initiated!",[]),
+ Kex1;
+
+renegotiate_test(Kex1, ConnectionRef) ->
+ case ssh_test_lib:get_kex_init(ConnectionRef) of
+ Kex1 ->
+ ct:log("Renegotiate test failed, Kex1 == Kex2!",[]),
+ error(renegotiate_failed);
+ _ ->
+ %% ct:log("Renegotiate test passed!",[]),
+ ok
+ end.
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image
new file mode 100755
index 0000000000..1cb7bf33e1
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+UBUNTU_VER=${1:-16.04}
+
+USER=sshtester
+PWD=foobar
+
+docker build \
+ -t ubuntubuildbase \
+ --build-arg https_proxy=$HTTPS_PROXY \
+ --build-arg http_proxy=$HTTP_PROXY \
+ - <<EOF
+
+ FROM ubuntu:$UBUNTU_VER
+ WORKDIR /buildroot
+
+ # Prepare for installing OpenSSH
+ RUN apt-get update
+ RUN apt-get upgrade -y
+ RUN apt-get -y install apt-utils
+ RUN apt-get -y install build-essential zlib1g-dev
+ RUN apt-get -y install sudo iputils-ping tcptraceroute net-tools
+ RUN apt-get -y install sshpass expect
+ RUN apt-get -y install libpam0g-dev
+
+ # A user for the tests
+ RUN (echo $PWD; echo $PWD; echo; echo; echo; echo; echo; echo ) | adduser $USER
+ RUN adduser $USER sudo
+
+ # Prepare the privsep preauth environment for openssh
+ RUN mkdir -p /var/empty
+ RUN chown root:sys /var/empty
+ RUN chmod 755 /var/empty
+ RUN groupadd -f sshd
+ RUN ls /bin/false
+ RUN id -u sshd 2> /dev/null || useradd -g sshd -c 'sshd privsep' -d /var/empty -s /bin/false sshd
+
+EOF
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh
new file mode 100755
index 0000000000..85973081d0
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# ./create-dropbear-ssh
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ubuntubuildbase
+
+ WORKDIR /buildroot
+
+ RUN apt-get -y update
+ RUN apt-get -y upgrade
+ RUN apt-get -y install openssh-sftp-server
+%% RUN echo 81 | apt-get -y install dropbear
+
+EOF
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh-dropbear -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run
new file mode 100755
index 0000000000..d98c0cfaa3
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# ./create-dropbear-ssh-run
+
+VER=v2016.72
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ssh_compat_suite-ssh-dropbear-installed:${VER}
+
+ WORKDIR /buildroot
+
+ CMD dropbear -F -p 1234
+
+EOF
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh:dropbear${VER} -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
new file mode 100755
index 0000000000..2e08408841
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+# ./create-image openssh 7.3p1 openssl 1.0.2m
+
+set -x
+
+case $1 in
+ openssh)
+ FAMssh=openssh
+ VERssh=$2
+ PFX=https://ftp.eu.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ *)
+ echo "Unsupported: $1"
+ exit
+esac
+
+FAMssl=$3
+VERssl=$4
+
+VER=${FAMssh}${VERssh}-${FAMssl}${VERssl}
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ssh_compat_suite-${FAMssl}:${VERssl}
+
+ LABEL openssh-version=${VER}
+
+ WORKDIR /buildroot
+
+ COPY ${TMP} .
+ RUN tar xf ${TMP}
+
+ # Build and install
+
+ WORKDIR ${FAMssh}-${VERssh}
+
+ # Probably VERY OpenSSH dependent...:
+ RUN ./configure --without-pie \
+ --prefix=/buildroot/ssh \
+ --with-ssl-dir=/buildroot/ssl \
+ --with-pam \
+ LDFLAGS=-Wl,-R/buildroot/ssl/lib
+ RUN make
+ RUN make install
+ RUN echo UsePAM yes >> /buildroot/ssh/etc/sshd_config
+
+ RUN echo Built ${VER}
+
+ # Start the daemon, but keep it in foreground to avoid killing the container
+ CMD /buildroot/ssh/sbin/sshd -D -p 1234
+
+EOF
+
+# Fetch the tar file. This could be done in an "ADD ..." in the Dockerfile,
+# but then we hit the proxy problem...
+wget -O $TMP $PFX$VERssh$SFX
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh:$VER -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
+
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
new file mode 100755
index 0000000000..4ab2a8bddc
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# ./create-image openssl 1.0.2m
+
+case "$1" in
+ "openssl")
+ FAM=openssl
+ VER=$2
+ PFX=https://www.openssl.org/source/openssl-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ "libressl")
+ FAM=libressl
+ VER=$2
+ PFX=https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ *)
+ echo No lib type
+ exit
+ ;;
+esac
+
+case $1$2 in
+ openssl0.9.8[a-l])
+ CONFIG_FLAGS=no-asm
+ ;;
+ *)
+ CONFIG_FLAGS=
+ ;;
+esac
+
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ubuntubuildbase
+
+ LABEL version=$FAM-$VER
+
+ WORKDIR /buildroot
+
+ COPY ${TMP} .
+ RUN tar xf ${TMP}
+
+ WORKDIR ${FAM}-${VER}
+
+ RUN ./config --prefix=/buildroot/ssl ${CONFIG_FLAGS}
+
+ RUN make
+ RUN make install_sw
+
+ RUN echo Built ${FAM}-${VER}
+EOF
+
+# Fetch the tar file. This could be done in an "ADD ..." in the Dockerfile,
+# but then we hit the proxy problem...
+wget -O $TMP $PFX$VER$SFX
+
+# Build the image:
+docker build -t ssh_compat_suite-$FAM:$VER -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
new file mode 100755
index 0000000000..0dcf8cb570
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+UBUNTU_VERSION=16.04
+
+SSH_SSL_VERSIONS=(\
+ openssh 4.4p1 openssl 0.9.8c \
+ openssh 4.5p1 openssl 0.9.8m \
+ openssh 5.0p1 openssl 0.9.8za \
+ openssh 6.2p2 openssl 0.9.8c \
+ openssh 6.3p1 openssl 0.9.8zh \
+ \
+ openssh 7.1p1 openssl 1.0.0a \
+ \
+ openssh 7.1p1 openssl 1.0.1p \
+ \
+ openssh 6.6p1 openssl 1.0.2n \
+ openssh 7.1p1 openssl 1.0.2n \
+ openssh 7.6p1 openssl 1.0.2n \
+ \
+ openssh 7.6p1 libressl 2.6.4 \
+ )
+
+if [ "x$1" == "x-b" ]
+then
+ shift
+ SKIP_CREATE_BASE=true
+fi
+
+WHAT_TO_DO=$1
+
+function create_one_image ()
+{
+ SSH_FAM=$1
+ SSH_VER=$2
+ SSL_FAM=$3
+ SSL_VER=$4
+
+ [ "x$SKIP_CREATE_BASE" == "xtrue" ] || ./create-base-image || (echo "Create base failed." >&2; exit 1)
+ ./create-ssl-image $SSL_FAM $SSL_VER \
+ || (echo "Create $SSL_FAM $SSL_VER failed." >&2; exit 2)
+
+ ./create-ssh-image $SSH_FAM $SSH_VER $SSL_FAM $SSL_VER \
+ || (echo "Create $SSH_FAM $SSH_VER on $SSL_FAM $SSL_VER failed." >&2; exit 3)
+}
+
+
+case ${WHAT_TO_DO} in
+ list)
+ ;;
+ listatoms)
+ PRE="["
+ POST="]"
+ C=\'
+ COMMA=,
+ ;;
+ build_one)
+ if [ $# != 5 ]
+ then
+ echo "$0 build_one openssh SSH_ver openssl SSL_ver " && exit
+ else
+ create_one_image $2 $3 $4 $5
+ exit
+ fi
+ ;;
+ build_all)
+ ;;
+ *)
+ echo "$0 [-b] list | listatoms | build_one openssh SSH_ver openssl SSL_ver | build_all" && exit
+ ;;
+esac
+
+
+echo -n $PRE
+i=0
+while [ "x${SSH_SSL_VERSIONS[i]}" != "x" ]
+do
+ case ${WHAT_TO_DO} in
+ list*)
+ [ $i -eq 0 ] || echo $COMMA
+ echo -n $C${SSH_SSL_VERSIONS[$i]}${SSH_SSL_VERSIONS[$(( $i + 1 ))]}-${SSH_SSL_VERSIONS[$(( $i + 2 ))]}${SSH_SSL_VERSIONS[$(( $i + 3 ))]}$C
+ ;;
+ build_all)
+ create_one_image ${SSH_SSL_VERSIONS[$i]} ${SSH_SSL_VERSIONS[$(( $i + 1 ))]} ${SSH_SSL_VERSIONS[$(( $i + 2 ))]} ${SSH_SSL_VERSIONS[$(( $i + 3 ))]}
+ ;;
+ esac
+
+ i=$(( $i + 4 ))
+done
+echo $POST
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key
new file mode 100644
index 0000000000..8b2354a7ea
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBugIBAAKBgQDlXDEddxFbTtPsu2bRTbSONFVKMxe430iqBoXoKK2Gyhlqn7J8
+SRGlmvTN7T06+9iFqgJi+x+dlSJGlNEY/v67Z8C7rWfJynYuRier4TujLwP452RT
+YrsnCq47pGJXHb9xAWr7UGMv85uDrECUiIdK4xIrwpW/gMb5zPSThDGNiwIVANts
+B9nBX0NH/B0lXthVCg2jRSkpAoGAIS3vG8VmjQNYrGfdcdvQtGubFXs4jZJO6iDe
+9u9/O95dcnH4ZIL4y3ZPHbw73dCKXFe5NlqI/POmn3MyFdpyqH5FTHWB/aAFrma6
+qo00F1mv83DkQCEfg6fwE/SaaBjDecr5I14hWOtocpYqlY1/x1aspahwK6NLPp/D
+A4aAt78CgYAmNgr3dnHgMXrEsAeHswioAad3YLtnPvdFdHqd5j4oSbgKwFd7Xmyq
+blfeQ6rRo8dmUF0rkUU8cn71IqbhpsCJQEZPt9WBlhHiY95B1ELKYHoHCbZA8qrZ
+iEIcfwch85Da0/uzv4VE0UHTC0P3WRD3sZDfXd9dZLdc80n6ImYRpgIURgW8SZGj
+X0mMkMJv/Ltdt0gYx60=
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..9116493472
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAOVcMR13EVtO0+y7ZtFNtI40VUozF7jfSKoGhegorYbKGWqfsnxJEaWa9M3tPTr72IWqAmL7H52VIkaU0Rj+/rtnwLutZ8nKdi5GJ6vhO6MvA/jnZFNiuycKrjukYlcdv3EBavtQYy/zm4OsQJSIh0rjEivClb+AxvnM9JOEMY2LAAAAFQDbbAfZwV9DR/wdJV7YVQoNo0UpKQAAAIAhLe8bxWaNA1isZ91x29C0a5sVeziNkk7qIN7273873l1ycfhkgvjLdk8dvDvd0IpcV7k2Woj886afczIV2nKofkVMdYH9oAWuZrqqjTQXWa/zcORAIR+Dp/AT9JpoGMN5yvkjXiFY62hyliqVjX/HVqylqHAro0s+n8MDhoC3vwAAAIAmNgr3dnHgMXrEsAeHswioAad3YLtnPvdFdHqd5j4oSbgKwFd7XmyqblfeQ6rRo8dmUF0rkUU8cn71IqbhpsCJQEZPt9WBlhHiY95B1ELKYHoHCbZA8qrZiEIcfwch85Da0/uzv4VE0UHTC0P3WRD3sZDfXd9dZLdc80n6ImYRpg== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256
new file mode 100644
index 0000000000..5ed2b361cc
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEILwQIf+Jul+oygeJn7cBSvn2LGqnW1ZfiHDQMDXZ96mooAoGCCqGSM49
+AwEHoUQDQgAEJUo0gCIhXEPJYvxec23IAjq7BjV1xw8deI8JV9vL5BMCZNhyj5Vt
+NbFPbKPuL/Sikn8p4YP/5y336ug7szvYrg==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub
new file mode 100644
index 0000000000..240387d901
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCVKNIAiIVxDyWL8XnNtyAI6uwY1dccPHXiPCVfby+QTAmTYco+VbTWxT2yj7i/0opJ/KeGD/+ct9+roO7M72K4= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384
new file mode 100644
index 0000000000..9d31d75cd5
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDBw+P1sic2i41wTGQgjyUlBtxQfnY77L8TFcDngoRiVrbCugnDrioNo
+JogqymWhSC+gBwYFK4EEACKhZANiAATwaqEp3vyLzfb08kqgIZLv/mAYJyGD+JMt
+f11OswGs3uFkbHZOErFCgeLuBvarSTAFkOlMR9GZGaDEfcrPBTtvKj+jEaAvh6yr
+JxS97rtwk2uadDMem2x4w9Ga4jw4S8E=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub
new file mode 100644
index 0000000000..cca85bda72
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPBqoSne/IvN9vTySqAhku/+YBgnIYP4ky1/XU6zAaze4WRsdk4SsUKB4u4G9qtJMAWQ6UxH0ZkZoMR9ys8FO28qP6MRoC+HrKsnFL3uu3CTa5p0Mx6bbHjD0ZriPDhLwQ== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521
new file mode 100644
index 0000000000..b698be1ec9
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBtGVvyn7kGX7BfWAYHK2ZXmhWscTOV0J0mAfab0u0ZMw0id2a3O9s
+sBjJoCqoAXTJ7d/OUw85qqQNDE5GDQpDFq6gBwYFK4EEACOhgYkDgYYABAHPWfUD
+tQ/JmfwmmSdWWjGm94hFqwaivI4H43acDdd71+vods4rN2Yh3X7fSUvJkeOhXFOJ
+yO9F+61ssKgS0a0nxQEvdXks3QyfKTPjYQuBUvY+AV/A4AskPBz731xCDmbYuWuh
+RPekZ7d5bF0U0pGlExbX+naQJMSbJSdZrPM9993EmA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub
new file mode 100644
index 0000000000..d181d30d69
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHPWfUDtQ/JmfwmmSdWWjGm94hFqwaivI4H43acDdd71+vods4rN2Yh3X7fSUvJkeOhXFOJyO9F+61ssKgS0a0nxQEvdXks3QyfKTPjYQuBUvY+AV/A4AskPBz731xCDmbYuWuhRPekZ7d5bF0U0pGlExbX+naQJMSbJSdZrPM9993EmA== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key
new file mode 100644
index 0000000000..84096298ca
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuC6uxC0P8voYQCrwJzczo9iSiwsovPv4etd2BLnu8cKWdnjR
+34tWvtguw2kO+iDyt4hFGGfDBQf2SXl+ZEsE2N1RlSp5A73me2byw/L4MreX2rbU
+TwyNXF3TBvKb3Gbpx7PoiB9frcb9RCMxtypBvGQD6bx6h5UWKuSkYzARaRLv3kbB
+swcqfrA3PfWybkIoaa2RO1Ca86u6K0v+a4r0OfRxTnghuakZkH6CD7+uU3irliPI
+UFt2wTI/qWmnDrMFh4RffToHK0QZHXdkq4ama5kRZdZ0svSorxqkl8EWGPhReoUj
+Yrz0bCNevSlDxHCxLi8epRxuv+AhZHW0YdMCCwIDAQABAoIBAHUyj1aZbfqolWHP
+cL0jbSKnHqiHU0bd9sED9T8QqTEBJwj/3Fwop+wMV8VURol3CbsrZPwgmoHLDTa3
+rmtXKSBtxAns2tA8uDpxyaxSIQj0thYgHHyoehL6SNu06OSYP84pdp+XhyRm6KXA
+11O7+dRMuAi1PCql/VMR5mCPJ6T5qWAVYHFyEBvMm4q5yYSRSPaAaZHC6WbEsxHN
+jGzcyl3tvmOyN0+M7v0U86lQ+H2tSXH+nQg/Ig6hWgFGg8AYoos/9yUGOY+e9bUE
+serYdsuiyxBfo4CgoSeDsjwNp1lAZ5UOrIDdRqK9C8jGVkHDzwfmmtczWXkVVzGZ
+Bd05izECgYEA31yHzSA/umamyZAQbi/5psk1Fc5m6MzsgmJmB6jm7hUZ0EbpSV4C
+6b1nOrk/IAtA12rvDHgWy0zpkJbC5b03C77RnBgTRgLQyolrcpLDJ47+Kxf/AHGk
+m63KaCpwZEQ4f9ARBXySD/jNoW9gz5S6Xa3RnHOC70DsIIk5VOCjWk0CgYEA0xiM
+Ay27PJcbAG/4tnjH8DZfHb8SULfnfUj8kMe3V2SDPDWbhY8zheo45wTBDRflFU5I
+XyGmfuZ7PTTnFVrJz8ua3mAMOzkFn4MmdaRCX9XtuE4YWq3lFvxlrJvfXSjEL0km
+8UwlhJMixaEPqFQjsKc9BHwWKRiKcF4zFQ1DybcCgYB46yfdhYLaj23lmqc6b6Bw
+iWbCql2N1DqJj2l65hY2d5fk6C6s+EcNcOrsoJKq70yoEgzdrDlyz+11yBg0tU2S
+fzgMkAAHG8kajHBts0QRK1kvzSrQe7VITjpQUAFOVpxbnTFJzhloqiHwLlKzremC
+g3IBh4svqO7r4j32VDI61QKBgQCQL4gS872cWSncVp7vI/iNHtZBHy2HbNX1QVEi
+Iwgb7U+mZIdh5roukhlj0l96bgPPVbUhJX7v1sX+vI/KikSmZk/V7IzuNrich5xR
+ZmzfwOOqq8z+wyBjXuqjx6P9oca+9Zxf3L8Tmtx5WNW1CCOImfKXiZopX9XPgsgp
+bPIMaQKBgQCql4uTSacSQ5s6rEEdvR+y6nTohF3zxhOQ+6xivm3Hf1mgTk40lQ+t
+sr6HsSTv8j/ZbhhtaUUb2efro3pDztjlxXFvITar9ZDB2B4QMlpSsDR9UNk8xKGY
+J9aYLr4fJC6J6VA7Wf0yq6LpjSXRH/2GeNtmMl5rFRsHt+VU7GZK9g==
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..4ac6e7b124
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4Lq7ELQ/y+hhAKvAnNzOj2JKLCyi8+/h613YEue7xwpZ2eNHfi1a+2C7DaQ76IPK3iEUYZ8MFB/ZJeX5kSwTY3VGVKnkDveZ7ZvLD8vgyt5fattRPDI1cXdMG8pvcZunHs+iIH1+txv1EIzG3KkG8ZAPpvHqHlRYq5KRjMBFpEu/eRsGzByp+sDc99bJuQihprZE7UJrzq7orS/5rivQ59HFOeCG5qRmQfoIPv65TeKuWI8hQW3bBMj+paacOswWHhF99OgcrRBkdd2SrhqZrmRFl1nSy9KivGqSXwRYY+FF6hSNivPRsI169KUPEcLEuLx6lHG6/4CFkdbRh0wIL uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa
new file mode 100644
index 0000000000..01a88acea2
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQC97XncQDaa9PQYEWK7llBxZQ2suVYTz1eadw2HtY+Y8ZKdUBLd
+9LUQ2lymUC9yq66rb5pBBR13k/9Zcbu8I0nafrZT4wJ4H0YGD6Ob5O4HR4EHjO5q
+hgnMJ17e1XnzI31MW5xAuAHTLLClNvnG05T1jaU+tRAsVSCHin3+sOenowIVAMSe
+ANBvw7fm5+Lw+ziOAHPjeYzRAoGBALkWCGpKmlJ65F3Y/RcownHQvsrDAllzKF/a
+cSfriCVVP5qVZ3Ach28ZZ9BFEnRE2SKqVsyBAiceb/+ISlu8CqKEvvoNIMJAu5rU
+MwZh+PeHN4ES6tWTwBGAwu84ke6N4BgV+6Q4qkcyywHsT5oU0EdVbn2zzAZw8c7v
+BpbsJ1KsAoGABraHWqSFhaX4+GHmtKwXZFVRKh/4R6GR2LpkFzGm3Ixv+eo9K5CI
+TjiBYiVMrWH23G1LiDuJyMGqHEnIef+sorNfNzdnwq+8qRCTS6mbpRXkUt9p1arJ
+MIKmosS+GFhTN6Z85gCwC51S2EDC4GW7J4ViHKacr1FwJSw9RC9F+WsCFQCRJayH
+P4vM1XUOVEeX7u04K1EAFg==
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub
new file mode 100644
index 0000000000..30661d5adf
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAL3tedxANpr09BgRYruWUHFlDay5VhPPV5p3DYe1j5jxkp1QEt30tRDaXKZQL3KrrqtvmkEFHXeT/1lxu7wjSdp+tlPjAngfRgYPo5vk7gdHgQeM7mqGCcwnXt7VefMjfUxbnEC4AdMssKU2+cbTlPWNpT61ECxVIIeKff6w56ejAAAAFQDEngDQb8O35ufi8Ps4jgBz43mM0QAAAIEAuRYIakqaUnrkXdj9FyjCcdC+ysMCWXMoX9pxJ+uIJVU/mpVncByHbxln0EUSdETZIqpWzIECJx5v/4hKW7wKooS++g0gwkC7mtQzBmH494c3gRLq1ZPAEYDC7ziR7o3gGBX7pDiqRzLLAexPmhTQR1VufbPMBnDxzu8GluwnUqwAAACABraHWqSFhaX4+GHmtKwXZFVRKh/4R6GR2LpkFzGm3Ixv+eo9K5CITjiBYiVMrWH23G1LiDuJyMGqHEnIef+sorNfNzdnwq+8qRCTS6mbpRXkUt9p1arJMIKmosS+GFhTN6Z85gCwC51S2EDC4GW7J4ViHKacr1FwJSw9RC9F+Ws= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa
new file mode 100644
index 0000000000..60e8f6eb6e
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIC557KPgmq+pWOAh1L8DV8GWW0u7W5vz6mim3FFB1l8koAoGCCqGSM49
+AwEHoUQDQgAEC3J5fQ8+8xQso0lhBdoLdvD14oSsQiMuweXq+Dy2+4Mjdw2/bbw0
+CvbE2+KWNcgwxRLycNGcMCBdf/cOgNyGkA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256
new file mode 100644
index 0000000000..60e8f6eb6e
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIC557KPgmq+pWOAh1L8DV8GWW0u7W5vz6mim3FFB1l8koAoGCCqGSM49
+AwEHoUQDQgAEC3J5fQ8+8xQso0lhBdoLdvD14oSsQiMuweXq+Dy2+4Mjdw2/bbw0
+CvbE2+KWNcgwxRLycNGcMCBdf/cOgNyGkA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub
new file mode 100644
index 0000000000..b349d26da3
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAtyeX0PPvMULKNJYQXaC3bw9eKErEIjLsHl6vg8tvuDI3cNv228NAr2xNviljXIMMUS8nDRnDAgXX/3DoDchpA= sshtester@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384
new file mode 100644
index 0000000000..ece6c8f284
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDBdgJs/xThHiy/aY1ymtQ4B0URNnRCm8l2WZMFjua57+nvq4Duf+igN
+pN/5p/+azLKgBwYFK4EEACKhZANiAATUw6pT/UW2HvTW6lL2BGY7NfUGEX285XVi
+9AcTXH1K+TOekbGmcpSirlGzSb15Wycajpmaae5vAzH1nnvcVd3FYODVdDXTHgV/
+FeXQ+vaw7CZnEAKZsr8mjXRX3fEyO1E=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub
new file mode 100644
index 0000000000..fd81e220f7
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNTDqlP9RbYe9NbqUvYEZjs19QYRfbzldWL0BxNcfUr5M56RsaZylKKuUbNJvXlbJxqOmZpp7m8DMfWee9xV3cVg4NV0NdMeBX8V5dD69rDsJmcQApmyvyaNdFfd8TI7UQ== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521
new file mode 100644
index 0000000000..21c000ea03
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHbAgEBBEEhm0w3xcGILU8eP61mThVBwCJfyzrFktGf7cCa1ciL4YLsukd20Q3Z
+yp0YcEDLcEm36CZGabgkEvblJ1Rx2lPTu6AHBgUrgQQAI6GBiQOBhgAEAYep8cX2
+7wUPw5pNYwFkWQXrJ2GSkmO8iHwkWJ6srRay/sF3WoPF/dyDVymFgirtsSTJ+D0u
+ex4qphOOJxkd1Yf+ANHvDFN9LoBvbgtNLTRJlpuNLCdWQlt+mEnPMDGMV/HWHHiz
+7/mWE+XUVIcQjhm5uv0ObI/wroZEurXMGEhTis3L
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub
new file mode 100644
index 0000000000..d9830da5de
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGHqfHF9u8FD8OaTWMBZFkF6ydhkpJjvIh8JFierK0Wsv7Bd1qDxf3cg1cphYIq7bEkyfg9LnseKqYTjicZHdWH/gDR7wxTfS6Ab24LTS00SZabjSwnVkJbfphJzzAxjFfx1hx4s+/5lhPl1FSHEI4Zubr9DmyP8K6GRLq1zBhIU4rNyw== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa
new file mode 100644
index 0000000000..2e50ac2304
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA7+C3gLoflKybq4I+clbg2SWf6cXyHpnLNDnZeMvIbOz2X/Ce
+XUm17DFeexTaVBs9Zq9WwDFOFkLQhbuXgpvB0shSY0nr+Em7InRM8AiRLxPe0txM
+mFFhL+v083dYwgaJOo1PthNM/tGRZJu+0sQDqrmN7CusFHdZg2NTzTzbwWqPiuP/
+mf3o7W4CWqDTBzbYTgpWlH7vRZf9FYwT4on5YWzLA8TvO2TwBGTfTMK5nswH++iO
+v4jKecoEwyBFMUSKqZ9UYHGw/kshHbltM65Ye/xjXEX0GxDdxu8ZyVKXd4acNbJJ
+P0tcxN4GzKJiR6zNYwCzDhjqDEbM5qCGhShhgQIDAQABAoIBAQCucdGBP9mvmUcs
+Fu+q3xttTztYGqfVMSrhtCA/BJOhA0K4ypegZ/Zw6gY3pBaSi6y/fEuuQSz0a2qR
+lra8OOFflGa15hBA4/2/NKyu8swCXITy+1qIesYev43HcMePcolhl1qcorSfq2/8
+pnbDd+Diy0Y2thvSVmk2b4mF+/gkUx3CHLhgRMcxCHLG1VeqIfLf+pa0jIw94tZ5
+CoIoI096pDTsneO9xhh1QxWQRRFVqdf3Q9zyiBgJCggpX+1fVsbQejuEK4hKRBKx
+SRPX/pX5aU+7+KSZ/DbtXGg1sCw9NUDFTIEV3UPmko4oWawNGv/CQAK80g3go28v
+UnVf11BBAoGBAP2amIFp+Ps33A5eesT7g/NNkGqBEi5W37K8qzYJxqXJvH0xmpFo
+8a3Je3PQRrzbTUJyISA6/XNnA62+bEvWiEXPiK3stQzNHoVz7ftCb19zgW4sLKRW
+Qhjq7QsGeRrdksJnZ7ekfzOv658vHJPElS1MdPu2WWhiNvAjtmdyFQulAoGBAPIk
+6831QAnCdp/ffH/K+cqV9vQYOFig8n4mQNNC+sLghrtZh9kbmTuuNKAhF56vdCCn
+ABD/+RiLXKVsF0PvQ5g9wRLKaiJubXI7XEBemCCLhjtESxGpWEV8GalslUgE1cKs
+d1pwSVjd0sYt0gOAf2VRhlbpSWhXA2xVll34xgetAoGAHaI089pYN7K9SgiMO/xP
+3NxRZcCTSUrpdM9LClN2HOVH2zEyqI8kvnPuswfBXEwb6QnBCS0bdKKy8Vhw+yOk
+ZNPtWrVwKoDFcj6rrlKDBRpQI3mR9doGezboYANvn04I2iKPIgxcuMNzuvQcWL/9
+1n86pDcYl3Pyi3kA1XGlN+kCgYEAz1boBxpqdDDsjGa8X1y5WUviAw8+KD3ghj5R
+IdTnjbjeBUxbc38bTawUac0MQZexE0iMWQImFGs4sHkGzufwdErkqSdjjAoMc1T6
+4C9fifaOwO7wbLYZ3J2wB4/vn5RsSV6OcIVXeN2wXnvbqZ38+A+/vWnSrqJbTwdW
+Uy7yup0CgYEA8M9vjpAoCr3XzNDwJyWRQcT7e+nRYUNDlXBl3jpQhHuJtnSnkoUv
+HXYXEwvp8peycNzeVz5OwFVMzCH8OG4WiGN4Pmo0rDWHED/W7eIRHIitHGTzZ+Qw
+gRxscoewblSLSkYMXidBLmQjr4U5bDBesRuGhm5NuLyMTa1f3Pc/90k=
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub
new file mode 100644
index 0000000000..26e560d4f8
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDv4LeAuh+UrJurgj5yVuDZJZ/pxfIemcs0Odl4y8hs7PZf8J5dSbXsMV57FNpUGz1mr1bAMU4WQtCFu5eCm8HSyFJjSev4SbsidEzwCJEvE97S3EyYUWEv6/Tzd1jCBok6jU+2E0z+0ZFkm77SxAOquY3sK6wUd1mDY1PNPNvBao+K4/+Z/ejtbgJaoNMHNthOClaUfu9Fl/0VjBPiiflhbMsDxO87ZPAEZN9MwrmezAf76I6/iMp5ygTDIEUxRIqpn1RgcbD+SyEduW0zrlh7/GNcRfQbEN3G7xnJUpd3hpw1skk/S1zE3gbMomJHrM1jALMOGOoMRszmoIaFKGGB uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index 9bbd9da817..b818a7f45d 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -45,6 +45,8 @@ all() ->
{group, openssh},
small_interrupted_send,
interrupted_send,
+ exec_erlang_term,
+ exec_erlang_term_non_default_shell,
start_shell,
start_shell_exec,
start_shell_exec_fun,
@@ -85,13 +87,14 @@ init_per_suite(Config) ->
?CHECK_CRYPTO(Config).
end_per_suite(Config) ->
+ catch ssh:stop(),
Config.
%%--------------------------------------------------------------------
init_per_group(openssh, Config) ->
case ssh_test_lib:gen_tcp_connect("localhost", 22, []) of
{error,econnrefused} ->
- {skip,"No openssh deamon"};
+ {skip,"No openssh deamon (econnrefused)"};
{ok, Socket} ->
gen_tcp:close(Socket),
ssh_test_lib:openssh_sanity_check(Config)
@@ -542,6 +545,79 @@ start_shell_exec(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+exec_erlang_term(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got expected ~p",[R]);
+ Other ->
+ ct:log("Got unexpected ~p",[Other])
+ after 5000 ->
+ {fail,"Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
+exec_erlang_term_non_default_shell(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {shell, fun(U, H) -> start_our_shell(U, H) end}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}
+ ]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got unexpected ~p",[R]),
+ {fail,"Could exec erlang term although non-erlang shell"};
+ Other ->
+ ct:log("Got expected ~p",[Other])
+ after 5000 ->
+ {fail, "Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
start_shell_exec_fun() ->
[{doc, "start shell to exec command"}].
@@ -800,6 +876,8 @@ stop_listener(Config) when is_list(Config) ->
ssh:stop_daemon(Pid0),
ssh:stop_daemon(Pid1);
Error ->
+ ssh:close(ConnectionRef0),
+ ssh:stop_daemon(Pid0),
ct:fail({unexpected, Error})
end.
diff --git a/lib/ssh/test/ssh_engine_SUITE.erl b/lib/ssh/test/ssh_engine_SUITE.erl
new file mode 100644
index 0000000000..c131a70973
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE.erl
@@ -0,0 +1,146 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssh_engine_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include("ssh_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,40}}].
+
+all() ->
+ [{group, dsa_key},
+ {group, rsa_key}
+ ].
+
+groups() ->
+ [{dsa_key, [], basic_tests()},
+ {rsa_key, [], basic_tests()}
+ ].
+
+basic_tests() ->
+ [simple_connect
+ ].
+
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ ssh:start(),
+ ?CHECK_CRYPTO(
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Strange Engine stuff"};
+
+ _ ->
+ case load_engine() of
+ {ok,E} ->
+ [{engine,E}|Config];
+ {error, notsup} ->
+ {skip, "Engine not supported on this OpenSSL version"};
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"};
+ Other ->
+ ct:log("Engine load failed: ~p",[Other]),
+ {fail, "Engine load failed"}
+ end
+ end
+ ).
+
+end_per_suite(Config) ->
+ catch crypto:engine_unload( proplists:get_value(engine,Config) ),
+ ssh:stop().
+
+%%--------------------------------------------------------------------
+init_per_group(dsa_key, Config) ->
+ case lists:member('ssh-dss',
+ ssh_transport:default_algorithms(public_key)) of
+ true ->
+ start_daemon(Config, 'ssh-dss', "dsa_private_key.pem");
+ false ->
+ {skip, unsupported_pub_key}
+ end;
+init_per_group(rsa_key, Config) ->
+ case lists:member('ssh-rsa',
+ ssh_transport:default_algorithms(public_key)) of
+ true ->
+ start_daemon(Config, 'ssh-rsa', "rsa_private_key.pem");
+ false ->
+ {skip, unsupported_pub_key}
+ end.
+
+start_daemon(Config, KeyType, KeyId) ->
+ SystemDir = proplists:get_value(data_dir, Config),
+ FullKeyId = filename:join(SystemDir, KeyId),
+ KeyCBOpts = [{engine, proplists:get_value(engine,Config)},
+ {KeyType, FullKeyId}
+ ],
+ Opts = [{key_cb, {ssh_key_cb_engine_keys, KeyCBOpts}}],
+ {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, Opts),
+ [{host_port,{Host,Port}}, {daemon_pid,Pid}| Config].
+
+
+end_per_group(_, Config) ->
+ catch ssh:stop_daemon(proplists:get_value(daemon_pid,Config)),
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+%% A simple exec call
+simple_connect(Config) ->
+ {Host,Port} = proplists:get_value(host_port, Config),
+ CRef = ssh_test_lib:std_connect(Config, Host, Port, []),
+ ssh:close(CRef).
+
+%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+load_engine() ->
+ case crypto:get_test_engine() of
+ {ok, Engine} ->
+ try crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ <<"LOAD">>],
+ [])
+ catch
+ error:notsup ->
+ {error, notsup}
+ end;
+
+ {error, Error} ->
+ {error, Error}
+ end.
+
+start_std_daemon(Opts, Config) ->
+ ct:log("starting std_daemon",[]),
+ {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, Opts),
+ ct:log("started ~p:~p ~p",[Host,Port,Opts]),
+ [{srvr_pid,Pid},{srvr_addr,{Host,Port}} | Config].
diff --git a/lib/ssh/test/ssh_engine_SUITE_data/dsa_private_key.pem b/lib/ssh/test/ssh_engine_SUITE_data/dsa_private_key.pem
new file mode 100644
index 0000000000..778ffac675
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE_data/dsa_private_key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PRIVATE KEY-----
+MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAMyitTMR7vPbpqyAXJpqnB0AhFwQ
+F87IE+JKFl5bD/MSkhhRV5sM73HUU1ooXY0FjhZ+cdLUCATuZR5ta4ydANqWIcAB
+gX3IwF1B4zf5SXEKTWkUYneL9dOKtiZLtoG28swrk8xMxwX+0fLHkltCEj6FiTW9
+PFrv8GmIfV6DjcI9AhUAqXWbb3RtoN9Ld28fVMhGZrj3LJUCgYEAwnxGHGBMpJaF
+2w7zAw3jHjL8PMYlV6vnufGHQlwF0ZUXJxRsvagMb/X1qACTu2VPYEVoLQGM3cfH
+EhHoQmvSXGAyTfR7Bmn3gf1n/s/DcFbdZduUCZ/rAyIrfd0eSbc1I+kZk85UCsKK
+w/IYdlqcuYa4Cgm2TapT5uEMqH4jhzEEFgIULh8swEUWmU8aJNWsrWl4eCiuUUg=
+-----END PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_engine_SUITE_data/ecdsa_private_key.pem b/lib/ssh/test/ssh_engine_SUITE_data/ecdsa_private_key.pem
new file mode 100644
index 0000000000..a45522064f
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE_data/ecdsa_private_key.pem
@@ -0,0 +1,8 @@
+-----BEGIN PRIVATE KEY-----
+MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBparGjr0KcdNrVM2J
+G0mW5ltP1QyvxDqBMyWLWo3fruRZv6Qoohl5skd1u4O+KJoM/UrrSTOXI/MDR7NN
+i1yl7O+hgYkDgYYABAG8K2XVsK0ahG9+HIIPwCO0pJY8ulwSTXwIjkCGyB2lpglh
+8qJmRzuyGcfRTslv8wfv0sPlT9H9PKDvgrTUL7rvQQDdOODNgVPXSecUoXoPn+X+
+eqxs77bjx+A5x0t/i3m5PfkaNPh5MZ1H/bWuOOdj2ZXZw0R4rlVc0zVrgnPU8L8S
+BQ==
+-----END PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key.pem b/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key.pem
new file mode 100644
index 0000000000..ea0e3d3958
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCwwb0/ddXGXTFK
+4FLxXdV6a/WJMSoPPS55RvZIAHFsiTtvPLbJ8LxDsZ6wSVZLN0/UQ4wdWn9jftyj
+U5/IxBVG8XOtKimTMvm3/ZOzVLueGHBbrLYscRv9oL85ulTKHWgrZDu0lBX5JJTI
+v5UTCErzJRQbka9DG1GaBgDb1PlXfkzBWMwfsBZmwoC77KvCcIGCgbW/XCY03TP2
+3Tg8drvpByMStddP2FQ4fZ91qFUzPu8uhZEsqSQTFlmhgGEx7dLlky0xvu62RuAD
+RTpINpcWZtWDHTdssOqu653LwwqBY8lBopCZ/4Af8QR3ZYkQhen1YLEbVheXRuzI
+LSCZIiJNAgMBAAECggEBAJH4/fxpqQkvr2Shy33Pu1xlyhnpw01gfn/jrcKasxEq
+aC4eWup86E2TY3U8q4pkfIXU3uLi+O9HNpmflwargNLc1mY8uqb44ygiv5bLNEKE
+9k2PXcdoBfC4jxPyoNFl5cBn/7LK1TazEjiTl15na9ZPWcLG1pG5/vMPYCgsQ1sP
+8J3c4E3aaXIj9QceYxBprl490OCzieGyZlRipncz3g4UShRc/b4cycvDZOJpmAy4
+zbWTcBcSMPVPi5coF0K8UcimiqZkotfb/2RLc433i34IdsIXMM+brdq+g8rmjg5a
++oQPy02M6tFApBruEhAz8DGgaLtDY6MLtyZAt3SjXnUCgYEA1zLgamdTHOqrrmIi
+eIQBnAJiyIfcY8B9SX1OsLGYFCHiPVwgUY35B2c7MavMsGcExJhtE+uxU7o5djtM
+R6r9cRHOXJ6EQwa8OwzzPqbM17/YqNDeK39bc9WOFUqRWrhDhVMPy6z8rmZr73mG
+IUC7mBNx/1GBdVYXIlsXzC96dI8CgYEA0kUAhz6I5nyPa70NDEUYHLHf3IW1BCmE
+UoVbraSePJtIEY/IqFx7oDuFo30d4n5z+8ICCtyid1h/Cp3mf3akOiqltYUfgV1G
+JgcEjKKYWEnO7cfFyO7LB7Y3GYYDJNy6EzVWPiwTGk9ZTfFJEESmHC45Unxgd17m
+Dx/R58rFgWMCgYBQXQWFdtSI5fH7C1bIHrPjKNju/h2FeurOuObcAVZDnmu4cmD3
+U8d9xkVKxVeJQM99A1coq0nrdI3k4zwXP3mp8fZYjDHkPe2pN6rW6L9yiohEcsuk
+/siON1/5/4DMmidM8LnjW9R45HLGWWGHpX7oyco2iJ+Jy/6Tq+T1MX3PbQKBgQCm
+hdsbQJ0u3CrBSmFQ/E9SOlRt0r4+45pVuCOY6yweF2QF9HcXTtbhWQJHLclDHJ5C
+Ha18aKuKFN3XzKFFBPKe1jOSBDGlQ/dQGnKx5fr8wMdObM3oiaTlIJuWbRmEUgJT
+QARjDIi8Z2b0YUhZx+Q9oSXoe3PyVYehJrQX+/BavQKBgQCIr7Zp0rQPbfqcTL+M
+OYHUoNcb14f9f8hXeXHQOqVpsGwxGdRQAU9wbx/4+obKB5xIkzBsVNcJwavisNja
+hegnGjTB/9Hc4m+5bMGwH0bhS2eQO4o+YYM2ypDmFQqDLRfFUlZ5PVHffm/aA9+g
+GanNBCsmtoHtV6CJ1UZ7NmBuIA==
+-----END PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key_pwd.pem b/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key_pwd.pem
new file mode 100644
index 0000000000..501662fc35
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key_pwd.pem
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIh888Iq6gxuMCAggA
+MBQGCCqGSIb3DQMHBAic/11YZ8Nt5gSCBMjG/Jb4qiMoBS50iQvHXqcETPE+0NBr
+jhsn9w94LkdRBstMPAsoKmY98Er96Rnde/NfmqlU9CupKTkd7Ce5poBf72Y6KMED
+cPURyjbGRFsu6x9skXB2obhyKYEqAEF2oQAg4Qbe5v1qXBIgDuC/NgiJnM+w2zCZ
+LkHSZB2/NmcnvDzcgPF7TM8pTO23xCJ33m37qjfWvHsgocVqZmL9wQ4+wr/NMYjJ
+pJvX1OHW1vBsZsXh40WchalYRSB1VeO368QfsE8coRJztqbMzdce9EQdMB6Q6jlO
+cetd3moLIoMP4I7HW0/SgokbycTbRiYSvRyU1TGc2WbW6BrFZV24IckcnnVUFatf
+6HKUcaYLG68dJcRgs5QMGkcmgVvlddENHFmHZlo0eym/xSiUl/AT8/5odscm6ML8
+wW5sneax+TF4J2eYmiN7yjAUCodXVTNYNDVKo6uUhntlymbM0o4UitVIbPIfTDHl
+sxJAEZ7vpuPqeNMxUk6G6zipuEjqsVbnuFSBSZmgKiGYcifRPUmqqINa3DdS4WVx
+xaPWdHbHVRD//ze3h/FsA+1lIE5q2kUE0xXseJA1ISog++kJp14XeaaL2j/tx3Ob
+OsbcaOAD/IUw/ItDt9kn0qzfnar7sS0Wov8AmJQxHmH7Lm93jHTLM05yE0AR/eBr
+Mig2ZdC+9OqVC+GPuBkRjSs8NpltQIDroz6EV9IMwPwXm0szSYoyoPLmlHJUdnLs
+ZUef+au6hYkEJBrvuisagnq5eT/fCV3hsjD7yODebNU2CmBTo6X2PRx/xsBHRMWl
+QkoM9PBdSCnKv6HpHl4pchuoqU2NpFjN0BCaad6aHfZSTnqgzK4bEh1oO6dI8/rB
+/eh71JyFFG5J4xbpaqz5Su01V1iwU5leK5bDwqals4M4+ZGHGciou7qnXUmX2fJl
+r6DlMUa/xy+A2ZG0NuZR05yk2oB3+KVNMgp6zFty3XaxwoNtc8GTLtLnBnIh2rlP
+mE1+I65LRWwrNQalPeOAUrYuEzhyp2Df7a8Ykas5PUH7MGR/S0Ge/dLxtE2bJuK4
+znbLAsGhvo/SbNxYqIp6D4iDtd3va6yUGncy41paA/vTKFVvXZDrXcwJQYYCVOGT
+OwdzNuozU8Dc7oxsd8oakfC46kvmVaOrGvZbm56PFfprcaL/Hslska5xxEni/eZe
+WRxZbCBhAVqS1pn5zkDQVUe9uFlR/x39Qi01HIlKLBsjpSs6qQsFArMe8hgXmXLG
+xP+dyVuOE18NzSewdEjeqSRKIM7Qi8EOjZsI4HdSRBY7bh9VhmaVXDZiCSf33TTE
+3y8nimzQAeuGoYg6WqHmWWC2Qnpki2HlaIH/ayXEyQWkP/qvg61e8ovdg9Fy8JOO
+0AacXVt5zj0q00AW5bKx7usi4NIjZedi86hUm6H19aBm7r86BKjwYTEI/GOcdrbV
+9HC/8ayOimgwiAG3gq+aLioWym+Z6KnsbVd7XReVbvM/InQx54WA2y5im0A+/c67
+oQFFPV84XGX9waeqv/K4Wzkm6HW+qVAEM67482VGOf0PVrlQMno6dOotT/Y7ljoZ
+2iz0LmN9yylJnLPDrr1i6gzbs5OhhUgbF5LI2YP2wWdCZTl/DrKSIvQZWl8U+tw3
+ciA=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_key_cb_engine_keys.erl b/lib/ssh/test/ssh_key_cb_engine_keys.erl
new file mode 100644
index 0000000000..fc9cbfd49b
--- /dev/null
+++ b/lib/ssh/test/ssh_key_cb_engine_keys.erl
@@ -0,0 +1,62 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+
+%% Note: This module is used by ssh_basic_SUITE
+
+-module(ssh_key_cb_engine_keys).
+-behaviour(ssh_server_key_api).
+-compile(export_all).
+
+host_key(SshAlg, Options) ->
+ KBopts = proplists:get_value(key_cb_private, Options, []),
+ Engine = proplists:get_value(engine, KBopts),
+ case proplists:get_value(SshAlg, KBopts) of
+ undefined ->
+ {error, {unknown_alg,SshAlg}};
+ KeyId ->
+ case crypto_alg(SshAlg) of
+ undefined ->
+ {error, {unsupported_alg,SshAlg}};
+ CryptoAlg ->
+ PrivKey = #{engine => Engine,
+ key_id => KeyId,
+ algorithm => CryptoAlg},
+ %% Is there a key with this reference ?
+ case crypto:privkey_to_pubkey(CryptoAlg, PrivKey) of
+ [_|_] ->
+ {ok, PrivKey};
+ _ ->
+ {error, {no_hostkey,SshAlg}}
+ end
+ end
+ end.
+
+is_auth_key(_PublicUserKey, _User, _Options) ->
+ false.
+
+
+
+crypto_alg('ssh-rsa') -> rsa;
+crypto_alg('ssh-dss') -> dss;
+crypto_alg(_) -> undefined.
+
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 144ec7f8fd..12a85c40aa 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -70,7 +70,8 @@
hostkey_fingerprint_check_sha256/1,
hostkey_fingerprint_check_sha384/1,
hostkey_fingerprint_check_sha512/1,
- hostkey_fingerprint_check_list/1
+ hostkey_fingerprint_check_list/1,
+ save_accepted_host_option/1
]).
%%% Common test callbacks
@@ -124,6 +125,7 @@ all() ->
id_string_own_string_server,
id_string_own_string_server_trail_space,
id_string_random_server,
+ save_accepted_host_option,
{group, hardening_tests}
].
@@ -206,32 +208,23 @@ end_per_group(_, Config) ->
%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
ssh:start(),
- Config.
-
-end_per_testcase(TestCase, Config) when TestCase == server_password_option;
- TestCase == server_userpassword_option;
- TestCase == server_pwdfun_option;
- TestCase == server_pwdfun_4_option ->
+ %% Create a clean user_dir
UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey),
ssh_test_lib:del_dirs(UserDir),
- end_per_testcase(Config);
-end_per_testcase(_TestCase, Config) ->
- end_per_testcase(Config).
+ file:make_dir(UserDir),
+ [{user_dir,UserDir}|Config].
-end_per_testcase(_Config) ->
+end_per_testcase(_TestCase, Config) ->
ssh:stop(),
ok.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-%%--------------------------------------------------------------------
%%% validate to server that uses the 'password' option
server_password_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
@@ -262,12 +255,10 @@ server_password_option(Config) when is_list(Config) ->
%%% validate to server that uses the 'password' option
server_userpassword_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{user_passwords, [{"vego", "morot"}]}]),
ConnectionRef =
@@ -297,15 +288,13 @@ server_userpassword_option(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun' option
server_pwdfun_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
CHKPWD = fun("foo",Pwd) -> Pwd=="bar";
(_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,CHKPWD}]),
ConnectionRef =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -335,9 +324,7 @@ server_pwdfun_option(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun/4' option
server_pwdfun_4_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
PWDFUN = fun("foo",Pwd,{_,_},undefined) -> Pwd=="bar";
("fie",Pwd,{_,_},undefined) -> {Pwd=="bar",new_state};
@@ -345,7 +332,7 @@ server_pwdfun_4_option(Config) ->
(_,_,_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,PWDFUN}]),
ConnectionRef1 =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -395,9 +382,7 @@ server_pwdfun_4_option(Config) ->
%%--------------------------------------------------------------------
server_pwdfun_4_option_repeat(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
%% Test that the state works
Parent = self(),
@@ -406,7 +391,7 @@ server_pwdfun_4_option_repeat(Config) ->
(_,P,_,S) -> Parent!{P,S}, {false,S+1}
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{auth_methods,"keyboard-interactive"},
{pwdfun,PWDFUN}]),
@@ -490,9 +475,7 @@ user_dir_option(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
@@ -530,9 +513,7 @@ ssh_msg_debug_fun_option_client(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -576,9 +557,7 @@ connectfun_disconnectfun_server(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -607,9 +586,7 @@ connectfun_disconnectfun_client(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -651,9 +628,7 @@ ssh_msg_debug_fun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -686,9 +661,7 @@ disconnectfun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -720,9 +693,7 @@ disconnectfun_option_client(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -763,9 +734,7 @@ unexpectedfun_option_server(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -840,14 +809,9 @@ supported_hash(HashAlg) ->
really_do_hostkey_fingerprint_check(Config, HashAlg) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDirServer = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDirServer),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
- UserDirClient =
- ssh_test_lib:create_random_dir(Config), % Ensure no 'known_hosts' disturbs
-
%% All host key fingerprints. Trust that public_key has checked the ssh_hostkey_fingerprint
%% function since that function is used by the ssh client...
FPs0 = [case HashAlg of
@@ -873,7 +837,7 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
%% Start daemon with the public keys that we got fingerprints from
{Pid, Host0, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, UserDirServer},
+ {user_dir, UserDir},
{password, "morot"}]),
Host = ssh_test_lib:ntoa(Host0),
FP_check_fun = fun(PeerName, FP) ->
@@ -896,7 +860,8 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
end},
{user, "foo"},
{password, "morot"},
- {user_dir, UserDirClient},
+ {user_dir, UserDir},
+ {save_accepted_host, false}, % Ensure no 'known_hosts' disturbs
{user_interaction, false}]),
ssh:stop_daemon(Pid).
@@ -987,9 +952,7 @@ ms_passed(T0) ->
%%--------------------------------------------------------------------
ssh_daemon_minimal_remote_max_packet_size_option(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
{Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
@@ -1314,6 +1277,33 @@ try_to_connect(Connect, Host, Port, Pid, Tref, N) ->
end.
%%--------------------------------------------------------------------
+save_accepted_host_option(Config) ->
+ UserDir = proplists:get_value(user_dir, Config),
+ KnownHosts = filename:join(UserDir, "known_hosts"),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"vego", "morot"}]}
+ ]),
+ {error,enoent} = file:read_file(KnownHosts),
+
+ {ok,_C1} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false},
+ {save_accepted_host, false},
+ {user_dir, UserDir}]),
+ {error,enoent} = file:read_file(KnownHosts),
+
+ {ok,_C2} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+ {ok,_} = file:read_file(KnownHosts),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 7da921adb2..3e3e151781 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -630,11 +630,12 @@ client_handles_keyboard_interactive_0_pwds(Config) ->
%%%--------------------------------------------------------------------
-client_info_line(_Config) ->
+client_info_line(Config) ->
%% A client must not send an info-line. If it does, the server should handle
%% handle this gracefully
{ok,Pid} = ssh_eqc_event_handler:add_report_handler(),
- {_, _, Port} = ssh_test_lib:daemon([]),
+ DataDir = proplists:get_value(data_dir, Config),
+ {_, _, Port} = ssh_test_lib:daemon([{system_dir,DataDir}]),
%% Fake client:
{ok,S} = gen_tcp:connect("localhost",Port,[]),
@@ -884,9 +885,9 @@ chk_pref_algs(Config,
filter_supported(K, Algs) -> Algs -- (Algs--supported(K)).
-supported(K) -> proplists:get_value(
- server2client,
- ssh_transport:supported_algorithms(cipher)).
+supported(_K) -> proplists:get_value(
+ server2client,
+ ssh_transport:supported_algorithms(cipher)).
to_lists(L) -> lists:map(fun erlang:atom_to_list/1, L).
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index 7aa3d8a00a..c2f9c0eba8 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -181,8 +181,9 @@ init_per_group(openssh_server, Config) ->
[{peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config];
{error,"Key exchange failed"} ->
{skip, "openssh server doesn't support the tested kex algorithm"};
- _ ->
- {skip, "No openssh server"}
+ Other ->
+ ct:log("No openssh server. Cause:~n~p~n",[Other]),
+ {skip, "No openssh daemon (see log in testcase)"}
end;
init_per_group(remote_tar, Config) ->
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index 763649a12f..5fc948fbed 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -34,7 +34,7 @@
-define(PASSWD, "Sesame").
-define(XFER_PACKET_SIZE, 32768).
-define(XFER_WINDOW_SIZE, 4*?XFER_PACKET_SIZE).
--define(TIMEOUT, 10000).
+-define(SSH_TIMEOUT, 10000).
-define(REG_ATTERS, <<0,0,0,0,1>>).
-define(UNIX_EPOCH, 62167219200).
@@ -161,9 +161,9 @@ init_per_testcase(TestCase, Config) ->
{silently_accept_hosts, true}]),
{ok, Channel} =
ssh_connection:session_channel(Cm, ?XFER_WINDOW_SIZE,
- ?XFER_PACKET_SIZE, ?TIMEOUT),
+ ?XFER_PACKET_SIZE, ?SSH_TIMEOUT),
- success = ssh_connection:subsystem(Cm, Channel, "sftp", ?TIMEOUT),
+ success = ssh_connection:subsystem(Cm, Channel, "sftp", ?SSH_TIMEOUT),
ProtocolVer = case atom_to_list(TestCase) of
"ver3_" ++ _ ->
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index 3920a1c592..1df55834b1 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -42,7 +42,9 @@ suite() ->
all() ->
[default_tree, sshc_subtree, sshd_subtree, sshd_subtree_profile,
- killed_acceptor_restarts].
+ killed_acceptor_restarts,
+ shell_channel_tree
+ ].
groups() ->
[].
@@ -199,7 +201,7 @@ killed_acceptor_restarts(Config) ->
Port2 = ssh_test_lib:daemon_port(DaemonPid2),
true = (Port /= Port2),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
{ok,[{AccPid,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
{ok,[{AccPid2,ListenAddr,Port2}]} = acceptor_pid(DaemonPid2),
@@ -216,11 +218,14 @@ killed_acceptor_restarts(Config) ->
%% Make acceptor restart:
exit(AccPid, kill),
+ ?wait_match(undefined, process_info(AccPid)),
%% Check it is a new acceptor:
- {ok,[{AccPid1,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
- true = (AccPid /= AccPid1),
- true = (AccPid2 /= AccPid1),
+ ?wait_match({ok,[{AccPid1,ListenAddr,Port}]}, AccPid1=/=AccPid,
+ acceptor_pid(DaemonPid),
+ AccPid1,
+ 500, 30),
+ AccPid1 =/= AccPid2,
%% Connect second client and check it is alive:
{ok,C2} = ssh:connect("localhost", Port, [{silently_accept_hosts, true},
@@ -230,21 +235,113 @@ killed_acceptor_restarts(Config) ->
{user_dir, UserDir}]),
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
%% Check first client is still alive:
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
ok = ssh:stop_daemon(DaemonPid2),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid2), 1000, 30),
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
ok = ssh:stop_daemon(DaemonPid),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid), 1000, 30),
{error,closed} = ssh:connection_info(C1,[client_version]),
{error,closed} = ssh:connection_info(C2,[client_version]).
+
+%%-------------------------------------------------------------------------
+shell_channel_tree(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ TimeoutShell =
+ fun() ->
+ io:format("TimeoutShell started!~n",[]),
+ timer:sleep(5000),
+ ct:log("~p TIMEOUT!",[self()])
+ end,
+ {Daemon, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {shell, fun(_User) ->
+ spawn(TimeoutShell)
+ end
+ }
+ ]),
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ [ChannelSup|_] = Sups0 = chk_empty_con_daemon(Daemon),
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:shell(ConnectionRef,ChannelId0),
+
+ ?wait_match([{_, GroupPid,worker,[ssh_channel]}],
+ supervisor:which_children(ChannelSup),
+ [GroupPid]),
+ {links,GroupLinks} = erlang:process_info(GroupPid, links),
+ [ShellPid] = GroupLinks--[ChannelSup],
+ ct:log("GroupPid = ~p, ShellPid = ~p",[GroupPid,ShellPid]),
+
+ receive
+ {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"TimeoutShell started!\r\n">>}} ->
+ receive
+ %%---- wait for the subsystem to terminate
+ {ssh_cm,ConnectionRef,{closed,ChannelId0}} ->
+ ct:log("Subsystem terminated",[]),
+ case {chk_empty_con_daemon(Daemon),
+ process_info(GroupPid),
+ process_info(ShellPid)} of
+ {Sups0, undefined, undefined} ->
+ %% SUCCESS
+ ssh:stop_daemon(Daemon);
+ {Sups0, _, undefined} ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Group proc lives!");
+ {Sups0, undefined, _} ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Shell proc lives!");
+ _ ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Sup tree changed!")
+ end
+ after 10000 ->
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Daemon),
+ ct:fail("CLI Timeout")
+ end
+ after 10000 ->
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Daemon),
+ ct:fail("CLI Timeout")
+ end.
+
+
+chk_empty_con_daemon(Daemon) ->
+ ?wait_match([{_,SubSysSup, supervisor,[ssh_subsystem_sup]},
+ {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}],
+ supervisor:which_children(Daemon),
+ [SubSysSup,AccSup]),
+ ?wait_match([{{server,ssh_connection_sup, _,_},
+ ConnectionSup, supervisor,
+ [ssh_connection_sup]},
+ {{server,ssh_channel_sup,_ ,_},
+ ChannelSup,supervisor,
+ [ssh_channel_sup]}],
+ supervisor:which_children(SubSysSup),
+ [ConnectionSup,ChannelSup]),
+ ?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}],
+ supervisor:which_children(AccSup)),
+ ?wait_match([{_, _, worker,[ssh_connection_handler]}],
+ supervisor:which_children(ConnectionSup)),
+ ?wait_match([], supervisor:which_children(ChannelSup)),
+ [ChannelSup, ConnectionSup, SubSysSup, AccSup].
+
%%-------------------------------------------------------------------------
%% Help functions
%%-------------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 7b273fecef..57ae2dbac2 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -28,9 +28,7 @@
-include_lib("public_key/include/public_key.hrl").
-include_lib("common_test/include/ct.hrl").
-include_lib("ssh/src/ssh_transport.hrl").
-
-
--define(TIMEOUT, 50000).
+-include("ssh_test_lib.hrl").
%%%----------------------------------------------------------------
connect(Port, Options) when is_integer(Port) ->
@@ -58,7 +56,9 @@ daemon(Host, Port, Options) ->
ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
case ssh:daemon(Host, Port, Options) of
{ok, Pid} ->
- {ok,L} = ssh:daemon_info(Pid),
+ R = ssh:daemon_info(Pid),
+ ct:log("~p:~p ssh:daemon_info(~p) ->~n ~p",[?MODULE,?LINE,Pid,R]),
+ {ok,L} = R,
ListenPort = proplists:get_value(port, L),
ListenIP = proplists:get_value(ip, L),
{Pid, ListenIP, ListenPort};
@@ -201,15 +201,17 @@ init_io_server(TestCase) ->
loop_io_server(TestCase, Buff0) ->
receive
- {input, TestCase, Line} ->
+ {input, TestCase, Line} = _INP ->
+ %%ct:log("io_server ~p:~p ~p got ~p",[?MODULE,?LINE,self(),_INP]),
loop_io_server(TestCase, Buff0 ++ [Line]);
- {io_request, From, ReplyAs, Request} ->
+ {io_request, From, ReplyAs, Request} = _REQ->
+ %%ct:log("io_server ~p:~p ~p got ~p",[?MODULE,?LINE,self(),_REQ]),
{ok, Reply, Buff} = io_request(Request, TestCase, From,
ReplyAs, Buff0),
io_reply(From, ReplyAs, Reply),
loop_io_server(TestCase, Buff);
{'EXIT',_, _} = _Exit ->
-%% ct:log("ssh_test_lib:loop_io_server/2 got ~p",[_Exit]),
+ ct:log("ssh_test_lib:loop_io_server/2 got ~p",[_Exit]),
ok
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
@@ -404,7 +406,7 @@ setup_ecdsa(Size, DataDir, UserDir) ->
file:copy(filename:join(DataDir, "ssh_host_ecdsa_key"++Size++".pub"), filename:join(System, "ssh_host_ecdsa_key.pub")),
ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
setup_ecdsa_known_host(Size, System, UserDir),
- setup_ecdsa_auth_keys(Size, UserDir, UserDir).
+ setup_ecdsa_auth_keys(Size, DataDir, UserDir).
clean_dsa(UserDir) ->
del_dirs(filename:join(UserDir, "system")),
@@ -438,6 +440,29 @@ setup_rsa_pass_pharse(DataDir, UserDir, Phrase) ->
setup_rsa_known_host(DataDir, UserDir),
setup_rsa_auth_keys(DataDir, UserDir).
+setup_ecdsa_pass_phrase(Size, DataDir, UserDir, Phrase) ->
+ try
+ {ok, KeyBin} =
+ case file:read_file(F=filename:join(DataDir, "id_ecdsa"++Size)) of
+ {error,E} ->
+ ct:log("Failed (~p) to read ~p~nFiles: ~p", [E,F,file:list_dir(DataDir)]),
+ file:read_file(filename:join(DataDir, "id_ecdsa"));
+ Other ->
+ Other
+ end,
+ setup_pass_pharse(KeyBin, filename:join(UserDir, "id_ecdsa"), Phrase),
+ System = filename:join(UserDir, "system"),
+ file:make_dir(System),
+ file:copy(filename:join(DataDir, "ssh_host_ecdsa_key"++Size), filename:join(System, "ssh_host_ecdsa_key")),
+ file:copy(filename:join(DataDir, "ssh_host_ecdsa_key"++Size++".pub"), filename:join(System, "ssh_host_ecdsa_key.pub")),
+ setup_ecdsa_known_host(Size, System, UserDir),
+ setup_ecdsa_auth_keys(Size, DataDir, UserDir)
+ of
+ _ -> true
+ catch
+ _:_ -> false
+ end.
+
setup_pass_pharse(KeyBin, OutFile, Phrase) ->
[{KeyType, _,_} = Entry0] = public_key:pem_decode(KeyBin),
Key = public_key:pem_entry_decode(Entry0),
@@ -489,8 +514,15 @@ setup_rsa_auth_keys(Dir, UserDir) ->
PKey = #'RSAPublicKey'{publicExponent = E, modulus = N},
setup_auth_keys([{ PKey, [{comment, "Test"}]}], UserDir).
-setup_ecdsa_auth_keys(_Size, Dir, UserDir) ->
- {ok, Pem} = file:read_file(filename:join(Dir, "id_ecdsa")),
+setup_ecdsa_auth_keys(Size, Dir, UserDir) ->
+ {ok, Pem} =
+ case file:read_file(F=filename:join(Dir, "id_ecdsa"++Size)) of
+ {error,E} ->
+ ct:log("Failed (~p) to read ~p~nFiles: ~p", [E,F,file:list_dir(Dir)]),
+ file:read_file(filename:join(Dir, "id_ecdsa"));
+ Other ->
+ Other
+ end,
ECDSA = public_key:pem_entry_decode(hd(public_key:pem_decode(Pem))),
#'ECPrivateKey'{publicKey = Q,
parameters = Param = {namedCurve,_Id0}} = ECDSA,
@@ -570,9 +602,9 @@ check_ssh_client_support2(P) ->
{P, {data, _A}} ->
check_ssh_client_support2(P);
{P, {exit_status, E}} ->
+ ct:log("~p:~p exit_status:~n~p",[?MODULE,?LINE,E]),
E
after 5000 ->
-
ct:log("Openssh command timed out ~n"),
-1
end.
@@ -622,14 +654,14 @@ default_algorithms(sshc, DaemonOptions) ->
{hostport,Srvr,{_Host,Port}} ->
spawn(fun()-> os:cmd(lists:concat(["ssh -o \"StrictHostKeyChecking no\" -p ",Port," localhost"])) end)
after ?TIMEOUT ->
- ct:fail("No server respons 1")
+ ct:fail("No server respons (timeout) 1")
end,
receive
{result,Srvr,L} ->
L
after ?TIMEOUT ->
- ct:fail("No server respons 2")
+ ct:fail("No server respons (timeout) 2")
end.
run_fake_ssh({ok,InitialState}) ->
@@ -743,12 +775,12 @@ ssh_type1() ->
not_found;
Path ->
ct:log("~p:~p Found \"ssh\" at ~p",[?MODULE,?LINE,Path]),
- case os:cmd("ssh -V") of
+ case installed_ssh_version(timeout) of
Version = "OpenSSH" ++ _ ->
ct:log("~p:~p Found OpenSSH ~p",[?MODULE,?LINE,Version]),
openSSH;
- Str ->
- ct:log("ssh client ~p is unknown",[Str]),
+ Other ->
+ ct:log("ssh client ~p is unknown",[Other]),
unknown
end
end
@@ -758,6 +790,20 @@ ssh_type1() ->
not_found
end.
+installed_ssh_version(TimeoutReturn) ->
+ Parent = self(),
+ Pid = spawn(fun() ->
+ Parent ! {open_ssh_version, os:cmd("ssh -V")}
+ end),
+ receive
+ {open_ssh_version, V} ->
+ V
+ after ?TIMEOUT ->
+ exit(Pid, kill),
+ TimeoutReturn
+ end.
+
+
algo_intersection([], _) -> [];
diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl
index 54c93b7e87..4b6579bd71 100644
--- a/lib/ssh/test/ssh_test_lib.hrl
+++ b/lib/ssh/test/ssh_test_lib.hrl
@@ -1,4 +1,9 @@
%%-------------------------------------------------------------------------
+%% Timeout time in ms
+%%-------------------------------------------------------------------------
+-define(TIMEOUT, 27000).
+
+%%-------------------------------------------------------------------------
%% Check for usable crypt
%%-------------------------------------------------------------------------
-define(CHECK_CRYPTO(Available),
@@ -11,12 +16,12 @@
%%-------------------------------------------------------------------------
%% Help macro
%%-------------------------------------------------------------------------
--define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+-define(wait_match(Pattern, Guard, FunctionCall, Bind, Timeout, Ntries),
Bind =
(fun() ->
F = fun(N, F1) ->
case FunctionCall of
- Pattern -> Bind;
+ Pattern when Guard -> Bind;
_ when N>0 ->
ct:pal("Must sleep ~p ms at ~p:~p",[Timeout,?MODULE,?LINE]),
timer:sleep(Timeout),
@@ -29,6 +34,9 @@
end)()
).
+-define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+ ?wait_match(Pattern, true, FunctionCall, Bind, Timeout, Ntries)).
+
-define(wait_match(Pattern, FunctionCall, Timeout, Ntries), ?wait_match(Pattern, FunctionCall, ok, Timeout, Ntries)).
-define(wait_match(Pattern, FunctionCall, Bind), ?wait_match(Pattern, FunctionCall, Bind, 500, 10) ).
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index 4d6aa93d4e..9df404d7ed 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -27,7 +27,6 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--define(TIMEOUT, 50000).
-define(SSH_DEFAULT_PORT, 22).
-define(REKEY_DATA_TMO, 65000).
@@ -49,19 +48,9 @@ all() ->
end.
groups() ->
- [{erlang_client, [], [erlang_shell_client_openssh_server,
- erlang_client_openssh_server_exec_compressed,
- erlang_client_openssh_server_setenv,
- erlang_client_openssh_server_publickey_dsa,
- erlang_client_openssh_server_publickey_rsa,
- erlang_client_openssh_server_password,
- erlang_client_openssh_server_kexs,
- erlang_client_openssh_server_nonexistent_subsystem,
- erlang_client_openssh_server_renegotiate
+ [{erlang_client, [], [erlang_shell_client_openssh_server
]},
- {erlang_server, [], [erlang_server_openssh_client_public_key_dsa,
- erlang_server_openssh_client_public_key_rsa,
- erlang_server_openssh_client_renegotiate
+ {erlang_server, [], [erlang_server_openssh_client_renegotiate
]}
].
@@ -69,7 +58,7 @@ init_per_suite(Config) ->
?CHECK_CRYPTO(
case gen_tcp:connect("localhost", 22, []) of
{error,econnrefused} ->
- {skip,"No openssh deamon"};
+ {skip,"No openssh deamon (econnrefused)"};
_ ->
ssh_test_lib:openssh_sanity_check(Config)
end
@@ -101,15 +90,6 @@ end_per_group(_, Config) ->
Config.
-init_per_testcase(erlang_server_openssh_client_public_key_dsa, Config) ->
- chk_key(sshc, 'ssh-dss', ".ssh/id_dsa", Config);
-init_per_testcase(erlang_server_openssh_client_public_key_rsa, Config) ->
- chk_key(sshc, 'ssh-rsa', ".ssh/id_rsa", Config);
-init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) ->
- chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config);
-init_per_testcase(erlang_client_openssh_server_publickey_rsa, Config) ->
- chk_key(sshd, 'ssh-rsa', ".ssh/id_rsa", Config);
-
init_per_testcase(erlang_server_openssh_client_renegotiate, Config) ->
case os:type() of
{unix,_} -> ssh:start(), Config;
@@ -123,27 +103,6 @@ end_per_testcase(_TestCase, _Config) ->
ssh:stop(),
ok.
-
-chk_key(Pgm, Name, File, Config) ->
- case ssh_test_lib:openssh_supports(Pgm, public_key, Name) of
- false ->
- {skip,lists:concat(["openssh client does not support ",Name])};
- true ->
- {ok,[[Home]]} = init:get_argument(home),
- KeyFile = filename:join(Home, File),
- case file:read_file(KeyFile) of
- {ok, Pem} ->
- case public_key:pem_decode(Pem) of
- [{_,_, not_encrypted}] ->
- init_per_testcase('__default__',Config);
- _ ->
- {skip, {error, "Has pass phrase can not be used by automated test case"}}
- end;
- _ ->
- {skip, lists:concat(["no ~/",File])}
- end
- end.
-
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -161,219 +120,6 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) ->
receive_logout(),
receive_normal_exit(Shell).
-%--------------------------------------------------------------------
-erlang_client_openssh_server_exec() ->
- [{doc, "Test api function ssh_connection:exec"}].
-
-erlang_client_openssh_server_exec(Config) when is_list(Config) ->
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
- {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId0,
- "echo testing", infinity),
- Data0 = {ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(Data0) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}}
- = ExitStatus0} ->
- ct:log("0: Collected data ~p", [ExitStatus0]),
- ssh_test_lib:receive_exec_result(Data0,
- ConnectionRef, ChannelId0);
- Other0 ->
- ct:fail(Other0)
- end,
-
- {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId1,
- "echo testing1", infinity),
- Data1 = {ssh_cm, ConnectionRef, {data, ChannelId1, 0, <<"testing1\n">>}},
- case ssh_test_lib:receive_exec_result(Data1) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId1, 0}}
- = ExitStatus1} ->
- ct:log("0: Collected data ~p", [ExitStatus1]),
- ssh_test_lib:receive_exec_result(Data1,
- ConnectionRef, ChannelId1);
- Other1 ->
- ct:fail(Other1)
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_exec_compressed() ->
- [{doc, "Test that compression option works"}].
-
-erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) ->
- CompressAlgs = [zlib, '[email protected]',none],
- case ssh_test_lib:ssh_supports(CompressAlgs, compression) of
- {false,L} ->
- {skip, io_lib:format("~p compression is not supported",[L])};
-
- true ->
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false},
- {preferred_algorithms,
- [{compression,CompressAlgs}]}]),
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId,
- "echo testing", infinity),
- Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(Data) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {exit_status, ChannelId, 0}} = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(Data, ConnectionRef, ChannelId);
- Other ->
- ct:fail(Other)
- end
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_kexs() ->
- [{doc, "Test that we can connect with different KEXs."}].
-
-erlang_client_openssh_server_kexs(Config) when is_list(Config) ->
- KexAlgos = try proplists:get_value(kex, proplists:get_value(common_algs,Config))
- catch _:_ -> []
- end,
- comment(KexAlgos),
- case KexAlgos of
- [] -> {skip, "No common kex algorithms"};
- _ ->
- Success =
- lists:foldl(
- fun(Kex, Acc) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false},
- {preferred_algorithms,
- [{kex,[Kex]}]}]),
-
- {ok, ChannelId} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- success =
- ssh_connection:exec(ConnectionRef, ChannelId,
- "echo testing", infinity),
-
- ExpectedData = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(ExpectedData) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
- Acc;
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {exit_status, ChannelId, 0}} = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(ExpectedData, ConnectionRef, ChannelId),
- Acc;
- Other ->
- ct:log("~p failed: ~p",[Kex,Other]),
- false
- end
- end, true, KexAlgos),
- case Success of
- true ->
- ok;
- false ->
- {fail, "Kex failed for one or more algos"}
- end
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_setenv() ->
- [{doc, "Test api function ssh_connection:setenv"}].
-
-erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
- {ok, ChannelId} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- Env = case ssh_connection:setenv(ConnectionRef, ChannelId,
- "ENV_TEST", "testing_setenv",
- infinity) of
- success ->
- <<"tesing_setenv\n">>;
- failure ->
- <<"\n">>
- end,
- success = ssh_connection:exec(ConnectionRef, ChannelId,
- "echo $ENV_TEST", infinity),
- Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, Env}},
- case ssh_test_lib:receive_exec_result(Data) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {data,0,1, UnxpectedData}}} ->
- %% Some os may return things as
- %% ENV_TEST: Undefined variable.\n"
- ct:log("UnxpectedData: ~p", [UnxpectedData]),
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId, 0}}
- = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(Data,
- ConnectionRef, ChannelId);
- Other ->
- ct:fail(Other)
- end.
-
-%%--------------------------------------------------------------------
-
-%% setenv not meaningfull on erlang ssh daemon!
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_publickey_rsa(Config) ->
- erlang_client_openssh_server_publickey_X(Config, 'ssh-rsa').
-
-erlang_client_openssh_server_publickey_dsa(Config) ->
- erlang_client_openssh_server_publickey_X(Config, 'ssh-dss').
-
-
-erlang_client_openssh_server_publickey_X(Config, Alg) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{pref_public_key_algs, [Alg]},
- {user_interaction, false},
- {auth_methods, "publickey"},
- silently_accept_hosts]),
- {ok, Channel} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, Channel),
- ok = ssh:close(ConnectionRef).
-
-%%--------------------------------------------------------------------
-erlang_server_openssh_client_public_key_dsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
-erlang_server_openssh_client_public_key_dsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, 'ssh-dss').
-
-erlang_server_openssh_client_public_key_rsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
-erlang_server_openssh_client_public_key_rsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, 'ssh-rsa').
-
-
-erlang_server_openssh_client_public_key_X(Config, Alg) ->
- SystemDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- KnownHosts = filename:join(PrivDir, "known_hosts"),
- {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
- {preferred_algorithms,[{public_key, [Alg]}]},
- {auth_methods, "publickey"},
- {failfun, fun ssh_test_lib:failfun/2}]),
- ct:sleep(500),
-
- Cmd = ssh_test_lib:open_sshc_cmd(Host, Port,
- [" -o UserKnownHostsFile=", KnownHosts,
- " -o StrictHostKeyChecking=no"],
- "1+1."),
- OpenSsh = ssh_test_lib:open_port({spawn, Cmd}),
- ssh_test_lib:rcv_expected({data,<<"2\n">>}, OpenSsh, ?TIMEOUT),
- ssh:stop_daemon(Pid).
-
%%--------------------------------------------------------------------
%% Test that the Erlang/OTP server can renegotiate with openSSH
erlang_server_openssh_client_renegotiate(Config) ->
@@ -431,108 +177,6 @@ erlang_server_openssh_client_renegotiate(Config) ->
end.
%%--------------------------------------------------------------------
-erlang_client_openssh_server_renegotiate(_Config) ->
- process_flag(trap_exit, true),
- IO = ssh_test_lib:start_io_server(),
- Ref = make_ref(),
- Parent = self(),
-
- Shell =
- spawn_link(
- fun() ->
- Host = ssh_test_lib:hostname(),
- Options = [{user_interaction, false},
- {silently_accept_hosts,true}],
- group_leader(IO, self()),
- {ok, ConnRef} = ssh:connect(Host, ?SSH_DEFAULT_PORT, Options),
- ct:log("Parent = ~p, IO = ~p, Shell = ~p, ConnRef = ~p~n",[Parent, IO, self(), ConnRef]),
- case ssh_connection:session_channel(ConnRef, infinity) of
- {ok,ChannelId} ->
- success = ssh_connection:ptty_alloc(ConnRef, ChannelId, []),
- Args = [{channel_cb, ssh_shell},
- {init_args,[ConnRef, ChannelId]},
- {cm, ConnRef}, {channel_id, ChannelId}],
- {ok, State} = ssh_channel:init([Args]),
- Parent ! {ok, Ref, ConnRef},
- ssh_channel:enter_loop(State);
- Error ->
- Parent ! {error, Ref, Error}
- end,
- receive
- nothing -> ok
- end
- end),
-
- receive
- {error, Ref, Error} ->
- ct:fail("Error=~p",[Error]);
- {ok, Ref, ConnectionRef} ->
- IO ! {input, self(), "echo Hej1\n"},
- receive_data("Hej1", ConnectionRef),
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
- ssh_connection_handler:renegotiate(ConnectionRef),
- IO ! {input, self(), "echo Hej2\n"},
- receive_data("Hej2", ConnectionRef),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
- IO ! {input, self(), "exit\n"},
- receive_logout(),
- receive_normal_exit(Shell),
- true = (Kex1 =/= Kex2)
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_password() ->
- [{doc, "Test client password option"}].
-erlang_client_openssh_server_password(Config) when is_list(Config) ->
- %% to make sure we don't public-key-auth
- UserDir = proplists:get_value(data_dir, Config),
- {error, Reason0} =
- ssh:connect(any, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
-
- ct:log("Test of user foo that does not exist. "
- "Error msg: ~p~n", [Reason0]),
-
- User = string:strip(os:cmd("whoami"), right, $\n),
-
- case length(string:tokens(User, " ")) of
- 1 ->
- {error, Reason1} =
- ssh:connect(any, ?SSH_DEFAULT_PORT,
- [{silently_accept_hosts, true},
- {user, User},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
- ct:log("Test of wrong Pasword. "
- "Error msg: ~p~n", [Reason1]);
- _ ->
- ct:log("Whoami failed reason: ~n", [])
- end.
-
-%%--------------------------------------------------------------------
-
-erlang_client_openssh_server_nonexistent_subsystem() ->
- [{doc, "Test client password option"}].
-erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config) ->
-
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{user_interaction, false},
- silently_accept_hosts]),
-
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
-
- failure = ssh_connection:subsystem(ConnectionRef, ChannelId, "foo", infinity).
-
-%%--------------------------------------------------------------------
-%
-%% Not possible to send password with openssh without user interaction
-%%
-%%--------------------------------------------------------------------
-%%--------------------------------------------------------------------
%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
receive_data(Data, Conn) ->
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 59775d2d7f..6aaa22a6b4 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.6.2
+SSH_VSN = 4.6.5
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index 669062779e..f9128e8e45 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2015. All Rights Reserved.
+# Copyright Ericsson AB 1999-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -38,25 +38,25 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = refman.xml
-XML_REF3_FILES = ssl.xml ssl_crl_cache.xml ssl_crl_cache_api.xml ssl_session_cache_api.xml
+XML_REF3_FILES = ssl.xml ssl_crl_cache.xml ssl_crl_cache_api.xml ssl_session_cache_api.xml
XML_REF6_FILES = ssl_app.xml
-XML_PART_FILES = release_notes.xml usersguide.xml
+XML_PART_FILES = usersguide.xml
XML_CHAPTER_FILES = \
+ ssl_introduction.xml \
ssl_protocol.xml \
using_ssl.xml \
- pkix_certs.xml \
ssl_distribution.xml \
notes.xml
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-GIF_FILES = warning.gif
+GIF_FILES =
-PS_FILES =
+PS_FILES =
XML_FLAGS += -defs cite cite.defs -booksty otpA4
@@ -81,10 +81,10 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -110,11 +110,11 @@ man: $(MAN3_FILES) $(MAN6_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/ssl/doc/src/fascicules.xml b/lib/ssl/doc/src/fascicules.xml
deleted file mode 100644
index 7a60e8dd1f..0000000000
--- a/lib/ssl/doc/src/fascicules.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="usersguide" href="usersguide_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="refman" href="refman_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="release_notes" href="release_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
-
diff --git a/lib/ssl/doc/src/note.gif b/lib/ssl/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/ssl/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index a8450c2630..bdf8711b2f 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -27,9 +27,62 @@
</header>
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 8.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Packet options cannot be supported for unreliable
+ transports, that is, packet option for DTLS over udp will
+ not be supported.</p>
+ <p>
+ Own Id: OTP-14664</p>
+ </item>
+ <item>
+ <p>
+ Ensure data delivery before close if possible. This fix
+ is related to fix in PR-1479.</p>
+ <p>
+ Own Id: OTP-14794</p>
+ </item>
+ </list>
+ </section>
-<section><title>SSL 8.2.2</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The crypto API is extended to use private/public keys
+ stored in an Engine for sign/verify or encrypt/decrypt
+ operations.</p>
+ <p>
+ The ssl application provides an API to use this new
+ engine concept in TLS.</p>
+ <p>
+ Own Id: OTP-14448</p>
+ </item>
+ <item>
+ <p>
+ Implemented renegotiation for DTLS</p>
+ <p>
+ Own Id: OTP-14563</p>
+ </item>
+ <item>
+ <p>
+ A new command line option <c>-ssl_dist_optfile</c> has
+ been added to facilitate specifying the many options
+ needed when using SSL as the distribution protocol.</p>
+ <p>
+ Own Id: OTP-14657</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -89,7 +142,6 @@
</list>
</section>
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -253,9 +305,74 @@
</item>
</list>
</section>
+</section>
+<section><title>SSL 8.1.3.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix alert handling so that unexpected messages are logged
+ and alerted correctly</p>
+ <p>
+ Own Id: OTP-14929</p>
+ </item>
+ </list>
+ </section>
</section>
+<section><title>SSL 8.1.3.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> An erlang TLS server configured with cipher suites
+ using rsa key exchange, may be vulnerable to an Adaptive
+ Chosen Ciphertext attack (AKA Bleichenbacher attack)
+ against RSA, which when exploited, may result in
+ plaintext recovery of encrypted messages and/or a
+ Man-in-the-middle (MiTM) attack, despite the attacker not
+ having gained access to the server’s private key
+ itself. <url
+ href="https://nvd.nist.gov/vuln/detail/CVE-2017-1000385">CVE-2017-1000385</url>
+ </p> <p> Exploiting this vulnerability to perform
+ plaintext recovery of encrypted messages will, in most
+ practical cases, allow an attacker to read the plaintext
+ only after the session has completed. Only TLS sessions
+ established using RSA key exchange are vulnerable to this
+ attack. </p> <p> Exploiting this vulnerability to conduct
+ a MiTM attack requires the attacker to complete the
+ initial attack, which may require thousands of server
+ requests, during the handshake phase of the targeted
+ session within the window of the configured handshake
+ timeout. This attack may be conducted against any TLS
+ session using RSA signatures, but only if cipher suites
+ using RSA key exchange are also enabled on the server.
+ The limited window of opportunity, limitations in
+ bandwidth, and latency make this attack significantly
+ more difficult to execute. </p> <p> RSA key exchange is
+ enabled by default although least prioritized if server
+ order is honored. For such a cipher suite to be chosen it
+ must also be supported by the client and probably the
+ only shared cipher suite. </p> <p> Captured TLS sessions
+ encrypted with ephemeral cipher suites (DHE or ECDHE) are
+ not at risk for subsequent decryption due to this
+ vulnerability. </p> <p> As a workaround if default cipher
+ suite configuration was used you can configure the server
+ to not use vulnerable suites with the ciphers option like
+ this: </p> <c> {ciphers, [Suite || Suite &lt;-
+ ssl:cipher_suites(), element(1,Suite) =/= rsa]} </c> <p>
+ that is your code will look somethingh like this: </p>
+ <c> ssl:listen(Port, [{ciphers, [Suite || Suite &lt;-
+ ssl:cipher_suites(), element(1,S) =/= rsa]} | Options]).
+ </c> <p> Thanks to Hanno Böck, Juraj Somorovsky and
+ Craig Young for reporting this vulnerability. </p>
+ <p>
+ Own Id: OTP-14748</p>
+ </item>
+ </list>
+ </section>
+</section>
<section><title>SSL 8.1.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -634,6 +751,60 @@
</section>
+ <section><title>SSL 7.3.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> An erlang TLS server configured with cipher suites
+ using rsa key exchange, may be vulnerable to an Adaptive
+ Chosen Ciphertext attack (AKA Bleichenbacher attack)
+ against RSA, which when exploited, may result in
+ plaintext recovery of encrypted messages and/or a
+ Man-in-the-middle (MiTM) attack, despite the attacker not
+ having gained access to the server’s private key
+ itself. <url
+ href="https://nvd.nist.gov/vuln/detail/CVE-2017-1000385">CVE-2017-1000385</url>
+ </p> <p> Exploiting this vulnerability to perform
+ plaintext recovery of encrypted messages will, in most
+ practical cases, allow an attacker to read the plaintext
+ only after the session has completed. Only TLS sessions
+ established using RSA key exchange are vulnerable to this
+ attack. </p> <p> Exploiting this vulnerability to conduct
+ a MiTM attack requires the attacker to complete the
+ initial attack, which may require thousands of server
+ requests, during the handshake phase of the targeted
+ session within the window of the configured handshake
+ timeout. This attack may be conducted against any TLS
+ session using RSA signatures, but only if cipher suites
+ using RSA key exchange are also enabled on the server.
+ The limited window of opportunity, limitations in
+ bandwidth, and latency make this attack significantly
+ more difficult to execute. </p> <p> RSA key exchange is
+ enabled by default although least prioritized if server
+ order is honored. For such a cipher suite to be chosen it
+ must also be supported by the client and probably the
+ only shared cipher suite. </p> <p> Captured TLS sessions
+ encrypted with ephemeral cipher suites (DHE or ECDHE) are
+ not at risk for subsequent decryption due to this
+ vulnerability. </p> <p> As a workaround if default cipher
+ suite configuration was used you can configure the server
+ to not use vulnerable suites with the ciphers option like
+ this: </p> <c> {ciphers, [Suite || Suite &lt;-
+ ssl:cipher_suites(), element(1,Suite) =/= rsa]} </c> <p>
+ that is your code will look somethingh like this: </p>
+ <c> ssl:listen(Port, [{ciphers, [Suite || Suite &lt;-
+ ssl:cipher_suites(), element(1,S) =/= rsa]} | Options]).
+ </c> <p> Thanks to Hanno Böck, Juraj Somorovsky and
+ Craig Young for reporting this vulnerability. </p>
+ <p>
+ Own Id: OTP-14748</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
<section><title>SSL 7.3.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -663,7 +834,59 @@
</list>
</section>
+ <section><title>SSL 7.3.3.0.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> An erlang TLS server configured with cipher suites
+ using rsa key exchange, may be vulnerable to an Adaptive
+ Chosen Ciphertext attack (AKA Bleichenbacher attack)
+ against RSA, which when exploited, may result in
+ plaintext recovery of encrypted messages and/or a
+ Man-in-the-middle (MiTM) attack, despite the attacker not
+ having gained access to the server’s private key
+ itself. <url
+ href="https://nvd.nist.gov/vuln/detail/CVE-2017-1000385">CVE-2017-1000385</url>
+ </p> <p> Exploiting this vulnerability to perform
+ plaintext recovery of encrypted messages will, in most
+ practical cases, allow an attacker to read the plaintext
+ only after the session has completed. Only TLS sessions
+ established using RSA key exchange are vulnerable to this
+ attack. </p> <p> Exploiting this vulnerability to conduct
+ a MiTM attack requires the attacker to complete the
+ initial attack, which may require thousands of server
+ requests, during the handshake phase of the targeted
+ session within the window of the configured handshake
+ timeout. This attack may be conducted against any TLS
+ session using RSA signatures, but only if cipher suites
+ using RSA key exchange are also enabled on the server.
+ The limited window of opportunity, limitations in
+ bandwidth, and latency make this attack significantly
+ more difficult to execute. </p> <p> RSA key exchange is
+ enabled by default although least prioritized if server
+ order is honored. For such a cipher suite to be chosen it
+ must also be supported by the client and probably the
+ only shared cipher suite. </p> <p> Captured TLS sessions
+ encrypted with ephemeral cipher suites (DHE or ECDHE) are
+ not at risk for subsequent decryption due to this
+ vulnerability. </p> <p> As a workaround if default cipher
+ suite configuration was used you can configure the server
+ to not use vulnerable suites with the ciphers option like
+ this: </p> <c> {ciphers, [Suite || Suite &lt;-
+ ssl:cipher_suites(), element(1,Suite) =/= rsa]} </c> <p>
+ that is your code will look somethingh like this: </p>
+ <c> ssl:listen(Port, [{ciphers, [Suite || Suite &lt;-
+ ssl:cipher_suites(), element(1,S) =/= rsa]} | Options]).
+ </c> <p> Thanks to Hanno Böck, Juraj Somorovsky and
+ Craig Young for reporting this vulnerability. </p>
+ <p>
+ Own Id: OTP-14748</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
<section><title>Improvements and New Features</title>
<list>
<item>
diff --git a/lib/ssl/doc/src/pkix_certs.xml b/lib/ssl/doc/src/pkix_certs.xml
deleted file mode 100644
index f365acef4d..0000000000
--- a/lib/ssl/doc/src/pkix_certs.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2003</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>PKIX Certificates</title>
- <prepared>UAB/F/P Peter H&ouml;gfeldt</prepared>
- <docno></docno>
- <date>2003-06-09</date>
- <rev>A</rev>
- <file>pkix_certs.xml</file>
- </header>
-
- <section>
- <title>Introduction to Certificates</title>
- <p>Certificates were originally defined by ITU (CCITT) and the latest
- definitions are described in <cite id="X.509"></cite>, but those definitions
- are (as always) not working.
- </p>
- <p>Working certificate definitions for the Internet Community are found
- in the the PKIX RFCs <cite id="rfc3279"></cite> and <cite id="rfc3280"></cite>.
- The parsing of certificates in the Erlang/OTP SSL application is
- based on those RFCS.
- </p>
- <p>Certificates are defined in terms of ASN.1 (<cite id="X.680"></cite>).
- For an introduction to ASN.1 see <url href="http://asn1.elibel.tm.fr/">ASN.1 Information Site</url>.
- </p>
- </section>
-
- <section>
- <title>PKIX Certificates</title>
- <p>Certificate handling is now handled by the <c>public_key</c> application.</p>
- <p>
- DER encoded certificates returned by <c>ssl:peercert/1</c> can for example
- be decoded by the <c>public_key:pkix_decode_cert/2</c> function.
- </p>
- </section>
-</chapter>
-
-
diff --git a/lib/ssl/doc/src/release_notes.xml b/lib/ssl/doc/src/release_notes.xml
deleted file mode 100644
index 2e263c69a7..0000000000
--- a/lib/ssl/doc/src/release_notes.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1999</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>SSL Release Notes</title>
- <prepared>Peter H&ouml;gfeldt</prepared>
- <docno></docno>
- <date>2003-05-26</date>
- <rev>A</rev>
- <file>release_notes.sgml</file>
- </header>
- <description>
- <p>The SSL application provides secure communication over sockets.
- </p>
- <p>This product includes software developed by the OpenSSL Project for
- use in the OpenSSL Toolkit (http://www.openssl.org/).
- </p>
- <p>This product includes cryptographic software written by Eric Young
- </p>
- <p>This product includes software written by Tim Hudson
- </p>
- <p>For full OpenSSL and SSLeay license texts, see <seealso marker="licenses#licenses">Licenses</seealso>.
- </p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
-
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index e80fd59a7f..3db5aa19ac 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -69,7 +69,9 @@
<p><c>| {cert, public_key:der_encoded()}</c></p>
<p><c>| {certfile, path()}</c></p>
<p><c>| {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey'
- | 'PrivateKeyInfo', public_key:der_encoded()}}</c></p>
+ | 'PrivateKeyInfo', public_key:der_encoded()} |
+ #{algorithm := rsa | dss | ecdsa,
+ engine := crypto:engine_ref(), key_id := crypto:key_id(), password => crypto:password()}</c></p>
<p><c>| {keyfile, path()}</c></p>
<p><c>| {password, string()}</c></p>
<p><c>| {cacerts, [public_key:der_encoded()]}</c></p>
@@ -136,17 +138,20 @@
<tag><c>sslsocket() =</c></tag>
<item><p>opaque()</p></item>
- <tag><marker id="type-protocol"/><c>protocol() =</c></tag>
+ <tag><marker id="type-protocol"/><c>protocol_version() =</c></tag>
<item><p><c>sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'</c></p></item>
<tag><c>ciphers() =</c></tag>
- <item><p><c>= [ciphersuite()] | string()</c></p>
- <p>According to old API.</p></item>
+ <item><p><c>= [ciphersuite()]</c></p>
+ <p>Tuples and string formats accepted by versions
+ before ssl-8.2.4 will be converted for backwards compatibility</p></item>
<tag><c>ciphersuite() =</c></tag>
-
- <item><p><c>{key_exchange(), cipher(), MAC::hash()} |
- {key_exchange(), cipher(), MAC::hash(), PRF::hash()}</c></p></item>
+ <item><p><c>
+ #{key_exchange := key_exchange(),
+ cipher := cipher(),
+ mac := MAC::hash() | aead,
+ prf := PRF::hash() | default_prf} </c></p></item>
<tag><c>key_exchange()=</c></tag>
<item><p><c>rsa | dhe_dss | dhe_rsa | dh_anon | psk | dhe_psk
@@ -163,6 +168,12 @@
<tag><c>prf_random() =</c></tag>
<item><p><c>client_random | server_random</c></p></item>
+ <tag><c>cipher_filters() =</c></tag>
+ <item><p><c> [{key_exchange | cipher | mac | prf, algo_filter()}])</c></p></item>
+
+ <tag><c>algo_filter() =</c></tag>
+ <item><p>fun(key_exchange() | cipher() | hash() | aead | default_prf) -> true | false </p></item>
+
<tag><c>srp_param_type() =</c></tag>
<item><p><c>srp_1024 | srp_1536 | srp_2048 | srp_3072
| srp_4096 | srp_6144 | srp_8192</c></p></item>
@@ -201,9 +212,15 @@
<tag><c>{certfile, path()}</c></tag>
<item><p>Path to a file containing the user certificate.</p></item>
- <tag><c>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey'
- |'PrivateKeyInfo', public_key:der_encoded()}}</c></tag>
- <item><p>The DER-encoded user's private key. If this option
+ <tag>
+ <marker id="key_option_def"/>
+ <c>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey'
+ |'PrivateKeyInfo', public_key:der_encoded()} | #{algorithm := rsa | dss | ecdsa,
+ engine := crypto:engine_ref(), key_id := crypto:key_id(), password => crypto:password()}</c></tag>
+ <item><p>The DER-encoded user's private key or a map refering to a crypto
+ engine and its key reference that optionally can be password protected,
+ seealso <seealso marker="crypto:crypto#engine_load-4"> crypto:engine_load/4
+ </seealso> and <seealso marker="crypto:engine_load"> Crypto's Users Guide</seealso>. If this option
is supplied, it overrides option <c>keyfile</c>.</p></item>
<tag><c>{keyfile, path()}</c></tag>
@@ -448,7 +465,7 @@ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_valid
marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso>
with the selected CA as trusted anchor and the rest of the chain.</p></item>
- <tag><c>{versions, [protocol()]}</c></tag>
+ <tag><c>{versions, [protocol_version()]}</c></tag>
<item><p>TLS protocol versions supported by started clients and servers.
This option overrides the application environment option
<c>protocol_version</c>. If the environment option is not set, it defaults
@@ -821,14 +838,34 @@ fun(srp, Username :: string(), UserState :: term()) ->
</section>
<funcs>
+
+ <func>
+ <name>append_cipher_suites(Deferred, Suites) -> ciphers() </name>
+ <fsummary></fsummary>
+ <type>
+ <v>Deferred = ciphers() | cipher_filters() </v>
+ <v>Suites = ciphers() </v>
+ </type>
+ <desc><p>Make <c>Deferred</c> suites become the least preferred
+ suites, that is put them at the end of the cipher suite list
+ <c>Suites</c> after removing them from <c>Suites</c> if
+ present. <c>Deferred</c> may be a list of cipher suits or a
+ list of filters in which case the filters are use on <c>Suites</c> to
+ extract the Deferred cipher list.</p>
+ </desc>
+ </func>
+
<func>
<name>cipher_suites() -></name>
- <name>cipher_suites(Type) -> ciphers()</name>
+ <name>cipher_suites(Type) -> old_ciphers()</name>
<fsummary>Returns a list of supported cipher suites.</fsummary>
<type>
<v>Type = erlang | openssl | all</v>
</type>
- <desc><p>Returns a list of supported cipher suites.
+ <desc>
+ <p>Returns a list of supported cipher suites.
+ This function will become deprecated in OTP 21, and replaced
+ by <seealso marker="#cipher_suites-2">ssl:cipher-suites/2</seealso>
<c>cipher_suites()</c> is equivalent to <c>cipher_suites(erlang).</c>
Type <c>openssl</c> is provided for backwards compatibility with the
old SSL, which used OpenSSL. <c>cipher_suites(all)</c> returns
@@ -836,12 +873,25 @@ fun(srp, Username :: string(), UserState :: term()) ->
in <c>cipher_suites(erlang)</c> but included in
<c>cipher_suites(all)</c> are not used unless explicitly configured
by the user.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>cipher_suites(Supported, Version) -> ciphers()</name>
+ <fsummary>Returns a list of all default or
+ all supported cipher suites.</fsummary>
+ <type>
+ <v> Supported = default | all | anonymous </v>
+ <v> Version = protocol_version() </v>
+ </type>
+ <desc><p>Returns all default or all supported (except anonymous), or all anonymous cipher suites for a
+ TLS version</p>
</desc>
</func>
<func>
<name>eccs() -></name>
- <name>eccs(protocol()) -> [named_curve()]</name>
+ <name>eccs(protocol_version()) -> [named_curve()]</name>
<fsummary>Returns a list of supported ECCs.</fsummary>
<desc><p>Returns a list of supported ECCs. <c>eccs()</c>
@@ -1000,6 +1050,21 @@ fun(srp, Username :: string(), UserState :: term()) ->
</desc>
</func>
+ <func>
+ <name>filter_cipher_suites(Suites, Filters) -> ciphers()</name>
+ <fsummary></fsummary>
+ <type>
+ <v> Suites = ciphers()</v>
+ <v> Filters = cipher_filters()</v>
+ </type>
+ <desc><p>Removes cipher suites if any of the filter functions
+ returns false for any part of the cipher suite. This function
+ also calls default filter functions to make sure the cipher
+ suites are supported by crypto. If no filter function is supplied for some
+ part the default behaviour is fun(Algorithm) -> true.</p>
+ </desc>
+ </func>
+
<func>
<name>format_error(Reason) -> string()</name>
<fsummary>Returns an error string.</fsummary>
@@ -1097,6 +1162,22 @@ fun(srp, Username :: string(), UserState :: term()) ->
<p>Returns the address and port number of the peer.</p>
</desc>
</func>
+
+ <func>
+ <name>prepend_cipher_suites(Preferred, Suites) -> ciphers()</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Preferred = ciphers() | cipher_filters() </v>
+ <v>Suites = ciphers() </v>
+ </type>
+ <desc><p>Make <c>Preferred</c> suites become the most preferred
+ suites that is put them at the head of the cipher suite list
+ <c>Suites</c> after removing them from <c>Suites</c> if
+ present. <c>Preferred</c> may be a list of cipher suits or a
+ list of filters in which case the filters are use on <c>Suites</c> to
+ extract the preferred cipher list. </p>
+ </desc>
+ </func>
<func>
<name>prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name>
@@ -1324,7 +1405,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<fsummary>Returns version information relevant for the
SSL application.</fsummary>
<type>
- <v>versions_info() = {app_vsn, string()} | {supported | available, [protocol()] </v>
+ <v>versions_info() = {app_vsn, string()} | {supported | available, [protocol_version()] </v>
</type>
<desc>
<p>Returns version information relevant for the SSL
diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
index 61f88e3860..7f8a08f704 100644
--- a/lib/ssl/doc/src/ssl_distribution.xml
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2016</year>
+ <year>2000</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -180,10 +180,96 @@ Eshell V5.0 (abort with ^G)
<section>
<title>Specifying SSL Options</title>
- <p>For SSL to work, at least
- a public key and a certificate must be specified for the server
- side. In the following example, the PEM-files consist of two
- entries, the server certificate and its private key.</p>
+
+ <p>
+ The SSL distribution options can be written into a file
+ that is consulted when the node is started. This file name
+ is then specified with the command line argument
+ <c>-ssl_dist_optfile</c>.
+ </p>
+ <p>
+ Any available SSL option can be specified in an options file,
+ but note that options that take a <c>fun()</c> has to use
+ the syntax <c>fun Mod:Func/Arity</c> since a function
+ body can not be compiled when consulting a file.
+ </p>
+ <p>
+ Do not tamper with the socket options
+ <c>list</c>, <c>binary</c>, <c>active</c>, <c>packet</c>,
+ <c>nodelay</c> and <c>deliver</c> since they are used
+ by the distribution protocol handler itself.
+ Other raw socket options such as <c>packet_size</c> may
+ interfere severely, so beware!
+ </p>
+ <p>
+ For SSL to work, at least a public key and a certificate
+ must be specified for the server side.
+ In the following example, the PEM file
+ <c>"/home/me/ssl/erlserver.pem"</c> contains both
+ the server certificate and its private key.
+ </p>
+ <p>
+ Create a file named for example
+ <c>"/home/me/ssl/[email protected]"</c>:
+ </p>
+ <code type="none"><![CDATA[
+[{server,
+ [{certfile, "/home/me/ssl/erlserver.pem"},
+ {secure_renegotiate, true}]},
+ {client,
+ [{secure_renegotiate, true}]}].]]>
+ </code>
+ <p>
+ And then start the node like this
+ (line breaks in the command are for readability,
+ and shall not be there when typed):
+ </p>
+ <code type="none"><![CDATA[
+$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
+ -ssl_dist_optfile "/home/me/ssl/[email protected]"
+ -sname ssl_test]]>
+ </code>
+ <p>
+ The options in the <c>{server, Opts}</c> tuple are used
+ when calling <c>ssl:ssl_accept/3</c>, and the options in the
+ <c>{client, Opts}</c> tuple are used when calling
+ <c>ssl:connect/4</c>.
+ </p>
+ <p>
+ For the client, the option
+ <c>{server_name_indication, atom_to_list(TargetNode)}</c>
+ is added when connecting.
+ This makes it possible to use the client option
+ <c>{verify, verify_peer}</c>,
+ and the client will verify that the certificate matches
+ the node name you are connecting to.
+ This only works if the the server certificate is issued
+ to the name <c>atom_to_list(TargetNode)</c>.
+ </p>
+ <p>
+ For the server it is also possible to use the option
+ <c>{verify, verify_peer}</c> and the server will only accept
+ client connections with certificates that are trusted by
+ a root certificate that the server knows.
+ A client that presents an untrusted certificate will be rejected.
+ This option is preferably combined with
+ <c>{fail_if_no_peer_cert, true}</c> or a client will
+ still be accepted if it does not present any certificate.
+ </p>
+ <p>
+ A node started in this way is fully functional, using SSL
+ as the distribution protocol.
+ </p>
+ </section>
+
+ <section>
+ <title>Specifying SSL Options (Legacy)</title>
+
+ <p>
+ As in the previous section the PEM file
+ <c>"/home/me/ssl/erlserver.pem"</c> contains both
+ the server certificate and its private key.
+ </p>
<p>On the <c>erl</c> command line you can specify options that the
SSL distribution adds when creating a socket.</p>
@@ -226,24 +312,26 @@ Eshell V5.0 (abort with ^G)
SSL options and their values. Argument <c>-ssl_dist_opt</c> can
be repeated any number of times.</p>
- <p>An example command line can now look as follows
+ <p>
+ An example command line doing the same as the example
+ in the previous section can now look as follows
(line breaks in the command are for readability,
- and are not be there when typed):</p>
- <code type="none">
+ and shall not be there when typed):
+ </p>
+ <code type="none"><![CDATA[
$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
- -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
+ -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
-ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true
-sname ssl_test
Erlang (BEAM) emulator version 5.0 [source]
-
+
Eshell V5.0 (abort with ^G)
-(ssl_test@myhost)1> </code>
- <p>A node started in this way is fully functional, using SSL
- as the distribution protocol.</p>
+(ssl_test@myhost)1>]]>
+ </code>
</section>
<section>
- <title>Setting up Environment to Always Use SSL</title>
+ <title>Setting up Environment to Always Use SSL (Legacy)</title>
<p>A convenient way to specify arguments to Erlang is to use environment
variable <c>ERL_FLAGS</c>. All the flags needed to
use the SSL distribution can be specified in that variable and are
@@ -285,15 +373,11 @@ Eshell V5.0 (abort with ^G)
variable.</p>
<p>An example command line with this option would look like this:</p>
- <code type="none">
+ <code type="none"><![CDATA[
$ erl -boot /home/me/ssl/start_ssl -proto_dist inet6_tls
- -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
- -ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true
- -sname ssl_test
-Erlang (BEAM) emulator version 5.0 [source]
-
-Eshell V5.0 (abort with ^G)
-(ssl_test@myhost)1> </code>
+ -ssl_dist_optfile "/home/me/ssl/[email protected]"
+ -sname ssl_test]]>
+ </code>
<p>A node started in this way will only be able to communicate with
other nodes using SSL distribution over IPv6.</p>
diff --git a/lib/ssl/doc/src/ssl_introduction.xml b/lib/ssl/doc/src/ssl_introduction.xml
index d3e39dbb01..25b05a769d 100644
--- a/lib/ssl/doc/src/ssl_introduction.xml
+++ b/lib/ssl/doc/src/ssl_introduction.xml
@@ -36,7 +36,7 @@
<title>Purpose</title>
<p>Transport Layer Security (TLS) and its predecessor, the Secure
Sockets Layer (SSL), are cryptographic protocols designed to
- provide communications security over a computer network. The protocols use
+ provide communications security over a computer network. The protocols
use X.509 certificates and hence public key (asymmetric) cryptography to
authenticate the counterpart with whom they communicate,
and to exchange a symmetric key for payload encryption. The protocol provides
diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml
index f84cd6e391..775066ef7d 100644
--- a/lib/ssl/doc/src/using_ssl.xml
+++ b/lib/ssl/doc/src/using_ssl.xml
@@ -152,4 +152,85 @@ Shell got {ssl,{sslsocket,[...]},"foo"}
ok</code>
</section>
</section>
+
+ <section>
+ <title>Customizing cipher suits</title>
+
+ <p>Fetch default cipher suite list for an TLS/DTLS version. Change default
+ to all to get all possible cipher suites.</p>
+ <code type="erl">1> Default = ssl:cipher_suites(default, 'tlsv1.2').
+ [#{cipher => aes_256_gcm,key_exchange => ecdhe_ecdsa,
+ mac => aead,prf => sha384}, ....]
+</code>
+
+ <p>In OTP 20 it is desirable to remove all cipher suites
+ that uses rsa kexchange (removed from default in 21) </p>
+ <code type="erl">2> NoRSA =
+ ssl:filter_cipher_suites(Default,
+ [{key_exchange, fun(rsa) -> false;
+ (_) -> true end}]).
+ [...]
+ </code>
+
+ <p> Pick just a few suites </p>
+ <code type="erl"> 3> Suites =
+ ssl:filter_cipher_suites(Default,
+ [{key_exchange, fun(ecdh_ecdsa) -> true;
+ (_) -> false end},
+ {cipher, fun(aes_128_cbc) ->true;
+ (_) ->false end}]).
+ [#{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,
+ mac => sha256,prf => sha256},
+ #{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,mac => sha,
+ prf => default_prf}]
+ </code>
+
+ <p> Make some particular suites the most preferred, or least
+ preferred by changing prepend to append.</p>
+ <code type="erl"> 4>ssl:prepend_cipher_suites(Suites, Default).
+ [#{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,
+ mac => sha256,prf => sha256},
+ #{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,mac => sha,
+ prf => default_prf},
+ #{cipher => aes_256_cbc,key_exchange => ecdhe_ecdsa,
+ mac => sha384,prf => sha384}, ...]
+ </code>
+ </section>
+
+ <section>
+ <title>Using an Engine Stored Key</title>
+
+ <p>Erlang ssl application is able to use private keys provided
+ by OpenSSL engines using the following mechanism:</p>
+
+ <code type="erl">1> ssl:start().
+ok</code>
+
+ <p>Load a crypto engine, should be done once per engine used. For example
+ dynamically load the engine called <c>MyEngine</c>:
+ </p>
+ <code type="erl">2> {ok, EngineRef} =
+crypto:engine_load(&lt;&lt;"dynamic">>,
+ [{&lt;&lt;"SO_PATH">>, "/tmp/user/engines/MyEngine"},&lt;&lt;"LOAD">>],[]).
+{ok,#Ref&lt;0.2399045421.3028942852.173962>}
+ </code>
+
+ <p>Create a map with the engine information and the algorithm used by the engine:</p>
+ <code type="erl">3> PrivKey =
+ #{algorithm => rsa,
+ engine => EngineRef,
+ key_id => "id of the private key in Engine"}.
+ </code>
+ <p>Use the map in the ssl key option:</p>
+ <code type="erl">4> {ok, SSLSocket} =
+ssl:connect("localhost", 9999,
+ [{cacertfile, "cacerts.pem"},
+ {certfile, "cert.pem"},
+ {key, PrivKey}], infinity).
+ </code>
+
+ <p>See also <seealso marker="crypto:engine_load#engine_load"> crypto documentation</seealso> </p>
+
+ </section>
+
</chapter>
diff --git a/lib/ssl/doc/src/warning.gif b/lib/ssl/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/ssl/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 78458f8379..03725089dd 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -39,20 +39,18 @@
-export([start_fsm/8, start_link/7, init/1]).
%% State transition handling
--export([next_record/1, next_event/3, next_event/4]).
+-export([next_record/1, next_event/3, next_event/4, handle_common_event/4]).
%% Handshake handling
--export([renegotiate/2,
- reinit_handshake_data/1,
- send_handshake/2, queue_handshake/2, queue_change_cipher/2,
- select_sni_extension/1]).
+-export([renegotiate/2, send_handshake/2,
+ queue_handshake/2, queue_change_cipher/2,
+ reinit_handshake_data/1, select_sni_extension/1, empty_connection_state/2]).
%% Alert and close handling
-export([encode_alert/3,send_alert/2, close/5, protocol_name/0]).
%% Data handling
-
--export([encode_data/3, passive_receive/2, next_record_if_active/1, handle_common_event/4,
+-export([encode_data/3, passive_receive/2, next_record_if_active/1,
send/3, socket/5, setopts/3, getopts/3]).
%% gen_statem state functions
@@ -64,6 +62,9 @@
%%====================================================================
%% Internal application API
+%%====================================================================
+%%====================================================================
+%% Setup
%%====================================================================
start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts,
User, {CbModule, _,_, _} = CbInfo,
@@ -79,9 +80,227 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker}
Error
end.
-send_handshake(Handshake, #state{connection_states = ConnectionStates} = States) ->
+%%--------------------------------------------------------------------
+-spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
+ {ok, pid()} | ignore | {error, reason()}.
+%%
+%% Description: Creates a gen_statem process which calls Module:init/1 to
+%% initialize.
+%%--------------------------------------------------------------------
+start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
+ {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}.
+
+init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
+ process_flag(trap_exit, true),
+ State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
+ try
+ State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0),
+ gen_statem:enter_loop(?MODULE, [], init, State)
+ catch
+ throw:Error ->
+ gen_statem:enter_loop(?MODULE, [], error, {Error,State0})
+ end.
+%%====================================================================
+%% State transition handling
+%%====================================================================
+next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
+ {no_record, State#state{unprocessed_handshake_events = N-1}};
+
+next_record(#state{protocol_buffers =
+ #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} = CT | Rest]}
+ = Buffers,
+ connection_states = #{current_read := #{epoch := Epoch}} = ConnectionStates} = State) ->
+ CurrentRead = dtls_record:get_connection_state_by_epoch(Epoch, ConnectionStates, read),
+ case dtls_record:replay_detect(CT, CurrentRead) of
+ false ->
+ decode_cipher_text(State#state{connection_states = ConnectionStates}) ;
+ true ->
+ %% Ignore replayed record
+ next_record(State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnectionStates})
+ end;
+next_record(#state{protocol_buffers =
+ #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} | Rest]}
+ = Buffers,
+ connection_states = #{current_read := #{epoch := CurrentEpoch}} = ConnectionStates} = State)
+ when Epoch > CurrentEpoch ->
+ %% TODO Buffer later Epoch message, drop it for now
+ next_record(State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnectionStates});
+next_record(#state{protocol_buffers =
+ #protocol_buffers{dtls_cipher_texts = [ _ | Rest]}
+ = Buffers,
+ connection_states = ConnectionStates} = State) ->
+ %% Drop old epoch message
+ next_record(State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnectionStates});
+next_record(#state{role = server,
+ socket = {Listener, {Client, _}},
+ transport_cb = gen_udp} = State) ->
+ dtls_udp_listener:active_once(Listener, Client, self()),
+ {no_record, State};
+next_record(#state{role = client,
+ socket = {_Server, Socket} = DTLSSocket,
+ close_tag = CloseTag,
+ transport_cb = Transport} = State) ->
+ case dtls_socket:setopts(Transport, Socket, [{active,once}]) of
+ ok ->
+ {no_record, State};
+ _ ->
+ self() ! {CloseTag, DTLSSocket},
+ {no_record, State}
+ end;
+next_record(State) ->
+ {no_record, State}.
+
+next_event(StateName, Record, State) ->
+ next_event(StateName, Record, State, []).
+
+next_event(connection = StateName, no_record,
+ #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) ->
+ case next_record_if_active(State0) of
+ {no_record, State} ->
+ ssl_connection:hibernate_after(StateName, State, Actions);
+ {#ssl_tls{epoch = CurrentEpoch,
+ type = ?HANDSHAKE,
+ version = Version} = Record, State1} ->
+ State = dtls_version(StateName, Version, State1),
+ {next_state, StateName, State,
+ [{next_event, internal, {protocol_record, Record}} | Actions]};
+ {#ssl_tls{epoch = CurrentEpoch} = Record, State} ->
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
+ {#ssl_tls{epoch = Epoch,
+ type = ?HANDSHAKE,
+ version = _Version}, State1} = _Record when Epoch == CurrentEpoch-1 ->
+ {State2, MoreActions} = send_handshake_flight(State1, CurrentEpoch),
+ {NextRecord, State} = next_record(State2),
+ next_event(StateName, NextRecord, State, Actions ++ MoreActions);
+ %% From FLIGHT perspective CHANGE_CIPHER_SPEC is treated as a handshake
+ {#ssl_tls{epoch = Epoch,
+ type = ?CHANGE_CIPHER_SPEC,
+ version = _Version}, State1} = _Record when Epoch == CurrentEpoch-1 ->
+ {State2, MoreActions} = send_handshake_flight(State1, CurrentEpoch),
+ {NextRecord, State} = next_record(State2),
+ next_event(StateName, NextRecord, State, Actions ++ MoreActions);
+ {#ssl_tls{epoch = _Epoch,
+ version = _Version}, State1} ->
+ %% TODO maybe buffer later epoch
+ {Record, State} = next_record(State1),
+ next_event(StateName, Record, State, Actions);
+ {#alert{} = Alert, State} ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ end;
+next_event(connection = StateName, Record,
+ #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) ->
+ case Record of
+ #ssl_tls{epoch = CurrentEpoch,
+ type = ?HANDSHAKE,
+ version = Version} = Record ->
+ State = dtls_version(StateName, Version, State0),
+ {next_state, StateName, State,
+ [{next_event, internal, {protocol_record, Record}} | Actions]};
+ #ssl_tls{epoch = CurrentEpoch} ->
+ {next_state, StateName, State0, [{next_event, internal, {protocol_record, Record}} | Actions]};
+ #ssl_tls{epoch = Epoch,
+ type = ?HANDSHAKE,
+ version = _Version} when Epoch == CurrentEpoch-1 ->
+ {State1, MoreActions} = send_handshake_flight(State0, CurrentEpoch),
+ {NextRecord, State} = next_record(State1),
+ next_event(StateName, NextRecord, State, Actions ++ MoreActions);
+ %% From FLIGHT perspective CHANGE_CIPHER_SPEC is treated as a handshake
+ #ssl_tls{epoch = Epoch,
+ type = ?CHANGE_CIPHER_SPEC,
+ version = _Version} when Epoch == CurrentEpoch-1 ->
+ {State1, MoreActions} = send_handshake_flight(State0, CurrentEpoch),
+ {NextRecord, State} = next_record(State1),
+ next_event(StateName, NextRecord, State, Actions ++ MoreActions);
+ _ ->
+ next_event(StateName, no_record, State0, Actions)
+ end;
+next_event(StateName, Record,
+ #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) ->
+ case Record of
+ no_record ->
+ {next_state, StateName, State0, Actions};
+ #ssl_tls{epoch = CurrentEpoch,
+ version = Version} = Record ->
+ State = dtls_version(StateName, Version, State0),
+ {next_state, StateName, State,
+ [{next_event, internal, {protocol_record, Record}} | Actions]};
+ #ssl_tls{epoch = _Epoch,
+ version = _Version} = _Record ->
+ %% TODO maybe buffer later epoch
+ {Record, State} = next_record(State0),
+ next_event(StateName, Record, State, Actions);
+ #alert{} = Alert ->
+ {next_state, StateName, State0, [{next_event, internal, Alert} | Actions]}
+ end.
+
+handle_common_event(internal, #alert{} = Alert, StateName,
+ #state{negotiated_version = Version} = State) ->
+ handle_own_alert(Alert, Version, StateName, State);
+%%% DTLS record protocol level handshake messages
+handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE,
+ fragment = Data},
+ StateName,
+ #state{protocol_buffers = Buffers0,
+ negotiated_version = Version} = State0) ->
+ try
+ case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0) of
+ {[], Buffers} ->
+ {Record, State} = next_record(State0#state{protocol_buffers = Buffers}),
+ next_event(StateName, Record, State);
+ {Packets, Buffers} ->
+ State = State0#state{protocol_buffers = Buffers},
+ Events = dtls_handshake_events(Packets),
+ {next_state, StateName,
+ State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
+ end
+ catch throw:#alert{} = Alert ->
+ handle_own_alert(Alert, Version, StateName, State0)
+ end;
+%%% DTLS record protocol level application data messages
+handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) ->
+ {next_state, StateName, State, [{next_event, internal, {application_data, Data}}]};
+%%% DTLS record protocol level change cipher messages
+handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) ->
+ {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]};
+%%% DTLS record protocol level Alert messages
+handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,
+ #state{negotiated_version = Version} = State) ->
+ case decode_alerts(EncAlerts) of
+ Alerts = [_|_] ->
+ handle_alerts(Alerts, {next_state, StateName, State});
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, StateName, State)
+ end;
+%% Ignore unknown TLS record level protocol messages
+handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) ->
+ {next_state, StateName, State}.
+
+%%====================================================================
+%% Handshake handling
+%%====================================================================
+
+renegotiate(#state{role = client} = State, Actions) ->
+ %% Handle same way as if server requested
+ %% the renegotiation
+ {next_state, connection, State,
+ [{next_event, internal, #hello_request{}} | Actions]};
+
+renegotiate(#state{role = server} = State0, Actions) ->
+ HelloRequest = ssl_handshake:hello_request(),
+ State1 = prepare_flight(State0),
+ {State2, MoreActions} = send_handshake(HelloRequest, State1),
+ {Record, State} = next_record(State2),
+ next_event(hello, Record, State, Actions ++ MoreActions).
+
+send_handshake(Handshake, #state{connection_states = ConnectionStates} = State) ->
#{epoch := Epoch} = ssl_record:current_connection_state(ConnectionStates, write),
- send_handshake_flight(queue_handshake(Handshake, States), Epoch).
+ send_handshake_flight(queue_handshake(Handshake, State), Epoch).
queue_handshake(Handshake0, #state{tls_handshake_history = Hist0,
negotiated_version = Version,
@@ -104,69 +323,39 @@ queue_handshake(Handshake0, #state{tls_handshake_history = Hist0,
next_sequence => Seq +1},
tls_handshake_history = Hist}.
-
-send_handshake_flight(#state{socket = Socket,
- transport_cb = Transport,
- flight_buffer = #{handshakes := Flight,
- change_cipher_spec := undefined},
- negotiated_version = Version,
- connection_states = ConnectionStates0} = State0, Epoch) ->
- %% TODO remove hardcoded Max size
- {Encoded, ConnectionStates} =
- encode_handshake_flight(lists:reverse(Flight), Version, 1400, Epoch, ConnectionStates0),
- send(Transport, Socket, Encoded),
- {State0#state{connection_states = ConnectionStates}, []};
-
-send_handshake_flight(#state{socket = Socket,
- transport_cb = Transport,
- flight_buffer = #{handshakes := [_|_] = Flight0,
- change_cipher_spec := ChangeCipher,
- handshakes_after_change_cipher_spec := []},
- negotiated_version = Version,
- connection_states = ConnectionStates0} = State0, Epoch) ->
- {HsBefore, ConnectionStates1} =
- encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0),
- {EncChangeCipher, ConnectionStates} = encode_change_cipher(ChangeCipher, Version, Epoch, ConnectionStates1),
-
- send(Transport, Socket, [HsBefore, EncChangeCipher]),
- {State0#state{connection_states = ConnectionStates}, []};
-
-send_handshake_flight(#state{socket = Socket,
- transport_cb = Transport,
- flight_buffer = #{handshakes := [_|_] = Flight0,
- change_cipher_spec := ChangeCipher,
- handshakes_after_change_cipher_spec := Flight1},
- negotiated_version = Version,
- connection_states = ConnectionStates0} = State0, Epoch) ->
- {HsBefore, ConnectionStates1} =
- encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0),
- {EncChangeCipher, ConnectionStates2} =
- encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates1),
- {HsAfter, ConnectionStates} =
- encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates2),
- send(Transport, Socket, [HsBefore, EncChangeCipher, HsAfter]),
- {State0#state{connection_states = ConnectionStates}, []};
-
-send_handshake_flight(#state{socket = Socket,
- transport_cb = Transport,
- flight_buffer = #{handshakes := [],
- change_cipher_spec := ChangeCipher,
- handshakes_after_change_cipher_spec := Flight1},
- negotiated_version = Version,
- connection_states = ConnectionStates0} = State0, Epoch) ->
- {EncChangeCipher, ConnectionStates1} =
- encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0),
- {HsAfter, ConnectionStates} =
- encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates1),
- send(Transport, Socket, [EncChangeCipher, HsAfter]),
- {State0#state{connection_states = ConnectionStates}, []}.
-
queue_change_cipher(ChangeCipher, #state{flight_buffer = Flight,
connection_states = ConnectionStates0} = State) ->
ConnectionStates =
dtls_record:next_epoch(ConnectionStates0, write),
State#state{flight_buffer = Flight#{change_cipher_spec => ChangeCipher},
connection_states = ConnectionStates}.
+reinit_handshake_data(#state{protocol_buffers = Buffers} = State) ->
+ State#state{premaster_secret = undefined,
+ public_key_info = undefined,
+ tls_handshake_history = ssl_handshake:init_handshake_history(),
+ flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
+ flight_buffer = new_flight(),
+ protocol_buffers =
+ Buffers#protocol_buffers{
+ dtls_handshake_next_seq = 0,
+ dtls_handshake_next_fragments = [],
+ dtls_handshake_later_fragments = []
+ }}.
+
+select_sni_extension(#client_hello{extensions = HelloExtensions}) ->
+ HelloExtensions#hello_extensions.sni;
+select_sni_extension(_) ->
+ undefined.
+
+empty_connection_state(ConnectionEnd, BeastMitigation) ->
+ Empty = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation),
+ dtls_record:empty_connection_state(Empty).
+
+%%====================================================================
+%% Alert and close handling
+%%====================================================================
+encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
+ dtls_record:encode_alert_record(Alert, Version, ConnectionStates).
send_alert(Alert, #state{negotiated_version = Version,
socket = Socket,
@@ -183,67 +372,55 @@ close(downgrade, _,_,_,_) ->
close(_, Socket, Transport, _,_) ->
dtls_socket:close(Transport,Socket).
-reinit_handshake_data(#state{protocol_buffers = Buffers} = State) ->
- State#state{premaster_secret = undefined,
- public_key_info = undefined,
- tls_handshake_history = ssl_handshake:init_handshake_history(),
- flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
- protocol_buffers =
- Buffers#protocol_buffers{
- dtls_handshake_next_seq = 0,
- dtls_handshake_next_fragments = [],
- dtls_handshake_later_fragments = []
- }}.
+protocol_name() ->
+ "DTLS".
+
+%%====================================================================
+%% Data handling
+%%====================================================================
-select_sni_extension(#client_hello{extensions = HelloExtensions}) ->
- HelloExtensions#hello_extensions.sni;
-select_sni_extension(_) ->
- undefined.
+encode_data(Data, Version, ConnectionStates0)->
+ dtls_record:encode_data(Data, Version, ConnectionStates0).
+
+passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
+ case Buffer of
+ <<>> ->
+ {Record, State} = next_record(State0),
+ next_event(StateName, Record, State);
+ _ ->
+ {Record, State} = ssl_connection:read_application_data(<<>>, State0),
+ next_event(StateName, Record, State)
+ end.
+next_record_if_active(State =
+ #state{socket_options =
+ #socket_options{active = false}}) ->
+ {no_record ,State};
+
+next_record_if_active(State) ->
+ next_record(State).
+
+send(Transport, {_, {{_,_}, _} = Socket}, Data) ->
+ send(Transport, Socket, Data);
+send(Transport, Socket, Data) ->
+ dtls_socket:send(Transport, Socket, Data).
socket(Pid, Transport, Socket, Connection, _) ->
dtls_socket:socket(Pid, Transport, Socket, Connection).
setopts(Transport, Socket, Other) ->
dtls_socket:setopts(Transport, Socket, Other).
+
getopts(Transport, Socket, Tag) ->
dtls_socket:getopts(Transport, Socket, Tag).
-protocol_name() ->
- "DTLS".
-
-%%====================================================================
-%% tls_connection_sup API
-%%====================================================================
-
%%--------------------------------------------------------------------
--spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
- {ok, pid()} | ignore | {error, reason()}.
-%%
-%% Description: Creates a gen_fsm process which calls Module:init/1 to
-%% initialize. To ensure a synchronized start-up procedure, this function
-%% does not return until Module:init/1 has returned.
+%% State functions
%%--------------------------------------------------------------------
-start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
- {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}.
-
-init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
- process_flag(trap_exit, true),
- State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
- try
- State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0),
- gen_statem:enter_loop(?MODULE, [], init, State)
- catch
- throw:Error ->
- gen_statem:enter_loop(?MODULE, [], error, {Error,State0})
- end.
-
-callback_mode() ->
- [state_functions, state_enter].
-
%%--------------------------------------------------------------------
-%% State functions
+-spec init(gen_statem:event_type(),
+ {start, timeout()} | term(), #state{}) ->
+ gen_statem:state_function_result().
%%--------------------------------------------------------------------
-
init(enter, _, State) ->
{keep_state, State};
init({call, From}, {start, Timeout},
@@ -273,28 +450,32 @@ init({call, From}, {start, Timeout},
{Record, State} = next_record(State3),
next_event(hello, Record, State, Actions);
init({call, _} = Type, Event, #state{role = server, transport_cb = gen_udp} = State) ->
- Result = ssl_connection:?FUNCTION_NAME(Type, Event,
- State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
- protocol_specific = #{current_cookie_secret => dtls_v1:cookie_secret(),
- previous_cookie_secret => <<>>,
- ignored_alerts => 0,
- max_ignored_alerts => 10}},
- ?MODULE),
+ Result = gen_handshake(?FUNCTION_NAME, Type, Event,
+ State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
+ protocol_specific = #{current_cookie_secret => dtls_v1:cookie_secret(),
+ previous_cookie_secret => <<>>,
+ ignored_alerts => 0,
+ max_ignored_alerts => 10}}),
erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret),
Result;
init({call, _} = Type, Event, #state{role = server} = State) ->
%% I.E. DTLS over sctp
- ssl_connection:?FUNCTION_NAME(Type, Event, State#state{flight_state = reliable}, ?MODULE);
+ gen_handshake(?FUNCTION_NAME, Type, Event, State#state{flight_state = reliable});
init(Type, Event, State) ->
- ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE).
-
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
+
+%%--------------------------------------------------------------------
+-spec error(gen_statem:event_type(),
+ {start, timeout()} | term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
error(enter, _, State) ->
{keep_state, State};
error({call, From}, {start, _Timeout}, {Error, State}) ->
{stop_and_reply, normal, {reply, From, {error, Error}}, State};
-error({call, From}, Msg, State) ->
- handle_call(Msg, From, ?FUNCTION_NAME, State);
+error({call, _} = Call, Msg, State) ->
+ gen_handshake(?FUNCTION_NAME, Call, Msg, State);
error(_, _, _) ->
{keep_state_and_data, [postpone]}.
@@ -355,13 +536,14 @@ hello(internal, #hello_verify_request{cookie = Cookie}, #state{role = client,
session_cache = Cache,
session_cache_cb = CacheCb
} = State0) ->
- State1 = prepare_flight(State0#state{tls_handshake_history = ssl_handshake:init_handshake_history()}),
+
Hello = dtls_handshake:client_hello(Host, Port, Cookie, ConnectionStates0,
SslOpts,
Cache, CacheCb, Renegotiation, OwnCert),
Version = Hello#client_hello.client_version,
- HelloVersion = dtls_record:lowest_protocol_version(SslOpts#ssl_options.versions),
- {State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}),
+ State1 = prepare_flight(State0#state{tls_handshake_history = ssl_handshake:init_handshake_history()}),
+
+ {State2, Actions} = send_handshake(Hello, State1),
State3 = State2#state{negotiated_version = Version, %% Requested version
session =
Session0#session{session_id =
@@ -388,49 +570,66 @@ hello(internal, {handshake, {#hello_verify_request{} = Handshake, _}}, State) ->
%% hello_verify should not be in handshake history
{next_state, ?FUNCTION_NAME, State, [{next_event, internal, Handshake}]};
hello(info, Event, State) ->
- handle_info(Event, ?FUNCTION_NAME, State);
+ gen_info(Event, ?FUNCTION_NAME, State);
hello(state_timeout, Event, State) ->
handle_state_timeout(Event, ?FUNCTION_NAME, State);
hello(Type, Event, State) ->
- ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE).
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
+%%--------------------------------------------------------------------
+-spec abbreviated(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
abbreviated(enter, _, State0) ->
{State, Actions} = handle_flight_timer(State0),
{keep_state, State, Actions};
abbreviated(info, Event, State) ->
- handle_info(Event, ?FUNCTION_NAME, State);
+ gen_info(Event, ?FUNCTION_NAME, State);
abbreviated(internal = Type,
#change_cipher_spec{type = <<1>>} = Event,
#state{connection_states = ConnectionStates0} = State) ->
ConnectionStates1 = dtls_record:save_current_connection_state(ConnectionStates0, read),
ConnectionStates = dtls_record:next_epoch(ConnectionStates1, read),
- ssl_connection:?FUNCTION_NAME(Type, Event, State#state{connection_states = ConnectionStates}, ?MODULE);
+ gen_handshake(?FUNCTION_NAME, Type, Event, State#state{connection_states = ConnectionStates});
abbreviated(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates} = State) ->
- ssl_connection:?FUNCTION_NAME(Type, Event,
- prepare_flight(State#state{connection_states = ConnectionStates,
- flight_state = connection}), ?MODULE);
+ gen_handshake(?FUNCTION_NAME, Type, Event,
+ prepare_flight(State#state{connection_states = ConnectionStates,
+ flight_state = connection}));
abbreviated(state_timeout, Event, State) ->
handle_state_timeout(Event, ?FUNCTION_NAME, State);
abbreviated(Type, Event, State) ->
- ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE).
-
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
+%%--------------------------------------------------------------------
+-spec certify(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
certify(enter, _, State0) ->
{State, Actions} = handle_flight_timer(State0),
{keep_state, State, Actions};
certify(info, Event, State) ->
- handle_info(Event, ?FUNCTION_NAME, State);
+ gen_info(Event, ?FUNCTION_NAME, State);
certify(internal = Type, #server_hello_done{} = Event, State) ->
ssl_connection:certify(Type, Event, prepare_flight(State), ?MODULE);
+certify(internal, #change_cipher_spec{type = <<1>>}, State0) ->
+ {State1, Actions0} = send_handshake_flight(State0, retransmit_epoch(?FUNCTION_NAME, State0)),
+ {Record, State2} = next_record(State1),
+ {next_state, ?FUNCTION_NAME, State, Actions} = next_event(?FUNCTION_NAME, Record, State2, Actions0),
+ %% This will reset the retransmission timer by repeating the enter state event
+ {repeat_state, State, Actions};
certify(state_timeout, Event, State) ->
handle_state_timeout(Event, ?FUNCTION_NAME, State);
certify(Type, Event, State) ->
- ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE).
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
+%%--------------------------------------------------------------------
+-spec cipher(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
cipher(enter, _, State0) ->
{State, Actions} = handle_flight_timer(State0),
{keep_state, State, Actions};
cipher(info, Event, State) ->
- handle_info(Event, ?FUNCTION_NAME, State);
+ gen_info(Event, ?FUNCTION_NAME, State);
cipher(internal = Type, #change_cipher_spec{type = <<1>>} = Event,
#state{connection_states = ConnectionStates0} = State) ->
ConnectionStates1 = dtls_record:save_current_connection_state(ConnectionStates0, read),
@@ -446,22 +645,32 @@ cipher(state_timeout, Event, State) ->
cipher(Type, Event, State) ->
ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE).
+%%--------------------------------------------------------------------
+-spec connection(gen_statem:event_type(),
+ #hello_request{} | #client_hello{}| term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
connection(enter, _, State) ->
{keep_state, State};
connection(info, Event, State) ->
- handle_info(Event, ?FUNCTION_NAME, State);
+ gen_info(Event, ?FUNCTION_NAME, State);
connection(internal, #hello_request{}, #state{host = Host, port = Port,
- session = #session{own_certificate = Cert} = Session0,
- session_cache = Cache, session_cache_cb = CacheCb,
- ssl_options = SslOpts,
- connection_states = ConnectionStates0,
- renegotiation = {Renegotiation, _}} = State0) ->
+ session = #session{own_certificate = Cert} = Session0,
+ session_cache = Cache, session_cache_cb = CacheCb,
+ ssl_options = SslOpts,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}} = State0) ->
+
Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
- {State1, Actions} = send_handshake(Hello, State0),
+ Version = Hello#client_hello.client_version,
+ HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions),
+ State1 = prepare_flight(State0),
+ {State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}),
{Record, State} =
next_record(
- State1#state{session = Session0#session{session_id
+ State2#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
+ session = Session0#session{session_id
= Hello#client_hello.session_id}}),
next_event(hello, Record, State, Actions);
connection(internal, #client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) ->
@@ -471,7 +680,8 @@ connection(internal, #client_hello{} = Hello, #state{role = server, allow_renego
%% initiated renegotiation we will disallow many client initiated
%% renegotiations immediately after each other.
erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate),
- {next_state, hello, State#state{allow_renegotiate = false}, [{next_event, internal, Hello}]};
+ {next_state, hello, State#state{allow_renegotiate = false, renegotiation = {true, peer}},
+ [{next_event, internal, Hello}]};
connection(internal, #client_hello{}, #state{role = server, allow_renegotiate = false} = State0) ->
Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION),
State1 = send_alert(Alert, State0),
@@ -481,137 +691,24 @@ connection(Type, Event, State) ->
ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE).
%%TODO does this make sense for DTLS ?
+%%--------------------------------------------------------------------
+-spec downgrade(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
downgrade(enter, _, State) ->
{keep_state, State};
downgrade(Type, Event, State) ->
ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE).
%%--------------------------------------------------------------------
-%% Description: This function is called by a gen_fsm when it receives any
-%% other message than a synchronous or asynchronous event
-%% (or a system message).
+%% gen_statem callbacks
%%--------------------------------------------------------------------
+callback_mode() ->
+ [state_functions, state_enter].
-%% raw data from socket, unpack records
-handle_info({Protocol, _, _, _, Data}, StateName,
- #state{data_tag = Protocol} = State0) ->
- case next_dtls_record(Data, State0) of
- {Record, State} ->
- next_event(StateName, Record, State);
- #alert{} = Alert ->
- ssl_connection:handle_normal_shutdown(Alert, StateName, State0),
- {stop, {shutdown, own_alert}}
- end;
-handle_info({CloseTag, Socket}, StateName,
- #state{socket = Socket,
- socket_options = #socket_options{active = Active},
- protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs},
- close_tag = CloseTag,
- negotiated_version = Version} = State) ->
- %% Note that as of DTLS 1.2 (TLS 1.1),
- %% failure to properly close a connection no longer requires that a
- %% session not be resumed. This is a change from DTLS 1.0 to conform
- %% with widespread implementation practice.
- case (Active == false) andalso (CTs =/= []) of
- false ->
- case Version of
- {254, N} when N =< 253 ->
- ok;
- _ ->
- %% As invalidate_sessions here causes performance issues,
- %% we will conform to the widespread implementation
- %% practice and go aginst the spec
- %%invalidate_session(Role, Host, Port, Session)
- ok
- end,
- ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
- {stop, {shutdown, transport_closed}};
- true ->
- %% Fixes non-delivery of final DTLS record in {active, once}.
- %% Basically allows the application the opportunity to set {active, once} again
- %% and then receive the final message.
- next_event(StateName, no_record, State)
- end;
-
-handle_info(new_cookie_secret, StateName,
- #state{protocol_specific = #{current_cookie_secret := Secret} = CookieInfo} = State) ->
- erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret),
- {next_state, StateName, State#state{protocol_specific =
- CookieInfo#{current_cookie_secret => dtls_v1:cookie_secret(),
- previous_cookie_secret => Secret}}};
-handle_info(Msg, StateName, State) ->
- ssl_connection:handle_info(Msg, StateName, State).
-
-
-handle_call(Event, From, StateName, State) ->
- ssl_connection:handle_call(Event, From, StateName, State, ?MODULE).
-
-handle_common_event(internal, #alert{} = Alert, StateName,
- #state{negotiated_version = Version} = State) ->
- handle_own_alert(Alert, Version, StateName, State);
-%%% DTLS record protocol level handshake messages
-handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE,
- fragment = Data},
- StateName,
- #state{protocol_buffers = Buffers0,
- negotiated_version = Version} = State0) ->
- try
- case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0) of
- {[], Buffers} ->
- {Record, State} = next_record(State0#state{protocol_buffers = Buffers}),
- next_event(StateName, Record, State);
- {Packets, Buffers} ->
- State = State0#state{protocol_buffers = Buffers},
- Events = dtls_handshake_events(Packets),
- {next_state, StateName,
- State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
- end
- catch throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State0)
- end;
-%%% DTLS record protocol level application data messages
-handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) ->
- {next_state, StateName, State, [{next_event, internal, {application_data, Data}}]};
-%%% DTLS record protocol level change cipher messages
-handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) ->
- {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]};
-%%% DTLS record protocol level Alert messages
-handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,
- #state{negotiated_version = Version} = State) ->
- case decode_alerts(EncAlerts) of
- Alerts = [_|_] ->
- handle_alerts(Alerts, {next_state, StateName, State});
- #alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State)
- end;
-%% Ignore unknown TLS record level protocol messages
-handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) ->
- {next_state, StateName, State}.
-
-handle_state_timeout(flight_retransmission_timeout, StateName,
- #state{flight_state = {retransmit, NextTimeout}} = State0) ->
- {State1, Actions} = send_handshake_flight(State0#state{flight_state = {retransmit, NextTimeout}},
- retransmit_epoch(StateName, State0)),
- {Record, State} = next_record(State1),
- next_event(StateName, Record, State, Actions).
-
-send(Transport, {_, {{_,_}, _} = Socket}, Data) ->
- send(Transport, Socket, Data);
-send(Transport, Socket, Data) ->
- dtls_socket:send(Transport, Socket, Data).
-%%--------------------------------------------------------------------
-%% Description:This function is called by a gen_fsm when it is about
-%% to terminate. It should be the opposite of Module:init/1 and do any
-%% necessary cleaning up. When it returns, the gen_fsm terminates with
-%% Reason. The return value is ignored.
-%%--------------------------------------------------------------------
terminate(Reason, StateName, State) ->
ssl_connection:terminate(Reason, StateName, State).
-%%--------------------------------------------------------------------
-%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State, _Extra) ->
{ok, StateName, State}.
@@ -621,56 +718,6 @@ format_status(Type, Data) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-handle_client_hello(#client_hello{client_version = ClientVersion} = Hello,
- #state{connection_states = ConnectionStates0,
- port = Port, session = #session{own_certificate = Cert} = Session0,
- renegotiation = {Renegotiation, _},
- session_cache = Cache,
- session_cache_cb = CacheCb,
- negotiated_protocol = CurrentProtocol,
- key_algorithm = KeyExAlg,
- ssl_options = SslOpts} = State0) ->
-
- case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
- ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
- #alert{} = Alert ->
- handle_own_alert(Alert, ClientVersion, hello, State0);
- {Version, {Type, Session},
- ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
- Protocol = case Protocol0 of
- undefined -> CurrentProtocol;
- _ -> Protocol0
- end,
-
- State = prepare_flight(State0#state{connection_states = ConnectionStates,
- negotiated_version = Version,
- client_hello_version = ClientVersion,
- hashsign_algorithm = HashSign,
- session = Session,
- negotiated_protocol = Protocol}),
-
- ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
- State, ?MODULE)
- end.
-
-encode_handshake_flight(Flight, Version, MaxFragmentSize, Epoch, ConnectionStates) ->
- Fragments = lists:map(fun(Handshake) ->
- dtls_handshake:fragment_handshake(Handshake, MaxFragmentSize)
- end, Flight),
- dtls_record:encode_handshake(Fragments, Version, Epoch, ConnectionStates).
-
-encode_change_cipher(#change_cipher_spec{}, Version, Epoch, ConnectionStates) ->
- dtls_record:encode_change_cipher_spec(Version, Epoch, ConnectionStates).
-
-encode_data(Data, Version, ConnectionStates0)->
- dtls_record:encode_data(Data, Version, ConnectionStates0).
-
-encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
- dtls_record:encode_alert_record(Alert, Version, ConnectionStates).
-
-decode_alerts(Bin) ->
- ssl_alert:decode(Bin).
-
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
#ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
@@ -711,10 +758,12 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User,
flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}
}.
-next_dtls_record(Data, #state{protocol_buffers = #protocol_buffers{
+next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{
dtls_record_buffer = Buf0,
dtls_cipher_texts = CT0} = Buffers} = State0) ->
- case dtls_record:get_dtls_records(Data, Buf0) of
+ case dtls_record:get_dtls_records(Data,
+ acceptable_record_versions(StateName, State0),
+ Buf0) of
{Records, Buf1} ->
CT1 = CT0 ++ Records,
next_record(State0#state{protocol_buffers =
@@ -724,141 +773,15 @@ next_dtls_record(Data, #state{protocol_buffers = #protocol_buffers{
Alert
end.
-next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
- {no_record, State#state{unprocessed_handshake_events = N-1}};
-
-next_record(#state{protocol_buffers =
- #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} = CT | Rest]}
- = Buffers,
- connection_states = #{current_read := #{epoch := Epoch}} = ConnectionStates} = State) ->
- CurrentRead = dtls_record:get_connection_state_by_epoch(Epoch, ConnectionStates, read),
- case dtls_record:replay_detect(CT, CurrentRead) of
- false ->
- decode_cipher_text(State#state{connection_states = ConnectionStates}) ;
- true ->
- %% Ignore replayed record
- next_record(State#state{protocol_buffers =
- Buffers#protocol_buffers{dtls_cipher_texts = Rest},
- connection_states = ConnectionStates})
- end;
-next_record(#state{protocol_buffers =
- #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} | Rest]}
- = Buffers,
- connection_states = #{current_read := #{epoch := CurrentEpoch}} = ConnectionStates} = State)
- when Epoch > CurrentEpoch ->
- %% TODO Buffer later Epoch message, drop it for now
- next_record(State#state{protocol_buffers =
- Buffers#protocol_buffers{dtls_cipher_texts = Rest},
- connection_states = ConnectionStates});
-next_record(#state{protocol_buffers =
- #protocol_buffers{dtls_cipher_texts = [ _ | Rest]}
- = Buffers,
- connection_states = ConnectionStates} = State) ->
- %% Drop old epoch message
- next_record(State#state{protocol_buffers =
- Buffers#protocol_buffers{dtls_cipher_texts = Rest},
- connection_states = ConnectionStates});
-next_record(#state{role = server,
- socket = {Listener, {Client, _}},
- transport_cb = gen_udp} = State) ->
- dtls_udp_listener:active_once(Listener, Client, self()),
- {no_record, State};
-next_record(#state{role = client,
- socket = {_Server, Socket},
- transport_cb = Transport} = State) ->
- dtls_socket:setopts(Transport, Socket, [{active,once}]),
- {no_record, State};
-next_record(State) ->
- {no_record, State}.
-
-next_record_if_active(State =
- #state{socket_options =
- #socket_options{active = false}}) ->
- {no_record ,State};
+acceptable_record_versions(hello, _) ->
+ [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_DATAGRAM_SUPPORTED_VERSIONS];
+acceptable_record_versions(_, #state{negotiated_version = Version}) ->
+ [Version].
-next_record_if_active(State) ->
- next_record(State).
-
-passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
- case Buffer of
- <<>> ->
- {Record, State} = next_record(State0),
- next_event(StateName, Record, State);
- _ ->
- {Record, State} = ssl_connection:read_application_data(<<>>, State0),
- next_event(StateName, Record, State)
- end.
-
-next_event(StateName, Record, State) ->
- next_event(StateName, Record, State, []).
-
-next_event(connection = StateName, no_record,
- #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) ->
- case next_record_if_active(State0) of
- {no_record, State} ->
- ssl_connection:hibernate_after(StateName, State, Actions);
- {#ssl_tls{epoch = CurrentEpoch} = Record, State} ->
- {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
- {#ssl_tls{epoch = Epoch,
- type = ?HANDSHAKE,
- version = _Version}, State1} = _Record when Epoch == CurrentEpoch-1 ->
- {State2, MoreActions} = send_handshake_flight(State1, CurrentEpoch),
- {NextRecord, State} = next_record(State2),
- next_event(StateName, NextRecord, State, Actions ++ MoreActions);
- %% From FLIGHT perspective CHANGE_CIPHER_SPEC is treated as a handshake
- {#ssl_tls{epoch = Epoch,
- type = ?CHANGE_CIPHER_SPEC,
- version = _Version}, State1} = _Record when Epoch == CurrentEpoch-1 ->
- {State2, MoreActions} = send_handshake_flight(State1, CurrentEpoch),
- {NextRecord, State} = next_record(State2),
- next_event(StateName, NextRecord, State, Actions ++ MoreActions);
- {#ssl_tls{epoch = _Epoch,
- version = _Version}, State1} ->
- %% TODO maybe buffer later epoch
- {Record, State} = next_record(State1),
- next_event(StateName, Record, State, Actions);
- {#alert{} = Alert, State} ->
- {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
- end;
-next_event(connection = StateName, Record,
- #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) ->
- case Record of
- #ssl_tls{epoch = CurrentEpoch} ->
- {next_state, StateName, State0, [{next_event, internal, {protocol_record, Record}} | Actions]};
- #ssl_tls{epoch = Epoch,
- type = ?HANDSHAKE,
- version = _Version} when Epoch == CurrentEpoch-1 ->
- {State1, MoreActions} = send_handshake_flight(State0, CurrentEpoch),
- {NextRecord, State} = next_record(State1),
- next_event(StateName, NextRecord, State, Actions ++ MoreActions);
- %% From FLIGHT perspective CHANGE_CIPHER_SPEC is treated as a handshake
- #ssl_tls{epoch = Epoch,
- type = ?CHANGE_CIPHER_SPEC,
- version = _Version} when Epoch == CurrentEpoch-1 ->
- {State1, MoreActions} = send_handshake_flight(State0, CurrentEpoch),
- {NextRecord, State} = next_record(State1),
- next_event(StateName, NextRecord, State, Actions ++ MoreActions);
- _ ->
- next_event(StateName, no_record, State0, Actions)
- end;
-next_event(StateName, Record,
- #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) ->
- case Record of
- no_record ->
- {next_state, StateName, State0, Actions};
- #ssl_tls{epoch = CurrentEpoch,
- version = Version} = Record ->
- {next_state, StateName,
- dtls_version(StateName, Version, State0),
- [{next_event, internal, {protocol_record, Record}} | Actions]};
- #ssl_tls{epoch = _Epoch,
- version = _Version} = _Record ->
- %% TODO maybe buffer later epoch
- {Record, State} = next_record(State0),
- next_event(StateName, Record, State, Actions);
- #alert{} = Alert ->
- {next_state, StateName, State0, [{next_event, internal, Alert} | Actions]}
- end.
+dtls_handshake_events(Packets) ->
+ lists:map(fun(Packet) ->
+ {next_event, internal, {handshake, Packet}}
+ end, Packets).
decode_cipher_text(#state{protocol_buffers = #protocol_buffers{dtls_cipher_texts = [ CT | Rest]} = Buffers,
connection_states = ConnStates0} = State) ->
@@ -876,6 +799,178 @@ dtls_version(hello, Version, #state{role = server} = State) ->
dtls_version(_,_, State) ->
State.
+handle_client_hello(#client_hello{client_version = ClientVersion} = Hello,
+ #state{connection_states = ConnectionStates0,
+ port = Port, session = #session{own_certificate = Cert} = Session0,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
+ session_cache_cb = CacheCb,
+ negotiated_protocol = CurrentProtocol,
+ key_algorithm = KeyExAlg,
+ ssl_options = SslOpts} = State0) ->
+
+ case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
+ ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ClientVersion, hello, State0);
+ {Version, {Type, Session},
+ ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
+ Protocol = case Protocol0 of
+ undefined -> CurrentProtocol;
+ _ -> Protocol0
+ end,
+
+ State = prepare_flight(State0#state{connection_states = ConnectionStates,
+ negotiated_version = Version,
+ hashsign_algorithm = HashSign,
+ client_hello_version = ClientVersion,
+ session = Session,
+ negotiated_protocol = Protocol}),
+
+ ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
+ State, ?MODULE)
+ end.
+
+
+%% raw data from socket, unpack records
+handle_info({Protocol, _, _, _, Data}, StateName,
+ #state{data_tag = Protocol} = State0) ->
+ case next_dtls_record(Data, StateName, State0) of
+ {Record, State} ->
+ next_event(StateName, Record, State);
+ #alert{} = Alert ->
+ ssl_connection:handle_normal_shutdown(Alert, StateName, State0),
+ {stop, {shutdown, own_alert}}
+ end;
+handle_info({CloseTag, Socket}, StateName,
+ #state{socket = Socket,
+ socket_options = #socket_options{active = Active},
+ protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs},
+ close_tag = CloseTag,
+ negotiated_version = Version} = State) ->
+ %% Note that as of DTLS 1.2 (TLS 1.1),
+ %% failure to properly close a connection no longer requires that a
+ %% session not be resumed. This is a change from DTLS 1.0 to conform
+ %% with widespread implementation practice.
+ case (Active == false) andalso (CTs =/= []) of
+ false ->
+ case Version of
+ {254, N} when N =< 253 ->
+ ok;
+ _ ->
+ %% As invalidate_sessions here causes performance issues,
+ %% we will conform to the widespread implementation
+ %% practice and go aginst the spec
+ %%invalidate_session(Role, Host, Port, Session)
+ ok
+ end,
+ ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ {stop, {shutdown, transport_closed}};
+ true ->
+ %% Fixes non-delivery of final DTLS record in {active, once}.
+ %% Basically allows the application the opportunity to set {active, once} again
+ %% and then receive the final message.
+ next_event(StateName, no_record, State)
+ end;
+
+handle_info(new_cookie_secret, StateName,
+ #state{protocol_specific = #{current_cookie_secret := Secret} = CookieInfo} = State) ->
+ erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret),
+ {next_state, StateName, State#state{protocol_specific =
+ CookieInfo#{current_cookie_secret => dtls_v1:cookie_secret(),
+ previous_cookie_secret => Secret}}};
+handle_info(Msg, StateName, State) ->
+ ssl_connection:StateName(info, Msg, State, ?MODULE).
+
+handle_state_timeout(flight_retransmission_timeout, StateName,
+ #state{flight_state = {retransmit, NextTimeout}} = State0) ->
+ {State1, Actions0} = send_handshake_flight(State0#state{flight_state = {retransmit, NextTimeout}},
+ retransmit_epoch(StateName, State0)),
+ {Record, State2} = next_record(State1),
+ {next_state, StateName, State, Actions} = next_event(StateName, Record, State2, Actions0),
+ %% This will reset the retransmission timer by repeating the enter state event
+ {repeat_state, State, Actions}.
+
+handle_alerts([], Result) ->
+ Result;
+handle_alerts(_, {stop,_} = Stop) ->
+ Stop;
+handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
+ handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State));
+handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
+ handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
+
+handle_own_alert(Alert, Version, StateName, #state{transport_cb = gen_udp,
+ role = Role,
+ ssl_options = Options} = State0) ->
+ case ignore_alert(Alert, State0) of
+ {true, State} ->
+ log_ignore_alert(Options#ssl_options.log_alert, StateName, Alert, Role),
+ {next_state, StateName, State};
+ {false, State} ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State)
+ end;
+handle_own_alert(Alert, Version, StateName, State) ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State).
+
+encode_handshake_flight(Flight, Version, MaxFragmentSize, Epoch, ConnectionStates) ->
+ Fragments = lists:map(fun(Handshake) ->
+ dtls_handshake:fragment_handshake(Handshake, MaxFragmentSize)
+ end, Flight),
+ dtls_record:encode_handshake(Fragments, Version, Epoch, ConnectionStates).
+
+encode_change_cipher(#change_cipher_spec{}, Version, Epoch, ConnectionStates) ->
+ dtls_record:encode_change_cipher_spec(Version, Epoch, ConnectionStates).
+
+decode_alerts(Bin) ->
+ ssl_alert:decode(Bin).
+
+gen_handshake(StateName, Type, Event,
+ #state{negotiated_version = Version} = State) ->
+ try ssl_connection:StateName(Type, Event, State, ?MODULE) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+ malformed_handshake_data),
+ Version, StateName, State)
+ end.
+
+gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) ->
+ try handle_info(Event, StateName, State) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR,
+ malformed_data),
+ Version, StateName, State)
+ end;
+
+gen_info(Event, StateName, #state{negotiated_version = Version} = State) ->
+ try handle_info(Event, StateName, State) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+ malformed_handshake_data),
+ Version, StateName, State)
+ end.
+unprocessed_events(Events) ->
+ %% The first handshake event will be processed immediately
+ %% as it is entered first in the event queue and
+ %% when it is processed there will be length(Events)-1
+ %% handshake events left to process before we should
+ %% process more TLS-records received on the socket.
+ erlang:length(Events)-1.
+
+update_handshake_history(#hello_verify_request{}, _, Hist) ->
+ Hist;
+update_handshake_history(_, Handshake, Hist) ->
+ %% DTLS never needs option "v2_hello_compatible" to be true
+ ssl_handshake:update_handshake_history(Hist, iolist_to_binary(Handshake), false).
prepare_flight(#state{flight_buffer = Flight,
connection_states = ConnectionStates0,
protocol_buffers =
@@ -896,7 +991,7 @@ next_flight(Flight) ->
Flight#{handshakes => [],
change_cipher_spec => undefined,
handshakes_after_change_cipher_spec => []}.
-
+
handle_flight_timer(#state{transport_cb = gen_udp,
flight_state = {retransmit, Timeout}} = State) ->
start_retransmision_timer(Timeout, State);
@@ -916,74 +1011,67 @@ new_timeout(N) when N =< 30 ->
new_timeout(_) ->
60.
-dtls_handshake_events(Packets) ->
- lists:map(fun(Packet) ->
- {next_event, internal, {handshake, Packet}}
- end, Packets).
+send_handshake_flight(#state{socket = Socket,
+ transport_cb = Transport,
+ flight_buffer = #{handshakes := Flight,
+ change_cipher_spec := undefined},
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0, Epoch) ->
+ %% TODO remove hardcoded Max size
+ {Encoded, ConnectionStates} =
+ encode_handshake_flight(lists:reverse(Flight), Version, 1400, Epoch, ConnectionStates0),
+ send(Transport, Socket, Encoded),
+ {State0#state{connection_states = ConnectionStates}, []};
-renegotiate(#state{role = client} = State, Actions) ->
- %% Handle same way as if server requested
- %% the renegotiation
- Hs0 = ssl_handshake:init_handshake_history(),
- {next_state, connection, State#state{tls_handshake_history = Hs0,
- protocol_buffers = #protocol_buffers{}},
- [{next_event, internal, #hello_request{}} | Actions]};
+send_handshake_flight(#state{socket = Socket,
+ transport_cb = Transport,
+ flight_buffer = #{handshakes := [_|_] = Flight0,
+ change_cipher_spec := ChangeCipher,
+ handshakes_after_change_cipher_spec := []},
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0, Epoch) ->
+ {HsBefore, ConnectionStates1} =
+ encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0),
+ {EncChangeCipher, ConnectionStates} = encode_change_cipher(ChangeCipher, Version, Epoch, ConnectionStates1),
-renegotiate(#state{role = server,
- connection_states = CS0} = State0, Actions) ->
- HelloRequest = ssl_handshake:hello_request(),
- CS = CS0#{write_msg_seq => 0},
- {State1, MoreActions} = send_handshake(HelloRequest,
- State0#state{connection_states =
- CS}),
- Hs0 = ssl_handshake:init_handshake_history(),
- {Record, State} = next_record(State1#state{tls_handshake_history = Hs0,
- protocol_buffers = #protocol_buffers{}}),
- next_event(hello, Record, State, Actions ++ MoreActions).
+ send(Transport, Socket, [HsBefore, EncChangeCipher]),
+ {State0#state{connection_states = ConnectionStates}, []};
-handle_alerts([], Result) ->
- Result;
-handle_alerts(_, {stop,_} = Stop) ->
- Stop;
-handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
- handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State));
-handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
- handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
+send_handshake_flight(#state{socket = Socket,
+ transport_cb = Transport,
+ flight_buffer = #{handshakes := [_|_] = Flight0,
+ change_cipher_spec := ChangeCipher,
+ handshakes_after_change_cipher_spec := Flight1},
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0, Epoch) ->
+ {HsBefore, ConnectionStates1} =
+ encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0),
+ {EncChangeCipher, ConnectionStates2} =
+ encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates1),
+ {HsAfter, ConnectionStates} =
+ encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates2),
+ send(Transport, Socket, [HsBefore, EncChangeCipher, HsAfter]),
+ {State0#state{connection_states = ConnectionStates}, []};
+
+send_handshake_flight(#state{socket = Socket,
+ transport_cb = Transport,
+ flight_buffer = #{handshakes := [],
+ change_cipher_spec := ChangeCipher,
+ handshakes_after_change_cipher_spec := Flight1},
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0, Epoch) ->
+ {EncChangeCipher, ConnectionStates1} =
+ encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0),
+ {HsAfter, ConnectionStates} =
+ encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates1),
+ send(Transport, Socket, [EncChangeCipher, HsAfter]),
+ {State0#state{connection_states = ConnectionStates}, []}.
retransmit_epoch(_StateName, #state{connection_states = ConnectionStates}) ->
#{epoch := Epoch} =
ssl_record:current_connection_state(ConnectionStates, write),
Epoch.
-
-update_handshake_history(#hello_verify_request{}, _, Hist) ->
- Hist;
-update_handshake_history(_, Handshake, Hist) ->
- %% DTLS never needs option "v2_hello_compatible" to be true
- ssl_handshake:update_handshake_history(Hist, iolist_to_binary(Handshake), false).
-
-unprocessed_events(Events) ->
- %% The first handshake event will be processed immediately
- %% as it is entered first in the event queue and
- %% when it is processed there will be length(Events)-1
- %% handshake events left to process before we should
- %% process more TLS-records received on the socket.
- erlang:length(Events)-1.
-
-handle_own_alert(Alert, Version, StateName, #state{transport_cb = gen_udp,
- role = Role,
- ssl_options = Options} = State0) ->
- case ignore_alert(Alert, State0) of
- {true, State} ->
- log_ignore_alert(Options#ssl_options.log_alert, StateName, Alert, Role),
- {next_state, StateName, State};
- {false, State} ->
- ssl_connection:handle_own_alert(Alert, Version, StateName, State)
- end;
-handle_own_alert(Alert, Version, StateName, State) ->
- ssl_connection:handle_own_alert(Alert, Version, StateName, State).
-
-
ignore_alert(#alert{level = ?FATAL}, #state{protocol_specific = #{ignored_alerts := N,
max_ignored_alerts := N}} = State) ->
{false, State};
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 37a46b862e..6071eece13 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -16,6 +16,11 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
+
+%%----------------------------------------------------------------------
+%% Purpose: Help funtions for handling the DTLS (specific parts of)
+%%% SSL/TLS/DTLS handshake protocol
+%%----------------------------------------------------------------------
-module(dtls_handshake).
-include("dtls_connection.hrl").
@@ -24,15 +29,21 @@
-include("ssl_internal.hrl").
-include("ssl_alert.hrl").
+%% Handshake handling
-export([client_hello/8, client_hello/9, cookie/4, hello/4,
- hello_verify_request/2, get_dtls_handshake/3, fragment_handshake/2,
- handshake_bin/2, encode_handshake/3]).
+ hello_verify_request/2]).
+
+%% Handshake encoding
+-export([fragment_handshake/2, encode_handshake/3]).
+
+%% Handshake decodeing
+-export([get_dtls_handshake/3]).
-type dtls_handshake() :: #client_hello{} | #hello_verify_request{} |
ssl_handshake:ssl_handshake().
%%====================================================================
-%% Internal application API
+%% Handshake handling
%%====================================================================
%%--------------------------------------------------------------------
-spec client_hello(host(), inet:port_number(), ssl_record:connection_states(),
@@ -56,7 +67,8 @@ client_hello(Host, Port, ConnectionStates, SslOpts,
%%--------------------------------------------------------------------
client_hello(Host, Port, Cookie, ConnectionStates,
#ssl_options{versions = Versions,
- ciphers = UserSuites
+ ciphers = UserSuites,
+ fallback = Fallback
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
Version = dtls_record:highest_protocol_version(Versions),
@@ -66,12 +78,15 @@ client_hello(Host, Port, Cookie, ConnectionStates,
CipherSuites = ssl_handshake:available_suites(UserSuites, TLSVersion),
Extensions = ssl_handshake:client_hello_extensions(TLSVersion, CipherSuites,
- SslOpts, ConnectionStates, Renegotiation),
+ SslOpts, ConnectionStates,
+ Renegotiation),
Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
#client_hello{session_id = Id,
client_version = Version,
- cipher_suites = ssl_handshake:cipher_suites(CipherSuites, Renegotiation),
+ cipher_suites =
+ ssl_handshake:cipher_suites(CipherSuites,
+ Renegotiation, Fallback),
compression_methods = ssl_record:compressions(),
random = SecParams#security_parameters.client_random,
cookie = Cookie,
@@ -87,11 +102,11 @@ hello(#server_hello{server_version = Version, random = Random,
case dtls_record:is_acceptable_version(Version, SupportedVersions) of
true ->
handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
- Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation);
+ Compression, HelloExt, SslOpt,
+ ConnectionStates0, Renegotiation);
false ->
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end;
-
hello(#client_hello{client_version = ClientVersion} = Hello,
#ssl_options{versions = Versions} = SslOpts,
Info, Renegotiation) ->
@@ -107,7 +122,7 @@ cookie(Key, Address, Port, #client_hello{client_version = {Major, Minor},
<<?BYTE(Major), ?BYTE(Minor)>>,
Random, SessionId, CipherSuites, CompressionMethods],
crypto:hmac(sha, Key, CookieData).
-
+%%--------------------------------------------------------------------
-spec hello_verify_request(binary(), dtls_record:dtls_version()) -> #hello_verify_request{}.
%%
%% Description: Creates a hello verify request message sent by server to
@@ -117,11 +132,8 @@ hello_verify_request(Cookie, Version) ->
#hello_verify_request{protocol_version = Version, cookie = Cookie}.
%%--------------------------------------------------------------------
-
-encode_handshake(Handshake, Version, Seq) ->
- {MsgType, Bin} = enc_handshake(Handshake, Version),
- Len = byte_size(Bin),
- [MsgType, ?uint24(Len), ?uint16(Seq), ?uint24(0), ?uint24(Len), Bin].
+%%% Handshake encoding
+%%--------------------------------------------------------------------
fragment_handshake(Bin, _) when is_binary(Bin)->
%% This is the change_cipher_spec not a "real handshake" but part of the flight
@@ -129,10 +141,15 @@ fragment_handshake(Bin, _) when is_binary(Bin)->
fragment_handshake([MsgType, Len, Seq, _, Len, Bin], Size) ->
Bins = bin_fragments(Bin, Size),
handshake_fragments(MsgType, Seq, Len, Bins, []).
+encode_handshake(Handshake, Version, Seq) ->
+ {MsgType, Bin} = enc_handshake(Handshake, Version),
+ Len = byte_size(Bin),
+ [MsgType, ?uint24(Len), ?uint16(Seq), ?uint24(0), ?uint24(Len), Bin].
+
+%%--------------------------------------------------------------------
+%%% Handshake decodeing
+%%--------------------------------------------------------------------
-handshake_bin([Type, Length, Data], Seq) ->
- handshake_bin(Type, Length, Seq, Data).
-
%%--------------------------------------------------------------------
-spec get_dtls_handshake(dtls_record:dtls_version(), binary(), #protocol_buffers{}) ->
{[dtls_handshake()], #protocol_buffers{}}.
@@ -147,16 +164,19 @@ get_dtls_handshake(Version, Fragment, ProtocolBuffers) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-handle_client_hello(Version, #client_hello{session_id = SugesstedId,
- cipher_suites = CipherSuites,
- compression_methods = Compressions,
- random = Random,
- extensions =
- #hello_extensions{elliptic_curves = Curves,
- signature_algs = ClientHashSigns} = HelloExt},
+handle_client_hello(Version,
+ #client_hello{session_id = SugesstedId,
+ cipher_suites = CipherSuites,
+ compression_methods = Compressions,
+ random = Random,
+ extensions =
+ #hello_extensions{elliptic_curves = Curves,
+ signature_algs = ClientHashSigns}
+ = HelloExt},
#ssl_options{versions = Versions,
signature_algs = SupportedHashSigns} = SslOpts,
- {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) ->
+ {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _},
+ Renegotiation) ->
case dtls_record:is_acceptable_version(Version, Versions) of
true ->
TLSVersion = dtls_v1:corresponding_tls_version(Version),
@@ -164,14 +184,15 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId,
ClientHashSigns, SupportedHashSigns, Cert,TLSVersion),
ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(TLSVersion)),
{Type, #session{cipher_suite = CipherSuite} = Session1}
- = ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions,
+ = ssl_handshake:select_session(SugesstedId, CipherSuites,
+ AvailableHashSigns, Compressions,
Port, Session0#session{ecc = ECCCurve}, TLSVersion,
SslOpts, Cache, CacheCb, Cert),
case CipherSuite of
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
_ ->
- {KeyExAlg,_,_,_} = ssl_cipher:suite_definition(CipherSuite),
+ #{key_exchange := KeyExAlg} = ssl_cipher:suite_definition(CipherSuite),
case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg,
SupportedHashSigns, TLSVersion) of
#alert{} = Alert ->
@@ -190,7 +211,8 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites,
HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation, HashSign) ->
try ssl_handshake:handle_client_hello_extensions(dtls_record, Random, CipherSuites,
HelloExt, dtls_v1:corresponding_tls_version(Version),
- SslOpts, Session0, ConnectionStates0, Renegotiation) of
+ SslOpts, Session0,
+ ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
Alert;
{Session, ConnectionStates, Protocol, ServerHelloExt} ->
@@ -212,7 +234,7 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
end.
-%%%%%%% Encodeing %%%%%%%%%%%%%
+%%--------------------------------------------------------------------
enc_handshake(#hello_verify_request{protocol_version = {Major, Minor},
cookie = Cookie}, _Version) ->
@@ -220,7 +242,6 @@ enc_handshake(#hello_verify_request{protocol_version = {Major, Minor},
{?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
?BYTE(CookieLength),
Cookie:CookieLength/binary>>};
-
enc_handshake(#hello_request{}, _Version) ->
{?HELLO_REQUEST, <<>>};
enc_handshake(#client_hello{client_version = {Major, Minor},
@@ -243,19 +264,29 @@ enc_handshake(#client_hello{client_version = {Major, Minor},
?BYTE(CookieLength), Cookie/binary,
?UINT16(CsLength), BinCipherSuites/binary,
?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
-
enc_handshake(#server_hello{} = HandshakeMsg, Version) ->
{Type, <<?BYTE(Major), ?BYTE(Minor), Rest/binary>>} =
ssl_handshake:encode_handshake(HandshakeMsg, Version),
{DTLSMajor, DTLSMinor} = dtls_v1:corresponding_dtls_version({Major, Minor}),
{Type, <<?BYTE(DTLSMajor), ?BYTE(DTLSMinor), Rest/binary>>};
-
enc_handshake(HandshakeMsg, Version) ->
ssl_handshake:encode_handshake(HandshakeMsg, dtls_v1:corresponding_tls_version(Version)).
+handshake_bin(#handshake_fragment{
+ type = Type,
+ length = Len,
+ message_seq = Seq,
+ fragment_length = Len,
+ fragment_offset = 0,
+ fragment = Fragment}) ->
+ handshake_bin(Type, Len, Seq, Fragment).
+handshake_bin(Type, Length, Seq, FragmentData) ->
+ <<?BYTE(Type), ?UINT24(Length),
+ ?UINT16(Seq), ?UINT24(0), ?UINT24(Length),
+ FragmentData:Length/binary>>.
+
bin_fragments(Bin, Size) ->
bin_fragments(Bin, size(Bin), Size, 0, []).
-
bin_fragments(Bin, BinSize, FragSize, Offset, Fragments) ->
case (BinSize - Offset - FragSize) > 0 of
true ->
@@ -279,7 +310,7 @@ address_to_bin({A,B,C,D}, Port) ->
address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
<<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
-%%%%%%% Decodeing %%%%%%%%%%%%%
+%%--------------------------------------------------------------------
handle_fragments(Version, FragmentData, Buffers0, Acc) ->
Fragments = decode_handshake_fragments(FragmentData),
@@ -322,7 +353,6 @@ decode_handshake(_Version, ?CLIENT_HELLO, <<?UINT24(_), ?UINT16(_),
compression_methods = Comp_methods,
extensions = DecodedExtensions
};
-
decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?UINT24(_), ?UINT16(_),
?UINT24(_), ?UINT24(_),
?BYTE(Major), ?BYTE(Minor),
@@ -330,7 +360,6 @@ decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?UINT24(_), ?UINT16(_),
Cookie:CookieLength/binary>>) ->
#hello_verify_request{protocol_version = {Major, Minor},
cookie = Cookie};
-
decode_handshake(Version, Tag, <<?UINT24(_), ?UINT16(_),
?UINT24(_), ?UINT24(_), Msg/binary>>) ->
%% DTLS specifics stripped
@@ -370,9 +399,10 @@ reassemble(Version, #handshake_fragment{message_seq = Seq} = Fragment,
end;
reassemble(_, #handshake_fragment{message_seq = FragSeq} = Fragment,
#protocol_buffers{dtls_handshake_next_seq = Seq,
- dtls_handshake_later_fragments = LaterFragments} = Buffers0) when FragSeq > Seq->
- {more_data,
- Buffers0#protocol_buffers{dtls_handshake_later_fragments = [Fragment | LaterFragments]}};
+ dtls_handshake_later_fragments = LaterFragments}
+ = Buffers0) when FragSeq > Seq->
+ {more_data,
+ Buffers0#protocol_buffers{dtls_handshake_later_fragments = [Fragment | LaterFragments]}};
reassemble(_, _, Buffers) ->
%% Disregard fragments FragSeq < Seq
{more_data, Buffers}.
@@ -396,26 +426,6 @@ merge_fragment(Frag0, [Frag1 | Rest]) ->
Frag ->
merge_fragment(Frag, Rest)
end.
-
-is_complete_handshake(#handshake_fragment{length = Length, fragment_length = Length}) ->
- true;
-is_complete_handshake(_) ->
- false.
-
-next_fragments(LaterFragments) ->
- case lists:keysort(#handshake_fragment.message_seq, LaterFragments) of
- [] ->
- {[], []};
- [#handshake_fragment{message_seq = Seq} | _] = Fragments ->
- split_frags(Fragments, Seq, [])
- end.
-
-split_frags([#handshake_fragment{message_seq = Seq} = Frag | Rest], Seq, Acc) ->
- split_frags(Rest, Seq, [Frag | Acc]);
-split_frags(Frags, _, Acc) ->
- {lists:reverse(Acc), Frags}.
-
-
%% Duplicate
merge_fragments(#handshake_fragment{
fragment_offset = PreviousOffSet,
@@ -486,17 +496,26 @@ merge_fragments(#handshake_fragment{
%% No merge there is a gap
merge_fragments(Previous, Current) ->
[Previous, Current].
-
-handshake_bin(#handshake_fragment{
- type = Type,
- length = Len,
- message_seq = Seq,
- fragment_length = Len,
- fragment_offset = 0,
- fragment = Fragment}) ->
- handshake_bin(Type, Len, Seq, Fragment).
-handshake_bin(Type, Length, Seq, FragmentData) ->
- <<?BYTE(Type), ?UINT24(Length),
- ?UINT16(Seq), ?UINT24(0), ?UINT24(Length),
- FragmentData:Length/binary>>.
+next_fragments(LaterFragments) ->
+ case lists:keysort(#handshake_fragment.message_seq, LaterFragments) of
+ [] ->
+ {[], []};
+ [#handshake_fragment{message_seq = Seq} | _] = Fragments ->
+ split_frags(Fragments, Seq, [])
+ end.
+
+split_frags([#handshake_fragment{message_seq = Seq} = Frag | Rest], Seq, Acc) ->
+ split_frags(Rest, Seq, [Frag | Acc]);
+split_frags(Frags, _, Acc) ->
+ {lists:reverse(Acc), Frags}.
+
+is_complete_handshake(#handshake_fragment{length = Length, fragment_length = Length}) ->
+ true;
+is_complete_handshake(_) ->
+ false.
+
+
+
+
+
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 8a7f8c1d0a..316de05532 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -30,15 +30,17 @@
-include("ssl_cipher.hrl").
%% Handling of incoming data
--export([get_dtls_records/2, init_connection_states/2]).
+-export([get_dtls_records/3, init_connection_states/2, empty_connection_state/1]).
-%% Decoding
--export([decode_cipher_text/2]).
+-export([save_current_connection_state/2, next_epoch/2, get_connection_state_by_epoch/3, replay_detect/2,
+ init_connection_state_seq/2, current_connection_state_epoch/2]).
%% Encoding
-export([encode_handshake/4, encode_alert_record/3,
- encode_change_cipher_spec/3, encode_data/3]).
--export([encode_plain_text/5]).
+ encode_change_cipher_spec/3, encode_data/3, encode_plain_text/5]).
+
+%% Decoding
+-export([decode_cipher_text/2]).
%% Protocol version handling
-export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2,
@@ -46,9 +48,6 @@
is_higher/2, supported_protocol_versions/0,
is_acceptable_version/2, hello_version/2]).
--export([save_current_connection_state/2, next_epoch/2, get_connection_state_by_epoch/3, replay_detect/2]).
-
--export([init_connection_state_seq/2, current_connection_state_epoch/2]).
-export_type([dtls_version/0, dtls_atom_version/0]).
@@ -60,7 +59,7 @@
-compile(inline).
%%====================================================================
-%% Internal application API
+%% Handling of incoming data
%%====================================================================
%%--------------------------------------------------------------------
-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled) ->
@@ -75,7 +74,7 @@ init_connection_states(Role, BeastMitigation) ->
Initial = initial_connection_state(ConnectionEnd, BeastMitigation),
Current = Initial#{epoch := 0},
InitialPending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation),
- Pending = InitialPending#{epoch => undefined, replay_window => init_replay_window(?REPLAY_WINDOW_SIZE)},
+ Pending = empty_connection_state(InitialPending),
#{saved_read => Current,
current_read => Current,
pending_read => Pending,
@@ -83,6 +82,9 @@ init_connection_states(Role, BeastMitigation) ->
current_write => Current,
pending_write => Pending}.
+empty_connection_state(Empty) ->
+ Empty#{epoch => undefined, replay_window => init_replay_window(?REPLAY_WINDOW_SIZE)}.
+
%%--------------------------------------------------------------------
-spec save_current_connection_state(ssl_record:connection_states(), read | write) ->
ssl_record:connection_states().
@@ -133,67 +135,58 @@ set_connection_state_by_epoch(ReadState, Epoch, #{saved_read := #{epoch := Epoch
States#{saved_read := ReadState}.
%%--------------------------------------------------------------------
--spec get_dtls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
+-spec init_connection_state_seq(dtls_version(), ssl_record:connection_states()) ->
+ ssl_record:connection_state().
+%%
+%% Description: Copy the read sequence number to the write sequence number
+%% This is only valid for DTLS in the first client_hello
+%%--------------------------------------------------------------------
+init_connection_state_seq({254, _},
+ #{current_read := #{epoch := 0, sequence_number := Seq},
+ current_write := #{epoch := 0} = Write} = ConnnectionStates0) ->
+ ConnnectionStates0#{current_write => Write#{sequence_number => Seq}};
+init_connection_state_seq(_, ConnnectionStates) ->
+ ConnnectionStates.
+
+%%--------------------------------------------------------
+-spec current_connection_state_epoch(ssl_record:connection_states(), read | write) ->
+ integer().
+%%
+%% Description: Returns the epoch the connection_state record
+%% that is currently defined as the current connection state.
+%%--------------------------------------------------------------------
+current_connection_state_epoch(#{current_read := #{epoch := Epoch}},
+ read) ->
+ Epoch;
+current_connection_state_epoch(#{current_write := #{epoch := Epoch}},
+ write) ->
+ Epoch.
+
+%%--------------------------------------------------------------------
+-spec get_dtls_records(binary(), [dtls_version()], binary()) -> {[binary()], binary()} | #alert{}.
%%
%% Description: Given old buffer and new data from UDP/SCTP, packs up a records
%% and returns it as a list of tls_compressed binaries also returns leftover
%% data
%%--------------------------------------------------------------------
-get_dtls_records(Data, <<>>) ->
- get_dtls_records_aux(Data, []);
-get_dtls_records(Data, Buffer) ->
- get_dtls_records_aux(list_to_binary([Buffer, Data]), []).
-
-get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Epoch), ?UINT48(SequenceNumber),
- ?UINT16(Length), Data:Length/binary, Rest/binary>>,
- Acc) ->
- get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
- version = {MajVer, MinVer},
- epoch = Epoch, sequence_number = SequenceNumber,
- fragment = Data} | Acc]);
-get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Epoch), ?UINT48(SequenceNumber),
- ?UINT16(Length),
- Data:Length/binary, Rest/binary>>, Acc) when MajVer >= 128 ->
- get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
- version = {MajVer, MinVer},
- epoch = Epoch, sequence_number = SequenceNumber,
- fragment = Data} | Acc]);
-get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Epoch), ?UINT48(SequenceNumber),
- ?UINT16(Length), Data:Length/binary,
- Rest/binary>>, Acc) ->
- get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
- version = {MajVer, MinVer},
- epoch = Epoch, sequence_number = SequenceNumber,
- fragment = Data} | Acc]);
-get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Epoch), ?UINT48(SequenceNumber),
- ?UINT16(Length), Data:Length/binary, Rest/binary>>,
- Acc) ->
- get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
- version = {MajVer, MinVer},
- epoch = Epoch, sequence_number = SequenceNumber,
- fragment = Data} | Acc]);
-
-get_dtls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
- ?UINT16(Length), _/binary>>,
- _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH ->
- ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
-
-get_dtls_records_aux(<<1:1, Length0:15, _/binary>>,_Acc)
- when Length0 > ?MAX_CIPHER_TEXT_LENGTH ->
- ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
-
-get_dtls_records_aux(Data, Acc) ->
- case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of
- true ->
- {lists:reverse(Acc), Data};
- false ->
- ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
+get_dtls_records(Data, Versions, Buffer) ->
+ BinData = list_to_binary([Buffer, Data]),
+ case erlang:byte_size(BinData) of
+ N when N >= 3 ->
+ case assert_version(BinData, Versions) of
+ true ->
+ get_dtls_records_aux(BinData, []);
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+ _ ->
+ get_dtls_records_aux(BinData, [])
end.
+%%====================================================================
+%% Encoding DTLS records
+%%====================================================================
+
%%--------------------------------------------------------------------
-spec encode_handshake(iolist(), dtls_version(), integer(), ssl_record:connection_states()) ->
{iolist(), ssl_record:connection_states()}.
@@ -241,11 +234,19 @@ encode_plain_text(Type, Version, Epoch, Data, ConnectionStates) ->
{CipherText, Write} = encode_dtls_cipher_text(Type, Version, CipherFragment, Write1),
{CipherText, set_connection_state_by_epoch(Write, Epoch, ConnectionStates, write)}.
+%%====================================================================
+%% Decoding
+%%====================================================================
decode_cipher_text(#ssl_tls{epoch = Epoch} = CipherText, ConnnectionStates0) ->
ReadState = get_connection_state_by_epoch(Epoch, ConnnectionStates0, read),
decode_cipher_text(CipherText, ReadState, ConnnectionStates0).
+
+%%====================================================================
+%% Protocol version handling
+%%====================================================================
+
%%--------------------------------------------------------------------
-spec protocol_version(dtls_atom_version() | dtls_version()) ->
dtls_version() | dtls_atom_version().
@@ -377,35 +378,6 @@ supported_protocol_versions([_|_] = Vsns) ->
is_acceptable_version(Version, Versions) ->
lists:member(Version, Versions).
-
-%%--------------------------------------------------------------------
--spec init_connection_state_seq(dtls_version(), ssl_record:connection_states()) ->
- ssl_record:connection_state().
-%%
-%% Description: Copy the read sequence number to the write sequence number
-%% This is only valid for DTLS in the first client_hello
-%%--------------------------------------------------------------------
-init_connection_state_seq({254, _},
- #{current_read := #{epoch := 0, sequence_number := Seq},
- current_write := #{epoch := 0} = Write} = ConnnectionStates0) ->
- ConnnectionStates0#{current_write => Write#{sequence_number => Seq}};
-init_connection_state_seq(_, ConnnectionStates) ->
- ConnnectionStates.
-
-%%--------------------------------------------------------
--spec current_connection_state_epoch(ssl_record:connection_states(), read | write) ->
- integer().
-%%
-%% Description: Returns the epoch the connection_state record
-%% that is currently defined as the current connection state.
-%%--------------------------------------------------------------------
-current_connection_state_epoch(#{current_read := #{epoch := Epoch}},
- read) ->
- Epoch;
-current_connection_state_epoch(#{current_write := #{epoch := Epoch}},
- write) ->
- Epoch.
-
-spec hello_version(dtls_version(), [dtls_version()]) -> dtls_version().
hello_version(Version, Versions) ->
case dtls_v1:corresponding_tls_version(Version) of
@@ -433,16 +405,92 @@ initial_connection_state(ConnectionEnd, BeastMitigation) ->
client_verify_data => undefined,
server_verify_data => undefined
}.
+assert_version(<<?BYTE(_), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>>, Versions) ->
+ is_acceptable_version({MajVer, MinVer}, Versions).
-lowest_list_protocol_version(Ver, []) ->
- Ver;
-lowest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
- lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest).
+get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Epoch), ?UINT48(SequenceNumber),
+ ?UINT16(Length), Data:Length/binary, Rest/binary>>,
+ Acc) ->
+ get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
+ version = {MajVer, MinVer},
+ epoch = Epoch, sequence_number = SequenceNumber,
+ fragment = Data} | Acc]);
+get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Epoch), ?UINT48(SequenceNumber),
+ ?UINT16(Length),
+ Data:Length/binary, Rest/binary>>, Acc) when MajVer >= 128 ->
+ get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
+ version = {MajVer, MinVer},
+ epoch = Epoch, sequence_number = SequenceNumber,
+ fragment = Data} | Acc]);
+get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Epoch), ?UINT48(SequenceNumber),
+ ?UINT16(Length), Data:Length/binary,
+ Rest/binary>>, Acc) ->
+ get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
+ version = {MajVer, MinVer},
+ epoch = Epoch, sequence_number = SequenceNumber,
+ fragment = Data} | Acc]);
+get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Epoch), ?UINT48(SequenceNumber),
+ ?UINT16(Length), Data:Length/binary, Rest/binary>>,
+ Acc) ->
+ get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
+ version = {MajVer, MinVer},
+ epoch = Epoch, sequence_number = SequenceNumber,
+ fragment = Data} | Acc]);
-highest_list_protocol_version(Ver, []) ->
- Ver;
-highest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
- highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest).
+get_dtls_records_aux(<<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer),
+ ?UINT16(Length), _/binary>>,
+ _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH ->
+ ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
+
+get_dtls_records_aux(Data, Acc) ->
+ case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of
+ true ->
+ {lists:reverse(Acc), Data};
+ false ->
+ ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
+ end.
+%%--------------------------------------------------------------------
+
+init_replay_window(Size) ->
+ #{size => Size,
+ top => Size,
+ bottom => 0,
+ mask => 0 bsl 64
+ }.
+
+replay_detect(#ssl_tls{sequence_number = SequenceNumber}, #{replay_window := Window}) ->
+ is_replay(SequenceNumber, Window).
+
+
+is_replay(SequenceNumber, #{bottom := Bottom}) when SequenceNumber < Bottom ->
+ true;
+is_replay(SequenceNumber, #{size := Size,
+ top := Top,
+ bottom := Bottom,
+ mask := Mask}) when (SequenceNumber >= Bottom) andalso (SequenceNumber =< Top) ->
+ Index = (SequenceNumber rem Size),
+ (Index band Mask) == 1;
+
+is_replay(_, _) ->
+ false.
+
+update_replay_window(SequenceNumber, #{replay_window := #{size := Size,
+ top := Top,
+ bottom := Bottom,
+ mask := Mask0} = Window0} = ConnectionStates) ->
+ NoNewBits = SequenceNumber - Top,
+ Index = SequenceNumber rem Size,
+ Mask = (Mask0 bsl NoNewBits) bor Index,
+ Window = Window0#{top => SequenceNumber,
+ bottom => Bottom + NoNewBits,
+ mask => Mask},
+ ConnectionStates#{replay_window := Window}.
+
+%%--------------------------------------------------------------------
encode_dtls_cipher_text(Type, {MajVer, MinVer}, Fragment,
#{epoch := Epoch, sequence_number := Seq} = WriteState) ->
@@ -486,6 +534,7 @@ encode_plain_text(Type, Version, Fragment, #{compression_state := CompS0,
ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MAC, Fragment, TLSVersion),
{CipherFragment, WriteState0#{cipher_state => CipherS1}}.
+%%--------------------------------------------------------------------
decode_cipher_text(#ssl_tls{type = Type, version = Version,
epoch = Epoch,
sequence_number = Seq,
@@ -537,6 +586,7 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
false ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end.
+%%--------------------------------------------------------------------
calc_mac_hash(Type, Version, #{mac_secret := MacSecret,
security_parameters := #security_parameters{mac_algorithm = MacAlg}},
@@ -545,16 +595,6 @@ calc_mac_hash(Type, Version, #{mac_secret := MacSecret,
mac_hash(Version, MacAlg, MacSecret, Epoch, SeqNo, Type,
Length, Fragment).
-highest_protocol_version() ->
- highest_protocol_version(supported_protocol_versions()).
-
-lowest_protocol_version() ->
- lowest_protocol_version(supported_protocol_versions()).
-
-sufficient_dtlsv1_2_crypto_support() ->
- CryptoSupport = crypto:supports(),
- proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
-
mac_hash({Major, Minor}, MacAlg, MacSecret, Epoch, SeqNo, Type, Length, Fragment) ->
Value = [<<?UINT16(Epoch), ?UINT48(SeqNo), ?BYTE(Type),
?BYTE(Major), ?BYTE(Minor), ?UINT16(Length)>>,
@@ -564,37 +604,25 @@ mac_hash({Major, Minor}, MacAlg, MacSecret, Epoch, SeqNo, Type, Length, Fragment
calc_aad(Type, {MajVer, MinVer}, Epoch, SeqNo) ->
<<?UINT16(Epoch), ?UINT48(SeqNo), ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
-init_replay_window(Size) ->
- #{size => Size,
- top => Size,
- bottom => 0,
- mask => 0 bsl 64
- }.
+%%--------------------------------------------------------------------
-replay_detect(#ssl_tls{sequence_number = SequenceNumber}, #{replay_window := Window}) ->
- is_replay(SequenceNumber, Window).
+lowest_list_protocol_version(Ver, []) ->
+ Ver;
+lowest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest).
+highest_list_protocol_version(Ver, []) ->
+ Ver;
+highest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest).
-is_replay(SequenceNumber, #{bottom := Bottom}) when SequenceNumber < Bottom ->
- true;
-is_replay(SequenceNumber, #{size := Size,
- top := Top,
- bottom := Bottom,
- mask := Mask}) when (SequenceNumber >= Bottom) andalso (SequenceNumber =< Top) ->
- Index = (SequenceNumber rem Size),
- (Index band Mask) == 1;
+highest_protocol_version() ->
+ highest_protocol_version(supported_protocol_versions()).
-is_replay(_, _) ->
- false.
+lowest_protocol_version() ->
+ lowest_protocol_version(supported_protocol_versions()).
+
+sufficient_dtlsv1_2_crypto_support() ->
+ CryptoSupport = crypto:supports(),
+ proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
-update_replay_window(SequenceNumber, #{replay_window := #{size := Size,
- top := Top,
- bottom := Bottom,
- mask := Mask0} = Window0} = ConnectionStates) ->
- NoNewBits = SequenceNumber - Top,
- Index = SequenceNumber rem Size,
- Mask = (Mask0 bsl NoNewBits) bor Index,
- Window = Window0#{top => SequenceNumber,
- bottom => Bottom + NoNewBits,
- mask => Mask},
- ConnectionStates#{replay_window := Window}.
diff --git a/lib/ssl/src/dtls_socket.erl b/lib/ssl/src/dtls_socket.erl
index 5f854fbb4b..0e4ab089dc 100644
--- a/lib/ssl/src/dtls_socket.erl
+++ b/lib/ssl/src/dtls_socket.erl
@@ -24,7 +24,7 @@
-export([send/3, listen/3, accept/3, connect/4, socket/4, setopts/3, getopts/3, getstat/3,
peername/2, sockname/2, port/2, close/2]).
--export([emulated_options/0, internal_inet_values/0, default_inet_values/0, default_cb_info/0]).
+-export([emulated_options/0, emulated_options/1, internal_inet_values/0, default_inet_values/0, default_cb_info/0]).
send(Transport, {{IP,Port},Socket}, Data) ->
Transport:send(Socket, IP, Port, Data).
@@ -133,6 +133,9 @@ port(Transport, Socket) ->
emulated_options() ->
[mode, active, packet, packet_size].
+emulated_options(Opts) ->
+ emulated_options(Opts, internal_inet_values(), default_inet_values()).
+
internal_inet_values() ->
[{active, false}, {mode,binary}].
@@ -158,3 +161,29 @@ emulated_socket_options(InetValues, #socket_options{
packet_size = proplists:get_value(packet_size, InetValues, PacketSize),
active = proplists:get_value(active, InetValues, Active)
}.
+
+emulated_options([{mode, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(mode, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(mode, Emulated)]);
+emulated_options([{header, _} = Opt | _], _, _) ->
+ throw({error, {options, {not_supported, Opt}}});
+emulated_options([{active, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(active, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(active, Emulated)]);
+emulated_options([{packet, _} = Opt | _], _, _) ->
+ throw({error, {options, {not_supported, Opt}}});
+emulated_options([{packet_size, _} = Opt | _], _, _) ->
+ throw({error, {options, {not_supported, Opt}}});
+emulated_options([Opt|Opts], Inet, Emulated) ->
+ emulated_options(Opts, [Opt|Inet], Emulated);
+emulated_options([], Inet,Emulated) ->
+ {Inet, Emulated}.
+
+validate_inet_option(mode, Value)
+ when Value =/= list, Value =/= binary ->
+ throw({error, {options, {mode,Value}}});
+validate_inet_option(active, Value)
+ when Value =/= true, Value =/= false, Value =/= once ->
+ throw({error, {options, {active,Value}}});
+validate_inet_option(_, _) ->
+ ok.
diff --git a/lib/ssl/src/dtls_udp_listener.erl b/lib/ssl/src/dtls_udp_listener.erl
index c789a32087..12e54a0e51 100644
--- a/lib/ssl/src/dtls_udp_listener.erl
+++ b/lib/ssl/src/dtls_udp_listener.erl
@@ -84,7 +84,7 @@ init([Port, EmOpts, InetOptions, DTLSOptions]) ->
listner = Socket,
close = false}}
catch _:_ ->
- {error, closed}
+ {stop, {shutdown, {error, closed}}}
end.
handle_call({accept, _}, _, #state{close = true} = State) ->
{reply, {error, closed}, State};
@@ -153,15 +153,18 @@ handle_info({udp_error, Socket, Error}, #state{listner = Socket} = State) ->
handle_info({'DOWN', _, process, Pid, _}, #state{clients = Clients,
dtls_processes = Processes0,
+ dtls_msq_queues = MsgQueues0,
close = ListenClosed} = State) ->
Client = kv_get(Pid, Processes0),
Processes = kv_delete(Pid, Processes0),
+ MsgQueues = kv_delete(Client, MsgQueues0),
case ListenClosed andalso kv_empty(Processes) of
true ->
{stop, normal, State};
false ->
{noreply, State#state{clients = set_delete(Client, Clients),
- dtls_processes = Processes}}
+ dtls_processes = Processes,
+ dtls_msq_queues = MsgQueues}}
end.
terminate(_Reason, _State) ->
diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl
index 51ee8ec047..0f6344b6f7 100644
--- a/lib/ssl/src/dtls_v1.erl
+++ b/lib/ssl/src/dtls_v1.erl
@@ -21,7 +21,7 @@
-include("ssl_cipher.hrl").
--export([suites/1, all_suites/1, hmac_hash/3, ecc_curves/1,
+-export([suites/1, all_suites/1, anonymous_suites/1,hmac_hash/3, ecc_curves/1,
corresponding_tls_version/1, corresponding_dtls_version/1,
cookie_secret/0, cookie_timeout/0]).
@@ -40,6 +40,12 @@ all_suites(Version) ->
end,
ssl_cipher:all_suites(corresponding_tls_version(Version))).
+anonymous_suites(Version) ->
+ lists:filter(fun(Cipher) ->
+ is_acceptable_cipher(ssl_cipher:suite_definition(Cipher))
+ end,
+ ssl_cipher:anonymous_suites(corresponding_tls_version(Version))).
+
hmac_hash(MacAlg, MacSecret, Value) ->
tls_v1:hmac_hash(MacAlg, MacSecret, Value).
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index 0da4b3587f..4c677b9c33 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -93,7 +93,11 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
?trace("port_please(~p) -> version ~p~n",
[Node,Version]),
dist_util:reset_timer(Timer),
- case ssl_tls_dist_proxy:connect(Driver, Ip, TcpPort) of
+ case
+ ssl_tls_dist_proxy:connect(
+ Driver, Address, TcpPort,
+ [{server_name_indication, atom_to_list(Node)}])
+ of
{ok, Socket} ->
HSData = connect_hs_data(Kernel, Node, MyNode, Socket,
Timer, Version, Ip, TcpPort, Address,
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 51407ef3b9..762aa2f8d8 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -64,6 +64,6 @@
{env, []},
{mod, {ssl_app, []}},
{runtime_dependencies, ["stdlib-3.2","public_key-1.5","kernel-3.0",
- "erts-7.0","crypto-3.3", "inets-5.10.7"]}]}.
+ "erts-7.0","crypto-4.2", "inets-5.10.7"]}]}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 054e3b7ae3..fb4448e180 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -39,7 +39,9 @@
]).
%% SSL/TLS protocol handling
--export([cipher_suites/0, cipher_suites/1, eccs/0, eccs/1, versions/0,
+-export([cipher_suites/0, cipher_suites/1, cipher_suites/2, filter_cipher_suites/2,
+ prepend_cipher_suites/2, append_cipher_suites/2,
+ eccs/0, eccs/1, versions/0,
format_error/1, renegotiate/1, prf/5, negotiated_protocol/1,
connection_information/1, connection_information/2]).
%% Misc
@@ -374,25 +376,98 @@ negotiated_protocol(#sslsocket{pid = Pid}) ->
ssl_connection:negotiated_protocol(Pid).
%%--------------------------------------------------------------------
--spec cipher_suites() -> [ssl_cipher:erl_cipher_suite()] | [string()].
+-spec cipher_suites() -> [ssl_cipher:old_erl_cipher_suite()] | [string()].
%%--------------------------------------------------------------------
cipher_suites() ->
cipher_suites(erlang).
%%--------------------------------------------------------------------
--spec cipher_suites(erlang | openssl | all) -> [ssl_cipher:erl_cipher_suite()] |
- [string()].
+-spec cipher_suites(erlang | openssl | all) ->
+ [ssl_cipher:old_erl_cipher_suite() | string()].
%% Description: Returns all supported cipher suites.
%%--------------------------------------------------------------------
cipher_suites(erlang) ->
[ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(default)];
cipher_suites(openssl) ->
- [ssl_cipher:openssl_suite_name(Suite) || Suite <- available_suites(default)];
+ [ssl_cipher:openssl_suite_name(Suite) ||
+ Suite <- available_suites(default)];
cipher_suites(all) ->
[ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(all)].
%%--------------------------------------------------------------------
+-spec cipher_suites(default | all | anonymous, tls_record:tls_version() | dtls_record:dtls_version() |
+ tls_record:tls_atom_version() | dtls_record:dtls_atom_version()) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Returns all default and all supported cipher suites for a
+%% TLS/DTLS version
+%%--------------------------------------------------------------------
+cipher_suites(Base, Version) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
+ Version == tlsv1;
+ Version == sslv3 ->
+ cipher_suites(Base, tls_record:protocol_version(Version));
+cipher_suites(Base, Version) when Version == 'dtlsv1.2';
+ Version == 'dtlsv1'->
+ cipher_suites(Base, dtls_record:protocol_version(Version));
+cipher_suites(Base, Version) ->
+ [ssl_cipher:suite_definition(Suite) || Suite <- supported_suites(Base, Version)].
+
+%%--------------------------------------------------------------------
+-spec filter_cipher_suites([ssl_cipher:erl_cipher_suite()],
+ [{key_exchange | cipher | mac | prf, fun()}] | []) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Removes cipher suites if any of the filter functions returns false
+%% for any part of the cipher suite. This function also calls default filter functions
+%% to make sure the cipher suite are supported by crypto.
+%%--------------------------------------------------------------------
+filter_cipher_suites(Suites, Filters0) ->
+ #{key_exchange_filters := KexF,
+ cipher_filters := CipherF,
+ mac_filters := MacF,
+ prf_filters := PrfF}
+ = ssl_cipher:crypto_support_filters(),
+ Filters = #{key_exchange_filters => add_filter(proplists:get_value(key_exchange, Filters0), KexF),
+ cipher_filters => add_filter(proplists:get_value(cipher, Filters0), CipherF),
+ mac_filters => add_filter(proplists:get_value(mac, Filters0), MacF),
+ prf_filters => add_filter(proplists:get_value(prf, Filters0), PrfF)},
+ ssl_cipher:filter_suites(Suites, Filters).
+%%--------------------------------------------------------------------
+-spec prepend_cipher_suites([ssl_cipher:erl_cipher_suite()] |
+ [{key_exchange | cipher | mac | prf, fun()}],
+ [ssl_cipher:erl_cipher_suite()]) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Make <Preferred> suites become the most prefered
+%% suites that is put them at the head of the cipher suite list
+%% and remove them from <Suites> if present. <Preferred> may be a
+%% list of cipher suits or a list of filters in which case the
+%% filters are use on Suites to extract the the preferred
+%% cipher list.
+%% --------------------------------------------------------------------
+prepend_cipher_suites([First | _] = Preferred, Suites0) when is_map(First) ->
+ Suites = Preferred ++ (Suites0 -- Preferred),
+ Suites;
+prepend_cipher_suites(Filters, Suites) ->
+ Preferred = filter_cipher_suites(Suites, Filters),
+ Preferred ++ (Suites -- Preferred).
+%%--------------------------------------------------------------------
+-spec append_cipher_suites(Deferred :: [ssl_cipher:erl_cipher_suite()] |
+ [{key_exchange | cipher | mac | prf, fun()}],
+ [ssl_cipher:erl_cipher_suite()]) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Make <Deferred> suites suites become the
+%% least prefered suites that is put them at the end of the cipher suite list
+%% and removed them from <Suites> if present.
+%%
+%%--------------------------------------------------------------------
+append_cipher_suites([First | _] = Deferred, Suites0) when is_map(First)->
+ Suites = (Suites0 -- Deferred) ++ Deferred,
+ Suites;
+append_cipher_suites(Filters, Suites) ->
+ Deferred = filter_cipher_suites(Suites, Filters),
+ (Suites -- Deferred) ++ Deferred.
+
+%%--------------------------------------------------------------------
-spec eccs() -> tls_v1:curves().
%% Description: returns all supported curves across all versions
%%--------------------------------------------------------------------
@@ -637,11 +712,17 @@ tls_version({254, _} = Version) ->
available_suites(default) ->
Version = tls_record:highest_protocol_version([]),
ssl_cipher:filter_suites(ssl_cipher:suites(Version));
-
available_suites(all) ->
Version = tls_record:highest_protocol_version([]),
ssl_cipher:filter_suites(ssl_cipher:all_suites(Version)).
+supported_suites(default, Version) ->
+ ssl_cipher:suites(Version);
+supported_suites(all, Version) ->
+ ssl_cipher:all_suites(Version);
+supported_suites(anonymous, Version) ->
+ ssl_cipher:anonymous_suites(Version).
+
do_listen(Port, #config{transport_info = {Transport, _, _, _}} = Config, tls_connection) ->
tls_socket:listen(Transport, Port, Config);
@@ -896,7 +977,8 @@ validate_option(key, {KeyType, Value}) when is_binary(Value),
KeyType == 'ECPrivateKey';
KeyType == 'PrivateKeyInfo' ->
{KeyType, Value};
-
+validate_option(key, #{algorithm := _} = Value) ->
+ Value;
validate_option(keyfile, undefined) ->
<<>>;
validate_option(keyfile, Value) when is_binary(Value) ->
@@ -991,17 +1073,21 @@ validate_option(next_protocols_advertised, Value) when is_list(Value) ->
Value;
validate_option(next_protocols_advertised, undefined) ->
undefined;
-validate_option(server_name_indication = Opt, Value) when is_list(Value) ->
+validate_option(server_name_indication, Value) when is_list(Value) ->
%% RFC 6066, Section 3: Currently, the only server names supported are
%% DNS hostnames
- case inet_parse:domain(Value) of
- false ->
- throw({error, {options, {{Opt, Value}}}});
- true ->
- Value
- end;
-validate_option(server_name_indication, undefined = Value) ->
+ %% case inet_parse:domain(Value) of
+ %% false ->
+ %% throw({error, {options, {{Opt, Value}}}});
+ %% true ->
+ %% Value
+ %% end;
+ %%
+ %% But the definition seems very diffuse, so let all strings through
+ %% and leave it up to public_key to decide...
Value;
+validate_option(server_name_indication, undefined) ->
+ undefined;
validate_option(server_name_indication, disable) ->
disable;
@@ -1113,24 +1199,6 @@ dtls_validate_versions([Version | Rest], Versions) when Version == 'dtlsv1';
dtls_validate_versions([Ver| _], Versions) ->
throw({error, {options, {Ver, {versions, Versions}}}}).
-validate_inet_option(mode, Value)
- when Value =/= list, Value =/= binary ->
- throw({error, {options, {mode,Value}}});
-validate_inet_option(packet, Value)
- when not (is_atom(Value) orelse is_integer(Value)) ->
- throw({error, {options, {packet,Value}}});
-validate_inet_option(packet_size, Value)
- when not is_integer(Value) ->
- throw({error, {options, {packet_size,Value}}});
-validate_inet_option(header, Value)
- when not is_integer(Value) ->
- throw({error, {options, {header,Value}}});
-validate_inet_option(active, Value)
- when Value =/= true, Value =/= false, Value =/= once ->
- throw({error, {options, {active,Value}}});
-validate_inet_option(_, _) ->
- ok.
-
%% The option cacerts overrides cacertsfile
ca_cert_default(_,_, [_|_]) ->
undefined;
@@ -1145,31 +1213,11 @@ ca_cert_default(verify_peer, undefined, _) ->
emulated_options(Protocol, Opts) ->
case Protocol of
tls ->
- emulated_options(Opts, tls_socket:internal_inet_values(), tls_socket:default_inet_values());
+ tls_socket:emulated_options(Opts);
dtls ->
- emulated_options(Opts, dtls_socket:internal_inet_values(), dtls_socket:default_inet_values())
+ dtls_socket:emulated_options(Opts)
end.
-emulated_options([{mode, Value} = Opt |Opts], Inet, Emulated) ->
- validate_inet_option(mode, Value),
- emulated_options(Opts, Inet, [Opt | proplists:delete(mode, Emulated)]);
-emulated_options([{header, Value} = Opt | Opts], Inet, Emulated) ->
- validate_inet_option(header, Value),
- emulated_options(Opts, Inet, [Opt | proplists:delete(header, Emulated)]);
-emulated_options([{active, Value} = Opt |Opts], Inet, Emulated) ->
- validate_inet_option(active, Value),
- emulated_options(Opts, Inet, [Opt | proplists:delete(active, Emulated)]);
-emulated_options([{packet, Value} = Opt |Opts], Inet, Emulated) ->
- validate_inet_option(packet, Value),
- emulated_options(Opts, Inet, [Opt | proplists:delete(packet, Emulated)]);
-emulated_options([{packet_size, Value} = Opt | Opts], Inet, Emulated) ->
- validate_inet_option(packet_size, Value),
- emulated_options(Opts, Inet, [Opt | proplists:delete(packet_size, Emulated)]);
-emulated_options([Opt|Opts], Inet, Emulated) ->
- emulated_options(Opts, [Opt|Inet], Emulated);
-emulated_options([], Inet,Emulated) ->
- {Inet, Emulated}.
-
handle_cipher_option(Value, Version) when is_list(Value) ->
try binary_cipher_suites(Version, Value) of
Suites ->
@@ -1184,18 +1232,21 @@ handle_cipher_option(Value, Version) when is_list(Value) ->
binary_cipher_suites(Version, []) ->
%% Defaults to all supported suites that does
%% not require explicit configuration
- ssl_cipher:filter_suites(ssl_cipher:suites(tls_version(Version)));
-binary_cipher_suites(Version, [Tuple|_] = Ciphers0) when is_tuple(Tuple) ->
+ default_binary_suites(Version);
+binary_cipher_suites(Version, [Map|_] = Ciphers0) when is_map(Map) ->
Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0],
binary_cipher_suites(Version, Ciphers);
-
+binary_cipher_suites(Version, [Tuple|_] = Ciphers0) when is_tuple(Tuple) ->
+ Ciphers = [ssl_cipher:suite(tuple_to_map(C)) || C <- Ciphers0],
+ binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
- All = ssl_cipher:all_suites(tls_version(Version)),
+ All = ssl_cipher:all_suites(Version) ++
+ ssl_cipher:anonymous_suites(Version),
case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, All)] of
[] ->
%% Defaults to all supported suites that does
%% not require explicit configuration
- ssl_cipher:filter_suites(ssl_cipher:suites(tls_version(Version)));
+ default_binary_suites(Version);
Ciphers ->
Ciphers
end;
@@ -1205,9 +1256,33 @@ binary_cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) ->
binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, Ciphers0) ->
%% Format: "RC4-SHA:RC4-MD5"
- Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")],
+ Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:lexemes(Ciphers0, ":")],
binary_cipher_suites(Version, Ciphers).
+default_binary_suites(Version) ->
+ ssl_cipher:filter_suites(ssl_cipher:suites(Version)).
+
+tuple_to_map({Kex, Cipher, Mac}) ->
+ #{key_exchange => Kex,
+ cipher => Cipher,
+ mac => Mac,
+ prf => default_prf};
+tuple_to_map({Kex, Cipher, Mac, Prf}) ->
+ #{key_exchange => Kex,
+ cipher => Cipher,
+ mac => tuple_to_map_mac(Cipher, Mac),
+ prf => Prf}.
+
+%% Backwards compatible
+tuple_to_map_mac(aes_128_gcm, _) ->
+ aead;
+tuple_to_map_mac(aes_256_gcm, _) ->
+ aead;
+tuple_to_map_mac(chacha20_poly1305, _) ->
+ aead;
+tuple_to_map_mac(_, MAC) ->
+ MAC.
+
handle_eccs_option(Value, Version) when is_list(Value) ->
{_Major, Minor} = tls_version(Version),
try tls_v1:ecc_curves(Minor, Value) of
@@ -1486,3 +1561,8 @@ reject_alpn_next_prot_options([Opt| AlpnNextOpts], Opts) ->
false ->
reject_alpn_next_prot_options(AlpnNextOpts, Opts)
end.
+
+add_filter(undefined, Filters) ->
+ Filters;
+add_filter(Filter, Filters) ->
+ [Filter | Filters].
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index db415a3666..95ab955ad0 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -57,7 +57,7 @@ decode(Bin) ->
reason_code(#alert{description = ?CLOSE_NOTIFY}, _) ->
closed;
reason_code(#alert{description = Description}, _) ->
- {tls_alert, string:to_lower(description_txt(Description))}.
+ {tls_alert, string:casefold(description_txt(Description))}.
%%--------------------------------------------------------------------
-spec own_alert_txt(#alert{}) -> string().
@@ -66,7 +66,7 @@ reason_code(#alert{description = Description}, _) ->
%% by the erlang implementation.
%%--------------------------------------------------------------------
own_alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}, reason = undefined, role = Role}) ->
- "at " ++ Mod ++ ":" ++ integer_to_list(Line) ++ " generated " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
+ "at " ++ Mod ++ ":" ++ integer_to_list(Line) ++ " generated " ++ string:uppercase(atom_to_list(Role)) ++ " ALERT: " ++
level_txt(Level) ++ description_txt(Description);
own_alert_txt(#alert{reason = Reason} = Alert) ->
BaseTxt = own_alert_txt(Alert#alert{reason = undefined}),
@@ -81,7 +81,7 @@ own_alert_txt(#alert{reason = Reason} = Alert) ->
%% the peer.
%%--------------------------------------------------------------------
alert_txt(#alert{level = Level, description = Description, reason = undefined, role = Role}) ->
- "received " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
+ "received " ++ string:uppercase(atom_to_list(Role)) ++ " ALERT: " ++
level_txt(Level) ++ description_txt(Description);
alert_txt(#alert{reason = Reason} = Alert) ->
BaseTxt = alert_txt(Alert#alert{reason = undefined}),
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 50c5f0d755..62a172ca7c 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -36,28 +36,30 @@
-export([security_parameters/2, security_parameters/3, suite_definition/1,
erl_suite_definition/1,
cipher_init/3, decipher/6, cipher/5, decipher_aead/6, cipher_aead/6,
- suite/1, suites/1, all_suites/1,
- ec_keyed_suites/0, anonymous_suites/1, psk_suites/1, srp_suites/0,
- rc4_suites/1, des_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
+ suite/1, suites/1, all_suites/1, crypto_support_filters/0,
+ ec_keyed_suites/0, anonymous_suites/1, psk_suites/1, psk_suites_anon/1, srp_suites/0,
+ srp_suites_anon/0, rc4_suites/1, des_suites/1, openssl_suite/1, openssl_suite_name/1,
+ filter/2, filter_suites/1, filter_suites/2,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1,
random_bytes/1, calc_mac_hash/4,
is_stream_ciphersuite/1]).
-export_type([cipher_suite/0,
- erl_cipher_suite/0, openssl_cipher_suite/0,
+ erl_cipher_suite/0, old_erl_cipher_suite/0, openssl_cipher_suite/0,
hash/0, key_algo/0, sign_algo/0]).
--type cipher() :: null |rc4_128 | des_cbc | '3des_ede_cbc'
- | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305.
+-type cipher() :: null |rc4_128 | des_cbc | '3des_ede_cbc' | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305.
-type hash() :: null | md5 | sha | sha224 | sha256 | sha384 | sha512.
-type sign_algo() :: rsa | dsa | ecdsa.
--type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss |
- psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
--type erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2
- %% TLS 1.2, internally PRE TLS 1.2 will use default_prf
- | {key_algo(), cipher(), hash(), hash() | default_prf}.
-
-
+-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
+-type erl_cipher_suite() :: #{key_exchange := key_algo(),
+ cipher := cipher(),
+ mac := hash() | aead,
+ prf := hash() | default_prf %% Old cipher suites, version dependent
+ }.
+-type old_erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2
+ %% TLS 1.2, internally PRE TLS 1.2 will use default_prf
+ | {key_algo(), cipher(), hash(), hash() | default_prf}.
-type cipher_suite() :: binary().
-type cipher_enum() :: integer().
-type openssl_cipher_suite() :: string().
@@ -83,7 +85,8 @@ security_parameters(?TLS_NULL_WITH_NULL_NULL = CipherSuite, SecParams) ->
%% cipher values has been updated according to <CipherSuite>
%%-------------------------------------------------------------------
security_parameters(Version, CipherSuite, SecParams) ->
- { _, Cipher, Hash, PrfHashAlg} = suite_definition(CipherSuite),
+ #{cipher := Cipher, mac := Hash,
+ prf := PrfHashAlg} = suite_definition(CipherSuite),
SecParams#security_parameters{
cipher_suite = CipherSuite,
bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
@@ -92,7 +95,7 @@ security_parameters(Version, CipherSuite, SecParams) ->
expanded_key_material_length = expanded_key_material(Cipher),
key_material_length = key_material(Cipher),
iv_size = iv_size(Cipher),
- mac_algorithm = hash_algorithm(Hash),
+ mac_algorithm = mac_algorithm(Hash),
prf_algorithm = prf_algorithm(PrfHashAlg, Version),
hash_size = hash_size(Hash)}.
@@ -318,7 +321,6 @@ suites({_, Minor}) ->
all_suites({3, _} = Version) ->
suites(Version)
- ++ anonymous_suites(Version)
++ psk_suites(Version)
++ srp_suites()
++ rc4_suites(Version)
@@ -334,12 +336,12 @@ all_suites(Version) ->
%%--------------------------------------------------------------------
anonymous_suites({3, N}) ->
- anonymous_suites(N);
+ srp_suites_anon() ++ anonymous_suites(N);
anonymous_suites({254, _} = Version) ->
- anonymous_suites(dtls_v1:corresponding_tls_version(Version))
- -- [?TLS_DH_anon_WITH_RC4_128_MD5];
+ dtls_v1:anonymous_suites(Version);
anonymous_suites(N)
when N >= 3 ->
+ psk_suites_anon(N) ++
[?TLS_DH_anon_WITH_AES_128_GCM_SHA256,
?TLS_DH_anon_WITH_AES_256_GCM_SHA384,
?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
@@ -348,20 +350,20 @@ anonymous_suites(N)
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_DH_anon_WITH_RC4_128_MD5];
-
-anonymous_suites(2) ->
+anonymous_suites(2 = N) ->
+ psk_suites_anon(N) ++
[?TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_DH_anon_WITH_DES_CBC_SHA,
?TLS_DH_anon_WITH_RC4_128_MD5];
-
anonymous_suites(N) when N == 0;
N == 1 ->
- [?TLS_DH_anon_WITH_RC4_128_MD5,
- ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DH_anon_WITH_DES_CBC_SHA
- ].
+ psk_suites_anon(N) ++
+ [?TLS_DH_anon_WITH_RC4_128_MD5,
+ ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA
+ ].
%%--------------------------------------------------------------------
-spec psk_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
@@ -371,38 +373,49 @@ anonymous_suites(N) when N == 0;
%%--------------------------------------------------------------------
psk_suites({3, N}) ->
psk_suites(N);
-
psk_suites(N)
when N >= 3 ->
[
- ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
+ ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+ ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
+ ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
+ ] ++ psk_suites(0);
+psk_suites(_) ->
+ [?TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
+ ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
+ ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_PSK_WITH_RC4_128_SHA].
+
+%%--------------------------------------------------------------------
+-spec psk_suites_anon(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
+%%
+%% Description: Returns a list of the anonymous PSK cipher suites, only supported
+%% if explicitly set by user.
+%%--------------------------------------------------------------------
+psk_suites_anon({3, N}) ->
+ psk_suites_anon(N);
+psk_suites_anon(N)
+ when N >= 3 ->
+ [
+ ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
?TLS_PSK_WITH_AES_256_GCM_SHA384,
?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
- ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
?TLS_PSK_WITH_AES_256_CBC_SHA384,
?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
- ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
?TLS_PSK_WITH_AES_128_GCM_SHA256,
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
- ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
?TLS_PSK_WITH_AES_128_CBC_SHA256
- ] ++ psk_suites(0);
-
-psk_suites(_) ->
+ ] ++ psk_suites_anon(0);
+psk_suites_anon(_) ->
[?TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
- ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
?TLS_PSK_WITH_AES_256_CBC_SHA,
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
?TLS_PSK_WITH_AES_128_CBC_SHA,
?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
- ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
?TLS_PSK_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_PSK_WITH_RC4_128_SHA,
- ?TLS_RSA_PSK_WITH_RC4_128_SHA,
?TLS_PSK_WITH_RC4_128_SHA].
-
%%--------------------------------------------------------------------
-spec srp_suites() -> [cipher_suite()].
%%
@@ -410,17 +423,26 @@ psk_suites(_) ->
%% if explicitly set by user.
%%--------------------------------------------------------------------
srp_suites() ->
- [?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
+ [?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
- ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
- ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA].
+
%%--------------------------------------------------------------------
--spec rc4_suites(Version::ssl_record:ssl_version()) -> [cipher_suite()].
+-spec srp_suites_anon() -> [cipher_suite()].
+%%
+%% Description: Returns a list of the SRP anonymous cipher suites, only supported
+%% if explicitly set by user.
+%%--------------------------------------------------------------------
+srp_suites_anon() ->
+ [?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
+ ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA].
+
+%%--------------------------------------------------------------------
+-spec rc4_suites(Version::ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
%% Description: Returns a list of the RSA|(ECDH/RSA)| (ECDH/ECDSA)
%% with RC4 cipher suites, only supported if explicitly set by user.
@@ -428,13 +450,15 @@ srp_suites() ->
%% belonged to the user configured only category.
%%--------------------------------------------------------------------
rc4_suites({3, 0}) ->
+ rc4_suites(0);
+rc4_suites({3, Minor}) ->
+ rc4_suites(Minor) ++ rc4_suites(0);
+rc4_suites(0) ->
[?TLS_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_MD5];
-rc4_suites({3, N}) when N =< 3 ->
+rc4_suites(N) when N =< 3 ->
[?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
?TLS_ECDHE_RSA_WITH_RC4_128_SHA,
- ?TLS_RSA_WITH_RC4_128_SHA,
- ?TLS_RSA_WITH_RC4_128_MD5,
?TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
?TLS_ECDH_RSA_WITH_RC4_128_SHA].
%%--------------------------------------------------------------------
@@ -457,314 +481,663 @@ des_suites(_)->
%%-------------------------------------------------------------------
%% TLS v1.1 suites
suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
- {null, null, null, null};
+ #{key_exchange => null,
+ cipher => null,
+ mac => null,
+ prf => null};
%% RFC 5746 - Not a real cipher suite used to signal empty "renegotiation_info" extension
%% to avoid handshake failure from old servers that do not ignore
%% hello extension data as they should.
suite_definition(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) ->
- {null, null, null, null};
-%% suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
-%% {rsa, null, md5, default_prf};
-%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
-%% {rsa, null, sha, default_prf};
+ #{key_exchange => null,
+ cipher => null,
+ mac => null,
+ prf => null};
suite_definition(?TLS_RSA_WITH_RC4_128_MD5) ->
- {rsa, rc4_128, md5, default_prf};
+ #{key_exchange => rsa,
+ cipher => rc4_128,
+ mac => md5,
+ prf => default_prf};
suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->
- {rsa, rc4_128, sha, default_prf};
+ #{key_exchange => rsa,
+ cipher => rc4_128,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->
- {rsa, des_cbc, sha, default_prf};
+ #{key_exchange => rsa,
+ cipher => des_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {rsa, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => rsa,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
- {dhe_dss, des_cbc, sha, default_prf};
+ #{key_exchange => dhe_dss,
+ cipher => des_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_dss, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => dhe_dss,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
- {dhe_rsa, des_cbc, sha, default_prf};
+ #{key_exchange => dhe_rsa,
+ cipher => des_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_rsa, '3des_ede_cbc', sha, default_prf};
-
+ #{key_exchange => dhe_rsa,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
%%% TSL V1.1 AES suites
suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
- {rsa, aes_128_cbc, sha, default_prf};
+ #{key_exchange => rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
- {dhe_dss, aes_128_cbc, sha, default_prf};
+ #{key_exchange => dhe_dss,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
- {dhe_rsa, aes_128_cbc, sha, default_prf};
+ #{key_exchange => dhe_rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
- {rsa, aes_256_cbc, sha, default_prf};
+ #{key_exchange => rsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
- {dhe_dss, aes_256_cbc, sha, default_prf};
+ #{key_exchange => dhe_dss,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
- {dhe_rsa, aes_256_cbc, sha, default_prf};
-
+ #{key_exchange => dhe_rsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
%% TLS v1.2 suites
-
%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
%% {rsa, null, sha, default_prf};
suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
- {rsa, aes_128_cbc, sha256, default_prf};
+ #{key_exchange => rsa,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
- {rsa, aes_256_cbc, sha256, default_prf};
+ #{key_exchange => rsa,
+ cipher => aes_256_cbc,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
- {dhe_dss, aes_128_cbc, sha256, default_prf};
+ #{key_exchange => dhe_dss,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
- {dhe_rsa, aes_128_cbc, sha256, default_prf};
+ #{key_exchange => dhe_rsa,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
- {dhe_dss, aes_256_cbc, sha256, default_prf};
+ #{key_exchange => dhe_dss,
+ cipher => aes_256_cbc,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
- {dhe_rsa, aes_256_cbc, sha256, default_prf};
-
+ #{key_exchange => dhe_rsa,
+ cipher => aes_256_cbc,
+ mac => sha256,
+ prf => default_prf};
%% not defined YET:
%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 DH_DSS AES_128_CBC SHA256
%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 DH_RSA AES_128_CBC SHA256
%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 DH_DSS AES_256_CBC SHA256
%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 DH_RSA AES_256_CBC SHA256
-
%%% DH-ANON deprecated by TLS spec and not available
%%% by default, but good for testing purposes.
suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) ->
- {dh_anon, rc4_128, md5, default_prf};
+ #{key_exchange => dh_anon,
+ cipher => rc4_128,
+ mac => md5,
+ prf => default_prf};
suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
- {dh_anon, des_cbc, sha, default_prf};
+ #{key_exchange => dh_anon,
+ cipher => des_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
- {dh_anon, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => dh_anon,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
- {dh_anon, aes_128_cbc, sha, default_prf};
+ #{key_exchange => dh_anon,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
- {dh_anon, aes_256_cbc, sha, default_prf};
+ #{key_exchange => dh_anon,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) ->
- {dh_anon, aes_128_cbc, sha256, default_prf};
+ #{key_exchange => dh_anon,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) ->
- {dh_anon, aes_256_cbc, sha256, default_prf};
-
+ #{key_exchange => dh_anon,
+ cipher => aes_256_cbc,
+ mac => sha256,
+ prf => default_prf};
%%% PSK Cipher Suites RFC 4279
-
suite_definition(?TLS_PSK_WITH_RC4_128_SHA) ->
- {psk, rc4_128, sha, default_prf};
+ #{key_exchange => psk,
+ cipher => rc4_128,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) ->
- {psk, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => psk,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA) ->
- {psk, aes_128_cbc, sha, default_prf};
+ #{key_exchange => psk,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA) ->
- {psk, aes_256_cbc, sha, default_prf};
+ #{key_exchange => psk,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_PSK_WITH_RC4_128_SHA) ->
- {dhe_psk, rc4_128, sha, default_prf};
+ #{key_exchange => dhe_psk,
+ cipher => rc4_128,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_psk, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => dhe_psk,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA) ->
- {dhe_psk, aes_128_cbc, sha, default_prf};
+ #{key_exchange => dhe_psk,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA) ->
- {dhe_psk, aes_256_cbc, sha, default_prf};
+ #{key_exchange => dhe_psk,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_RSA_PSK_WITH_RC4_128_SHA) ->
- {rsa_psk, rc4_128, sha, default_prf};
+ #{key_exchange => rsa_psk,
+ cipher => rc4_128,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA) ->
- {rsa_psk, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => rsa_psk,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA) ->
- {rsa_psk, aes_128_cbc, sha, default_prf};
+ #{key_exchange => rsa_psk,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA) ->
- {rsa_psk, aes_256_cbc, sha, default_prf};
-
+ #{key_exchange => rsa_psk,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
%%% TLS 1.2 PSK Cipher Suites RFC 5487
-
suite_definition(?TLS_PSK_WITH_AES_128_GCM_SHA256) ->
- {psk, aes_128_gcm, null, sha256};
+ #{key_exchange => psk,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_PSK_WITH_AES_256_GCM_SHA384) ->
- {psk, aes_256_gcm, null, sha384};
+ #{key_exchange => psk,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) ->
- {dhe_psk, aes_128_gcm, null, sha256};
+ #{key_exchange => dhe_psk,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384) ->
- {dhe_psk, aes_256_gcm, null, sha384};
+ #{key_exchange => dhe_psk,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256) ->
- {rsa_psk, aes_128_gcm, null, sha256};
+ #{key_exchange => rsa_psk,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384) ->
- {rsa_psk, aes_256_gcm, null, sha384};
-
+ #{key_exchange => rsa_psk,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA256) ->
- {psk, aes_128_cbc, sha256, default_prf};
+ #{key_exchange => psk,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA384) ->
- {psk, aes_256_cbc, sha384, default_prf};
+ #{key_exchange => psk,
+ cipher => aes_256_cbc,
+ mac => sha384,
+ prf => default_prf};
suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256) ->
- {dhe_psk, aes_128_cbc, sha256, default_prf};
+ #{key_exchange => dhe_psk,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384) ->
- {dhe_psk, aes_256_cbc, sha384, default_prf};
+ #{key_exchange => dhe_psk,
+ cipher => aes_256_cbc,
+ mac => sha384,
+ prf => default_prf};
suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256) ->
- {rsa_psk, aes_128_cbc, sha256, default_prf};
+ #{key_exchange => rsa_psk,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384) ->
- {rsa_psk, aes_256_cbc, sha384, default_prf};
-
+ #{key_exchange => rsa_psk,
+ cipher => aes_256_cbc,
+ mac => sha384,
+ prf => default_prf};
suite_definition(?TLS_PSK_WITH_NULL_SHA256) ->
- {psk, null, sha256, default_prf};
+ #{key_exchange => psk,
+ cipher => null,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_PSK_WITH_NULL_SHA384) ->
- {psk, null, sha384, default_prf};
+ #{key_exchange => psk,
+ cipher => null,
+ mac => sha384,
+ prf => default_prf};
suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA256) ->
- {dhe_psk, null, sha256, default_prf};
+ #{key_exchange => dhe_psk,
+ cipher => null,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA384) ->
- {dhe_psk, null, sha384, default_prf};
+ #{key_exchange => dhe_psk,
+ cipher => null,
+ mac => sha384,
+ prf => default_prf};
suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA256) ->
- {rsa_psk, null, sha256, default_prf};
+ #{key_exchange => rsa_psk,
+ cipher => null,
+ mac => sha256,
+ prf => default_prf};
suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA384) ->
- {rsa_psk, null, sha384, default_prf};
-
+ #{key_exchange => rsa_psk,
+ cipher => null,
+ mac => sha384,
+ prf => default_prf};
%%% SRP Cipher Suites RFC 5054
-
suite_definition(?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) ->
- {srp_anon, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => srp_anon,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {srp_rsa, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => srp_rsa,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) ->
- {srp_dss, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => srp_dss,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_SRP_SHA_WITH_AES_128_CBC_SHA) ->
- {srp_anon, aes_128_cbc, sha, default_prf};
+ #{key_exchange => srp_anon,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) ->
- {srp_rsa, aes_128_cbc, sha, default_prf};
+ #{key_exchange => srp_rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) ->
- {srp_dss, aes_128_cbc, sha, default_prf};
+ #{key_exchange => srp_dss,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_SRP_SHA_WITH_AES_256_CBC_SHA) ->
- {srp_anon, aes_256_cbc, sha, default_prf};
+ #{key_exchange => srp_anon,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) ->
- {srp_rsa, aes_256_cbc, sha, default_prf};
+ #{key_exchange => srp_rsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) ->
- {srp_dss, aes_256_cbc, sha, default_prf};
-
+ #{key_exchange => srp_dss,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
%% RFC 4492 EC TLS suites
suite_definition(?TLS_ECDH_ECDSA_WITH_NULL_SHA) ->
- {ecdh_ecdsa, null, sha, default_prf};
+ #{key_exchange => ecdh_ecdsa,
+ cipher => null,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) ->
- {ecdh_ecdsa, rc4_128, sha, default_prf};
+ #{key_exchange => ecdh_ecdsa,
+ cipher => rc4_128,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) ->
- {ecdh_ecdsa, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => ecdh_ecdsa,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) ->
- {ecdh_ecdsa, aes_128_cbc, sha, default_prf};
+ #{key_exchange => ecdh_ecdsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) ->
- {ecdh_ecdsa, aes_256_cbc, sha, default_prf};
-
+ #{key_exchange => ecdh_ecdsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_ECDSA_WITH_NULL_SHA) ->
- {ecdhe_ecdsa, null, sha, default_prf};
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => null,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) ->
- {ecdhe_ecdsa, rc4_128, sha, default_prf};
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => rc4_128,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) ->
- {ecdhe_ecdsa, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) ->
- {ecdhe_ecdsa, aes_128_cbc, sha, default_prf};
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) ->
- {ecdhe_ecdsa, aes_256_cbc, sha, default_prf};
-
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_RSA_WITH_NULL_SHA) ->
- {ecdh_rsa, null, sha, default_prf};
+ #{key_exchange => ecdh_rsa,
+ cipher => null,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_RSA_WITH_RC4_128_SHA) ->
- {ecdh_rsa, rc4_128, sha, default_prf};
+ #{key_exchange => ecdh_rsa,
+ cipher => rc4_128,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {ecdh_rsa, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => ecdh_rsa,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) ->
- {ecdh_rsa, aes_128_cbc, sha, default_prf};
+ #{key_exchange => ecdh_rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) ->
- {ecdh_rsa, aes_256_cbc, sha, default_prf};
-
+ #{key_exchange => ecdh_rsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_RSA_WITH_NULL_SHA) ->
- {ecdhe_rsa, null, sha, default_prf};
+ #{key_exchange => ecdhe_rsa,
+ cipher => null,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) ->
- {ecdhe_rsa, rc4_128, sha, default_prf};
+ #{key_exchange => ecdhe_rsa,
+ cipher => rc4_128,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {ecdhe_rsa, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => ecdhe_rsa,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) ->
- {ecdhe_rsa, aes_128_cbc, sha, default_prf};
+ #{key_exchange => ecdhe_rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) ->
- {ecdhe_rsa, aes_256_cbc, sha, default_prf};
-
+ #{key_exchange => ecdhe_rsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_anon_WITH_NULL_SHA) ->
- {ecdh_anon, null, sha, default_prf};
+ #{key_exchange => ecdh_anon,
+ cipher => null,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_anon_WITH_RC4_128_SHA) ->
- {ecdh_anon, rc4_128, sha, default_prf};
+ #{key_exchange => ecdh_anon,
+ cipher => rc4_128,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA) ->
- {ecdh_anon, '3des_ede_cbc', sha, default_prf};
+ #{key_exchange => ecdh_anon,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_anon_WITH_AES_128_CBC_SHA) ->
- {ecdh_anon, aes_128_cbc, sha, default_prf};
+ #{key_exchange => ecdh_anon,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf};
suite_definition(?TLS_ECDH_anon_WITH_AES_256_CBC_SHA) ->
- {ecdh_anon, aes_256_cbc, sha, default_prf};
-
+ #{key_exchange => ecdh_anon,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf};
%% RFC 5289 EC TLS suites
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) ->
- {ecdhe_ecdsa, aes_128_cbc, sha256, sha256};
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => sha256};
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) ->
- {ecdhe_ecdsa, aes_256_cbc, sha384, sha384};
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => aes_256_cbc,
+ mac => sha384,
+ prf => sha384};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) ->
- {ecdh_ecdsa, aes_128_cbc, sha256, sha256};
+ #{key_exchange => ecdh_ecdsa,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => sha256};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) ->
- {ecdh_ecdsa, aes_256_cbc, sha384, sha384};
+ #{key_exchange => ecdh_ecdsa,
+ cipher => aes_256_cbc,
+ mac => sha384,
+ prf => sha384};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) ->
- {ecdhe_rsa, aes_128_cbc, sha256, sha256};
+ #{key_exchange => ecdhe_rsa,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => sha256};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) ->
- {ecdhe_rsa, aes_256_cbc, sha384, sha384};
+ #{key_exchange => ecdhe_rsa,
+ cipher => aes_256_cbc,
+ mac => sha384,
+ prf => sha384};
suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) ->
- {ecdh_rsa, aes_128_cbc, sha256, sha256};
+ #{key_exchange => ecdh_rsa,
+ cipher => aes_128_cbc,
+ mac => sha256,
+ prf => sha256};
suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) ->
- {ecdh_rsa, aes_256_cbc, sha384, sha384};
-
+ #{key_exchange => ecdh_rsa,
+ cipher => aes_256_cbc,
+ mac => sha384,
+ prf => sha384};
%% RFC 5288 AES-GCM Cipher Suites
suite_definition(?TLS_RSA_WITH_AES_128_GCM_SHA256) ->
- {rsa, aes_128_gcm, null, sha256};
+ #{key_exchange => rsa,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_RSA_WITH_AES_256_GCM_SHA384) ->
- {rsa, aes_256_gcm, null, sha384};
+ #{key_exchange => rsa,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) ->
- {dhe_rsa, aes_128_gcm, null, sha256};
+ #{key_exchange => dhe_rsa,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) ->
- {dhe_rsa, aes_256_gcm, null, sha384};
+ #{key_exchange => dhe_rsa,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) ->
- {dh_rsa, aes_128_gcm, null, sha256};
+ #{key_exchange => dh_rsa,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) ->
- {dh_rsa, aes_256_gcm, null, sha384};
+ #{key_exchange => dh_rsa,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) ->
- {dhe_dss, aes_128_gcm, null, sha256};
+ #{key_exchange => dhe_dss,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) ->
- {dhe_dss, aes_256_gcm, null, sha384};
+ #{key_exchange => dhe_dss,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
- {dh_dss, aes_128_gcm, null, sha256};
+ #{key_exchange => dh_dss,
+ cipher => aes_128_gcm,
+ mac => null,
+ prf => sha256};
suite_definition(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) ->
- {dh_dss, aes_256_gcm, null, sha384};
+ #{key_exchange => dh_dss,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_DH_anon_WITH_AES_128_GCM_SHA256) ->
- {dh_anon, aes_128_gcm, null, sha256};
+ #{key_exchange => dh_anon,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_DH_anon_WITH_AES_256_GCM_SHA384) ->
- {dh_anon, aes_256_gcm, null, sha384};
-
+ #{key_exchange => dh_anon,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
%% RFC 5289 ECC AES-GCM Cipher Suites
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) ->
- {ecdhe_ecdsa, aes_128_gcm, null, sha256};
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) ->
- {ecdhe_ecdsa, aes_256_gcm, null, sha384};
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) ->
- {ecdh_ecdsa, aes_128_gcm, null, sha256};
+ #{key_exchange => ecdh_ecdsa,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) ->
- {ecdh_ecdsa, aes_256_gcm, null, sha384};
+ #{key_exchange => ecdh_ecdsa,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) ->
- {ecdhe_rsa, aes_128_gcm, null, sha256};
+ #{key_exchange => ecdhe_rsa,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) ->
- {ecdhe_rsa, aes_256_gcm, null, sha384};
+ #{key_exchange => ecdhe_rsa,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
suite_definition(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) ->
- {ecdh_rsa, aes_128_gcm, null, sha256};
+ #{key_exchange => ecdh_rsa,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) ->
- {ecdh_rsa, aes_256_gcm, null, sha384};
-
+ #{key_exchange => ecdh_rsa,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384};
%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
suite_definition(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
- {ecdhe_rsa, chacha20_poly1305, null, sha256};
+ #{key_exchange => ecdhe_rsa,
+ cipher => chacha20_poly1305,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) ->
- {ecdhe_ecdsa, chacha20_poly1305, null, sha256};
+ #{key_exchange => ecdhe_ecdsa,
+ cipher => chacha20_poly1305,
+ mac => aead,
+ prf => sha256};
suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
- {dhe_rsa, chacha20_poly1305, null, sha256}.
+ #{key_exchange => dhe_rsa,
+ cipher => chacha20_poly1305,
+ mac => aead,
+ prf => sha256}.
%%--------------------------------------------------------------------
--spec erl_suite_definition(cipher_suite()) -> erl_cipher_suite().
+-spec erl_suite_definition(cipher_suite() | erl_cipher_suite()) -> old_erl_cipher_suite().
%%
%% Description: Return erlang cipher suite definition. Filters last value
%% for now (compatibility reasons).
%%--------------------------------------------------------------------
-erl_suite_definition(S) ->
- case suite_definition(S) of
- {KeyExchange, Cipher, Hash, default_prf} ->
+erl_suite_definition(Bin) when is_binary(Bin) ->
+ erl_suite_definition(suite_definition(Bin));
+erl_suite_definition(#{key_exchange := KeyExchange, cipher := Cipher,
+ mac := Hash, prf := Prf}) ->
+ case Prf of
+ default_prf ->
{KeyExchange, Cipher, Hash};
- Suite ->
- Suite
+ _ ->
+ {KeyExchange, Cipher, Hash, Prf}
end.
%%--------------------------------------------------------------------
@@ -772,288 +1145,540 @@ erl_suite_definition(S) ->
%%
%% Description: Return TLS cipher suite definition.
%%--------------------------------------------------------------------
-
%% TLS v1.1 suites
-%%suite({rsa, null, md5}) ->
-%% ?TLS_RSA_WITH_NULL_MD5;
-%%suite({rsa, null, sha}) ->
-%% ?TLS_RSA_WITH_NULL_SHA;
-suite({rsa, rc4_128, md5}) ->
+suite(#{key_exchange := rsa,
+ cipher := rc4_128,
+ mac := md5}) ->
?TLS_RSA_WITH_RC4_128_MD5;
-suite({rsa, rc4_128, sha}) ->
+suite(#{key_exchange := rsa,
+ cipher := rc4_128,
+ mac := sha}) ->
?TLS_RSA_WITH_RC4_128_SHA;
-suite({rsa, des_cbc, sha}) ->
+suite(#{key_exchange := rsa,
+ cipher := des_cbc,
+ mac := sha}) ->
?TLS_RSA_WITH_DES_CBC_SHA;
-suite({rsa, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := rsa,
+ cipher :='3des_ede_cbc',
+ mac := sha}) ->
?TLS_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({dhe_dss, des_cbc, sha}) ->
+suite(#{key_exchange := dhe_dss,
+ cipher:= des_cbc,
+ mac := sha}) ->
?TLS_DHE_DSS_WITH_DES_CBC_SHA;
-suite({dhe_dss, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := dhe_dss,
+ cipher:= '3des_ede_cbc',
+ mac := sha}) ->
?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA;
-suite({dhe_rsa, des_cbc, sha}) ->
+suite(#{key_exchange := dhe_rsa,
+ cipher:= des_cbc,
+ mac := sha}) ->
?TLS_DHE_RSA_WITH_DES_CBC_SHA;
-suite({dhe_rsa, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := dhe_rsa,
+ cipher:= '3des_ede_cbc',
+ mac := sha}) ->
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({dh_anon, rc4_128, md5}) ->
+suite(#{key_exchange := dh_anon,
+ cipher:= rc4_128,
+ mac := md5}) ->
?TLS_DH_anon_WITH_RC4_128_MD5;
-suite({dh_anon, des_cbc, sha}) ->
+suite(#{key_exchange := dh_anon,
+ cipher:= des_cbc,
+ mac := sha}) ->
?TLS_DH_anon_WITH_DES_CBC_SHA;
-suite({dh_anon, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := dh_anon,
+ cipher:= '3des_ede_cbc',
+ mac := sha}) ->
?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
-
%%% TSL V1.1 AES suites
-suite({rsa, aes_128_cbc, sha}) ->
+suite(#{key_exchange := rsa,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_RSA_WITH_AES_128_CBC_SHA;
-suite({dhe_dss, aes_128_cbc, sha}) ->
+suite(#{key_exchange := dhe_dss,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
-suite({dhe_rsa, aes_128_cbc, sha}) ->
+suite(#{key_exchange := dhe_rsa,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
-suite({dh_anon, aes_128_cbc, sha}) ->
+suite(#{key_exchange := dh_anon,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_DH_anon_WITH_AES_128_CBC_SHA;
-suite({rsa, aes_256_cbc, sha}) ->
+suite(#{key_exchange := rsa,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_RSA_WITH_AES_256_CBC_SHA;
-suite({dhe_dss, aes_256_cbc, sha}) ->
+suite(#{key_exchange := dhe_dss,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA;
-suite({dhe_rsa, aes_256_cbc, sha}) ->
+suite(#{key_exchange := dhe_rsa,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
-suite({dh_anon, aes_256_cbc, sha}) ->
+suite(#{key_exchange := dh_anon,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_DH_anon_WITH_AES_256_CBC_SHA;
-
%% TLS v1.2 suites
-
-%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
-%% {rsa, null, sha, sha256};
-suite({rsa, aes_128_cbc, sha256}) ->
+suite(#{key_exchange := rsa,
+ cipher := aes_128_cbc,
+ mac := sha256}) ->
?TLS_RSA_WITH_AES_128_CBC_SHA256;
-suite({rsa, aes_256_cbc, sha256}) ->
+suite(#{key_exchange := rsa,
+ cipher := aes_256_cbc,
+ mac := sha256}) ->
?TLS_RSA_WITH_AES_256_CBC_SHA256;
-suite({dhe_dss, aes_128_cbc, sha256}) ->
+suite(#{key_exchange := dhe_dss,
+ cipher := aes_128_cbc,
+ mac := sha256}) ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256;
-suite({dhe_rsa, aes_128_cbc, sha256}) ->
+suite(#{key_exchange := dhe_rsa,
+ cipher := aes_128_cbc,
+ mac := sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
-suite({dhe_dss, aes_256_cbc, sha256}) ->
+suite(#{key_exchange := dhe_dss,
+ cipher := aes_256_cbc,
+ mac := sha256}) ->
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256;
-suite({dhe_rsa, aes_256_cbc, sha256}) ->
+suite(#{key_exchange := dhe_rsa,
+ cipher := aes_256_cbc,
+ mac := sha256}) ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
-suite({dh_anon, aes_128_cbc, sha256}) ->
+suite(#{key_exchange := dh_anon,
+ cipher := aes_128_cbc,
+ mac := sha256}) ->
?TLS_DH_anon_WITH_AES_128_CBC_SHA256;
-suite({dh_anon, aes_256_cbc, sha256}) ->
+suite(#{key_exchange := dh_anon,
+ cipher := aes_256_cbc,
+ mac := sha256}) ->
?TLS_DH_anon_WITH_AES_256_CBC_SHA256;
-
%%% PSK Cipher Suites RFC 4279
-
-suite({psk, rc4_128,sha}) ->
+suite(#{key_exchange := psk,
+ cipher := rc4_128,
+ mac := sha}) ->
?TLS_PSK_WITH_RC4_128_SHA;
-suite({psk, '3des_ede_cbc',sha}) ->
+suite(#{key_exchange := psk,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_PSK_WITH_3DES_EDE_CBC_SHA;
-suite({psk, aes_128_cbc,sha}) ->
+suite(#{key_exchange := psk,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_PSK_WITH_AES_128_CBC_SHA;
-suite({psk, aes_256_cbc,sha}) ->
+suite(#{key_exchange := psk,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_PSK_WITH_AES_256_CBC_SHA;
-suite({dhe_psk, rc4_128,sha}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := rc4_128,
+ mac := sha}) ->
?TLS_DHE_PSK_WITH_RC4_128_SHA;
-suite({dhe_psk, '3des_ede_cbc',sha}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA;
-suite({dhe_psk, aes_128_cbc,sha}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA;
-suite({dhe_psk, aes_256_cbc,sha}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_DHE_PSK_WITH_AES_256_CBC_SHA;
-suite({rsa_psk, rc4_128,sha}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := rc4_128,
+ mac := sha}) ->
?TLS_RSA_PSK_WITH_RC4_128_SHA;
-suite({rsa_psk, '3des_ede_cbc',sha}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA;
-suite({rsa_psk, aes_128_cbc,sha}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_RSA_PSK_WITH_AES_128_CBC_SHA;
-suite({rsa_psk, aes_256_cbc,sha}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_RSA_PSK_WITH_AES_256_CBC_SHA;
-
%%% TLS 1.2 PSK Cipher Suites RFC 5487
-
-suite({psk, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := psk,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_PSK_WITH_AES_128_GCM_SHA256;
-suite({psk, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := psk,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_PSK_WITH_AES_256_GCM_SHA384;
-suite({dhe_psk, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256;
-suite({dhe_psk, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384;
-suite({rsa_psk, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256;
-suite({rsa_psk, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384;
-
-suite({psk, aes_128_cbc, sha256}) ->
+suite(#{key_exchange := psk,
+ cipher := aes_128_cbc,
+ mac := sha256}) ->
?TLS_PSK_WITH_AES_128_CBC_SHA256;
-suite({psk, aes_256_cbc, sha384}) ->
+suite(#{key_exchange := psk,
+ cipher := aes_256_cbc,
+ mac := sha384}) ->
?TLS_PSK_WITH_AES_256_CBC_SHA384;
-suite({dhe_psk, aes_128_cbc, sha256}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := aes_128_cbc,
+ mac := sha256}) ->
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256;
-suite({dhe_psk, aes_256_cbc, sha384}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := aes_256_cbc,
+ mac := sha384}) ->
?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384;
-suite({rsa_psk, aes_128_cbc, sha256}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := aes_128_cbc,
+ mac := sha256}) ->
?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256;
-suite({rsa_psk, aes_256_cbc, sha384}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := aes_256_cbc,
+ mac := sha384}) ->
?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384;
-
-suite({psk, null, sha256}) ->
+suite(#{key_exchange := psk,
+ cipher := null,
+ mac := sha256}) ->
?TLS_PSK_WITH_NULL_SHA256;
-suite({psk, null, sha384}) ->
+suite(#{key_exchange := psk,
+ cipher := null,
+ mac := sha384}) ->
?TLS_PSK_WITH_NULL_SHA384;
-suite({dhe_psk, null, sha256}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := null,
+ mac := sha256}) ->
?TLS_DHE_PSK_WITH_NULL_SHA256;
-suite({dhe_psk, null, sha384}) ->
+suite(#{key_exchange := dhe_psk,
+ cipher := null,
+ mac := sha384}) ->
?TLS_DHE_PSK_WITH_NULL_SHA384;
-suite({rsa_psk, null, sha256}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := null,
+ mac := sha256}) ->
?TLS_RSA_PSK_WITH_NULL_SHA256;
-suite({rsa_psk, null, sha384}) ->
+suite(#{key_exchange := rsa_psk,
+ cipher := null,
+ mac := sha384}) ->
?TLS_RSA_PSK_WITH_NULL_SHA384;
-
%%% SRP Cipher Suites RFC 5054
-
-suite({srp_anon, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := srp_anon,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA;
-suite({srp_rsa, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := srp_rsa,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({srp_dss, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := srp_dss,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA;
-suite({srp_anon, aes_128_cbc, sha}) ->
+suite(#{key_exchange := srp_anon,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_SRP_SHA_WITH_AES_128_CBC_SHA;
-suite({srp_rsa, aes_128_cbc, sha}) ->
+suite(#{key_exchange := srp_rsa,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA;
-suite({srp_dss, aes_128_cbc, sha}) ->
+suite(#{key_exchange := srp_dss,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA;
-suite({srp_anon, aes_256_cbc, sha}) ->
+suite(#{key_exchange := srp_anon,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_SRP_SHA_WITH_AES_256_CBC_SHA;
-suite({srp_rsa, aes_256_cbc, sha}) ->
+suite(#{key_exchange := srp_rsa,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA;
-suite({srp_dss, aes_256_cbc, sha}) ->
+suite(#{key_exchange := srp_dss,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA;
-
%%% RFC 4492 EC TLS suites
-suite({ecdh_ecdsa, null, sha}) ->
+suite(#{key_exchange := ecdh_ecdsa,
+ cipher := null,
+ mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_NULL_SHA;
-suite({ecdh_ecdsa, rc4_128, sha}) ->
+suite(#{key_exchange := ecdh_ecdsa,
+ cipher := rc4_128,
+ mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_RC4_128_SHA;
-suite({ecdh_ecdsa, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := ecdh_ecdsa,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA;
-suite({ecdh_ecdsa, aes_128_cbc, sha}) ->
+suite(#{key_exchange := ecdh_ecdsa,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA;
-suite({ecdh_ecdsa, aes_256_cbc, sha}) ->
+suite(#{key_exchange := ecdh_ecdsa,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA;
-
-suite({ecdhe_ecdsa, null, sha}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := null,
+ mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_NULL_SHA;
-suite({ecdhe_ecdsa, rc4_128, sha}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := rc4_128,
+ mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA;
-suite({ecdhe_ecdsa, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA;
-suite({ecdhe_ecdsa, aes_128_cbc, sha}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA;
-suite({ecdhe_ecdsa, aes_256_cbc, sha}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA;
-
-suite({ecdh_rsa, null, sha}) ->
+suite(#{key_exchange := ecdh_rsa,
+ cipher := null,
+ mac := sha}) ->
?TLS_ECDH_RSA_WITH_NULL_SHA;
-suite({ecdh_rsa, rc4_128, sha}) ->
+suite(#{key_exchange := ecdh_rsa,
+ cipher := rc4_128,
+ mac := sha}) ->
?TLS_ECDH_RSA_WITH_RC4_128_SHA;
-suite({ecdh_rsa, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := ecdh_rsa,
+ cipher := '3des_ede_cbc', mac := sha}) ->
?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({ecdh_rsa, aes_128_cbc, sha}) ->
+suite(#{key_exchange := ecdh_rsa,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA;
-suite({ecdh_rsa, aes_256_cbc, sha}) ->
+suite(#{key_exchange := ecdh_rsa,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA;
-
-suite({ecdhe_rsa, null, sha}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := null,
+ mac := sha}) ->
?TLS_ECDHE_RSA_WITH_NULL_SHA;
-suite({ecdhe_rsa, rc4_128, sha}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := rc4_128,
+ mac := sha}) ->
?TLS_ECDHE_RSA_WITH_RC4_128_SHA;
-suite({ecdhe_rsa, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({ecdhe_rsa, aes_128_cbc, sha}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA;
-suite({ecdhe_rsa, aes_256_cbc, sha}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA;
-
-suite({ecdh_anon, null, sha}) ->
+suite(#{key_exchange := ecdh_anon,
+ cipher := null,
+ mac := sha}) ->
?TLS_ECDH_anon_WITH_NULL_SHA;
-suite({ecdh_anon, rc4_128, sha}) ->
+suite(#{key_exchange := ecdh_anon,
+ cipher := rc4_128,
+ mac := sha}) ->
?TLS_ECDH_anon_WITH_RC4_128_SHA;
-suite({ecdh_anon, '3des_ede_cbc', sha}) ->
+suite(#{key_exchange := ecdh_anon,
+ cipher := '3des_ede_cbc',
+ mac := sha}) ->
?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA;
-suite({ecdh_anon, aes_128_cbc, sha}) ->
+suite(#{key_exchange := ecdh_anon,
+ cipher := aes_128_cbc,
+ mac := sha}) ->
?TLS_ECDH_anon_WITH_AES_128_CBC_SHA;
-suite({ecdh_anon, aes_256_cbc, sha}) ->
+suite(#{key_exchange := ecdh_anon,
+ cipher := aes_256_cbc,
+ mac := sha}) ->
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA;
-
%%% RFC 5289 EC TLS suites
-suite({ecdhe_ecdsa, aes_128_cbc, sha256, sha256}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := aes_128_cbc,
+ mac:= sha256,
+ prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
-suite({ecdhe_ecdsa, aes_256_cbc, sha384, sha384}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := aes_256_cbc,
+ mac := sha384,
+ prf := sha384}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384;
-suite({ecdh_ecdsa, aes_128_cbc, sha256, sha256}) ->
+suite(#{key_exchange := ecdh_ecdsa,
+ cipher := aes_128_cbc,
+ mac := sha256,
+ prf := sha256}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256;
-suite({ecdh_ecdsa, aes_256_cbc, sha384, sha384}) ->
+suite(#{key_exchange := ecdh_ecdsa,
+ cipher := aes_256_cbc,
+ mac := sha384,
+ prf := sha384}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384;
-suite({ecdhe_rsa, aes_128_cbc, sha256, sha256}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := aes_128_cbc,
+ mac := sha256,
+ prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256;
-suite({ecdhe_rsa, aes_256_cbc, sha384, sha384}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := aes_256_cbc,
+ mac := sha384,
+ prf := sha384}) ->
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384;
-suite({ecdh_rsa, aes_128_cbc, sha256, sha256}) ->
+suite(#{key_exchange := ecdh_rsa,
+ cipher := aes_128_cbc,
+ mac := sha256,
+ prf := sha256}) ->
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256;
-suite({ecdh_rsa, aes_256_cbc, sha384, sha384}) ->
+suite(#{key_exchange := ecdh_rsa,
+ cipher := aes_256_cbc,
+ mac := sha384,
+ prf := sha384}) ->
?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384;
-
%% RFC 5288 AES-GCM Cipher Suites
-suite({rsa, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := rsa,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_RSA_WITH_AES_128_GCM_SHA256;
-suite({rsa, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := rsa,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_RSA_WITH_AES_256_GCM_SHA384;
-suite({dhe_rsa, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := dhe_rsa,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
-suite({dhe_rsa, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := dhe_rsa,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
-suite({dh_rsa, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := dh_rsa,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
-suite({dh_rsa, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := dh_rsa,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
-suite({dhe_dss, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := dhe_dss,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
-suite({dhe_dss, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := dhe_dss,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
-suite({dh_dss, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := dh_dss,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
-suite({dh_dss, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := dh_dss,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
-suite({dh_anon, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := dh_anon,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_DH_anon_WITH_AES_128_GCM_SHA256;
-suite({dh_anon, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := dh_anon,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_DH_anon_WITH_AES_256_GCM_SHA384;
-
%% RFC 5289 ECC AES-GCM Cipher Suites
-suite({ecdhe_ecdsa, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
-suite({ecdhe_ecdsa, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
-suite({ecdh_ecdsa, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := ecdh_ecdsa,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
-suite({ecdh_ecdsa, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := ecdh_ecdsa,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
-suite({ecdhe_rsa, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
-suite({ecdhe_rsa, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
-suite({ecdh_rsa, aes_128_gcm, null, sha256}) ->
+suite(#{key_exchange := ecdh_rsa,
+ cipher := aes_128_gcm,
+ mac := aead,
+ prf := sha256}) ->
?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
-suite({ecdh_rsa, aes_256_gcm, null, sha384}) ->
+suite(#{key_exchange := ecdh_rsa,
+ cipher := aes_256_gcm,
+ mac := aead,
+ prf := sha384}) ->
?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384;
-
-
%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
-suite({ecdhe_rsa, chacha20_poly1305, null, sha256}) ->
+suite(#{key_exchange := ecdhe_rsa,
+ cipher := chacha20_poly1305,
+ mac := aead,
+ prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
-suite({ecdhe_ecdsa, chacha20_poly1305, null, sha256}) ->
+suite(#{key_exchange := ecdhe_ecdsa,
+ cipher := chacha20_poly1305,
+ mac := aead,
+ prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
-suite({dhe_rsa, chacha20_poly1305, null, sha256}) ->
+suite(#{key_exchange := dhe_rsa,
+ cipher := chacha20_poly1305,
+ mac := aead,
+ prf := sha256}) ->
?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256.
%%--------------------------------------------------------------------
@@ -1212,9 +1837,9 @@ openssl_suite("ECDH-RSA-AES256-GCM-SHA384") ->
?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384.
%%--------------------------------------------------------------------
--spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite().
+-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite() | erl_cipher_suite().
%%
-%% Description: Return openssl cipher suite name.
+%% Description: Return openssl cipher suite name if possible
%%-------------------------------------------------------------------
openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
"DHE-RSA-AES256-SHA";
@@ -1424,36 +2049,74 @@ filter(DerCert, Ciphers) ->
{_, ecdsa} ->
Ciphers1 -- rsa_signed_suites()
end.
-
%%--------------------------------------------------------------------
--spec filter_suites([cipher_suite()]) -> [cipher_suite()].
+-spec filter_suites([erl_cipher_suite()] | [cipher_suite()], map()) ->
+ [erl_cipher_suite()] | [cipher_suite()].
+%%
+%% Description: Filter suites using supplied filter funs
+%%-------------------------------------------------------------------
+filter_suites(Suites, Filters) ->
+ ApplyFilters = fun(Suite) ->
+ filter_suite(Suite, Filters)
+ end,
+ lists:filter(ApplyFilters, Suites).
+
+filter_suite(#{key_exchange := KeyExchange,
+ cipher := Cipher,
+ mac := Hash,
+ prf := Prf},
+ #{key_exchange_filters := KeyFilters,
+ cipher_filters := CipherFilters,
+ mac_filters := HashFilters,
+ prf_filters := PrfFilters}) ->
+ all_filters(KeyExchange, KeyFilters) andalso
+ all_filters(Cipher, CipherFilters) andalso
+ all_filters(Hash, HashFilters) andalso
+ all_filters(Prf, PrfFilters);
+filter_suite(Suite, Filters) ->
+ filter_suite(suite_definition(Suite), Filters).
+
+%%--------------------------------------------------------------------
+-spec filter_suites([erl_cipher_suite()] | [cipher_suite()]) ->
+ [erl_cipher_suite()] | [cipher_suite()].
%%
%% Description: Filter suites for algorithms supported by crypto.
%%-------------------------------------------------------------------
-filter_suites(Suites = [Value|_]) when is_tuple(Value) ->
- Algos = crypto:supports(),
- Hashs = proplists:get_value(hashs, Algos),
- lists:filter(fun({KeyExchange, Cipher, Hash}) ->
- is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
- is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
- is_acceptable_hash(Hash, proplists:get_value(hashs, Algos));
- ({KeyExchange, Cipher, Hash, Prf}) ->
- is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
- is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
- is_acceptable_hash(Hash, Hashs) andalso
- is_acceptable_prf(Prf, Hashs)
- end, Suites);
-
filter_suites(Suites) ->
+ Filters = crypto_support_filters(),
+ filter_suites(Suites, Filters).
+
+all_filters(_, []) ->
+ true;
+all_filters(Value, [Filter| Rest]) ->
+ case Filter(Value) of
+ true ->
+ all_filters(Value, Rest);
+ false ->
+ false
+ end.
+crypto_support_filters() ->
Algos = crypto:supports(),
Hashs = proplists:get_value(hashs, Algos),
- lists:filter(fun(Suite) ->
- {KeyExchange, Cipher, Hash, Prf} = ssl_cipher:suite_definition(Suite),
- is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
- is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
- is_acceptable_hash(Hash, Hashs) andalso
- is_acceptable_prf(Prf, Hashs)
- end, Suites).
+ #{key_exchange_filters =>
+ [fun(KeyExchange) ->
+ is_acceptable_keyexchange(KeyExchange,
+ proplists:get_value(public_keys, Algos))
+ end],
+ cipher_filters =>
+ [fun(Cipher) ->
+ is_acceptable_cipher(Cipher,
+ proplists:get_value(ciphers, Algos))
+ end],
+ mac_filters =>
+ [fun(Hash) ->
+ is_acceptable_hash(Hash, Hashs)
+ end],
+ prf_filters =>
+ [fun(Prf) ->
+ is_acceptable_prf(Prf,
+ proplists:get_value(hashs, Algos))
+ end]}.
is_acceptable_keyexchange(KeyExchange, _Algos) when KeyExchange == psk;
KeyExchange == null ->
@@ -1543,7 +2206,7 @@ calc_mac_hash(Type, Version,
MacSecret, SeqNo, Type,
Length, PlainFragment).
-is_stream_ciphersuite({_, rc4_128, _, _}) ->
+is_stream_ciphersuite(#{cipher := rc4_128}) ->
true;
is_stream_ciphersuite(_) ->
false.
@@ -1671,6 +2334,11 @@ prf_algorithm(default_prf, {3, _}) ->
prf_algorithm(Algo, _) ->
hash_algorithm(Algo).
+mac_algorithm(aead) ->
+ aead;
+mac_algorithm(Algo) ->
+ hash_algorithm(Algo).
+
hash_algorithm(null) -> ?NULL;
hash_algorithm(md5) -> ?MD5;
hash_algorithm(sha) -> ?SHA; %% Only sha always refers to "SHA-1"
@@ -1701,6 +2369,10 @@ sign_algorithm(Other) when is_integer(Other) andalso ((Other >= 224) and (Other
hash_size(null) ->
0;
+%% The AEAD MAC hash size is not used in the context
+%% of calculating the master secret. See RFC 5246 Section 6.2.3.3.
+hash_size(aead) ->
+ 0;
hash_size(md5) ->
16;
hash_size(sha) ->
diff --git a/lib/ssl/src/ssl_config.erl b/lib/ssl/src/ssl_config.erl
index e4611995ec..022fb7eac0 100644
--- a/lib/ssl/src/ssl_config.erl
+++ b/lib/ssl/src/ssl_config.erl
@@ -91,7 +91,15 @@ init_certificates(undefined, #{pem_cache := PemCache} = Config, CertFile, server
end;
init_certificates(Cert, Config, _, _) ->
{ok, Config#{own_certificate => Cert}}.
-
+init_private_key(_, #{algorithm := Alg} = Key, <<>>, _Password, _Client) when Alg == ecdsa;
+ Alg == rsa;
+ Alg == dss ->
+ case maps:is_key(engine, Key) andalso maps:is_key(key_id, Key) of
+ true ->
+ Key;
+ false ->
+ throw({key, {invalid_key_id, Key}})
+ end;
init_private_key(_, undefined, <<>>, _Password, _Client) ->
undefined;
init_private_key(DbHandle, undefined, KeyFile, Password, _) ->
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 6a1df3c56d..63fae78195 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -44,31 +44,31 @@
-export([send/2, recv/3, close/2, shutdown/2,
new_user/2, get_opts/2, set_opts/2,
peer_certificate/1, renegotiation/1, negotiated_protocol/1, prf/5,
- connection_information/2, handle_common_event/5
+ connection_information/2
]).
-%% General gen_statem state functions with extra callback argument
-%% to determine if it is an SSL/TLS or DTLS gen_statem machine
--export([init/4, hello/4, abbreviated/4, certify/4, cipher/4, connection/4, downgrade/4]).
-
-%% gen_statem callbacks
--export([terminate/3, format_status/2]).
-
-%%
--export([handle_info/3, handle_call/5, handle_session/7, ssl_config/3,
- prepare_connection/2, hibernate_after/3]).
-
%% Alert and close handling
--export([handle_own_alert/4,handle_alert/3,
+-export([handle_own_alert/4, handle_alert/3,
handle_normal_shutdown/3
]).
%% Data handling
-export([write_application_data/3, read_application_data/2]).
+%% Help functions for tls|dtls_connection.erl
+-export([handle_session/7, ssl_config/3,
+ prepare_connection/2, hibernate_after/3]).
+
+%% General gen_statem state functions with extra callback argument
+%% to determine if it is an SSL/TLS or DTLS gen_statem machine
+-export([init/4, error/4, hello/4, abbreviated/4, certify/4, cipher/4, connection/4, downgrade/4]).
+
+%% gen_statem callbacks
+-export([terminate/3, format_status/2]).
+
+%%====================================================================
+%% Setup
%%====================================================================
-%% Internal application API
-%%====================================================================
%%--------------------------------------------------------------------
-spec connect(tls_connection | dtls_connection,
host(), inet:port_number(),
@@ -164,6 +164,16 @@ socket_control(dtls_connection = Connection, {_, Socket}, Pid, Transport, Listen
{error, Reason} ->
{error, Reason}
end.
+
+start_or_recv_cancel_timer(infinity, _RecvFrom) ->
+ undefined;
+start_or_recv_cancel_timer(Timeout, RecvFrom) ->
+ erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
+
+%%====================================================================
+%% User events
+%%====================================================================
+
%%--------------------------------------------------------------------
-spec send(pid(), iodata()) -> ok | {error, reason()}.
%%
@@ -272,6 +282,161 @@ renegotiation(ConnectionPid) ->
prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
call(ConnectionPid, {prf, Secret, Label, Seed, WantedLength}).
+%%====================================================================
+%% Alert and close handling
+%%====================================================================
+handle_own_alert(Alert, Version, StateName,
+ #state{role = Role,
+ transport_cb = Transport,
+ socket = Socket,
+ protocol_cb = Connection,
+ connection_states = ConnectionStates,
+ ssl_options = SslOpts} = State) ->
+ try %% Try to tell the other side
+ {BinMsg, _} =
+ Connection:encode_alert(Alert, Version, ConnectionStates),
+ Connection:send(Transport, Socket, BinMsg)
+ catch _:_ -> %% Can crash if we are in a uninitialized state
+ ignore
+ end,
+ try %% Try to tell the local user
+ log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = Role}),
+ handle_normal_shutdown(Alert,StateName, State)
+ catch _:_ ->
+ ok
+ end,
+ {stop, {shutdown, own_alert}}.
+
+handle_normal_shutdown(Alert, _, #state{socket = Socket,
+ transport_cb = Transport,
+ protocol_cb = Connection,
+ start_or_recv_from = StartFrom,
+ tracker = Tracker,
+ role = Role, renegotiation = {false, first}}) ->
+ alert_user(Transport, Tracker,Socket, StartFrom, Alert, Role, Connection);
+
+handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
+ socket_options = Opts,
+ transport_cb = Transport,
+ protocol_cb = Connection,
+ user_application = {_Mon, Pid},
+ tracker = Tracker,
+ start_or_recv_from = RecvFrom, role = Role}) ->
+ alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection).
+
+handle_alert(#alert{level = ?FATAL} = Alert, StateName,
+ #state{socket = Socket, transport_cb = Transport,
+ protocol_cb = Connection,
+ ssl_options = SslOpts, start_or_recv_from = From, host = Host,
+ port = Port, session = Session, user_application = {_Mon, Pid},
+ role = Role, socket_options = Opts, tracker = Tracker}) ->
+ invalidate_session(Role, Host, Port, Session),
+ log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(),
+ StateName, Alert#alert{role = opposite_role(Role)}),
+ alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection),
+ {stop, normal};
+
+handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
+ StateName, State) ->
+ handle_normal_shutdown(Alert, StateName, State),
+ {stop, {shutdown, peer_close}};
+
+handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
+ #state{role = Role, ssl_options = SslOpts, protocol_cb = Connection, renegotiation = {true, internal}} = State) ->
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ handle_normal_shutdown(Alert, StateName, State),
+ {stop, {shutdown, peer_close}};
+
+handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
+ #state{role = Role,
+ ssl_options = SslOpts, renegotiation = {true, From},
+ protocol_cb = Connection} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ gen_statem:reply(From, {error, renegotiation_rejected}),
+ {Record, State1} = Connection:next_record(State0),
+ %% Go back to connection!
+ State = Connection:reinit_handshake_data(State1#state{renegotiation = undefined}),
+ Connection:next_event(connection, Record, State);
+
+%% Gracefully log and ignore all other warning alerts
+handle_alert(#alert{level = ?WARNING} = Alert, StateName,
+ #state{ssl_options = SslOpts, protocol_cb = Connection, role = Role} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ {Record, State} = Connection:next_record(State0),
+ Connection:next_event(StateName, Record, State).
+
+%%====================================================================
+%% Data handling
+%%====================================================================
+write_application_data(Data0, From,
+ #state{socket = Socket,
+ negotiated_version = Version,
+ protocol_cb = Connection,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ socket_options = SockOpts,
+ ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State) ->
+ Data = encode_packet(Data0, SockOpts),
+
+ case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
+ true ->
+ Connection:renegotiate(State#state{renegotiation = {true, internal}},
+ [{next_event, {call, From}, {application_data, Data0}}]);
+ false ->
+ {Msgs, ConnectionStates} = Connection:encode_data(Data, Version, ConnectionStates0),
+ Result = Connection:send(Transport, Socket, Msgs),
+ ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates},
+ [{reply, From, Result}])
+ end.
+
+read_application_data(Data, #state{user_application = {_Mon, Pid},
+ socket = Socket,
+ protocol_cb = Connection,
+ transport_cb = Transport,
+ socket_options = SOpts,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ timer = Timer,
+ user_data_buffer = Buffer0,
+ tracker = Tracker} = State0) ->
+ Buffer1 = if
+ Buffer0 =:= <<>> -> Data;
+ Data =:= <<>> -> Buffer0;
+ true -> <<Buffer0/binary, Data/binary>>
+ end,
+ case get_data(SOpts, BytesToRead, Buffer1) of
+ {ok, ClientData, Buffer} -> % Send data
+ SocketOpt = deliver_app_data(Transport, Socket, SOpts,
+ ClientData, Pid, RecvFrom, Tracker, Connection),
+ cancel_timer(Timer),
+ State = State0#state{user_data_buffer = Buffer,
+ start_or_recv_from = undefined,
+ timer = undefined,
+ bytes_to_read = undefined,
+ socket_options = SocketOpt
+ },
+ if
+ SocketOpt#socket_options.active =:= false; Buffer =:= <<>> ->
+ %% Passive mode, wait for active once or recv
+ %% Active and empty, get more data
+ Connection:next_record_if_active(State);
+ true -> %% We have more data
+ read_application_data(<<>>, State)
+ end;
+ {more, Buffer} -> % no reply, we need more data
+ Connection:next_record(State0#state{user_data_buffer = Buffer});
+ {passive, Buffer} ->
+ Connection:next_record_if_active(State0#state{user_data_buffer = Buffer});
+ {error,_Reason} -> %% Invalid packet in packet mode
+ deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker, Connection),
+ {stop, normal, State0}
+ end.
+%%====================================================================
+%% Help functions for tls|dtls_connection.erl
+%%====================================================================
%%--------------------------------------------------------------------
-spec handle_session(#server_hello{}, ssl_record:ssl_version(),
binary(), ssl_record:connection_states(), _,_, #state{}) ->
@@ -283,7 +448,7 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
#state{session = #session{session_id = OldId},
negotiated_version = ReqVersion,
negotiated_protocol = CurrentProtocol} = State0) ->
- {KeyAlgorithm, _, _, _} =
+ #{key_exchange := KeyAlgorithm} =
ssl_cipher:suite_definition(CipherSuite),
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
@@ -340,7 +505,7 @@ ssl_config(Opts, Role, State) ->
ssl_options = Opts}.
%%====================================================================
-%% gen_statem state functions
+%% gen_statem general state functions with connection cb argument
%%====================================================================
%%--------------------------------------------------------------------
-spec init(gen_statem:event_type(),
@@ -371,6 +536,15 @@ init(_Type, _Event, _State, _Connection) ->
{keep_state_and_data, [postpone]}.
%%--------------------------------------------------------------------
+-spec error(gen_statem:event_type(),
+ {start, timeout()} | term(), #state{},
+ tls_connection | dtls_connection) ->
+ gen_statem:state_function_result().
+%%--------------------------------------------------------------------
+error({call, From}, Msg, State, Connection) ->
+ handle_call(Msg, From, ?FUNCTION_NAME, State, Connection).
+
+%%--------------------------------------------------------------------
-spec hello(gen_statem:event_type(),
#hello_request{} | #server_hello{} | term(),
#state{}, tls_connection | dtls_connection) ->
@@ -393,7 +567,6 @@ hello(Type, Msg, State, Connection) ->
%%--------------------------------------------------------------------
abbreviated({call, From}, Msg, State, Connection) ->
handle_call(Msg, From, ?FUNCTION_NAME, State, Connection);
-
abbreviated(internal, #finished{verify_data = Data} = Finished,
#state{role = server,
negotiated_version = Version,
@@ -414,7 +587,6 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0)
end;
-
abbreviated(internal, #finished{verify_data = Data} = Finished,
#state{role = client, tls_handshake_history = Handshake0,
session = #session{master_secret = MasterSecret},
@@ -434,7 +606,6 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0)
end;
-
%% only allowed to send next_protocol message after change cipher spec
%% & before finished message and it is not allowed during renegotiation
abbreviated(internal, #next_protocol{selected_protocol = SelectedProtocol},
@@ -448,7 +619,7 @@ abbreviated(internal,
#change_cipher_spec{type = <<1>>}, #state{connection_states = ConnectionStates0} =
State0, Connection) ->
ConnectionStates1 =
- ssl_record:activate_pending_connection_state(ConnectionStates0, read),
+ ssl_record:activate_pending_connection_state(ConnectionStates0, read, Connection),
{Record, State} = Connection:next_record(State0#state{connection_states =
ConnectionStates1}),
Connection:next_event(?FUNCTION_NAME, Record, State#state{expecting_finished = true});
@@ -475,7 +646,6 @@ certify(internal, #certificate{asn1_certificates = []},
State, _) ->
Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE),
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State);
-
certify(internal, #certificate{asn1_certificates = []},
#state{role = server,
ssl_options = #ssl_options{verify = verify_peer,
@@ -484,7 +654,6 @@ certify(internal, #certificate{asn1_certificates = []},
{Record, State} =
Connection:next_record(State0#state{client_certificate_requested = false}),
Connection:next_event(?FUNCTION_NAME, Record, State);
-
certify(internal, #certificate{},
#state{role = server,
negotiated_version = Version,
@@ -492,7 +661,6 @@ certify(internal, #certificate{},
State, _) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate),
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State);
-
certify(internal, #certificate{} = Cert,
#state{negotiated_version = Version,
role = Role,
@@ -509,7 +677,6 @@ certify(internal, #certificate{} = Cert,
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State)
end;
-
certify(internal, #server_key_exchange{exchange_keys = Keys},
#state{role = client, negotiated_version = Version,
key_algorithm = Alg,
@@ -542,7 +709,6 @@ certify(internal, #server_key_exchange{exchange_keys = Keys},
Version, ?FUNCTION_NAME, State)
end
end;
-
certify(internal, #certificate_request{} = CertRequest,
#state{session = #session{own_certificate = Cert},
role = client,
@@ -556,7 +722,6 @@ certify(internal, #certificate_request{} = CertRequest,
Connection:next_event(?FUNCTION_NAME, Record,
State#state{cert_hashsign_algorithm = NegotiatedHashSign})
end;
-
%% PSK and RSA_PSK might bypass the Server-Key-Exchange
certify(internal, #server_hello_done{},
#state{session = #session{master_secret = undefined},
@@ -575,7 +740,6 @@ certify(internal, #server_hello_done{},
State0#state{premaster_secret = PremasterSecret}),
client_certify_and_key_exchange(State, Connection)
end;
-
certify(internal, #server_hello_done{},
#state{session = #session{master_secret = undefined},
ssl_options = #ssl_options{user_lookup_fun = PSKLookup},
@@ -596,7 +760,6 @@ certify(internal, #server_hello_done{},
State0#state{premaster_secret = RSAPremasterSecret}),
client_certify_and_key_exchange(State, Connection)
end;
-
%% Master secret was determined with help of server-key exchange msg
certify(internal, #server_hello_done{},
#state{session = #session{master_secret = MasterSecret} = Session,
@@ -612,7 +775,6 @@ certify(internal, #server_hello_done{},
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0)
end;
-
%% Master secret is calculated from premaster_secret
certify(internal, #server_hello_done{},
#state{session = Session0,
@@ -630,7 +792,6 @@ certify(internal, #server_hello_done{},
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0)
end;
-
certify(internal = Type, #client_key_exchange{} = Msg,
#state{role = server,
client_certificate_requested = true,
@@ -638,7 +799,6 @@ certify(internal = Type, #client_key_exchange{} = Msg,
Connection) ->
%% We expect a certificate here
handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection);
-
certify(internal, #client_key_exchange{exchange_keys = Keys},
State = #state{key_algorithm = KeyAlg, negotiated_version = Version}, Connection) ->
try
@@ -648,7 +808,6 @@ certify(internal, #client_key_exchange{exchange_keys = Keys},
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State)
end;
-
certify(Type, Msg, State, Connection) ->
handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection).
@@ -660,10 +819,8 @@ certify(Type, Msg, State, Connection) ->
%%--------------------------------------------------------------------
cipher({call, From}, Msg, State, Connection) ->
handle_call(Msg, From, ?FUNCTION_NAME, State, Connection);
-
cipher(info, Msg, State, _) ->
handle_info(Msg, ?FUNCTION_NAME, State);
-
cipher(internal, #certificate_verify{signature = Signature,
hashsign_algorithm = CertHashSign},
#state{role = server,
@@ -686,14 +843,12 @@ cipher(internal, #certificate_verify{signature = Signature,
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0)
end;
-
%% client must send a next protocol message if we are expecting it
cipher(internal, #finished{},
#state{role = server, expecting_next_protocol_negotiation = true,
negotiated_protocol = undefined, negotiated_version = Version} = State0,
_Connection) ->
handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, ?FUNCTION_NAME, State0);
-
cipher(internal, #finished{verify_data = Data} = Finished,
#state{negotiated_version = Version,
host = Host,
@@ -716,7 +871,6 @@ cipher(internal, #finished{verify_data = Data} = Finished,
#alert{} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State)
end;
-
%% only allowed to send next_protocol message after change cipher spec
%% & before finished message and it is not allowed during renegotiation
cipher(internal, #next_protocol{selected_protocol = SelectedProtocol},
@@ -729,7 +883,7 @@ cipher(internal, #next_protocol{selected_protocol = SelectedProtocol},
cipher(internal, #change_cipher_spec{type = <<1>>}, #state{connection_states = ConnectionStates0} =
State0, Connection) ->
ConnectionStates1 =
- ssl_record:activate_pending_connection_state(ConnectionStates0, read),
+ ssl_record:activate_pending_connection_state(ConnectionStates0, read, Connection),
{Record, State} = Connection:next_record(State0#state{connection_states =
ConnectionStates1}),
Connection:next_event(?FUNCTION_NAME, Record, State#state{expecting_finished = true});
@@ -845,8 +999,8 @@ handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName,
StateName, State);
handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} = State,
_) ->
- Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version, {StateName, Msg}, State).
+ Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, Msg}),
+ handle_own_alert(Alert, Version, StateName, State).
handle_call({application_data, _Data}, _, _, _, _) ->
%% In renegotiation priorities handshake, send data when handshake is finished
@@ -958,25 +1112,21 @@ handle_info({ErrorTag, Socket, econnaborted}, StateName,
alert_user(Transport, Tracker,Socket,
StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, Connection),
{stop, normal, State};
-
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
error_tag = ErrorTag} = State) ->
Report = io_lib:format("SSL: Socket error: ~p ~n", [Reason]),
error_logger:info_report(Report),
handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
{stop, normal, State};
-
handle_info({'DOWN', MonitorRef, _, _, _}, _,
State = #state{user_application={MonitorRef,_Pid}}) ->
{stop, normal, State};
-
%%% So that terminate will be run when supervisor issues shutdown
handle_info({'EXIT', _Sup, shutdown}, _StateName, State) ->
{stop, shutdown, State};
handle_info({'EXIT', Socket, normal}, _StateName, #state{socket = Socket} = State) ->
%% Handle as transport close"
{stop, {shutdown, transport_closed}, State};
-
handle_info(allow_renegotiate, StateName, State) ->
{next_state, StateName, State#state{allow_renegotiate = true}};
@@ -984,13 +1134,11 @@ handle_info({cancel_start_or_recv, StartFrom}, StateName,
#state{renegotiation = {false, first}} = State) when StateName =/= connection ->
{stop_and_reply, {shutdown, user_timeout},
{reply, StartFrom, {error, timeout}}, State#state{timer = undefined}};
-
handle_info({cancel_start_or_recv, RecvFrom}, StateName,
#state{start_or_recv_from = RecvFrom} = State) when RecvFrom =/= undefined ->
{next_state, StateName, State#state{start_or_recv_from = undefined,
bytes_to_read = undefined,
timer = undefined}, [{reply, RecvFrom, {error, timeout}}]};
-
handle_info({cancel_start_or_recv, _RecvFrom}, StateName, State) ->
{next_state, StateName, State#state{timer = undefined}};
@@ -999,9 +1147,9 @@ handle_info(Msg, StateName, #state{socket = Socket, error_tag = Tag} = State) ->
error_logger:info_report(Report),
{next_state, StateName, State}.
-%%--------------------------------------------------------------------
-%% gen_statem callbacks
-%%--------------------------------------------------------------------
+%%====================================================================
+%% general gen_statem callbacks
+%%====================================================================
terminate(_, _, #state{terminated = true}) ->
%% Happens when user closes the connection using ssl:close/1
%% we want to guarantee that Transport:close has been called
@@ -1010,7 +1158,6 @@ terminate(_, _, #state{terminated = true}) ->
%% returning. In both cases terminate has been run manually
%% before run by gen_statem which will end up here
ok;
-
terminate({shutdown, transport_closed} = Reason,
_StateName, #state{protocol_cb = Connection,
socket = Socket, transport_cb = Transport} = State) ->
@@ -1037,7 +1184,6 @@ terminate(Reason, connection, #state{negotiated_version = Version,
{BinAlert, ConnectionStates} = terminate_alert(Reason, Version, ConnectionStates0, Connection),
Connection:send(Transport, Socket, BinAlert),
Connection:close(Reason, Socket, Transport, ConnectionStates, Check);
-
terminate(Reason, _StateName, #state{transport_cb = Transport, protocol_cb = Connection,
socket = Socket
} = State) ->
@@ -1071,118 +1217,6 @@ format_status(terminate, [_, StateName, State]) ->
}}]}].
%%--------------------------------------------------------------------
-%%%
-%%--------------------------------------------------------------------
-write_application_data(Data0, From,
- #state{socket = Socket,
- negotiated_version = Version,
- protocol_cb = Connection,
- transport_cb = Transport,
- connection_states = ConnectionStates0,
- socket_options = SockOpts,
- ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State) ->
- Data = encode_packet(Data0, SockOpts),
-
- case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
- true ->
- Connection:renegotiate(State#state{renegotiation = {true, internal}},
- [{next_event, {call, From}, {application_data, Data0}}]);
- false ->
- {Msgs, ConnectionStates} = Connection:encode_data(Data, Version, ConnectionStates0),
- Result = Connection:send(Transport, Socket, Msgs),
- ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates},
- [{reply, From, Result}])
- end.
-
-read_application_data(Data, #state{user_application = {_Mon, Pid},
- socket = Socket,
- protocol_cb = Connection,
- transport_cb = Transport,
- socket_options = SOpts,
- bytes_to_read = BytesToRead,
- start_or_recv_from = RecvFrom,
- timer = Timer,
- user_data_buffer = Buffer0,
- tracker = Tracker} = State0) ->
- Buffer1 = if
- Buffer0 =:= <<>> -> Data;
- Data =:= <<>> -> Buffer0;
- true -> <<Buffer0/binary, Data/binary>>
- end,
- case get_data(SOpts, BytesToRead, Buffer1) of
- {ok, ClientData, Buffer} -> % Send data
- SocketOpt = deliver_app_data(Transport, Socket, SOpts,
- ClientData, Pid, RecvFrom, Tracker, Connection),
- cancel_timer(Timer),
- State = State0#state{user_data_buffer = Buffer,
- start_or_recv_from = undefined,
- timer = undefined,
- bytes_to_read = undefined,
- socket_options = SocketOpt
- },
- if
- SocketOpt#socket_options.active =:= false; Buffer =:= <<>> ->
- %% Passive mode, wait for active once or recv
- %% Active and empty, get more data
- Connection:next_record_if_active(State);
- true -> %% We have more data
- read_application_data(<<>>, State)
- end;
- {more, Buffer} -> % no reply, we need more data
- Connection:next_record(State0#state{user_data_buffer = Buffer});
- {passive, Buffer} ->
- Connection:next_record_if_active(State0#state{user_data_buffer = Buffer});
- {error,_Reason} -> %% Invalid packet in packet mode
- deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker, Connection),
- {stop, normal, State0}
- end.
-%%--------------------------------------------------------------------
-%%%
-%%--------------------------------------------------------------------
-handle_alert(#alert{level = ?FATAL} = Alert, StateName,
- #state{socket = Socket, transport_cb = Transport,
- protocol_cb = Connection,
- ssl_options = SslOpts, start_or_recv_from = From, host = Host,
- port = Port, session = Session, user_application = {_Mon, Pid},
- role = Role, socket_options = Opts, tracker = Tracker}) ->
- invalidate_session(Role, Host, Port, Session),
- log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(),
- StateName, Alert#alert{role = opposite_role(Role)}),
- alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection),
- {stop, normal};
-
-handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
- StateName, State) ->
- handle_normal_shutdown(Alert, StateName, State),
- {stop, {shutdown, peer_close}};
-
-handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
- #state{role = Role, ssl_options = SslOpts, protocol_cb = Connection, renegotiation = {true, internal}} = State) ->
- log_alert(SslOpts#ssl_options.log_alert, Role,
- Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
- handle_normal_shutdown(Alert, StateName, State),
- {stop, {shutdown, peer_close}};
-
-handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
- #state{role = Role,
- ssl_options = SslOpts, renegotiation = {true, From},
- protocol_cb = Connection} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, Role,
- Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
- gen_statem:reply(From, {error, renegotiation_rejected}),
- {Record, State} = Connection:next_record(State0),
- %% Go back to connection!
- Connection:next_event(connection, Record, State);
-
-%% Gracefully log and ignore all other warning alerts
-handle_alert(#alert{level = ?WARNING} = Alert, StateName,
- #state{ssl_options = SslOpts, protocol_cb = Connection, role = Role} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, Role,
- Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
- {Record, State} = Connection:next_record(State0),
- Connection:next_event(StateName, Record, State).
-
-%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
connection_info(#state{sni_hostname = SNIHostname,
@@ -1192,9 +1226,9 @@ connection_info(#state{sni_hostname = SNIHostname,
negotiated_version = {_,_} = Version,
ssl_options = Opts}) ->
RecordCB = record_cb(Connection),
- CipherSuiteDef = ssl_cipher:erl_suite_definition(CipherSuite),
- IsNamedCurveSuite = lists:member(element(1,CipherSuiteDef),
- [ecdh_ecdsa, ecdhe_ecdsa, ecdh_anon]),
+ CipherSuiteDef = #{key_exchange := KexAlg} = ssl_cipher:suite_definition(CipherSuite),
+ IsNamedCurveSuite = lists:member(KexAlg,
+ [ecdh_ecdsa, ecdhe_ecdsa, ecdh_anon]),
CurveInfo = case ECCCurve of
{namedCurve, Curve} when IsNamedCurveSuite ->
[{ecc, {named_curve, pubkey_cert_records:namedCurves(Curve)}}];
@@ -1203,7 +1237,7 @@ connection_info(#state{sni_hostname = SNIHostname,
end,
[{protocol, RecordCB:protocol_version(Version)},
{session_id, SessionId},
- {cipher_suite, CipherSuiteDef},
+ {cipher_suite, ssl_cipher:erl_suite_definition(CipherSuiteDef)},
{sni_hostname, SNIHostname} | CurveInfo] ++ ssl_options_list(Opts).
security_info(#state{connection_states = ConnectionStates}) ->
@@ -1271,7 +1305,7 @@ resumed_server_hello(#state{session = Session,
server_hello(ServerHello, State0, Connection) ->
CipherSuite = ServerHello#server_hello.cipher_suite,
- {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
+ #{key_exchange := KeyAlgorithm} = ssl_cipher:suite_definition(CipherSuite),
State = Connection:queue_handshake(ServerHello, State0),
State#state{key_algorithm = KeyAlgorithm}.
@@ -1285,8 +1319,8 @@ handle_peer_cert(Role, PeerCert, PublicKeyInfo,
State1 = State0#state{session =
Session#session{peer_certificate = PeerCert},
public_key_info = PublicKeyInfo},
- {KeyAlg,_,_,_} = ssl_cipher:suite_definition(CipherSuite),
- State2 = handle_peer_cert_key(Role, PeerCert, PublicKeyInfo, KeyAlg, State1),
+ #{key_exchange := KeyAlgorithm} = ssl_cipher:suite_definition(CipherSuite),
+ State2 = handle_peer_cert_key(Role, PeerCert, PublicKeyInfo, KeyAlgorithm, State1),
{Record, State} = Connection:next_record(State2),
Connection:next_event(certify, Record, State).
@@ -1299,7 +1333,6 @@ handle_peer_cert_key(client, _,
ECDHKey = public_key:generate_key(PublicKeyParams),
PremasterSecret = ssl_handshake:premaster_secret(PublicKey, ECDHKey),
master_secret(PremasterSecret, State#state{diffie_hellman_keys = ECDHKey});
-
%% We do currently not support cipher suites that use fixed DH.
%% If we want to implement that the following clause can be used
%% to extract DH parameters form cert.
@@ -1319,7 +1352,6 @@ certify_client(#state{client_certificate_requested = true, role = client,
= State, Connection) ->
Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client),
Connection:queue_handshake(Certificate, State);
-
certify_client(#state{client_certificate_requested = false} = State, _) ->
State.
@@ -1370,26 +1402,25 @@ server_certify_and_key_exchange(State0, Connection) ->
certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS},
#state{private_key = Key, client_hello_version = {Major, Minor} = Version} = State, Connection) ->
-
+ FakeSecret = make_premaster_secret(Version, rsa),
%% Countermeasure for Bleichenbacher attack always provide some kind of premaster secret
%% and fail handshake later.RFC 5246 section 7.4.7.1.
PremasterSecret =
try ssl_handshake:premaster_secret(EncPMS, Key) of
Secret when erlang:byte_size(Secret) == ?NUM_OF_PREMASTERSECRET_BYTES ->
case Secret of
- <<?BYTE(Major), ?BYTE(Minor), _/binary>> -> %% Correct
- Secret;
+ <<?BYTE(Major), ?BYTE(Minor), Rest/binary>> -> %% Correct
+ <<?BYTE(Major), ?BYTE(Minor), Rest/binary>>;
<<?BYTE(_), ?BYTE(_), Rest/binary>> -> %% Version mismatch
<<?BYTE(Major), ?BYTE(Minor), Rest/binary>>
end;
_ -> %% erlang:byte_size(Secret) =/= ?NUM_OF_PREMASTERSECRET_BYTES
- make_premaster_secret(Version, rsa)
+ FakeSecret
catch
#alert{description = ?DECRYPT_ERROR} ->
- make_premaster_secret(Version, rsa)
- end,
+ FakeSecret
+ end,
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
-
certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey},
#state{diffie_hellman_params = #'DHParameter'{} = Params,
diffie_hellman_keys = {_, ServerDhPrivateKey}} = State,
@@ -1401,14 +1432,12 @@ certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientP
#state{diffie_hellman_keys = ECDHKey} = State, Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(#'ECPoint'{point = ClientPublicEcDhPoint}, ECDHKey),
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
-
certify_client_key_exchange(#client_psk_identity{} = ClientKey,
#state{ssl_options =
#ssl_options{user_lookup_fun = PSKLookup}} = State0,
Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
-
certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,
#state{diffie_hellman_params = #'DHParameter'{} = Params,
diffie_hellman_keys = {_, ServerDhPrivateKey},
@@ -1425,7 +1454,6 @@ certify_client_key_exchange(#client_rsa_psk_identity{} = ClientKey,
Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
-
certify_client_key_exchange(#client_srp_public{} = ClientKey,
#state{srp_params = Params,
srp_keys = Key
@@ -1439,7 +1467,6 @@ certify_server(#state{key_algorithm = Algo} = State, _) when Algo == dh_anon;
Algo == dhe_psk;
Algo == srp_anon ->
State;
-
certify_server(#state{cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert}} = State, Connection) ->
@@ -1473,7 +1500,6 @@ key_exchange(#state{role = server, key_algorithm = Algo,
PrivateKey}),
State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = DHKeys};
-
key_exchange(#state{role = server, private_key = Key, key_algorithm = Algo} = State, _)
when Algo == ecdh_ecdsa; Algo == ecdh_rsa ->
State#state{diffie_hellman_keys = Key};
@@ -1499,7 +1525,6 @@ key_exchange(#state{role = server, key_algorithm = Algo,
PrivateKey}),
State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = ECDHKeys};
-
key_exchange(#state{role = server, key_algorithm = psk,
ssl_options = #ssl_options{psk_identity = undefined}} = State, _) ->
State;
@@ -1520,7 +1545,6 @@ key_exchange(#state{role = server, key_algorithm = psk,
ServerRandom,
PrivateKey}),
Connection:queue_handshake(Msg, State0);
-
key_exchange(#state{role = server, key_algorithm = dhe_psk,
ssl_options = #ssl_options{psk_identity = PskIdentityHint},
hashsign_algorithm = HashSignAlgo,
@@ -1542,7 +1566,6 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk,
PrivateKey}),
State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = DHKeys};
-
key_exchange(#state{role = server, key_algorithm = rsa_psk,
ssl_options = #ssl_options{psk_identity = undefined}} = State, _) ->
State;
@@ -1563,7 +1586,6 @@ key_exchange(#state{role = server, key_algorithm = rsa_psk,
ServerRandom,
PrivateKey}),
Connection:queue_handshake(Msg, State0);
-
key_exchange(#state{role = server, key_algorithm = Algo,
ssl_options = #ssl_options{user_lookup_fun = LookupFun},
hashsign_algorithm = HashSignAlgo,
@@ -1594,7 +1616,6 @@ key_exchange(#state{role = server, key_algorithm = Algo,
State = Connection:queue_handshake(Msg, State0),
State#state{srp_params = SrpParams,
srp_keys = Keys};
-
key_exchange(#state{role = client,
key_algorithm = rsa,
public_key_info = PublicKeyInfo,
@@ -1602,7 +1623,6 @@ key_exchange(#state{role = client,
premaster_secret = PremasterSecret} = State0, Connection) ->
Msg = rsa_key_exchange(ssl:tls_version(Version), PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
-
key_exchange(#state{role = client,
key_algorithm = Algorithm,
negotiated_version = Version,
@@ -1623,7 +1643,6 @@ key_exchange(#state{role = client,
Algorithm == ecdh_anon ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {ecdh, Keys}),
Connection:queue_handshake(Msg, State0);
-
key_exchange(#state{role = client,
ssl_options = SslOpts,
key_algorithm = psk,
@@ -1631,7 +1650,6 @@ key_exchange(#state{role = client,
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{psk, SslOpts#ssl_options.psk_identity}),
Connection:queue_handshake(Msg, State0);
-
key_exchange(#state{role = client,
ssl_options = SslOpts,
key_algorithm = dhe_psk,
@@ -1651,7 +1669,6 @@ key_exchange(#state{role = client,
Msg = rsa_psk_key_exchange(ssl:tls_version(Version), SslOpts#ssl_options.psk_identity,
PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
-
key_exchange(#state{role = client,
key_algorithm = Algorithm,
negotiated_version = Version,
@@ -1740,7 +1757,7 @@ finalize_handshake(State0, StateName, Connection) ->
ConnectionStates =
ssl_record:activate_pending_connection_state(ConnectionStates0,
- write),
+ write, Connection),
State2 = State1#state{connection_states = ConnectionStates},
State = next_protocol(State2, Connection),
@@ -2021,10 +2038,7 @@ set_socket_opts(_,_, _, [{active, _} = Opt| _], SockOpts, _) ->
set_socket_opts(ConnectionCb, Transport, Socket, [Opt | Opts], SockOpts, Other) ->
set_socket_opts(ConnectionCb, Transport, Socket, Opts, SockOpts, [Opt | Other]).
-start_or_recv_cancel_timer(infinity, _RecvFrom) ->
- undefined;
-start_or_recv_cancel_timer(Timeout, RecvFrom) ->
- erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
+
hibernate_after(connection = StateName,
#state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}} = State,
@@ -2409,45 +2423,6 @@ log_alert(true, Role, ProtocolName, StateName, Alert) ->
log_alert(false, _, _, _, _) ->
ok.
-handle_own_alert(Alert, Version, StateName,
- #state{role = Role,
- transport_cb = Transport,
- socket = Socket,
- protocol_cb = Connection,
- connection_states = ConnectionStates,
- ssl_options = SslOpts} = State) ->
- try %% Try to tell the other side
- {BinMsg, _} =
- Connection:encode_alert(Alert, Version, ConnectionStates),
- Connection:send(Transport, Socket, BinMsg)
- catch _:_ -> %% Can crash if we are in a uninitialized state
- ignore
- end,
- try %% Try to tell the local user
- log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = Role}),
- handle_normal_shutdown(Alert,StateName, State)
- catch _:_ ->
- ok
- end,
- {stop, {shutdown, own_alert}}.
-
-handle_normal_shutdown(Alert, _, #state{socket = Socket,
- transport_cb = Transport,
- protocol_cb = Connection,
- start_or_recv_from = StartFrom,
- tracker = Tracker,
- role = Role, renegotiation = {false, first}}) ->
- alert_user(Transport, Tracker,Socket, StartFrom, Alert, Role, Connection);
-
-handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
- socket_options = Opts,
- transport_cb = Transport,
- protocol_cb = Connection,
- user_application = {_Mon, Pid},
- tracker = Tracker,
- start_or_recv_from = RecvFrom, role = Role}) ->
- alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection).
-
invalidate_session(client, Host, Port, Session) ->
ssl_manager:invalidate_session(Host, Port, Session);
invalidate_session(server, _, Port, Session) ->
diff --git a/lib/ssl/src/ssl_crl_cache.erl b/lib/ssl/src/ssl_crl_cache.erl
index 86c0207515..8817b0c884 100644
--- a/lib/ssl/src/ssl_crl_cache.erl
+++ b/lib/ssl/src/ssl_crl_cache.erl
@@ -94,7 +94,7 @@ delete({der, CRLs}) ->
delete(URI) ->
case http_uri:parse(URI) of
{ok, {http, _, _ , _, Path,_}} ->
- ssl_manager:delete_crls(string:strip(Path, left, $/));
+ ssl_manager:delete_crls(string:trim(Path, leading, "/"));
_ ->
{error, {only_http_distribution_points_supported, URI}}
end.
@@ -105,7 +105,7 @@ delete(URI) ->
do_insert(URI, CRLs) ->
case http_uri:parse(URI) of
{ok, {http, _, _ , _, Path,_}} ->
- ssl_manager:insert_crls(string:strip(Path, left, $/), CRLs);
+ ssl_manager:insert_crls(string:trim(Path, leading, "/"), CRLs);
_ ->
{error, {only_http_distribution_points_supported, URI}}
end.
@@ -162,7 +162,7 @@ cache_lookup(_, undefined) ->
[];
cache_lookup(URL, {{Cache, _}, _}) ->
{ok, {_, _, _ , _, Path,_}} = http_uri:parse(URL),
- case ssl_pkix_db:lookup(string:strip(Path, left, $/), Cache) of
+ case ssl_pkix_db:lookup(string:trim(Path, leading, "/"), Cache) of
undefined ->
[];
CRLs ->
diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl
index 690b896919..e92f3d3979 100644
--- a/lib/ssl/src/ssl_dist_sup.erl
+++ b/lib/ssl/src/ssl_dist_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -30,6 +30,9 @@
%% Supervisor callback
-export([init/1]).
+%% Debug
+-export([consult/1]).
+
%%%=========================================================================
%%% API
%%%=========================================================================
@@ -37,7 +40,18 @@
-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
start_link() ->
- supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+ case init:get_argument(ssl_dist_optfile) of
+ {ok, [File]} ->
+ DistOpts = consult(File),
+ TabOpts = [set, protected, named_table],
+ Tab = ets:new(ssl_dist_opts, TabOpts),
+ true = ets:insert(Tab, DistOpts),
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []);
+ {ok, BadArg} ->
+ error({bad_ssl_dist_optfile, BadArg});
+ error ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, [])
+ end.
%%%=========================================================================
%%% Supervisor callback
@@ -78,3 +92,52 @@ proxy_server_child_spec() ->
Modules = [ssl_tls_dist_proxy],
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+consult(File) ->
+ case erl_prim_loader:get_file(File) of
+ {ok, Binary, _FullName} ->
+ Encoding =
+ case epp:read_encoding_from_binary(Binary) of
+ none -> latin1;
+ Enc -> Enc
+ end,
+ case unicode:characters_to_list(Binary, Encoding) of
+ {error, _String, Rest} ->
+ error(
+ {bad_ssl_dist_optfile, {encoding_error, Rest}});
+ {incomplete, _String, Rest} ->
+ error(
+ {bad_ssl_dist_optfile, {encoding_incomplete, Rest}});
+ String when is_list(String) ->
+ consult_string(String)
+ end;
+ error ->
+ error({bad_ssl_dist_optfile, File})
+ end.
+
+consult_string(String) ->
+ case erl_scan:string(String) of
+ {error, Info, Location} ->
+ error({bad_ssl_dist_optfile, {scan_error, Info, Location}});
+ {ok, Tokens, _EndLocation} ->
+ consult_tokens(Tokens)
+ end.
+
+consult_tokens(Tokens) ->
+ case erl_parse:parse_exprs(Tokens) of
+ {error, Info} ->
+ error({bad_ssl_dist_optfile, {parse_error, Info}});
+ {ok, [Expr]} ->
+ consult_expr(Expr);
+ {ok, Other} ->
+ error({bad_ssl_dist_optfile, {parse_error, Other}})
+ end.
+
+consult_expr(Expr) ->
+ {value, Value, Bs} = erl_eval:expr(Expr, erl_eval:new_bindings()),
+ case erl_eval:bindings(Bs) of
+ [] ->
+ Value;
+ Other ->
+ error({bad_ssl_dist_optfile, {bindings, Other}})
+ end.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 0ee9ee3322..5e687b1bb7 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -44,46 +44,44 @@
#client_key_exchange{} | #finished{} | #certificate_verify{} |
#hello_request{} | #next_protocol{}.
-%% Handshake messages
+%% Create handshake messages
-export([hello_request/0, server_hello/4, server_hello_done/0,
- certificate/4, certificate_request/5, key_exchange/3,
+ certificate/4, client_certificate_verify/6, certificate_request/5, key_exchange/3,
finished/5, next_protocol/1]).
%% Handle handshake messages
--export([certify/7, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
+-export([certify/7, certificate_verify/6, verify_signature/5,
master_secret/4, server_key_exchange_hash/2, verify_connection/6,
- init_handshake_history/0, update_handshake_history/3, verify_server_key/5
+ init_handshake_history/0, update_handshake_history/3, verify_server_key/5,
+ select_version/3
]).
-%% Encode/Decode
+%% Encode
-export([encode_handshake/2, encode_hello_extensions/1,
- encode_client_protocol_negotiation/2, encode_protocols_advertised_on_server/1,
- decode_handshake/3, decode_hello_extensions/1,
+ encode_client_protocol_negotiation/2, encode_protocols_advertised_on_server/1]).
+%% Decode
+-export([decode_handshake/3, decode_hello_extensions/1,
decode_server_key/3, decode_client_key/3,
decode_suites/2
]).
%% Cipher suites handling
--export([available_suites/2, available_signature_algs/2, cipher_suites/2,
- select_session/11, supported_ecc/1, available_signature_algs/4]).
+-export([available_suites/2, available_signature_algs/2, available_signature_algs/4,
+ cipher_suites/3, prf/6, select_session/11, supported_ecc/1,
+ premaster_secret/2, premaster_secret/3, premaster_secret/4]).
%% Extensions handling
-export([client_hello_extensions/5,
handle_client_hello_extensions/9, %% Returns server hello extensions
- handle_server_hello_extensions/9, select_curve/2, select_curve/3
+ handle_server_hello_extensions/9, select_curve/2, select_curve/3,
+ select_hashsign/4, select_hashsign/5,
+ select_hashsign_algs/3
]).
-%% MISC
--export([select_version/3, prf/6, select_hashsign/4, select_hashsign/5,
- select_hashsign_algs/3,
- premaster_secret/2, premaster_secret/3, premaster_secret/4]).
-
%%====================================================================
-%% Internal application API
+%% Create handshake messages
%%====================================================================
-%% ---------- Create handshake messages ----------
-
%%--------------------------------------------------------------------
-spec hello_request() -> #hello_request{}.
%%
@@ -119,31 +117,6 @@ server_hello(SessionId, Version, ConnectionStates, Extensions) ->
server_hello_done() ->
#server_hello_done{}.
-client_hello_extensions(Version, CipherSuites,
- #ssl_options{signature_algs = SupportedHashSigns,
- eccs = SupportedECCs} = SslOpts, ConnectionStates, Renegotiation) ->
- {EcPointFormats, EllipticCurves} =
- case advertises_ec_ciphers(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites)) of
- true ->
- client_ecc_extensions(SupportedECCs);
- false ->
- {undefined, undefined}
- end,
- SRP = srp_user(SslOpts),
-
- #hello_extensions{
- renegotiation_info = renegotiation_info(tls_record, client,
- ConnectionStates, Renegotiation),
- srp = SRP,
- signature_algs = available_signature_algs(SupportedHashSigns, Version),
- ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves,
- alpn = encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation),
- next_protocol_negotiation =
- encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector,
- Renegotiation),
- sni = sni(SslOpts#ssl_options.server_name_indication)}.
-
%%--------------------------------------------------------------------
-spec certificate(der_cert(), db_handle(), certdb_ref(), client | server) -> #certificate{} | #alert{}.
%%
@@ -171,14 +144,6 @@ certificate(OwnCert, CertDbHandle, CertDbRef, server) ->
end.
%%--------------------------------------------------------------------
--spec next_protocol(binary()) -> #next_protocol{}.
-%%
-%% Description: Creates a next protocol message
-%%-------------------------------------------------------------------
-next_protocol(SelectedProtocol) ->
- #next_protocol{selected_protocol = SelectedProtocol}.
-
-%%--------------------------------------------------------------------
-spec client_certificate_verify(undefined | der_cert(), binary(),
ssl_record:ssl_version(), term(), public_key:private_key(),
ssl_handshake_history()) ->
@@ -328,22 +293,51 @@ key_exchange(server, Version, {srp, {PublicKey, _},
finished(Version, Role, PrfAlgo, MasterSecret, {Handshake, _}) -> % use the current handshake
#finished{verify_data =
calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake)}.
+%%--------------------------------------------------------------------
+-spec next_protocol(binary()) -> #next_protocol{}.
+%%
+%% Description: Creates a next protocol message
+%%-------------------------------------------------------------------
+next_protocol(SelectedProtocol) ->
+ #next_protocol{selected_protocol = SelectedProtocol}.
-%% ---------- Handle handshake messages ----------
+%%====================================================================
+%% Handle handshake messages
+%%====================================================================
+%%--------------------------------------------------------------------
+-spec certify(#certificate{}, db_handle(), certdb_ref(), #ssl_options{}, term(),
+ client | server, inet:hostname() | inet:ip_address()) -> {der_cert(), public_key_info()} | #alert{}.
+%%
+%% Description: Handles a certificate handshake message
+%%--------------------------------------------------------------------
+certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
+ Opts, CRLDbHandle, Role, Host) ->
-verify_server_key(#server_key_params{params_bin = EncParams,
- signature = Signature},
- HashSign = {HashAlgo, _},
- ConnectionStates, Version, PubKeyInfo) ->
- #{security_parameters := SecParams} =
- ssl_record:pending_connection_state(ConnectionStates, read),
- #security_parameters{client_random = ClientRandom,
- server_random = ServerRandom} = SecParams,
- Hash = server_key_exchange_hash(HashAlgo,
- <<ClientRandom/binary,
- ServerRandom/binary,
- EncParams/binary>>),
- verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo).
+ ServerName = server_name(Opts#ssl_options.server_name_indication, Host, Role),
+ [PeerCert | _] = ASN1Certs,
+ try
+ {TrustedCert, CertPath} =
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef,
+ Opts#ssl_options.partial_chain),
+ ValidationFunAndState = validation_fun_and_state(Opts#ssl_options.verify_fun, Role,
+ CertDbHandle, CertDbRef, ServerName,
+ Opts#ssl_options.crl_check, CRLDbHandle, CertPath),
+ case public_key:pkix_path_validation(TrustedCert,
+ CertPath,
+ [{max_path_length, Opts#ssl_options.depth},
+ {verify_fun, ValidationFunAndState}]) of
+ {ok, {PublicKeyInfo,_}} ->
+ {PeerCert, PublicKeyInfo};
+ {error, Reason} ->
+ path_validation_alert(Reason)
+ end
+ catch
+ error:{badmatch,{asn1, Asn1Reason}} ->
+ %% ASN-1 decode of certificate somehow failed
+ ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, {failed_to_decode_certificate, Asn1Reason});
+ error:OtherReason ->
+ ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {unexpected_error, OtherReason})
+ end.
%%--------------------------------------------------------------------
-spec certificate_verify(binary(), public_key_info(), ssl_record:ssl_version(), term(),
@@ -386,43 +380,55 @@ verify_signature(_, Hash, {HashAlgo, _SignAlg}, Signature,
{?'id-ecPublicKey', PublicKey, PublicKeyParams}) ->
public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}).
-
%%--------------------------------------------------------------------
--spec certify(#certificate{}, db_handle(), certdb_ref(), #ssl_options{}, term(),
- client | server, inet:hostname() | inet:ip_address()) -> {der_cert(), public_key_info()} | #alert{}.
+-spec master_secret(ssl_record:ssl_version(), #session{} | binary(), ssl_record:connection_states(),
+ client | server) -> {binary(), ssl_record:connection_states()} | #alert{}.
%%
-%% Description: Handles a certificate handshake message
-%%--------------------------------------------------------------------
-certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
- Opts, CRLDbHandle, Role, Host) ->
+%% Description: Sets or calculates the master secret and calculate keys,
+%% updating the pending connection states. The Mastersecret and the update
+%% connection states are returned or an alert if the calculation fails.
+%%-------------------------------------------------------------------
+master_secret(Version, #session{master_secret = Mastersecret},
+ ConnectionStates, Role) ->
+ #{security_parameters := SecParams} =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ try master_secret(Version, Mastersecret, SecParams,
+ ConnectionStates, Role)
+ catch
+ exit:_ ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, key_calculation_failure)
+ end;
- ServerName = server_name(Opts#ssl_options.server_name_indication, Host, Role),
- [PeerCert | _] = ASN1Certs,
- try
- {TrustedCert, CertPath} =
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef,
- Opts#ssl_options.partial_chain),
- ValidationFunAndState = validation_fun_and_state(Opts#ssl_options.verify_fun, Role,
- CertDbHandle, CertDbRef, ServerName,
- Opts#ssl_options.crl_check, CRLDbHandle, CertPath),
- case public_key:pkix_path_validation(TrustedCert,
- CertPath,
- [{max_path_length, Opts#ssl_options.depth},
- {verify_fun, ValidationFunAndState}]) of
- {ok, {PublicKeyInfo,_}} ->
- {PeerCert, PublicKeyInfo};
- {error, Reason} ->
- path_validation_alert(Reason)
- end
+master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
+ #{security_parameters := SecParams} =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+
+ #security_parameters{prf_algorithm = PrfAlgo,
+ client_random = ClientRandom,
+ server_random = ServerRandom} = SecParams,
+ try master_secret(Version,
+ calc_master_secret(Version,PrfAlgo,PremasterSecret,
+ ClientRandom, ServerRandom),
+ SecParams, ConnectionStates, Role)
catch
- error:{badmatch,{asn1, Asn1Reason}} ->
- %% ASN-1 decode of certificate somehow failed
- ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, {failed_to_decode_certificate, Asn1Reason});
- error:OtherReason ->
- ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {unexpected_error, OtherReason})
+ exit:_ ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, master_secret_calculation_failure)
end.
%%--------------------------------------------------------------------
+-spec server_key_exchange_hash(md5sha | md5 | sha | sha224 |sha256 | sha384 | sha512, binary()) -> binary().
+%%
+%% Description: Calculate server key exchange hash
+%%--------------------------------------------------------------------
+server_key_exchange_hash(md5sha, Value) ->
+ MD5 = crypto:hash(md5, Value),
+ SHA = crypto:hash(sha, Value),
+ <<MD5/binary, SHA/binary>>;
+
+server_key_exchange_hash(Hash, Value) ->
+ crypto:hash(Hash, Value).
+
+%%--------------------------------------------------------------------
-spec verify_connection(ssl_record:ssl_version(), #finished{}, client | server, integer(), binary(),
ssl_handshake_history()) -> verified | #alert{}.
%%
@@ -469,275 +475,31 @@ update_handshake_history(Handshake, % special-case SSL2 client hello
update_handshake_history({Handshake0, _Prev}, Data, _) ->
{[Data|Handshake0], Handshake0}.
-%% %%--------------------------------------------------------------------
-%% -spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary().
-
-%% %%
-%% %% Description: Public key decryption using the private key.
-%% %%--------------------------------------------------------------------
-%% decrypt_premaster_secret(Secret, RSAPrivateKey) ->
-%% try public_key:decrypt_private(Secret, RSAPrivateKey,
-%% [{rsa_pad, rsa_pkcs1_padding}])
-%% catch
-%% _:_ ->
-%% throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR))
-%% end.
-
-premaster_secret(OtherPublicDhKey, MyPrivateKey, #'DHParameter'{} = Params) ->
- try
- public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params)
- catch
- error:computation_failed ->
- throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
- end;
-premaster_secret(PublicDhKey, PrivateDhKey, #server_dh_params{dh_p = Prime, dh_g = Base}) ->
- try
- crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base])
- catch
- error:computation_failed ->
- throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
- end;
-premaster_secret(#client_srp_public{srp_a = ClientPublicKey}, ServerKey, #srp_user{prime = Prime,
- verifier = Verifier}) ->
- case crypto:compute_key(srp, ClientPublicKey, ServerKey, {host, [Verifier, Prime, '6a']}) of
- error ->
- throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
- PremasterSecret ->
- PremasterSecret
- end;
-premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Salt, srp_b = Public},
- ClientKeys, {Username, Password}) ->
- case ssl_srp_primes:check_srp_params(Generator, Prime) of
- ok ->
- DerivedKey = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, Password])]),
- case crypto:compute_key(srp, Public, ClientKeys, {user, [DerivedKey, Prime, Generator, '6a']}) of
- error ->
- throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
- PremasterSecret ->
- PremasterSecret
- end;
- _ ->
- throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
- end;
-premaster_secret(#client_rsa_psk_identity{
- identity = PSKIdentity,
- exchange_keys = #encrypted_premaster_secret{premaster_secret = EncPMS}
- }, #'RSAPrivateKey'{} = Key, PSKLookup) ->
- PremasterSecret = premaster_secret(EncPMS, Key),
- psk_secret(PSKIdentity, PSKLookup, PremasterSecret);
-premaster_secret(#server_dhe_psk_params{
- hint = IdentityHint,
- dh_params = #server_dh_params{dh_y = PublicDhKey} = Params},
- PrivateDhKey,
- LookupFun) ->
- PremasterSecret = premaster_secret(PublicDhKey, PrivateDhKey, Params),
- psk_secret(IdentityHint, LookupFun, PremasterSecret);
-premaster_secret({rsa_psk, PSKIdentity}, PSKLookup, RSAPremasterSecret) ->
- psk_secret(PSKIdentity, PSKLookup, RSAPremasterSecret).
-
-premaster_secret(#client_dhe_psk_identity{
- identity = PSKIdentity,
- dh_public = PublicDhKey}, PrivateKey, #'DHParameter'{} = Params, PSKLookup) ->
- PremasterSecret = premaster_secret(PublicDhKey, PrivateKey, Params),
- psk_secret(PSKIdentity, PSKLookup, PremasterSecret).
-premaster_secret(#client_psk_identity{identity = PSKIdentity}, PSKLookup) ->
- psk_secret(PSKIdentity, PSKLookup);
-premaster_secret({psk, PSKIdentity}, PSKLookup) ->
- psk_secret(PSKIdentity, PSKLookup);
-premaster_secret(#'ECPoint'{} = ECPoint, #'ECPrivateKey'{} = ECDHKeys) ->
- public_key:compute_key(ECPoint, ECDHKeys);
-premaster_secret(EncSecret, #'RSAPrivateKey'{} = RSAPrivateKey) ->
- try public_key:decrypt_private(EncSecret, RSAPrivateKey,
- [{rsa_pad, rsa_pkcs1_padding}])
- catch
- _:_ ->
- throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR))
- end.
-%%--------------------------------------------------------------------
--spec server_key_exchange_hash(md5sha | md5 | sha | sha224 |sha256 | sha384 | sha512, binary()) -> binary().
-%%
-%% Description: Calculate server key exchange hash
-%%--------------------------------------------------------------------
-server_key_exchange_hash(md5sha, Value) ->
- MD5 = crypto:hash(md5, Value),
- SHA = crypto:hash(sha, Value),
- <<MD5/binary, SHA/binary>>;
-
-server_key_exchange_hash(Hash, Value) ->
- crypto:hash(Hash, Value).
-%%--------------------------------------------------------------------
--spec prf(ssl_record:ssl_version(), non_neg_integer(), binary(), binary(), [binary()], non_neg_integer()) ->
- {ok, binary()} | {error, undefined}.
-%%
-%% Description: use the TLS PRF to generate key material
-%%--------------------------------------------------------------------
-prf({3,0}, _, _, _, _, _) ->
- {error, undefined};
-prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
- {ok, tls_v1:prf(PRFAlgo, Secret, Label, Seed, WantedLength)}.
-
-
-%%--------------------------------------------------------------------
--spec select_hashsign(#hash_sign_algos{} | undefined, undefined | binary(),
- atom(), [atom()], ssl_record:ssl_version()) ->
- {atom(), atom()} | undefined | #alert{}.
-
-%%
-%% Description: Handles signature_algorithms hello extension (server)
-%%--------------------------------------------------------------------
-select_hashsign(_, undefined, _, _, _Version) ->
- {null, anon};
-%% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have
-%% negotiated a lower version.
-select_hashsign(HashSigns, Cert, KeyExAlgo,
- undefined, {Major, Minor} = Version) when Major >= 3 andalso Minor >= 3->
- select_hashsign(HashSigns, Cert, KeyExAlgo, tls_v1:default_signature_algs(Version), Version);
-select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns,
- {Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
- #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPCertificate'{tbsCertificate = TBSCert,
- signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
- TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
-
- Sign = sign_algo(SignAlgo),
- SubSing = sign_algo(SubjAlgo),
-
- case lists:filter(fun({_, S} = Algos) when S == Sign ->
- is_acceptable_hash_sign(Algos, Sign,
- SubSing, KeyExAlgo, SupportedHashSigns);
- (_) ->
- false
- end, HashSigns) of
- [] ->
- ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm);
- [HashSign | _] ->
- HashSign
- end;
-select_hashsign(_, Cert, _, _, Version) ->
- #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- select_hashsign_algs(undefined, Algo, Version).
-%%--------------------------------------------------------------------
--spec select_hashsign(#certificate_request{}, binary(),
- [atom()], ssl_record:ssl_version()) ->
- {atom(), atom()} | #alert{}.
-
-%%
-%% Description: Handles signature algorithms selection for certificate requests (client)
-%%--------------------------------------------------------------------
-select_hashsign(#certificate_request{}, undefined, _, {Major, Minor}) when Major >= 3 andalso Minor >= 3->
- %% There client does not have a certificate and will send an empty reply, the server may fail
- %% or accept the connection by its own preference. No signature algorihms needed as there is
- %% no certificate to verify.
- {undefined, undefined};
-
-select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSigns},
- certificate_types = Types}, Cert, SupportedHashSigns,
- {Major, Minor}) when Major >= 3 andalso Minor >= 3->
- #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPCertificate'{tbsCertificate = TBSCert,
- signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
- TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
-
- Sign = sign_algo(SignAlgo),
- SubSign = sign_algo(SubjAlgo),
-
- case is_acceptable_cert_type(SubSign, HashSigns, Types) andalso is_supported_sign(Sign, HashSigns) of
- true ->
- case lists:filter(fun({_, S} = Algos) when S == SubSign ->
- is_acceptable_hash_sign(Algos, SupportedHashSigns);
- (_) ->
- false
- end, HashSigns) of
- [] ->
- ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm);
- [HashSign | _] ->
- HashSign
- end;
- false ->
- ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)
- end;
-select_hashsign(#certificate_request{}, Cert, _, Version) ->
- select_hashsign(undefined, Cert, undefined, [], Version).
-
-%%--------------------------------------------------------------------
--spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) ->
- {atom(), atom()}.
-
-%% Description: For TLS 1.2 hash function and signature algorithm pairs can be
-%% negotiated with the signature_algorithms extension,
-%% for previous versions always use appropriate defaults.
-%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
-%% If the client does not send the signature_algorithms extension, the
-%% server MUST do the following: (e.i defaults for TLS 1.2)
-%%
-%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
-%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
-%% sent the value {sha1,rsa}.
-%%
-%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
-%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
-%%
-%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
-%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
-
-%%--------------------------------------------------------------------
-select_hashsign_algs(HashSign, _, {Major, Minor}) when HashSign =/= undefined andalso
- Major >= 3 andalso Minor >= 3 ->
- HashSign;
-select_hashsign_algs(undefined, ?rsaEncryption, {Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
- {sha, rsa};
-select_hashsign_algs(undefined,?'id-ecPublicKey', _) ->
- {sha, ecdsa};
-select_hashsign_algs(undefined, ?rsaEncryption, _) ->
- {md5sha, rsa};
-select_hashsign_algs(undefined, ?'id-dsa', _) ->
- {sha, dsa}.
-
-
-%%--------------------------------------------------------------------
--spec master_secret(ssl_record:ssl_version(), #session{} | binary(), ssl_record:connection_states(),
- client | server) -> {binary(), ssl_record:connection_states()} | #alert{}.
-%%
-%% Description: Sets or calculates the master secret and calculate keys,
-%% updating the pending connection states. The Mastersecret and the update
-%% connection states are returned or an alert if the calculation fails.
-%%-------------------------------------------------------------------
-master_secret(Version, #session{master_secret = Mastersecret},
- ConnectionStates, Role) ->
- #{security_parameters := SecParams} =
- ssl_record:pending_connection_state(ConnectionStates, read),
- try master_secret(Version, Mastersecret, SecParams,
- ConnectionStates, Role)
- catch
- exit:_ ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, key_calculation_failure)
- end;
-
-master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
+verify_server_key(#server_key_params{params_bin = EncParams,
+ signature = Signature},
+ HashSign = {HashAlgo, _},
+ ConnectionStates, Version, PubKeyInfo) ->
#{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates, read),
-
- #security_parameters{prf_algorithm = PrfAlgo,
- client_random = ClientRandom,
+ #security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- try master_secret(Version,
- calc_master_secret(Version,PrfAlgo,PremasterSecret,
- ClientRandom, ServerRandom),
- SecParams, ConnectionStates, Role)
- catch
- exit:_ ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, master_secret_calculation_failure)
- end.
+ Hash = server_key_exchange_hash(HashAlgo,
+ <<ClientRandom/binary,
+ ServerRandom/binary,
+ EncParams/binary>>),
+ verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo).
+
+select_version(RecordCB, ClientVersion, Versions) ->
+ do_select_version(RecordCB, ClientVersion, Versions).
+
+%%====================================================================
+%% Encode handshake
+%%====================================================================
-%%-------------Encode/Decode --------------------------------
encode_handshake(#next_protocol{selected_protocol = SelectedProtocol}, _Version) ->
PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32),
{?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary,
?BYTE(PaddingLength), 0:(PaddingLength * 8)>>};
-
encode_handshake(#server_hello{server_version = {Major, Minor},
random = Random,
session_id = Session_ID,
@@ -859,70 +621,6 @@ encode_hello_extensions([#sni{hostname = Hostname} | Rest], Acc) ->
?UINT16(HostLen), HostnameBin/binary,
Acc/binary>>).
-enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
- ClientRandom, ServerRandom, PrivateKey) ->
- EncParams = encode_server_key(Params),
- case HashAlgo of
- null ->
- #server_key_params{params = Params,
- params_bin = EncParams,
- hashsign = {null, anon},
- signature = <<>>};
- _ ->
- Hash =
- server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
- ServerRandom/binary,
- EncParams/binary>>),
- Signature = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
- #server_key_params{params = Params,
- params_bin = EncParams,
- hashsign = {HashAlgo, SignAlgo},
- signature = Signature}
- end.
-
-%%--------------------------------------------------------------------
--spec decode_client_key(binary(), ssl_cipher:key_algo(), ssl_record:ssl_version()) ->
- #encrypted_premaster_secret{}
- | #client_diffie_hellman_public{}
- | #client_ec_diffie_hellman_public{}
- | #client_psk_identity{}
- | #client_dhe_psk_identity{}
- | #client_rsa_psk_identity{}
- | #client_srp_public{}.
-%%
-%% Description: Decode client_key data and return appropriate type
-%%--------------------------------------------------------------------
-decode_client_key(ClientKey, Type, Version) ->
- dec_client_key(ClientKey, key_exchange_alg(Type), Version).
-
-%%--------------------------------------------------------------------
--spec decode_server_key(binary(), ssl_cipher:key_algo(), ssl_record:ssl_version()) ->
- #server_key_params{}.
-%%
-%% Description: Decode server_key data and return appropriate type
-%%--------------------------------------------------------------------
-decode_server_key(ServerKey, Type, Version) ->
- dec_server_key(ServerKey, key_exchange_alg(Type), Version).
-
-%%
-%% Description: Encode and decode functions for ALPN extension data.
-%%--------------------------------------------------------------------
-
-%% While the RFC opens the door to allow ALPN during renegotiation, in practice
-%% this does not work and it is recommended to ignore any ALPN extension during
-%% renegotiation, as done here.
-encode_alpn(_, true) ->
- undefined;
-encode_alpn(undefined, _) ->
- undefined;
-encode_alpn(Protocols, _) ->
- #alpn{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}.
-
-decode_alpn(undefined) ->
- undefined;
-decode_alpn(#alpn{extension_data=Data}) ->
- decode_protocols(Data, []).
-
encode_client_protocol_negotiation(undefined, _) ->
undefined;
encode_client_protocol_negotiation(_, false) ->
@@ -936,6 +634,10 @@ encode_protocols_advertised_on_server(undefined) ->
encode_protocols_advertised_on_server(Protocols) ->
#next_protocol_negotiation{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}.
+%%====================================================================
+%% Decode handshake
+%%====================================================================
+
decode_handshake(_, ?HELLO_REQUEST, <<>>) ->
#hello_request{};
decode_handshake(_, ?NEXT_PROTOCOL, <<?BYTE(SelectedProtocolLength),
@@ -968,7 +670,6 @@ decode_handshake(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:3
cipher_suite = Cipher_suite,
compression_method = Comp_method,
extensions = HelloExtensions};
-
decode_handshake(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
decode_handshake(_Version, ?SERVER_KEY_EXCHANGE, Keys) ->
@@ -1015,66 +716,29 @@ decode_hello_extensions({client, <<?UINT16(ExtLen), Extensions:ExtLen/binary>>})
decode_hello_extensions(Extensions) ->
dec_hello_extensions(Extensions, #hello_extensions{}).
-dec_server_key(<<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
- ?KEY_EXCHANGE_DIFFIE_HELLMAN, Version) ->
- Params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
- {BinMsg, HashSign, Signature} = dec_server_key_params(PLen + GLen + YLen + 6, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-%% ECParameters with named_curve
-%% TODO: explicit curve
-dec_server_key(<<?BYTE(?NAMED_CURVE), ?UINT16(CurveID),
- ?BYTE(PointLen), ECPoint:PointLen/binary,
- _/binary>> = KeyStruct,
- ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, Version) ->
- Params = #server_ecdh_params{curve = {namedCurve, tls_v1:enum_to_oid(CurveID)},
- public = ECPoint},
- {BinMsg, HashSign, Signature} = dec_server_key_params(PointLen + 4, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-dec_server_key(<<?UINT16(Len), PskIdentityHint:Len/binary, _/binary>> = KeyStruct,
- KeyExchange, Version)
- when KeyExchange == ?KEY_EXCHANGE_PSK; KeyExchange == ?KEY_EXCHANGE_RSA_PSK ->
- Params = #server_psk_params{
- hint = PskIdentityHint},
- {BinMsg, HashSign, Signature} = dec_server_key_params(Len + 2, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary,
- ?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
- ?KEY_EXCHANGE_DHE_PSK, Version) ->
- DHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
- Params = #server_dhe_psk_params{
- hint = IdentityHint,
- dh_params = DHParams},
- {BinMsg, HashSign, Signature} = dec_server_key_params(Len + PLen + GLen + YLen + 8, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-dec_server_key(<<?UINT16(NLen), N:NLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?BYTE(SLen), S:SLen/binary,
- ?UINT16(BLen), B:BLen/binary, _/binary>> = KeyStruct,
- ?KEY_EXCHANGE_SRP, Version) ->
- Params = #server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B},
- {BinMsg, HashSign, Signature} = dec_server_key_params(NLen + GLen + SLen + BLen + 7, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-dec_server_key(_, KeyExchange, _) ->
- throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {unknown_or_malformed_key_exchange, KeyExchange})).
+%%--------------------------------------------------------------------
+-spec decode_server_key(binary(), ssl_cipher:key_algo(), ssl_record:ssl_version()) ->
+ #server_key_params{}.
+%%
+%% Description: Decode server_key data and return appropriate type
+%%--------------------------------------------------------------------
+decode_server_key(ServerKey, Type, Version) ->
+ dec_server_key(ServerKey, key_exchange_alg(Type), Version).
+
+%%--------------------------------------------------------------------
+-spec decode_client_key(binary(), ssl_cipher:key_algo(), ssl_record:ssl_version()) ->
+ #encrypted_premaster_secret{}
+ | #client_diffie_hellman_public{}
+ | #client_ec_diffie_hellman_public{}
+ | #client_psk_identity{}
+ | #client_dhe_psk_identity{}
+ | #client_rsa_psk_identity{}
+ | #client_srp_public{}.
+%%
+%% Description: Decode client_key data and return appropriate type
+%%--------------------------------------------------------------------
+decode_client_key(ClientKey, Type, Version) ->
+ dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
-spec decode_suites('2_bytes'|'3_bytes', binary()) -> list().
@@ -1086,7 +750,9 @@ decode_suites('2_bytes', Dec) ->
decode_suites('3_bytes', Dec) ->
from_3bytes(Dec).
-%%-------------Cipeher suite handling --------------------------------
+%%====================================================================
+%% Cipher suite handling
+%%====================================================================
available_suites(UserSuites, Version) ->
lists:filtermap(fun(Suite) ->
@@ -1099,60 +765,42 @@ available_suites(ServerCert, UserSuites, Version, undefined, Curve) ->
available_suites(ServerCert, UserSuites, Version, HashSigns, Curve) ->
Suites = available_suites(ServerCert, UserSuites, Version, undefined, Curve),
filter_hashsigns(Suites, [ssl_cipher:suite_definition(Suite) || Suite <- Suites], HashSigns, []).
-filter_hashsigns([], [], _, Acc) ->
- lists:reverse(Acc);
-filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns,
- Acc) when KeyExchange == dhe_ecdsa;
- KeyExchange == ecdhe_ecdsa ->
- do_filter_hashsigns(ecdsa, Suite, Suites, Algos, HashSigns, Acc);
-filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns,
- Acc) when KeyExchange == rsa;
- KeyExchange == dhe_rsa;
- KeyExchange == ecdhe_rsa;
- KeyExchange == srp_rsa;
- KeyExchange == rsa_psk ->
- do_filter_hashsigns(rsa, Suite, Suites, Algos, HashSigns, Acc);
-filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc) when
- KeyExchange == dhe_dss;
- KeyExchange == srp_dss ->
- do_filter_hashsigns(dsa, Suite, Suites, Algos, HashSigns, Acc);
-filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc) when
- KeyExchange == dh_dss;
- KeyExchange == dh_rsa;
- KeyExchange == dh_ecdsa;
- KeyExchange == ecdh_rsa;
- KeyExchange == ecdh_ecdsa ->
- %% Fixed DH certificates MAY be signed with any hash/signature
- %% algorithm pair appearing in the hash_sign extension. The names
- %% DH_DSS, DH_RSA, ECDH_ECDSA, and ECDH_RSA are historical.
- filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]);
-filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc) when
- KeyExchange == dh_anon;
- KeyExchange == ecdh_anon;
- KeyExchange == srp_anon;
- KeyExchange == psk;
- KeyExchange == dhe_psk ->
- %% In this case hashsigns is not used as the kexchange is anonaymous
- filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]).
-
-do_filter_hashsigns(SignAlgo, Suite, Suites, Algos, HashSigns, Acc) ->
- case lists:keymember(SignAlgo, 2, HashSigns) of
- true ->
- filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]);
- false ->
- filter_hashsigns(Suites, Algos, HashSigns, Acc)
- end.
-
-unavailable_ecc_suites(no_curve) ->
- ssl_cipher:ec_keyed_suites();
-unavailable_ecc_suites(_) ->
- [].
+available_signature_algs(undefined, _) ->
+ undefined;
+available_signature_algs(SupportedHashSigns, Version) when Version >= {3, 3} ->
+ #hash_sign_algos{hash_sign_algos = SupportedHashSigns};
+available_signature_algs(_, _) ->
+ undefined.
+available_signature_algs(undefined, SupportedHashSigns, _, Version) when
+ Version >= {3,3} ->
+ SupportedHashSigns;
+available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns,
+ _, Version) when Version >= {3,3} ->
+ sets:to_list(sets:intersection(sets:from_list(ClientHashSigns),
+ sets:from_list(SupportedHashSigns)));
+available_signature_algs(_, _, _, _) ->
+ undefined.
+cipher_suites(Suites, Renegotiation, true) ->
+ %% TLS_FALLBACK_SCSV should be placed last -RFC7507
+ cipher_suites(Suites, Renegotiation) ++ [?TLS_FALLBACK_SCSV];
+cipher_suites(Suites, Renegotiation, false) ->
+ cipher_suites(Suites, Renegotiation).
cipher_suites(Suites, false) ->
[?TLS_EMPTY_RENEGOTIATION_INFO_SCSV | Suites];
cipher_suites(Suites, true) ->
Suites.
+%%--------------------------------------------------------------------
+-spec prf(ssl_record:ssl_version(), non_neg_integer(), binary(), binary(), [binary()], non_neg_integer()) ->
+ {ok, binary()} | {error, undefined}.
+%%
+%% Description: use the TLS PRF to generate key material
+%%--------------------------------------------------------------------
+prf({3,0}, _, _, _, _, _) ->
+ {error, undefined};
+prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
+ {ok, tls_v1:prf(PRFAlgo, Secret, Label, Seed, WantedLength)}.
select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, Port, #session{ecc = ECCCurve} =
Session, Version,
@@ -1173,68 +821,109 @@ select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, Port,
{resumed, Resumed}
end.
-%% Deprecated?
supported_ecc({Major, Minor}) when ((Major == 3) and (Minor >= 1)) orelse (Major > 3) ->
Curves = tls_v1:ecc_curves(Minor),
#elliptic_curves{elliptic_curve_list = Curves};
supported_ecc(_) ->
#elliptic_curves{elliptic_curve_list = []}.
-%%-------------certificate handling --------------------------------
-
-certificate_types(_, {N, M}) when N >= 3 andalso M >= 3 ->
- case proplists:get_bool(ecdsa,
- proplists:get_value(public_keys, crypto:supports())) of
- true ->
- <<?BYTE(?ECDSA_SIGN), ?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>;
- false ->
- <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>
+premaster_secret(OtherPublicDhKey, MyPrivateKey, #'DHParameter'{} = Params) ->
+ try
+ public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params)
+ catch
+ error:computation_failed ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end;
+premaster_secret(PublicDhKey, PrivateDhKey, #server_dh_params{dh_p = Prime, dh_g = Base}) ->
+ try
+ crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base])
+ catch
+ error:computation_failed ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end;
+premaster_secret(#client_srp_public{srp_a = ClientPublicKey}, ServerKey, #srp_user{prime = Prime,
+ verifier = Verifier}) ->
+ case crypto:compute_key(srp, ClientPublicKey, ServerKey, {host, [Verifier, Prime, '6a']}) of
+ error ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
+ PremasterSecret ->
+ PremasterSecret
+ end;
+premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Salt, srp_b = Public},
+ ClientKeys, {Username, Password}) ->
+ case ssl_srp_primes:check_srp_params(Generator, Prime) of
+ ok ->
+ DerivedKey = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, Password])]),
+ case crypto:compute_key(srp, Public, ClientKeys, {user, [DerivedKey, Prime, Generator, '6a']}) of
+ error ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
+ PremasterSecret ->
+ PremasterSecret
+ end;
+ _ ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end;
+premaster_secret(#client_rsa_psk_identity{
+ identity = PSKIdentity,
+ exchange_keys = #encrypted_premaster_secret{premaster_secret = EncPMS}
+ }, #'RSAPrivateKey'{} = Key, PSKLookup) ->
+ PremasterSecret = premaster_secret(EncPMS, Key),
+ psk_secret(PSKIdentity, PSKLookup, PremasterSecret);
+premaster_secret(#server_dhe_psk_params{
+ hint = IdentityHint,
+ dh_params = #server_dh_params{dh_y = PublicDhKey} = Params},
+ PrivateDhKey,
+ LookupFun) ->
+ PremasterSecret = premaster_secret(PublicDhKey, PrivateDhKey, Params),
+ psk_secret(IdentityHint, LookupFun, PremasterSecret);
+premaster_secret({rsa_psk, PSKIdentity}, PSKLookup, RSAPremasterSecret) ->
+ psk_secret(PSKIdentity, PSKLookup, RSAPremasterSecret).
-certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == rsa;
- KeyExchange == dh_rsa;
- KeyExchange == dhe_rsa;
- KeyExchange == ecdhe_rsa ->
- <<?BYTE(?RSA_SIGN)>>;
-
-certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dh_dss;
- KeyExchange == dhe_dss;
- KeyExchange == srp_dss ->
- <<?BYTE(?DSS_SIGN)>>;
-
-certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dh_ecdsa;
- KeyExchange == dhe_ecdsa;
- KeyExchange == ecdh_ecdsa;
- KeyExchange == ecdhe_ecdsa ->
- <<?BYTE(?ECDSA_SIGN)>>;
-
-certificate_types(_, _) ->
- <<?BYTE(?RSA_SIGN)>>.
-
-certificate_authorities(CertDbHandle, CertDbRef) ->
- Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef),
- Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
- OTPSubj = TBSCert#'OTPTBSCertificate'.subject,
- DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp),
- DNEncodedLen = byte_size(DNEncodedBin),
- <<?UINT16(DNEncodedLen), DNEncodedBin/binary>>
- end,
- list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]).
-
-certificate_authorities_from_db(CertDbHandle, CertDbRef) when is_reference(CertDbRef) ->
- ConnectionCerts = fun({{Ref, _, _}, Cert}, Acc) when Ref == CertDbRef ->
- [Cert | Acc];
- (_, Acc) ->
- Acc
- end,
- ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle);
-certificate_authorities_from_db(_CertDbHandle, {extracted, CertDbData}) ->
- %% Cache disabled, Ref contains data
- lists:foldl(fun({decoded, {_Key,Cert}}, Acc) -> [Cert | Acc] end,
- [], CertDbData).
-
+premaster_secret(#client_dhe_psk_identity{
+ identity = PSKIdentity,
+ dh_public = PublicDhKey}, PrivateKey, #'DHParameter'{} = Params, PSKLookup) ->
+ PremasterSecret = premaster_secret(PublicDhKey, PrivateKey, Params),
+ psk_secret(PSKIdentity, PSKLookup, PremasterSecret).
+premaster_secret(#client_psk_identity{identity = PSKIdentity}, PSKLookup) ->
+ psk_secret(PSKIdentity, PSKLookup);
+premaster_secret({psk, PSKIdentity}, PSKLookup) ->
+ psk_secret(PSKIdentity, PSKLookup);
+premaster_secret(#'ECPoint'{} = ECPoint, #'ECPrivateKey'{} = ECDHKeys) ->
+ public_key:compute_key(ECPoint, ECDHKeys);
+premaster_secret(EncSecret, #'RSAPrivateKey'{} = RSAPrivateKey) ->
+ try public_key:decrypt_private(EncSecret, RSAPrivateKey,
+ [{rsa_pad, rsa_pkcs1_padding}])
+ catch
+ _:_ ->
+ throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR))
+ end.
+%%====================================================================
+%% Extensions handling
+%%====================================================================
+client_hello_extensions(Version, CipherSuites,
+ #ssl_options{signature_algs = SupportedHashSigns,
+ eccs = SupportedECCs} = SslOpts, ConnectionStates, Renegotiation) ->
+ {EcPointFormats, EllipticCurves} =
+ case advertises_ec_ciphers(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites)) of
+ true ->
+ client_ecc_extensions(SupportedECCs);
+ false ->
+ {undefined, undefined}
+ end,
+ SRP = srp_user(SslOpts),
-%%-------------Extension handling --------------------------------
+ #hello_extensions{
+ renegotiation_info = renegotiation_info(tls_record, client,
+ ConnectionStates, Renegotiation),
+ srp = SRP,
+ signature_algs = available_signature_algs(SupportedHashSigns, Version),
+ ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves,
+ alpn = encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation),
+ next_protocol_negotiation =
+ encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector,
+ Renegotiation),
+ sni = sni(SslOpts#ssl_options.server_name_indication)}.
handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites,
#hello_extensions{renegotiation_info = Info,
@@ -1311,231 +1000,208 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, too_many_protocols_in_server_hello)
end.
-select_version(RecordCB, ClientVersion, Versions) ->
- do_select_version(RecordCB, ClientVersion, Versions).
-
-do_select_version(_, ClientVersion, []) ->
- ClientVersion;
-do_select_version(RecordCB, ClientVersion, [Version | Versions]) ->
- case RecordCB:is_higher(Version, ClientVersion) of
- true ->
- %% Version too high for client - keep looking
- do_select_version(RecordCB, ClientVersion, Versions);
- false ->
- %% Version ok for client - look for a higher
- do_select_version(RecordCB, ClientVersion, Versions, Version)
- end.
-%%
-do_select_version(_, _, [], GoodVersion) ->
- GoodVersion;
-do_select_version(
- RecordCB, ClientVersion, [Version | Versions], GoodVersion) ->
- BetterVersion =
- case RecordCB:is_higher(Version, ClientVersion) of
- true ->
- %% Version too high for client
- GoodVersion;
- false ->
- %% Version ok for client
- case RecordCB:is_higher(Version, GoodVersion) of
- true ->
- %% Use higher version
- Version;
- false ->
- GoodVersion
- end
- end,
- do_select_version(RecordCB, ClientVersion, Versions, BetterVersion).
+select_curve(Client, Server) ->
+ select_curve(Client, Server, false).
-renegotiation_info(_, client, _, false) ->
- #renegotiation_info{renegotiated_connection = undefined};
-renegotiation_info(_RecordCB, server, ConnectionStates, false) ->
- ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
- case maps:get(secure_renegotiation, ConnectionState) of
- true ->
- #renegotiation_info{renegotiated_connection = ?byte(0)};
- false ->
- #renegotiation_info{renegotiated_connection = undefined}
- end;
-renegotiation_info(_RecordCB, client, ConnectionStates, true) ->
- ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
- case maps:get(secure_renegotiation, ConnectionState) of
- true ->
- Data = maps:get(client_verify_data, ConnectionState),
- #renegotiation_info{renegotiated_connection = Data};
- false ->
- #renegotiation_info{renegotiated_connection = undefined}
+select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves},
+ #elliptic_curves{elliptic_curve_list = ServerCurves},
+ ServerOrder) ->
+ case ServerOrder of
+ false ->
+ select_shared_curve(ClientCurves, ServerCurves);
+ true ->
+ select_shared_curve(ServerCurves, ClientCurves)
end;
+select_curve(undefined, _, _) ->
+ %% Client did not send ECC extension use default curve if
+ %% ECC cipher is negotiated
+ {namedCurve, ?secp256r1}.
-renegotiation_info(_RecordCB, server, ConnectionStates, true) ->
- ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
- case maps:get(secure_renegotiation, ConnectionState) of
- true ->
- CData = maps:get(client_verify_data, ConnectionState),
- SData = maps:get(server_verify_data, ConnectionState),
- #renegotiation_info{renegotiated_connection = <<CData/binary, SData/binary>>};
- false ->
- #renegotiation_info{renegotiated_connection = undefined}
- end.
+%%--------------------------------------------------------------------
+-spec select_hashsign(#hash_sign_algos{} | undefined, undefined | binary(),
+ atom(), [atom()], ssl_record:ssl_version()) ->
+ {atom(), atom()} | undefined | #alert{}.
-handle_renegotiation_info(_RecordCB, _, #renegotiation_info{renegotiated_connection = ?byte(0)},
- ConnectionStates, false, _, _) ->
- {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
+%%
+%% Description: Handles signature_algorithms hello extension (server)
+%%--------------------------------------------------------------------
+select_hashsign(_, undefined, _, _, _Version) ->
+ {null, anon};
+%% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have
+%% negotiated a lower version.
+select_hashsign(HashSigns, Cert, KeyExAlgo,
+ undefined, {Major, Minor} = Version) when Major >= 3 andalso Minor >= 3->
+ select_hashsign(HashSigns, Cert, KeyExAlgo, tls_v1:default_signature_algs(Version), Version);
+select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns,
+ {Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
+ #'OTPCertificate'{tbsCertificate = TBSCert,
+ signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
+ TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
-handle_renegotiation_info(_RecordCB, server, undefined, ConnectionStates, _, _, CipherSuites) ->
- case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
- true ->
- {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
- false ->
- {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)}
+ Sign = sign_algo(SignAlgo),
+ SubSing = sign_algo(SubjAlgo),
+
+ case lists:filter(fun({_, S} = Algos) when S == Sign ->
+ is_acceptable_hash_sign(Algos, Sign,
+ SubSing, KeyExAlgo, SupportedHashSigns);
+ (_) ->
+ false
+ end, HashSigns) of
+ [] ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm);
+ [HashSign | _] ->
+ HashSign
end;
+select_hashsign(_, Cert, _, _, Version) ->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+ select_hashsign_algs(undefined, Algo, Version).
+%%--------------------------------------------------------------------
+-spec select_hashsign(#certificate_request{}, binary(),
+ [atom()], ssl_record:ssl_version()) ->
+ {atom(), atom()} | #alert{}.
-handle_renegotiation_info(_RecordCB, _, undefined, ConnectionStates, false, _, _) ->
- {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)};
+%%
+%% Description: Handles signature algorithms selection for certificate requests (client)
+%%--------------------------------------------------------------------
+select_hashsign(#certificate_request{}, undefined, _, {Major, Minor}) when Major >= 3 andalso Minor >= 3->
+ %% There client does not have a certificate and will send an empty reply, the server may fail
+ %% or accept the connection by its own preference. No signature algorihms needed as there is
+ %% no certificate to verify.
+ {undefined, undefined};
+
+select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSigns},
+ certificate_types = Types}, Cert, SupportedHashSigns,
+ {Major, Minor}) when Major >= 3 andalso Minor >= 3->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPCertificate'{tbsCertificate = TBSCert,
+ signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
+ TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
-handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
- ConnectionStates, true, _, _) ->
- ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
- CData = maps:get(client_verify_data, ConnectionState),
- SData = maps:get(server_verify_data, ConnectionState),
- case <<CData/binary, SData/binary>> == ClientServerVerify of
+ Sign = sign_algo(SignAlgo),
+ SubSign = sign_algo(SubjAlgo),
+
+ case is_acceptable_cert_type(SubSign, HashSigns, Types) andalso is_supported_sign(Sign, HashSigns) of
true ->
- {ok, ConnectionStates};
+ case lists:filter(fun({_, S} = Algos) when S == SubSign ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+ (_) ->
+ false
+ end, HashSigns) of
+ [] ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm);
+ [HashSign | _] ->
+ HashSign
+ end;
false ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation)
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)
end;
-handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify},
- ConnectionStates, true, _, CipherSuites) ->
-
- case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
- true ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv});
- false ->
- ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
- Data = maps:get(client_verify_data, ConnectionState),
- case Data == ClientVerify of
- true ->
- {ok, ConnectionStates};
- false ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation)
- end
- end;
+select_hashsign(#certificate_request{}, Cert, _, Version) ->
+ select_hashsign(undefined, Cert, undefined, [], Version).
-handle_renegotiation_info(RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
- handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation);
+%%--------------------------------------------------------------------
+-spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) ->
+ {atom(), atom()}.
-handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) ->
- case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
- true ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv});
- false ->
- handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation)
- end.
+%% Description: For TLS 1.2 hash function and signature algorithm pairs can be
+%% negotiated with the signature_algorithms extension,
+%% for previous versions always use appropriate defaults.
+%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
+%% If the client does not send the signature_algorithms extension, the
+%% server MUST do the following: (e.i defaults for TLS 1.2)
+%%
+%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
+%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
+%% sent the value {sha1,rsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
+%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
+%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
-handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) ->
- ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
- case {SecureRenegotation, maps:get(secure_renegotiation, ConnectionState)} of
- {_, true} ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure);
- {true, false} ->
- ?ALERT_REC(?FATAL, ?NO_RENEGOTIATION);
- {false, false} ->
- {ok, ConnectionStates}
- end.
+%%--------------------------------------------------------------------
+select_hashsign_algs(HashSign, _, {Major, Minor}) when HashSign =/= undefined andalso
+ Major >= 3 andalso Minor >= 3 ->
+ HashSign;
+select_hashsign_algs(undefined, ?rsaEncryption, {Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
+ {sha, rsa};
+select_hashsign_algs(undefined,?'id-ecPublicKey', _) ->
+ {sha, ecdsa};
+select_hashsign_algs(undefined, ?rsaEncryption, _) ->
+ {md5sha, rsa};
+select_hashsign_algs(undefined, ?'id-dsa', _) ->
+ {sha, dsa}.
-hello_extensions_list(#hello_extensions{renegotiation_info = RenegotiationInfo,
- srp = SRP,
- signature_algs = HashSigns,
- ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves,
- alpn = ALPN,
- next_protocol_negotiation = NextProtocolNegotiation,
- sni = Sni}) ->
- [Ext || Ext <- [RenegotiationInfo, SRP, HashSigns,
- EcPointFormats, EllipticCurves, ALPN, NextProtocolNegotiation, Sni], Ext =/= undefined].
srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
#srp{username = UserName};
srp_user(_) ->
undefined.
-client_ecc_extensions(SupportedECCs) ->
- CryptoSupport = proplists:get_value(public_keys, crypto:supports()),
- case proplists:get_bool(ecdh, CryptoSupport) of
- true ->
- EcPointFormats = #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]},
- EllipticCurves = SupportedECCs,
- {EcPointFormats, EllipticCurves};
- _ ->
- {undefined, undefined}
- end.
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+%%------------- Create handshake messages ----------------------------
-server_ecc_extension(_Version, EcPointFormats) ->
- CryptoSupport = proplists:get_value(public_keys, crypto:supports()),
- case proplists:get_bool(ecdh, CryptoSupport) of
+int_to_bin(I) ->
+ L = (length(integer_to_list(I, 16)) + 1) div 2,
+ <<I:(L*8)>>.
+
+certificate_types(_, {N, M}) when N >= 3 andalso M >= 3 ->
+ case proplists:get_bool(ecdsa,
+ proplists:get_value(public_keys, crypto:supports())) of
true ->
- handle_ecc_point_fmt_extension(EcPointFormats);
+ <<?BYTE(?ECDSA_SIGN), ?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>;
false ->
- undefined
- end.
+ <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>
+ end;
-handle_ecc_point_fmt_extension(undefined) ->
- undefined;
-handle_ecc_point_fmt_extension(_) ->
- #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}.
+certificate_types(#{key_exchange := KeyExchange}, _) when KeyExchange == rsa;
+ KeyExchange == dh_rsa;
+ KeyExchange == dhe_rsa;
+ KeyExchange == ecdhe_rsa ->
+ <<?BYTE(?RSA_SIGN)>>;
-advertises_ec_ciphers([]) ->
- false;
-advertises_ec_ciphers([{ecdh_ecdsa, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([{ecdhe_ecdsa, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([{ecdh_rsa, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([{ecdhe_rsa, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([_| Rest]) ->
- advertises_ec_ciphers(Rest).
+certificate_types(#{key_exchange := KeyExchange}, _) when KeyExchange == dh_dss;
+ KeyExchange == dhe_dss;
+ KeyExchange == srp_dss ->
+ <<?BYTE(?DSS_SIGN)>>;
-select_curve(Client, Server) ->
- select_curve(Client, Server, false).
+certificate_types(#{key_exchange := KeyExchange}, _) when KeyExchange == dh_ecdsa;
+ KeyExchange == dhe_ecdsa;
+ KeyExchange == ecdh_ecdsa;
+ KeyExchange == ecdhe_ecdsa ->
+ <<?BYTE(?ECDSA_SIGN)>>;
-select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves},
- #elliptic_curves{elliptic_curve_list = ServerCurves},
- ServerOrder) ->
- case ServerOrder of
- false ->
- select_shared_curve(ClientCurves, ServerCurves);
- true ->
- select_shared_curve(ServerCurves, ClientCurves)
- end;
-select_curve(undefined, _, _) ->
- %% Client did not send ECC extension use default curve if
- %% ECC cipher is negotiated
- {namedCurve, ?secp256r1}.
+certificate_types(_, _) ->
+ <<?BYTE(?RSA_SIGN)>>.
-select_shared_curve([], _) ->
- no_curve;
-select_shared_curve([Curve | Rest], Curves) ->
- case lists:member(Curve, Curves) of
- true ->
- {namedCurve, Curve};
- false ->
- select_shared_curve(Rest, Curves)
- end.
+certificate_authorities(CertDbHandle, CertDbRef) ->
+ Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef),
+ Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
+ OTPSubj = TBSCert#'OTPTBSCertificate'.subject,
+ DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp),
+ DNEncodedLen = byte_size(DNEncodedBin),
+ <<?UINT16(DNEncodedLen), DNEncodedBin/binary>>
+ end,
+ list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]).
-sni(undefined) ->
- undefined;
-sni(disable) ->
- undefined;
-sni(Hostname) ->
- #sni{hostname = Hostname}.
+certificate_authorities_from_db(CertDbHandle, CertDbRef) when is_reference(CertDbRef) ->
+ ConnectionCerts = fun({{Ref, _, _}, Cert}, Acc) when Ref == CertDbRef ->
+ [Cert | Acc];
+ (_, Acc) ->
+ Acc
+ end,
+ ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle);
+certificate_authorities_from_db(_CertDbHandle, {extracted, CertDbData}) ->
+ %% Cache disabled, Ref contains data
+ lists:foldl(fun({decoded, {_Key,Cert}}, Acc) -> [Cert | Acc] end,
+ [], CertDbData).
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
+%%-------------Handle handshake messages --------------------------------
validation_fun_and_state({Fun, UserState0}, Role, CertDbHandle, CertDbRef,
ServerNameIndication, CRLCheck, CRLDbHandle, CertPath) ->
{fun(OtpCert, {extension, _} = Extension, {SslState, UserState}) ->
@@ -1627,17 +1293,6 @@ path_validation_alert({bad_cert, unknown_ca}) ->
path_validation_alert(Reason) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason).
-encrypted_premaster_secret(Secret, RSAPublicKey) ->
- try
- PreMasterSecret = public_key:encrypt_public(Secret, RSAPublicKey,
- [{rsa_pad,
- rsa_pkcs1_padding}]),
- #encrypted_premaster_secret{premaster_secret = PreMasterSecret}
- catch
- _:_->
- throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, premaster_encryption_failed))
- end.
-
digitally_signed(Version, Hashes, HashAlgo, PrivateKey) ->
try do_digitally_signed(Version, Hashes, HashAlgo, PrivateKey) of
Signature ->
@@ -1646,17 +1301,123 @@ digitally_signed(Version, Hashes, HashAlgo, PrivateKey) ->
error:badkey->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, bad_key(PrivateKey)))
end.
-
+do_digitally_signed({3, Minor}, Hash, HashAlgo, #{algorithm := Alg} = Engine)
+ when Minor >= 3 ->
+ crypto:sign(Alg, HashAlgo, {digest, Hash}, maps:remove(algorithm, Engine));
do_digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 ->
public_key:sign({digest, Hash}, HashAlgo, Key);
-do_digitally_signed(_Version, Hash, HashAlgo, #'DSAPrivateKey'{} = Key) ->
- public_key:sign({digest, Hash}, HashAlgo, Key);
do_digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) ->
public_key:encrypt_private(Hash, Key,
[{rsa_pad, rsa_pkcs1_padding}]);
+do_digitally_signed({3, _}, Hash, _,
+ #{algorithm := rsa} = Engine) ->
+ crypto:private_encrypt(rsa, Hash, maps:remove(algorithm, Engine),
+ rsa_pkcs1_padding);
+do_digitally_signed({3, _}, Hash, HashAlgo, #{algorithm := Alg} = Engine) ->
+ crypto:sign(Alg, HashAlgo, {digest, Hash}, maps:remove(algorithm, Engine));
do_digitally_signed(_Version, Hash, HashAlgo, Key) ->
public_key:sign({digest, Hash}, HashAlgo, Key).
+bad_key(#'DSAPrivateKey'{}) ->
+ unacceptable_dsa_key;
+bad_key(#'RSAPrivateKey'{}) ->
+ unacceptable_rsa_key;
+bad_key(#'ECPrivateKey'{}) ->
+ unacceptable_ecdsa_key.
+
+crl_check(_, false, _,_,_, _, _) ->
+ valid;
+crl_check(_, peer, _, _,_, valid, _) -> %% Do not check CAs with this option.
+ valid;
+crl_check(OtpCert, Check, CertDbHandle, CertDbRef, {Callback, CRLDbHandle}, _, CertPath) ->
+ Options = [{issuer_fun, {fun(_DP, CRL, Issuer, DBInfo) ->
+ ssl_crl:trusted_cert_and_path(CRL, Issuer, {CertPath,
+ DBInfo})
+ end, {CertDbHandle, CertDbRef}}},
+ {update_crl, fun(DP, CRL) -> Callback:fresh_crl(DP, CRL) end},
+ {undetermined_details, true}
+ ],
+ case dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) of
+ no_dps ->
+ crl_check_same_issuer(OtpCert, Check,
+ dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer),
+ Options);
+ DpsAndCRLs -> %% This DP list may be empty if relevant CRLs existed
+ %% but could not be retrived, will result in {bad_cert, revocation_status_undetermined}
+ case public_key:pkix_crls_validate(OtpCert, DpsAndCRLs, Options) of
+ {bad_cert, {revocation_status_undetermined, _}} ->
+ crl_check_same_issuer(OtpCert, Check, dps_and_crls(OtpCert, Callback,
+ CRLDbHandle, same_issuer), Options);
+ Other ->
+ Other
+ end
+ end.
+
+crl_check_same_issuer(OtpCert, best_effort, Dps, Options) ->
+ case public_key:pkix_crls_validate(OtpCert, Dps, Options) of
+ {bad_cert, {revocation_status_undetermined, _}} ->
+ valid;
+ Other ->
+ Other
+ end;
+crl_check_same_issuer(OtpCert, _, Dps, Options) ->
+ public_key:pkix_crls_validate(OtpCert, Dps, Options).
+
+dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) ->
+ case public_key:pkix_dist_points(OtpCert) of
+ [] ->
+ no_dps;
+ DistPoints ->
+ Issuer = OtpCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.issuer,
+ CRLs = distpoints_lookup(DistPoints, Issuer, Callback, CRLDbHandle),
+ dps_and_crls(DistPoints, CRLs, [])
+ end;
+
+dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) ->
+ DP = #'DistributionPoint'{distributionPoint = {fullName, GenNames}} =
+ public_key:pkix_dist_point(OtpCert),
+ CRLs = lists:flatmap(fun({directoryName, Issuer}) ->
+ Callback:select(Issuer, CRLDbHandle);
+ (_) ->
+ []
+ end, GenNames),
+ [{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs].
+
+dps_and_crls([], _, Acc) ->
+ Acc;
+dps_and_crls([DP | Rest], CRLs, Acc) ->
+ DpCRL = [{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs],
+ dps_and_crls(Rest, CRLs, DpCRL ++ Acc).
+
+distpoints_lookup([],_, _, _) ->
+ [];
+distpoints_lookup([DistPoint | Rest], Issuer, Callback, CRLDbHandle) ->
+ Result =
+ try Callback:lookup(DistPoint, Issuer, CRLDbHandle)
+ catch
+ error:undef ->
+ %% The callback module still uses the 2-argument
+ %% version of the lookup function.
+ Callback:lookup(DistPoint, CRLDbHandle)
+ end,
+ case Result of
+ not_available ->
+ distpoints_lookup(Rest, Issuer, Callback, CRLDbHandle);
+ CRLs ->
+ CRLs
+ end.
+
+encrypted_premaster_secret(Secret, RSAPublicKey) ->
+ try
+ PreMasterSecret = public_key:encrypt_public(Secret, RSAPublicKey,
+ [{rsa_pad,
+ rsa_pkcs1_padding}]),
+ #encrypted_premaster_secret{premaster_secret = PreMasterSecret}
+ catch
+ _:_->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, premaster_encryption_failed))
+ end.
+
calc_certificate_verify({3, 0}, HashAlgo, MasterSecret, Handshake) ->
ssl_v3:certificate_verify(HashAlgo, MasterSecret, lists:reverse(Handshake));
calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) ->
@@ -1709,24 +1470,7 @@ calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom)
calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
tls_v1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom).
-
-handle_renegotiation_extension(Role, RecordCB, Version, Info, Random, NegotiatedCipherSuite,
- ClientCipherSuites, Compression,
- ConnectionStates0, Renegotiation, SecureRenegotation) ->
- case handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0,
- Renegotiation, SecureRenegotation,
- ClientCipherSuites) of
- {ok, ConnectionStates} ->
- hello_pending_connection_states(RecordCB, Role,
- Version,
- NegotiatedCipherSuite,
- Random,
- Compression,
- ConnectionStates);
- #alert{} = Alert ->
- throw(Alert)
- end.
-
+
%% Update pending connection states with parameters exchanged via
%% hello messages
%% NOTE : Role is the role of the receiver of the hello message
@@ -1766,7 +1510,43 @@ hello_security_parameters(server, Version, #{security_parameters := SecParams},
compression_algorithm = Compression
}.
-%%-------------Encode/Decode --------------------------------
+select_compression(_CompressionMetodes) ->
+ ?NULL.
+
+do_select_version(_, ClientVersion, []) ->
+ ClientVersion;
+do_select_version(RecordCB, ClientVersion, [Version | Versions]) ->
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client - keep looking
+ do_select_version(RecordCB, ClientVersion, Versions);
+ false ->
+ %% Version ok for client - look for a higher
+ do_select_version(RecordCB, ClientVersion, Versions, Version)
+ end.
+%%
+do_select_version(_, _, [], GoodVersion) ->
+ GoodVersion;
+do_select_version(
+ RecordCB, ClientVersion, [Version | Versions], GoodVersion) ->
+ BetterVersion =
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client
+ GoodVersion;
+ false ->
+ %% Version ok for client
+ case RecordCB:is_higher(Version, GoodVersion) of
+ true ->
+ %% Use higher version
+ Version;
+ false ->
+ GoodVersion
+ end
+ end,
+ do_select_version(RecordCB, ClientVersion, Versions, BetterVersion).
+
+%%-------------Encode handshakes --------------------------------
encode_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) ->
PLen = byte_size(P),
@@ -1854,6 +1634,110 @@ encode_protocol(Protocol, Acc) ->
Len = byte_size(Protocol),
<<Acc/binary, ?BYTE(Len), Protocol/binary>>.
+enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
+ ClientRandom, ServerRandom, PrivateKey) ->
+ EncParams = encode_server_key(Params),
+ case HashAlgo of
+ null ->
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {null, anon},
+ signature = <<>>};
+ _ ->
+ Hash =
+ server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
+ ServerRandom/binary,
+ EncParams/binary>>),
+ Signature = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {HashAlgo, SignAlgo},
+ signature = Signature}
+ end.
+
+%% While the RFC opens the door to allow ALPN during renegotiation, in practice
+%% this does not work and it is recommended to ignore any ALPN extension during
+%% renegotiation, as done here.
+encode_alpn(_, true) ->
+ undefined;
+encode_alpn(undefined, _) ->
+ undefined;
+encode_alpn(Protocols, _) ->
+ #alpn{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}.
+
+hello_extensions_list(#hello_extensions{renegotiation_info = RenegotiationInfo,
+ srp = SRP,
+ signature_algs = HashSigns,
+ ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves,
+ alpn = ALPN,
+ next_protocol_negotiation = NextProtocolNegotiation,
+ sni = Sni}) ->
+ [Ext || Ext <- [RenegotiationInfo, SRP, HashSigns,
+ EcPointFormats, EllipticCurves, ALPN, NextProtocolNegotiation, Sni], Ext =/= undefined].
+
+%%-------------Decode handshakes---------------------------------
+dec_server_key(<<?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, Version) ->
+ Params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(PLen + GLen + YLen + 6, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+%% ECParameters with named_curve
+%% TODO: explicit curve
+dec_server_key(<<?BYTE(?NAMED_CURVE), ?UINT16(CurveID),
+ ?BYTE(PointLen), ECPoint:PointLen/binary,
+ _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, Version) ->
+ Params = #server_ecdh_params{curve = {namedCurve, tls_v1:enum_to_oid(CurveID)},
+ public = ECPoint},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(PointLen + 4, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(<<?UINT16(Len), PskIdentityHint:Len/binary, _/binary>> = KeyStruct,
+ KeyExchange, Version)
+ when KeyExchange == ?KEY_EXCHANGE_PSK; KeyExchange == ?KEY_EXCHANGE_RSA_PSK ->
+ Params = #server_psk_params{
+ hint = PskIdentityHint},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(Len + 2, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary,
+ ?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_DHE_PSK, Version) ->
+ DHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
+ Params = #server_dhe_psk_params{
+ hint = IdentityHint,
+ dh_params = DHParams},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(Len + PLen + GLen + YLen + 8, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(<<?UINT16(NLen), N:NLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?BYTE(SLen), S:SLen/binary,
+ ?UINT16(BLen), B:BLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_SRP, Version) ->
+ Params = #server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(NLen + GLen + SLen + BLen + 7, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(_, KeyExchange, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {unknown_or_malformed_key_exchange, KeyExchange})).
+
dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
#encrypted_premaster_secret{premaster_secret = PKEPMS};
dec_client_key(<<?UINT16(_), PKEPMS/binary>>, ?KEY_EXCHANGE_RSA, _) ->
@@ -1995,6 +1879,11 @@ dec_sni(<<?BYTE(?SNI_NAMETYPE_HOST_NAME), ?UINT16(Len),
dec_sni(<<?BYTE(_), ?UINT16(Len), _:Len, Rest/binary>>) -> dec_sni(Rest);
dec_sni(_) -> undefined.
+decode_alpn(undefined) ->
+ undefined;
+decode_alpn(#alpn{extension_data=Data}) ->
+ decode_protocols(Data, []).
+
decode_next_protocols({next_protocol_negotiation, Protocols}) ->
decode_protocols(Protocols, []).
@@ -2039,6 +1928,7 @@ from_2bytes(<<>>, Acc) ->
lists:reverse(Acc);
from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) ->
from_2bytes(Rest, [?uint16(N) | Acc]).
+
key_exchange_alg(rsa) ->
?KEY_EXCHANGE_RSA;
key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss;
@@ -2060,8 +1950,122 @@ key_exchange_alg(Alg)
key_exchange_alg(_) ->
?NULL.
+%%-------------Cipher suite handling -----------------------------
+select_cipher_suite(CipherSuites, Suites, false) ->
+ select_cipher_suite(CipherSuites, Suites);
+select_cipher_suite(CipherSuites, Suites, true) ->
+ select_cipher_suite(Suites, CipherSuites).
+
+select_cipher_suite([], _) ->
+ no_suite;
+select_cipher_suite([Suite | ClientSuites], SupportedSuites) ->
+ case is_member(Suite, SupportedSuites) of
+ true ->
+ Suite;
+ false ->
+ select_cipher_suite(ClientSuites, SupportedSuites)
+ end.
+
+is_member(Suite, SupportedSuites) ->
+ lists:member(Suite, SupportedSuites).
+
+psk_secret(PSKIdentity, PSKLookup) ->
+ case handle_psk_identity(PSKIdentity, PSKLookup) of
+ {ok, PSK} when is_binary(PSK) ->
+ Len = erlang:byte_size(PSK),
+ <<?UINT16(Len), 0:(Len*8), ?UINT16(Len), PSK/binary>>;
+ #alert{} = Alert ->
+ Alert;
+ _ ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end.
+
+psk_secret(PSKIdentity, PSKLookup, PremasterSecret) ->
+ case handle_psk_identity(PSKIdentity, PSKLookup) of
+ {ok, PSK} when is_binary(PSK) ->
+ Len = erlang:byte_size(PremasterSecret),
+ PSKLen = erlang:byte_size(PSK),
+ <<?UINT16(Len), PremasterSecret/binary, ?UINT16(PSKLen), PSK/binary>>;
+ #alert{} = Alert ->
+ Alert;
+ _ ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end.
+
+handle_psk_identity(_PSKIdentity, LookupFun)
+ when LookupFun == undefined ->
+ error;
+handle_psk_identity(PSKIdentity, {Fun, UserState}) ->
+ Fun(psk, PSKIdentity, UserState).
+
+filter_hashsigns([], [], _, Acc) ->
+ lists:reverse(Acc);
+filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns,
+ Acc) when KeyExchange == dhe_ecdsa;
+ KeyExchange == ecdhe_ecdsa ->
+ do_filter_hashsigns(ecdsa, Suite, Suites, Algos, HashSigns, Acc);
+
+filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns,
+ Acc) when KeyExchange == rsa;
+ KeyExchange == dhe_rsa;
+ KeyExchange == ecdhe_rsa;
+ KeyExchange == srp_rsa;
+ KeyExchange == rsa_psk ->
+ do_filter_hashsigns(rsa, Suite, Suites, Algos, HashSigns, Acc);
+filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns, Acc) when
+ KeyExchange == dhe_dss;
+ KeyExchange == srp_dss ->
+ do_filter_hashsigns(dsa, Suite, Suites, Algos, HashSigns, Acc);
+filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns, Acc) when
+ KeyExchange == dh_dss;
+ KeyExchange == dh_rsa;
+ KeyExchange == dh_ecdsa;
+ KeyExchange == ecdh_rsa;
+ KeyExchange == ecdh_ecdsa ->
+ %% Fixed DH certificates MAY be signed with any hash/signature
+ %% algorithm pair appearing in the hash_sign extension. The names
+ %% DH_DSS, DH_RSA, ECDH_ECDSA, and ECDH_RSA are historical.
+ filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]);
+filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns, Acc) when
+ KeyExchange == dh_anon;
+ KeyExchange == ecdh_anon;
+ KeyExchange == srp_anon;
+ KeyExchange == psk;
+ KeyExchange == dhe_psk ->
+ %% In this case hashsigns is not used as the kexchange is anonaymous
+ filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]).
+
+do_filter_hashsigns(SignAlgo, Suite, Suites, Algos, HashSigns, Acc) ->
+ case lists:keymember(SignAlgo, 2, HashSigns) of
+ true ->
+ filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]);
+ false ->
+ filter_hashsigns(Suites, Algos, HashSigns, Acc)
+ end.
+
+unavailable_ecc_suites(no_curve) ->
+ ssl_cipher:ec_keyed_suites();
+unavailable_ecc_suites(_) ->
+ [].
%%-------------Extension handling --------------------------------
+handle_renegotiation_extension(Role, RecordCB, Version, Info, Random, NegotiatedCipherSuite,
+ ClientCipherSuites, Compression,
+ ConnectionStates0, Renegotiation, SecureRenegotation) ->
+ case handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0,
+ Renegotiation, SecureRenegotation,
+ ClientCipherSuites) of
+ {ok, ConnectionStates} ->
+ hello_pending_connection_states(RecordCB, Role,
+ Version,
+ NegotiatedCipherSuite,
+ Random,
+ Compression,
+ ConnectionStates);
+ #alert{} = Alert ->
+ throw(Alert)
+ end.
+
%% Receive protocols, choose one from the list, return it.
handle_alpn_extension(_, {error, Reason}) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason);
@@ -2124,150 +2128,6 @@ handle_srp_extension(undefined, Session) ->
handle_srp_extension(#srp{username = Username}, Session) ->
Session#session{srp_username = Username}.
-%%-------------Misc --------------------------------
-
-select_cipher_suite(CipherSuites, Suites, false) ->
- select_cipher_suite(CipherSuites, Suites);
-select_cipher_suite(CipherSuites, Suites, true) ->
- select_cipher_suite(Suites, CipherSuites).
-
-select_cipher_suite([], _) ->
- no_suite;
-select_cipher_suite([Suite | ClientSuites], SupportedSuites) ->
- case is_member(Suite, SupportedSuites) of
- true ->
- Suite;
- false ->
- select_cipher_suite(ClientSuites, SupportedSuites)
- end.
-
-int_to_bin(I) ->
- L = (length(integer_to_list(I, 16)) + 1) div 2,
- <<I:(L*8)>>.
-
-is_member(Suite, SupportedSuites) ->
- lists:member(Suite, SupportedSuites).
-
-select_compression(_CompressionMetodes) ->
- ?NULL.
-
-available_signature_algs(undefined, _) ->
- undefined;
-available_signature_algs(SupportedHashSigns, Version) when Version >= {3, 3} ->
- #hash_sign_algos{hash_sign_algos = SupportedHashSigns};
-available_signature_algs(_, _) ->
- undefined.
-
-psk_secret(PSKIdentity, PSKLookup) ->
- case handle_psk_identity(PSKIdentity, PSKLookup) of
- {ok, PSK} when is_binary(PSK) ->
- Len = erlang:byte_size(PSK),
- <<?UINT16(Len), 0:(Len*8), ?UINT16(Len), PSK/binary>>;
- #alert{} = Alert ->
- Alert;
- _ ->
- throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
- end.
-
-psk_secret(PSKIdentity, PSKLookup, PremasterSecret) ->
- case handle_psk_identity(PSKIdentity, PSKLookup) of
- {ok, PSK} when is_binary(PSK) ->
- Len = erlang:byte_size(PremasterSecret),
- PSKLen = erlang:byte_size(PSK),
- <<?UINT16(Len), PremasterSecret/binary, ?UINT16(PSKLen), PSK/binary>>;
- #alert{} = Alert ->
- Alert;
- _ ->
- throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
- end.
-
-handle_psk_identity(_PSKIdentity, LookupFun)
- when LookupFun == undefined ->
- error;
-handle_psk_identity(PSKIdentity, {Fun, UserState}) ->
- Fun(psk, PSKIdentity, UserState).
-
-crl_check(_, false, _,_,_, _, _) ->
- valid;
-crl_check(_, peer, _, _,_, valid, _) -> %% Do not check CAs with this option.
- valid;
-crl_check(OtpCert, Check, CertDbHandle, CertDbRef, {Callback, CRLDbHandle}, _, CertPath) ->
- Options = [{issuer_fun, {fun(_DP, CRL, Issuer, DBInfo) ->
- ssl_crl:trusted_cert_and_path(CRL, Issuer, {CertPath,
- DBInfo})
- end, {CertDbHandle, CertDbRef}}},
- {update_crl, fun(DP, CRL) -> Callback:fresh_crl(DP, CRL) end},
- {undetermined_details, true}
- ],
- case dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) of
- no_dps ->
- crl_check_same_issuer(OtpCert, Check,
- dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer),
- Options);
- DpsAndCRLs -> %% This DP list may be empty if relevant CRLs existed
- %% but could not be retrived, will result in {bad_cert, revocation_status_undetermined}
- case public_key:pkix_crls_validate(OtpCert, DpsAndCRLs, Options) of
- {bad_cert, {revocation_status_undetermined, _}} ->
- crl_check_same_issuer(OtpCert, Check, dps_and_crls(OtpCert, Callback,
- CRLDbHandle, same_issuer), Options);
- Other ->
- Other
- end
- end.
-
-crl_check_same_issuer(OtpCert, best_effort, Dps, Options) ->
- case public_key:pkix_crls_validate(OtpCert, Dps, Options) of
- {bad_cert, {revocation_status_undetermined, _}} ->
- valid;
- Other ->
- Other
- end;
-crl_check_same_issuer(OtpCert, _, Dps, Options) ->
- public_key:pkix_crls_validate(OtpCert, Dps, Options).
-
-dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) ->
- case public_key:pkix_dist_points(OtpCert) of
- [] ->
- no_dps;
- DistPoints ->
- Issuer = OtpCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.issuer,
- CRLs = distpoints_lookup(DistPoints, Issuer, Callback, CRLDbHandle),
- dps_and_crls(DistPoints, CRLs, [])
- end;
-
-dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) ->
- DP = #'DistributionPoint'{distributionPoint = {fullName, GenNames}} =
- public_key:pkix_dist_point(OtpCert),
- CRLs = lists:flatmap(fun({directoryName, Issuer}) ->
- Callback:select(Issuer, CRLDbHandle);
- (_) ->
- []
- end, GenNames),
- [{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs].
-
-dps_and_crls([], _, Acc) ->
- Acc;
-dps_and_crls([DP | Rest], CRLs, Acc) ->
- DpCRL = [{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs],
- dps_and_crls(Rest, CRLs, DpCRL ++ Acc).
-
-distpoints_lookup([],_, _, _) ->
- [];
-distpoints_lookup([DistPoint | Rest], Issuer, Callback, CRLDbHandle) ->
- Result =
- try Callback:lookup(DistPoint, Issuer, CRLDbHandle)
- catch
- error:undef ->
- %% The callback module still uses the 2-argument
- %% version of the lookup function.
- Callback:lookup(DistPoint, CRLDbHandle)
- end,
- case Result of
- not_available ->
- distpoints_lookup(Rest, Issuer, Callback, CRLDbHandle);
- CRLs ->
- CRLs
- end.
sign_algo(?rsaEncryption) ->
rsa;
@@ -2317,7 +2177,6 @@ is_acceptable_hash_sign(_, _, _, KeyExAlgo, _) when
true;
is_acceptable_hash_sign(_,_, _,_,_) ->
false.
-
is_acceptable_hash_sign(Algos, SupportedHashSigns) ->
lists:member(Algos, SupportedHashSigns).
@@ -2337,27 +2196,162 @@ sign_type(dsa) ->
sign_type(ecdsa) ->
?ECDSA_SIGN.
-
-bad_key(#'DSAPrivateKey'{}) ->
- unacceptable_dsa_key;
-bad_key(#'RSAPrivateKey'{}) ->
- unacceptable_rsa_key;
-bad_key(#'ECPrivateKey'{}) ->
- unacceptable_ecdsa_key.
-
-available_signature_algs(undefined, SupportedHashSigns, _, Version) when
- Version >= {3,3} ->
- SupportedHashSigns;
-available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns,
- _, Version) when Version >= {3,3} ->
- sets:to_list(sets:intersection(sets:from_list(ClientHashSigns),
- sets:from_list(SupportedHashSigns)));
-available_signature_algs(_, _, _, _) ->
- undefined.
-
server_name(_, _, server) ->
undefined; %% Not interesting to check your own name.
server_name(undefined, Host, client) ->
{fallback, Host}; %% Fallback to Host argument to connect
server_name(SNI, _, client) ->
SNI. %% If Server Name Indication is available
+
+client_ecc_extensions(SupportedECCs) ->
+ CryptoSupport = proplists:get_value(public_keys, crypto:supports()),
+ case proplists:get_bool(ecdh, CryptoSupport) of
+ true ->
+ EcPointFormats = #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]},
+ EllipticCurves = SupportedECCs,
+ {EcPointFormats, EllipticCurves};
+ _ ->
+ {undefined, undefined}
+ end.
+
+server_ecc_extension(_Version, EcPointFormats) ->
+ CryptoSupport = proplists:get_value(public_keys, crypto:supports()),
+ case proplists:get_bool(ecdh, CryptoSupport) of
+ true ->
+ handle_ecc_point_fmt_extension(EcPointFormats);
+ false ->
+ undefined
+ end.
+
+handle_ecc_point_fmt_extension(undefined) ->
+ undefined;
+handle_ecc_point_fmt_extension(_) ->
+ #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}.
+
+advertises_ec_ciphers([]) ->
+ false;
+advertises_ec_ciphers([#{key_exchange := ecdh_ecdsa} | _]) ->
+ true;
+advertises_ec_ciphers([#{key_exchange := ecdhe_ecdsa} | _]) ->
+ true;
+advertises_ec_ciphers([#{key_exchange := ecdh_rsa} | _]) ->
+ true;
+advertises_ec_ciphers([#{key_exchange := ecdhe_rsa} | _]) ->
+ true;
+advertises_ec_ciphers([#{key_exchange := ecdh_anon} | _]) ->
+ true;
+advertises_ec_ciphers([_| Rest]) ->
+ advertises_ec_ciphers(Rest).
+
+select_shared_curve([], _) ->
+ no_curve;
+select_shared_curve([Curve | Rest], Curves) ->
+ case lists:member(Curve, Curves) of
+ true ->
+ {namedCurve, Curve};
+ false ->
+ select_shared_curve(Rest, Curves)
+ end.
+
+sni(undefined) ->
+ undefined;
+sni(disable) ->
+ undefined;
+sni(Hostname) ->
+ #sni{hostname = Hostname}.
+
+renegotiation_info(_, client, _, false) ->
+ #renegotiation_info{renegotiated_connection = undefined};
+renegotiation_info(_RecordCB, server, ConnectionStates, false) ->
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case maps:get(secure_renegotiation, ConnectionState) of
+ true ->
+ #renegotiation_info{renegotiated_connection = ?byte(0)};
+ false ->
+ #renegotiation_info{renegotiated_connection = undefined}
+ end;
+renegotiation_info(_RecordCB, client, ConnectionStates, true) ->
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case maps:get(secure_renegotiation, ConnectionState) of
+ true ->
+ Data = maps:get(client_verify_data, ConnectionState),
+ #renegotiation_info{renegotiated_connection = Data};
+ false ->
+ #renegotiation_info{renegotiated_connection = undefined}
+ end;
+
+renegotiation_info(_RecordCB, server, ConnectionStates, true) ->
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case maps:get(secure_renegotiation, ConnectionState) of
+ true ->
+ CData = maps:get(client_verify_data, ConnectionState),
+ SData = maps:get(server_verify_data, ConnectionState),
+ #renegotiation_info{renegotiated_connection = <<CData/binary, SData/binary>>};
+ false ->
+ #renegotiation_info{renegotiated_connection = undefined}
+ end.
+
+handle_renegotiation_info(_RecordCB, _, #renegotiation_info{renegotiated_connection = ?byte(0)},
+ ConnectionStates, false, _, _) ->
+ {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
+
+handle_renegotiation_info(_RecordCB, server, undefined, ConnectionStates, _, _, CipherSuites) ->
+ case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
+ true ->
+ {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
+ false ->
+ {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)}
+ end;
+
+handle_renegotiation_info(_RecordCB, _, undefined, ConnectionStates, false, _, _) ->
+ {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)};
+
+handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
+ ConnectionStates, true, _, _) ->
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ CData = maps:get(client_verify_data, ConnectionState),
+ SData = maps:get(server_verify_data, ConnectionState),
+ case <<CData/binary, SData/binary>> == ClientServerVerify of
+ true ->
+ {ok, ConnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation)
+ end;
+handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify},
+ ConnectionStates, true, _, CipherSuites) ->
+
+ case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
+ true ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv});
+ false ->
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ Data = maps:get(client_verify_data, ConnectionState),
+ case Data == ClientVerify of
+ true ->
+ {ok, ConnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation)
+ end
+ end;
+
+handle_renegotiation_info(RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
+ handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation);
+
+handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) ->
+ case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
+ true ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv});
+ false ->
+ handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation)
+ end.
+
+handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) ->
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case {SecureRenegotation, maps:get(secure_renegotiation, ConnectionState)} of
+ {_, true} ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure);
+ {true, false} ->
+ ?ALERT_REC(?FATAL, ?NO_RENEGOTIATION);
+ {false, false} ->
+ {ok, ConnectionStates}
+ end.
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 24ac34653e..9bb1cbaeb0 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -95,7 +95,8 @@
certfile :: binary(),
cert :: public_key:der_encoded() | secret_printout() | 'undefined',
keyfile :: binary(),
- key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()} | secret_printout() | 'undefined',
+ key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo',
+ public_key:der_encoded()} | key_map() | secret_printout() | 'undefined',
password :: string() | secret_printout() | 'undefined',
cacerts :: [public_key:der_encoded()] | secret_printout() | 'undefined',
cacertfile :: binary(),
@@ -164,7 +165,15 @@
connection_cb
}).
-
+-type key_map() :: #{algorithm := rsa | dss | ecdsa,
+ %% engine and key_id ought to
+ %% be :=, but putting it in
+ %% the spec gives dialyzer warning
+ %% of correct code!
+ engine => crypto:engine_ref(),
+ key_id => crypto:key_id(),
+ password => crypto:password()
+ }.
-type state_name() :: hello | abbreviated | certify | cipher | connection.
-type gen_fsm_state_return() :: {next_state, state_name(), term()} |
{next_state, state_name(), term(), timeout()} |
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 62c2ffce8b..dd6a3e8521 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -31,7 +31,7 @@
%% Connection state handling
-export([initial_security_params/1, current_connection_state/2, pending_connection_state/2,
- activate_pending_connection_state/2,
+ activate_pending_connection_state/3,
set_security_params/3,
set_mac_secret/4,
set_master_secret/2,
@@ -53,11 +53,11 @@
-type ssl_atom_version() :: tls_record:tls_atom_version().
-type connection_states() :: term(). %% Map
-type connection_state() :: term(). %% Map
+
%%====================================================================
-%% Internal application API
+%% Connection state handling
%%====================================================================
-
%%--------------------------------------------------------------------
-spec current_connection_state(connection_states(), read | write) ->
connection_state().
@@ -83,7 +83,7 @@ pending_connection_state(ConnectionStates, write) ->
maps:get(pending_write, ConnectionStates).
%%--------------------------------------------------------------------
--spec activate_pending_connection_state(connection_states(), read | write) ->
+-spec activate_pending_connection_state(connection_states(), read | write, tls_connection | dtls_connection) ->
connection_states().
%%
%% Description: Creates a new instance of the connection_states record
@@ -91,13 +91,13 @@ pending_connection_state(ConnectionStates, write) ->
%%--------------------------------------------------------------------
activate_pending_connection_state(#{current_read := Current,
pending_read := Pending} = States,
- read) ->
+ read, Connection) ->
#{secure_renegotiation := SecureRenegotation} = Current,
#{beast_mitigation := BeastMitigation,
security_parameters := SecParams} = Pending,
NewCurrent = Pending#{sequence_number => 0},
ConnectionEnd = SecParams#security_parameters.connection_end,
- EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation),
+ EmptyPending = Connection:empty_connection_state(ConnectionEnd, BeastMitigation),
NewPending = EmptyPending#{secure_renegotiation => SecureRenegotation},
States#{current_read => NewCurrent,
pending_read => NewPending
@@ -105,13 +105,13 @@ activate_pending_connection_state(#{current_read := Current,
activate_pending_connection_state(#{current_write := Current,
pending_write := Pending} = States,
- write) ->
+ write, Connection) ->
NewCurrent = Pending#{sequence_number => 0},
#{secure_renegotiation := SecureRenegotation} = Current,
#{beast_mitigation := BeastMitigation,
security_parameters := SecParams} = Pending,
ConnectionEnd = SecParams#security_parameters.connection_end,
- EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation),
+ EmptyPending = Connection:empty_connection_state(ConnectionEnd, BeastMitigation),
NewPending = EmptyPending#{secure_renegotiation => SecureRenegotation},
States#{current_write => NewCurrent,
pending_write => NewPending
@@ -267,6 +267,9 @@ set_pending_cipher_state(#{pending_read := Read,
pending_read => Read#{cipher_state => ServerState},
pending_write => Write#{cipher_state => ClientState}}.
+%%====================================================================
+%% Compression
+%%====================================================================
uncompress(?NULL, Data, CS) ->
{Data, CS}.
@@ -282,6 +285,11 @@ compress(?NULL, Data, CS) ->
compressions() ->
[?byte(?NULL)].
+
+%%====================================================================
+%% Payload encryption/decryption
+%%====================================================================
+
%%--------------------------------------------------------------------
-spec cipher(ssl_version(), iodata(), connection_state(), MacHash::binary()) ->
{CipherFragment::binary(), connection_state()}.
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index 08947f24dd..12a057fd22 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
-module(ssl_tls_dist_proxy).
--export([listen/2, accept/2, connect/3, get_tcp_address/1]).
+-export([listen/2, accept/2, connect/4, get_tcp_address/1]).
-export([init/1, start_link/0, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, ssl_options/2]).
@@ -45,8 +45,9 @@ listen(Driver, Name) ->
accept(Driver, Listen) ->
gen_server:call(?MODULE, {accept, Driver, Listen}, infinity).
-connect(Driver, Ip, Port) ->
- gen_server:call(?MODULE, {connect, Driver, Ip, Port}, infinity).
+connect(Driver, Ip, Port, ExtraOpts) ->
+ gen_server:call(
+ ?MODULE, {connect, Driver, Ip, Port, ExtraOpts}, infinity).
do_listen(Options) ->
@@ -134,9 +135,11 @@ handle_call({accept, _Driver, Listen}, {From, _}, State = #state{listen={_, Worl
WorldPid = spawn_link(fun() -> accept_loop(Self, world, World, Listen) end),
{reply, ErtsPid, State#state{accept_loop={ErtsPid, WorldPid}}};
-handle_call({connect, Driver, Ip, Port}, {From, _}, State) ->
+handle_call({connect, Driver, Ip, Port, ExtraOpts}, {From, _}, State) ->
Me = self(),
- Pid = spawn_link(fun() -> setup_proxy(Driver, Ip, Port, Me) end),
+ Pid =
+ spawn_link(
+ fun() -> setup_proxy(Driver, Ip, Port, ExtraOpts, Me) end),
receive
{Pid, go_ahead, LPort} ->
Res = {ok, Socket} = try_connect(LPort),
@@ -270,9 +273,9 @@ try_connect(Port) ->
try_connect(Port)
end.
-setup_proxy(Driver, Ip, Port, Parent) ->
+setup_proxy(Driver, Ip, Port, ExtraOpts, Parent) ->
process_flag(trap_exit, true),
- Opts = connect_options(get_ssl_options(client)),
+ Opts = connect_options(ExtraOpts ++ get_ssl_options(client)),
case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay(),
Driver:family()] ++ Opts) of
{ok, World} ->
@@ -369,6 +372,17 @@ loop_conn(World, Erts) ->
end.
get_ssl_options(Type) ->
+ try ets:lookup(ssl_dist_opts, Type) of
+ [{Type, Opts}] ->
+ [{erl_dist, true} | Opts];
+ _ ->
+ get_ssl_dist_arguments(Type)
+ catch
+ error:badarg ->
+ get_ssl_dist_arguments(Type)
+ end.
+
+get_ssl_dist_arguments(Type) ->
case init:get_argument(ssl_dist_opt) of
{ok, Args} ->
[{erl_dist, true} | ssl_options(Type, lists:append(Args))];
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index b566551143..914ee9f22f 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -45,22 +45,20 @@
%% Setup
-export([start_fsm/8, start_link/7, init/1]).
--export([encode_data/3, encode_alert/3]).
-
%% State transition handling
--export([next_record/1, next_event/3, next_event/4]).
+-export([next_record/1, next_event/3, next_event/4, handle_common_event/4]).
%% Handshake handling
-export([renegotiate/2, send_handshake/2,
queue_handshake/2, queue_change_cipher/2,
- reinit_handshake_data/1, select_sni_extension/1]).
+ reinit_handshake_data/1, select_sni_extension/1, empty_connection_state/2]).
%% Alert and close handling
--export([send_alert/2, close/5, protocol_name/0]).
+-export([encode_alert/3, send_alert/2, close/5, protocol_name/0]).
%% Data handling
--export([passive_receive/2, next_record_if_active/1, handle_common_event/4, send/3,
- socket/5, setopts/3, getopts/3]).
+-export([encode_data/3, passive_receive/2, next_record_if_active/1, send/3,
+ socket/5, setopts/3, getopts/3]).
%% gen_statem state functions
-export([init/3, error/3, downgrade/3, %% Initiation and take down states
@@ -72,6 +70,9 @@
%%====================================================================
%% Internal application API
%%====================================================================
+%%====================================================================
+%% Setup
+%%====================================================================
start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts,
User, {CbModule, _,_, _} = CbInfo,
Timeout) ->
@@ -100,6 +101,165 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} =
Error
end.
+%%--------------------------------------------------------------------
+-spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
+ {ok, pid()} | ignore | {error, reason()}.
+%%
+%% Description: Creates a gen_statem process which calls Module:init/1 to
+%% initialize.
+%%--------------------------------------------------------------------
+start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
+ {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}.
+
+init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
+ process_flag(trap_exit, true),
+ State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
+ try
+ State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0),
+ gen_statem:enter_loop(?MODULE, [], init, State)
+ catch throw:Error ->
+ gen_statem:enter_loop(?MODULE, [], error, {Error, State0})
+ end.
+%%====================================================================
+%% State transition handling
+%%====================================================================
+next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
+ {no_record, State#state{unprocessed_handshake_events = N-1}};
+
+next_record(#state{protocol_buffers =
+ #protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]}
+ = Buffers,
+ connection_states = ConnStates0,
+ ssl_options = #ssl_options{padding_check = Check}} = State) ->
+ case tls_record:decode_cipher_text(CT, ConnStates0, Check) of
+ {Plain, ConnStates} ->
+ {Plain, State#state{protocol_buffers =
+ Buffers#protocol_buffers{tls_cipher_texts = Rest},
+ connection_states = ConnStates}};
+ #alert{} = Alert ->
+ {Alert, State}
+ end;
+next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_cipher_texts = []},
+ socket = Socket,
+ close_tag = CloseTag,
+ transport_cb = Transport} = State) ->
+ case tls_socket:setopts(Transport, Socket, [{active,once}]) of
+ ok ->
+ {no_record, State};
+ _ ->
+ self() ! {CloseTag, Socket},
+ {no_record, State}
+ end;
+next_record(State) ->
+ {no_record, State}.
+
+next_event(StateName, Record, State) ->
+ next_event(StateName, Record, State, []).
+
+next_event(connection = StateName, no_record, State0, Actions) ->
+ case next_record_if_active(State0) of
+ {no_record, State} ->
+ ssl_connection:hibernate_after(StateName, State, Actions);
+ {#ssl_tls{} = Record, State} ->
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
+ {#alert{} = Alert, State} ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ end;
+next_event(StateName, Record, State, Actions) ->
+ case Record of
+ no_record ->
+ {next_state, StateName, State, Actions};
+ #ssl_tls{} = Record ->
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
+ #alert{} = Alert ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
+ end.
+
+handle_common_event(internal, #alert{} = Alert, StateName,
+ #state{negotiated_version = Version} = State) ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State);
+%%% TLS record protocol level handshake messages
+handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
+ StateName, #state{protocol_buffers =
+ #protocol_buffers{tls_handshake_buffer = Buf0} = Buffers,
+ negotiated_version = Version,
+ ssl_options = Options} = State0) ->
+ try
+ {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0, Options),
+ State1 =
+ State0#state{protocol_buffers =
+ Buffers#protocol_buffers{tls_handshake_buffer = Buf}},
+ case Packets of
+ [] ->
+ assert_buffer_sanity(Buf, Options),
+ {Record, State} = next_record(State1),
+ next_event(StateName, Record, State);
+ _ ->
+ Events = tls_handshake_events(Packets),
+ case StateName of
+ connection ->
+ ssl_connection:hibernate_after(StateName, State1, Events);
+ _ ->
+ {next_state, StateName,
+ State1#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
+ end
+ end
+ catch throw:#alert{} = Alert ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State0)
+ end;
+%%% TLS record protocol level application data messages
+handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) ->
+ {next_state, StateName, State, [{next_event, internal, {application_data, Data}}]};
+%%% TLS record protocol level change cipher messages
+handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) ->
+ {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]};
+%%% TLS record protocol level Alert messages
+handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,
+ #state{negotiated_version = Version} = State) ->
+ try decode_alerts(EncAlerts) of
+ Alerts = [_|_] ->
+ handle_alerts(Alerts, {next_state, StateName, State});
+ [] ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert),
+ Version, StateName, State);
+ #alert{} = Alert ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State)
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, alert_decode_error),
+ Version, StateName, State)
+
+ end;
+%% Ignore unknown TLS record level protocol messages
+handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) ->
+ {next_state, StateName, State}.
+%%====================================================================
+%% Handshake handling
+%%====================================================================
+renegotiate(#state{role = client} = State, Actions) ->
+ %% Handle same way as if server requested
+ %% the renegotiation
+ Hs0 = ssl_handshake:init_handshake_history(),
+ {next_state, connection, State#state{tls_handshake_history = Hs0},
+ [{next_event, internal, #hello_request{}} | Actions]};
+
+renegotiate(#state{role = server,
+ socket = Socket,
+ transport_cb = Transport,
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0, Actions) ->
+ HelloRequest = ssl_handshake:hello_request(),
+ Frag = tls_handshake:encode_handshake(HelloRequest, Version),
+ Hs0 = ssl_handshake:init_handshake_history(),
+ {BinMsg, ConnectionStates} =
+ tls_record:encode_handshake(Frag, Version, ConnectionStates0),
+ send(Transport, Socket, BinMsg),
+ State1 = State0#state{connection_states =
+ ConnectionStates,
+ tls_handshake_history = Hs0},
+ {Record, State} = next_record(State1),
+ next_event(hello, Record, State, Actions).
+
send_handshake(Handshake, State) ->
send_handshake_flight(queue_handshake(Handshake, State)).
@@ -128,15 +288,6 @@ queue_change_cipher(Msg, #state{negotiated_version = Version,
State0#state{connection_states = ConnectionStates,
flight_buffer = Flight0 ++ [BinChangeCipher]}.
-send_alert(Alert, #state{negotiated_version = Version,
- socket = Socket,
- transport_cb = Transport,
- connection_states = ConnectionStates0} = State0) ->
- {BinMsg, ConnectionStates} =
- encode_alert(Alert, Version, ConnectionStates0),
- send(Transport, Socket, BinMsg),
- State0#state{connection_states = ConnectionStates}.
-
reinit_handshake_data(State) ->
%% premaster_secret, public_key_info and tls_handshake_info
%% are only needed during the handshake phase.
@@ -152,8 +303,20 @@ select_sni_extension(#client_hello{extensions = HelloExtensions}) ->
select_sni_extension(_) ->
undefined.
-encode_data(Data, Version, ConnectionStates0)->
- tls_record:encode_data(Data, Version, ConnectionStates0).
+empty_connection_state(ConnectionEnd, BeastMitigation) ->
+ ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation).
+
+%%====================================================================
+%% Alert and close handling
+%%====================================================================
+send_alert(Alert, #state{negotiated_version = Version,
+ socket = Socket,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0} = State0) ->
+ {BinMsg, ConnectionStates} =
+ encode_alert(Alert, Version, ConnectionStates0),
+ send(Transport, Socket, BinMsg),
+ State0#state{connection_states = ConnectionStates}.
%%--------------------------------------------------------------------
-spec encode_alert(#alert{}, ssl_record:ssl_version(), ssl_record:connection_states()) ->
@@ -163,42 +326,66 @@ encode_data(Data, Version, ConnectionStates0)->
%%--------------------------------------------------------------------
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
tls_record:encode_alert_record(Alert, Version, ConnectionStates).
-
+%% User closes or recursive call!
+close({close, Timeout}, Socket, Transport = gen_tcp, _,_) ->
+ tls_socket:setopts(Transport, Socket, [{active, false}]),
+ Transport:shutdown(Socket, write),
+ _ = Transport:recv(Socket, 0, Timeout),
+ ok;
+%% Peer closed socket
+close({shutdown, transport_closed}, Socket, Transport = gen_tcp, ConnectionStates, Check) ->
+ close({close, 0}, Socket, Transport, ConnectionStates, Check);
+%% We generate fatal alert
+close({shutdown, own_alert}, Socket, Transport = gen_tcp, ConnectionStates, Check) ->
+ %% Standard trick to try to make sure all
+ %% data sent to the tcp port is really delivered to the
+ %% peer application before tcp port is closed so that the peer will
+ %% get the correct TLS alert message and not only a transport close.
+ %% Will return when other side has closed or after timout millisec
+ %% e.g. we do not want to hang if something goes wrong
+ %% with the network but we want to maximise the odds that
+ %% peer application gets all data sent on the tcp connection.
+ close({close, ?DEFAULT_TIMEOUT}, Socket, Transport, ConnectionStates, Check);
+close(downgrade, _,_,_,_) ->
+ ok;
+%% Other
+close(_, Socket, Transport, _,_) ->
+ Transport:close(Socket).
protocol_name() ->
"TLS".
-%%====================================================================
-%% tls_connection_sup API
-%%====================================================================
-%%--------------------------------------------------------------------
--spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
- {ok, pid()} | ignore | {error, reason()}.
-%%
-%% Description: Creates a gen_fsm process which calls Module:init/1 to
-%% initialize. To ensure a synchronized start-up procedure, this function
-%% does not return until Module:init/1 has returned.
-%%--------------------------------------------------------------------
-start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
- {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}.
+%%====================================================================
+%% Data handling
+%%====================================================================
+encode_data(Data, Version, ConnectionStates0)->
+ tls_record:encode_data(Data, Version, ConnectionStates0).
-init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
- process_flag(trap_exit, true),
- State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
- try
- State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0),
- gen_statem:enter_loop(?MODULE, [], init, State)
- catch throw:Error ->
- gen_statem:enter_loop(?MODULE, [], error, {Error, State0})
+passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
+ case Buffer of
+ <<>> ->
+ {Record, State} = next_record(State0),
+ next_event(StateName, Record, State);
+ _ ->
+ {Record, State} = ssl_connection:read_application_data(<<>>, State0),
+ next_event(StateName, Record, State)
end.
-callback_mode() ->
- state_functions.
+next_record_if_active(State =
+ #state{socket_options =
+ #socket_options{active = false}}) ->
+ {no_record ,State};
+next_record_if_active(State) ->
+ next_record(State).
+
+send(Transport, Socket, Data) ->
+ tls_socket:send(Transport, Socket, Data).
socket(Pid, Transport, Socket, Connection, Tracker) ->
tls_socket:socket(Pid, Transport, Socket, Connection, Tracker).
setopts(Transport, Socket, Other) ->
tls_socket:setopts(Transport, Socket, Other).
+
getopts(Transport, Socket, Tag) ->
tls_socket:getopts(Transport, Socket, Tag).
@@ -241,7 +428,7 @@ init({call, From}, {start, Timeout},
{Record, State} = next_record(State1),
next_event(hello, Record, State);
init(Type, Event, State) ->
- gen_handshake(ssl_connection, ?FUNCTION_NAME, Type, Event, State).
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
%%--------------------------------------------------------------------
-spec error(gen_statem:event_type(),
@@ -251,8 +438,10 @@ init(Type, Event, State) ->
error({call, From}, {start, _Timeout}, {Error, State}) ->
{stop_and_reply, normal, {reply, From, {error, Error}}, State};
-error({call, From}, Msg, State) ->
- handle_call(Msg, From, ?FUNCTION_NAME, State);
+error({call, From}, {start, _Timeout}, #state{protocol_specific = #{error := Error}} = State) ->
+ {stop_and_reply, normal, {reply, From, {error, Error}}, State};
+error({call, _} = Call, Msg, {Error, #state{protocol_specific = Map} = State}) ->
+ gen_handshake(?FUNCTION_NAME, Call, Msg, State#state{protocol_specific = Map#{error => Error}});
error(_, _, _) ->
{keep_state_and_data, [postpone]}.
@@ -282,14 +471,13 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello,
undefined -> CurrentProtocol;
_ -> Protocol0
end,
-
- gen_handshake(ssl_connection, hello, internal, {common_client_hello, Type, ServerHelloExt},
- State#state{connection_states = ConnectionStates,
- negotiated_version = Version,
- client_hello_version = ClientVersion,
- hashsign_algorithm = HashSign,
- session = Session,
- negotiated_protocol = Protocol})
+ gen_handshake(?FUNCTION_NAME, internal, {common_client_hello, Type, ServerHelloExt},
+ State#state{connection_states = ConnectionStates,
+ negotiated_version = Version,
+ hashsign_algorithm = HashSign,
+ client_hello_version = ClientVersion,
+ session = Session,
+ negotiated_protocol = Protocol})
end;
hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
@@ -307,7 +495,7 @@ hello(internal, #server_hello{} = Hello,
hello(info, Event, State) ->
gen_info(Event, ?FUNCTION_NAME, State);
hello(Type, Event, State) ->
- gen_handshake(ssl_connection, ?FUNCTION_NAME, Type, Event, State).
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
%%--------------------------------------------------------------------
-spec abbreviated(gen_statem:event_type(), term(), #state{}) ->
@@ -316,7 +504,7 @@ hello(Type, Event, State) ->
abbreviated(info, Event, State) ->
gen_info(Event, ?FUNCTION_NAME, State);
abbreviated(Type, Event, State) ->
- gen_handshake(ssl_connection, ?FUNCTION_NAME, Type, Event, State).
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
%%--------------------------------------------------------------------
-spec certify(gen_statem:event_type(), term(), #state{}) ->
@@ -325,7 +513,7 @@ abbreviated(Type, Event, State) ->
certify(info, Event, State) ->
gen_info(Event, ?FUNCTION_NAME, State);
certify(Type, Event, State) ->
- gen_handshake(ssl_connection, ?FUNCTION_NAME, Type, Event, State).
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
%%--------------------------------------------------------------------
-spec cipher(gen_statem:event_type(), term(), #state{}) ->
@@ -334,7 +522,7 @@ certify(Type, Event, State) ->
cipher(info, Event, State) ->
gen_info(Event, ?FUNCTION_NAME, State);
cipher(Type, Event, State) ->
- gen_handshake(ssl_connection, ?FUNCTION_NAME, Type, Event, State).
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
%%--------------------------------------------------------------------
-spec connection(gen_statem:event_type(),
@@ -385,156 +573,24 @@ connection(Type, Event, State) ->
downgrade(Type, Event, State) ->
ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE).
-%%--------------------------------------------------------------------
-%% Event handling functions called by state functions to handle
-%% common or unexpected events for the state.
-%%--------------------------------------------------------------------
-handle_call(Event, From, StateName, State) ->
- ssl_connection:handle_call(Event, From, StateName, State, ?MODULE).
-
-%% raw data from socket, unpack records
-handle_info({Protocol, _, Data}, StateName,
- #state{data_tag = Protocol} = State0) ->
- case next_tls_record(Data, State0) of
- {Record, State} ->
- next_event(StateName, Record, State);
- #alert{} = Alert ->
- ssl_connection:handle_normal_shutdown(Alert, StateName, State0),
- {stop, {shutdown, own_alert}}
- end;
-handle_info({CloseTag, Socket}, StateName,
- #state{socket = Socket, close_tag = CloseTag,
- socket_options = #socket_options{active = Active},
- protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs},
- negotiated_version = Version} = State) ->
-
- %% Note that as of TLS 1.1,
- %% failure to properly close a connection no longer requires that a
- %% session not be resumed. This is a change from TLS 1.0 to conform
- %% with widespread implementation practice.
-
- case (Active == false) andalso (CTs =/= []) of
- false ->
- case Version of
- {1, N} when N >= 1 ->
- ok;
- _ ->
- %% As invalidate_sessions here causes performance issues,
- %% we will conform to the widespread implementation
- %% practice and go aginst the spec
- %%invalidate_session(Role, Host, Port, Session)
- ok
- end,
-
- ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
- {stop, {shutdown, transport_closed}};
- true ->
- %% Fixes non-delivery of final TLS record in {active, once}.
- %% Basically allows the application the opportunity to set {active, once} again
- %% and then receive the final message.
- next_event(StateName, no_record, State)
- end;
-handle_info(Msg, StateName, State) ->
- ssl_connection:handle_info(Msg, StateName, State).
-
-handle_common_event(internal, #alert{} = Alert, StateName,
- #state{negotiated_version = Version} = State) ->
- ssl_connection:handle_own_alert(Alert, Version, StateName, State);
-
-%%% TLS record protocol level handshake messages
-handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
- StateName, #state{protocol_buffers =
- #protocol_buffers{tls_handshake_buffer = Buf0} = Buffers,
- negotiated_version = Version,
- ssl_options = Options} = State0) ->
- try
- {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0, Options),
- State1 =
- State0#state{protocol_buffers =
- Buffers#protocol_buffers{tls_handshake_buffer = Buf}},
- case Packets of
- [] ->
- assert_buffer_sanity(Buf, Options),
- {Record, State} = next_record(State1),
- next_event(StateName, Record, State);
- _ ->
- Events = tls_handshake_events(Packets),
- case StateName of
- connection ->
- ssl_connection:hibernate_after(StateName, State1, Events);
- _ ->
- {next_state, StateName,
- State1#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
- end
- end
- catch throw:#alert{} = Alert ->
- ssl_connection:handle_own_alert(Alert, Version, StateName, State0)
- end;
-%%% TLS record protocol level application data messages
-handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) ->
- {next_state, StateName, State, [{next_event, internal, {application_data, Data}}]};
-%%% TLS record protocol level change cipher messages
-handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) ->
- {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]};
-%%% TLS record protocol level Alert messages
-handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,
- #state{negotiated_version = Version} = State) ->
- try decode_alerts(EncAlerts) of
- Alerts = [_|_] ->
- handle_alerts(Alerts, {next_state, StateName, State});
- [] ->
- ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert),
- Version, StateName, State);
- #alert{} = Alert ->
- ssl_connection:handle_own_alert(Alert, Version, StateName, State)
- catch
- _:_ ->
- ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, alert_decode_error),
- Version, StateName, State)
-
- end;
-%% Ignore unknown TLS record level protocol messages
-handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) ->
- {next_state, StateName, State}.
-
-send(Transport, Socket, Data) ->
- tls_socket:send(Transport, Socket, Data).
-
-%%--------------------------------------------------------------------
+%--------------------------------------------------------------------
%% gen_statem callbacks
%%--------------------------------------------------------------------
+callback_mode() ->
+ state_functions.
+
terminate(Reason, StateName, State) ->
catch ssl_connection:terminate(Reason, StateName, State).
format_status(Type, Data) ->
ssl_connection:format_status(Type, Data).
-%%--------------------------------------------------------------------
-%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, StateName, State0, {Direction, From, To}) ->
- State = convert_state(State0, Direction, From, To),
- {ok, StateName, State};
code_change(_OldVsn, StateName, State, _) ->
{ok, StateName, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp) ->
- Frag = tls_handshake:encode_handshake(Handshake, Version),
- Hist = ssl_handshake:update_handshake_history(Hist0, Frag, V2HComp),
- {Encoded, ConnectionStates} =
- tls_record:encode_handshake(Frag, Version, ConnectionStates0),
- {Encoded, ConnectionStates, Hist}.
-
-encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
- tls_record:encode_change_cipher_spec(Version, ConnectionStates).
-
-decode_alerts(Bin) ->
- ssl_alert:decode(Bin).
-
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
#ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
@@ -573,119 +629,83 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, Us
flight_buffer = []
}.
-next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{tls_record_buffer = Buf0,
- tls_cipher_texts = CT0} = Buffers} = State0) ->
- case tls_record:get_tls_records(Data, Buf0) of
+next_tls_record(Data, StateName, #state{protocol_buffers =
+ #protocol_buffers{tls_record_buffer = Buf0,
+ tls_cipher_texts = CT0} = Buffers}
+ = State0) ->
+ case tls_record:get_tls_records(Data,
+ acceptable_record_versions(StateName, State0),
+ Buf0) of
{Records, Buf1} ->
CT1 = CT0 ++ Records,
next_record(State0#state{protocol_buffers =
Buffers#protocol_buffers{tls_record_buffer = Buf1,
tls_cipher_texts = CT1}});
#alert{} = Alert ->
- Alert
+ handle_record_alert(Alert, State0)
end.
-next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
- {no_record, State#state{unprocessed_handshake_events = N-1}};
-
-next_record(#state{protocol_buffers =
- #protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]}
- = Buffers,
- connection_states = ConnStates0,
- ssl_options = #ssl_options{padding_check = Check}} = State) ->
- case tls_record:decode_cipher_text(CT, ConnStates0, Check) of
- {Plain, ConnStates} ->
- {Plain, State#state{protocol_buffers =
- Buffers#protocol_buffers{tls_cipher_texts = Rest},
- connection_states = ConnStates}};
- #alert{} = Alert ->
- {Alert, State}
- end;
-next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_cipher_texts = []},
- socket = Socket,
- transport_cb = Transport} = State) ->
- case tls_socket:setopts(Transport, Socket, [{active,once}]) of
- ok ->
- {no_record, State};
- _ ->
- {socket_closed, State}
- end;
-next_record(State) ->
- {no_record, State}.
-next_record_if_active(State =
- #state{socket_options =
- #socket_options{active = false}}) ->
- {no_record ,State};
-
-next_record_if_active(State) ->
- next_record(State).
-
-passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
- case Buffer of
- <<>> ->
- {Record, State} = next_record(State0),
- next_event(StateName, Record, State);
- _ ->
- {Record, State} = ssl_connection:read_application_data(<<>>, State0),
- next_event(StateName, Record, State)
- end.
-
-next_event(StateName, Record, State) ->
- next_event(StateName, Record, State, []).
-
-next_event(StateName, socket_closed, State, _) ->
- ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
- {stop, {shutdown, transport_closed}, State};
-next_event(connection = StateName, no_record, State0, Actions) ->
- case next_record_if_active(State0) of
- {no_record, State} ->
- ssl_connection:hibernate_after(StateName, State, Actions);
- {socket_closed, State} ->
- next_event(StateName, socket_closed, State, Actions);
- {#ssl_tls{} = Record, State} ->
- {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
- {#alert{} = Alert, State} ->
- {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
- end;
-next_event(StateName, Record, State, Actions) ->
- case Record of
- no_record ->
- {next_state, StateName, State, Actions};
- #ssl_tls{} = Record ->
- {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
- #alert{} = Alert ->
- {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
- end.
+acceptable_record_versions(hello, #state{ssl_options = #ssl_options{v2_hello_compatible = true}}) ->
+ [tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS ++ ['sslv2']];
+acceptable_record_versions(hello, _) ->
+ [tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS];
+acceptable_record_versions(_, #state{negotiated_version = Version}) ->
+ [Version].
+handle_record_alert(#alert{description = ?BAD_RECORD_MAC},
+ #state{ssl_options = #ssl_options{v2_hello_compatible = true}}) ->
+ ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION);
+handle_record_alert(Alert, _) ->
+ Alert.
tls_handshake_events(Packets) ->
lists:map(fun(Packet) ->
{next_event, internal, {handshake, Packet}}
end, Packets).
+%% raw data from socket, upack records
+handle_info({Protocol, _, Data}, StateName,
+ #state{data_tag = Protocol} = State0) ->
+ case next_tls_record(Data, StateName, State0) of
+ {Record, State} ->
+ next_event(StateName, Record, State);
+ #alert{} = Alert ->
+ ssl_connection:handle_normal_shutdown(Alert, StateName, State0),
+ {stop, {shutdown, own_alert}}
+ end;
+handle_info({CloseTag, Socket}, StateName,
+ #state{socket = Socket, close_tag = CloseTag,
+ socket_options = #socket_options{active = Active},
+ protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs},
+ negotiated_version = Version} = State) ->
-renegotiate(#state{role = client} = State, Actions) ->
- %% Handle same way as if server requested
- %% the renegotiation
- Hs0 = ssl_handshake:init_handshake_history(),
- {next_state, connection, State#state{tls_handshake_history = Hs0},
- [{next_event, internal, #hello_request{}} | Actions]};
+ %% Note that as of TLS 1.1,
+ %% failure to properly close a connection no longer requires that a
+ %% session not be resumed. This is a change from TLS 1.0 to conform
+ %% with widespread implementation practice.
-renegotiate(#state{role = server,
- socket = Socket,
- transport_cb = Transport,
- negotiated_version = Version,
- connection_states = ConnectionStates0} = State0, Actions) ->
- HelloRequest = ssl_handshake:hello_request(),
- Frag = tls_handshake:encode_handshake(HelloRequest, Version),
- Hs0 = ssl_handshake:init_handshake_history(),
- {BinMsg, ConnectionStates} =
- tls_record:encode_handshake(Frag, Version, ConnectionStates0),
- send(Transport, Socket, BinMsg),
- State1 = State0#state{connection_states =
- ConnectionStates,
- tls_handshake_history = Hs0},
- {Record, State} = next_record(State1),
- next_event(hello, Record, State, Actions).
+ case (Active == false) andalso (CTs =/= []) of
+ false ->
+ case Version of
+ {1, N} when N >= 1 ->
+ ok;
+ _ ->
+ %% As invalidate_sessions here causes performance issues,
+ %% we will conform to the widespread implementation
+ %% practice and go aginst the spec
+ %%invalidate_session(Role, Host, Port, Session)
+ ok
+ end,
+
+ ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ {stop, {shutdown, transport_closed}};
+ true ->
+ %% Fixes non-delivery of final TLS record in {active, once}.
+ %% Basically allows the application the opportunity to set {active, once} again
+ %% and then receive the final message.
+ next_event(StateName, no_record, State)
+ end;
+handle_info(Msg, StateName, State) ->
+ ssl_connection:StateName(info, Msg, State, ?MODULE).
handle_alerts([], Result) ->
Result;
@@ -696,47 +716,22 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
+encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp) ->
+ Frag = tls_handshake:encode_handshake(Handshake, Version),
+ Hist = ssl_handshake:update_handshake_history(Hist0, Frag, V2HComp),
+ {Encoded, ConnectionStates} =
+ tls_record:encode_handshake(Frag, Version, ConnectionStates0),
+ {Encoded, ConnectionStates, Hist}.
-%% User closes or recursive call!
-close({close, Timeout}, Socket, Transport = gen_tcp, _,_) ->
- tls_socket:setopts(Transport, Socket, [{active, false}]),
- Transport:shutdown(Socket, write),
- _ = Transport:recv(Socket, 0, Timeout),
- ok;
-%% Peer closed socket
-close({shutdown, transport_closed}, Socket, Transport = gen_tcp, ConnectionStates, Check) ->
- close({close, 0}, Socket, Transport, ConnectionStates, Check);
-%% We generate fatal alert
-close({shutdown, own_alert}, Socket, Transport = gen_tcp, ConnectionStates, Check) ->
- %% Standard trick to try to make sure all
- %% data sent to the tcp port is really delivered to the
- %% peer application before tcp port is closed so that the peer will
- %% get the correct TLS alert message and not only a transport close.
- %% Will return when other side has closed or after timout millisec
- %% e.g. we do not want to hang if something goes wrong
- %% with the network but we want to maximise the odds that
- %% peer application gets all data sent on the tcp connection.
- close({close, ?DEFAULT_TIMEOUT}, Socket, Transport, ConnectionStates, Check);
-close(downgrade, _,_,_,_) ->
- ok;
-%% Other
-close(_, Socket, Transport, _,_) ->
- Transport:close(Socket).
-
-convert_state(#state{ssl_options = Options} = State, up, "5.3.5", "5.3.6") ->
- State#state{ssl_options = convert_options_partial_chain(Options, up)};
-convert_state(#state{ssl_options = Options} = State, down, "5.3.6", "5.3.5") ->
- State#state{ssl_options = convert_options_partial_chain(Options, down)}.
+encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
+ tls_record:encode_change_cipher_spec(Version, ConnectionStates).
-convert_options_partial_chain(Options, up) ->
- {Head, Tail} = lists:split(5, tuple_to_list(Options)),
- list_to_tuple(Head ++ [{partial_chain, fun(_) -> unknown_ca end}] ++ Tail);
-convert_options_partial_chain(Options, down) ->
- list_to_tuple(proplists:delete(partial_chain, tuple_to_list(Options))).
+decode_alerts(Bin) ->
+ ssl_alert:decode(Bin).
-gen_handshake(GenConnection, StateName, Type, Event,
+gen_handshake(StateName, Type, Event,
#state{negotiated_version = Version} = State) ->
- try GenConnection:StateName(Type, Event, State, ?MODULE) of
+ try ssl_connection:StateName(Type, Event, State, ?MODULE) of
Result ->
Result
catch
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index b54540393a..8817418fb0 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -32,13 +32,19 @@
-include("ssl_cipher.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([client_hello/8, hello/4,
- get_tls_handshake/4, encode_handshake/2, decode_handshake/4]).
+%% Handshake handling
+-export([client_hello/8, hello/4]).
+
+%% Handshake encoding
+-export([encode_handshake/2]).
+
+%% Handshake decodeing
+-export([get_tls_handshake/4, decode_handshake/4]).
-type tls_handshake() :: #client_hello{} | ssl_handshake:ssl_handshake().
%%====================================================================
-%% Internal application API
+%% Handshake handling
%%====================================================================
%%--------------------------------------------------------------------
-spec client_hello(host(), inet:port_number(), ssl_record:connection_states(),
@@ -54,18 +60,14 @@ client_hello(Host, Port, ConnectionStates,
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
Version = tls_record:highest_protocol_version(Versions),
- #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read),
+ #{security_parameters := SecParams} =
+ ssl_record:pending_connection_state(ConnectionStates, read),
AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version),
Extensions = ssl_handshake:client_hello_extensions(Version,
AvailableCipherSuites,
- SslOpts, ConnectionStates, Renegotiation),
- CipherSuites =
- case Fallback of
- true ->
- [?TLS_FALLBACK_SCSV | ssl_handshake:cipher_suites(AvailableCipherSuites, Renegotiation)];
- false ->
- ssl_handshake:cipher_suites(AvailableCipherSuites, Renegotiation)
- end,
+ SslOpts, ConnectionStates,
+ Renegotiation),
+ CipherSuites = ssl_handshake:cipher_suites(AvailableCipherSuites, Renegotiation, Fallback),
Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
#client_hello{session_id = Id,
client_version = Version,
@@ -85,8 +87,8 @@ client_hello(Host, Port, ConnectionStates,
ssl_record:connection_states(), alpn | npn, binary() | undefined}|
{tls_record:tls_version(), {resumed | new, #session{}},
ssl_record:connection_states(), binary() | undefined,
- #hello_extensions{}, {ssl_cipher:hash(), ssl_cipher:sign_algo()} | undefined} |
- #alert{}.
+ #hello_extensions{}, {ssl_cipher:hash(), ssl_cipher:sign_algo()} |
+ undefined} | #alert{}.
%%
%% Description: Handles a received hello message
%%--------------------------------------------------------------------
@@ -99,7 +101,8 @@ hello(#server_hello{server_version = Version, random = Random,
case tls_record:is_acceptable_version(Version, SupportedVersions) of
true ->
handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
- Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation);
+ Compression, HelloExt, SslOpt,
+ ConnectionStates0, Renegotiation);
false ->
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end;
@@ -127,18 +130,29 @@ hello(#client_hello{client_version = ClientVersion,
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data)
end.
+
+%%--------------------------------------------------------------------
+%%% Handshake encodeing
+%%--------------------------------------------------------------------
+
%%--------------------------------------------------------------------
-spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist().
%%
%% Description: Encode a handshake packet
-%%--------------------------------------------------------------------x
+%%--------------------------------------------------------------------
encode_handshake(Package, Version) ->
{MsgType, Bin} = enc_handshake(Package, Version),
Len = byte_size(Bin),
[MsgType, ?uint24(Len), Bin].
+
+%%--------------------------------------------------------------------
+%%% Handshake decodeing
+%%--------------------------------------------------------------------
+
%%--------------------------------------------------------------------
--spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(), #ssl_options{}) ->
+-spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(),
+ #ssl_options{}) ->
{[tls_handshake()], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
@@ -153,37 +167,45 @@ get_tls_handshake(Version, Data, Buffer, Options) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-handle_client_hello(Version, #client_hello{session_id = SugesstedId,
- cipher_suites = CipherSuites,
- compression_methods = Compressions,
- random = Random,
- extensions = #hello_extensions{elliptic_curves = Curves,
- signature_algs = ClientHashSigns} = HelloExt},
+handle_client_hello(Version,
+ #client_hello{session_id = SugesstedId,
+ cipher_suites = CipherSuites,
+ compression_methods = Compressions,
+ random = Random,
+ extensions =
+ #hello_extensions{elliptic_curves = Curves,
+ signature_algs = ClientHashSigns}
+ = HelloExt},
#ssl_options{versions = Versions,
signature_algs = SupportedHashSigns,
eccs = SupportedECCs,
honor_ecc_order = ECCOrder} = SslOpts,
- {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) ->
+ {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _},
+ Renegotiation) ->
case tls_record:is_acceptable_version(Version, Versions) of
true ->
AvailableHashSigns = ssl_handshake:available_signature_algs(
ClientHashSigns, SupportedHashSigns, Cert, Version),
ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder),
{Type, #session{cipher_suite = CipherSuite} = Session1}
- = ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions,
- Port, Session0#session{ecc = ECCCurve}, Version,
- SslOpts, Cache, CacheCb, Cert),
+ = ssl_handshake:select_session(SugesstedId, CipherSuites,
+ AvailableHashSigns, Compressions,
+ Port, Session0#session{ecc = ECCCurve},
+ Version, SslOpts, Cache, CacheCb, Cert),
case CipherSuite of
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_ciphers);
_ ->
- {KeyExAlg,_,_,_} = ssl_cipher:suite_definition(CipherSuite),
- case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of
+ #{key_exchange := KeyExAlg} = ssl_cipher:suite_definition(CipherSuite),
+ case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg,
+ SupportedHashSigns, Version) of
#alert{} = Alert ->
Alert;
HashSign ->
- handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt,
- SslOpts, Session1, ConnectionStates0,
+ handle_client_hello_extensions(Version, Type, Random,
+ CipherSuites, HelloExt,
+ SslOpts, Session1,
+ ConnectionStates0,
Renegotiation, HashSign)
end
end;
@@ -191,6 +213,59 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId,
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end.
+handle_client_hello_extensions(Version, Type, Random, CipherSuites,
+ HelloExt, SslOpts, Session0, ConnectionStates0,
+ Renegotiation, HashSign) ->
+ try ssl_handshake:handle_client_hello_extensions(tls_record, Random, CipherSuites,
+ HelloExt, Version, SslOpts,
+ Session0, ConnectionStates0,
+ Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ {Session, ConnectionStates, Protocol, ServerHelloExt} ->
+ {Version, {Type, Session}, ConnectionStates, Protocol,
+ ServerHelloExt, HashSign}
+ catch throw:Alert ->
+ Alert
+ end.
+
+
+handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
+ Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) ->
+ case ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite,
+ Compression, HelloExt, Version,
+ SslOpt, ConnectionStates0,
+ Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ {ConnectionStates, ProtoExt, Protocol} ->
+ {Version, SessionId, ConnectionStates, ProtoExt, Protocol}
+ end.
+%%--------------------------------------------------------------------
+enc_handshake(#hello_request{}, _Version) ->
+ {?HELLO_REQUEST, <<>>};
+enc_handshake(#client_hello{client_version = {Major, Minor},
+ random = Random,
+ session_id = SessionID,
+ cipher_suites = CipherSuites,
+ compression_methods = CompMethods,
+ extensions = HelloExtensions}, _Version) ->
+ SIDLength = byte_size(SessionID),
+ BinCompMethods = list_to_binary(CompMethods),
+ CmLength = byte_size(BinCompMethods),
+ BinCipherSuites = list_to_binary(CipherSuites),
+ CsLength = byte_size(BinCipherSuites),
+ ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
+
+ {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SIDLength), SessionID/binary,
+ ?UINT16(CsLength), BinCipherSuites/binary,
+ ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
+
+enc_handshake(HandshakeMsg, Version) ->
+ ssl_handshake:encode_handshake(HandshakeMsg, Version).
+
+%%--------------------------------------------------------------------
get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
Body:Length/binary,Rest/binary>>,
#ssl_options{v2_hello_compatible = V2Hello} = Opts, Acc) ->
@@ -219,11 +294,12 @@ decode_handshake(_Version, ?CLIENT_HELLO, Bin, true) ->
decode_handshake(_Version, ?CLIENT_HELLO, Bin, false) ->
decode_hello(Bin);
-decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SID_length), Session_ID:SID_length/binary,
- ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
- ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
- Extensions/binary>>, _) ->
+decode_handshake(_Version, ?CLIENT_HELLO,
+ <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SID_length), Session_ID:SID_length/binary,
+ ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
+ ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
+ Extensions/binary>>, _) ->
DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}),
@@ -268,53 +344,3 @@ decode_v2_hello(<<?BYTE(Major), ?BYTE(Minor),
compression_methods = [?NULL],
extensions = #hello_extensions{}
}.
-
-enc_handshake(#hello_request{}, _Version) ->
- {?HELLO_REQUEST, <<>>};
-enc_handshake(#client_hello{client_version = {Major, Minor},
- random = Random,
- session_id = SessionID,
- cipher_suites = CipherSuites,
- compression_methods = CompMethods,
- extensions = HelloExtensions}, _Version) ->
- SIDLength = byte_size(SessionID),
- BinCompMethods = list_to_binary(CompMethods),
- CmLength = byte_size(BinCompMethods),
- BinCipherSuites = list_to_binary(CipherSuites),
- CsLength = byte_size(BinCipherSuites),
- ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
-
- {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SIDLength), SessionID/binary,
- ?UINT16(CsLength), BinCipherSuites/binary,
- ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
-
-enc_handshake(HandshakeMsg, Version) ->
- ssl_handshake:encode_handshake(HandshakeMsg, Version).
-
-
-handle_client_hello_extensions(Version, Type, Random, CipherSuites,
- HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation, HashSign) ->
- try ssl_handshake:handle_client_hello_extensions(tls_record, Random, CipherSuites,
- HelloExt, Version, SslOpts,
- Session0, ConnectionStates0, Renegotiation) of
- #alert{} = Alert ->
- Alert;
- {Session, ConnectionStates, Protocol, ServerHelloExt} ->
- {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign}
- catch throw:Alert ->
- Alert
- end.
-
-
-handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
- Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) ->
- case ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite,
- Compression, HelloExt, Version,
- SslOpt, ConnectionStates0, Renegotiation) of
- #alert{} = Alert ->
- Alert;
- {ConnectionStates, ProtoExt, Protocol} ->
- {Version, SessionId, ConnectionStates, ProtoExt, Protocol}
- end.
-
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 4ac6cdc6b5..188ec6809d 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -32,22 +32,22 @@
-include("ssl_cipher.hrl").
%% Handling of incoming data
--export([get_tls_records/2, init_connection_states/2]).
+-export([get_tls_records/3, init_connection_states/2]).
%% Encoding TLS records
-export([encode_handshake/3, encode_alert_record/3,
encode_change_cipher_spec/2, encode_data/3]).
-export([encode_plain_text/4]).
+%% Decoding
+-export([decode_cipher_text/3]).
+
%% Protocol version handling
-export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2,
highest_protocol_version/1, highest_protocol_version/2,
is_higher/2, supported_protocol_versions/0,
is_acceptable_version/1, is_acceptable_version/2, hello_version/2]).
-%% Decoding
--export([decode_cipher_text/3]).
-
-export_type([tls_version/0, tls_atom_version/0]).
-type tls_version() :: ssl_record:ssl_version().
@@ -56,13 +56,12 @@
-compile(inline).
%%====================================================================
-%% Internal application API
+%% Handling of incoming data
%%====================================================================
%%--------------------------------------------------------------------
-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled) ->
ssl_record:connection_states().
-%% %
- %
+%%
%% Description: Creates a connection_states record with appropriate
%% values for the initial SSL connection setup.
%%--------------------------------------------------------------------
@@ -76,16 +75,29 @@ init_connection_states(Role, BeastMitigation) ->
pending_write => Pending}.
%%--------------------------------------------------------------------
--spec get_tls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
+-spec get_tls_records(binary(), [tls_version()], binary()) -> {[binary()], binary()} | #alert{}.
%%
%% and returns it as a list of tls_compressed binaries also returns leftover
%% Description: Given old buffer and new data from TCP, packs up a records
%% data
%%--------------------------------------------------------------------
-get_tls_records(Data, <<>>) ->
- get_tls_records_aux(Data, []);
-get_tls_records(Data, Buffer) ->
- get_tls_records_aux(list_to_binary([Buffer, Data]), []).
+get_tls_records(Data, Versions, Buffer) ->
+ BinData = list_to_binary([Buffer, Data]),
+ case erlang:byte_size(BinData) of
+ N when N >= 3 ->
+ case assert_version(BinData, Versions) of
+ true ->
+ get_tls_records_aux(BinData, []);
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+ _ ->
+ get_tls_records_aux(BinData, [])
+ end.
+
+%%====================================================================
+%% Encoding
+%%====================================================================
%%--------------------------------------------------------------------
-spec encode_handshake(iolist(), tls_version(), ssl_record:connection_states()) ->
@@ -141,6 +153,74 @@ encode_data(Frag, Version,
Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).
+%%====================================================================
+%% Decoding
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states(), boolean()) ->
+ {#ssl_tls{}, ssl_record:connection_states()}| #alert{}.
+%%
+%% Description: Decode cipher text
+%%--------------------------------------------------------------------
+decode_cipher_text(#ssl_tls{type = Type, version = Version,
+ fragment = CipherFragment} = CipherText,
+ #{current_read :=
+ #{compression_state := CompressionS0,
+ sequence_number := Seq,
+ cipher_state := CipherS0,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ bulk_cipher_algorithm =
+ BulkCipherAlgo,
+ compression_algorithm = CompAlg}
+ } = ReadState0} = ConnnectionStates0, _) ->
+ AAD = calc_aad(Type, Version, ReadState0),
+ case ssl_cipher:decipher_aead(BulkCipherAlgo, CipherS0, Seq, AAD, CipherFragment, Version) of
+ {PlainFragment, CipherS1} ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
+ PlainFragment, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#{
+ current_read => ReadState0#{
+ cipher_state => CipherS1,
+ sequence_number => Seq + 1,
+ compression_state => CompressionS1}},
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end;
+
+decode_cipher_text(#ssl_tls{type = Type, version = Version,
+ fragment = CipherFragment} = CipherText,
+ #{current_read :=
+ #{compression_state := CompressionS0,
+ sequence_number := Seq,
+ security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg}
+ } = ReadState0} = ConnnectionStates0, PaddingCheck) ->
+ case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of
+ {PlainFragment, Mac, ReadState1} ->
+ MacHash = ssl_cipher:calc_mac_hash(Type, Version, PlainFragment, ReadState1),
+ case ssl_record:is_correct_mac(Mac, MacHash) of
+ true ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
+ PlainFragment, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#{
+ current_read => ReadState1#{
+ sequence_number => Seq + 1,
+ compression_state => CompressionS1}},
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+ #alert{} = Alert ->
+ Alert
+ end.
+
+%%====================================================================
+%% Protocol version handling
+%%====================================================================
%%--------------------------------------------------------------------
-spec protocol_version(tls_atom_version() | tls_version()) ->
@@ -278,11 +358,6 @@ supported_protocol_versions([_|_] = Vsns) ->
end
end.
-%%--------------------------------------------------------------------
-%%
-%% Description: ssl version 2 is not acceptable security risks are too big.
-%%
-%%--------------------------------------------------------------------
-spec is_acceptable_version(tls_version()) -> boolean().
is_acceptable_version({N,_})
when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION ->
@@ -302,6 +377,7 @@ hello_version(Version, _) when Version >= {3, 3} ->
Version;
hello_version(_, Versions) ->
lowest_protocol_version(Versions).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -318,6 +394,19 @@ initial_connection_state(ConnectionEnd, BeastMitigation) ->
server_verify_data => undefined
}.
+assert_version(<<1:1, Length0:15, Data0:Length0/binary, _/binary>>, Versions) ->
+ case Data0 of
+ <<?BYTE(?CLIENT_HELLO), ?BYTE(Major), ?BYTE(Minor), _/binary>> ->
+ %% First check v2_hello_compatible mode is active
+ lists:member({2,0}, Versions) andalso
+ %% andalso we want to negotiate higher version
+ lists:member({Major, Minor}, Versions -- [{2,0}]);
+ _ ->
+ false
+ end;
+assert_version(<<?BYTE(_), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>>, Versions) ->
+ is_acceptable_version({MajVer, MinVer}, Versions).
+
get_tls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Length), Data:Length/binary, Rest/binary>>,
Acc) ->
@@ -361,10 +450,9 @@ get_tls_records_aux(<<1:1, Length0:15, Data0:Length0/binary, Rest/binary>>,
end;
get_tls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
- ?UINT16(Length), _/binary>>,
+ ?UINT16(Length), _/binary>>,
_Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH ->
?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
-
get_tls_records_aux(<<1:1, Length0:15, _/binary>>,_Acc)
when Length0 > ?MAX_CIPHER_TEXT_LENGTH ->
?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
@@ -376,37 +464,17 @@ get_tls_records_aux(Data, Acc) ->
false ->
?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
end.
-
+%%--------------------------------------------------------------------
encode_plain_text(Type, Version, Data, #{current_write := Write0} = ConnectionStates) ->
{CipherFragment, Write1} = do_encode_plain_text(Type, Version, Data, Write0),
{CipherText, Write} = encode_tls_cipher_text(Type, Version, CipherFragment, Write1),
{CipherText, ConnectionStates#{current_write => Write}}.
-lowest_list_protocol_version(Ver, []) ->
- Ver;
-lowest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
- lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest).
-
-highest_list_protocol_version(Ver, []) ->
- Ver;
-highest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
- highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest).
-
encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment, #{sequence_number := Seq} = Write) ->
Length = erlang:iolist_size(Fragment),
{[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment],
Write#{sequence_number => Seq +1}}.
-highest_protocol_version() ->
- highest_protocol_version(supported_protocol_versions()).
-
-lowest_protocol_version() ->
- lowest_protocol_version(supported_protocol_versions()).
-
-sufficient_tlsv1_2_crypto_support() ->
- CryptoSupport = crypto:supports(),
- proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
-
encode_iolist(Type, Data, Version, ConnectionStates0) ->
{ConnectionStates, EncodedMsg} =
lists:foldl(fun(Text, {CS0, Encoded}) ->
@@ -415,6 +483,31 @@ encode_iolist(Type, Data, Version, ConnectionStates0) ->
{CS1, [Enc | Encoded]}
end, {ConnectionStates0, []}, Data),
{lists:reverse(EncodedMsg), ConnectionStates}.
+%%--------------------------------------------------------------------
+do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm = CompAlg}
+ } = WriteState0) ->
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ WriteState1 = WriteState0#{compression_state => CompS1},
+ AAD = calc_aad(Type, Version, WriteState1),
+ ssl_record:cipher_aead(Version, Comp, WriteState1, AAD);
+do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
+ security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg}
+ }= WriteState0) ->
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ WriteState1 = WriteState0#{compression_state => CompS1},
+ MacHash = ssl_cipher:calc_mac_hash(Type, Version, Comp, WriteState1),
+ ssl_record:cipher(Version, Comp, WriteState1, MacHash);
+do_encode_plain_text(_,_,_,CS) ->
+ exit({cs, CS}).
+%%--------------------------------------------------------------------
+calc_aad(Type, {MajVer, MinVer},
+ #{sequence_number := SeqNo}) ->
+ <<?UINT64(SeqNo), ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are
%% not vulnerable to this attack.
@@ -440,89 +533,25 @@ do_split_bin(Bin, ChunkSize, Acc) ->
_ ->
lists:reverse(Acc, [Bin])
end.
-
%%--------------------------------------------------------------------
--spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states(), boolean()) ->
- {#ssl_tls{}, ssl_record:connection_states()}| #alert{}.
-%%
-%% Description: Decode cipher text
-%%--------------------------------------------------------------------
-decode_cipher_text(#ssl_tls{type = Type, version = Version,
- fragment = CipherFragment} = CipherText,
- #{current_read :=
- #{compression_state := CompressionS0,
- sequence_number := Seq,
- cipher_state := CipherS0,
- security_parameters :=
- #security_parameters{
- cipher_type = ?AEAD,
- bulk_cipher_algorithm =
- BulkCipherAlgo,
- compression_algorithm = CompAlg}
- } = ReadState0} = ConnnectionStates0, _) ->
- AAD = calc_aad(Type, Version, ReadState0),
- case ssl_cipher:decipher_aead(BulkCipherAlgo, CipherS0, Seq, AAD, CipherFragment, Version) of
- {PlainFragment, CipherS1} ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
- PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#{
- current_read => ReadState0#{
- cipher_state => CipherS1,
- sequence_number => Seq + 1,
- compression_state => CompressionS1}},
- {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
- #alert{} = Alert ->
- Alert
- end;
+lowest_list_protocol_version(Ver, []) ->
+ Ver;
+lowest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest).
-decode_cipher_text(#ssl_tls{type = Type, version = Version,
- fragment = CipherFragment} = CipherText,
- #{current_read :=
- #{compression_state := CompressionS0,
- sequence_number := Seq,
- security_parameters :=
- #security_parameters{compression_algorithm = CompAlg}
- } = ReadState0} = ConnnectionStates0, PaddingCheck) ->
- case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of
- {PlainFragment, Mac, ReadState1} ->
- MacHash = ssl_cipher:calc_mac_hash(Type, Version, PlainFragment, ReadState1),
- case ssl_record:is_correct_mac(Mac, MacHash) of
- true ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
- PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#{
- current_read => ReadState1#{
- sequence_number => Seq + 1,
- compression_state => CompressionS1}},
- {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
- false ->
- ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
- end;
- #alert{} = Alert ->
- Alert
- end.
+highest_list_protocol_version(Ver, []) ->
+ Ver;
+highest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest).
+
+highest_protocol_version() ->
+ highest_protocol_version(supported_protocol_versions()).
+
+lowest_protocol_version() ->
+ lowest_protocol_version(supported_protocol_versions()).
+
+sufficient_tlsv1_2_crypto_support() ->
+ CryptoSupport = crypto:supports(),
+ proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
-do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
- security_parameters :=
- #security_parameters{
- cipher_type = ?AEAD,
- compression_algorithm = CompAlg}
- } = WriteState0) ->
- {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#{compression_state => CompS1},
- AAD = calc_aad(Type, Version, WriteState1),
- ssl_record:cipher_aead(Version, Comp, WriteState1, AAD);
-do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
- security_parameters :=
- #security_parameters{compression_algorithm = CompAlg}
- }= WriteState0) ->
- {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#{compression_state => CompS1},
- MacHash = ssl_cipher:calc_mac_hash(Type, Version, Comp, WriteState1),
- ssl_record:cipher(Version, Comp, WriteState1, MacHash);
-do_encode_plain_text(_,_,_,CS) ->
- exit({cs, CS}).
-calc_aad(Type, {MajVer, MinVer},
- #{sequence_number := SeqNo}) ->
- <<?UINT64(SeqNo), ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
diff --git a/lib/ssl/src/tls_socket.erl b/lib/ssl/src/tls_socket.erl
index e76d9c100a..453a908401 100644
--- a/lib/ssl/src/tls_socket.erl
+++ b/lib/ssl/src/tls_socket.erl
@@ -27,7 +27,7 @@
-export([send/3, listen/3, accept/3, socket/5, connect/4, upgrade/3,
setopts/3, getopts/3, getstat/3, peername/2, sockname/2, port/2]).
-export([split_options/1, get_socket_opts/3]).
--export([emulated_options/0, internal_inet_values/0, default_inet_values/0,
+-export([emulated_options/0, emulated_options/1, internal_inet_values/0, default_inet_values/0,
init/1, start_link/3, terminate/2, inherit_tracker/3,
emulated_socket_options/2, get_emulated_opts/1,
set_emulated_opts/2, get_all_opts/1, handle_call/3, handle_cast/2,
@@ -170,6 +170,9 @@ port(Transport, Socket) ->
emulated_options() ->
[mode, packet, active, header, packet_size].
+emulated_options(Opts) ->
+ emulated_options(Opts, internal_inet_values(), default_inet_values()).
+
internal_inet_values() ->
[{packet_size,0}, {packet, 0}, {header, 0}, {active, false}, {mode,binary}].
@@ -328,3 +331,41 @@ emulated_socket_options(InetValues, #socket_options{
packet = proplists:get_value(packet, InetValues, Packet),
packet_size = proplists:get_value(packet_size, InetValues, Size)
}.
+
+emulated_options([{mode, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(mode, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(mode, Emulated)]);
+emulated_options([{header, Value} = Opt | Opts], Inet, Emulated) ->
+ validate_inet_option(header, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(header, Emulated)]);
+emulated_options([{active, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(active, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(active, Emulated)]);
+emulated_options([{packet, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(packet, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(packet, Emulated)]);
+emulated_options([{packet_size, Value} = Opt | Opts], Inet, Emulated) ->
+ validate_inet_option(packet_size, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(packet_size, Emulated)]);
+emulated_options([Opt|Opts], Inet, Emulated) ->
+ emulated_options(Opts, [Opt|Inet], Emulated);
+emulated_options([], Inet,Emulated) ->
+ {Inet, Emulated}.
+
+validate_inet_option(mode, Value)
+ when Value =/= list, Value =/= binary ->
+ throw({error, {options, {mode,Value}}});
+validate_inet_option(packet, Value)
+ when not (is_atom(Value) orelse is_integer(Value)) ->
+ throw({error, {options, {packet,Value}}});
+validate_inet_option(packet_size, Value)
+ when not is_integer(Value) ->
+ throw({error, {options, {packet_size,Value}}});
+validate_inet_option(header, Value)
+ when not is_integer(Value) ->
+ throw({error, {options, {header,Value}}});
+validate_inet_option(active, Value)
+ when Value =/= true, Value =/= false, Value =/= once ->
+ throw({error, {options, {active,Value}}});
+validate_inet_option(_, _) ->
+ ok.
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index c7e2f402af..aa01552c39 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -44,6 +44,7 @@ MODULES = \
ssl_certificate_verify_SUITE\
ssl_crl_SUITE\
ssl_dist_SUITE \
+ ssl_engine_SUITE\
ssl_handshake_SUITE \
ssl_npn_hello_SUITE \
ssl_npn_handshake_SUITE \
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 9efde4752f..ce62017a7e 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -83,13 +83,14 @@ groups() ->
].
tls_versions_groups ()->
- [{group, renegotiate}, %% Should be in all_versions_groups not fixed for DTLS yet
+ [
{group, api_tls},
{group, tls_ciphers},
{group, error_handling_tests_tls}].
all_versions_groups ()->
[{group, api},
+ {group, renegotiate},
{group, ciphers},
{group, ciphers_ec},
{group, error_handling_tests}].
@@ -162,7 +163,8 @@ api_tests() ->
server_name_indication_option,
accept_pool,
prf,
- socket_options
+ socket_options,
+ cipher_suites
].
api_tests_tls() ->
@@ -206,7 +208,7 @@ tls_cipher_tests() ->
rc4_ecdsa_cipher_suites].
cipher_tests() ->
- [cipher_suites,
+ [old_cipher_suites,
cipher_suites_mix,
ciphers_rsa_signed_certs,
ciphers_rsa_signed_certs_openssl_names,
@@ -279,8 +281,11 @@ end_per_suite(_Config) ->
init_per_group(GroupName, Config) when GroupName == basic_tls;
GroupName == options_tls;
+ GroupName == options;
GroupName == basic;
- GroupName == options ->
+ GroupName == session;
+ GroupName == error_handling_tests_tls
+ ->
ssl_test_lib:clean_tls_version(Config);
init_per_group(GroupName, Config) ->
case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of
@@ -380,12 +385,12 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
TestCase == anonymous_cipher_suites;
TestCase == psk_anon_cipher_suites;
TestCase == psk_anon_with_hint_cipher_suites;
- TestCase == srp_cipher_suites,
- TestCase == srp_anon_cipher_suites,
- TestCase == srp_dsa_cipher_suites,
- TestCase == des_rsa_cipher_suites,
- TestCase == des_ecdh_rsa_cipher_suites,
- TestCase == versions_option,
+ TestCase == srp_cipher_suites;
+ TestCase == srp_anon_cipher_suites;
+ TestCase == srp_dsa_cipher_suites;
+ TestCase == des_rsa_cipher_suites;
+ TestCase == des_ecdh_rsa_cipher_suites;
+ TestCase == versions_option;
TestCase == tls_tcp_connect_big ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 60}),
@@ -426,6 +431,12 @@ init_per_testcase(rizzo_disabled, Config) ->
ct:timetrap({seconds, 60}),
rizzo_add_mitigation_option(disabled, Config);
+init_per_testcase(TestCase, Config) when TestCase == no_reuses_session_server_restart_new_cert_file;
+ TestCase == no_reuses_session_server_restart_new_cert ->
+ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
+ ct:timetrap({seconds, 15}),
+ Config;
+
init_per_testcase(prf, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
ct:timetrap({seconds, 40}),
@@ -692,8 +703,6 @@ secret_connection_info(Config) when is_list(Config) ->
ct:log("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
-
- Version = ssl_test_lib:protocol_version(Config),
ssl_test_lib:check_result(Server, true, Client, true),
@@ -1118,11 +1127,16 @@ fallback(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
cipher_format() ->
- [{doc, "Test that cipher conversion from tuples to binarys works"}].
+ [{doc, "Test that cipher conversion from maps | tuples | stings to binarys works"}].
cipher_format(Config) when is_list(Config) ->
- {ok, Socket} = ssl:listen(0, [{ciphers, ssl:cipher_suites()}]),
- ssl:close(Socket).
-
+ {ok, Socket0} = ssl:listen(0, [{ciphers, ssl:cipher_suites(default, 'tlsv1.2')}]),
+ ssl:close(Socket0),
+ %% Legacy
+ {ok, Socket1} = ssl:listen(0, [{ciphers, ssl:cipher_suites()}]),
+ ssl:close(Socket1),
+ {ok, Socket2} = ssl:listen(0, [{ciphers, ssl:cipher_suites(openssl)}]),
+ ssl:close(Socket2).
+
%%--------------------------------------------------------------------
peername() ->
@@ -1273,10 +1287,62 @@ sockname_result(S) ->
ssl:sockname(S).
%%--------------------------------------------------------------------
+
cipher_suites() ->
- [{doc,"Test API function cipher_suites/0"}].
+ [{doc,"Test API function cipher_suites/2, filter_cipher_suites/2"
+ " and prepend|append_cipher_suites/2"}].
cipher_suites(Config) when is_list(Config) ->
+ Version = ssl_test_lib:protocol_version(Config),
+ All = [_|_] = ssl:cipher_suites(all, Version),
+ Default = [_|_] = ssl:cipher_suites(default, Version),
+ Anonymous = [_|_] = ssl:cipher_suites(anonymous, Version),
+ true = length(Default) < length(All),
+ Filters = [{key_exchange,
+ fun(dhe_rsa) ->
+ true;
+ (_) ->
+ false
+ end
+ },
+ {cipher,
+ fun(aes_256_cbc) ->
+ true;
+ (_) ->
+ false
+ end
+ },
+ {mac,
+ fun(sha) ->
+ true;
+ (_) ->
+ false
+ end
+ }
+ ],
+ Cipher = #{cipher => aes_256_cbc,
+ key_exchange => dhe_rsa,
+ mac => sha,
+ prf => default_prf},
+ [Cipher] = ssl:filter_cipher_suites(All, Filters),
+ [Cipher | Rest0] = ssl:prepend_cipher_suites([Cipher], Default),
+ [Cipher | Rest0] = ssl:prepend_cipher_suites(Filters, Default),
+ true = lists:member(Cipher, Default),
+ false = lists:member(Cipher, Rest0),
+ [Cipher | Rest1] = lists:reverse(ssl:append_cipher_suites([Cipher], Default)),
+ [Cipher | Rest1] = lists:reverse(ssl:append_cipher_suites(Filters, Default)),
+ true = lists:member(Cipher, Default),
+ false = lists:member(Cipher, Rest1),
+ [] = lists:dropwhile(fun(X) -> not lists:member(X, Default) end, Anonymous),
+ [] = lists:dropwhile(fun(X) -> not lists:member(X, All) end, Anonymous).
+
+
+%%--------------------------------------------------------------------
+
+old_cipher_suites() ->
+ [{doc,"Test API function cipher_suites/0"}].
+
+old_cipher_suites(Config) when is_list(Config) ->
MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha},
[_|_] = Suites = ssl:cipher_suites(),
true = lists:member(MandatoryCipherSuite, Suites),
@@ -3758,9 +3824,23 @@ rizzo() ->
vunrable to Rizzo/Dungon attack"}].
rizzo(Config) when is_list(Config) ->
- Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion),
+ [{key_exchange,
+ fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
+ true;
+ (_) ->
+ false
+ end},
+ {cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
+
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_rizzo, []}).
%%--------------------------------------------------------------------
@@ -3772,8 +3852,13 @@ no_rizzo_rc4(Config) when is_list(Config) ->
Version = proplists:get_value(name, Prop),
NVersion = ssl_test_lib:protocol_version(Config, tuple),
%% Test uses RSA certs
- Ciphers = ssl_test_lib:rc4_suites(NVersion) -- [{ecdhe_ecdsa,rc4_128,sha},
- {ecdh_ecdsa,rc4_128,sha}],
+ Ciphers = ssl:filter_cipher_suites(ssl_test_lib:rc4_suites(NVersion),
+ [{key_exchange,
+ fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
+ true;
+ (_) ->
+ false
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -3784,10 +3869,21 @@ rizzo_one_n_minus_one(Config) when is_list(Config) ->
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
NVersion = ssl_test_lib:protocol_version(Config, tuple),
- AllSuites = ssl_test_lib:available_suites(NVersion),
- Ciphers = [X || X ={_,Y,_} <- AllSuites, Y =/= rc4_128],
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion),
+ [{key_exchange,
+ fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
+ true;
+ (_) ->
+ false
+ end},
+ {cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
- {?MODULE, send_recv_result_active_rizzo, []}).
+ {?MODULE, send_recv_result_active_rizzo, []}).
rizzo_zero_n() ->
[{doc,"Test that the 0/n-split mitigation of Rizzo/Dungon attack can be explicitly selected"}].
@@ -3796,8 +3892,13 @@ rizzo_zero_n(Config) when is_list(Config) ->
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
NVersion = ssl_test_lib:protocol_version(Config, tuple),
- AllSuites = ssl_test_lib:available_suites(NVersion),
- Ciphers = [X || X ={_,Y,_} <- AllSuites, Y =/= rc4_128],
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion),
+ [{cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -3805,9 +3906,16 @@ rizzo_disabled() ->
[{doc,"Test that the mitigation of Rizzo/Dungon attack can be explicitly disabled"}].
rizzo_disabled(Config) when is_list(Config) ->
- Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion),
+ [{cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -4582,19 +4690,21 @@ rizzo_test(Cipher, Config, Version, Mfa) ->
[{Cipher, Error}]
end.
-client_server_opts({KeyAlgo,_,_}, Config)
+client_server_opts(#{key_exchange := KeyAlgo}, Config)
when KeyAlgo == rsa orelse
KeyAlgo == dhe_rsa orelse
- KeyAlgo == ecdhe_rsa ->
+ KeyAlgo == ecdhe_rsa orelse
+ KeyAlgo == rsa_psk orelse
+ KeyAlgo == srp_rsa ->
{ssl_test_lib:ssl_options(client_opts, Config),
ssl_test_lib:ssl_options(server_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss ->
+client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss ->
{ssl_test_lib:ssl_options(client_dsa_opts, Config),
ssl_test_lib:ssl_options(server_dsa_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa ->
+client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa ->
{ssl_test_lib:ssl_options(client_opts, Config),
ssl_test_lib:ssl_options(server_ecdsa_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_rsa ->
+client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_rsa ->
{ssl_test_lib:ssl_options(client_opts, Config),
ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}.
diff --git a/lib/ssl/test/ssl_engine_SUITE.erl b/lib/ssl/test/ssl_engine_SUITE.erl
new file mode 100644
index 0000000000..bc221d35fd
--- /dev/null
+++ b/lib/ssl/test/ssl_engine_SUITE.erl
@@ -0,0 +1,142 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_engine_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ [
+ private_key
+ ].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ case crypto:get_test_engine() of
+ {ok, EngineName} ->
+ try crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, EngineName},
+ <<"LOAD">>],
+ []) of
+ {ok, Engine} ->
+ [{engine, Engine} |Config];
+ {error, Reason} ->
+ ct:pal("Reason ~p", [Reason]),
+ {skip, "No dynamic engine support"}
+ catch error:notsup ->
+ {skip, "No engine support in OpenSSL"}
+ end;
+ {error, notexist} ->
+ {skip, "Test engine not found"}
+ end
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(Config) ->
+ Engine = proplists:get_value(engine, Config),
+ crypto:engine_unload(Engine),
+ ssl:stop(),
+ application:stop(crypto).
+
+
+init_per_testcase(_TestCase, Config) ->
+ ssl:stop(),
+ ssl:start(),
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Config.
+
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+private_key(Config) when is_list(Config) ->
+ ClientFileBase = filename:join([proplists:get_value(priv_dir, Config), "client_engine"]),
+ ServerFileBase = filename:join([proplists:get_value(priv_dir, Config), "server_engine"]),
+ #{server_config := ServerConf,
+ client_config := ClientConf} = GenCertData =
+ public_key:pkix_test_data(#{server_chain =>
+ #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}],
+ intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]],
+ peer => [{key, ssl_test_lib:hardcode_rsa_key(3)}
+ ]},
+ client_chain =>
+ #{root => [{key, ssl_test_lib:hardcode_rsa_key(4)}],
+ intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(5)}]],
+ peer => [{key, ssl_test_lib:hardcode_rsa_key(6)}]}}),
+ [{server_config, FileServerConf},
+ {client_config, FileClientConf}] =
+ x509_test:gen_pem_config_files(GenCertData, ClientFileBase, ServerFileBase),
+
+ Engine = proplists:get_value(engine, Config),
+
+ ClientKey = engine_key(FileClientConf),
+ ServerKey = engine_key(FileServerConf),
+
+ EngineClientConf = [{key, #{algorithm => rsa,
+ engine => Engine,
+ key_id => ClientKey}} | proplists:delete(key, ClientConf)],
+
+ EngineServerConf = [{key, #{algorithm => rsa,
+ engine => Engine,
+ key_id => ServerKey}} | proplists:delete(key, ServerConf)],
+ %% Test with engine
+ test_tls_connection(EngineServerConf, EngineClientConf, Config),
+ %% Test that sofware fallback is available
+ test_tls_connection(ServerConf, [{reuse_sessions, false} |ClientConf], Config).
+
+engine_key(Conf) ->
+ FileStr = proplists:get_value(keyfile, Conf),
+ list_to_binary(FileStr).
+
+
+test_tls_connection(ServerConf, ClientConf, Config) ->
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_peer}
+ | ServerConf]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_peer} | ClientConf]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index 408d62ce9c..3261244ace 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -63,8 +63,10 @@ groups() ->
{'tlsv1.1', [], socket_packet_tests() ++ protocol_packet_tests()},
{'tlsv1', [], socket_packet_tests() ++ protocol_packet_tests()},
{'sslv3', [], socket_packet_tests() ++ protocol_packet_tests()},
- {'dtlsv1.2', [], protocol_packet_tests()},
- {'dtlsv1', [], protocol_packet_tests()}
+ %% We will not support any packet types if the transport is
+ %% not reliable. We might support it for DTLS over SCTP in the future
+ {'dtlsv1.2', [], [reject_packet_opt]},
+ {'dtlsv1', [], [reject_packet_opt]}
].
socket_packet_tests() ->
@@ -1924,6 +1926,25 @@ header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+reject_packet_opt() ->
+ [{doc,"Test packet option is rejected for DTLS over udp"}].
+
+reject_packet_opt(Config) when is_list(Config) ->
+
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+
+ {error,{options,{not_supported,{packet,4}}}} =
+ ssl:listen(9999, [{packet, 4} | ServerOpts]),
+ {error,{options,{not_supported,{packet_size,1}}}} =
+ ssl:listen(9999, [{packet_size, 1} | ServerOpts]),
+ {error,{options,{not_supported,{header,1}}}} =
+ ssl:listen(9999, [{header, 1} | ServerOpts]),
+
+ client_reject_packet_opt(Config, {packet,4}),
+ client_reject_packet_opt(Config, {packet_size, 1}),
+ client_reject_packet_opt(Config, {header, 1}).
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -2245,3 +2266,23 @@ add_tpkt_header(IOList) when is_list(IOList) ->
Binary = list_to_binary(IOList),
L = size(Binary) + 4,
[3, 0, ((L) bsr 8) band 16#ff, (L) band 16#ff , Binary].
+
+
+client_reject_packet_opt(Config, PacketOpt) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result_msg ,[]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result_msg, []}},
+ {options, [PacketOpt |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, {error, {options, {not_supported, PacketOpt}}}).
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 13265debb1..f9cc976815 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1025,48 +1025,56 @@ string_regex_filter(_Str, _Search) ->
false.
anonymous_suites(Version) ->
- Suites = [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:anonymous_suites(Version)],
- ssl_cipher:filter_suites(Suites).
-
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:anonymous_suites(Version)],[]).
psk_suites(Version) ->
- Suites = [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:psk_suites(Version)],
- ssl_cipher:filter_suites(Suites).
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:psk_suites(Version)], []).
psk_anon_suites(Version) ->
- Suites = [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite)],
- ssl_cipher:filter_suites(Suites).
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:psk_suites_anon(Version)],
+ [{key_exchange,
+ fun(psk) ->
+ true;
+ (psk_dhe) ->
+ true;
+ (_) ->
+ false
+ end}]).
srp_suites() ->
- Suites =
- [{srp_anon, '3des_ede_cbc', sha},
- {srp_rsa, '3des_ede_cbc', sha},
- {srp_anon, aes_128_cbc, sha},
- {srp_rsa, aes_128_cbc, sha},
- {srp_anon, aes_256_cbc, sha},
- {srp_rsa, aes_256_cbc, sha}],
- ssl_cipher:filter_suites(Suites).
-
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ [{key_exchange,
+ fun(srp_rsa) ->
+ true;
+ (_) ->
+ false
+ end}]).
srp_anon_suites() ->
- Suites =
- [{srp_anon, '3des_ede_cbc', sha},
- {srp_anon, aes_128_cbc, sha},
- {srp_anon, aes_256_cbc, sha}],
- ssl_cipher:filter_suites(Suites).
-
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites_anon()],
+ []).
srp_dss_suites() ->
- Suites =
- [{srp_dss, '3des_ede_cbc', sha},
- {srp_dss, aes_128_cbc, sha},
- {srp_dss, aes_256_cbc, sha}],
- ssl_cipher:filter_suites(Suites).
-
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ [{key_exchange,
+ fun(srp_dss) ->
+ true;
+ (_) ->
+ false
+ end}]).
rc4_suites(Version) ->
- Suites = [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:rc4_suites(Version)],
- ssl_cipher:filter_suites(Suites).
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <-ssl_cipher:rc4_suites(Version)], []).
des_suites(Version) ->
- Suites = ssl_cipher:des_suites(Version),
- ssl_cipher:filter_suites(Suites).
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <-ssl_cipher:des_suites(Version)], []).
+
+tuple_to_map({Kex, Cipher, Mac}) ->
+ #{key_exchange => Kex,
+ cipher => Cipher,
+ mac => Mac,
+ prf => default_prf};
+tuple_to_map({Kex, Cipher, Mac, Prf}) ->
+ #{key_exchange => Kex,
+ cipher => Cipher,
+ mac => Mac,
+ prf => Prf}.
pem_to_der(File) ->
{ok, PemBin} = file:read_file(File),
@@ -1292,6 +1300,32 @@ cipher_restriction(Config0) ->
Config0
end.
+openssl_dsa_support() ->
+ case os:cmd("openssl version") of
+ "LibreSSL 2.6.1" ++ _ ->
+ true;
+ "LibreSSL 2.6.2" ++ _ ->
+ true;
+ "LibreSSL 2.6" ++ _ ->
+ false;
+ "LibreSSL 2.4" ++ _ ->
+ true;
+ "LibreSSL 2.3" ++ _ ->
+ true;
+ "LibreSSL 2.2" ++ _ ->
+ true;
+ "LibreSSL 2.1" ++ _ ->
+ true;
+ "LibreSSL 2.0" ++ _ ->
+ true;
+ "LibreSSL" ++ _ ->
+ false;
+ "OpenSSL 1.0.1" ++ Rest ->
+ hd(Rest) >= s;
+ _ ->
+ true
+ end.
+
check_sane_openssl_version(Version) ->
case supports_ssl_tls_version(Version) of
true ->
@@ -1329,8 +1363,9 @@ enough_openssl_crl_support(_) -> true.
wait_for_openssl_server(Port, tls) ->
do_wait_for_openssl_tls_server(Port, 10);
-wait_for_openssl_server(Port, dtls) ->
- do_wait_for_openssl_dtls_server(Port, 10).
+wait_for_openssl_server(_Port, dtls) ->
+ ok. %% No need to wait for DTLS over UDP server
+ %% client will retransmitt until it is up.
do_wait_for_openssl_tls_server(_, 0) ->
exit(failed_to_connect_to_openssl);
@@ -1343,21 +1378,6 @@ do_wait_for_openssl_tls_server(Port, N) ->
do_wait_for_openssl_tls_server(Port, N-1)
end.
-do_wait_for_openssl_dtls_server(_, 0) ->
- %%exit(failed_to_connect_to_openssl);
- ok;
-do_wait_for_openssl_dtls_server(Port, N) ->
- %% case gen_udp:open(0) of
- %% {ok, S} ->
- %% gen_udp:connect(S, "localhost", Port),
- %% gen_udp:close(S);
- %% _ ->
- %% ct:sleep(?SLEEP),
- %% do_wait_for_openssl_dtls_server(Port, N-1)
- %% end.
- ct:sleep(500),
- do_wait_for_openssl_dtls_server(Port, N-1).
-
version_flag(tlsv1) ->
"-tls1";
version_flag('tlsv1.1') ->
@@ -1384,7 +1404,9 @@ filter_suites(Ciphers0, AtomVersion) ->
Supported0 = ssl_cipher:suites(Version)
++ ssl_cipher:anonymous_suites(Version)
++ ssl_cipher:psk_suites(Version)
+ ++ ssl_cipher:psk_suites_anon(Version)
++ ssl_cipher:srp_suites()
+ ++ ssl_cipher:srp_suites_anon()
++ ssl_cipher:rc4_suites(Version),
Supported1 = ssl_cipher:filter_suites(Supported0),
Supported2 = [ssl_cipher:erl_suite_definition(S) || S <- Supported1],
@@ -1654,78 +1676,3 @@ hardcode_dsa_key(3) ->
y = 48598545580251057979126570873881530215432219542526130654707948736559463436274835406081281466091739849794036308281564299754438126857606949027748889019480936572605967021944405048011118039171039273602705998112739400664375208228641666852589396502386172780433510070337359132965412405544709871654840859752776060358,
x = 1457508827177594730669011716588605181448418352823}.
-dtls_hello() ->
- [1,
- <<0,1,4>>,
- <<0,0>>,
- <<0,0,0>>,
- <<0,1,4>>,
- <<254,253,88,
- 156,129,61,
- 131,216,15,
- 131,194,242,
- 46,154,190,
- 20,228,234,
- 234,150,44,
- 62,96,96,103,
- 127,95,103,
- 23,24,42,138,
- 13,142,32,57,
- 230,177,32,
- 210,154,152,
- 188,121,134,
- 136,53,105,
- 118,96,106,
- 103,231,223,
- 133,10,165,
- 50,32,211,
- 227,193,14,
- 181,143,48,
- 66,0,0,100,0,
- 255,192,44,
- 192,48,192,
- 36,192,40,
- 192,46,192,
- 50,192,38,
- 192,42,0,159,
- 0,163,0,107,
- 0,106,0,157,
- 0,61,192,43,
- 192,47,192,
- 35,192,39,
- 192,45,192,
- 49,192,37,
- 192,41,0,158,
- 0,162,0,103,
- 0,64,0,156,0,
- 60,192,10,
- 192,20,0,57,
- 0,56,192,5,
- 192,15,0,53,
- 192,8,192,18,
- 0,22,0,19,
- 192,3,192,13,
- 0,10,192,9,
- 192,19,0,51,
- 0,50,192,4,
- 192,14,0,47,
- 1,0,0,86,0,0,
- 0,14,0,12,0,
- 0,9,108,111,
- 99,97,108,
- 104,111,115,
- 116,0,10,0,
- 58,0,56,0,14,
- 0,13,0,25,0,
- 28,0,11,0,12,
- 0,27,0,24,0,
- 9,0,10,0,26,
- 0,22,0,23,0,
- 8,0,6,0,7,0,
- 20,0,21,0,4,
- 0,5,0,18,0,
- 19,0,1,0,2,0,
- 3,0,15,0,16,
- 0,17,0,11,0,
- 2,1,0>>].
-
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 2e1a0b94ea..f091c8786e 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -90,9 +90,9 @@ dtls_all_versions_tests() ->
erlang_client_openssl_server_dsa_cert,
erlang_server_openssl_client_dsa_cert,
erlang_server_openssl_client_reuse_session,
- %%erlang_client_openssl_server_renegotiate,
- %%erlang_client_openssl_server_nowrap_seqnum,
- %%erlang_server_openssl_client_nowrap_seqnum,
+ erlang_client_openssl_server_renegotiate,
+ erlang_client_openssl_server_nowrap_seqnum,
+ erlang_server_openssl_client_nowrap_seqnum,
erlang_client_openssl_server_no_server_ca_cert,
erlang_client_openssl_server_client_cert,
erlang_server_openssl_client_client_cert,
@@ -143,10 +143,15 @@ init_per_suite(Config0) ->
try crypto:start() of
ok ->
ssl_test_lib:clean_start(),
-
- Config1 = ssl_test_lib:make_rsa_cert(Config0),
- Config2 = ssl_test_lib:make_dsa_cert(Config1),
- ssl_test_lib:cipher_restriction(Config2)
+ Config =
+ case ssl_test_lib:openssl_dsa_support() of
+ true ->
+ Config1 = ssl_test_lib:make_rsa_cert(Config0),
+ ssl_test_lib:make_dsa_cert(Config1);
+ false ->
+ ssl_test_lib:make_rsa_cert(Config0)
+ end,
+ ssl_test_lib:cipher_restriction(Config)
catch _:_ ->
{skip, "Crypto did not start"}
end
@@ -199,15 +204,27 @@ init_per_testcase(expired_session, Config) ->
ssl:start(),
Config;
-init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs;
- TestCase == ciphers_dsa_signed_certs ->
- ct:timetrap({seconds, 60}),
- special_init(TestCase, Config);
-
+init_per_testcase(TestCase, Config) when
+ TestCase == ciphers_dsa_signed_certs;
+ TestCase == erlang_client_openssl_server_dsa_cert;
+ TestCase == erlang_server_openssl_client_dsa_cert;
+ TestCase == erlang_client_openssl_server_dsa_cert;
+ TestCase == erlang_server_openssl_client_dsa_cert ->
+ case ssl_test_lib:openssl_dsa_support() of
+ true ->
+ special_init(TestCase, Config);
+ false ->
+ {skip, "DSA not supported by OpenSSL"}
+ end;
init_per_testcase(TestCase, Config) ->
- ct:timetrap({seconds, 20}),
+ ct:timetrap({seconds, 35}),
special_init(TestCase, Config).
+special_init(TestCase, Config) when
+ TestCase == ciphers_rsa_signed_certs;
+ TestCase == ciphers_dsa_signed_certs->
+ ct:timetrap({seconds, 90}),
+ Config;
special_init(TestCase, Config)
when TestCase == erlang_client_openssl_server_renegotiate;
TestCase == erlang_client_openssl_server_nowrap_seqnum;
@@ -1016,7 +1033,7 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
consume_port_exit(OpenSslPort),
- ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}),
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "bad record mac"}}),
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
ssl2_erlang_server_openssl_client_comp() ->
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index cf6481d14c..2650399eea 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 8.2.2
+SSL_VSN = 8.2.3
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index 26602764a6..e7ea38c5c3 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
+# Copyright Ericsson AB 1997-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -103,8 +103,9 @@ XML_REF3_FILES = \
XML_REF6_FILES = stdlib_app.xml
-XML_PART_FILES = part.xml part_notes.xml part_notes_history.xml
-XML_CHAPTER_FILES = io_protocol.xml unicode_usage.xml notes.xml notes_history.xml assert_hrl.xml
+XML_PART_FILES = part.xml
+XML_CHAPTER_FILES = introduction.xml io_protocol.xml unicode_usage.xml \
+ notes.xml assert_hrl.xml
BOOK_FILES = book.xml
@@ -131,9 +132,9 @@ SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
TOP_SPECS_FILE = specs.xml
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
SPECS_FLAGS = -I../../include -I../../../kernel/include
@@ -150,7 +151,7 @@ html: $(HTML_REF_MAN_FILE)
man: $(MAN3_FILES) $(MAN6_FILES)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
@@ -158,7 +159,7 @@ clean clean_docs:
rm -f $(MAN6DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f $(SPECDIR)/*
- rm -f errs core *~
+ rm -f errs core *~
$(SPECDIR)/specs_erl_id_trans.xml:
escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
@@ -166,7 +167,7 @@ $(SPECDIR)/specs_erl_id_trans.xml:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/stdlib/doc/src/assert_hrl.xml b/lib/stdlib/doc/src/assert_hrl.xml
index ea23cca2ee..33f29f38da 100644
--- a/lib/stdlib/doc/src/assert_hrl.xml
+++ b/lib/stdlib/doc/src/assert_hrl.xml
@@ -93,7 +93,7 @@ erlc -DNOASSERT=true *.erl</code>
<taglist>
<tag><c>assert(BoolExpr)</c></tag>
<item></item>
- <tag><c>URKAassert(BoolExpr, Comment)</c></tag>
+ <tag><c>assert(BoolExpr, Comment)</c></tag>
<item>
<p>Tests that <c>BoolExpr</c> completes normally returning
<c>true</c>.</p>
diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml
index 5332d7aba5..db96beed6c 100644
--- a/lib/stdlib/doc/src/digraph.xml
+++ b/lib/stdlib/doc/src/digraph.xml
@@ -170,6 +170,10 @@
<p>If the edge would create a cycle in
an <seealso marker="#acyclic_digraph">acyclic digraph</seealso>,
<c>{error,&nbsp;{bad_edge,&nbsp;<anno>Path</anno>}}</c> is returned.
+ If <c><anno>G</anno></c> already has an edge with value
+ <c><anno>E</anno></c> connecting a different pair of vertices,
+ <c>{error,&nbsp;{bad_edge,&nbsp;[<anno>V1</anno>,&nbsp;<anno>V2</anno>]}}</c>
+ is returned.
If either of <c><anno>V1</anno></c> or <c><anno>V2</anno></c> is not
a vertex of digraph <c><anno>G</anno></c>,
<c>{error,&nbsp;{bad_vertex,&nbsp;</c><anno>V</anno><c>}}</c> is
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 95af2b77a5..51e35cd2df 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -325,7 +325,7 @@
<p><c><anno>Acc0</anno></c> is returned if the table is empty.
This function is similar to
<seealso marker="lists#foldl/3"><c>lists:foldl/3</c></seealso>.
- The table elements are traversed is unspecified order, except for
+ The table elements are traversed in an unspecified order, except for
<c>ordered_set</c> tables, where they are traversed first to last.</p>
<p>If <c><anno>Function</anno></c> inserts objects into the table,
or another
@@ -341,7 +341,7 @@
<p><c><anno>Acc0</anno></c> is returned if the table is empty.
This function is similar to
<seealso marker="lists#foldr/3"><c>lists:foldr/3</c></seealso>.
- The table elements are traversed is unspecified order, except for
+ The table elements are traversed in an unspecified order, except for
<c>ordered_set</c> tables, where they are traversed last to first.</p>
<p>If <c><anno>Function</anno></c> inserts objects into the table,
or another
@@ -963,11 +963,11 @@ ets:is_compiled_ms(Broken).</code>
<func>
<name name="match_spec_run" arity="2"/>
<fsummary>Perform matching, using a compiled match specification on a
- list of tuples.</fsummary>
+ list of terms.</fsummary>
<desc>
<p>Executes the matching specified in a compiled
<seealso marker="#match_spec">match specification</seealso> on a list
- of tuples. Term <c><anno>CompiledMatchSpec</anno></c> is to be
+ of terms. Term <c><anno>CompiledMatchSpec</anno></c> is to be
the result of a call to <seealso marker="#match_spec_compile/1">
<c>match_spec_compile/1</c></seealso> and is hence the internal
representation of the match specification one wants to use.</p>
@@ -985,7 +985,7 @@ Table = ets:new...
MatchSpec = ...
% The following call...
ets:match_spec_run(ets:tab2list(Table),
-ets:match_spec_compile(MatchSpec)),
+ ets:match_spec_compile(MatchSpec)),
% ...gives the same result as the more common (and more efficient)
ets:select(Table, MatchSpec),</code>
<note>
diff --git a/lib/stdlib/doc/src/fascicules.xml b/lib/stdlib/doc/src/fascicules.xml
deleted file mode 100644
index 0ded9007e0..0000000000
--- a/lib/stdlib/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- STDLIB User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml
index 80c4acffdb..11762a3c5a 100644
--- a/lib/stdlib/doc/src/filelib.xml
+++ b/lib/stdlib/doc/src/filelib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2017</year>
+ <year>2003</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -267,7 +267,7 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code>
for a file with the extension <c>.beam</c>, the default rule is to
look for a file with a corresponding extension <c>.erl</c> by
replacing the suffix <c>"ebin"</c> of the object directory path with
- <c>"src"</c>.
+ <c>"src"</c> or <c>"src/*"</c>.
The file search is done through <seealso
marker="#find_file/3"><c>find_file/3</c></seealso>. The directory of
the object file is always tried before any other directory specified
diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml
index 14fd5ef787..1135a6dd80 100644
--- a/lib/stdlib/doc/src/filename.xml
+++ b/lib/stdlib/doc/src/filename.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2017</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -372,15 +372,18 @@ true
tuples <c>{<anno>BinSuffix</anno>, <anno>SourceSuffix</anno>}</c> and
is interpreted as follows: if the end of the directory name where the
object is located matches <c><anno>BinSuffix</anno></c>, then the
- source code directory has the same name, but with
- <c><anno>BinSuffix</anno></c> replaced by
- <c><anno>SourceSuffix</anno></c>. <c><anno>Rules</anno></c> defaults
+ name created by replacing <c><anno>BinSuffix</anno></c> with
+ <c><anno>SourceSuffix</anno></c> is expanded by calling
+ <seealso marker="filelib#wildcard/1">
+ <c>filelib:wildcard/1</c></seealso>.
+ If a regular file is found among the matches, the function
+ returns that location together with <c><anno>Options</anno></c>.
+ Otherwise the next rule is tried, and so on.</p>
+ <p><c><anno>Rules</anno></c> defaults
to:</p>
<code type="none">
-[{"", ""}, {"ebin", "src"}, {"ebin", "esrc"}]</code>
- <p>If the source file is found in the resulting directory, the function
- returns that location together with <c><anno>Options</anno></c>.
- Otherwise the next rule is tried, and so on.</p>
+[{"", ""}, {"ebin", "src"}, {"ebin", "esrc"},
+ {"ebin", "src/*"}, {"ebin", "esrc/*"}]</code>
<p>The function returns <c>{<anno>SourceFile</anno>,
<anno>Options</anno>}</c> if it succeeds.
<c><anno>SourceFile</anno></c> is the absolute path to the source
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index a7caa71dcb..574f488e91 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -543,7 +543,23 @@ handle_event(_, _, State, Data) ->
<name name="event_type"/>
<desc>
<p>
- External events are of three types:
+ There are 3 categories of events:
+ <seealso marker="#type-external_event_type">external</seealso>,
+ <seealso marker="#type-timeout_event_type">timeout</seealso>,
+ and <c>internal</c>.
+ </p>
+ <p>
+ <c>internal</c> events can only be generated by the
+ state machine itself through the state transition action
+ <seealso marker="#type-action"><c>next_event</c></seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="external_event_type"/>
+ <desc>
+ <p>
+ External events are of 3 types:
<c>{call,<anno>From</anno>}</c>, <c>cast</c>, or <c>info</c>.
<seealso marker="#call/2">Calls</seealso>
(synchronous) and
@@ -551,12 +567,17 @@ handle_event(_, _, State, Data) ->
originate from the corresponding API functions.
For calls, the event contains whom to reply to.
Type <c>info</c> originates from regular process messages sent
- to the <c>gen_statem</c>. The state machine
- implementation can, in addition to the above,
- generate
- <seealso marker="#type-event_type"><c>events of types</c></seealso>
- <c>timeout</c>, <c>{timeout,<anno>Name</anno>}</c>,
- <c>state_timeout</c>, and <c>internal</c> to itself.
+ to the <c>gen_statem</c>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="timeout_event_type"/>
+ <desc>
+ <p>
+ There are 3 types of timeout events that the state machine
+ can generate for itself with the corresponding
+ <seealso marker="#type-timeout_action">timeout_action()</seealso>s.
</p>
</desc>
</datatype>
@@ -1026,6 +1047,25 @@ handle_event(_, _, State, Data) ->
for this state transition.
</p>
</item>
+ </taglist>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="timeout_action"/>
+ <desc>
+ <p>
+ These state transition actions can be invoked by
+ returning them from the
+ <seealso marker="#state callback">state callback</seealso>, from
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ or by giving them to
+ <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
+ </p>
+ <p>
+ These timeout actions sets timeout
+ <seealso marker="#type-transition_option">transition options</seealso>.
+ </p>
+ <taglist>
<tag><c>Timeout</c></tag>
<item>
<p>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index d396f1de8f..b61e5b9b9e 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,49 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.4.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Make <c>ets:i/1</c> exit cleaner when ^D is input
+ while browsing a table. Only the old Erlang shell is
+ affected (<c>erl(1)</c> flag <c>-oldshell</c>). </p>
+ <p>
+ Own Id: OTP-14663</p>
+ </item>
+ <item>
+ <p>
+ Fixed handling of windows UNC paths in module
+ <c>filename</c>.</p>
+ <p>
+ Own Id: OTP-14693</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improve performance of the new string functionality when
+ handling ASCII characters.</p>
+ <p>
+ Own Id: OTP-14670</p>
+ </item>
+ <item>
+ <p>
+ Added a clarification to the documentation of
+ <c>unicode:characters_to_list/2</c>.</p>
+ <p>
+ Own Id: OTP-14798</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/part_notes.xml b/lib/stdlib/doc/src/part_notes.xml
deleted file mode 100644
index 461de749dd..0000000000
--- a/lib/stdlib/doc/src/part_notes.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>STDLIB Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The Standard Erlang Libraries application, <em>STDLIB</em>,
- contains modules for manipulating lists, strings and files etc.</p>
- <p>For information about older versions, see
- <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/stdlib/doc/src/part_notes_history.xml b/lib/stdlib/doc/src/part_notes_history.xml
deleted file mode 100644
index 8fd048a41e..0000000000
--- a/lib/stdlib/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2006</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>STDLIB Release Notes History</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The Standard Erlang Libraries application, <em>STDLIB</em>,
- contains modules for manipulating lists, strings and files etc.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml
index fcaccdb2cb..350847bf7d 100644
--- a/lib/stdlib/doc/src/timer.xml
+++ b/lib/stdlib/doc/src/timer.xml
@@ -270,7 +270,7 @@
<item>
<p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
<anno>Arguments</anno>)</c> and measures the elapsed real time as
- reported by <seealso marker="os:timestamp/0">
+ reported by <seealso marker="kernel:os#timestamp/0">
<c>os:timestamp/0</c></seealso>.</p>
<p>Returns <c>{<anno>Time</anno>, <anno>Value</anno>}</c>, where
<c><anno>Time</anno></c> is the elapsed real time in
diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml
index e86f45431f..d822aca89c 100644
--- a/lib/stdlib/doc/src/unicode.xml
+++ b/lib/stdlib/doc/src/unicode.xml
@@ -239,8 +239,13 @@
<c><anno>InEncoding</anno></c>.</p>
</item>
</list>
- <p>Only when <c><anno>InEncoding</anno></c> is one of the UTF
- encodings, integers in the list are allowed to be &gt; 255.</p>
+ <p>
+ Note that integers in the list always represent code points
+ regardless of <c><anno>InEncoding</anno></c> passed. If
+ <c><anno>InEncoding</anno> latin1</c> is passed, only code
+ points &lt; 256 are allowed; otherwise, all valid unicode code
+ points are allowed.
+ </p>
<p>If <c><anno>InEncoding</anno></c> is <c>latin1</c>, parameter
<c><anno>Data</anno></c> corresponds to the <c>iodata()</c> type,
but for <c>unicode</c>, parameter <c><anno>Data</anno></c> can
diff --git a/lib/stdlib/doc/src/user_guide.gif b/lib/stdlib/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/stdlib/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 31d0d499e3..b8e48bff6c 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1194,21 +1194,21 @@ skip_else(_Else, From, St, Sis) ->
%% macro_expansion(Tokens, Anno)
%% Extract the macro parameters and the expansion from a macro definition.
-macro_pars([{')',_Lp}, {',',Ld}|Ex], Args) ->
- {ok, {lists:reverse(Args), macro_expansion(Ex, Ld)}};
-macro_pars([{var,_,Name}, {')',_Lp}, {',',Ld}|Ex], Args) ->
+macro_pars([{')',_Lp}, {',',_Ld}=Comma|Ex], Args) ->
+ {ok, {lists:reverse(Args), macro_expansion(Ex, Comma)}};
+macro_pars([{var,_,Name}, {')',_Lp}, {',',_Ld}=Comma|Ex], Args) ->
false = lists:member(Name, Args), %Prolog is nice
- {ok, {lists:reverse([Name|Args]), macro_expansion(Ex, Ld)}};
+ {ok, {lists:reverse([Name|Args]), macro_expansion(Ex, Comma)}};
macro_pars([{var,_L,Name}, {',',_}|Ts], Args) ->
false = lists:member(Name, Args),
macro_pars(Ts, [Name|Args]).
-macro_expansion([{')',_Lp},{dot,_Ld}], _Anno0) -> [];
-macro_expansion([{dot,_}=Dot], _Anno0) ->
+macro_expansion([{')',_Lp},{dot,_Ld}], _T0) -> [];
+macro_expansion([{dot,_}=Dot], _T0) ->
throw({error,loc(Dot),missing_parenthesis});
-macro_expansion([T|Ts], _Anno0) ->
+macro_expansion([T|Ts], _T0) ->
[T|macro_expansion(Ts, T)];
-macro_expansion([], Anno0) -> throw({error,loc(Anno0),premature_end}).
+macro_expansion([], T0) -> throw({error,loc(T0),premature_end}).
%% expand_macros(Tokens, St)
%% expand_macro(Tokens, MacroToken, RestTokens)
diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index 76f0b38108..5ee584d612 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -189,7 +189,7 @@ table(Name) ->
%% Returns a list of names of the files in the tar file Name.
%% Options accepted: compressed, verbose, cooked.
-spec table(open_handle(), [compressed | verbose | cooked]) ->
- {ok, [tar_entry()]} | {error, term()}.
+ {ok, [string() | tar_entry()]} | {error, term()}.
table(Name, Opts) when is_list(Opts) ->
foldl_read(Name, fun table1/4, [], table_opts(Opts)).
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index b5d3cd3c8d..42fa8ede92 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -277,7 +277,7 @@ match_spec_compile(_) ->
erlang:nif_error(undef).
-spec match_spec_run_r(List, CompiledMatchSpec, list()) -> list() when
- List :: [tuple()],
+ List :: [term()],
CompiledMatchSpec :: comp_match_spec().
match_spec_run_r(_, _, _) ->
@@ -517,7 +517,7 @@ update_element(_, _, _) ->
-opaque comp_match_spec() :: reference().
-spec match_spec_run(List, CompiledMatchSpec) -> list() when
- List :: [tuple()],
+ List :: [term()],
CompiledMatchSpec :: comp_match_spec().
match_spec_run(List, CompiledMS) ->
@@ -1700,6 +1700,8 @@ choice(Height, Width, P, Mode, Tab, Key, Turn, Opos) ->
io:format("~ts\n", [ErrorString]),
choice(Height, Width, P, Mode, Tab, Key, Turn, Opos)
end;
+ eof ->
+ ok;
_ ->
choice(Height, Width, P, Mode, Tab, Key, Turn, Opos)
end.
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index d7c313f214..a9c055f72d 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -544,17 +544,16 @@ default_search_rules() ->
{"", ".c", c_source_search_rules()},
{"", ".in", basic_source_search_rules()},
%% plain old directory rules, backwards compatible
- {"", ""},
- {"ebin","src"},
- {"ebin","esrc"}
- ].
+ {"", ""}] ++ erl_source_search_rules().
basic_source_search_rules() ->
(erl_source_search_rules()
++ c_source_search_rules()).
erl_source_search_rules() ->
- [{"ebin","src"}, {"ebin","esrc"}].
+ [{"ebin","src"}, {"ebin","esrc"},
+ {"ebin",filename:join("src", "*")},
+ {"ebin",filename:join("esrc", "*")}].
c_source_search_rules() ->
[{"priv","c_src"}, {"priv","src"}, {"bin","c_src"}, {"bin","src"}, {"", "src"}].
@@ -634,8 +633,16 @@ try_dir_rule(Dir, Filename, From, To) ->
Src = filename:join(NewDir, Filename),
case is_regular(Src) of
true -> {ok, Src};
- false -> error
+ false -> find_regular_file(wildcard(Src))
end;
false ->
error
end.
+
+find_regular_file([]) ->
+ error;
+find_regular_file([File|Files]) ->
+ case is_regular(File) of
+ true -> {ok, File};
+ false -> find_regular_file(Files)
+ end.
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index 9bf4290916..ee807dfd09 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -439,6 +439,10 @@ join(Name1, Name2) when is_atom(Name2) ->
join1([UcLetter, $:|Rest], RelativeName, [], win32)
when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z ->
join1(Rest, RelativeName, [$:, UcLetter+$a-$A], win32);
+join1([$\\,$\\|Rest], RelativeName, [], win32) ->
+ join1([$/,$/|Rest], RelativeName, [], win32);
+join1([$/,$/|Rest], RelativeName, [], win32) ->
+ join1(Rest, RelativeName, [$/,$/], win32);
join1([$\\|Rest], RelativeName, Result, win32) ->
join1([$/|Rest], RelativeName, Result, win32);
join1([$/|Rest], RelativeName, [$., $/|Result], OsType) ->
@@ -467,6 +471,10 @@ join1([Atom|Rest], RelativeName, Result, OsType) when is_atom(Atom) ->
join1b(<<UcLetter, $:, Rest/binary>>, RelativeName, [], win32)
when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z ->
join1b(Rest, RelativeName, [$:, UcLetter+$a-$A], win32);
+join1b(<<$\\,$\\,Rest/binary>>, RelativeName, [], win32) ->
+ join1b(<<$/,$/,Rest/binary>>, RelativeName, [], win32);
+join1b(<<$/,$/,Rest/binary>>, RelativeName, [], win32) ->
+ join1b(Rest, RelativeName, [$/,$/], win32);
join1b(<<$\\,Rest/binary>>, RelativeName, Result, win32) ->
join1b(<<$/,Rest/binary>>, RelativeName, Result, win32);
join1b(<<$/,Rest/binary>>, RelativeName, [$., $/|Result], OsType) ->
@@ -477,6 +485,8 @@ join1b(<<>>, <<>>, Result, OsType) ->
list_to_binary(maybe_remove_dirsep(Result, OsType));
join1b(<<>>, RelativeName, [$:|Rest], win32) ->
join1b(RelativeName, <<>>, [$:|Rest], win32);
+join1b(<<>>, RelativeName, [$/,$/|Result], win32) ->
+ join1b(RelativeName, <<>>, [$/,$/|Result], win32);
join1b(<<>>, RelativeName, [$/|Result], OsType) ->
join1b(RelativeName, <<>>, [$/|Result], OsType);
join1b(<<>>, RelativeName, [$., $/|Result], OsType) ->
@@ -490,6 +500,8 @@ maybe_remove_dirsep([$/, $:, Letter], win32) ->
[Letter, $:, $/];
maybe_remove_dirsep([$/], _) ->
[$/];
+maybe_remove_dirsep([$/,$/], win32) ->
+ [$/,$/];
maybe_remove_dirsep([$/|Name], _) ->
lists:reverse(Name);
maybe_remove_dirsep(Name, _) ->
@@ -679,6 +691,9 @@ win32_splitb(<<Letter0,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter0) ->
Letter = fix_driveletter(Letter0),
L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
[<<Letter,$:>> | [ X || X <- L, X =/= <<>> ]];
+win32_splitb(<<Slash,Slash,Rest/binary>>) when ((Slash =:= $\\) orelse (Slash =:= $/)) ->
+ L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
+ [<<"//">> | [ X || X <- L, X =/= <<>> ]];
win32_splitb(<<Slash,Rest/binary>>) when ((Slash =:= $\\) orelse (Slash =:= $/)) ->
L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
[<<$/>> | [ X || X <- L, X =/= <<>> ]];
@@ -690,6 +705,8 @@ win32_splitb(Name) ->
unix_split(Name) ->
split(Name, [], unix).
+win32_split([Slash,Slash|Rest]) when ((Slash =:= $\\) orelse (Slash =:= $/)) ->
+ split(Rest, [[$/,$/]], win32);
win32_split([$\\|Rest]) ->
win32_split([$/|Rest]);
win32_split([X, $\\|Rest]) when is_integer(X) ->
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index a9b98911e2..73e4457bd0 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -125,7 +125,8 @@
| {'logfile', string()}.
-type option() :: {'timeout', timeout()}
| {'debug', [debug_flag()]}
- | {'spawn_opt', [proc_lib:spawn_option()]}.
+ | {'spawn_opt', [proc_lib:spawn_option()]}
+ | {'hibernate_after', timeout()}.
-type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()}
| {'via', atom(), term()} | pid().
-type start_ret() :: {'ok', pid()} | {'error', term()}.
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 96a53426e2..8c7db65563 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -198,7 +198,7 @@
%%% start(Name, Mod, Args, Options)
%%% start_link(Mod, Args, Options)
%%% start_link(Name, Mod, Args, Options) where:
-%%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()}
+%%% Name ::= {local, atom()} | {global, term()} | {via, atom(), term()}
%%% Mod ::= atom(), callback module implementing the 'real' fsm
%%% Args ::= term(), init arguments (to Mod:init/1)
%%% Options ::= [{debug, [Flag]}]
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 1110d18af6..b1d99e2e14 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2016-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2016-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -78,9 +78,11 @@
-type data() :: term().
-type event_type() ::
- {'call',From :: from()} | 'cast' | 'info' |
- 'timeout' | {'timeout', Name :: term()} | 'state_timeout' |
- 'internal'.
+ external_event_type() | timeout_event_type() | 'internal'.
+-type external_event_type() ::
+ {'call',From :: from()} | 'cast' | 'info'.
+-type timeout_event_type() ::
+ 'timeout' | {'timeout', Name :: term()} | 'state_timeout'.
-type callback_mode_result() ::
callback_mode() | [callback_mode() | state_enter()].
@@ -138,7 +140,9 @@
-type enter_action() ::
'hibernate' | % Set the hibernate option
{'hibernate', Hibernate :: hibernate()} |
- %%
+ timeout_action() |
+ reply_action().
+-type timeout_action() ::
(Timeout :: event_timeout()) | % {timeout,Timeout}
{'timeout', % Set the event_timeout option
Time :: event_timeout(), EventContent :: term()} |
@@ -159,9 +163,7 @@
{'state_timeout', % Set the state_timeout option
Time :: state_timeout(),
EventContent :: term(),
- Options :: (timeout_option() | [timeout_option()])} |
- %%
- reply_action().
+ Options :: (timeout_option() | [timeout_option()])}.
-type reply_action() ::
{'reply', % Reply to a caller
From :: from(), Reply :: term()}.
@@ -320,7 +322,13 @@
handle_event/4 % For callback_mode() =:= handle_event_function
]).
+
+
%% Type validation functions
+-compile(
+ {inline,
+ [callback_mode/1, state_enter/1, from/1, event_type/1]}).
+%%
callback_mode(CallbackMode) ->
case CallbackMode of
state_functions -> true;
@@ -328,6 +336,14 @@ callback_mode(CallbackMode) ->
_ -> false
end.
%%
+state_enter(StateEnter) ->
+ case StateEnter of
+ state_enter ->
+ true;
+ _ ->
+ false
+ end.
+%%
from({Pid,_}) when is_pid(Pid) -> true;
from(_) -> false.
%%
@@ -351,6 +367,48 @@ event_type(Type) ->
STACKTRACE(),
try throw(ok) catch _ -> erlang:get_stacktrace() end).
+-define(not_sys_debug, []).
+%%
+%% This is a macro to only evaluate arguments if Debug =/= [].
+%% Debug is evaluated multiple times.
+-define(
+ sys_debug(Debug, NameState, Entry),
+ case begin Debug end of
+ ?not_sys_debug ->
+ begin Debug end;
+ _ ->
+ sys_debug(begin Debug end, begin NameState end, begin Entry end)
+ end).
+
+-record(state,
+ {callback_mode = undefined :: callback_mode() | undefined,
+ state_enter = false :: boolean(),
+ module :: atom(),
+ name :: atom(),
+ state :: term(),
+ data :: term(),
+ postponed = [] :: [{event_type(),term()}],
+ %%
+ timer_refs = #{} :: % timer ref => the timer's event type
+ #{reference() => timeout_event_type()},
+ timer_types = #{} :: % timer's event type => timer ref
+ #{timeout_event_type() => reference()},
+ cancel_timers = 0 :: non_neg_integer(),
+ %% We add a timer to both timer_refs and timer_types
+ %% when we start it. When we request an asynchronous
+ %% timer cancel we remove it from timer_types. When
+ %% the timer cancel message arrives we remove it from
+ %% timer_refs.
+ %%
+ hibernate = false :: boolean(),
+ hibernate_after = infinity :: timeout()}).
+
+-record(trans_opts,
+ {hibernate = false,
+ postpone = false,
+ timeouts_r = [],
+ next_events_r = []}).
+
%%%==========================================================================
%%% API
@@ -422,6 +480,10 @@ stop(ServerRef, Reason, Timeout) ->
%% Send an event to a state machine that arrives with type 'event'
-spec cast(ServerRef :: server_ref(), Msg :: term()) -> ok.
+cast(ServerRef, Msg) when is_pid(ServerRef) ->
+ send(ServerRef, wrap_cast(Msg));
+cast(ServerRef, Msg) when is_atom(ServerRef) ->
+ send(ServerRef, wrap_cast(Msg));
cast({global,Name}, Msg) ->
try global:send(Name, wrap_cast(Msg)) of
_ -> ok
@@ -435,10 +497,6 @@ cast({via,RegMod,Name}, Msg) ->
_:_ -> ok
end;
cast({Name,Node} = ServerRef, Msg) when is_atom(Name), is_atom(Node) ->
- send(ServerRef, wrap_cast(Msg));
-cast(ServerRef, Msg) when is_atom(ServerRef) ->
- send(ServerRef, wrap_cast(Msg));
-cast(ServerRef, Msg) when is_pid(ServerRef) ->
send(ServerRef, wrap_cast(Msg)).
%% Call a state machine (synchronous; a reply is expected) that
@@ -455,75 +513,18 @@ call(ServerRef, Request) ->
{'clean_timeout',T :: timeout()} |
{'dirty_timeout',T :: timeout()}) ->
Reply :: term().
+call(ServerRef, Request, infinity = T = Timeout) ->
+ call_dirty(ServerRef, Request, Timeout, T);
+call(ServerRef, Request, {dirty_timeout, T} = Timeout) ->
+ call_dirty(ServerRef, Request, Timeout, T);
+call(ServerRef, Request, {clean_timeout, infinity = T} = Timeout) ->
+ call_dirty(ServerRef, Request, Timeout, T);
+call(ServerRef, Request, {clean_timeout, T} = Timeout) ->
+ call_clean(ServerRef, Request, Timeout, T);
+call(ServerRef, Request, {_, _} = Timeout) ->
+ erlang:error(badarg, [ServerRef,Request,Timeout]);
call(ServerRef, Request, Timeout) ->
- case parse_timeout(Timeout) of
- {dirty_timeout,T} ->
- try gen:call(ServerRef, '$gen_call', Request, T) of
- {ok,Reply} ->
- Reply
- catch
- Class:Reason ->
- erlang:raise(
- Class,
- {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
- erlang:get_stacktrace())
- end;
- {clean_timeout,T} ->
- %% Call server through proxy process to dodge any late reply
- Ref = make_ref(),
- Self = self(),
- Pid = spawn(
- fun () ->
- Self !
- try gen:call(
- ServerRef, '$gen_call', Request, T) of
- Result ->
- {Ref,Result}
- catch Class:Reason ->
- {Ref,Class,Reason,
- erlang:get_stacktrace()}
- end
- end),
- Mref = monitor(process, Pid),
- receive
- {Ref,Result} ->
- demonitor(Mref, [flush]),
- case Result of
- {ok,Reply} ->
- Reply
- end;
- {Ref,Class,Reason,Stacktrace} ->
- demonitor(Mref, [flush]),
- erlang:raise(
- Class,
- {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
- Stacktrace);
- {'DOWN',Mref,_,_,Reason} ->
- %% There is a theoretical possibility that the
- %% proxy process gets killed between try--of and !
- %% so this clause is in case of that
- exit(Reason)
- end;
- Error when is_atom(Error) ->
- erlang:error(Error, [ServerRef,Request,Timeout])
- end.
-
-parse_timeout(Timeout) ->
- case Timeout of
- {clean_timeout,infinity} ->
- {dirty_timeout,infinity};
- {clean_timeout,_} ->
- Timeout;
- {dirty_timeout,_} ->
- Timeout;
- {_,_} ->
- %% Be nice and throw a badarg for speling errors
- badarg;
- infinity ->
- {dirty_timeout,infinity};
- T ->
- {clean_timeout,T}
- end.
+ call_clean(ServerRef, Request, Timeout, Timeout).
%% Reply from a state machine callback to whom awaits in call/2
-spec reply([reply_action()] | reply_action()) -> ok.
@@ -532,6 +533,7 @@ reply({reply,From,Reply}) ->
reply(Replies) when is_list(Replies) ->
replies(Replies).
%%
+-compile({inline, [reply/2]}).
-spec reply(From :: from(), Reply :: term()) -> ok.
reply({To,Tag}, Reply) when is_pid(To) ->
Msg = {Tag,Reply},
@@ -581,9 +583,59 @@ enter_loop(Module, Opts, State, Data, Server, Actions) ->
%%---------------------------------------------------------------------------
%% API helpers
+-compile({inline, [wrap_cast/1]}).
wrap_cast(Event) ->
{'$gen_cast',Event}.
+call_dirty(ServerRef, Request, Timeout, T) ->
+ try gen:call(ServerRef, '$gen_call', Request, T) of
+ {ok,Reply} ->
+ Reply
+ catch
+ Class:Reason ->
+ erlang:raise(
+ Class,
+ {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
+ erlang:get_stacktrace())
+ end.
+
+call_clean(ServerRef, Request, Timeout, T) ->
+ %% Call server through proxy process to dodge any late reply
+ Ref = make_ref(),
+ Self = self(),
+ Pid = spawn(
+ fun () ->
+ Self !
+ try gen:call(
+ ServerRef, '$gen_call', Request, T) of
+ Result ->
+ {Ref,Result}
+ catch Class:Reason ->
+ {Ref,Class,Reason,
+ erlang:get_stacktrace()}
+ end
+ end),
+ Mref = monitor(process, Pid),
+ receive
+ {Ref,Result} ->
+ demonitor(Mref, [flush]),
+ case Result of
+ {ok,Reply} ->
+ Reply
+ end;
+ {Ref,Class,Reason,Stacktrace} ->
+ demonitor(Mref, [flush]),
+ erlang:raise(
+ Class,
+ {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
+ Stacktrace);
+ {'DOWN',Mref,_,_,Reason} ->
+ %% There is a theoretical possibility that the
+ %% proxy process gets killed between try--of and !
+ %% so this clause is in case of that
+ exit(Reason)
+ end.
+
replies([{reply,From,Reply}|Replies]) ->
reply(From, Reply),
replies(Replies);
@@ -608,60 +660,28 @@ enter(Module, Opts, State, Data, Server, Actions, Parent) ->
%% The values should already have been type checked
Name = gen:get_proc_name(Server),
Debug = gen:debug_options(Name, Opts),
- HibernateAfterTimeout = gen:hibernate_after(Opts),
- Events = [],
- P = [],
+ HibernateAfterTimeout = gen:hibernate_after(Opts),
+ Events = [],
Event = {internal,init_state},
%% We enforce {postpone,false} to ensure that
%% our fake Event gets discarded, thought it might get logged
- NewActions =
- if
- is_list(Actions) ->
- Actions ++ [{postpone,false}];
- true ->
- [Actions,{postpone,false}]
- end,
- TimerRefs = #{},
- %% Key: timer ref
- %% Value: the timer type i.e the timer's event type
- %%
- TimerTypes = #{},
- %% Key: timer type i.e the timer's event type
- %% Value: timer ref
- %%
- %% We add a timer to both timer_refs and timer_types
- %% when we start it. When we request an asynchronous
- %% timer cancel we remove it from timer_types. When
- %% the timer cancel message arrives we remove it from
- %% timer_refs.
- %%
- Hibernate = false,
- CancelTimers = 0,
- S = #{
- callback_mode => undefined,
- state_enter => false,
- module => Module,
- name => Name,
- state => State,
- data => Data,
- postponed => P,
- %%
- %% The following fields are finally set from to the arguments to
- %% loop_event_actions/9 when it finally loops back to loop/3
- %% in loop_event_result/11
- timer_refs => TimerRefs,
- timer_types => TimerTypes,
- hibernate => Hibernate,
- hibernate_after => HibernateAfterTimeout,
- cancel_timers => CancelTimers
- },
- NewDebug = sys_debug(Debug, S, State, {enter,Event,State}),
+ NewActions = listify(Actions) ++ [{postpone,false}],
+ S =
+ #state{
+ module = Module,
+ name = Name,
+ state = State,
+ data = Data,
+ hibernate_after = HibernateAfterTimeout},
+ CallEnter = true,
+ NewDebug = ?sys_debug(Debug, {Name,State}, {enter,Event,State}),
case call_callback_mode(S) of
- {ok,NewS} ->
+ #state{} = NewS ->
loop_event_actions(
Parent, NewDebug, NewS,
- Events, Event, State, Data, NewActions, true);
- {Class,Reason,Stacktrace} ->
+ Events, Event, State, Data, #trans_opts{},
+ NewActions, CallEnter);
+ [Class,Reason,Stacktrace] ->
terminate(
Class, Reason, Stacktrace, NewDebug,
S, [Event|Events])
@@ -686,10 +706,8 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
proc_lib:init_ack(Starter, {error,Reason}),
error_info(
Class, Reason, Stacktrace,
- #{name => Name,
- callback_mode => undefined,
- state_enter => false},
- [], [], undefined),
+ #state{name = Name},
+ [], undefined),
erlang:raise(Class, Reason, Stacktrace)
end.
@@ -719,10 +737,8 @@ init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
proc_lib:init_ack(Starter, {error,Error}),
error_info(
error, Error, ?STACKTRACE(),
- #{name => Name,
- callback_mode => undefined,
- state_enter => false},
- [], [], undefined),
+ #state{name = Name},
+ [], undefined),
exit(Error)
end.
@@ -736,9 +752,10 @@ system_terminate(Reason, _Parent, Debug, S) ->
terminate(exit, Reason, ?STACKTRACE(), Debug, S, []).
system_code_change(
- #{module := Module,
- state := State,
- data := Data} = S,
+ #state{
+ module = Module,
+ state = State,
+ data = Data} = S,
_Mod, OldVsn, Extra) ->
case
try Module:code_change(OldVsn, State, Data, Extra)
@@ -748,29 +765,31 @@ system_code_change(
of
{ok,NewState,NewData} ->
{ok,
- S#{callback_mode := undefined,
- state := NewState,
- data := NewData}};
+ S#state{
+ callback_mode = undefined,
+ state = NewState,
+ data = NewData}};
{ok,_} = Error ->
error({case_clause,Error});
Error ->
Error
end.
-system_get_state(#{state := State, data := Data}) ->
+system_get_state(#state{state = State, data = Data}) ->
{ok,{State,Data}}.
system_replace_state(
StateFun,
- #{state := State,
- data := Data} = S) ->
+ #state{
+ state = State,
+ data = Data} = S) ->
{NewState,NewData} = Result = StateFun({State,Data}),
- {ok,Result,S#{state := NewState, data := NewData}}.
+ {ok,Result,S#state{state = NewState, data = NewData}}.
format_status(
Opt,
[PDict,SysState,Parent,Debug,
- #{name := Name, postponed := P} = S]) ->
+ #state{name = Name, postponed = P} = S]) ->
Header = gen:format_status_header("Status for state machine", Name),
Log = sys:get_debug(log, Debug, []),
[{header,Header},
@@ -789,6 +808,9 @@ format_status(
%% them, not as the real erlang messages. Use trace for that.
%%---------------------------------------------------------------------------
+sys_debug(Debug, NameState, Entry) ->
+ sys:handle_debug(Debug, fun print_event/3, NameState, Entry).
+
print_event(Dev, {in,Event}, {Name,State}) ->
io:format(
Dev, "*DBG* ~tp receive ~ts in state ~tp~n",
@@ -821,15 +843,6 @@ event_string(Event) ->
io_lib:format("~tw ~tp", [EventType,EventContent])
end.
-sys_debug(Debug, #{name := Name}, State, Entry) ->
- case Debug of
- [] ->
- Debug;
- _ ->
- sys:handle_debug(
- Debug, fun print_event/3, {Name,State}, Entry)
- end.
-
%%%==========================================================================
%%% Internal callbacks
@@ -844,14 +857,16 @@ wakeup_from_hibernate(Parent, Debug, S) ->
%% and detours through sys:handle_system_message/7 and proc_lib:hibernate/3
%% Entry point for system_continue/3
-loop(Parent, Debug, #{hibernate := true, cancel_timers := 0} = S) ->
+loop(Parent, Debug, #state{hibernate = true, cancel_timers = 0} = S) ->
loop_hibernate(Parent, Debug, S);
loop(Parent, Debug, S) ->
loop_receive(Parent, Debug, S).
loop_hibernate(Parent, Debug, S) ->
+ %%
%% Does not return but restarts process at
%% wakeup_from_hibernate/3 that jumps to loop_receive/3
+ %%
proc_lib:hibernate(
?MODULE, wakeup_from_hibernate, [Parent,Debug,S]),
error(
@@ -859,17 +874,18 @@ loop_hibernate(Parent, Debug, S) ->
{wakeup_from_hibernate,3}}).
%% Entry point for wakeup_from_hibernate/3
-loop_receive(Parent, Debug, #{hibernate_after := HibernateAfterTimeout} = S) ->
+loop_receive(
+ Parent, Debug, #state{hibernate_after = HibernateAfterTimeout} = S) ->
+ %%
receive
Msg ->
case Msg of
{system,Pid,Req} ->
- #{hibernate := Hibernate} = S,
%% Does not return but tail recursively calls
%% system_continue/3 that jumps to loop/3
sys:handle_system_msg(
Req, Pid, Parent, ?MODULE, Debug, S,
- Hibernate);
+ S#state.hibernate);
{'EXIT',Parent,Reason} = EXIT ->
%% EXIT is not a 2-tuple therefore
%% not an event but this will stand out
@@ -877,9 +893,9 @@ loop_receive(Parent, Debug, #{hibernate_after := HibernateAfterTimeout} = S) ->
Q = [EXIT],
terminate(exit, Reason, ?STACKTRACE(), Debug, S, Q);
{timeout,TimerRef,TimerMsg} ->
- #{timer_refs := TimerRefs,
- timer_types := TimerTypes,
- hibernate := Hibernate} = S,
+ #state{
+ timer_refs = TimerRefs,
+ timer_types = TimerTypes} = S,
case TimerRefs of
#{TimerRef := TimerType} ->
%% We know of this timer; is it a running
@@ -889,7 +905,6 @@ loop_receive(Parent, Debug, #{hibernate_after := HibernateAfterTimeout} = S) ->
#{TimerType := TimerRef} ->
%% The timer type maps back to this
%% timer ref, so it was a running timer
- Event = {TimerType,TimerMsg},
%% Unregister the triggered timeout
NewTimerRefs =
maps:remove(TimerRef, TimerRefs),
@@ -897,11 +912,10 @@ loop_receive(Parent, Debug, #{hibernate_after := HibernateAfterTimeout} = S) ->
maps:remove(TimerType, TimerTypes),
loop_receive_result(
Parent, Debug,
- S#{
- timer_refs := NewTimerRefs,
- timer_types := NewTimerTypes},
- Hibernate,
- Event);
+ S#state{
+ timer_refs = NewTimerRefs,
+ timer_types = NewTimerTypes},
+ TimerType, TimerMsg);
_ ->
%% This was a late timeout message
%% from timer being cancelled, so
@@ -911,14 +925,13 @@ loop_receive(Parent, Debug, #{hibernate_after := HibernateAfterTimeout} = S) ->
end;
_ ->
%% Not our timer; present it as an event
- Event = {info,Msg},
- loop_receive_result(
- Parent, Debug, S, Hibernate, Event)
+ loop_receive_result(Parent, Debug, S, info, Msg)
end;
{cancel_timer,TimerRef,_} ->
- #{timer_refs := TimerRefs,
- cancel_timers := CancelTimers,
- hibernate := Hibernate} = S,
+ #state{
+ timer_refs = TimerRefs,
+ cancel_timers = CancelTimers,
+ hibernate = Hibernate} = S,
case TimerRefs of
#{TimerRef := _} ->
%% We must have requested a cancel
@@ -928,9 +941,9 @@ loop_receive(Parent, Debug, #{hibernate_after := HibernateAfterTimeout} = S) ->
maps:remove(TimerRef, TimerRefs),
NewCancelTimers = CancelTimers - 1,
NewS =
- S#{
- timer_refs := NewTimerRefs,
- cancel_timers := NewCancelTimers},
+ S#state{
+ timer_refs = NewTimerRefs,
+ cancel_timers = NewCancelTimers},
if
Hibernate =:= true, NewCancelTimers =:= 0 ->
%% No more cancel_timer msgs to expect;
@@ -942,238 +955,631 @@ loop_receive(Parent, Debug, #{hibernate_after := HibernateAfterTimeout} = S) ->
_ ->
%% Not our cancel_timer msg;
%% present it as an event
- Event = {info,Msg},
- loop_receive_result(
- Parent, Debug, S, Hibernate, Event)
+ loop_receive_result(Parent, Debug, S, info, Msg)
end;
_ ->
%% External msg
- #{hibernate := Hibernate} = S,
- Event =
- case Msg of
- {'$gen_call',From,Request} ->
- {{call,From},Request};
- {'$gen_cast',E} ->
- {cast,E};
- _ ->
- {info,Msg}
- end,
- loop_receive_result(
- Parent, Debug, S, Hibernate, Event)
+ case Msg of
+ {'$gen_call',From,Request} ->
+ loop_receive_result(
+ Parent, Debug, S, {call,From}, Request);
+ {'$gen_cast',Cast} ->
+ loop_receive_result(Parent, Debug, S, cast, Cast);
+ _ ->
+ loop_receive_result(Parent, Debug, S, info, Msg)
+ end
end
after
HibernateAfterTimeout ->
loop_hibernate(Parent, Debug, S)
end.
+loop_receive_result(Parent, ?not_sys_debug, S, Type, Content) ->
+ %% Here is the queue of not yet handled events created
+ Events = [],
+ loop_event(Parent, ?not_sys_debug, S, Events, Type, Content);
loop_receive_result(
- Parent, Debug,
- #{state := State,
- timer_types := TimerTypes, cancel_timers := CancelTimers} = S,
- Hibernate, Event) ->
- %% From now the 'hibernate' field in S is invalid
- %% and will be restored when looping back
- %% in loop_event_result/11
- NewDebug = sys_debug(Debug, S, State, {in,Event}),
+ Parent, Debug, #state{name = Name, state = State} = S, Type, Content) ->
+ NewDebug = sys_debug(Debug, {Name,State}, {in,{Type,Content}}),
%% Here is the queue of not yet handled events created
Events = [],
- %% Cancel any running event timer
- case
- cancel_timer_by_type(timeout, TimerTypes, CancelTimers)
- of
- {_,CancelTimers} ->
- %% No timer cancelled
- loop_event(Parent, NewDebug, S, Events, Event, Hibernate);
- {NewTimerTypes,NewCancelTimers} ->
- %% The timer is removed from NewTimerTypes but
- %% remains in TimerRefs until we get
- %% the cancel_timer msg
- NewS =
- S#{
- timer_types := NewTimerTypes,
- cancel_timers := NewCancelTimers},
- loop_event(Parent, NewDebug, NewS, Events, Event, Hibernate)
- end.
+ loop_event(Parent, NewDebug, S, Events, Type, Content).
%% Entry point for handling an event, received or enqueued
loop_event(
+ Parent, Debug, #state{hibernate = Hibernate} = S,
+ Events, Type, Content) ->
+ %%
+ case Hibernate of
+ true ->
+ %%
+ %% If (this old) Hibernate is true here it can only be
+ %% because it was set from an event action
+ %% and we did not go into hibernation since there were
+ %% events in queue, so we do what the user
+ %% might rely on i.e collect garbage which
+ %% would have happened if we actually hibernated
+ %% and immediately was awakened.
+ %%
+ _ = garbage_collect(),
+ loop_event_state_function(
+ Parent, Debug, S, Events, Type, Content);
+ false ->
+ loop_event_state_function(
+ Parent, Debug, S, Events, Type, Content)
+ end.
+
+%% Call the state function
+loop_event_state_function(
Parent, Debug,
- #{state := State, data := Data} = S,
- Events, {Type,Content} = Event, Hibernate) ->
+ #state{state = State, data = Data} = S,
+ Events, Type, Content) ->
+ %%
+ %% The field 'hibernate' in S is now invalid and will be
+ %% restored when looping back to loop/3 or loop_event/6.
%%
- %% If (this old) Hibernate is true here it can only be
- %% because it was set from an event action
- %% and we did not go into hibernation since there were
- %% events in queue, so we do what the user
- %% might rely on i.e collect garbage which
- %% would have happened if we actually hibernated
- %% and immediately was awakened
- Hibernate andalso garbage_collect(),
+ Event = {Type,Content},
+ TransOpts = false,
case call_state_function(S, Type, Content, State, Data) of
- {ok,Result,NewS} ->
- {NextState,NewData,Actions,EnterCall} =
- parse_event_result(
- true, Debug, NewS,
- Events, Event, State, Data, Result),
- loop_event_actions(
- Parent, Debug, NewS,
- Events, Event, NextState, NewData, Actions, EnterCall);
- {Class,Reason,Stacktrace} ->
+ {Result, NewS} ->
+ loop_event_result(
+ Parent, Debug, NewS,
+ Events, Event, State, Data, TransOpts, Result);
+ [Class,Reason,Stacktrace] ->
terminate(
- Class, Reason, Stacktrace, Debug, S,
- [Event|Events])
+ Class, Reason, Stacktrace, Debug, S, [Event|Events])
end.
-loop_event_actions(
- Parent, Debug,
- #{state := State, state_enter := StateEnter} = S,
- Events, Event, NextState, NewData,
- Actions, EnterCall) ->
- %% Hibernate is reborn here as false being
- %% the default value from parse_actions/4
- case parse_actions(Debug, S, State, Actions) of
- {ok,NewDebug,Hibernate,TimeoutsR,Postpone,NextEventsR} ->
- if
- StateEnter, EnterCall ->
- loop_event_enter(
- Parent, NewDebug, S,
- Events, Event, NextState, NewData,
- Hibernate, TimeoutsR, Postpone, NextEventsR);
- true ->
- loop_event_result(
- Parent, NewDebug, S,
- Events, Event, NextState, NewData,
- Hibernate, TimeoutsR, Postpone, NextEventsR)
- end;
- {Class,Reason,Stacktrace} ->
+%% Make a state enter call to the state function
+loop_event_state_enter(
+ Parent, Debug, #state{state = PrevState} = S,
+ Events, Event, NextState, NewData, TransOpts) ->
+ %%
+ case call_state_function(S, enter, PrevState, NextState, NewData) of
+ {Result, NewS} ->
+ loop_event_result(
+ Parent, Debug, NewS,
+ Events, Event, NextState, NewData, TransOpts, Result);
+ [Class,Reason,Stacktrace] ->
terminate(
- Class, Reason, Stacktrace, Debug, S,
- [Event|Events])
+ Class, Reason, Stacktrace, Debug, S, [Event|Events])
end.
-loop_event_enter(
- Parent, Debug, #{state := State} = S,
- Events, Event, NextState, NewData,
- Hibernate, TimeoutsR, Postpone, NextEventsR) ->
- case call_state_function(S, enter, State, NextState, NewData) of
- {ok,Result,NewS} ->
- case parse_event_result(
- false, Debug, NewS,
- Events, Event, NextState, NewData, Result) of
- {_,NewerData,Actions,EnterCall} ->
- loop_event_enter_actions(
- Parent, Debug, NewS,
- Events, Event, NextState, NewerData,
- Hibernate, TimeoutsR, Postpone, NextEventsR,
- Actions, EnterCall)
- end;
- {Class,Reason,Stacktrace} ->
- terminate(
- Class, Reason, Stacktrace, Debug,
- S#{
- state := NextState,
- data := NewData,
- hibernate := Hibernate},
- [Event|Events])
+%% Process the result from the state function.
+%% When TransOpts =:= false it was a state function call,
+%% otherwise it is an option tuple and it was a state enter call.
+%%
+loop_event_result(
+ Parent, Debug, S,
+ Events, Event, State, Data, TransOpts, Result) ->
+ %%
+ case Result of
+ {next_state,State,NewData} ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, NewData, TransOpts,
+ [], false);
+ {next_state,NextState,NewData}
+ when TransOpts =:= false ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, NextState, NewData, TransOpts,
+ [], true);
+ {next_state,State,NewData,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, NewData, TransOpts,
+ Actions, false);
+ {next_state,NextState,NewData,Actions}
+ when TransOpts =:= false ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, NextState, NewData, TransOpts,
+ Actions, true);
+ %%
+ {keep_state,NewData} ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, NewData, TransOpts,
+ [], false);
+ {keep_state,NewData,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, NewData, TransOpts,
+ Actions, false);
+ %%
+ keep_state_and_data ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, Data, TransOpts,
+ [], false);
+ {keep_state_and_data,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, Data, TransOpts,
+ Actions, false);
+ %%
+ {repeat_state,NewData} ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, NewData, TransOpts,
+ [], true);
+ {repeat_state,NewData,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, NewData, TransOpts,
+ Actions, true);
+ %%
+ repeat_state_and_data ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, Data, TransOpts,
+ [], true);
+ {repeat_state_and_data,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, State, Data, TransOpts,
+ Actions, true);
+ %%
+ stop ->
+ terminate(
+ exit, normal, ?STACKTRACE(), Debug,
+ S#state{
+ state = State, data = Data,
+ hibernate = hibernate_in_trans_opts(TransOpts)},
+ [Event|Events]);
+ {stop,Reason} ->
+ terminate(
+ exit, Reason, ?STACKTRACE(), Debug,
+ S#state{
+ state = State, data = Data,
+ hibernate = hibernate_in_trans_opts(TransOpts)},
+ [Event|Events]);
+ {stop,Reason,NewData} ->
+ terminate(
+ exit, Reason, ?STACKTRACE(), Debug,
+ S#state{
+ state = State, data = NewData,
+ hibernate = hibernate_in_trans_opts(TransOpts)},
+ [Event|Events]);
+ %%
+ {stop_and_reply,Reason,Replies} ->
+ reply_then_terminate(
+ exit, Reason, ?STACKTRACE(), Debug,
+ S#state{
+ state = State, data = Data,
+ hibernate = hibernate_in_trans_opts(TransOpts)},
+ [Event|Events], Replies);
+ {stop_and_reply,Reason,Replies,NewData} ->
+ reply_then_terminate(
+ exit, Reason, ?STACKTRACE(), Debug,
+ S#state{
+ state = State, data = NewData,
+ hibernate = hibernate_in_trans_opts(TransOpts)},
+ [Event|Events], Replies);
+ %%
+ _ ->
+ terminate(
+ error,
+ {bad_return_from_state_function,Result},
+ ?STACKTRACE(), Debug,
+ S#state{
+ state = State, data = Data,
+ hibernate = hibernate_in_trans_opts(TransOpts)},
+ [Event|Events])
end.
-loop_event_enter_actions(
- Parent, Debug, #{state_enter := StateEnter} = S,
- Events, Event, NextState, NewData,
- Hibernate, TimeoutsR, Postpone, NextEventsR,
- Actions, EnterCall) ->
- case
- parse_enter_actions(
- Debug, S, NextState, Actions, Hibernate, TimeoutsR)
- of
- {ok,NewDebug,NewHibernate,NewTimeoutsR,_,_} ->
- if
- StateEnter, EnterCall ->
- loop_event_enter(
- Parent, NewDebug, S,
- Events, Event, NextState, NewData,
- NewHibernate, NewTimeoutsR, Postpone, NextEventsR);
- true ->
- loop_event_result(
- Parent, NewDebug, S,
- Events, Event, NextState, NewData,
- NewHibernate, NewTimeoutsR, Postpone, NextEventsR)
- end;
- {Class,Reason,Stacktrace} ->
- terminate(
- Class, Reason, Stacktrace, Debug,
- S#{
- state := NextState,
- data := NewData,
- hibernate := Hibernate},
- [Event|Events])
+-compile({inline, [hibernate_in_trans_opts/1]}).
+hibernate_in_trans_opts(false) ->
+ (#trans_opts{})#trans_opts.hibernate;
+hibernate_in_trans_opts(#trans_opts{hibernate = Hibernate}) ->
+ Hibernate.
+
+%% Ensure that Actions are a list
+loop_event_actions(
+ Parent, Debug, S,
+ Events, Event, NextState, NewerData, TransOpts,
+ Actions, CallEnter) ->
+ loop_event_actions_list(
+ Parent, Debug, S,
+ Events, Event, NextState, NewerData, TransOpts,
+ listify(Actions), CallEnter).
+
+%% Process actions from the state function
+loop_event_actions_list(
+ Parent, Debug, #state{state_enter = StateEnter} = S,
+ Events, Event, NextState, NewerData, TransOpts,
+ Actions, CallEnter) ->
+ %%
+ case parse_actions(TransOpts, Debug, S, Actions) of
+ {NewDebug,NewTransOpts}
+ when StateEnter, CallEnter ->
+ loop_event_state_enter(
+ Parent, NewDebug, S,
+ Events, Event, NextState, NewerData, NewTransOpts);
+ {NewDebug,NewTransOpts} ->
+ loop_event_done(
+ Parent, NewDebug, S,
+ Events, Event, NextState, NewerData, NewTransOpts);
+ [Class,Reason,Stacktrace,NewDebug] ->
+ terminate(
+ Class, Reason, Stacktrace, NewDebug,
+ S#state{
+ state = NextState,
+ data = NewerData,
+ hibernate = TransOpts#trans_opts.hibernate},
+ [Event|Events])
end.
-loop_event_result(
+parse_actions(false, Debug, S, Actions) ->
+ parse_actions(true, Debug, S, Actions, #trans_opts{});
+parse_actions(TransOpts, Debug, S, Actions) ->
+ parse_actions(false, Debug, S, Actions, TransOpts).
+%%
+parse_actions(_StateCall, Debug, _S, [], TransOpts) ->
+ {Debug,TransOpts};
+parse_actions(StateCall, Debug, S, [Action|Actions], TransOpts) ->
+ case Action of
+ %% Actual actions
+ {reply,From,Reply} ->
+ parse_actions_reply(
+ StateCall, Debug, S, Actions, TransOpts, From, Reply);
+ %%
+ %% Actions that set options
+ {hibernate,NewHibernate} when is_boolean(NewHibernate) ->
+ parse_actions(
+ StateCall, Debug, S, Actions,
+ TransOpts#trans_opts{hibernate = NewHibernate});
+ hibernate ->
+ parse_actions(
+ StateCall, Debug, S, Actions,
+ TransOpts#trans_opts{hibernate = true});
+ %%
+ {postpone,NewPostpone} when not NewPostpone orelse StateCall ->
+ parse_actions(
+ StateCall, Debug, S, Actions,
+ TransOpts#trans_opts{postpone = NewPostpone});
+ postpone when StateCall ->
+ parse_actions(
+ StateCall, Debug, S, Actions,
+ TransOpts#trans_opts{postpone = true});
+ %%
+ {next_event,Type,Content} ->
+ parse_actions_next_event(
+ StateCall, Debug, S, Actions, TransOpts, Type, Content);
+ %%
+ _ ->
+ parse_actions_timeout(
+ StateCall, Debug, S, Actions, TransOpts, Action)
+ end.
+
+parse_actions_reply(
+ StateCall, ?not_sys_debug, S, Actions, TransOpts,
+ From, Reply) ->
+ %%
+ case from(From) of
+ true ->
+ reply(From, Reply),
+ parse_actions(StateCall, ?not_sys_debug, S, Actions, TransOpts);
+ false ->
+ [error,
+ {bad_action_from_state_function,{reply,From,Reply}},
+ ?STACKTRACE(),
+ ?not_sys_debug]
+ end;
+parse_actions_reply(
+ StateCall, Debug, #state{name = Name, state = State} = S,
+ Actions, TransOpts, From, Reply) ->
+ %%
+ case from(From) of
+ true ->
+ reply(From, Reply),
+ NewDebug = sys_debug(Debug, {Name,State}, {out,Reply,From}),
+ parse_actions(StateCall, NewDebug, S, Actions, TransOpts);
+ false ->
+ [error,
+ {bad_action_from_state_function,{reply,From,Reply}},
+ ?STACKTRACE(),
+ Debug]
+ end.
+
+parse_actions_next_event(
+ StateCall, ?not_sys_debug, S,
+ Actions, TransOpts, Type, Content) ->
+ case event_type(Type) of
+ true when StateCall ->
+ NextEventsR = TransOpts#trans_opts.next_events_r,
+ parse_actions(
+ StateCall, ?not_sys_debug, S, Actions,
+ TransOpts#trans_opts{
+ next_events_r = [{Type,Content}|NextEventsR]});
+ _ ->
+ [error,
+ {bad_action_from_state_function,{next_events,Type,Content}},
+ ?STACKTRACE(),
+ ?not_sys_debug]
+ end;
+parse_actions_next_event(
+ StateCall, Debug, #state{name = Name, state = State} = S,
+ Actions, TransOpts, Type, Content) ->
+ case event_type(Type) of
+ true when StateCall ->
+ NewDebug = sys_debug(Debug, {Name,State}, {in,{Type,Content}}),
+ NextEventsR = TransOpts#trans_opts.next_events_r,
+ parse_actions(
+ StateCall, NewDebug, S, Actions,
+ TransOpts#trans_opts{
+ next_events_r = [{Type,Content}|NextEventsR]});
+ _ ->
+ [error,
+ {bad_action_from_state_function,{next_events,Type,Content}},
+ ?STACKTRACE(),
+ Debug]
+ end.
+
+parse_actions_timeout(
+ StateCall, Debug, S, Actions, TransOpts,
+ {TimerType,Time,TimerMsg,TimerOpts} = AbsoluteTimeout) ->
+ %%
+ case classify_timer(Time, listify(TimerOpts)) of
+ absolute ->
+ parse_actions_timeout_add(
+ StateCall, Debug, S, Actions,
+ TransOpts, AbsoluteTimeout);
+ relative ->
+ RelativeTimeout = {TimerType,Time,TimerMsg},
+ parse_actions_timeout_add(
+ StateCall, Debug, S, Actions,
+ TransOpts, RelativeTimeout);
+ badarg ->
+ [error,
+ {bad_action_from_state_function,AbsoluteTimeout},
+ ?STACKTRACE(),
+ Debug]
+ end;
+parse_actions_timeout(
+ StateCall, Debug, S, Actions, TransOpts,
+ {_,Time,_} = RelativeTimeout) ->
+ case classify_timer(Time, []) of
+ relative ->
+ parse_actions_timeout_add(
+ StateCall, Debug, S, Actions,
+ TransOpts, RelativeTimeout);
+ badarg ->
+ [error,
+ {bad_action_from_state_function,RelativeTimeout},
+ ?STACKTRACE(),
+ Debug]
+ end;
+parse_actions_timeout(
+ StateCall, Debug, S, Actions, TransOpts,
+ Timeout) ->
+ case classify_timer(Timeout, []) of
+ relative ->
+ parse_actions_timeout_add(
+ StateCall, Debug, S, Actions, TransOpts, Timeout);
+ badarg ->
+ [error,
+ {bad_action_from_state_function,Timeout},
+ ?STACKTRACE(),
+ Debug]
+ end.
+
+parse_actions_timeout_add(
+ StateCall, Debug, S, Actions,
+ #trans_opts{timeouts_r = TimeoutsR} = TransOpts, Timeout) ->
+ parse_actions(
+ StateCall, Debug, S, Actions,
+ TransOpts#trans_opts{timeouts_r = [Timeout|TimeoutsR]}).
+
+%% Do the state transition
+loop_event_done(
+ Parent, ?not_sys_debug,
+ #state{postponed = P} = S,
+ Events, Event, NextState, NewData,
+ #trans_opts{
+ postpone = Postpone, hibernate = Hibernate,
+ timeouts_r = [], next_events_r = []}) ->
+ %%
+ %% Optimize the simple cases
+ %% i.e no timer changes, no inserted events and no debug,
+ %% by duplicate stripped down code
+ %%
+ %% Fast path
+ %%
+ case Postpone of
+ true ->
+ loop_event_done_fast(
+ Parent, Hibernate,
+ S,
+ Events, [Event|P], NextState, NewData);
+ false ->
+ loop_event_done_fast(
+ Parent, Hibernate,
+ S,
+ Events, P, NextState, NewData)
+ end;
+loop_event_done(
Parent, Debug_0,
- #{state := State, postponed := P_0,
- timer_refs := TimerRefs_0, timer_types := TimerTypes_0,
- cancel_timers := CancelTimers_0} = S_0,
+ #state{
+ state = State, postponed = P_0,
+ timer_refs = TimerRefs_0, timer_types = TimerTypes_0,
+ cancel_timers = CancelTimers_0} = S,
Events_0, Event_0, NextState, NewData,
- Hibernate, TimeoutsR, Postpone, NextEventsR) ->
+ #trans_opts{
+ hibernate = Hibernate, timeouts_r = TimeoutsR,
+ postpone = Postpone, next_events_r = NextEventsR}) ->
%%
%% All options have been collected and next_events are buffered.
%% Do the actual state transition.
%%
- {Debug_1,P_1} = % Move current event to postponed if Postpone
+ %% Full feature path
+ %%
+ [Debug_1|P_1] = % Move current event to postponed if Postpone
case Postpone of
true ->
- {sys_debug(Debug_0, S_0, State, {postpone,Event_0,State}),
- [Event_0|P_0]};
+ [?sys_debug(
+ Debug_0,
+ {S#state.name,State},
+ {postpone,Event_0,State}),
+ Event_0|P_0];
false ->
- {sys_debug(Debug_0, S_0, State, {consume,Event_0,State}),
- P_0}
+ [?sys_debug(
+ Debug_0,
+ {S#state.name,State},
+ {consume,Event_0,State})|P_0]
end,
- {Events_1,P_2,{TimerTypes_1,CancelTimers_1}} =
- %% Move all postponed events to queue and cancel the
- %% state timeout if the state changes
+ {Events_2,P_2,Timers_2} =
+ %% Move all postponed events to queue,
+ %% cancel the event timer,
+ %% and cancel the state timeout if the state changes
if
NextState =:= State ->
- {Events_0,P_1,{TimerTypes_0,CancelTimers_0}};
+ {Events_0,P_1,
+ cancel_timer_by_type(
+ timeout, {TimerTypes_0,CancelTimers_0})};
true ->
{lists:reverse(P_1, Events_0),
[],
cancel_timer_by_type(
- state_timeout, TimerTypes_0, CancelTimers_0)}
- %% The state timer is removed from TimerTypes_1
- %% but remains in TimerRefs_0 until we get
+ state_timeout,
+ cancel_timer_by_type(
+ timeout, {TimerTypes_0,CancelTimers_0}))}
+ %% The state timer is removed from TimerTypes
+ %% but remains in TimerRefs until we get
%% the cancel_timer msg
end,
- {TimerRefs_2,TimerTypes_2,CancelTimers_2,TimeoutEvents} =
- %% Stop and start non-event timers
- parse_timers(TimerRefs_0, TimerTypes_1, CancelTimers_1, TimeoutsR),
+ {TimerRefs_3,{TimerTypes_3,CancelTimers_3},TimeoutEvents} =
+ %% Stop and start timers
+ parse_timers(TimerRefs_0, Timers_2, TimeoutsR),
%% Place next events last in reversed queue
- Events_2R = lists:reverse(Events_1, NextEventsR),
- %% Enqueue immediate timeout events and start event timer
- Events_3R = prepend_timeout_events(TimeoutEvents, Events_2R),
- S_1 =
- S_0#{
- state := NextState,
- data := NewData,
- postponed := P_2,
- timer_refs := TimerRefs_2,
- timer_types := TimerTypes_2,
- cancel_timers := CancelTimers_2,
- hibernate := Hibernate},
- case lists:reverse(Events_3R) of
- [] ->
- %% Get a new event
- loop(Parent, Debug_1, S_1);
- [Event|Events] ->
+ Events_3R = lists:reverse(Events_2, NextEventsR),
+ %% Enqueue immediate timeout events
+ Events_4R = prepend_timeout_events(TimeoutEvents, Events_3R),
+ loop_event_done(
+ Parent, Debug_1,
+ S#state{
+ state = NextState,
+ data = NewData,
+ postponed = P_2,
+ timer_refs = TimerRefs_3,
+ timer_types = TimerTypes_3,
+ cancel_timers = CancelTimers_3,
+ hibernate = Hibernate},
+ lists:reverse(Events_4R)).
+
+%% Fast path
+%%
+loop_event_done_fast(
+ Parent, Hibernate,
+ #state{
+ state = NextState,
+ timer_types = #{timeout := _} = TimerTypes,
+ cancel_timers = CancelTimers} = S,
+ Events, P, NextState, NewData) ->
+ %%
+ %% Same state, event timeout active
+ %%
+ loop_event_done_fast(
+ Parent, Hibernate, S,
+ Events, P, NextState, NewData,
+ cancel_timer_by_type(
+ timeout, {TimerTypes,CancelTimers}));
+loop_event_done_fast(
+ Parent, Hibernate,
+ #state{state = NextState} = S,
+ Events, P, NextState, NewData) ->
+ %%
+ %% Same state
+ %%
+ loop_event_done(
+ Parent, ?not_sys_debug,
+ S#state{
+ data = NewData,
+ postponed = P,
+ hibernate = Hibernate},
+ Events);
+loop_event_done_fast(
+ Parent, Hibernate,
+ #state{
+ timer_types = #{timeout := _} = TimerTypes,
+ cancel_timers = CancelTimers} = S,
+ Events, P, NextState, NewData) ->
+ %%
+ %% State change, event timeout active
+ %%
+ loop_event_done_fast(
+ Parent, Hibernate, S,
+ lists:reverse(P, Events), [], NextState, NewData,
+ cancel_timer_by_type(
+ state_timeout,
+ cancel_timer_by_type(
+ timeout, {TimerTypes,CancelTimers})));
+loop_event_done_fast(
+ Parent, Hibernate,
+ #state{
+ timer_types = #{state_timeout := _} = TimerTypes,
+ cancel_timers = CancelTimers} = S,
+ Events, P, NextState, NewData) ->
+ %%
+ %% State change, state timeout active
+ %%
+ loop_event_done_fast(
+ Parent, Hibernate, S,
+ lists:reverse(P, Events), [], NextState, NewData,
+ cancel_timer_by_type(
+ state_timeout,
+ cancel_timer_by_type(
+ timeout, {TimerTypes,CancelTimers})));
+loop_event_done_fast(
+ Parent, Hibernate,
+ #state{} = S,
+ Events, P, NextState, NewData) ->
+ %%
+ %% State change, no timeout to automatically cancel
+ %%
+ loop_event_done(
+ Parent, ?not_sys_debug,
+ S#state{
+ state = NextState,
+ data = NewData,
+ postponed = [],
+ hibernate = Hibernate},
+ lists:reverse(P, Events)).
+%%
+%% Fast path
+%%
+loop_event_done_fast(
+ Parent, Hibernate, S,
+ Events, P, NextState, NewData,
+ {TimerTypes,CancelTimers}) ->
+ %%
+ loop_event_done(
+ Parent, ?not_sys_debug,
+ S#state{
+ state = NextState,
+ data = NewData,
+ postponed = P,
+ timer_types = TimerTypes,
+ cancel_timers = CancelTimers,
+ hibernate = Hibernate},
+ Events).
+
+loop_event_done(Parent, Debug, S, Q) ->
+ case Q of
+ [] ->
+ %% Get a new event
+ loop(Parent, Debug, S);
+ [{Type,Content}|Events] ->
%% Loop until out of enqueued events
- loop_event(Parent, Debug_1, S_1, Events, Event, Hibernate)
+ loop_event(Parent, Debug, S, Events, Type, Content)
end.
%%---------------------------------------------------------------------------
%% Server loop helpers
-call_callback_mode(#{module := Module} = S) ->
+call_callback_mode(#state{module = Module} = S) ->
try Module:callback_mode() of
CallbackMode ->
callback_mode_result(S, CallbackMode)
@@ -1181,58 +1587,45 @@ call_callback_mode(#{module := Module} = S) ->
CallbackMode ->
callback_mode_result(S, CallbackMode);
Class:Reason ->
- {Class,Reason,erlang:get_stacktrace()}
+ [Class,Reason,erlang:get_stacktrace()]
end.
callback_mode_result(S, CallbackMode) ->
- case
- parse_callback_mode(
- if
- is_atom(CallbackMode) ->
- [CallbackMode];
- true ->
- CallbackMode
- end, undefined, false)
- of
- {undefined,_} ->
- {error,
- {bad_return_from_callback_mode,CallbackMode},
- ?STACKTRACE()};
- {CBMode,StateEnter} ->
- {ok,
- S#{
- callback_mode := CBMode,
- state_enter := StateEnter}}
- end.
-
-parse_callback_mode([], CBMode, StateEnter) ->
- {CBMode,StateEnter};
-parse_callback_mode([H|T], CBMode, StateEnter) ->
+ callback_mode_result(
+ S, CallbackMode, listify(CallbackMode), undefined, false).
+%%
+callback_mode_result(_S, CallbackMode, [], undefined, _StateEnter) ->
+ [error,
+ {bad_return_from_callback_mode,CallbackMode},
+ ?STACKTRACE()];
+callback_mode_result(S, _CallbackMode, [], CBMode, StateEnter) ->
+ S#state{callback_mode = CBMode, state_enter = StateEnter};
+callback_mode_result(S, CallbackMode, [H|T], CBMode, StateEnter) ->
case callback_mode(H) of
true ->
- parse_callback_mode(T, H, StateEnter);
+ callback_mode_result(S, CallbackMode, T, H, StateEnter);
false ->
- case H of
- state_enter ->
- parse_callback_mode(T, CBMode, true);
- _ ->
- {undefined,StateEnter}
+ case state_enter(H) of
+ true ->
+ callback_mode_result(S, CallbackMode, T, CBMode, true);
+ false ->
+ [error,
+ {bad_return_from_callback_mode,CallbackMode},
+ ?STACKTRACE()]
end
- end;
-parse_callback_mode(_, _CBMode, StateEnter) ->
- {undefined,StateEnter}.
+ end.
call_state_function(
- #{callback_mode := undefined} = S, Type, Content, State, Data) ->
+ #state{callback_mode = undefined} = S, Type, Content, State, Data) ->
case call_callback_mode(S) of
- {ok,NewS} ->
+ #state{} = NewS ->
call_state_function(NewS, Type, Content, State, Data);
Error ->
Error
end;
call_state_function(
- #{callback_mode := CallbackMode, module := Module} = S,
+ #state{callback_mode = CallbackMode, module = Module} = S,
Type, Content, State, Data) ->
try
case CallbackMode of
@@ -1243,333 +1636,108 @@ call_state_function(
end
of
Result ->
- {ok,Result,S}
+ {Result,S}
catch
Result ->
- {ok,Result,S};
+ {Result,S};
Class:Reason ->
- {Class,Reason,erlang:get_stacktrace()}
- end.
-
-
-%% Interpret all callback return variants
-parse_event_result(
- AllowStateChange, Debug, S,
- Events, Event, State, Data, Result) ->
- case Result of
- stop ->
- terminate(
- exit, normal, ?STACKTRACE(), Debug,
- S#{state := State, data := Data},
- [Event|Events]);
- {stop,Reason} ->
- terminate(
- exit, Reason, ?STACKTRACE(), Debug,
- S#{state := State, data := Data},
- [Event|Events]);
- {stop,Reason,NewData} ->
- terminate(
- exit, Reason, ?STACKTRACE(), Debug,
- S#{state := State, data := NewData},
- [Event|Events]);
- %%
- {stop_and_reply,Reason,Replies} ->
- reply_then_terminate(
- exit, Reason, ?STACKTRACE(), Debug,
- S#{state := State, data := Data},
- [Event|Events], Replies);
- {stop_and_reply,Reason,Replies,NewData} ->
- reply_then_terminate(
- exit, Reason, ?STACKTRACE(), Debug,
- S#{state := State, data := NewData},
- [Event|Events], Replies);
- %%
- {next_state,State,NewData} ->
- {State,NewData,[],false};
- {next_state,NextState,NewData} when AllowStateChange ->
- {NextState,NewData,[],true};
- {next_state,State,NewData,Actions} ->
- {State,NewData,Actions,false};
- {next_state,NextState,NewData,Actions} when AllowStateChange ->
- {NextState,NewData,Actions,true};
- %%
- {keep_state,NewData} ->
- {State,NewData,[],false};
- {keep_state,NewData,Actions} ->
- {State,NewData,Actions,false};
- keep_state_and_data ->
- {State,Data,[],false};
- {keep_state_and_data,Actions} ->
- {State,Data,Actions,false};
- %%
- {repeat_state,NewData} ->
- {State,NewData,[],true};
- {repeat_state,NewData,Actions} ->
- {State,NewData,Actions,true};
- repeat_state_and_data ->
- {State,Data,[],true};
- {repeat_state_and_data,Actions} ->
- {State,Data,Actions,true};
- %%
- _ ->
- terminate(
- error,
- {bad_return_from_state_function,Result},
- ?STACKTRACE(), Debug,
- S#{state := State, data := Data},
- [Event|Events])
- end.
-
-
-parse_enter_actions(Debug, S, State, Actions, Hibernate, TimeoutsR) ->
- Postpone = forbidden,
- NextEventsR = forbidden,
- parse_actions(
- Debug, S, State, listify(Actions),
- Hibernate, TimeoutsR, Postpone, NextEventsR).
-
-parse_actions(Debug, S, State, Actions) ->
- Hibernate = false,
- TimeoutsR = [infinity], %% Will cancel event timer
- Postpone = false,
- NextEventsR = [],
- parse_actions(
- Debug, S, State, listify(Actions),
- Hibernate, TimeoutsR, Postpone, NextEventsR).
-%%
-parse_actions(
- Debug, _S, _State, [],
- Hibernate, TimeoutsR, Postpone, NextEventsR) ->
- {ok,Debug,Hibernate,TimeoutsR,Postpone,NextEventsR};
-parse_actions(
- Debug, S, State, [Action|Actions],
- Hibernate, TimeoutsR, Postpone, NextEventsR) ->
- case Action of
- %% Actual actions
- {reply,From,Reply} ->
- case from(From) of
- true ->
- NewDebug = do_reply(Debug, S, State, From, Reply),
- parse_actions(
- NewDebug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone, NextEventsR);
- false ->
- {error,
- {bad_action_from_state_function,Action},
- ?STACKTRACE()}
- end;
- %%
- %% Actions that set options
- {hibernate,NewHibernate} when is_boolean(NewHibernate) ->
- parse_actions(
- Debug, S, State, Actions,
- NewHibernate, TimeoutsR, Postpone, NextEventsR);
- hibernate ->
- NewHibernate = true,
- parse_actions(
- Debug, S, State, Actions,
- NewHibernate, TimeoutsR, Postpone, NextEventsR);
- %%
- {postpone,NewPostpone}
- when is_boolean(NewPostpone), Postpone =/= forbidden ->
- parse_actions(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, NewPostpone, NextEventsR);
- postpone when Postpone =/= forbidden ->
- NewPostpone = true,
- parse_actions(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, NewPostpone, NextEventsR);
- %%
- {next_event,Type,Content} ->
- case event_type(Type) of
- true when NextEventsR =/= forbidden ->
- NewDebug =
- sys_debug(Debug, S, State, {in,{Type,Content}}),
- parse_actions(
- NewDebug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone,
- [{Type,Content}|NextEventsR]);
- _ ->
- {error,
- {bad_action_from_state_function,Action},
- ?STACKTRACE()}
- end;
- %%
- {{timeout,_},_,_} = Timeout ->
- parse_actions_timeout(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout);
- {{timeout,_},_,_,_} = Timeout ->
- parse_actions_timeout(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout);
- {timeout,_,_} = Timeout ->
- parse_actions_timeout(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout);
- {timeout,_,_,_} = Timeout ->
- parse_actions_timeout(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout);
- {state_timeout,_,_} = Timeout ->
- parse_actions_timeout(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout);
- {state_timeout,_,_,_} = Timeout ->
- parse_actions_timeout(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout);
- Time ->
- parse_actions_timeout(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone, NextEventsR, Time)
+ [Class,Reason,erlang:get_stacktrace()]
end.
-parse_actions_timeout(
- Debug, S, State, Actions,
- Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout) ->
- case Timeout of
- {TimerType,Time,TimerMsg,TimerOpts} ->
- case validate_timer_args(Time, listify(TimerOpts)) of
- true ->
- parse_actions(
- Debug, S, State, Actions,
- Hibernate, [Timeout|TimeoutsR],
- Postpone, NextEventsR);
- false ->
- NewTimeout = {TimerType,Time,TimerMsg},
- parse_actions(
- Debug, S, State, Actions,
- Hibernate, [NewTimeout|TimeoutsR],
- Postpone, NextEventsR);
- error ->
- {error,
- {bad_action_from_state_function,Timeout},
- ?STACKTRACE()}
- end;
- {_,Time,_} ->
- case validate_timer_args(Time, []) of
- false ->
- parse_actions(
- Debug, S, State, Actions,
- Hibernate, [Timeout|TimeoutsR],
- Postpone, NextEventsR);
- error ->
- {error,
- {bad_action_from_state_function,Timeout},
- ?STACKTRACE()}
- end;
- Time ->
- case validate_timer_args(Time, []) of
- false ->
- parse_actions(
- Debug, S, State, Actions,
- Hibernate, [Timeout|TimeoutsR],
- Postpone, NextEventsR);
- error ->
- {error,
- {bad_action_from_state_function,Timeout},
- ?STACKTRACE()}
- end
- end.
-validate_timer_args(Time, Opts) ->
- validate_timer_args(Time, Opts, false).
+%% -> absolute | relative | badarg
+classify_timer(Time, Opts) ->
+ classify_timer(Time, Opts, false).
%%
-validate_timer_args(Time, [], true) when is_integer(Time) ->
- true;
-validate_timer_args(Time, [], false) when is_integer(Time), Time >= 0 ->
- false;
-validate_timer_args(infinity, [], Abs) ->
- Abs;
-validate_timer_args(Time, [{abs,Abs}|Opts], _) when is_boolean(Abs) ->
- validate_timer_args(Time, Opts, Abs);
-validate_timer_args(_, [_|_], _) ->
- error.
+classify_timer(Time, [], Abs) ->
+ case Abs of
+ true when
+ is_integer(Time);
+ Time =:= infinity ->
+ absolute;
+ false when
+ is_integer(Time), 0 =< Time;
+ Time =:= infinity ->
+ relative;
+ _ ->
+ badarg
+ end;
+classify_timer(Time, [{abs,Abs}|Opts], _) when is_boolean(Abs) ->
+ classify_timer(Time, Opts, Abs);
+classify_timer(_, Opts, _) when is_list(Opts) ->
+ badarg.
%% Stop and start timers as well as create timeout zero events
%% and pending event timer
%%
%% Stop and start timers non-event timers
-parse_timers(TimerRefs, TimerTypes, CancelTimers, TimeoutsR) ->
- parse_timers(TimerRefs, TimerTypes, CancelTimers, TimeoutsR, #{}, []).
+parse_timers(TimerRefs, Timers, TimeoutsR) ->
+ parse_timers(TimerRefs, Timers, TimeoutsR, #{}, []).
%%
parse_timers(
- TimerRefs, TimerTypes, CancelTimers, [], _Seen, TimeoutEvents) ->
- {TimerRefs,TimerTypes,CancelTimers,TimeoutEvents};
+ TimerRefs, Timers, [], _Seen, TimeoutEvents) ->
+ %%
+ {TimerRefs,Timers,TimeoutEvents};
parse_timers(
- TimerRefs, TimerTypes, CancelTimers, [Timeout|TimeoutsR],
- Seen, TimeoutEvents) ->
+ TimerRefs, Timers, [Timeout|TimeoutsR], Seen, TimeoutEvents) ->
+ %%
case Timeout of
{TimerType,Time,TimerMsg,TimerOpts} ->
%% Absolute timer
parse_timers(
- TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
- Seen, TimeoutEvents,
+ TimerRefs, Timers, TimeoutsR, Seen, TimeoutEvents,
TimerType, Time, TimerMsg, listify(TimerOpts));
%% Relative timers below
{TimerType,0,TimerMsg} ->
parse_timers(
- TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
- Seen, TimeoutEvents,
+ TimerRefs, Timers, TimeoutsR, Seen, TimeoutEvents,
TimerType, zero, TimerMsg, []);
{TimerType,Time,TimerMsg} ->
parse_timers(
- TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
- Seen, TimeoutEvents,
+ TimerRefs, Timers, TimeoutsR, Seen, TimeoutEvents,
TimerType, Time, TimerMsg, []);
0 ->
parse_timers(
- TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
- Seen, TimeoutEvents,
+ TimerRefs, Timers, TimeoutsR, Seen, TimeoutEvents,
timeout, zero, 0, []);
Time ->
parse_timers(
- TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
- Seen, TimeoutEvents,
+ TimerRefs, Timers, TimeoutsR, Seen, TimeoutEvents,
timeout, Time, Time, [])
end.
parse_timers(
- TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
- Seen, TimeoutEvents,
+ TimerRefs, Timers, TimeoutsR, Seen, TimeoutEvents,
TimerType, Time, TimerMsg, TimerOpts) ->
case Seen of
#{TimerType := _} ->
%% Type seen before - ignore
parse_timers(
- TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
- Seen, TimeoutEvents);
+ TimerRefs, Timers, TimeoutsR, Seen, TimeoutEvents);
#{} ->
%% Unseen type - handle
NewSeen = Seen#{TimerType => true},
case Time of
infinity ->
%% Cancel any running timer
- {NewTimerTypes,NewCancelTimers} =
- cancel_timer_by_type(
- TimerType, TimerTypes, CancelTimers),
parse_timers(
- TimerRefs, NewTimerTypes, NewCancelTimers, TimeoutsR,
- NewSeen, TimeoutEvents);
+ TimerRefs, cancel_timer_by_type(TimerType, Timers),
+ TimeoutsR, NewSeen, TimeoutEvents);
zero ->
%% Cancel any running timer
- {NewTimerTypes,NewCancelTimers} =
- cancel_timer_by_type(
- TimerType, TimerTypes, CancelTimers),
%% Handle zero time timeouts later
- TimeoutEvent = {TimerType,TimerMsg},
parse_timers(
- TimerRefs, NewTimerTypes, NewCancelTimers, TimeoutsR,
- NewSeen, [TimeoutEvent|TimeoutEvents]);
+ TimerRefs, cancel_timer_by_type(TimerType, Timers),
+ TimeoutsR, NewSeen,
+ [{TimerType,TimerMsg}|TimeoutEvents]);
_ ->
%% (Re)start the timer
TimerRef =
erlang:start_timer(
Time, self(), TimerMsg, TimerOpts),
- case TimerTypes of
- #{TimerType := OldTimerRef} ->
+ case Timers of
+ {#{TimerType := OldTimerRef} = TimerTypes,
+ CancelTimers} ->
%% Cancel the running timer
cancel_timer(OldTimerRef),
NewCancelTimers = CancelTimers + 1,
@@ -1577,17 +1745,17 @@ parse_timers(
%% both TimerRefs and TimerTypes
parse_timers(
TimerRefs#{TimerRef => TimerType},
- TimerTypes#{TimerType => TimerRef},
- NewCancelTimers, TimeoutsR,
- NewSeen, TimeoutEvents);
- #{} ->
+ {TimerTypes#{TimerType => TimerRef},
+ NewCancelTimers},
+ TimeoutsR, NewSeen, TimeoutEvents);
+ {#{} = TimerTypes,CancelTimers} ->
%% Insert the new timer into
%% both TimerRefs and TimerTypes
parse_timers(
TimerRefs#{TimerRef => TimerType},
- TimerTypes#{TimerType => TimerRef},
- CancelTimers, TimeoutsR,
- NewSeen, TimeoutEvents)
+ {TimerTypes#{TimerType => TimerRef},
+ CancelTimers},
+ TimeoutsR, NewSeen, TimeoutEvents)
end
end
end.
@@ -1609,6 +1777,8 @@ prepend_timeout_events([], EventsR) ->
prepend_timeout_events([{timeout,_} = TimeoutEvent|TimeoutEvents], []) ->
prepend_timeout_events(TimeoutEvents, [TimeoutEvent]);
prepend_timeout_events([{timeout,_}|TimeoutEvents], EventsR) ->
+ %% Ignore since there are other events in queue
+ %% so they have cancelled the event timeout 0.
prepend_timeout_events(TimeoutEvents, EventsR);
prepend_timeout_events([TimeoutEvent|TimeoutEvents], EventsR) ->
%% Just prepend all others
@@ -1619,23 +1789,28 @@ prepend_timeout_events([TimeoutEvent|TimeoutEvents], EventsR) ->
%%---------------------------------------------------------------------------
%% Server helpers
-reply_then_terminate(
- Class, Reason, Stacktrace, Debug,
- #{state := State} = S, Q, Replies) ->
+reply_then_terminate(Class, Reason, Stacktrace, Debug, S, Q, Replies) ->
do_reply_then_terminate(
- Class, Reason, Stacktrace, Debug,
- S, Q, listify(Replies), State).
+ Class, Reason, Stacktrace, Debug, S, Q, listify(Replies)).
%%
do_reply_then_terminate(
- Class, Reason, Stacktrace, Debug, S, Q, [], _State) ->
+ Class, Reason, Stacktrace, Debug, S, Q, []) ->
terminate(Class, Reason, Stacktrace, Debug, S, Q);
do_reply_then_terminate(
- Class, Reason, Stacktrace, Debug, S, Q, [R|Rs], State) ->
+ Class, Reason, Stacktrace, Debug, S, Q, [R|Rs]) ->
case R of
{reply,{_To,_Tag}=From,Reply} ->
- NewDebug = do_reply(Debug, S, State, From, Reply),
+ reply(From, Reply),
+ NewDebug =
+ ?sys_debug(
+ Debug,
+ begin
+ #state{name = Name, state = State} = S,
+ {Name,State}
+ end,
+ {out,Reply,From}),
do_reply_then_terminate(
- Class, Reason, Stacktrace, NewDebug, S, Q, Rs, State);
+ Class, Reason, Stacktrace, NewDebug, S, Q, Rs);
_ ->
terminate(
error,
@@ -1644,14 +1819,9 @@ do_reply_then_terminate(
Debug, S, Q)
end.
-do_reply(Debug, S, State, From, Reply) ->
- reply(From, Reply),
- sys_debug(Debug, S, State, {out,Reply,From}).
-
-
terminate(
Class, Reason, Stacktrace, Debug,
- #{module := Module, state := State, data := Data, postponed := P} = S,
+ #state{module = Module, state = State, data = Data} = S,
Q) ->
case erlang:function_exported(Module, terminate, 3) of
true ->
@@ -1662,7 +1832,7 @@ terminate(
C:R ->
ST = erlang:get_stacktrace(),
error_info(
- C, R, ST, S, Q, P,
+ C, R, ST, S, Q,
format_status(terminate, get(), S)),
sys:print_log(Debug),
erlang:raise(C, R, ST)
@@ -1673,14 +1843,14 @@ terminate(
_ =
case Reason of
normal ->
- sys_debug(Debug, S, State, {terminate,Reason});
+ terminate_sys_debug(Debug, S, State, Reason);
shutdown ->
- sys_debug(Debug, S, State, {terminate,Reason});
+ terminate_sys_debug(Debug, S, State, Reason);
{shutdown,_} ->
- sys_debug(Debug, S, State, {terminate,Reason});
+ terminate_sys_debug(Debug, S, State, Reason);
_ ->
error_info(
- Class, Reason, Stacktrace, S, Q, P,
+ Class, Reason, Stacktrace, S, Q,
format_status(terminate, get(), S)),
sys:print_log(Debug)
end,
@@ -1691,12 +1861,18 @@ terminate(
erlang:raise(Class, Reason, Stacktrace)
end.
+terminate_sys_debug(Debug, S, State, Reason) ->
+ ?sys_debug(Debug, {S#state.name,State}, {terminate,Reason}).
+
+
error_info(
Class, Reason, Stacktrace,
- #{name := Name,
- callback_mode := CallbackMode,
- state_enter := StateEnter},
- Q, P, FmtData) ->
+ #state{
+ name = Name,
+ callback_mode = CallbackMode,
+ state_enter = StateEnter,
+ postponed = P},
+ Q, FmtData) ->
{FixedReason,FixedStacktrace} =
case Stacktrace of
[{M,F,Args,_}|ST]
@@ -1777,7 +1953,7 @@ error_info(
%% Call Module:format_status/2 or return a default value
format_status(
Opt, PDict,
- #{module := Module, state := State, data := Data}) ->
+ #state{module = Module, state = State, data = Data}) ->
case erlang:function_exported(Module, format_status, 2) of
true ->
try Module:format_status(Opt, [PDict,State,Data])
@@ -1802,6 +1978,7 @@ format_status_default(Opt, State, Data) ->
[{data,[{"State",StateData}]}]
end.
+-compile({inline, [listify/1]}).
listify(Item) when is_list(Item) ->
Item;
listify(Item) ->
@@ -1815,14 +1992,16 @@ listify(Item) ->
%%
%% Remove the timer from TimerTypes.
%% When we get the cancel_timer msg we remove it from TimerRefs.
-cancel_timer_by_type(TimerType, TimerTypes, CancelTimers) ->
+-compile({inline, [cancel_timer_by_type/2]}).
+cancel_timer_by_type(TimerType, {TimerTypes,CancelTimers} = TT_CT) ->
case TimerTypes of
#{TimerType := TimerRef} ->
- cancel_timer(TimerRef),
+ ok = erlang:cancel_timer(TimerRef, [{async,true}]),
{maps:remove(TimerType, TimerTypes),CancelTimers + 1};
#{} ->
- {TimerTypes,CancelTimers}
+ TT_CT
end.
+-compile({inline, [cancel_timer/1]}).
cancel_timer(TimerRef) ->
ok = erlang:cancel_timer(TimerRef, [{async,true}]).
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 9d447418f8..50bf959db5 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -149,7 +149,7 @@ fread(Chars, Format) ->
-spec fread(Continuation, CharSpec, Format) -> Return when
Continuation :: continuation() | [],
- CharSpec :: string() | eof,
+ CharSpec :: string() | 'eof',
Format :: string(),
Return :: {'more', Continuation1 :: continuation()}
| {'done', Result, LeftOverChars :: string()},
diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl
index 983e8d4566..319bff484e 100644
--- a/lib/stdlib/src/io_lib_fread.erl
+++ b/lib/stdlib/src/io_lib_fread.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@
-spec fread(Continuation, String, Format) -> Return when
Continuation :: io_lib:continuation() | [],
- String :: string(),
+ String :: string() | 'eof',
Format :: string(),
Return :: {'more', Continuation1 :: io_lib:continuation()}
| {'done', Result, LeftOverChars :: string()},
diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl
index c6eb0d7915..be11e86100 100644
--- a/lib/stdlib/src/lib.erl
+++ b/lib/stdlib/src/lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -551,7 +551,7 @@ format_stacktrace1(S0, Stack0, PF, SF, Enc) ->
format_stacktrace2(S, Stack, 1, PF, Enc).
format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF, Enc) when is_integer(A) ->
- [io_lib:fwrite(<<"~s~s ~ts ~s">>,
+ [io_lib:fwrite(<<"~s~s ~ts ~ts">>,
[sep(N, S), origin(N, M, F, A),
mfa_to_string(M, F, A, Enc),
location(L)])
@@ -573,7 +573,7 @@ location(L) ->
Line = proplists:get_value(line, L),
if
File =/= undefined, Line =/= undefined ->
- io_lib:format("(~s, line ~w)", [File, Line]);
+ io_lib:format("(~ts, line ~w)", [File, Line]);
true ->
""
end.
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 212b143b1d..ad4984b64c 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -701,7 +701,9 @@ exprs([E0|Es], Bs1, RT, Lf, Ef, Bs0, W) ->
{W,V0};
true -> case result_will_be_saved() of
true -> V0;
- false -> ignored
+ false ->
+ erlang:garbage_collect(),
+ ignored
end
end,
{{value,V,Bs,get()},Bs};
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index 4972da297d..6f5e617230 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -74,19 +74,21 @@
-export([to_upper/1, to_lower/1]).
%%
-import(lists,[member/2]).
-
-compile({no_auto_import,[length/1]}).
+-compile({inline, [btoken/2, rev/1, append/2, stack/2, search_compile/1]}).
+-define(ASCII_LIST(CP1,CP2), CP1 < 256, CP2 < 256, CP1 =/= $\r).
-export_type([grapheme_cluster/0]).
-type grapheme_cluster() :: char() | [char()].
-type direction() :: 'leading' | 'trailing'.
--dialyzer({no_improper_lists, stack/2}).
+-dialyzer({no_improper_lists, [stack/2, length_b/3]}).
%%% BIFs internal (not documented) should not to be used outside of this module
%%% May be removed
-export([list_to_float/1, list_to_integer/1]).
+
%% Uses bifs: string:list_to_float/1 and string:list_to_integer/1
-spec list_to_float(String) -> {Float, Rest} | {'error', Reason} when
String :: string(),
@@ -117,8 +119,10 @@ is_empty(_) -> false.
%% Count the number of grapheme clusters in chardata
-spec length(String::unicode:chardata()) -> non_neg_integer().
+length(<<CP1/utf8, Bin/binary>>) ->
+ length_b(Bin, CP1, 0);
length(CD) ->
- length_1(unicode_util:gc(CD), 0).
+ length_1(CD, 0).
%% Convert a string to a list of grapheme clusters
-spec to_graphemes(String::unicode:chardata()) -> [grapheme_cluster()].
@@ -166,6 +170,8 @@ equal(A, B, true, Norm) ->
%% Reverse grapheme clusters
-spec reverse(String::unicode:chardata()) -> [grapheme_cluster()].
+reverse(<<CP1/utf8, Rest/binary>>) ->
+ reverse_b(Rest, CP1, []);
reverse(CD) ->
reverse_1(CD, []).
@@ -176,7 +182,10 @@ reverse(CD) ->
Start :: non_neg_integer(),
Slice :: unicode:chardata().
slice(CD, N) when is_integer(N), N >= 0 ->
- slice_l(CD, N, is_binary(CD)).
+ case slice_l0(CD, N) of
+ [] when is_binary(CD) -> <<>>;
+ Res -> Res
+ end.
-spec slice(String, Start, Length) -> Slice when
String::unicode:chardata(),
@@ -185,9 +194,15 @@ slice(CD, N) when is_integer(N), N >= 0 ->
Slice :: unicode:chardata().
slice(CD, N, Length)
when is_integer(N), N >= 0, is_integer(Length), Length > 0 ->
- slice_trail(slice_l(CD, N, is_binary(CD)), Length);
+ case slice_l0(CD, N) of
+ [] when is_binary(CD) -> <<>>;
+ L -> slice_trail(L, Length)
+ end;
slice(CD, N, infinity) ->
- slice_l(CD, N, is_binary(CD));
+ case slice_l0(CD, N) of
+ [] when is_binary(CD) -> <<>>;
+ Res -> Res
+ end;
slice(CD, _, 0) ->
case is_binary(CD) of
true -> <<>>;
@@ -246,18 +261,22 @@ trim(Str, Dir) ->
Dir :: direction() | 'both',
Characters :: [grapheme_cluster()].
trim(Str, _, []) -> Str;
+trim(Str, leading, [Sep]) when is_list(Str), Sep < 256 ->
+ trim_ls(Str, Sep);
trim(Str, leading, Sep) when is_list(Sep) ->
- trim_l(Str, search_pattern(Sep));
-trim(Str, trailing, Sep) when is_list(Sep) ->
- trim_t(Str, 0, search_pattern(Sep));
-trim(Str, both, Sep0) when is_list(Sep0) ->
- Sep = search_pattern(Sep0),
- trim_t(trim_l(Str,Sep), 0, Sep).
+ trim_l(Str, Sep);
+trim(Str, trailing, [Sep]) when is_list(Str), Sep < 256 ->
+ trim_ts(Str, Sep);
+trim(Str, trailing, Seps0) when is_list(Seps0) ->
+ Seps = search_pattern(Seps0),
+ trim_t(Str, 0, Seps);
+trim(Str, both, Sep) when is_list(Sep) ->
+ trim(trim(Str,leading,Sep), trailing, Sep).
%% Delete trailing newlines or \r\n
-spec chomp(String::unicode:chardata()) -> unicode:chardata().
chomp(Str) ->
- trim_t(Str,0, {[[$\r,$\n],$\n], [$\r,$\n], [<<$\r>>,<<$\n>>]}).
+ trim(Str, trailing, [[$\r,$\n],$\n]).
%% Split String into two parts where the leading part consists of Characters
-spec take(String, Characters) -> {Leading, Trailing} when
@@ -290,8 +309,7 @@ take(Str, [], Complement, Dir) ->
{true, leading} -> {Str, Empty};
{true, trailing} -> {Empty, Str}
end;
-take(Str, Sep0, false, leading) ->
- Sep = search_pattern(Sep0),
+take(Str, Sep, false, leading) ->
take_l(Str, Sep, []);
take(Str, Sep0, true, leading) ->
Sep = search_pattern(Sep0),
@@ -393,10 +411,12 @@ to_number(_, Number, Rest, _, Tail) ->
%% Return the remaining string with prefix removed or else nomatch
-spec prefix(String::unicode:chardata(), Prefix::unicode:chardata()) ->
'nomatch' | unicode:chardata().
-prefix(Str, []) -> Str;
prefix(Str, Prefix0) ->
- Prefix = unicode:characters_to_list(Prefix0),
- case prefix_1(Str, Prefix) of
+ Result = case unicode:characters_to_list(Prefix0) of
+ [] -> Str;
+ Prefix -> prefix_1(Str, Prefix)
+ end,
+ case Result of
[] when is_binary(Str) -> <<>>;
Res -> Res
end.
@@ -451,6 +471,7 @@ replace(String, SearchPattern, Replacement, Where) ->
SeparatorList::[grapheme_cluster()]) ->
[unicode:chardata()].
lexemes([], _) -> [];
+lexemes(Str, []) -> [Str];
lexemes(Str, Seps0) when is_list(Seps0) ->
Seps = search_pattern(Seps0),
lexemes_m(Str, Seps, []).
@@ -484,13 +505,13 @@ find(String, SearchPattern, leading) ->
find(String, SearchPattern, trailing) ->
find_r(String, unicode:characters_to_list(SearchPattern), nomatch).
-%% Fetch first codepoint and return rest in tail
+%% Fetch first grapheme cluster and return rest in tail
-spec next_grapheme(String::unicode:chardata()) ->
maybe_improper_list(grapheme_cluster(),unicode:chardata()) |
{error,unicode:chardata()}.
next_grapheme(CD) -> unicode_util:gc(CD).
-%% Fetch first grapheme cluster and return rest in tail
+%% Fetch first codepoint and return rest in tail
-spec next_codepoint(String::unicode:chardata()) ->
maybe_improper_list(char(),unicode:chardata()) |
{error,unicode:chardata()}.
@@ -498,10 +519,23 @@ next_codepoint(CD) -> unicode_util:cp(CD).
%% Internals
-length_1([_|Rest], N) ->
- length_1(unicode_util:gc(Rest), N+1);
-length_1([], N) ->
- N.
+length_1([CP1|[CP2|_]=Cont], N) when ?ASCII_LIST(CP1,CP2) ->
+ length_1(Cont, N+1);
+length_1(Str, N) ->
+ case unicode_util:gc(Str) of
+ [] -> N;
+ [_|Rest] -> length_1(Rest, N+1)
+ end.
+
+length_b(<<CP2/utf8, Rest/binary>>, CP1, N)
+ when ?ASCII_LIST(CP1,CP2) ->
+ length_b(Rest, CP2, N+1);
+length_b(Bin0, CP1, N) ->
+ [_|Bin1] = unicode_util:gc([CP1|Bin0]),
+ case unicode_util:cp(Bin1) of
+ [] -> N+1;
+ [CP3|Bin] -> length_b(Bin, CP3, N+1)
+ end.
equal_1([A|AR], [B|BR]) when is_integer(A), is_integer(B) ->
A =:= B andalso equal_1(AR, BR);
@@ -540,29 +574,66 @@ equal_norm_nocase(A0, B0, Norm) ->
{L1,L2} when is_list(L1), is_list(L2) -> false
end.
+reverse_1([CP1|[CP2|_]=Cont], Acc) when ?ASCII_LIST(CP1,CP2) ->
+ reverse_1(Cont, [CP1|Acc]);
reverse_1(CD, Acc) ->
case unicode_util:gc(CD) of
[GC|Rest] -> reverse_1(Rest, [GC|Acc]);
[] -> Acc
end.
-slice_l(CD, N, Binary) when N > 0 ->
+reverse_b(<<CP2/utf8, Rest/binary>>, CP1, Acc)
+ when ?ASCII_LIST(CP1,CP2) ->
+ reverse_b(Rest, CP2, [CP1|Acc]);
+reverse_b(Bin0, CP1, Acc) ->
+ [GC|Bin1] = unicode_util:gc([CP1|Bin0]),
+ case unicode_util:cp(Bin1) of
+ [] -> [GC|Acc];
+ [CP3|Bin] -> reverse_b(Bin, CP3, [GC|Acc])
+ end.
+
+slice_l0(<<CP1/utf8, Bin/binary>>, N) when N > 0 ->
+ slice_lb(Bin, CP1, N);
+slice_l0(L, N) ->
+ slice_l(L, N).
+
+slice_l([CP1|[CP2|_]=Cont], N) when ?ASCII_LIST(CP1,CP2),N > 0 ->
+ slice_l(Cont, N-1);
+slice_l(CD, N) when N > 0 ->
case unicode_util:gc(CD) of
- [_|Cont] -> slice_l(Cont, N-1, Binary);
- [] when Binary -> <<>>;
+ [_|Cont] -> slice_l(Cont, N-1);
[] -> []
end;
-slice_l(Cont, 0, Binary) ->
- case is_empty(Cont) of
- true when Binary -> <<>>;
- _ -> Cont
+slice_l(Cont, 0) ->
+ Cont.
+
+slice_lb(<<CP2/utf8, Bin/binary>>, CP1, N) when ?ASCII_LIST(CP1,CP2), N > 1 ->
+ slice_lb(Bin, CP2, N-1);
+slice_lb(Bin, CP1, N) ->
+ [_|Rest] = unicode_util:gc([CP1|Bin]),
+ if N > 1 ->
+ case unicode_util:cp(Rest) of
+ [CP2|Cont] -> slice_lb(Cont, CP2, N-1);
+ [] -> <<>>
+ end;
+ N =:= 1 ->
+ Rest
end.
+slice_trail(Orig, N) when is_binary(Orig) ->
+ case Orig of
+ <<CP1/utf8, Bin/binary>> when N > 0 ->
+ Length = slice_bin(Bin, CP1, N),
+ Sz = byte_size(Orig) - Length,
+ <<Keep:Sz/binary, _/binary>> = Orig,
+ Keep;
+ _ -> <<>>
+ end;
slice_trail(CD, N) when is_list(CD) ->
- slice_list(CD, N);
-slice_trail(CD, N) when is_binary(CD) ->
- slice_bin(CD, N, CD).
+ slice_list(CD, N).
+slice_list([CP1|[CP2|_]=Cont], N) when ?ASCII_LIST(CP1,CP2),N > 0 ->
+ [CP1|slice_list(Cont, N-1)];
slice_list(CD, N) when N > 0 ->
case unicode_util:gc(CD) of
[GC|Cont] -> append(GC, slice_list(Cont, N-1));
@@ -571,17 +642,16 @@ slice_list(CD, N) when N > 0 ->
slice_list(_, 0) ->
[].
-slice_bin(CD, N, Orig) when N > 0 ->
- case unicode_util:gc(CD) of
- [_|Cont] -> slice_bin(Cont, N-1, Orig);
- [] -> Orig
+slice_bin(<<CP2/utf8, Bin/binary>>, CP1, N) when ?ASCII_LIST(CP1,CP2), N > 0 ->
+ slice_bin(Bin, CP2, N-1);
+slice_bin(CD, CP1, N) when N > 0 ->
+ [_|Bin] = unicode_util:gc([CP1|CD]),
+ case unicode_util:cp(Bin) of
+ [CP2|Cont] -> slice_bin(Cont, CP2, N-1);
+ [] -> 0
end;
-slice_bin([], 0, Orig) ->
- Orig;
-slice_bin(CD, 0, Orig) ->
- Sz = byte_size(Orig) - byte_size(CD),
- <<Keep:Sz/binary, _/binary>> = Orig,
- Keep.
+slice_bin(CD, CP1, 0) ->
+ byte_size(CD)+byte_size(<<CP1/utf8>>).
uppercase_list(CPs0) ->
case unicode_util:uppercase(CPs0) of
@@ -631,16 +701,31 @@ casefold_bin(CPs0, Acc) ->
[] -> Acc
end.
-
+%% Fast path for ascii searching for one character in lists
+trim_ls([CP1|[CP2|_]=Cont]=Str, Sep)
+ when ?ASCII_LIST(CP1,CP2) ->
+ case Sep of
+ CP1 -> trim_ls(Cont, Sep);
+ _ -> Str
+ end;
+trim_ls(Str, Sep) ->
+ trim_l(Str, [Sep]).
+
+trim_l([CP1|[CP2|_]=Cont]=Str, Sep)
+ when ?ASCII_LIST(CP1,CP2) ->
+ case lists:member(CP1, Sep) of
+ true -> trim_l(Cont, Sep);
+ false -> Str
+ end;
trim_l([Bin|Cont0], Sep) when is_binary(Bin) ->
case bin_search_inv(Bin, Cont0, Sep) of
{nomatch, Cont} -> trim_l(Cont, Sep);
Keep -> Keep
end;
-trim_l(Str, {GCs, _, _}=Sep) when is_list(Str) ->
+trim_l(Str, Sep) when is_list(Str) ->
case unicode_util:gc(Str) of
[C|Cs] ->
- case lists:member(C, GCs) of
+ case lists:member(C, Sep) of
true -> trim_l(Cs, Sep);
false -> Str
end;
@@ -652,15 +737,51 @@ trim_l(Bin, Sep) when is_binary(Bin) ->
[Keep] -> Keep
end.
-trim_t([Bin|Cont0], N, Sep) when is_binary(Bin) ->
+%% Fast path for ascii searching for one character in lists
+trim_ts([Sep|Cs1]=Str, Sep) ->
+ case Cs1 of
+ [] -> [];
+ [CP2|_] when ?ASCII_LIST(Sep,CP2) ->
+ Tail = trim_ts(Cs1, Sep),
+ case is_empty(Tail) of
+ true -> [];
+ false -> [Sep|Tail]
+ end;
+ _ ->
+ trim_t(Str, 0, search_pattern([Sep]))
+ end;
+trim_ts([CP|Cont],Sep) when is_integer(CP) ->
+ [CP|trim_ts(Cont, Sep)];
+trim_ts(Str, Sep) ->
+ trim_t(Str, 0, search_pattern([Sep])).
+
+trim_t([CP1|Cont]=Cs0, _, {GCs,CPs,_}=Seps) when is_integer(CP1) ->
+ case lists:member(CP1, CPs) of
+ true ->
+ [GC|Cs1] = unicode_util:gc(Cs0),
+ case lists:member(GC, GCs) of
+ true ->
+ Tail = trim_t(Cs1, 0, Seps),
+ case is_empty(Tail) of
+ true -> [];
+ false -> append(GC,Tail)
+ end;
+ false ->
+ append(GC,trim_t(Cs1, 0, Seps))
+ end;
+ false ->
+ [CP1|trim_t(Cont, 0, Seps)]
+ end;
+trim_t([Bin|Cont0], N, {GCs,_,_}=Seps0) when is_binary(Bin) ->
<<_:N/binary, Rest/binary>> = Bin,
- case bin_search(Rest, Cont0, Sep) of
+ Seps = search_compile(Seps0),
+ case bin_search(Rest, Cont0, Seps) of
{nomatch,_} ->
- stack(Bin, trim_t(Cont0, 0, Sep));
+ stack(Bin, trim_t(Cont0, 0, Seps));
[SepStart|Cont1] ->
- case bin_search_inv(SepStart, Cont1, Sep) of
+ case bin_search_inv(SepStart, Cont1, GCs) of
{nomatch, Cont} ->
- Tail = trim_t(Cont, 0, Sep),
+ Tail = trim_t(Cont, 0, Seps),
case is_empty(Tail) of
true ->
KeepSz = byte_size(Bin) - byte_size(SepStart),
@@ -672,67 +793,69 @@ trim_t([Bin|Cont0], N, Sep) when is_binary(Bin) ->
end;
[NonSep|Cont] when is_binary(NonSep) ->
KeepSz = byte_size(Bin) - byte_size(NonSep),
- trim_t([Bin|Cont], KeepSz, Sep)
+ trim_t([Bin|Cont], KeepSz, Seps)
end
end;
-trim_t(Str, 0, {GCs,CPs,_}=Sep) when is_list(Str) ->
- case unicode_util:cp(Str) of
- [CP|Cs] ->
- case lists:member(CP, CPs) of
+trim_t(Str, 0, {GCs,_,_}=Seps) when is_list(Str) ->
+ case unicode_util:gc(Str) of
+ [GC|Cs1] ->
+ case lists:member(GC, GCs) of
true ->
- [GC|Cs1] = unicode_util:gc(Str),
- case lists:member(GC, GCs) of
- true ->
- Tail = trim_t(Cs1, 0, Sep),
- case is_empty(Tail) of
- true -> [];
- false -> append(GC,Tail)
- end;
- false ->
- append(GC,trim_t(Cs1, 0, Sep))
+ Tail = trim_t(Cs1, 0, Seps),
+ case is_empty(Tail) of
+ true -> [];
+ false -> append(GC,Tail)
end;
false ->
- append(CP,trim_t(Cs, 0, Sep))
+ append(GC,trim_t(Cs1, 0, Seps))
end;
[] -> []
end;
-trim_t(Bin, N, Sep) when is_binary(Bin) ->
+trim_t(Bin, N, {GCs,_,_}=Seps0) when is_binary(Bin) ->
<<_:N/binary, Rest/binary>> = Bin,
- case bin_search(Rest, Sep) of
+ Seps = search_compile(Seps0),
+ case bin_search(Rest, [], Seps) of
{nomatch,_} -> Bin;
[SepStart] ->
- case bin_search_inv(SepStart, [], Sep) of
+ case bin_search_inv(SepStart, [], GCs) of
{nomatch,_} ->
KeepSz = byte_size(Bin) - byte_size(SepStart),
<<Keep:KeepSz/binary, _/binary>> = Bin,
Keep;
[NonSep] ->
KeepSz = byte_size(Bin) - byte_size(NonSep),
- trim_t(Bin, KeepSz, Sep)
+ trim_t(Bin, KeepSz, Seps)
end
end.
-take_l([Bin|Cont0], Sep, Acc) when is_binary(Bin) ->
- case bin_search_inv(Bin, Cont0, Sep) of
+
+take_l([CP1|[CP2|_]=Cont]=Str, Seps, Acc)
+ when ?ASCII_LIST(CP1,CP2) ->
+ case lists:member(CP1, Seps) of
+ true -> take_l(Cont, Seps, [CP1|Acc]);
+ false -> {rev(Acc), Str}
+ end;
+take_l([Bin|Cont0], Seps, Acc) when is_binary(Bin) ->
+ case bin_search_inv(Bin, Cont0, Seps) of
{nomatch, Cont} ->
Used = cp_prefix(Cont0, Cont),
- take_l(Cont, Sep, [unicode:characters_to_binary([Bin|Used])|Acc]);
+ take_l(Cont, Seps, [unicode:characters_to_binary([Bin|Used])|Acc]);
[Bin1|_]=After when is_binary(Bin1) ->
First = byte_size(Bin) - byte_size(Bin1),
<<Keep:First/binary, _/binary>> = Bin,
{btoken(Keep,Acc), After}
end;
-take_l(Str, {GCs, _, _}=Sep, Acc) when is_list(Str) ->
+take_l(Str, Seps, Acc) when is_list(Str) ->
case unicode_util:gc(Str) of
[C|Cs] ->
- case lists:member(C, GCs) of
- true -> take_l(Cs, Sep, append(rev(C),Acc));
+ case lists:member(C, Seps) of
+ true -> take_l(Cs, Seps, append(rev(C),Acc));
false -> {rev(Acc), Str}
end;
[] -> {rev(Acc), []}
end;
-take_l(Bin, Sep, Acc) when is_binary(Bin) ->
- case bin_search_inv(Bin, [], Sep) of
+take_l(Bin, Seps, Acc) when is_binary(Bin) ->
+ case bin_search_inv(Bin, [], Seps) of
{nomatch,_} ->
{btoken(Bin, Acc), <<>>};
[After] ->
@@ -741,27 +864,41 @@ take_l(Bin, Sep, Acc) when is_binary(Bin) ->
{btoken(Keep, Acc), After}
end.
-take_lc([Bin|Cont0], Sep, Acc) when is_binary(Bin) ->
- case bin_search(Bin, Cont0, Sep) of
+
+take_lc([CP1|Cont]=Str0, {GCs,CPs,_}=Seps, Acc) when is_integer(CP1) ->
+ case lists:member(CP1, CPs) of
+ true ->
+ [GC|Str] = unicode_util:gc(Str0),
+ case lists:member(GC, GCs) of
+ false -> take_lc(Str, Seps, append(rev(GC),Acc));
+ true -> {rev(Acc), Str0}
+ end;
+ false ->
+ take_lc(Cont, Seps, append(CP1,Acc))
+ end;
+take_lc([Bin|Cont0], Seps0, Acc) when is_binary(Bin) ->
+ Seps = search_compile(Seps0),
+ case bin_search(Bin, Cont0, Seps) of
{nomatch, Cont} ->
Used = cp_prefix(Cont0, Cont),
- take_lc(Cont, Sep, [unicode:characters_to_binary([Bin|Used])|Acc]);
+ take_lc(Cont, Seps, [unicode:characters_to_binary([Bin|Used])|Acc]);
[Bin1|_]=After when is_binary(Bin1) ->
First = byte_size(Bin) - byte_size(Bin1),
<<Keep:First/binary, _/binary>> = Bin,
{btoken(Keep,Acc), After}
end;
-take_lc(Str, {GCs, _, _}=Sep, Acc) when is_list(Str) ->
+take_lc(Str, {GCs,_,_}=Seps, Acc) when is_list(Str) ->
case unicode_util:gc(Str) of
[C|Cs] ->
case lists:member(C, GCs) of
- false -> take_lc(Cs, Sep, append(rev(C),Acc));
+ false -> take_lc(Cs, Seps, append(rev(C),Acc));
true -> {rev(Acc), Str}
end;
[] -> {rev(Acc), []}
end;
-take_lc(Bin, Sep, Acc) when is_binary(Bin) ->
- case bin_search(Bin, [], Sep) of
+take_lc(Bin, Seps0, Acc) when is_binary(Bin) ->
+ Seps = search_compile(Seps0),
+ case bin_search(Bin, [], Seps) of
{nomatch,_} ->
{btoken(Bin, Acc), <<>>};
[After] ->
@@ -770,148 +907,192 @@ take_lc(Bin, Sep, Acc) when is_binary(Bin) ->
{btoken(Keep, Acc), After}
end.
-take_t([Bin|Cont0], N, Sep) when is_binary(Bin) ->
+
+take_t([CP1|Cont]=Str0, _, {GCs,CPs,_}=Seps) when is_integer(CP1) ->
+ case lists:member(CP1, CPs) of
+ true ->
+ [GC|Str] = unicode_util:gc(Str0),
+ case lists:member(GC, GCs) of
+ true ->
+ {Head, Tail} = take_t(Str, 0, Seps),
+ case is_empty(Head) of
+ true -> {Head, append(GC,Tail)};
+ false -> {append(GC,Head), Tail}
+ end;
+ false ->
+ {Head, Tail} = take_t(Str, 0, Seps),
+ {append(GC,Head), Tail}
+ end;
+ false ->
+ {Head, Tail} = take_t(Cont, 0, Seps),
+ {[CP1|Head], Tail}
+ end;
+take_t([Bin|Cont0], N, {GCs,_,_}=Seps0) when is_binary(Bin) ->
<<_:N/binary, Rest/binary>> = Bin,
- case bin_search(Rest, Cont0, Sep) of
+ Seps = search_compile(Seps0),
+ case bin_search(Rest, Cont0, Seps) of
{nomatch,Cont} ->
Used = cp_prefix(Cont0, Cont),
- {Head, Tail} = take_t(Cont, 0, Sep),
+ {Head, Tail} = take_t(Cont, 0, Seps),
{stack(unicode:characters_to_binary([Bin|Used]), Head), Tail};
[SepStart|Cont1] ->
- case bin_search_inv(SepStart, Cont1, Sep) of
+ case bin_search_inv(SepStart, Cont1, GCs) of
{nomatch, Cont} ->
- {Head, Tail} = take_t(Cont, 0, Sep),
+ {Head, Tail} = take_t(Cont, 0, Seps),
Used = cp_prefix(Cont0, Cont),
- case equal(Tail, Cont) of
+ case is_empty(Head) of
true ->
KeepSz = byte_size(Bin) - byte_size(SepStart),
<<Keep:KeepSz/binary, End/binary>> = Bin,
- {stack(Keep,Head), stack(stack(End,Used),Tail)};
+ {Keep, stack(stack(End,Used),Tail)};
false ->
{stack(unicode:characters_to_binary([Bin|Used]),Head), Tail}
end;
[NonSep|Cont] when is_binary(NonSep) ->
KeepSz = byte_size(Bin) - byte_size(NonSep),
- take_t([Bin|Cont], KeepSz, Sep)
+ take_t([Bin|Cont], KeepSz, Seps)
end
end;
-take_t(Str, 0, {GCs,CPs,_}=Sep) when is_list(Str) ->
- case unicode_util:cp(Str) of
- [CP|Cs] ->
- case lists:member(CP, CPs) of
+take_t(Str, 0, {GCs,_,_}=Seps) when is_list(Str) ->
+ case unicode_util:gc(Str) of
+ [GC|Cs1] ->
+ case lists:member(GC, GCs) of
true ->
- [GC|Cs1] = unicode_util:gc(Str),
- case lists:member(GC, GCs) of
- true ->
- {Head, Tail} = take_t(Cs1, 0, Sep),
- case equal(Tail, Cs1) of
- true -> {Head, append(GC,Tail)};
- false -> {append(GC,Head), Tail}
- end;
- false ->
- {Head, Tail} = take_t(Cs, 0, Sep),
- {append(CP,Head), Tail}
+ {Head, Tail} = take_t(Cs1, 0, Seps),
+ case is_empty(Head) of
+ true -> {Head, append(GC,Tail)};
+ false -> {append(GC,Head), Tail}
end;
false ->
- {Head, Tail} = take_t(Cs, 0, Sep),
- {append(CP,Head), Tail}
+ {Head, Tail} = take_t(Cs1, 0, Seps),
+ {append(GC,Head), Tail}
end;
[] -> {[],[]}
end;
-take_t(Bin, N, Sep) when is_binary(Bin) ->
+take_t(Bin, N, {GCs,_,_}=Seps0) when is_binary(Bin) ->
<<_:N/binary, Rest/binary>> = Bin,
- case bin_search(Rest, Sep) of
+ Seps = search_compile(Seps0),
+ case bin_search(Rest, [], Seps) of
{nomatch,_} -> {Bin, <<>>};
[SepStart] ->
- case bin_search_inv(SepStart, [], Sep) of
+ case bin_search_inv(SepStart, [], GCs) of
{nomatch,_} ->
KeepSz = byte_size(Bin) - byte_size(SepStart),
<<Before:KeepSz/binary, End/binary>> = Bin,
{Before, End};
[NonSep] ->
KeepSz = byte_size(Bin) - byte_size(NonSep),
- take_t(Bin, KeepSz, Sep)
+ take_t(Bin, KeepSz, Seps)
end
end.
-take_tc([Bin|Cont0], N, Sep) when is_binary(Bin) ->
+take_tc([CP1|[CP2|_]=Cont], _, {GCs,_,_}=Seps) when ?ASCII_LIST(CP1,CP2) ->
+ case lists:member(CP1, GCs) of
+ false ->
+ {Head, Tail} = take_tc(Cont, 0, Seps),
+ case is_empty(Head) of
+ true -> {Head, append(CP1,Tail)};
+ false -> {append(CP1,Head), Tail}
+ end;
+ true ->
+ {Head, Tail} = take_tc(Cont, 0, Seps),
+ {append(CP1,Head), Tail}
+ end;
+take_tc([Bin|Cont0], N, {GCs,_,_}=Seps0) when is_binary(Bin) ->
<<_:N/binary, Rest/binary>> = Bin,
- case bin_search_inv(Rest, Cont0, Sep) of
+ case bin_search_inv(Rest, Cont0, GCs) of
{nomatch,Cont} ->
Used = cp_prefix(Cont0, Cont),
- {Head, Tail} = take_tc(Cont, 0, Sep),
+ {Head, Tail} = take_tc(Cont, 0, Seps0),
{stack(unicode:characters_to_binary([Bin|Used]), Head), Tail};
[SepStart|Cont1] ->
- case bin_search(SepStart, Cont1, Sep) of
+ Seps = search_compile(Seps0),
+ case bin_search(SepStart, Cont1, Seps) of
{nomatch, Cont} ->
- {Head, Tail} = take_tc(Cont, 0, Sep),
+ {Head, Tail} = take_tc(Cont, 0, Seps),
Used = cp_prefix(Cont0, Cont),
- case equal(Tail, Cont) of
+ case is_empty(Head) of
true ->
KeepSz = byte_size(Bin) - byte_size(SepStart),
<<Keep:KeepSz/binary, End/binary>> = Bin,
- {stack(Keep,Head), stack(stack(End,Used),Tail)};
+ {Keep, stack(stack(End,Used),Tail)};
false ->
{stack(unicode:characters_to_binary([Bin|Used]),Head), Tail}
end;
[NonSep|Cont] when is_binary(NonSep) ->
KeepSz = byte_size(Bin) - byte_size(NonSep),
- take_tc([Bin|Cont], KeepSz, Sep)
+ take_tc([Bin|Cont], KeepSz, Seps)
end
end;
-take_tc(Str, 0, {GCs,CPs,_}=Sep) when is_list(Str) ->
- case unicode_util:cp(Str) of
- [CP|Cs] ->
- case lists:member(CP, CPs) of
- true ->
- [GC|Cs1] = unicode_util:gc(Str),
- case lists:member(GC, GCs) of
- false ->
- {Head, Tail} = take_tc(Cs1, 0, Sep),
- case equal(Tail, Cs1) of
- true -> {Head, append(GC,Tail)};
- false -> {append(GC,Head), Tail}
- end;
- true ->
- {Head, Tail} = take_tc(Cs1, 0, Sep),
- {append(GC,Head), Tail}
- end;
+take_tc(Str, 0, {GCs,_,_}=Seps) when is_list(Str) ->
+ case unicode_util:gc(Str) of
+ [GC|Cs1] ->
+ case lists:member(GC, GCs) of
false ->
- {Head, Tail} = take_tc(Cs, 0, Sep),
- case equal(Tail, Cs) of
- true -> {Head, append(CP,Tail)};
- false -> {append(CP,Head), Tail}
- end
+ {Head, Tail} = take_tc(Cs1, 0, Seps),
+ case is_empty(Head) of
+ true -> {Head, append(GC,Tail)};
+ false -> {append(GC,Head), Tail}
+ end;
+ true ->
+ {Head, Tail} = take_tc(Cs1, 0, Seps),
+ {append(GC,Head), Tail}
end;
[] -> {[],[]}
end;
-take_tc(Bin, N, Sep) when is_binary(Bin) ->
+take_tc(Bin, N, {GCs,_,_}=Seps0) when is_binary(Bin) ->
<<_:N/binary, Rest/binary>> = Bin,
- case bin_search_inv(Rest, [], Sep) of
+ case bin_search_inv(Rest, [], GCs) of
{nomatch,_} -> {Bin, <<>>};
[SepStart] ->
- case bin_search(SepStart, [], Sep) of
+ Seps = search_compile(Seps0),
+ case bin_search(SepStart, [], Seps) of
{nomatch,_} ->
KeepSz = byte_size(Bin) - byte_size(SepStart),
<<Before:KeepSz/binary, End/binary>> = Bin,
{Before, End};
[NonSep] ->
KeepSz = byte_size(Bin) - byte_size(NonSep),
- take_tc(Bin, KeepSz, Sep)
+ take_tc(Bin, KeepSz, Seps)
end
end.
-prefix_1(Cs, []) -> Cs;
-prefix_1(Cs, [_]=Pre) ->
- prefix_2(unicode_util:gc(Cs), Pre);
-prefix_1(Cs, Pre) ->
- prefix_2(unicode_util:cp(Cs), Pre).
-
-prefix_2([C|Cs], [C|Pre]) ->
- prefix_1(Cs, Pre);
-prefix_2(_, _) ->
- nomatch.
+prefix_1(Cs0, [GC]) ->
+ case unicode_util:gc(Cs0) of
+ [GC|Cs] -> Cs;
+ _ -> nomatch
+ end;
+prefix_1([CP|Cs], [Pre|PreR]) when is_integer(CP) ->
+ case CP =:= Pre of
+ true -> prefix_1(Cs,PreR);
+ false -> nomatch
+ end;
+prefix_1(<<CP/utf8, Cs/binary>>, [Pre|PreR]) ->
+ case CP =:= Pre of
+ true -> prefix_1(Cs,PreR);
+ false -> nomatch
+ end;
+prefix_1(Cs0, [Pre|PreR]) ->
+ case unicode_util:cp(Cs0) of
+ [Pre|Cs] -> prefix_1(Cs,PreR);
+ _ -> nomatch
+ end.
+split_1([CP1|Cs]=Cs0, [C|_]=Needle, _, Where, Curr, Acc) when is_integer(CP1) ->
+ case CP1=:=C of
+ true ->
+ case prefix_1(Cs0, Needle) of
+ nomatch -> split_1(Cs, Needle, 0, Where, append(C,Curr), Acc);
+ Rest when Where =:= leading ->
+ [rev(Curr), Rest];
+ Rest when Where =:= trailing ->
+ split_1(Cs, Needle, 0, Where, [C|Curr], [rev(Curr), Rest]);
+ Rest when Where =:= all ->
+ split_1(Rest, Needle, 0, Where, [], [rev(Curr)|Acc])
+ end;
+ false ->
+ split_1(Cs, Needle, 0, Where, append(CP1,Curr), Acc)
+ end;
split_1([Bin|Cont0], Needle, Start, Where, Curr0, Acc)
when is_binary(Bin) ->
case bin_search_str(Bin, Start, Cont0, Needle) of
@@ -971,32 +1152,50 @@ split_1(Bin, [_C|_]=Needle, Start, Where, Curr0, Acc) ->
end
end.
-lexemes_m([Bin|Cont0], Seps, Ts) when is_binary(Bin) ->
- case bin_search_inv(Bin, Cont0, Seps) of
+lexemes_m([CP|_]=Cs0, {GCs,CPs,_}=Seps, Ts) when is_integer(CP) ->
+ case lists:member(CP, CPs) of
+ true ->
+ [GC|Cs2] = unicode_util:gc(Cs0),
+ case lists:member(GC, GCs) of
+ true ->
+ lexemes_m(Cs2, Seps, Ts);
+ false ->
+ {Lexeme,Rest} = lexeme_pick(Cs0, Seps, []),
+ lexemes_m(Rest, Seps, [Lexeme|Ts])
+ end;
+ false ->
+ {Lexeme,Rest} = lexeme_pick(Cs0, Seps, []),
+ lexemes_m(Rest, Seps, [Lexeme|Ts])
+ end;
+lexemes_m([Bin|Cont0], {GCs,_,_}=Seps0, Ts) when is_binary(Bin) ->
+ case bin_search_inv(Bin, Cont0, GCs) of
{nomatch,Cont} ->
- lexemes_m(Cont, Seps, Ts);
+ lexemes_m(Cont, Seps0, Ts);
Cs ->
+ Seps = search_compile(Seps0),
{Lexeme,Rest} = lexeme_pick(Cs, Seps, []),
lexemes_m(Rest, Seps, [Lexeme|Ts])
end;
-lexemes_m(Cs0, {GCs, _, _}=Seps, Ts) when is_list(Cs0) ->
+lexemes_m(Cs0, {GCs, _, _}=Seps0, Ts) when is_list(Cs0) ->
case unicode_util:gc(Cs0) of
[C|Cs] ->
case lists:member(C, GCs) of
true ->
- lexemes_m(Cs, Seps, Ts);
+ lexemes_m(Cs, Seps0, Ts);
false ->
+ Seps = search_compile(Seps0),
{Lexeme,Rest} = lexeme_pick(Cs0, Seps, []),
lexemes_m(Rest, Seps, [Lexeme|Ts])
end;
[] ->
lists:reverse(Ts)
end;
-lexemes_m(Bin, Seps, Ts) when is_binary(Bin) ->
- case bin_search_inv(Bin, [], Seps) of
+lexemes_m(Bin, {GCs,_,_}=Seps0, Ts) when is_binary(Bin) ->
+ case bin_search_inv(Bin, [], GCs) of
{nomatch,_} ->
lists:reverse(Ts);
[Cs] ->
+ Seps = search_compile(Seps0),
{Lexeme,Rest} = lexeme_pick(Cs, Seps, []),
lexemes_m(Rest, Seps, add_non_empty(Lexeme,Ts))
end.
@@ -1027,7 +1226,7 @@ lexeme_pick(Cs0, {GCs, CPs, _} = Seps, Tkn) when is_list(Cs0) ->
true ->
[GC|Cs2] = unicode_util:gc(Cs0),
case lists:member(GC, GCs) of
- true -> {rev(Tkn), Cs0};
+ true -> {rev(Tkn), Cs2};
false -> lexeme_pick(Cs2, Seps, append(rev(GC),Tkn))
end;
false ->
@@ -1037,7 +1236,7 @@ lexeme_pick(Cs0, {GCs, CPs, _} = Seps, Tkn) when is_list(Cs0) ->
{rev(Tkn), []}
end;
lexeme_pick(Bin, Seps, Tkn) when is_binary(Bin) ->
- case bin_search(Bin, Seps) of
+ case bin_search(Bin, [], Seps) of
{nomatch,_} ->
{btoken(Bin,Tkn), []};
[Left] ->
@@ -1046,35 +1245,38 @@ lexeme_pick(Bin, Seps, Tkn) when is_binary(Bin) ->
{btoken(Lexeme, Tkn), Left}
end.
-nth_lexeme_m([Bin|Cont0], Seps, N) when is_binary(Bin) ->
- case bin_search_inv(Bin, Cont0, Seps) of
+nth_lexeme_m([Bin|Cont0], {GCs,_,_}=Seps0, N) when is_binary(Bin) ->
+ case bin_search_inv(Bin, Cont0, GCs) of
{nomatch,Cont} ->
- nth_lexeme_m(Cont, Seps, N);
+ nth_lexeme_m(Cont, Seps0, N);
Cs when N > 1 ->
- Rest = lexeme_skip(Cs, Seps),
- nth_lexeme_m(Rest, Seps, N-1);
+ Rest = lexeme_skip(Cs, Seps0),
+ nth_lexeme_m(Rest, Seps0, N-1);
Cs ->
+ Seps = search_compile(Seps0),
{Lexeme,_} = lexeme_pick(Cs, Seps, []),
Lexeme
end;
-nth_lexeme_m(Cs0, {GCs, _, _}=Seps, N) when is_list(Cs0) ->
+nth_lexeme_m(Cs0, {GCs, _, _}=Seps0, N) when is_list(Cs0) ->
case unicode_util:gc(Cs0) of
[C|Cs] ->
case lists:member(C, GCs) of
true ->
- nth_lexeme_m(Cs, Seps, N);
+ nth_lexeme_m(Cs, Seps0, N);
false when N > 1 ->
- Cs1 = lexeme_skip(Cs, Seps),
- nth_lexeme_m(Cs1, Seps, N-1);
+ Cs1 = lexeme_skip(Cs, Seps0),
+ nth_lexeme_m(Cs1, Seps0, N-1);
false ->
+ Seps = search_compile(Seps0),
{Lexeme,_} = lexeme_pick(Cs0, Seps, []),
Lexeme
end;
[] ->
[]
end;
-nth_lexeme_m(Bin, Seps, N) when is_binary(Bin) ->
- case bin_search_inv(Bin, [], Seps) of
+nth_lexeme_m(Bin, {GCs,_,_}=Seps0, N) when is_binary(Bin) ->
+ Seps = search_compile(Seps0),
+ case bin_search_inv(Bin, [], GCs) of
[Cs] when N > 1 ->
Cs1 = lexeme_skip(Cs, Seps),
nth_lexeme_m(Cs1, Seps, N-1);
@@ -1090,16 +1292,17 @@ lexeme_skip([CP|Cs1]=Cs0, {GCs,CPs,_}=Seps) when is_integer(CP) ->
true ->
[GC|Cs2] = unicode_util:gc(Cs0),
case lists:member(GC, GCs) of
- true -> Cs0;
+ true -> Cs2;
false -> lexeme_skip(Cs2, Seps)
end;
false ->
lexeme_skip(Cs1, Seps)
end;
-lexeme_skip([Bin|Cont0], Seps) when is_binary(Bin) ->
+lexeme_skip([Bin|Cont0], Seps0) when is_binary(Bin) ->
+ Seps = search_compile(Seps0),
case bin_search(Bin, Cont0, Seps) of
{nomatch,_} -> lexeme_skip(Cont0, Seps);
- Cs -> Cs
+ Cs -> tl(unicode_util:gc(Cs))
end;
lexeme_skip(Cs0, {GCs, CPs, _} = Seps) when is_list(Cs0) ->
case unicode_util:cp(Cs0) of
@@ -1108,7 +1311,7 @@ lexeme_skip(Cs0, {GCs, CPs, _} = Seps) when is_list(Cs0) ->
true ->
[GC|Cs2] = unicode_util:gc(Cs0),
case lists:member(GC, GCs) of
- true -> Cs0;
+ true -> Cs2;
false -> lexeme_skip(Cs2, Seps)
end;
false ->
@@ -1117,12 +1320,23 @@ lexeme_skip(Cs0, {GCs, CPs, _} = Seps) when is_list(Cs0) ->
[] ->
[]
end;
-lexeme_skip(Bin, Seps) when is_binary(Bin) ->
- case bin_search(Bin, Seps) of
+lexeme_skip(Bin, Seps0) when is_binary(Bin) ->
+ Seps = search_compile(Seps0),
+ case bin_search(Bin, [], Seps) of
{nomatch,_} -> <<>>;
- [Left] -> Left
+ [Left] -> tl(unicode_util:gc(Left))
end.
+find_l([C1|Cs]=Cs0, [C|_]=Needle) when is_integer(C1) ->
+ case C1 of
+ C ->
+ case prefix_1(Cs0, Needle) of
+ nomatch -> find_l(Cs, Needle);
+ _ -> Cs0
+ end;
+ _ ->
+ find_l(Cs, Needle)
+ end;
find_l([Bin|Cont0], Needle) when is_binary(Bin) ->
case bin_search_str(Bin, 0, Cont0, Needle) of
{nomatch, _, Cont} ->
@@ -1147,6 +1361,16 @@ find_l(Bin, Needle) ->
{_Before, [Cs], _After} -> Cs
end.
+find_r([Cp|Cs]=Cs0, [C|_]=Needle, Res) when is_integer(Cp) ->
+ case Cp of
+ C ->
+ case prefix_1(Cs0, Needle) of
+ nomatch -> find_r(Cs, Needle, Res);
+ _ -> find_r(Cs, Needle, Cs0)
+ end;
+ _ ->
+ find_r(Cs, Needle, Res)
+ end;
find_r([Bin|Cont0], Needle, Res) when is_binary(Bin) ->
case bin_search_str(Bin, 0, Cont0, Needle) of
{nomatch,_,Cont} ->
@@ -1217,11 +1441,6 @@ cp_prefix_1(Orig, Until, Cont) ->
%% Binary special
-bin_search(Bin, Seps) ->
- bin_search(Bin, [], Seps).
-
-bin_search(_Bin, Cont, {[],_,_}) ->
- {nomatch, Cont};
bin_search(Bin, Cont, {Seps,_,BP}) ->
bin_search_loop(Bin, 0, BP, Cont, Seps).
@@ -1229,10 +1448,14 @@ bin_search(Bin, Cont, {Seps,_,BP}) ->
%% i.e. å in nfd form $a "COMBINING RING ABOVE"
%% and PREPEND characters like "ARABIC NUMBER SIGN" 1536 <<216,128>>
%% combined with other characters are currently ignored.
+search_pattern({_,_,_}=P) -> P;
search_pattern(Seps) ->
CPs = search_cp(Seps),
- Bin = bin_pattern(CPs),
- {Seps, CPs, Bin}.
+ {Seps, CPs, undefined}.
+
+search_compile({Sep, CPs, undefined}) ->
+ {Sep, CPs, binary:compile_pattern(bin_pattern(CPs))};
+search_compile({_,_,_}=Compiled) -> Compiled.
search_cp([CP|Seps]) when is_integer(CP) ->
[CP|search_cp(Seps)];
@@ -1253,9 +1476,21 @@ bin_search_loop(Bin0, Start, BinSeps, Cont, Seps) ->
case binary:match(Bin, BinSeps) of
nomatch ->
{nomatch,Cont};
+ {Where, _CL} when Cont =:= [] ->
+ <<_:Where/binary, Cont1/binary>> = Bin,
+ [GC|Cont2] = unicode_util:gc(Cont1),
+ case lists:member(GC, Seps) of
+ false when Cont2 =:= [] ->
+ {nomatch, []};
+ false ->
+ Next = byte_size(Bin0) - byte_size(Cont2),
+ bin_search_loop(Bin0, Next, BinSeps, Cont, Seps);
+ true ->
+ [Cont1]
+ end;
{Where, _CL} ->
<<_:Where/binary, Cont0/binary>> = Bin,
- Cont1 = stack(Cont0, Cont),
+ Cont1 = [Cont0|Cont],
[GC|Cont2] = unicode_util:gc(Cont1),
case lists:member(GC, Seps) of
false ->
@@ -1263,55 +1498,108 @@ bin_search_loop(Bin0, Start, BinSeps, Cont, Seps) ->
[BinR|Cont] when is_binary(BinR) ->
Next = byte_size(Bin0) - byte_size(BinR),
bin_search_loop(Bin0, Next, BinSeps, Cont, Seps);
- BinR when is_binary(BinR), Cont =:= [] ->
- Next = byte_size(Bin0) - byte_size(BinR),
- bin_search_loop(Bin0, Next, BinSeps, Cont, Seps);
_ ->
{nomatch, Cont2}
end;
- true when is_list(Cont1) ->
- Cont1;
true ->
- [Cont1]
+ Cont1
end
end.
-bin_search_inv(Bin, Cont, {[], _, _}) ->
- [Bin|Cont];
-bin_search_inv(Bin, Cont, {[Sep], _, _}) ->
- bin_search_inv_1([Bin|Cont], Sep);
-bin_search_inv(Bin, Cont, {Seps, _, _}) ->
- bin_search_inv_n([Bin|Cont], Seps).
-
-bin_search_inv_1([<<>>|CPs], _) ->
- {nomatch, CPs};
-bin_search_inv_1(CPs = [Bin0|Cont], Sep) when is_binary(Bin0) ->
- case unicode_util:gc(CPs) of
- [Sep|Bin] when is_binary(Bin), Cont =:= [] ->
- bin_search_inv_1([Bin], Sep);
- [Sep|[Bin|Cont]=Cs] when is_binary(Bin) ->
- bin_search_inv_1(Cs, Sep);
- [Sep|Cs] ->
- {nomatch, Cs};
- _ -> CPs
- end.
+bin_search_inv(<<>>, Cont, _) ->
+ {nomatch, Cont};
+bin_search_inv(Bin, Cont, [Sep]) ->
+ bin_search_inv_1(Bin, Cont, Sep);
+bin_search_inv(Bin, Cont, Seps) ->
+ bin_search_inv_n(Bin, Cont, Seps).
+
+bin_search_inv_1(<<CP1/utf8, BinRest/binary>>=Bin0, Cont, Sep) ->
+ case BinRest of
+ <<CP2/utf8, _/binary>> when ?ASCII_LIST(CP1, CP2) ->
+ case CP1 of
+ Sep -> bin_search_inv_1(BinRest, Cont, Sep);
+ _ -> [Bin0|Cont]
+ end;
+ _ when Cont =:= [] ->
+ case unicode_util:gc(Bin0) of
+ [Sep|Bin] -> bin_search_inv_1(Bin, Cont, Sep);
+ _ -> [Bin0|Cont]
+ end;
+ _ ->
+ case unicode_util:gc([Bin0|Cont]) of
+ [Sep|[Bin|Cont]] when is_binary(Bin) ->
+ bin_search_inv_1(Bin, Cont, Sep);
+ [Sep|Cs] ->
+ {nomatch, Cs};
+ _ -> [Bin0|Cont]
+ end
+ end;
+bin_search_inv_1(<<>>, Cont, _Sep) ->
+ {nomatch, Cont};
+bin_search_inv_1([], Cont, _Sep) ->
+ {nomatch, Cont}.
-bin_search_inv_n([<<>>|CPs], _) ->
- {nomatch, CPs};
-bin_search_inv_n([Bin0|Cont]=CPs, Seps) when is_binary(Bin0) ->
- [C|Cs0] = unicode_util:gc(CPs),
- case {lists:member(C, Seps), Cs0} of
- {true, Cs} when is_binary(Cs), Cont =:= [] ->
- bin_search_inv_n([Cs], Seps);
- {true, [Bin|Cont]=Cs} when is_binary(Bin) ->
- bin_search_inv_n(Cs, Seps);
- {true, Cs} -> {nomatch, Cs};
- {false, _} -> CPs
- end.
+bin_search_inv_n(<<CP1/utf8, BinRest/binary>>=Bin0, Cont, Seps) ->
+ case BinRest of
+ <<CP2/utf8, _/binary>> when ?ASCII_LIST(CP1, CP2) ->
+ case lists:member(CP1,Seps) of
+ true -> bin_search_inv_n(BinRest, Cont, Seps);
+ false -> [Bin0|Cont]
+ end;
+ _ when Cont =:= [] ->
+ [GC|Bin] = unicode_util:gc(Bin0),
+ case lists:member(GC, Seps) of
+ true -> bin_search_inv_n(Bin, Cont, Seps);
+ false -> [Bin0|Cont]
+ end;
+ _ ->
+ [GC|Cs0] = unicode_util:gc([Bin0|Cont]),
+ case lists:member(GC, Seps) of
+ false -> [Bin0|Cont];
+ true ->
+ case Cs0 of
+ [Bin|Cont] when is_binary(Bin) ->
+ bin_search_inv_n(Bin, Cont, Seps);
+ _ ->
+ {nomatch, Cs0}
+ end
+ end
+ end;
+bin_search_inv_n(<<>>, Cont, _Sep) ->
+ {nomatch, Cont};
+bin_search_inv_n([], Cont, _Sep) ->
+ {nomatch, Cont}.
+
+bin_search_str(Bin0, Start, [], SearchCPs) ->
+ Compiled = binary:compile_pattern(unicode:characters_to_binary(SearchCPs)),
+ bin_search_str_1(Bin0, Start, Compiled, SearchCPs);
bin_search_str(Bin0, Start, Cont, [CP|_]=SearchCPs) ->
+ First = binary:compile_pattern(<<CP/utf8>>),
+ bin_search_str_2(Bin0, Start, Cont, First, SearchCPs).
+
+bin_search_str_1(Bin0, Start, First, SearchCPs) ->
+ <<_:Start/binary, Bin/binary>> = Bin0,
+ case binary:match(Bin, First) of
+ nomatch -> {nomatch, byte_size(Bin0), []};
+ {Where0, _} ->
+ Where = Start+Where0,
+ <<Keep:Where/binary, Cs0/binary>> = Bin0,
+ case prefix_1(Cs0, SearchCPs) of
+ nomatch ->
+ <<_/utf8, Cs/binary>> = Cs0,
+ KeepSz = byte_size(Bin0) - byte_size(Cs),
+ bin_search_str_1(Bin0, KeepSz, First, SearchCPs);
+ [] ->
+ {Keep, [Cs0], <<>>};
+ Rest ->
+ {Keep, [Cs0], Rest}
+ end
+ end.
+
+bin_search_str_2(Bin0, Start, Cont, First, SearchCPs) ->
<<_:Start/binary, Bin/binary>> = Bin0,
- case binary:match(Bin, <<CP/utf8>>) of
+ case binary:match(Bin, First) of
nomatch -> {nomatch, byte_size(Bin0), Cont};
{Where0, _} ->
Where = Start+Where0,
@@ -1320,7 +1608,7 @@ bin_search_str(Bin0, Start, Cont, [CP|_]=SearchCPs) ->
case prefix_1(stack(Cs0,Cont), SearchCPs) of
nomatch when is_binary(Cs) ->
KeepSz = byte_size(Bin0) - byte_size(Cs),
- bin_search_str(Bin0, KeepSz, Cont, SearchCPs);
+ bin_search_str_2(Bin0, KeepSz, Cont, First, SearchCPs);
nomatch ->
{nomatch, Where, stack([GC|Cs],Cont)};
[] ->
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index 1f966411c5..0c578acf21 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -38,7 +38,9 @@
-export_type([dbg_opt/0]).
--type name() :: pid() | atom() | {'global', atom()}.
+-type name() :: pid() | atom()
+ | {'global', term()}
+ | {'via', module(), term()}.
-type system_event() :: {'in', Msg :: _}
| {'in', Msg :: _, From :: _}
| {'out', Msg :: _, To :: _}
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index 915f478dfa..9123bf2f28 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -551,8 +551,8 @@ otp_8130(Config) when is_list(Config) ->
"t() -> "
" L = \"{ 34 , \\\"1\\\\x{AAA}\\\" , \\\"34\\\" , X . a , $\\\\x{AAA} }\", "
" R = ?M({34,\"1\\x{aaa}\",\"34\",X.a,$\\x{aaa}}),"
- " Lt = erl_scan:string(L, 1, [unicode]),"
- " Rt = erl_scan:string(R, 1, [unicode]),"
+ " Lt = erl_scan:string(L, 1),"
+ " Rt = erl_scan:string(R, 1),"
" Lt = Rt, ok. ">>,
ok},
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index b76bece07f..272a71432a 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -3981,8 +3981,9 @@ non_latin1_module(Config) ->
do_non_latin1_module(Mod) ->
File = atom_to_list(Mod) ++ ".erl",
- Forms = [{attribute,1,file,{File,1}},
- {attribute,1,module,Mod},
+ L1 = erl_anno:new(1),
+ Forms = [{attribute,L1,file,{File,1}},
+ {attribute,L1,module,Mod},
{eof,2}],
error = compile:forms(Forms),
{error,_,[]} = compile:forms(Forms, [return]),
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 808ba9b4c1..dda8d0a12e 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1262,7 +1262,7 @@ parse_forms(Chars) ->
parse_forms2([], _Cont, _Line, Forms) ->
lists:reverse(Forms);
parse_forms2(String, Cont0, Line, Forms) ->
- case erl_scan:tokens(Cont0, String, Line, [unicode]) of
+ case erl_scan:tokens(Cont0, String, Line) of
{done, {ok, Tokens, EndLine}, Chars} ->
{ok, Form} = erl_parse:parse_form(Tokens),
parse_forms2(Chars, [], EndLine, [Form | Forms]);
@@ -1303,7 +1303,7 @@ parse_and_pp_expr(String, Indent, Options) ->
erl_pp:expr(parse_expr(StringDot), Indent, Options).
parse_expr(Chars) ->
- {ok, Tokens, _} = erl_scan:string(Chars, 1, [unicode]),
+ {ok, Tokens, _} = erl_scan:string(Chars, 1),
{ok, [Expr]} = erl_parse:parse_exprs(Tokens),
Expr.
diff --git a/lib/stdlib/test/escript_SUITE_data/unicode1 b/lib/stdlib/test/escript_SUITE_data/unicode1
index 351bb785e5..8dc9d450b8 100755
--- a/lib/stdlib/test/escript_SUITE_data/unicode1
+++ b/lib/stdlib/test/escript_SUITE_data/unicode1
@@ -8,7 +8,7 @@ main(_) ->
_D = erlang:system_flag(backtrace_depth, 0),
A = <<"\x{aaa}"/utf8>>,
S = lists:flatten(io_lib:format("~p/~p.", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B).
diff --git a/lib/stdlib/test/escript_SUITE_data/unicode2 b/lib/stdlib/test/escript_SUITE_data/unicode2
index 495188f6f0..d0195b036c 100755
--- a/lib/stdlib/test/escript_SUITE_data/unicode2
+++ b/lib/stdlib/test/escript_SUITE_data/unicode2
@@ -8,7 +8,7 @@ main(_) ->
_D = erlang:system_flag(backtrace_depth, 0),
A = <<"\x{aa}">>,
S = lists:flatten(io_lib:format("~p/~p.", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B).
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 05451a83fb..1a8260b041 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -6033,17 +6033,23 @@ etsmem() ->
end},
{Mem,AllTabs}.
-verify_etsmem({MemInfo,AllTabs}) ->
+
+verify_etsmem(MI) ->
wait_for_test_procs(),
+ verify_etsmem(MI, 1).
+
+verify_etsmem({MemInfo,AllTabs}, Try) ->
case etsmem() of
{MemInfo,_} ->
io:format("Ets mem info: ~p", [MemInfo]),
- case MemInfo of
- {ErlMem,EtsAlloc} when ErlMem == notsup; EtsAlloc == undefined ->
+ case {MemInfo, Try} of
+ {{ErlMem,EtsAlloc},_} when ErlMem == notsup; EtsAlloc == undefined ->
%% Use 'erl +Mea max' to do more complete memory leak testing.
{comment,"Incomplete or no mem leak testing"};
- _ ->
- ok
+ {_, 1} ->
+ ok;
+ _ ->
+ {comment, "Transient memory discrepancy"}
end;
{MemInfo2, AllTabs2} ->
@@ -6051,7 +6057,15 @@ verify_etsmem({MemInfo,AllTabs}) ->
io:format("Actual: ~p", [MemInfo2]),
io:format("Changed tables before: ~p\n",[AllTabs -- AllTabs2]),
io:format("Changed tables after: ~p\n", [AllTabs2 -- AllTabs]),
- ct:fail("Failed memory check")
+ case Try < 2 of
+ true ->
+ io:format("\nThis discrepancy could be caused by an "
+ "inconsistent memory \"snapshot\""
+ "\nTry again...\n", []),
+ verify_etsmem({MemInfo, AllTabs}, Try+1);
+ false ->
+ ct:fail("Failed memory check")
+ end
end.
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index c94821bc75..afaf2404fa 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
wildcard_one/1,wildcard_two/1,wildcard_errors/1,
fold_files/1,otp_5960/1,ensure_dir_eexist/1,ensure_dir_symlink/1,
wildcard_symlink/1, is_file_symlink/1, file_props_symlink/1,
- find_source/1]).
+ find_source/1, find_source_subdir/1]).
-import(lists, [foreach/2]).
@@ -47,7 +47,7 @@ all() ->
[wildcard_one, wildcard_two, wildcard_errors,
fold_files, otp_5960, ensure_dir_eexist, ensure_dir_symlink,
wildcard_symlink, is_file_symlink, file_props_symlink,
- find_source].
+ find_source, find_source_subdir].
groups() ->
[].
@@ -536,16 +536,18 @@ find_source(Config) when is_list(Config) ->
[{".erl",".yrl",[{"",""}]}]),
{ok, ParserErl} = filelib:find_source(code:which(core_parse)),
+ ParserErlName = filename:basename(ParserErl),
+ ParserErlDir = filename:dirname(ParserErl),
{ok, ParserYrl} = filelib:find_source(ParserErl),
"lry." ++ _ = lists:reverse(ParserYrl),
- {ok, ParserYrl} = filelib:find_source(ParserErl,
+ {ok, ParserYrl} = filelib:find_source(ParserErlName, ParserErlDir,
[{".beam",".erl",[{"ebin","src"}]},
{".erl",".yrl",[{"",""}]}]),
%% find_source automatically checks the local directory regardless of rules
{ok, ParserYrl} = filelib:find_source(ParserErl),
- {ok, ParserYrl} = filelib:find_source(ParserErl,
- [{".beam",".erl",[{"ebin","src"}]}]),
+ {ok, ParserYrl} = filelib:find_source(ParserErlName, ParserErlDir,
+ [{".erl",".yrl",[{"ebin","src"}]}]),
%% find_file does not check the local directory unless in the rules
ParserYrlName = filename:basename(ParserYrl),
@@ -559,3 +561,24 @@ find_source(Config) when is_list(Config) ->
{ok, ParserYrl} = filelib:find_file(ParserYrlName, ParserYrlDir),
{ok, ParserYrl} = filelib:find_file(ParserYrlName, ParserYrlDir, []),
ok.
+
+find_source_subdir(Config) when is_list(Config) ->
+ BeamFile = code:which(inets), % Located in lib/inets/src/inets_app/
+ BeamName = filename:basename(BeamFile),
+ BeamDir = filename:dirname(BeamFile),
+ SrcName = filename:basename(BeamFile, ".beam") ++ ".erl",
+
+ {ok, SrcFile} = filelib:find_source(BeamName, BeamDir),
+ SrcName = filename:basename(SrcFile),
+
+ {error, not_found} =
+ filelib:find_source(BeamName, BeamDir,
+ [{".beam",".erl",[{"ebin","src"}]}]),
+ {ok, SrcFile} =
+ filelib:find_source(BeamName, BeamDir,
+ [{".beam",".erl",
+ [{"ebin",filename:join("src", "*")}]}]),
+
+ {ok, SrcFile} = filelib:find_file(SrcName, BeamDir),
+
+ ok.
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index fc77593bb8..f284eb1ed6 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -107,6 +107,17 @@ absname(Config) when is_list(Config) ->
[Drive|":/erlang/src"] = filename:absname([Drive|":erlang/src"]),
"a:/erlang" = filename:absname("a:erlang"),
+ "//foo" = filename:absname("//foo"),
+ "//foo/bar" = filename:absname("//foo/bar"),
+ "//foo/\bar" = filename:absname("//foo/\bar"),
+ "//foo/bar/baz" = filename:absname("//foo/bar\\baz"),
+ "//foo/bar/baz" = filename:absname("//foo\\bar/baz"),
+ "//foo" = filename:absname("\\\\foo"),
+ "//foo/bar" = filename:absname("\\\\foo/bar"),
+ "//foo/\bar" = filename:absname("\\\\foo/\bar"),
+ "//foo/bar/baz" = filename:absname("\\\\foo/bar\\baz"),
+ "//foo/bar/baz" = filename:absname("\\\\foo\\bar/baz"),
+
file:set_cwd(Cwd),
ok;
{unix, _} ->
@@ -167,6 +178,23 @@ absname_2(Config) when is_list(Config) ->
[Drive|":/"]),
"a:/erlang" = filename:absname("a:erlang", [Drive|":/"]),
+ "//foo" = filename:absname("foo","//"),
+ "//foo/bar" = filename:absname("foo/bar", "//"),
+ "//foo/bar" = filename:absname("bar", "//foo"),
+ "//bar" = filename:absname("/bar", "//foo"),
+ "//foo/bar/baz" = filename:absname("bar/baz", "//foo"),
+ "//bar/baz" = filename:absname("//bar/baz", "//foo"),
+ "//\bar" = filename:absname("/\bar", "//foo"),
+ "//foo" = filename:absname("foo","\\\\"),
+ "//foo/bar" = filename:absname("foo/bar", "\\\\"),
+ "//foo/bar" = filename:absname("bar", "\\\\foo"),
+ "//bar" = filename:absname("/bar", "\\\\foo"),
+ "//foo/bar/baz" = filename:absname("bar/baz", "\\\\foo"),
+ "//bar/baz" = filename:absname("\\\\bar/baz", "\\\\foo"),
+ "//\bar" = filename:absname("/\bar", "\\\\foo"),
+ "//bar/baz" = filename:absname("\\\\bar/baz", "//foo"),
+ "//bar/baz" = filename:absname("//bar/baz", "\\\\foo"),
+
ok;
_ ->
"/usr/foo" = filename:absname(foo, "/usr"),
@@ -244,6 +272,18 @@ dirname(Config) when is_list(Config) ->
"A:usr" = filename:dirname("A:usr/foo.erl"),
"/usr" = filename:dirname("\\usr\\foo.erl"),
"/" = filename:dirname("\\usr"),
+ "//foo/bar" = filename:dirname("//foo/bar/baz.erl"),
+ "//foo/\bar" = filename:dirname("//foo/\bar/baz.erl"),
+ "//foo/bar" = filename:dirname("//foo\\bar/baz.erl"),
+ "//foo/bar" = filename:dirname("\\\\foo/bar/baz.erl"),
+ "//foo/\bar" = filename:dirname("\\\\foo/\bar/baz.erl"),
+ "//foo/bar" = filename:dirname("\\\\foo\\bar/baz.erl"),
+ "//foo" = filename:dirname("//foo/baz.erl"),
+ "//foo" = filename:dirname("//foo/\baz.erl"),
+ "//foo" = filename:dirname("//foo\\baz.erl"),
+ "//foo" = filename:dirname("\\\\foo/baz.erl"),
+ "//foo" = filename:dirname("\\\\foo/\baz.erl"),
+ "//foo" = filename:dirname("\\\\foo\\baz.erl"),
"A:" = filename:dirname("A:");
_ -> true
end,
@@ -289,7 +329,6 @@ join(Config) when is_list(Config) ->
%% join/1 and join/2 (OTP-12158) by using help function
%% filename_join/2.
"/" = filename:join(["/"]),
- "/" = filename:join(["//"]),
"usr/foo.erl" = filename_join("usr","foo.erl"),
"/src/foo.erl" = filename_join(usr, "/src/foo.erl"),
"/src/foo.erl" = filename_join("/src/",'foo.erl'),
@@ -301,7 +340,6 @@ join(Config) when is_list(Config) ->
"a/b/c/d/e/f/g" = filename_join("a//b/c/", "d//e/f/g"),
"a/b/c/d/e/f/g" = filename_join("a//b/c", "d//e/f/g"),
"/d/e/f/g" = filename_join("a//b/c", "/d//e/f/g"),
- "/d/e/f/g" = filename:join("a//b/c", "//d//e/f/g"),
"foo/bar" = filename_join([$f,$o,$o,$/,[]], "bar"),
@@ -332,6 +370,7 @@ join(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
+ "//" = filename:join(["//"]),
"d:/" = filename:join(["D:/"]),
"d:/" = filename:join(["D:\\"]),
"d:/abc" = filename_join("D:/", "abc"),
@@ -345,8 +384,35 @@ join(Config) when is_list(Config) ->
"c:/usr/foo.erl" = filename:join(["A:","C:/usr","foo.erl"]),
"c:usr/foo.erl" = filename:join(["A:","C:usr","foo.erl"]),
"d:/foo" = filename:join([$D, $:, $/, []], "foo"),
+ "//" = filename:join("\\\\", ""),
+ "//foo" = filename:join("\\\\", "foo"),
+ "//foo/bar" = filename:join("\\\\", "foo\\\\bar"),
+ "//foo/bar/baz" = filename:join("\\\\foo", "bar\\\\baz"),
+ "//foo/bar/baz" = filename:join("\\\\foo", "bar\\baz"),
+ "//foo/bar/baz" = filename:join("\\\\foo\\bar", baz),
+ "//foo/\bar/baz" = filename:join("\\\\foo/\bar", baz),
+ "//foo/bar/baz" = filename:join("\\\\foo/bar", baz),
+ "//bar/baz" = filename:join("\\\\foo", "\\\\bar\\baz"),
+ "//bar/baz" = filename:join("\\\\foo", "//bar\\baz"),
+ "//bar/baz" = filename:join("\\\\foo", "//bar/baz"),
+ "//bar/baz" = filename:join("\\\\foo", "\\\\bar/baz"),
+ "//d/e/f/g" = filename:join("a//b/c", "//d//e/f/g"),
+ "//" = filename:join("//", ""),
+ "//foo" = filename:join("//", "foo"),
+ "//foo/bar" = filename:join("//", "foo\\\\bar"),
+ "//foo/bar/baz" = filename:join("//foo", "bar\\\\baz"),
+ "//foo/bar/baz" = filename:join("//foo", "bar\\baz"),
+ "//foo/bar/baz" = filename:join("//foo\\bar", baz),
+ "//foo/\bar/baz" = filename:join("//foo/\bar", baz),
+ "//foo/bar/baz" = filename:join("//foo/bar", baz),
+ "//bar/baz" = filename:join("//foo", "\\\\bar\\baz"),
+ "//bar/baz" = filename:join("//foo", "//bar\\baz"),
+ "//bar/baz" = filename:join("//foo", "//bar/baz"),
+ "//bar/baz" = filename:join("//foo", "\\\\bar/baz"),
ok;
_ ->
+ "/" = filename:join(["//"]),
+ "/d/e/f/g" = filename:join("a//b/c", "//d//e/f/g"),
ok
end.
@@ -402,6 +468,16 @@ split(Config) when is_list(Config) ->
filename:split("a:\\msdev\\include"),
["a:","msdev","include"] =
filename:split("a:msdev\\include"),
+ ["//","foo"] =
+ filename:split("\\\\foo"),
+ ["//","foo"] =
+ filename:split("//foo"),
+ ["//","foo","bar"] =
+ filename:split("\\\\foo\\\\bar"),
+ ["//","foo","baz"] =
+ filename:split("\\\\foo\\baz"),
+ ["//","foo","baz"] =
+ filename:split("//foo\\baz"),
ok;
_ ->
ok
@@ -630,7 +706,6 @@ extension_bin(Config) when is_list(Config) ->
join_bin(Config) when is_list(Config) ->
<<"/">> = filename:join([<<"/">>]),
- <<"/">> = filename:join([<<"//">>]),
<<"usr/foo.erl">> = filename:join(<<"usr">>,<<"foo.erl">>),
<<"/src/foo.erl">> = filename:join(usr, <<"/src/foo.erl">>),
<<"/src/foo.erl">> = filename:join([<<"/src/">>,'foo.erl']),
@@ -642,7 +717,6 @@ join_bin(Config) when is_list(Config) ->
<<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/">>, <<"d//e/f/g">>]),
<<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"d//e/f/g">>]),
<<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"/d//e/f/g">>]),
- <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]),
<<"foo/bar">> = filename:join([$f,$o,$o,$/,[]], <<"bar">>),
@@ -695,6 +769,7 @@ join_bin(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
+ <<"//">> = filename:join([<<"//">>]),
<<"d:/">> = filename:join([<<"D:/">>]),
<<"d:/">> = filename:join([<<"D:\\">>]),
<<"d:/abc">> = filename:join([<<"D:/">>, <<"abc">>]),
@@ -708,8 +783,35 @@ join_bin(Config) when is_list(Config) ->
<<"c:/usr/foo.erl">> = filename:join([<<"A:">>,<<"C:/usr">>,<<"foo.erl">>]),
<<"c:usr/foo.erl">> = filename:join([<<"A:">>,<<"C:usr">>,<<"foo.erl">>]),
<<"d:/foo">> = filename:join([$D, $:, $/, []], <<"foo">>),
+ <<"//">> = filename:join(<<"\\\\">>, <<"">>),
+ <<"//foo">> = filename:join(<<"\\\\">>, <<"foo">>),
+ <<"//foo/bar">> = filename:join(<<"\\\\">>, <<"foo\\\\bar">>),
+ <<"//foo/bar/baz">> = filename:join(<<"\\\\foo">>, <<"bar\\\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"\\\\bar\\baz">>),
+ <<"//foo/bar/baz">> = filename:join(<<"\\\\foo\\bar">>, baz),
+ <<"//foo/\bar/baz">> = filename:join(<<"\\\\foo/\bar">>, baz),
+ <<"//foo/bar/baz">> = filename:join(<<"\\\\foo/bar">>, baz),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"\\\\bar\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"//bar\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"//bar/baz">>),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"\\\\bar/baz">>),
+ <<"//d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]),
+ <<"//">> = filename:join(<<"//">>, <<"">>),
+ <<"//foo">> = filename:join(<<"//">>, <<"foo">>),
+ <<"//foo/bar">> = filename:join(<<"//">>, <<"foo\\\\bar">>),
+ <<"//foo/bar/baz">> = filename:join(<<"//foo">>, <<"bar\\\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"\\\\bar\\baz">>),
+ <<"//foo/bar/baz">> = filename:join(<<"//foo\\bar">>, baz),
+ <<"//foo/\bar/baz">> = filename:join(<<"//foo/\bar">>, baz),
+ <<"//foo/bar/baz">> = filename:join(<<"//foo/bar">>, baz),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"\\\\bar\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"//bar\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"//bar/baz">>),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"\\\\bar/baz">>),
ok;
_ ->
+ <<"/">> = filename:join([<<"//">>]),
+ <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]),
ok
end.
@@ -756,6 +858,16 @@ split_bin(Config) when is_list(Config) ->
filename:split(<<"a:\\msdev\\include">>),
[<<"a:">>,<<"msdev">>,<<"include">>] =
filename:split(<<"a:msdev\\include">>),
+ [<<"//">>,<<"foo">>] =
+ filename:split(<<"\\\\foo">>),
+ [<<"//">>,<<"foo">>] =
+ filename:split(<<"//foo">>),
+ [<<"//">>,<<"foo">>,<<"bar">>] =
+ filename:split(<<"\\\\foo\\\\bar">>),
+ [<<"//">>,<<"foo">>,<<"baz">>] =
+ filename:split(<<"\\\\foo\\baz">>),
+ [<<"//">>,<<"foo">>,<<"baz">>] =
+ filename:split(<<"//foo\\baz">>),
ok;
_ ->
ok
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index 86cf58566b..41ee3246f5 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -389,7 +389,7 @@ stop10(_Config) ->
Dir = filename:dirname(code:which(?MODULE)),
rpc:call(Node,code,add_path,[Dir]),
{ok, Pid} = rpc:call(Node,gen_fsm,start,[{global,to_stop},?MODULE,[],[]]),
- global:sync(),
+ ok = global:sync(),
ok = gen_fsm:stop({global,to_stop}),
false = rpc:call(Node,erlang,is_process_alive,[Pid]),
{'EXIT',noproc} = (catch gen_fsm:stop({global,to_stop})),
@@ -1005,7 +1005,7 @@ undef_in_terminate(Config) when is_list(Config) ->
State = {undef_in_terminate, {?MODULE, terminate}},
{ok, FSM} = gen_fsm:start(?MODULE, {state_data, State}, []),
try
- gen_fsm:stop(FSM),
+ ok = gen_fsm:stop(FSM),
ct:fail(failed)
catch
exit:{undef, [{?MODULE, terminate, _, _}|_]} ->
@@ -1201,7 +1201,7 @@ timeout({timeout,Ref,{timeout,Time}}, {From,Ref}) ->
Cref = gen_fsm:start_timer(Time, cancel),
Time4 = Time*4,
receive after Time4 -> ok end,
- gen_fsm:cancel_timer(Cref),
+ _= gen_fsm:cancel_timer(Cref),
{next_state, timeout, {From,Ref2}};
timeout({timeout,Ref2,ok},{From,Ref2}) ->
gen_fsm:reply(From, ok),
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 2e9dc4d4fb..7d9561db24 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -346,7 +346,7 @@ stop10(_Config) ->
Dir = filename:dirname(code:which(?MODULE)),
rpc:call(Node,code,add_path,[Dir]),
{ok, Pid} = rpc:call(Node,gen_server,start,[{global,to_stop},?MODULE,[],[]]),
- global:sync(),
+ ok = global:sync(),
ok = gen_server:stop({global,to_stop}),
false = rpc:call(Node,erlang,is_process_alive,[Pid]),
{'EXIT',noproc} = (catch gen_server:stop({global,to_stop})),
@@ -467,7 +467,7 @@ start_node(Name) ->
%% After starting a slave, it takes a little while until global knows
%% about it, even if nodes() includes it, so we make sure that global
%% knows about it before registering something on all nodes.
- global:sync(),
+ ok = global:sync(),
N.
call_remote1(Config) when is_list(Config) ->
@@ -605,7 +605,7 @@ cast_fast(Config) when is_list(Config) ->
cast_fast_messup() ->
%% Register a false node: hopp@hostname
unregister(erl_epmd),
- erl_epmd:start_link(),
+ {ok, _} = erl_epmd:start_link(),
{ok,S} = gen_tcp:listen(0, []),
{ok,P} = inet:port(S),
{ok,_Creation} = erl_epmd:register_node(hopp, P),
@@ -1309,7 +1309,7 @@ do_call_with_huge_message_queue() ->
{Time,ok} = tc(fun() -> calls(10000, Pid) end),
- [self() ! {msg,N} || N <- lists:seq(1, 500000)],
+ _ = [self() ! {msg,N} || N <- lists:seq(1, 500000)],
erlang:garbage_collect(),
{NewTime,ok} = tc(fun() -> calls(10000, Pid) end),
io:format("Time for empty message queue: ~p", [Time]),
@@ -1426,7 +1426,7 @@ undef_in_terminate(Config) when is_list(Config) ->
State = {undef_in_terminate, {oc_server, terminate}},
{ok, Server} = gen_server:start(?MODULE, {state, State}, []),
try
- gen_server:stop(Server),
+ ok = gen_server:stop(Server),
ct:fail(failed)
catch
exit:{undef, [{oc_server, terminate, [], _}|_]} ->
@@ -1615,7 +1615,7 @@ handle_cast({From,delayed_cast,T}, _State) ->
handle_cast(hibernate_now, _State) ->
{noreply, [], hibernate};
handle_cast(hibernate_later, _State) ->
- timer:send_after(1000,self(),hibernate_now),
+ {ok, _} = timer:send_after(1000,self(),hibernate_now),
{noreply, []};
handle_cast({call_undef_fun, Mod, Fun}, State) ->
Mod:Fun(),
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index e2c73371cd..16e3dba969 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -714,7 +714,7 @@ p(Term, D) ->
rp(Term, 1, 80, D).
p(Term, Col, Ll, D) ->
- rp(Term, Col, Ll, D, no_fun).
+ rp(Term, Col, Ll, D, none).
rp(Term, Col, Ll, D) ->
rp(Term, Col, Ll, D, fun rfd/2).
@@ -724,6 +724,8 @@ rp(Term, Col, Ll, D) ->
rp(Term, Col, Ll, D, RF) ->
rp(Term, Col, Ll, D, ?MAXCS, RF).
+rp(Term, Col, Ll, D, M, none) ->
+ rp(Term, Col, Ll, D, M, fun(_, _) -> no end);
rp(Term, Col, Ll, D, M, RF) ->
%% io:format("~n~n*** Col = ~p Ll = ~p D = ~p~n~p~n-->~n",
%% [Col, Ll, D, Term]),
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 5e9e03e410..949142ec77 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -7871,7 +7871,7 @@ run_test(Config, Extra, {cres, Body, Opts, ExpectedCompileReturn}) ->
{module, _} = code:load_abs(AbsFile, Mod),
Ms0 = erlang:process_info(self(),messages),
- Before = {{get(), lists:sort(ets:all()), Ms0}, pps()},
+ Before = {{lget(), lists:sort(ets:all()), Ms0}, pps()},
%% Prepare the check that the qlc module does not call qlc_pt.
_ = [unload_pt() || {file, Name} <- [code:is_loaded(qlc_pt)],
@@ -7903,7 +7903,7 @@ run_test(Config, Extra, Body) ->
wait_for_expected(R, {Strict0,PPS0}=Before, SourceFile, Wait) ->
Ms = erlang:process_info(self(),messages),
- After = {_,PPS1} = {{get(), lists:sort(ets:all()), Ms}, pps()},
+ After = {_,PPS1} = {{lget(), lists:sort(ets:all()), Ms}, pps()},
case {R, After} of
{ok, Before} ->
ok;
@@ -7931,6 +7931,18 @@ wait_for_expected(R, {Strict0,PPS0}=Before, SourceFile, Wait) ->
expected({ok,Before}, {R,After}, SourceFile)
end.
+%% The qlc modules uses the process dictionary for storing names of files.
+lget() ->
+ lists:sort([T || {K, _} = T <- get(), is_qlc_key(K)]).
+
+%% Copied from the qlc module.
+-define(LCACHE_FILE(Ref), {Ref, '$_qlc_cache_tmpfiles_'}).
+-define(MERGE_JOIN_FILE, '$_qlc_merge_join_tmpfiles_').
+
+is_qlc_key(?LCACHE_FILE(_)) -> true;
+is_qlc_key(?MERGE_JOIN_FILE) -> true;
+is_qlc_key(_) -> false.
+
unload_pt() ->
erlang:garbage_collect(), % get rid of references to qlc_pt...
_ = code:purge(qlc_pt),
diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl
index 71f86e32e5..7b82647416 100644
--- a/lib/stdlib/test/re_SUITE.erl
+++ b/lib/stdlib/test/re_SUITE.erl
@@ -894,10 +894,13 @@ match_limit(Config) when is_list(Config) ->
%% Test that we get sub-binaries if subject is a binary and we capture
%% binaries.
sub_binaries(Config) when is_list(Config) ->
- Bin = list_to_binary(lists:seq(1,255)),
- {match,[B,C]}=re:run(Bin,"(a)",[{capture,all,binary}]),
- 255 = binary:referenced_byte_size(B),
- 255 = binary:referenced_byte_size(C),
- {match,[D]}=re:run(Bin,"(a)",[{capture,[1],binary}]),
- 255 = binary:referenced_byte_size(D),
+ %% The GC can auto-convert tiny sub-binaries to heap binaries, so we
+ %% extract large sequences to make the test more stable.
+ Bin = << <<I>> || I <- lists:seq(1, 4096) >>,
+ {match,[B,C]}=re:run(Bin,"a(.+)$",[{capture,all,binary}]),
+ true = byte_size(B) =/= byte_size(C),
+ 4096 = binary:referenced_byte_size(B),
+ 4096 = binary:referenced_byte_size(C),
+ {match,[D]}=re:run(Bin,"a(.+)$",[{capture,[1],binary}]),
+ 4096 = binary:referenced_byte_size(D),
ok.
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 217e8cc252..ca85314775 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -561,9 +561,10 @@ otp_5226(Config) when is_list(Config) ->
otp_5327(Config) when is_list(Config) ->
"exception error: bad argument" =
comm_err(<<"<<\"hej\":default>>.">>),
+ L1 = erl_anno:new(1),
<<"abc">> =
- erl_parse:normalise({bin,1,[{bin_element,1,{string,1,"abc"},
- default,default}]}),
+ erl_parse:normalise({bin,L1,[{bin_element,L1,{string,L1,"abc"},
+ default,default}]}),
[<<"abc">>] = scan(<<"<<(<<\"abc\">>):3/binary>>.">>),
[<<"abc">>] = scan(<<"<<(<<\"abc\">>)/binary>>.">>),
"exception error: bad argument" =
@@ -576,9 +577,9 @@ otp_5327(Config) when is_list(Config) ->
comm_err(<<"<<10:default>>.">>),
[<<98,1:1>>] = scan(<<"<<3:3,5:6>>.">>),
{'EXIT',{badarg,_}} =
- (catch erl_parse:normalise({bin,1,[{bin_element,1,{integer,1,17},
- {atom,1,all},
- default}]})),
+ (catch erl_parse:normalise({bin,L1,[{bin_element,L1,{integer,L1,17},
+ {atom,L1,all},
+ default}]})),
[<<-20/signed>>] = scan(<<"<<-20/signed>> = <<-20>>.">>),
[<<-300:16/signed>>] =
scan(<<"<<-300:16/signed>> = <<-300:16>>.">>),
@@ -2784,7 +2785,7 @@ otp_10302(Config) when is_list(Config) ->
<<"begin
A = <<\"\\xaa\">>,
S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B)
@@ -2797,7 +2798,7 @@ otp_10302(Config) when is_list(Config) ->
<<"io:setopts([{encoding,utf8}]).
A = <<\"\\xaa\">>,
S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B).">>,
@@ -2809,7 +2810,7 @@ otp_10302(Config) when is_list(Config) ->
<<"begin
A = [1089],
S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B)
@@ -2821,7 +2822,7 @@ otp_10302(Config) when is_list(Config) ->
<<"io:setopts([{encoding,utf8}]).
A = [1089],
S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B).">>,
@@ -2940,7 +2941,7 @@ otp_14296(Config) when is_list(Config) ->
end(),
fun() ->
- Port = open_port({spawn, "ls"}, [line]),
+ Port = open_port({spawn, "ls"}, [{line,1}]),
KnownPort = erlang:port_to_list(Port),
S = KnownPort ++ ".",
R = KnownPort ++ ".\n",
@@ -3012,7 +3013,7 @@ scan(B) ->
scan(t(B), F).
scan(S0, F) ->
- case erl_scan:tokens([], S0, 1, [unicode]) of
+ case erl_scan:tokens([], S0, 1) of
{done,{ok,Ts,_},S} ->
[F(Ts) | scan(S, F)];
_Else ->
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index 90f980c0e5..17714b8d4d 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -92,14 +92,11 @@ end_per_testcase(_Case, _Config) ->
ok.
debug() ->
- Config = [{data_dir, ?MODULE_STRING++"_data"}],
+ Config = [{data_dir, "./" ++ ?MODULE_STRING++"_data"}],
[io:format("~p:~p~n",[Test,?MODULE:Test(Config)]) ||
{_,Tests} <- groups(), Test <- Tests].
-define(TEST(B,C,D), test(?LINE,?FUNCTION_NAME,B,C,D, true)).
--define(TEST_EQ(B,C,D),
- test(?LINE,?FUNCTION_NAME,B,C,D, true),
- test(?LINE,?FUNCTION_NAME,hd(C),[B|tl(C),D, true)).
-define(TEST_NN(B,C,D),
test(?LINE,?FUNCTION_NAME,B,C,D, false),
@@ -294,6 +291,7 @@ trim(_) ->
?TEST(["..h", ".e", <<"j..">>], [both, ". "], "h.ej"),
?TEST(["..h", <<".ejsa"/utf8>>, "n.."], [both, ". "], "h.ejsan"),
%% Test that it behaves with graphemes (i.e. nfd tests are the hard part)
+ ?TEST([1013,101,778,101,101], [trailing, [101]], [1013,101,778]),
?TEST("aaåaa", [both, "a"], "å"),
?TEST(["aaa",778,"äöoo"], [both, "ao"], "åäö"),
?TEST([<<"aaa">>,778,"äöoo"], [both, "ao"], "åäö"),
@@ -353,6 +351,7 @@ take(_) ->
?TEST([<<>>,<<"..">>, " h.ej", <<" ..">>], [Chars, true, leading], {".. ", "h.ej .."}),
?TEST(["..h", <<".ejsa"/utf8>>, "n.."], [Chars, true, leading], {"..", "h.ejsan.."}),
%% Test that it behaves with graphemes (i.e. nfd tests are the hard part)
+ ?TEST([101,778], [[[101, 779]], true], {[101,778], []}),
?TEST(["aaee",778,"äöoo"], [[[$e,778]], true, leading], {"aae", [$e,778|"äöoo"]}),
?TEST([<<"aae">>,778,"äöoo"], [[[$e,778]],true,leading], {"aa", [$e,778|"äöoo"]}),
?TEST([<<"e">>,778,"åäöe", <<778/utf8>>], [[[$e,778]], true, leading], {[], [$e,778]++"åäöe"++[778]}),
@@ -486,6 +485,10 @@ to_float(_) ->
prefix(_) ->
?TEST("", ["a"], nomatch),
?TEST("a", [""], "a"),
+ ?TEST("a", [[[]]], "a"),
+ ?TEST("a", [<<>>], "a"),
+ ?TEST("a", [[<<>>]], "a"),
+ ?TEST("a", [[[<<>>]]], "a"),
?TEST("b", ["a"], nomatch),
?TEST("a", ["a"], ""),
?TEST("å", ["a"], nomatch),
@@ -713,29 +716,123 @@ nth_lexeme(_) ->
meas(Config) ->
+ Parent = self(),
+ Exec = fun() ->
+ DataDir0 = proplists:get_value(data_dir, Config),
+ DataDir = filename:join(lists:droplast(filename:split(DataDir0))),
+ case proplists:get_value(profile, Config, false) of
+ false ->
+ do_measure(DataDir);
+ eprof ->
+ eprof:profile(fun() -> do_measure(DataDir) end, [set_on_spawn]),
+ eprof:stop_profiling(),
+ eprof:analyze(),
+ eprof:stop()
+ end,
+ Parent ! {test_done, self()},
+ normal
+ end,
+ ct:timetrap({minutes,2}),
case ct:get_timetrap_info() of
{_,{_,Scale}} when Scale > 1 ->
{skip,{will_not_run_in_debug,Scale}};
- _ -> % No scaling
- DataDir = proplists:get_value(data_dir, Config),
- TestDir = filename:dirname(string:trim(DataDir, trailing, "/")),
- do_measure(TestDir)
+ _ -> % No scaling, run at most 1.5 min
+ Tester = spawn(Exec),
+ receive {test_done, Tester} -> ok
+ after 90000 ->
+ io:format("Timelimit reached stopping~n",[]),
+ exit(Tester, die)
+ end,
+ ok
end.
-do_measure(TestDir) ->
- File = filename:join(TestDir, ?MODULE_STRING ++ ".erl"),
+do_measure(DataDir) ->
+ File = filename:join([DataDir,"unicode_util_SUITE_data","NormalizationTest.txt"]),
io:format("File ~s ",[File]),
{ok, Bin} = file:read_file(File),
io:format("~p~n",[byte_size(Bin)]),
Do = fun(Name, Func, Mode) ->
{N, Mean, Stddev, _} = time_func(Func, Mode, Bin),
- io:format("~10w ~6w ~6.2fms ±~4.2fms #~.2w gc included~n",
+ io:format("~15w ~6w ~6.2fms ±~5.2fms #~.2w gc included~n",
[Name, Mode, Mean/1000, Stddev/1000, N])
end,
+ Do2 = fun(Name, Func, Mode) ->
+ {N, Mean, Stddev, _} = time_func(Func, binary, <<>>),
+ io:format("~15w ~6w ~6.2fms ±~5.2fms #~.2w gc included~n",
+ [Name, Mode, Mean/1000, Stddev/1000, N])
+ end,
io:format("----------------------~n"),
- Do(tokens, fun(Str) -> string:tokens(Str, [$\n,$\r]) end, list),
+
+ Do(old_tokens, fun(Str) -> string:tokens(Str, [$\n,$\r]) end, list),
Tokens = {lexemes, fun(Str) -> string:lexemes(Str, [$\n,$\r]) end},
[Do(Name,Fun,Mode) || {Name,Fun} <- [Tokens], Mode <- [list, binary]],
+
+ S0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy.....",
+ S0B = <<"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy.....">>,
+ Do2(old_strip_l, repeat(fun() -> string:strip(S0, left, $x) end), list),
+ Do2(trim_l, repeat(fun() -> string:trim(S0, leading, [$x]) end), list),
+ Do2(trim_l, repeat(fun() -> string:trim(S0B, leading, [$x]) end), binary),
+ Do2(old_strip_r, repeat(fun() -> string:strip(S0, right, $.) end), list),
+ Do2(trim_t, repeat(fun() -> string:trim(S0, trailing, [$.]) end), list),
+ Do2(trim_t, repeat(fun() -> string:trim(S0B, trailing, [$.]) end), binary),
+
+ Do2(old_chr_sub, repeat(fun() -> string:sub_string(S0, string:chr(S0, $.)) end), list),
+ Do2(old_str_sub, repeat(fun() -> string:sub_string(S0, string:str(S0, [$.])) end), list),
+ Do2(find, repeat(fun() -> string:find(S0, [$.]) end), list),
+ Do2(find, repeat(fun() -> string:find(S0B, [$.]) end), binary),
+ Do2(old_str_sub2, repeat(fun() -> N = string:str(S0, "xy.."),
+ {string:sub_string(S0,1,N), string:sub_string(S0,N+4)} end), list),
+ Do2(split, repeat(fun() -> string:split(S0, "xy..") end), list),
+ Do2(split, repeat(fun() -> string:split(S0B, "xy..") end), binary),
+
+ Do2(old_rstr_sub, repeat(fun() -> string:sub_string(S0, string:rstr(S0, [$y])) end), list),
+ Do2(find_t, repeat(fun() -> string:find(S0, [$y], trailing) end), list),
+ Do2(find_t, repeat(fun() -> string:find(S0B, [$y], trailing) end), binary),
+ Do2(old_rstr_sub2, repeat(fun() -> N = string:rstr(S0, "y.."),
+ {string:sub_string(S0,1,N), string:sub_string(S0,N+3)} end), list),
+ Do2(split_t, repeat(fun() -> string:split(S0, "y..", trailing) end), list),
+ Do2(split_t, repeat(fun() -> string:split(S0B, "y..", trailing) end), binary),
+
+ Do2(old_span, repeat(fun() -> N=string:span(S0, [$x, $y]),
+ {string:sub_string(S0,1,N),string:sub_string(S0,N+1)}
+ end), list),
+ Do2(take, repeat(fun() -> string:take(S0, [$x, $y]) end), list),
+ Do2(take, repeat(fun() -> string:take(S0B, [$x, $y]) end), binary),
+
+ Do2(old_cspan, repeat(fun() -> N=string:cspan(S0, [$.,$y]),
+ {string:sub_string(S0,1,N),string:sub_string(S0,N+1)}
+ end), list),
+ Do2(take_c, repeat(fun() -> string:take(S0, [$.,$y], true) end), list),
+ Do2(take_c, repeat(fun() -> string:take(S0B, [$.,$y], true) end), binary),
+
+ Do2(old_substr, repeat(fun() -> string:substr(S0, 21, 15) end), list),
+ Do2(slice, repeat(fun() -> string:slice(S0, 20, 15) end), list),
+ Do2(slice, repeat(fun() -> string:slice(S0B, 20, 15) end), binary),
+
+ io:format("--~n",[]),
+ NthTokens = {nth_lexemes, fun(Str) -> string:nth_lexeme(Str, 18000, [$\n,$\r]) end},
+ [Do(Name,Fun,Mode) || {Name,Fun} <- [NthTokens], Mode <- [list, binary]],
+ Do2(take_t, repeat(fun() -> string:take(S0, [$.,$y], false, trailing) end), list),
+ Do2(take_t, repeat(fun() -> string:take(S0B, [$.,$y], false, trailing) end), binary),
+ Do2(take_tc, repeat(fun() -> string:take(S0, [$x], true, trailing) end), list),
+ Do2(take_tc, repeat(fun() -> string:take(S0B, [$x], true, trailing) end), binary),
+
+ Length = {length, fun(Str) -> string:length(Str) end},
+ [Do(Name,Fun,Mode) || {Name,Fun} <- [Length], Mode <- [list, binary]],
+
+ Reverse = {reverse, fun(Str) -> string:reverse(Str) end},
+ [Do(Name,Fun,Mode) || {Name,Fun} <- [Reverse], Mode <- [list, binary]],
+
+ ok.
+
+repeat(F) ->
+ fun(_) -> repeat_1(F,20000) end.
+
+repeat_1(F, N) when N > 0 ->
+ F(),
+ repeat_1(F, N-1);
+repeat_1(_, _) ->
+ erlang:garbage_collect(),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -865,8 +962,6 @@ check_types_1({list, _},{list, undefined}) ->
ok;
check_types_1({list, _},{list, codepoints}) ->
ok;
-check_types_1({list, _},{list, {list, codepoints}}) ->
- ok;
check_types_1({list, {list, _}},{list, {list, codepoints}}) ->
ok;
check_types_1(mixed,_) ->
@@ -947,7 +1042,7 @@ time_func(Fun, Mode, Bin) ->
end),
receive {Pid,Msg} -> Msg end.
-time_func(N,Sum,SumSq, Fun, Str, _) when N < 50 ->
+time_func(N,Sum,SumSq, Fun, Str, _) when N < 20 ->
{Time, Res} = timer:tc(fun() -> Fun(Str) end),
time_func(N+1,Sum+Time,SumSq+Time*Time, Fun, Str, Res);
time_func(N,Sum,SumSq, _, _, Res) ->
diff --git a/lib/stdlib/test/unicode_util_SUITE.erl b/lib/stdlib/test/unicode_util_SUITE.erl
index 03c24c7027..a89627eba5 100644
--- a/lib/stdlib/test/unicode_util_SUITE.erl
+++ b/lib/stdlib/test/unicode_util_SUITE.erl
@@ -310,12 +310,23 @@ get(_) ->
add_get_tests.
count(Config) ->
+ Parent = self(),
+ Exec = fun() ->
+ do_measure(Config),
+ Parent ! {test_done, self()}
+ end,
ct:timetrap({minutes,5}),
case ct:get_timetrap_info() of
- {_,{_,Scale}} ->
+ {_,{_,Scale}} when Scale > 1 ->
{skip,{measurments_skipped_debug,Scale}};
- _ -> % No scaling
- do_measure(Config)
+ _ -> % No scaling, run at most 2 min
+ Tester = spawn(Exec),
+ receive {test_done, Tester} -> ok
+ after 120000 ->
+ io:format("Timelimit reached stopping~n",[]),
+ exit(Tester, die)
+ end,
+ ok
end.
do_measure(Config) ->
diff --git a/lib/stdlib/uc_spec/gen_unicode_mod.escript b/lib/stdlib/uc_spec/gen_unicode_mod.escript
index fefd7d3b70..73c351e1af 100755
--- a/lib/stdlib/uc_spec/gen_unicode_mod.escript
+++ b/lib/stdlib/uc_spec/gen_unicode_mod.escript
@@ -170,7 +170,7 @@ gen_header(Fd) ->
io:put_chars(Fd, "-export([spec_version/0, lookup/1, get_case/1]).\n"),
io:put_chars(Fd, "-inline([class/1]).\n"),
io:put_chars(Fd, "-compile(nowarn_unused_vars).\n"),
- io:put_chars(Fd, "-dialyzer({no_improper_lists, [cp/1, gc_prepend/2, gc_e_cont/2]}).\n"),
+ io:put_chars(Fd, "-dialyzer({no_improper_lists, [cp/1, gc/1, gc_prepend/2, gc_e_cont/2]}).\n"),
io:put_chars(Fd, "-type gc() :: char()|[char()].\n\n\n"),
ok.
@@ -240,7 +240,7 @@ gen_norm(Fd) ->
"-spec nfd(unicode:chardata()) -> maybe_improper_list(gc(),unicode:chardata()) | {error, unicode:chardata()}.\n"
"nfd(Str0) ->\n"
" case gc(Str0) of\n"
- " [GC|R] when GC < 127 -> [GC|R];\n"
+ " [GC|R] when GC < 128 -> [GC|R];\n"
" [GC|Str] -> [decompose(GC)|Str];\n"
" [] -> [];\n"
" {error,_}=Error -> Error\n end.\n\n"
@@ -250,7 +250,7 @@ gen_norm(Fd) ->
"-spec nfkd(unicode:chardata()) -> maybe_improper_list(gc(),unicode:chardata()) | {error, unicode:chardata()}.\n"
"nfkd(Str0) ->\n"
" case gc(Str0) of\n"
- " [GC|R] when GC < 127 -> [GC|R];\n"
+ " [GC|R] when GC < 128 -> [GC|R];\n"
" [GC|Str] -> [decompose_compat(GC)|Str];\n"
" [] -> [];\n"
" {error,_}=Error -> Error\n end.\n\n"
@@ -260,7 +260,7 @@ gen_norm(Fd) ->
"-spec nfc(unicode:chardata()) -> maybe_improper_list(gc(),unicode:chardata()) | {error, unicode:chardata()}.\n"
"nfc(Str0) ->\n"
" case gc(Str0) of\n"
- " [GC|R] when GC < 255 -> [GC|R];\n"
+ " [GC|R] when GC < 256 -> [GC|R];\n"
" [GC|Str] -> [compose(decompose(GC))|Str];\n"
" [] -> [];\n"
" {error,_}=Error -> Error\n end.\n\n"
@@ -270,7 +270,7 @@ gen_norm(Fd) ->
"-spec nfkc(unicode:chardata()) -> maybe_improper_list(gc(),unicode:chardata()) | {error, unicode:chardata()}.\n"
"nfkc(Str0) ->\n"
" case gc(Str0) of\n"
- " [GC|R] when GC < 127 -> [GC|R];\n"
+ " [GC|R] when GC < 128 -> [GC|R];\n"
" [GC|Str] -> [compose_compat_0(decompose_compat(GC))|Str];\n"
" [] -> [];\n"
" {error,_}=Error -> Error\n end.\n\n"
@@ -476,13 +476,30 @@ gen_gc(Fd, GBP) ->
"-spec gc(String::unicode:chardata()) ->"
" maybe_improper_list() | {error, unicode:chardata()}.\n"),
io:put_chars(Fd,
+ "gc([CP1, CP2|_]=T)\n"
+ " when CP1 < 256, CP2 < 256, CP1 =/= $\r -> %% Ascii Fast path\n"
+ " T;\n"
+ "gc(<<CP1/utf8, Rest/binary>>) ->\n"
+ " if CP1 < 256, CP1 =/= $\r ->\n"
+ " case Rest of\n"
+ " <<CP2/utf8, _/binary>> when CP2 < 256 -> %% Ascii Fast path\n"
+ " [CP1|Rest];\n"
+ " _ -> gc_1([CP1|Rest])\n"
+ " end;\n"
+ " true -> gc_1([CP1|Rest])\n"
+ " end;\n"
"gc(Str) ->\n"
" gc_1(cp(Str)).\n\n"
"gc_1([$\\r|R0] = R) ->\n"
" case cp(R0) of % Don't break CRLF\n"
" [$\\n|R1] -> [[$\\r,$\\n]|R1];\n"
" _ -> R\n"
- " end;\n"),
+ " end;\n"
+ %% "gc_1([CP1, CP2|_]=T) when CP1 < 256, CP2 < 256 ->\n"
+ %% " T; %% Fast path\n"
+ %% "gc_1([CP1|<<CP2/utf8, _/binary>>]=T) when CP1 < 256, CP2 < 256 ->\n"
+ %% " T; %% Fast path\n"
+ ),
io:put_chars(Fd, "%% Handle control\n"),
GenControl = fun(Range) -> io:format(Fd, "gc_1~s R0;\n", [gen_clause(Range)]) end,
@@ -490,7 +507,7 @@ gen_gc(Fd, GBP) ->
[R1,R2,R3|Crs] = CRs0,
[GenControl(CP) || CP <- merge_ranges([R1,R2,R3], split), CP =/= {$\r, undefined}],
%%GenControl(R1),GenControl(R2),GenControl(R3),
- io:format(Fd, "gc_1([CP|R]) when CP < 255 -> gc_extend(R,CP);\n", []),
+ io:format(Fd, "gc_1([CP|R]) when CP < 256 -> gc_extend(R,CP);\n", []),
[GenControl(CP) || CP <- Crs],
%% One clause per CP
%% CRs0 = merge_ranges(maps:get(cr, GBP) ++ maps:get(lf, GBP) ++ maps:get(control, GBP)),
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 48db5dc900..69d258c2f0 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.4.2
+STDLIB_VSN = 3.4.3
diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile
index e55222e59c..1ce620b3d6 100644
--- a/lib/syntax_tools/doc/src/Makefile
+++ b/lib/syntax_tools/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2016. All Rights Reserved.
+# Copyright Ericsson AB 2006-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -55,7 +55,7 @@ XML_REF3_FILES = \
merl_transform.xml \
prettypr.xml
-XML_PART_FILES = part.xml part_notes.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = chapter.xml
XML_NOTES_FILES = notes.xml
@@ -86,11 +86,11 @@ SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
TOP_SPECS_FILE = specs.xml
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
SPECS_FLAGS = -I../../include
-DVIPS_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -112,13 +112,13 @@ $(XML_REF3_FILES):
escript $(DOCGEN)/priv/bin/xml_from_edoc.escript $(SRC_DIR)/$(@:%.xml=%.erl)
$(XML_CHAPTER_FILES):
- escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) -chapter ../overview.edoc
+ escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) -chapter ../overview.edoc
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
xml: $(XML_REF3_FILES) $(XML_CHAPTER_FILES)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
@@ -126,11 +126,11 @@ clean clean_docs:
rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f $(SPECDIR)/*
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
diff --git a/lib/syntax_tools/doc/src/fascicules.xml b/lib/syntax_tools/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/syntax_tools/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index 8c91f01e3b..bd2bcde2c2 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -32,6 +32,21 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 2.1.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 2.1.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/syntax_tools/doc/src/part_notes.xml b/lib/syntax_tools/doc/src/part_notes.xml
deleted file mode 100644
index e02ffddcb2..0000000000
--- a/lib/syntax_tools/doc/src/part_notes.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2007</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Syntax_Tools Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>Syntax_Tools</em> contains modules for handling abstract
- Erlang syntax trees, in a way that is compatible with the "parse
- trees" of the STDLIB module <c>erl_parse</c>, together with
- utilities for reading source files in unusual ways and
- pretty-printing syntax trees. Also included is an amazing module
- merger and renamer called Igor, as well as an automatic
- code-cleaner.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index e0880d61ee..8d37c40742 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 2.1.3
+SYNTAX_TOOLS_VSN = 2.1.4
diff --git a/lib/tools/doc/src/Makefile b/lib/tools/doc/src/Makefile
index d9c3b0ad2a..b554781382 100644
--- a/lib/tools/doc/src/Makefile
+++ b/lib/tools/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
+# Copyright Ericsson AB 1997-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -47,9 +47,9 @@ XML_REF3_FILES = \
make.xml \
tags.xml \
xref.xml \
- erlang_mode.xml
+ erlang_mode.xml
-XML_PART_FILES = part.xml part_notes.xml part_notes_history.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = \
cover_chapter.xml \
@@ -58,8 +58,7 @@ XML_CHAPTER_FILES = \
lcnt_chapter.xml \
erlang_mode_chapter.xml \
xref_chapter.xml \
- notes.xml \
- notes_history.xml
+ notes.xml
BOOK_FILES = book.xml
@@ -86,9 +85,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -108,17 +107,17 @@ man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -132,4 +131,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
release_spec:
-
diff --git a/lib/tools/doc/src/fascicules.xml b/lib/tools/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/tools/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml
index 5bdfc60448..0c24375b91 100644
--- a/lib/tools/doc/src/lcnt.xml
+++ b/lib/tools/doc/src/lcnt.xml
@@ -371,7 +371,7 @@
<v>Serial = integer()</v>
</type>
<desc>
- <p>Creates a process id with creation 0. Example:</p>
+ <p>Creates a process id with creation 0.</p>
</desc>
</func>
diff --git a/lib/tools/doc/src/note.gif b/lib/tools/doc/src/note.gif
deleted file mode 100644
index 6fffe30419..0000000000
--- a/lib/tools/doc/src/note.gif
+++ /dev/null
Binary files differ
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index 3eaa2058a0..1edc08c9cd 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.11.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.11</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/doc/src/part_notes.xml b/lib/tools/doc/src/part_notes.xml
deleted file mode 100644
index c4c6fa4d7d..0000000000
--- a/lib/tools/doc/src/part_notes.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Tools Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>Tools</em> application contains a number of stand-alone
- tools, which are useful when developing Erlang programs.</p>
- <p>For information about older versions, see
- <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/tools/doc/src/part_notes_history.xml b/lib/tools/doc/src/part_notes_history.xml
deleted file mode 100644
index a34e35fc56..0000000000
--- a/lib/tools/doc/src/part_notes_history.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part>
- <header>
- <copyright>
- <year>2006</year>
- <year>2016</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>Tools Release Notes History</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>Tools</em> application contains a number of stand-alone
- tools, which are useful when developing Erlang programs.</p>
- </description>
- <include file="notes_history"></include>
-</part>
-
diff --git a/lib/tools/doc/src/warning.gif b/lib/tools/doc/src/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/lib/tools/doc/src/warning.gif
+++ /dev/null
Binary files differ
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index 35c93ba4ed..ea4d6cb723 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -54,8 +54,6 @@ EL_FILES = $(EMACS_FILES:%=%.el)
ELC_FILES = $(EMACS_FILES:%=%.elc)
-TEST_FILES = test.erl.indented test.erl.orig
-
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
@@ -75,7 +73,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/emacs"
- $(INSTALL_DATA) $(EL_FILES) $(README_FILES) $(TEST_FILES) \
+ $(INSTALL_DATA) $(EL_FILES) $(README_FILES) \
"$(RELSYSDIR)/emacs"
ifeq ($(DOCTYPE),pdf)
@@ -89,19 +87,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3"
endif
endif
-
-EMACS ?= emacs
-
-test_indentation:
- @rm -f test.erl
- @rm -f test_indent.el
- @echo '(load "erlang-start")' >> test_indent.el
- @echo '(find-file "test.erl.orig")' >> test_indent.el
- @echo "(require 'cl) ; required with Emacs < 23 for ignore-errors" >> test_indent.el
- @echo '(erlang-mode)' >> test_indent.el
- @echo '(toggle-debug-on-error)' >> test_indent.el
- @echo '(erlang-indent-current-buffer)' >> test_indent.el
- @echo '(write-file "test.erl")' >> test_indent.el
- $(EMACS) --batch -Q -L . -l test_indent.el
- diff -u test.erl.indented test.erl
- @echo "No differences between expected and actual indentation"
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index bdb3d9ad4a..534f50ab33 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -279,7 +279,8 @@ Please see the function `tempo-define-template'.")
'((erlang-skel-include erlang-skel-large-header)
"-behaviour(application)." n n
"%% Application callbacks" n
- "-export([start/2, stop/1])." n n
+ "-export([start/2, start_phase/3, stop/1, prep_stop/1," n>
+ "config_change/3])." n n
(erlang-skel-double-separator-start 3)
"%%% Application callbacks" n
(erlang-skel-double-separator-end 3) n
@@ -291,13 +292,14 @@ Please see the function `tempo-define-template'.")
"%% application. If the application is structured according to the OTP" n
"%% design principles as a supervision tree, this means starting the" n
"%% top supervisor of the tree." n
- "%%" n
- "%% @spec start(StartType, StartArgs) -> {ok, Pid} |" n
- "%% {ok, Pid, State} |" n
- "%% {error, Reason}" n
- "%% StartType = normal | {takeover, Node} | {failover, Node}" n
- "%% StartArgs = term()" n
(erlang-skel-separator-end 2)
+ "-spec start(StartType :: normal |" n>
+ "{takeover, Node :: node()} |" n>
+ "{failover, Node :: node()}," n>
+ "StartArgs :: term()) ->" n>
+ "{ok, Pid :: pid()} |" n>
+ "{ok, Pid :: pid(), State :: term()} |" n>
+ "{error, Reason :: term()}." n
"start(_StartType, _StartArgs) ->" n>
"case 'TopSupervisor':start_link() of" n>
"{ok, Pid} ->" n>
@@ -309,15 +311,52 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
+ "%% top supervisor of the tree." n
+ "%% Starts an application with included applications, when" n
+ "%% synchronization is needed between processes in the different" n
+ "%% applications during startup."
+ (erlang-skel-separator-end 2)
+ "-spec start_phase(Phase :: atom()," n>
+ "StartType :: normal |" n>
+ "{takeover, Node :: node()} |" n>
+ "{failover, Node :: node()}," n>
+ "PhaseArgs :: term()) -> ok | {error, Reason :: term()}." n
+ "start_phase(_Phase, _StartType, _PhaseArgs) ->" n>
+ "ok."n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
"%% This function is called whenever an application has stopped. It" n
"%% is intended to be the opposite of Module:start/2 and should do" n
"%% any necessary cleaning up. The return value is ignored." n
- "%%" n
- "%% @spec stop(State) -> void()" n
(erlang-skel-separator-end 2)
+ "-spec stop(State :: term()) -> any()." n
"stop(_State) ->" n>
"ok." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called when an application is about to be stopped," n
+ "%% before shutting down the processes of the application." n
+ (erlang-skel-separator-end 2)
+ "-spec prep_stop(State :: term()) -> NewState :: term()." n
+ "prep_stop(State) ->" n>
+ "State." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called by an application after a code replacement," n
+ "%% if the configuration parameters have changed." n
+ (erlang-skel-separator-end 2)
+ "-spec config_change(Changed :: [{Par :: atom(), Val :: term()}]," n>
+ "New :: [{Par :: atom(), Val :: term()}]," n>
+ "Removed :: [Par :: atom()]) -> ok." n
+ "config_change(_Changed, _New, _Removed) ->" n>
+ "ok." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
@@ -343,9 +382,12 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the supervisor" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, {already_started, Pid :: pid()}} |" n>
+ "{error, {shutdown, term()}} |" n>
+ "{error, term()} |" n>
+ "ignore." n
"start_link() ->" n>
"supervisor:start_link({local, ?SERVER}, ?MODULE, [])." n
n
@@ -359,11 +401,11 @@ Please see the function `tempo-define-template'.")
"%% this function is called by the new process to find out about" n
"%% restart strategy, maximum restart intensity, and child" n
"%% specifications." n
- "%%" n
- "%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |" n
- "%% ignore |" n
- "%% {error, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) ->" n>
+ "{ok, {SupFlags :: supervisor:sup_flags()," n>
+ "[ChildSpec :: supervisor:child_spec()]}} |" n>
+ "ignore." n
"init([]) ->" n
"" n>
"SupFlags = #{strategy => one_for_one," n>
@@ -406,9 +448,11 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the supervisor bridge" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, {already_started, Pid :: pid()}} |" n>
+ "{error, term()} |" n>
+ "ignore." n
"start_link() ->" n>
"supervisor_bridge:start_link({local, ?SERVER}, ?MODULE, [])." n
n
@@ -422,11 +466,10 @@ Please see the function `tempo-define-template'.")
"%% which calls Module:init/1 to start the subsystem. To ensure a" n
"%% synchronized start-up procedure, this function does not return" n
"%% until Module:init/1 has returned." n
- "%%" n
- "%% @spec init(Args) -> {ok, Pid, State} |" n
- "%% ignore |" n
- "%% {error, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) -> {ok, Pid :: pid(), State :: term()} |" n>
+ "{error, Error :: term()} |" n>
+ "ignore." n
"init([]) ->" n>
"case 'AModule':start_link() of" n>
"{ok, Pid} ->" n>
@@ -442,10 +485,9 @@ Please see the function `tempo-define-template'.")
"%% to terminate. It should be the opposite of Module:init/1 and stop" n
"%% the subsystem and do any necessary cleaning up.The return value is" n
"%% ignored." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
- "terminate(Reason, State) ->" n>
+ "-spec terminate(Reason :: shutdown | term(), State :: term()) -> any()." n
+ "terminate(_Reason, _State) ->" n>
"'AModule':stop()," n>
"ok." n
n
@@ -464,9 +506,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0])." n n
"%% gen_server callbacks" n
- "-export([init/1, handle_call/3, handle_cast/2, "
- "handle_info/2," n>
- "terminate/2, code_change/3])." n n
+ "-export([init/1, handle_call/3, handle_cast/2, handle_info/2," n>
+ "terminate/2, code_change/3, format_status/2])." n n
"-define(SERVER, ?MODULE)." n n
@@ -478,9 +519,11 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the server" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, Error :: {already_started, pid()}} |" n>
+ "{error, Error :: term()} |" n>
+ "ignore." n
"start_link() ->" n>
"gen_server:start_link({local, ?SERVER}, ?MODULE, [], [])." n
n
@@ -492,12 +535,12 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Initializes the server" n
- "%%" n
- "%% @spec init(Args) -> {ok, State} |" n
- "%% {ok, State, Timeout} |" n
- "%% ignore |" n
- "%% {stop, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) -> {ok, State :: term()} |" n>
+ "{ok, State :: term(), Timeout :: timeout()} |" n>
+ "{ok, State :: term(), hibernate} |" n>
+ "{stop, Reason :: term()} |" n>
+ "ignore." n
"init([]) ->" n>
"process_flag(trap_exit, true)," n>
"{ok, #state{}}." n
@@ -506,15 +549,16 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Handling call messages" n
- "%%" n
- "%% @spec handle_call(Request, From, State) ->" n
- "%% {reply, Reply, State} |" n
- "%% {reply, Reply, State, Timeout} |" n
- "%% {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, Reply, State} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
+ "-spec handle_call(Request :: term(), From :: {pid(), term()}, State :: term()) ->" n>
+ "{reply, Reply :: term(), NewState :: term()} |" n>
+ "{reply, Reply :: term(), NewState :: term(), Timeout :: timeout()} |" n>
+ "{reply, Reply :: term(), NewState :: term(), hibernate} |" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: term(), Reply :: term(), NewState :: term()} |" n>
+ "{stop, Reason :: term(), NewState :: term()}." n
"handle_call(_Request, _From, State) ->" n>
"Reply = ok," n>
"{reply, Reply, State}." n
@@ -523,23 +567,25 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Handling cast messages" n
- "%%" n
- "%% @spec handle_cast(Msg, State) -> {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
- "handle_cast(_Msg, State) ->" n>
+ "-spec handle_cast(Request :: term(), State :: term()) ->" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: term(), NewState :: term()}." n
+ "handle_cast(_Request, State) ->" n>
"{noreply, State}." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
"%% Handling all non call/cast messages" n
- "%%" n
- "%% @spec handle_info(Info, State) -> {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
+ "-spec handle_info(Info :: timeout() | term(), State :: term()) ->" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: normal | term(), NewState :: term()}." n
"handle_info(_Info, State) ->" n>
"{noreply, State}." n
n
@@ -550,9 +596,9 @@ Please see the function `tempo-define-template'.")
"%% terminate. It should be the opposite of Module:init/1 and do any" n
"%% necessary cleaning up. When it returns, the gen_server terminates" n
"%% with Reason. The return value is ignored." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
+ "-spec terminate(Reason :: normal | shutdown | {shutdown, term()} | term()," n>
+ "State :: term()) -> any()." n
"terminate(_Reason, _State) ->" n>
"ok." n
n
@@ -560,12 +606,26 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Convert process state when code is changed" n
- "%%" n
- "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n
(erlang-skel-separator-end 2)
+ "-spec code_change(OldVsn :: term() | {down, term()}," n>
+ "State :: term()," n>
+ "Extra :: term()) -> {ok, NewState :: term()} |" n>
+ "{error, Reason :: term()}." n
"code_change(_OldVsn, State, _Extra) ->" n>
"{ok, State}." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for changing the form and appearance" n
+ "%% of gen_server status when it is returned from sys:get_status/1,2" n
+ "%% or when it appears in termination error logs." n
+ (erlang-skel-separator-end 2)
+ "-spec format_status(Opt :: normal | terminate," n>
+ "Status :: list()) -> Status :: term()." n
+ "format_status(_Opt, Status) ->" n>
+ "Status." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
@@ -581,8 +641,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0, add_handler/0])." n n
"%% gen_event callbacks" n
- "-export([init/1, handle_event/2, handle_call/2, " n>
- "handle_info/2, terminate/2, code_change/3])." n n
+ "-export([init/1, handle_event/2, handle_call/2, handle_info/2," n>
+ "terminate/2, code_change/3, format_status/2])." n n
"-define(SERVER, ?MODULE)." n n
@@ -594,18 +654,17 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Creates an event manager" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, Error :: {already_started, pid()} | term()}." n
"start_link() ->" n>
"gen_event:start_link({local, ?SERVER})." n
n
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Adds an event handler" n
- "%%" n
- "%% @spec add_handler() -> ok | {'EXIT', Reason} | term()" n
(erlang-skel-separator-end 2)
+ "-spec add_handler() -> ok | {'EXIT', Reason :: term()} | term()." n
"add_handler() ->" n>
"gen_event:add_handler(?SERVER, ?MODULE, [])." n
n
@@ -617,9 +676,11 @@ Please see the function `tempo-define-template'.")
"%% @doc" n
"%% Whenever a new event handler is added to an event manager," n
"%% this function is called to initialize the event handler." n
- "%%" n
- "%% @spec init(Args) -> {ok, State}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term() | {Args :: term(), Term :: term()}) ->" n>
+ "{ok, State :: term()} |" n>
+ "{ok, State :: term(), hibernate} |" n>
+ "{error, Reason :: term()}." n
"init([]) ->" n>
"{ok, #state{}}." n
n
@@ -629,12 +690,13 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event manager receives an event sent using" n
"%% gen_event:notify/2 or gen_event:sync_notify/2, this function is" n
"%% called for each installed event handler to handle the event." n
- "%%" n
- "%% @spec handle_event(Event, State) ->" n
- "%% {ok, State} |" n
- "%% {swap_handler, Args1, State1, Mod2, Args2} |"n
- "%% remove_handler" n
(erlang-skel-separator-end 2)
+ "-spec handle_event(Event :: term(), State :: term()) ->" n>
+ "{ok, NewState :: term()} |" n>
+ "{ok, NewState :: term(), hibernate} |" n>
+ "remove_handler |" n>
+ "{swap_handler, Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()} , Args2 :: term()}." n>
"handle_event(_Event, State) ->" n>
"{ok, State}." n
n
@@ -644,12 +706,13 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event manager receives a request sent using" n
"%% gen_event:call/3,4, this function is called for the specified" n
"%% event handler to handle the request." n
- "%%" n
- "%% @spec handle_call(Request, State) ->" n
- "%% {ok, Reply, State} |" n
- "%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |" n
- "%% {remove_handler, Reply}" n
(erlang-skel-separator-end 2)
+ "-spec handle_call(Request :: term(), State :: term()) ->" n>
+ "{ok, Reply :: term(), NewState :: term()} |" n>
+ "{ok, Reply :: term(), NewState :: term(), hibernate} |" n>
+ "{remove_handler, Reply :: term()} |" n>
+ "{swap_handler, Reply :: term(), Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()}, Args2 :: term()}." n
"handle_call(_Request, State) ->" n>
"Reply = ok," n>
"{ok, Reply, State}." n
@@ -660,12 +723,13 @@ Please see the function `tempo-define-template'.")
"%% This function is called for each installed event handler when" n
"%% an event manager receives any other message than an event or a" n
"%% synchronous request (or a system message)." n
- "%%" n
- "%% @spec handle_info(Info, State) ->" n
- "%% {ok, State} |" n
- "%% {swap_handler, Args1, State1, Mod2, Args2} |" n
- "%% remove_handler" n
(erlang-skel-separator-end 2)
+ "-spec handle_info(Info :: term(), State :: term()) ->" n>
+ "{ok, NewState :: term()} |" n>
+ "{ok, NewState :: term(), hibernate} |" n>
+ "remove_handler |" n>
+ "{swap_handler, Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()}, Args2 :: term()}." n
"handle_info(_Info, State) ->" n>
"{ok, State}." n
n
@@ -675,22 +739,40 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event handler is deleted from an event manager, this" n
"%% function is called. It should be the opposite of Module:init/1 and" n
"%% do any necessary cleaning up." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
- "terminate(_Reason, _State) ->" n>
+ "-spec terminate(Arg :: {stop, Reason :: term()} |" n>
+ "stop |" n>
+ "remove_handler |" n>
+ "{error, {'EXIT', Reason :: term()}} |" n>
+ "{error, Term :: term()} |" n>
+ "term()," n>
+ "State :: term()) -> any()." n
+ "terminate(_Arg, _State) ->" n>
"ok." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
"%% Convert process state when code is changed" n
- "%%" n
- "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n
(erlang-skel-separator-end 2)
+ "-spec code_change(OldVsn :: term() | {down, term()}," n>
+ "State :: term()," n>
+ "Extra :: term()) -> {ok, NewState :: term()}." n
"code_change(_OldVsn, State, _Extra) ->" n>
"{ok, State}." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for changing the form and appearance" n
+ "%% of gen_event status when it is returned from sys:get_status/1,2" n
+ "%% or when it appears in termination error logs." n
+ (erlang-skel-separator-end 2)
+ "-spec format_status(Opt :: normal | terminate," n>
+ "Status :: list()) -> Status :: term()." n
+ "format_status(_Opt, Status) ->" n>
+ "Status." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 411e0e13df..e4166053d5 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -4,7 +4,7 @@
;; Author: Anders Lindgren
;; Keywords: erlang, languages, processes
;; Date: 2011-12-11
-;; Version: 2.8.0
+;; Version: 2.8.1
;; Package-Requires: ((emacs "24.1"))
;; %CopyrightBegin%
@@ -84,7 +84,7 @@
"The Erlang programming language."
:group 'languages)
-(defconst erlang-version "2.8.0"
+(defconst erlang-version "2.8.1"
"The version number of Erlang mode.")
(defcustom erlang-root-dir nil
@@ -2745,7 +2745,7 @@ Return nil if inside string, t if in a comment."
(1+ (nth 2 stack-top)))
((= (char-syntax (following-char)) ?\))
(goto-char (nth 1 stack-top))
- (cond ((looking-at "[({]\\s *\\($\\|%\\)")
+ (cond ((erlang-record-or-function-args-p)
;; Line ends with parenthesis.
(let ((previous (erlang-indent-find-preceding-expr))
(stack-pos (nth 2 stack-top)))
@@ -2755,19 +2755,10 @@ Return nil if inside string, t if in a comment."
(nth 2 stack-top))))
((= (following-char) ?,)
;; a comma at the start of the line: line up with opening parenthesis.
- (nth 2 stack-top))
+ (min (nth 2 stack-top)
+ (erlang-indent-element stack-top indent-point token)))
(t
- (goto-char (nth 1 stack-top))
- (let ((base (cond ((looking-at "[({]\\s *\\($\\|%\\)")
- ;; Line ends with parenthesis.
- (erlang-indent-parenthesis (nth 2 stack-top)))
- (t
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (1+ (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column)))))
- (erlang-indent-standard indent-point token base 't)))))
+ (erlang-indent-element stack-top indent-point token))))
;;
((eq (car stack-top) '<<)
;; Element of binary (possible comprehension) expression,
@@ -2776,13 +2767,11 @@ Return nil if inside string, t if in a comment."
(+ 2 (nth 2 stack-top)))
((looking-at "\\(>>\\)[^_a-zA-Z0-9]")
(nth 2 stack-top))
+ ((= (following-char) ?,)
+ (min (+ (nth 2 stack-top) 1)
+ (- (erlang-indent-to-first-element stack-top 2) 1)))
(t
- (goto-char (nth 1 stack-top))
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (+ 2 (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column))))
+ (erlang-indent-to-first-element stack-top 2))))
((memq (car stack-top) '(icr fun spec))
;; The default indentation is the column of the option
@@ -2838,12 +2827,13 @@ Return nil if inside string, t if in a comment."
(let ((base (erlang-indent-find-base stack indent-point off skip)))
;; Special cases
(goto-char indent-point)
- (cond ((looking-at "\\(end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
+ (cond ((looking-at "\\(;\\|end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
(if (eq (car stack-top) '->)
(erlang-pop stack))
- (if stack
- (erlang-caddr (car stack))
- 0))
+ (cond ((and stack (looking-at ";"))
+ (+ (erlang-caddr (car stack)) (- erlang-indent-level 2)))
+ (stack (erlang-caddr (car stack)))
+ (t off)))
((looking-at "catch\\b\\($\\|[^_a-zA-Z0-9]\\)")
;; Are we in a try
(let ((start (if (eq (car stack-top) '->)
@@ -2917,6 +2907,22 @@ Return nil if inside string, t if in a comment."
(current-column))) start-alternativ))))))
)))
+(defun erlang-indent-to-first-element (stack-top extra)
+ ;; Indent to the same column as the first
+ ;; argument. extra should be 1 for lists tuples or 2 for binaries
+ (goto-char (+ (nth 1 stack-top) extra))
+ (skip-chars-forward " \t")
+ (current-column))
+
+(defun erlang-indent-element (stack-top indent-point token)
+ (goto-char (nth 1 stack-top))
+ (let ((base (cond ((erlang-record-or-function-args-p)
+ ;; Line ends with parenthesis.
+ (erlang-indent-parenthesis (nth 2 stack-top)))
+ (t
+ (erlang-indent-to-first-element stack-top 1)))))
+ (erlang-indent-standard indent-point token base 't)))
+
(defun erlang-indent-standard (indent-point token base inside-parenthesis)
"Standard indent when in blocks or tuple or arguments.
Look at last thing to see in what state we are, move relative to the base."
@@ -2942,6 +2948,9 @@ Return nil if inside string, t if in a comment."
;; Avoid treating comments a continued line.
((= (following-char) ?%)
base)
+ ((and (= (following-char) ?,) inside-parenthesis)
+ ;; a comma at the start of the line line up with parenthesis
+ (- base 1))
;; Continued line (e.g. line beginning
;; with an operator.)
(t
@@ -3031,11 +3040,21 @@ This assumes that the preceding expression is either simple
(t col)))
col))))
+(defun erlang-record-or-function-args-p ()
+ (and (looking-at "[({]\\s *\\($\\|%\\)")
+ (or (eq (following-char) ?\( )
+ (save-excursion
+ (ignore-errors (forward-sexp (- 1)))
+ (eq (preceding-char) ?#)))))
+
(defun erlang-indent-parenthesis (stack-position)
(let ((previous (erlang-indent-find-preceding-expr)))
- (if (> previous stack-position)
- (+ stack-position erlang-argument-indent)
- (+ previous erlang-argument-indent))))
+ (cond ((eq previous stack-position) ;; tuple or map not a record
+ (1+ stack-position))
+ ((> previous stack-position)
+ (+ stack-position erlang-argument-indent))
+ (t
+ (+ previous erlang-argument-indent)))))
(defun erlang-skip-blank (&optional lim)
"Skip over whitespace and comments until limit reached."
@@ -5169,7 +5188,6 @@ future, a new shell on an already running host will be started."
;; e.g. it does not assume that we are running an inferior
;; Erlang, there exists a lot of other possibilities.
-
(defvar erlang-shell-buffer-name "*erlang*"
"The name of the Erlang link shell buffer.")
@@ -5180,46 +5198,28 @@ Also see the description of `ielm-prompt-read-only'."
:type 'boolean
:package-version '(erlang . "2.8.0"))
-(defvar erlang-shell-mode-map nil
- "Keymap used by Erlang shells.")
-
-
-(defvar erlang-shell-mode-hook nil
- "User functions to run when an Erlang shell is started.
-
-This hook is used to change the behaviour of Erlang mode. It is
-normally used by the user to personalise the programming environment.
-When used in a site init file, it could be used to customise Erlang
-mode for all users on the system.
-
-The function added to this hook is run every time a new Erlang
-shell is started.
+(defvar erlang-shell-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "\M-\t" 'erlang-complete-tag)
-See also `erlang-load-hook', a hook which is run once, when Erlang
-mode is loaded, and `erlang-mode-hook' which is run every time a new
-Erlang source file is loaded into Emacs.")
+ ;; Normally the other way around.
+ (define-key map "\C-a" 'comint-bol)
+ (define-key map "\C-c\C-a" 'beginning-of-line)
+ (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
+ (define-key map "\M-\C-m" 'compile-goto-error)
+ map)
+ "Keymap used by Erlang shells.")
(defvar erlang-input-ring-file-name "~/.erlang_history"
"When non-nil, file name used to store Erlang shell history information.")
-
-(defun erlang-shell-mode ()
+(define-derived-mode erlang-shell-mode comint-mode "Erlang Shell"
"Major mode for interacting with an Erlang shell.
-We assume that we already are in Comint mode.
-
The following special commands are available:
\\{erlang-shell-mode-map}"
- (interactive)
- (setq major-mode 'erlang-shell-mode)
- (setq mode-name "Erlang Shell")
(erlang-mode-variables)
- (if erlang-shell-mode-map
- nil
- (setq erlang-shell-mode-map (copy-keymap comint-mode-map))
- (erlang-shell-mode-commands erlang-shell-mode-map))
- (use-local-map erlang-shell-mode-map)
;; Needed when compiling directly from the Erlang shell.
(setq compilation-last-buffer (current-buffer))
(setq comint-prompt-regexp "^[^>=]*> *")
@@ -5233,7 +5233,6 @@ The following special commands are available:
'inferior-erlang-strip-delete nil t)
(add-hook 'comint-output-filter-functions
'inferior-erlang-strip-ctrl-m nil t)
-
(setq comint-input-ring-file-name erlang-input-ring-file-name)
(comint-read-input-ring t)
(make-local-variable 'kill-buffer-hook)
@@ -5252,8 +5251,7 @@ The following special commands are available:
(define-key map [menu-bar compilation]
(cons "Errors" compilation-menu-map)))
map))))
- (erlang-tags-init)
- (run-hooks 'erlang-shell-mode-hook))
+ (erlang-tags-init))
(defun erlang-mouse-2-command (event)
@@ -5275,13 +5273,6 @@ Selects Comint or Compilation mode command as appropriate."
(call-interactively (lookup-key compilation-mode-map "\C-m"))
(call-interactively (lookup-key comint-mode-map "\C-m"))))
-(defun erlang-shell-mode-commands (map)
- (define-key map "\M-\t" 'erlang-complete-tag)
- (define-key map "\C-a" 'comint-bol) ; Normally the other way around.
- (define-key map "\C-c\C-a" 'beginning-of-line)
- (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
- (define-key map "\M-\C-m" 'compile-goto-error))
-
;;;
;;; Inferior Erlang -- Run an Erlang shell as a subprocess.
;;;
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
deleted file mode 100644
index 14a4eca7c3..0000000000
--- a/lib/tools/emacs/test.erl.indented
+++ /dev/null
@@ -1,784 +0,0 @@
-%% -*- Mode: erlang; indent-tabs-mode: nil -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-
-%%%-------------------------------------------------------------------
-%%% File : test.erl
-%%% Author : Dan Gudmundsson <[email protected]>
-%%% Description : Test emacs mode indention and font-locking
-%%% this file is intentionally not indented.
-%%% Copy the file and indent it and you should end up with test.erl.indented
-%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
-%%%-------------------------------------------------------------------
-
-%% Start off with syntax highlighting you have to verify this by looking here
-%% and see that the code looks alright
-
--module(test).
--compile(export_all).
-
-%% Used to cause an "Unbalanced parentheses" error.
-foo(M) ->
- M#{a :=<<"a">>
- ,b:=1}.
-foo() ->
- #{a =><<"a">>
- ,b=>1}.
-
-%% Module attributes should be highlighted
-
--export([t/1]).
--record(record1, {a,
- b,
- c
- }).
--record(record2, {
- a,
- b
- }).
-
--record(record3, {a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record4, {
- a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record5, { a = 1 :: integer()
- , b = foobar :: atom()
- }).
-
--define(MACRO_1, macro).
--define(MACRO_2(_), macro).
-
--spec t(integer()) -> any().
-
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
- | 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
--type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
- %% Should be highlighted
- term() |
- bool() |
- byte() |
- char() |
- non_neg_integer() | nonempty_list() |
- pos_integer() |
- neg_integer() |
- number() |
- list() |
- nonempty_improper_list() | nonempty_maybe_improper_list() |
- maybe_improper_list() | string() | iolist() | byte() |
- module() |
- mfa() |
- node() |
- timeout() |
- no_return() |
- %% Should not be highlighted
- nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-
-
--type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
- <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
- <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
- <<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
--type t19() :: fun((t18()) -> t16()) |
- fun((nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
- nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
- 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())]}.
--type t26() :: #rec4{ a :: integer()
- , b :: any()
- }.
--type t27() :: { integer()
- , atom()
- }.
--type t99() ::
- {t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
- t15(),t20(),t21(), t22(),t25()}.
--spec t1(FooBar :: t99()) -> t99();
- (t2()) -> t2();
- (t4()) -> t4() when is_subtype(t4(), t24);
- (t23()) -> t23() when is_subtype(t23(), atom()),
- is_subtype(t23(), t14());
- (t24()) -> t24() when is_subtype(t24(), atom()),
- is_subtype(t24(), t14()),
- is_subtype(t24(), t4()).
-
--spec over(I :: integer()) -> R1 :: foo:typen();
- (A :: atom()) -> R2 :: foo:atomen();
- (T :: tuple()) -> R3 :: bar:typen().
-
--spec mod:t2() -> any().
-
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec all(fun((T) -> boolean()), List :: [T]) ->
- boolean() when is_subtype(T, term()). % (*)
-
--spec get_closest_pid(term()) ->
- Return :: pid()
- | {'error', {'no_process', term()}
- | {'no_such_group', term()}}.
-
--spec add( X :: integer()
- , Y :: integer()
- ) -> integer().
-
--opaque attributes_data() ::
- [{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
--record(r,{
- f1 :: attributes_data(),
- f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
- [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f333 :: [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f3 = x:y(),
- f4 = x:z() :: t99(),
- f17 :: 'undefined',
- f18 :: 1 | 2 | 'undefined',
- f19 = 3 :: integer()|undefined,
- f5 = 3 :: undefined|integer()}).
-
--record(state, {
- sequence_number = 1 :: integer()
- }).
-
-
-highlighting(X) % Function definitions should be highlighted
- when is_integer(X) -> % and so should `when' and `is_integer' be
- %% Highlighting
- %% Various characters (we keep an `atom' after to see that highlighting ends)
- $a,atom, % Characters should be marked
- "string",atom, % and strings
- 'asdasd',atom, % quote should be atoms??
- 'VaV',atom,
- 'aVa',atom,
- '\'atom',atom,
- 'atom\'',atom,
- 'at\'om',atom,
- '#1',atom,
-
- $", atom, % atom should be ok
- $', atom,
-
- "string$", atom, "string$", atom, % currently buggy I know...
- "string\$", atom, % workaround for bug above
-
- "char $in string", atom,
-
- 'atom$', atom, 'atom$', atom,
- 'atom\$', atom,
-
- 'char $in atom', atom,
-
- $[, ${, $\\, atom,
- ?MACRO_1,
- ?MACRO_2(foo),
-
- %% Numerical constants
- 16#DD, % AD Should not be highlighted
- 32#dd, % AD Should not be highlighted
- 32#ddAB, % AD Should not be highlighted
- 32#101, % AD Should not be highlighted
- 32#ABTR, % AD Should not be highlighted
-
- %% Variables
- Variables = lists:foo(),
- _Variables = lists:foo(), % AD
- AppSpec = Xyz/2,
- Module42 = Xyz(foo, bar),
- Module:foo(),
- _Module:foo(), % AD
- FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
- _FooÅÅ = 42, % AD Should highlight _FooÅÅ
-
- %% Bifs
- erlang:registered(),
- registered(),
- hd(tl(tl(hd([a,b,c])))),
- erlang:anything(lists),
- %% Guards
- is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
- not_a_guard:is_list([]),
- %% Other Types
-
- atom, % not (currently) hightlighted
- 234234,
- 234.43,
-
- [list, are, not, higlighted],
- {nor, is, tuple},
- ok.
-
-%%%
-%%% Indentation
-%%%
-
-%%% Left
-
-%% Indented
-
- % Right
-
-
-indent_basics(X, Y, Z)
- when X > 42,
- Z < 13;
- Y =:= 4711 ->
- %% comments
- % right comments
- case lists:filter(fun(_, AlongName,
- B,
- C) ->
- true
- end,
- [a,v,b])
- of
- [] ->
- Y = 5 * 43,
- ok;
- [_|_] ->
- Y = 5 * 43,
- ok
- end,
- Y,
- %% List, tuples and binaries
- [a,
- b, c
- ],
- [ a,
- b, c
- ],
-
- [
- a,
- b
- ],
- {a,
- b,c
- },
- { a,
- b,c
- },
-
- {
- a,
- b
- },
-
- <<1:8,
- 2:8
- >>,
- <<
- 1:8,
- 2:8
- >>,
- << 1:8,
- 2:8
- >>,
-
- (a,
- b,
- c
- ),
-
- ( a,
- b,
- c
- ),
-
-
- (
- a,
- b,
- c
- ),
-
- call(2#42423 bor
- #4234,
- 2#5432,
- other_arg),
- ok;
-indent_basics(Xlongname,
- #struct{a=Foo,
- b=Bar},
- [X|
- Y]) ->
- testing_next_clause,
- ok;
-indent_basics( % AD added clause
- X, % not sure how this should look
- Y,
- Z)
- when
- X < 42, Z > 13;
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) when % AD added clause
- X < 42, Z > 13; % testing when indentation
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) % AD added clause
- when % testing when indentation
- X < 42, Z > 13; % unsure about this one
- Y =:= 4711 ->
- foo.
-
-
-
-indent_nested() ->
- [
- {foo, 2, "string"},
- {bar, 3, "another string"}
- ].
-
-
-indent_icr(Z) -> % icr = if case receive
- %% If
- if Z >= 0 ->
- X = 43 div 4,
- foo(X);
- Z =< 10 ->
- X = 43 div 4,
- foo(X);
- Z == 5 orelse
- Z == 7 ->
- X = 43 div 4,
- foo(X);
- true ->
- if_works
- end,
- %% Case
- case {Z, foo, bar} of
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- {Z,_,_} when
- Z =:= 42 -> % AD line should be indented as a when
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when Z < 10 -> % AD when should be indented
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when % AD when should be indented
- Z < 10 % and the guards should follow when
- andalso % unsure about how though
- true ->
- X = 43 div 4,
- foo(X)
- end,
- %% begin
- begin
- sune,
- X = 74234 + foo(8456) +
- 345 div 43,
- ok
- end,
-
-
- %% receive
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- end,
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z % AD added clause
- when Z =:= 1 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z when % AD added clause
- Z =:= 2 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- after infinity ->
- foo(X),
- asd(X),
- 5*43
- end,
- receive
- after 10 ->
- foo(X),
- asd(X),
- 5*43
- end,
- ok.
-
-indent_fun() ->
- %% Changed fun to one indention level
- Var = spawn(fun(X)
- when X == 2;
- X > 10 ->
- hello,
- case Hello() of
- true when is_atom(X) ->
- foo;
- false ->
- bar
- end;
- (Foo) when is_atom(Foo),
- is_integer(X) ->
- X = 6* 45,
- Y = true andalso
- kalle
- end),
- %% check EEP37 named funs
- Fn1 = fun Fact(N) when N > 0 ->
- F = Fact(N-1),
- N * F;
- Fact(0) ->
- 1
- end,
- %% check anonymous funs too
- Fn2 = fun(0) ->
- 1;
- (N) ->
- N
- end,
- ok.
-
-indent_try_catch() ->
- try
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R % AD added clause
- when R =:= 42 -> % when should be indented
- foo(R);
- error:R % AD added clause
- when % when should be indented
- R =:= 42 -> % but unsure about this (maybe 2 more)
- foo(R);
- error:R when % AD added clause
- R =:= foo -> % line should be 2 indented (works)
- foo(R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile)
- end;
-indent_try_catch() ->
- try
- foo(bar)
- of
- X when true andalso
- kalle ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2);
- X % AD added clause
- when false andalso % when should be 2 indented
- bengt ->
- gurka();
- X when % AD added clause
- false andalso % line should be 2 indented
- not bengt ->
- gurka();
- X ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile),
- bar(with_long_arg,
- with_second_arg)
- end;
-indent_try_catch() ->
- try foo()
- after
- foo(),
- bar(with_long_arg,
- with_second_arg)
- end.
-
-indent_catch() ->
- D = B +
- float(43.1),
-
- B = catch oskar(X),
-
- A = catch (baz +
- bax),
- catch foo(),
-
- C = catch B +
- float(43.1),
-
- case catch foo(X) of
- A ->
- B
- end,
-
- case
- catch foo(X)
- of
- A ->
- B
- end,
-
- case
- foo(X)
- of
- A ->
- catch B,
- X
- end,
-
- try sune of
- _ -> foo
- catch _:_ -> baf
- end,
-
- try
- sune
- of
- _ ->
- X = 5,
- (catch foo(X)),
- X + 10
- catch _:_ -> baf
- end,
-
- try
- (catch sune)
- of
- _ ->
- catch foo() %% BUGBUG can't handle catch inside try without parentheses
- catch _:_ ->
- baf
- end,
-
- try
- (catch exit())
- catch
- _ ->
- catch baf()
- end,
- ok.
-
-indent_binary() ->
- X = lists:foldr(fun(M) ->
- <<Ma/binary, " ">>
- end, [], A),
- A = <<X/binary, 0:8>>,
- B.
-
-
-indent_comprehensions() ->
- %% I don't have a good idea how we want to handle this
- %% but they are here to show how they are indented today.
- Result1 = [X ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- ],
- Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- ],
-
- Binary1 = << <<X:8>> ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- >>,
-
- Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- >>,
- ok.
-
-%% This causes an error in earlier erlang-mode versions.
-foo() ->
- [#foo{
- foo = foo}].
-
-%% Record indentation
-some_function_with_a_very_long_name() ->
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b},
- case dummy_function_with_a_very_very_long_name(x) of
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- ok;
- Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b};
- #xyz{
- a=1,
- b=2} ->
- ok
- end.
-
-another_function_with_a_very_very_long_name() ->
- #rec{
- field1=1,
- field2=1}.
-
-some_function_name_xyz(xyzzy, #some_record{
- field1=Field1,
- field2=Field2}) ->
- SomeVariable = f(#'Some-long-record-name'{
- field_a = 1,
- 'inter-xyz-parameters' =
- #'Some-other-very-long-record-name'{
- field2 = Field1,
- field2 = Field2}}),
- {ok, SomeVariable}.
-
-commas_first() ->
- {abc, [ {some_var, 1}
- , {some_other_var, 2}
- , {erlang_ftw, 9}
- , {erlang_cookie, 'cookie'}
- , {cmds,
- [ {one, "sudo ls"}
- , {one, "sudo ls"}
- , {two, "sudo ls"}
- , {three, "sudo ls"}
- , {four, "sudo ls"}
- , {three, "sudo ls"}
- ] }
- , {ssh_username, "yow"}
- , {cluster,
- [ {aaaa, [ {"10.198.55.12" , "" }
- , {"10.198.55.13" , "" }
- ] }
- , {bbbb, [ {"10.198.55.151", "" }
- , {"10.198.55.123", "" }
- , {"10.198.55.34" , "" }
- , {"10.198.55.85" , "" }
- , {"10.198.55.67" , "" }
- ] }
- , {cccc, [ {"10.198.55.68" , "" }
- , {"10.198.55.69" , "" }
- ] }
- ] }
- ]
- }.
-
-
-%% this used to result in a scan-sexp error
-[{
- }].
-
-%% this used to result in 2x the correct indentation within the function
-%% body, due to the function name being mistaken for a keyword
-catcher(N) ->
- try generate_exception(N) of
- Val -> {N, normal, Val}
- catch
- throw:X -> {N, caught, thrown, X};
- exit:X -> {N, caught, exited, X};
- error:X -> {N, caught, error, X}
- end.
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
deleted file mode 100644
index c0cf1749b6..0000000000
--- a/lib/tools/emacs/test.erl.orig
+++ /dev/null
@@ -1,784 +0,0 @@
-%% -*- Mode: erlang; indent-tabs-mode: nil -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-
-%%%-------------------------------------------------------------------
-%%% File : test.erl
-%%% Author : Dan Gudmundsson <[email protected]>
-%%% Description : Test emacs mode indention and font-locking
-%%% this file is intentionally not indented.
-%%% Copy the file and indent it and you should end up with test.erl.indented
-%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
-%%%-------------------------------------------------------------------
-
-%% Start off with syntax highlighting you have to verify this by looking here
-%% and see that the code looks alright
-
--module(test).
--compile(export_all).
-
-%% Used to cause an "Unbalanced parentheses" error.
-foo(M) ->
-M#{a :=<<"a">>
-,b:=1}.
-foo() ->
-#{a =><<"a">>
-,b=>1}.
-
-%% Module attributes should be highlighted
-
--export([t/1]).
--record(record1, {a,
- b,
- c
-}).
--record(record2, {
- a,
- b
- }).
-
--record(record3, {a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
-234,
- d}).
-
--record(record4, {
- a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record5, { a = 1 :: integer()
-, b = foobar :: atom()
-}).
-
--define(MACRO_1, macro).
--define(MACRO_2(_), macro).
-
--spec t(integer()) -> any().
-
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
- | 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
--type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
- %% Should be highlighted
- term() |
- bool() |
- byte() |
- char() |
- non_neg_integer() | nonempty_list() |
- pos_integer() |
- neg_integer() |
- number() |
- list() |
- nonempty_improper_list() | nonempty_maybe_improper_list() |
- maybe_improper_list() | string() | iolist() | byte() |
- module() |
- mfa() |
- node() |
- timeout() |
- no_return() |
- %% Should not be highlighted
- nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-
-
--type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
- <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
-<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
-<<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
--type t19() :: fun((t18()) -> t16()) |
- fun((nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
-nonempty_maybe_improper_list('integer', any())|
-1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
-1|2|3|4|a|b|c|d|
-nonempty_maybe_improper_list(integer, any())]}.
--type t26() :: #rec4{ a :: integer()
-, b :: any()
-}.
--type t27() :: { integer()
-, atom()
-}.
--type t99() ::
-{t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
-t15(),t20(),t21(), t22(),t25()}.
--spec t1(FooBar :: t99()) -> t99();
-(t2()) -> t2();
- (t4()) -> t4() when is_subtype(t4(), t24);
-(t23()) -> t23() when is_subtype(t23(), atom()),
- is_subtype(t23(), t14());
-(t24()) -> t24() when is_subtype(t24(), atom()),
- is_subtype(t24(), t14()),
- is_subtype(t24(), t4()).
-
--spec over(I :: integer()) -> R1 :: foo:typen();
- (A :: atom()) -> R2 :: foo:atomen();
- (T :: tuple()) -> R3 :: bar:typen().
-
--spec mod:t2() -> any().
-
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec all(fun((T) -> boolean()), List :: [T]) ->
- boolean() when is_subtype(T, term()). % (*)
-
--spec get_closest_pid(term()) ->
- Return :: pid()
- | {'error', {'no_process', term()}
- | {'no_such_group', term()}}.
-
--spec add( X :: integer()
-, Y :: integer()
-) -> integer().
-
--opaque attributes_data() ::
-[{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
--record(r,{
- f1 :: attributes_data(),
-f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
-[t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
-f333 :: [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
-f3 = x:y(),
-f4 = x:z() :: t99(),
-f17 :: 'undefined',
-f18 :: 1 | 2 | 'undefined',
-f19 = 3 :: integer()|undefined,
-f5 = 3 :: undefined|integer()}).
-
--record(state, {
- sequence_number = 1 :: integer()
- }).
-
-
-highlighting(X) % Function definitions should be highlighted
- when is_integer(X) -> % and so should `when' and `is_integer' be
- %% Highlighting
- %% Various characters (we keep an `atom' after to see that highlighting ends)
- $a,atom, % Characters should be marked
- "string",atom, % and strings
- 'asdasd',atom, % quote should be atoms??
- 'VaV',atom,
- 'aVa',atom,
- '\'atom',atom,
- 'atom\'',atom,
- 'at\'om',atom,
- '#1',atom,
-
- $", atom, % atom should be ok
- $', atom,
-
- "string$", atom, "string$", atom, % currently buggy I know...
- "string\$", atom, % workaround for bug above
-
- "char $in string", atom,
-
- 'atom$', atom, 'atom$', atom,
- 'atom\$', atom,
-
- 'char $in atom', atom,
-
- $[, ${, $\\, atom,
- ?MACRO_1,
- ?MACRO_2(foo),
-
- %% Numerical constants
- 16#DD, % AD Should not be highlighted
- 32#dd, % AD Should not be highlighted
- 32#ddAB, % AD Should not be highlighted
- 32#101, % AD Should not be highlighted
- 32#ABTR, % AD Should not be highlighted
-
- %% Variables
- Variables = lists:foo(),
- _Variables = lists:foo(), % AD
- AppSpec = Xyz/2,
- Module42 = Xyz(foo, bar),
- Module:foo(),
- _Module:foo(), % AD
- FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
- _FooÅÅ = 42, % AD Should highlight _FooÅÅ
-
- %% Bifs
- erlang:registered(),
- registered(),
- hd(tl(tl(hd([a,b,c])))),
- erlang:anything(lists),
- %% Guards
- is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
- not_a_guard:is_list([]),
- %% Other Types
-
- atom, % not (currently) hightlighted
- 234234,
- 234.43,
-
- [list, are, not, higlighted],
- {nor, is, tuple},
- ok.
-
-%%%
-%%% Indentation
-%%%
-
-%%% Left
-
-%% Indented
-
-% Right
-
-
-indent_basics(X, Y, Z)
- when X > 42,
-Z < 13;
-Y =:= 4711 ->
- %% comments
- % right comments
- case lists:filter(fun(_, AlongName,
- B,
- C) ->
- true
- end,
- [a,v,b])
- of
- [] ->
- Y = 5 * 43,
- ok;
- [_|_] ->
- Y = 5 * 43,
- ok
- end,
- Y,
- %% List, tuples and binaries
- [a,
- b, c
- ],
- [ a,
- b, c
- ],
-
- [
- a,
- b
-],
- {a,
- b,c
- },
- { a,
- b,c
- },
-
- {
- a,
- b
- },
-
-<<1:8,
- 2:8
- >>,
- <<
- 1:8,
- 2:8
- >>,
- << 1:8,
- 2:8
- >>,
-
- (a,
- b,
- c
- ),
-
- ( a,
- b,
- c
- ),
-
-
- (
- a,
- b,
- c
- ),
-
- call(2#42423 bor
- #4234,
- 2#5432,
- other_arg),
- ok;
-indent_basics(Xlongname,
- #struct{a=Foo,
- b=Bar},
- [X|
- Y]) ->
- testing_next_clause,
- ok;
-indent_basics( % AD added clause
- X, % not sure how this should look
- Y,
- Z)
- when
- X < 42, Z > 13;
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) when % AD added clause
- X < 42, Z > 13; % testing when indentation
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) % AD added clause
- when % testing when indentation
- X < 42, Z > 13; % unsure about this one
- Y =:= 4711 ->
- foo.
-
-
-
-indent_nested() ->
- [
- {foo, 2, "string"},
- {bar, 3, "another string"}
- ].
-
-
-indent_icr(Z) -> % icr = if case receive
- %% If
- if Z >= 0 ->
- X = 43 div 4,
- foo(X);
- Z =< 10 ->
- X = 43 div 4,
- foo(X);
- Z == 5 orelse
- Z == 7 ->
- X = 43 div 4,
- foo(X);
- true ->
- if_works
- end,
- %% Case
- case {Z, foo, bar} of
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- {Z,_,_} when
- Z =:= 42 -> % AD line should be indented as a when
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when Z < 10 -> % AD when should be indented
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when % AD when should be indented
- Z < 10 % and the guards should follow when
- andalso % unsure about how though
- true ->
- X = 43 div 4,
- foo(X)
- end,
- %% begin
- begin
- sune,
- X = 74234 + foo(8456) +
- 345 div 43,
- ok
- end,
-
-
- %% receive
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- end,
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z % AD added clause
- when Z =:= 1 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z when % AD added clause
- Z =:= 2 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- after infinity ->
- foo(X),
- asd(X),
- 5*43
- end,
- receive
- after 10 ->
- foo(X),
- asd(X),
- 5*43
- end,
- ok.
-
-indent_fun() ->
- %% Changed fun to one indention level
-Var = spawn(fun(X)
- when X == 2;
- X > 10 ->
- hello,
- case Hello() of
- true when is_atom(X) ->
- foo;
- false ->
- bar
- end;
- (Foo) when is_atom(Foo),
- is_integer(X) ->
- X = 6* 45,
- Y = true andalso
- kalle
- end),
-%% check EEP37 named funs
-Fn1 = fun Fact(N) when N > 0 ->
- F = Fact(N-1),
- N * F;
-Fact(0) ->
- 1
- end,
-%% check anonymous funs too
- Fn2 = fun(0) ->
-1;
- (N) ->
- N
- end,
- ok.
-
-indent_try_catch() ->
- try
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R % AD added clause
- when R =:= 42 -> % when should be indented
- foo(R);
- error:R % AD added clause
- when % when should be indented
- R =:= 42 -> % but unsure about this (maybe 2 more)
- foo(R);
- error:R when % AD added clause
- R =:= foo -> % line should be 2 indented (works)
- foo(R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile)
- end;
-indent_try_catch() ->
- try
- foo(bar)
- of
- X when true andalso
- kalle ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2);
- X % AD added clause
- when false andalso % when should be 2 indented
- bengt ->
- gurka();
- X when % AD added clause
- false andalso % line should be 2 indented
- not bengt ->
- gurka();
- X ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile),
- bar(with_long_arg,
- with_second_arg)
- end;
- indent_try_catch() ->
- try foo()
- after
- foo(),
- bar(with_long_arg,
- with_second_arg)
- end.
-
-indent_catch() ->
- D = B +
- float(43.1),
-
- B = catch oskar(X),
-
- A = catch (baz +
- bax),
- catch foo(),
-
- C = catch B +
- float(43.1),
-
- case catch foo(X) of
- A ->
- B
- end,
-
- case
- catch foo(X)
- of
- A ->
- B
- end,
-
- case
- foo(X)
- of
- A ->
- catch B,
- X
- end,
-
- try sune of
- _ -> foo
- catch _:_ -> baf
- end,
-
- try
-sune
- of
- _ ->
- X = 5,
- (catch foo(X)),
- X + 10
- catch _:_ -> baf
- end,
-
- try
- (catch sune)
- of
- _ ->
- catch foo() %% BUGBUG can't handle catch inside try without parentheses
- catch _:_ ->
- baf
- end,
-
- try
-(catch exit())
- catch
-_ ->
- catch baf()
- end,
- ok.
-
-indent_binary() ->
- X = lists:foldr(fun(M) ->
- <<Ma/binary, " ">>
- end, [], A),
- A = <<X/binary, 0:8>>,
- B.
-
-
-indent_comprehensions() ->
-%% I don't have a good idea how we want to handle this
-%% but they are here to show how they are indented today.
-Result1 = [X ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- ],
-Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- ],
-
-Binary1 = << <<X:8>> ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- >>,
-
-Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- >>,
-ok.
-
-%% This causes an error in earlier erlang-mode versions.
-foo() ->
-[#foo{
-foo = foo}].
-
-%% Record indentation
-some_function_with_a_very_long_name() ->
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b},
- case dummy_function_with_a_very_very_long_name(x) of
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- ok;
- Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b};
- #xyz{
- a=1,
- b=2} ->
- ok
- end.
-
-another_function_with_a_very_very_long_name() ->
- #rec{
- field1=1,
- field2=1}.
-
-some_function_name_xyz(xyzzy, #some_record{
- field1=Field1,
- field2=Field2}) ->
- SomeVariable = f(#'Some-long-record-name'{
- field_a = 1,
- 'inter-xyz-parameters' =
- #'Some-other-very-long-record-name'{
- field2 = Field1,
- field2 = Field2}}),
- {ok, SomeVariable}.
-
-commas_first() ->
- {abc, [ {some_var, 1}
- , {some_other_var, 2}
- , {erlang_ftw, 9}
- , {erlang_cookie, 'cookie'}
- , {cmds,
- [ {one, "sudo ls"}
- , {one, "sudo ls"}
- , {two, "sudo ls"}
- , {three, "sudo ls"}
- , {four, "sudo ls"}
- , {three, "sudo ls"}
- ] }
- , {ssh_username, "yow"}
- , {cluster,
- [ {aaaa, [ {"10.198.55.12" , "" }
- , {"10.198.55.13" , "" }
- ] }
- , {bbbb, [ {"10.198.55.151", "" }
- , {"10.198.55.123", "" }
- , {"10.198.55.34" , "" }
- , {"10.198.55.85" , "" }
- , {"10.198.55.67" , "" }
- ] }
- , {cccc, [ {"10.198.55.68" , "" }
- , {"10.198.55.69" , "" }
- ] }
- ] }
-]
-}.
-
-
-%% this used to result in a scan-sexp error
-[{
-}].
-
-%% this used to result in 2x the correct indentation within the function
-%% body, due to the function name being mistaken for a keyword
-catcher(N) ->
-try generate_exception(N) of
-Val -> {N, normal, Val}
-catch
-throw:X -> {N, caught, thrown, X};
-exit:X -> {N, caught, exited, X};
-error:X -> {N, caught, error, X}
-end.
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index 5517882ffa..4e64d7aa4e 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -2456,7 +2456,9 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
Pattern = {#bump{module=Module,line='$1',_='_'},'$2'},
MS = [{Pattern,[{is_integer,'$1'},{'>','$1',0}],[{{'$1','$2'}}]}],
- CovLines = lists:keysort(1,ets:select(?COLLECTION_TABLE, MS)),
+ CovLines0 =
+ lists:keysort(1, ets:select(?COLLECTION_TABLE, MS)),
+ CovLines = merge_dup_lines(CovLines0),
print_lines(Module, CovLines, InFd, OutFd, 1, HTML),
if HTML ->
@@ -2477,19 +2479,23 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
{error, {file, ErlFile, Reason}}
end.
+merge_dup_lines(CovLines) ->
+ merge_dup_lines(CovLines, []).
+merge_dup_lines([{L, N}|T], [{L, NAcc}|TAcc]) ->
+ merge_dup_lines(T, [{L, NAcc + N}|TAcc]);
+merge_dup_lines([{L, N}|T], Acc) ->
+ merge_dup_lines(T, [{L, N}|Acc]);
+merge_dup_lines([], Acc) ->
+ lists:reverse(Acc).
print_lines(Module, CovLines, InFd, OutFd, L, HTML) ->
case file:read_line(InFd) of
eof ->
ignore;
- {ok,"%"++_=Line} -> %Comment line - not executed.
- ok = file:write(OutFd, [tab(),escape_lt_and_gt(Line, HTML)]),
- print_lines(Module, CovLines, InFd, OutFd, L+1, HTML);
{ok,RawLine} ->
Line = escape_lt_and_gt(RawLine,HTML),
case CovLines of
[{L,N}|CovLines1] ->
- %% N = lists:foldl(fun([Ni], Nacc) -> Nacc+Ni end, 0, Ns),
if N=:=0, HTML=:=true ->
LineNoNL = Line -- "\n",
Str = " 0",
@@ -2508,7 +2514,7 @@ print_lines(Module, CovLines, InFd, OutFd, L, HTML) ->
ok = file:write(OutFd, [Str,fill3(),Line])
end,
print_lines(Module, CovLines1, InFd, OutFd, L+1, HTML);
- _ ->
+ _ -> %Including comment lines
ok = file:write(OutFd, [tab(),Line]),
print_lines(Module, CovLines, InFd, OutFd, L+1, HTML)
end
diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl
index 139b3d8a4a..d0152a4915 100644
--- a/lib/tools/src/lcnt.erl
+++ b/lib/tools/src/lcnt.erl
@@ -218,9 +218,11 @@ raw() -> call(raw).
set(Option, Value) -> call({set, Option, Value}).
set({Option, Value}) -> call({set, Option, Value}).
save(Filename) -> call({save, Filename}).
-load(Filename) -> ok = start_internal(), call({load, Filename}).
+load(Filename) -> call({load, Filename}).
-call(Msg) -> gen_server:call(?MODULE, Msg, infinity).
+call(Msg) ->
+ ok = start_internal(),
+ gen_server:call(?MODULE, Msg, infinity).
%% -------------------------------------------------------------------- %%
%%
@@ -237,7 +239,6 @@ apply(Fun) when is_function(Fun) ->
lcnt:apply(Fun, []).
apply(Fun, As) when is_function(Fun) ->
- ok = start_internal(),
Opt = lcnt:rt_opt({copy_save, true}),
lcnt:clear(),
Res = erlang:apply(Fun, As),
@@ -943,7 +944,7 @@ print_state_information(#state{locks = Locks} = State) ->
print(kv("#tries", s(Stats#stats.tries))),
print(kv("#colls", s(Stats#stats.colls))),
print(kv("wait time", s(Stats#stats.time) ++ " us" ++ " ( " ++ s(Stats#stats.time/1000000) ++ " s)")),
- print(kv("percent of duration", s(Stats#stats.time/State#state.duration*100) ++ " %")),
+ print(kv("percent of duration", s(percent(Stats#stats.time, State#state.duration)) ++ " %")),
ok.
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index 90e113c178..161b0105b9 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ all() ->
distribution, reconnect, die_and_reconnect,
dont_reconnect_after_stop, stop_node_after_disconnect,
export_import, otp_5031, otp_6115,
- otp_8270, otp_10979_hanging_node],
+ otp_8270, otp_10979_hanging_node, otp_14817],
case whereis(cover_server) of
undefined ->
[coverage,StartStop ++ NoStartStop];
@@ -1574,6 +1574,30 @@ otp_10979_hanging_node(_Config) ->
ok.
+otp_14817(Config) when is_list(Config) ->
+ Test = <<"-module(otp_14817).
+ -export([a/0, b/0, c/0, d/0]).
+ a() -> ok. b() -> ok. c() -> ok.
+ d() -> ok.
+ ">>,
+ File = cc_mod(otp_14817, Test, Config),
+ ok = otp_14817:a(),
+ ok = otp_14817:b(),
+ ok = otp_14817:c(),
+ ok = otp_14817:d(),
+ {ok,[{{otp_14817,3},1},
+ {{otp_14817,3},1},
+ {{otp_14817,3},1},
+ {{otp_14817,4},1}]} =
+ cover:analyse(otp_14817, calls, line),
+ {ok, CovOut} = cover:analyse_to_file(otp_14817),
+ {ok, Bin} = file:read_file(CovOut),
+ <<"3..|",_/binary>> = string:find(Bin, "3..|"),
+ <<"1..|",_/binary>> = string:find(Bin, "1..|"),
+ ok = file:delete(File),
+ ok = file:delete(CovOut),
+ ok.
+
%% Take compiler options from beam in cover:compile_beam
compile_beam_opts(Config) when is_list(Config) ->
{ok, Cwd} = file:get_cwd(),
diff --git a/lib/tools/test/emacs_SUITE.erl b/lib/tools/test/emacs_SUITE.erl
index 77a8813db5..f4e78da667 100644
--- a/lib/tools/test/emacs_SUITE.erl
+++ b/lib/tools/test/emacs_SUITE.erl
@@ -23,10 +23,10 @@
-export([all/0, init_per_testcase/2, end_per_testcase/2]).
--export([bif_highlight/1]).
+-export([bif_highlight/1, indent/1]).
-all() ->
- [bif_highlight].
+all() ->
+ [bif_highlight, indent].
init_per_testcase(_Case, Config) ->
ErlangEl = filename:join([code:lib_dir(tools),"emacs","erlang.el"]),
@@ -74,4 +74,69 @@ check_bif_highlight(Bin, Tag, Compare) ->
[] = Compare -- EmacsIntBifs,
[] = EmacsIntBifs -- Compare.
-
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+indent(Config) ->
+ case emacs_version_ok() of
+ false -> {skip, "Old or no emacs found"};
+ true ->
+ Def = filename:dirname(code:which(?MODULE)) ++ "/" ++ ?MODULE_STRING ++ "_data",
+ Dir = proplists:get_value(data_dir, Config, Def),
+ OrigFs = filelib:wildcard(Dir ++ "/*"),
+ io:format("Dir: ~s~nFs: ~p~n", [Dir, OrigFs]),
+ Fs = [{File, unindent(File)} || File <- OrigFs,
+ filename:extension(File) =:= ""],
+ Indent = fun emacs/1,
+ [Indent(File) || {_, File} <- Fs],
+ Res = [diff(Orig, File) || {Orig, File} <- Fs],
+ [file:delete(File) || {ok, File} <- Res], %% Cleanup
+ [] = [Fail || {fail, Fail} <- Res],
+ ok
+ end.
+
+unindent(Input) ->
+ Output = Input ++ ".erl",
+ {ok, Bin} = file:read_file(Input),
+ Lines0 = string:split(Bin, "\n", all),
+ Lines = [string:trim(Line, leading, [$\s,$\t]) || Line <- Lines0],
+ %% io:format("File: ~s lines: ~w~n", [Input, length(Lines0)]),
+ %% [io:format("~s~n", [L]) || L <- Lines],
+ ok = file:write_file(Output, lists:join("\n", Lines)),
+ Output.
+
+diff(Orig, File) ->
+ case os:cmd(["diff ", Orig, " ", File]) of
+ "" -> {ok, File};
+ Diff ->
+ io:format("Fail: ~s vs ~s~n~s~n~n",[Orig, File, Diff]),
+ {fail, File}
+ end.
+
+emacs_version_ok() ->
+ case os:cmd("emacs --version | head -1") of
+ "GNU Emacs " ++ Ver ->
+ case string:to_float(Ver) of
+ {Vsn, _} when Vsn >= 24.1 ->
+ true;
+ _ ->
+ io:format("Emacs version fail~n~s~n~n",[Ver]),
+ false
+ end;
+ Res ->
+ io:format("Emacs version fail~n~s~n~n",[Res]),
+ false
+ end.
+
+emacs(File) ->
+ EmacsErlDir = filename:join([code:lib_dir(tools), "emacs"]),
+ Cmd = ["emacs ",
+ "--batch --quick ",
+ "--directory ", EmacsErlDir, " ",
+ "--eval \"(require 'erlang-start)\" ",
+ File, " ",
+ "--eval '(indent-region (point-min) (point-max) nil)' ",
+ "--eval '(save-buffer 0)'"
+ ],
+ _Res = os:cmd(Cmd),
+ % io:format("cmd ~s:~n=> ~s~n", [Cmd, _Res]),
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/comments b/lib/tools/test/emacs_SUITE_data/comments
new file mode 100644
index 0000000000..ff974ca295
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/comments
@@ -0,0 +1,25 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% 3 comment chars: always left indented
+%%% 2 comment chars: Context indented
+%%% 1 comment char: Rigth indented
+
+%%% left
+%% context dependent
+ % rigth
+
+func() ->
+%%% left
+ %% context dependent
+ % right indented
+ case get(foo) of
+ undefined ->
+ %% Testing indention
+ ok;
+ %% Catch all
+ Other ->
+ Other
+ end,
+ ok.
+
diff --git a/lib/tools/test/emacs_SUITE_data/comprehensions b/lib/tools/test/emacs_SUITE_data/comprehensions
new file mode 100644
index 0000000000..45279850a5
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/comprehensions
@@ -0,0 +1,47 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of comprehensions
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+list() ->
+ %% I don't have a good idea how we want to handle this
+ %% but they are here to show how they are indented today.
+ Result1 = [X ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+ Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ ],
+ Res = [ func(X,
+ arg2)
+ ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+ Result1.
+
+binary(B) ->
+ Binary1 = << <<X:8>> ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ >>,
+
+ Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+
+ Bin3 = <<
+ <<
+ X:8,
+ 34:8
+ >>
+ || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/funcs b/lib/tools/test/emacs_SUITE_data/funcs
new file mode 100644
index 0000000000..877f982005
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/funcs
@@ -0,0 +1,174 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Function (and funs) indentation
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+-export([
+ func1/0,
+ func2/0,
+ a_function_with_a_very_very_long_name/0,
+ when1/2
+ ]).
+
+-compile([nowarn_unused_functions,
+ {inline, [
+ func2/2,
+ func3/2
+ ]
+ }
+ ]).
+
+func1() ->
+ basic.
+
+func2(A1,
+ A2) ->
+ ok.
+
+func3(
+ A1,
+ A2
+ ) ->
+ ok.
+
+%% Okeefe style
+func4(A1
+ ,A2
+ ,A3
+ ) ->
+ ok.
+
+func5(
+ A41
+ ,A42) ->
+ ok.
+
+a_function_with_a_very_very_long_name() ->
+ A00 = #record{
+ field1=1,
+ field2=1
+ },
+ A00.
+
+when1(W1, W2)
+ when is_number(W1),
+ is_number(W2) ->
+ ok.
+
+when2(W1,W2,W3) when
+ W1 > W2,
+ W2 > W3 ->
+ ok.
+
+when3(W1,W2,W3) when
+ W1 > W2,
+ W2 > W3
+ ->
+ ok.
+
+when4(W1,W2,W3)
+ when
+ W1 > W2,
+ W2 > W3 ->
+ ok.
+
+match1({[H|T],
+ Other},
+ M1A2) ->
+ ok.
+
+match2(
+ {
+ [H|T],
+ Other
+ },
+ M2A2
+ ) ->
+ ok.
+
+match3({
+ M3A1,
+ [
+ H |
+ T
+ ],
+ Other
+ },
+ M3A2
+ ) ->
+ ok.
+
+match4(<<
+ M4A:8,
+ M4B:16/unsigned-integer,
+ _/binary
+ >>,
+ M4C) ->
+ ok.
+
+match5(M5A,
+ #record{
+ b=M5B,
+ c=M5C
+ }
+ ) ->
+ ok.
+
+match6(M6A,
+ #{key6a := a6,
+ key6b := b6
+ }) ->
+ ok.
+
+funs(1)
+ when
+ X ->
+ %% Changed fun to one indention level
+ %% 'when' and several clause forces a depth of '4'
+ Var = spawn(fun(X, _)
+ when X == 2;
+ X > 10 ->
+ hello,
+ case Hello() of
+ true when is_atom(X) ->
+ foo;
+ false ->
+ bar
+ end;
+ (Foo) when is_atom(Foo),
+ is_integer(X) ->
+ X = 6 * 45,
+ Y = true andalso
+ kalle
+ end),
+ Var;
+funs(2) ->
+ %% check EEP37 named funs
+ Fn1 = fun
+ Factory(N) when
+ N > 0 ->
+ F = Fact(N-1),
+ N * F;
+ Factory(0) ->
+ 1
+ end,
+ Fn1;
+funs(3) ->
+ %% check anonymous funs too
+ Fn2 = fun(0) ->
+ 1;
+ (N) ->
+ N
+ end,
+ ok;
+funs(4) ->
+ X = lists:foldr(fun(M) ->
+ <<M/binary, " ">>
+ end, [], Z),
+ A = <<X/binary, 0:8>>,
+ A.
diff --git a/lib/tools/test/emacs_SUITE_data/highlight b/lib/tools/test/emacs_SUITE_data/highlight
new file mode 100644
index 0000000000..0719f6516a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/highlight
@@ -0,0 +1,78 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Open this file in your editor and manually check the colors of
+%%% different types and calls and builtin words
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+
+highlighting(X) % Function definitions should be highlighted
+ when is_integer(X) -> % and so should `when' and `is_integer' be
+ %% Highlighting
+ %% Various characters (we keep an `atom' after to see that highlighting ends)
+ $a,atom, % Characters should be marked
+ "string",atom, % and strings
+ 'asdasd',atom, % quote should be atoms??
+ 'VaV',atom,
+ 'aVa',atom,
+ '\'atom',atom,
+ 'atom\'',atom,
+ 'at\'om',atom,
+ '#1',atom,
+
+ $", atom, % atom should be ok
+ $', atom,
+
+ "string$", atom, "string$", atom, % currently buggy I know...
+ "string\$", atom, % workaround for bug above
+
+ "char $in string", atom,
+
+ 'atom$', atom, 'atom$', atom,
+ 'atom\$', atom,
+
+ 'char $in atom', atom,
+
+ $[, ${, $\\, atom,
+ ?MACRO_1,
+ ?MACRO_2(foo),
+
+ %% Numerical constants
+ 16#DD, % Should not be highlighted
+ 32#dd, % Should not be highlighted
+ 32#ddAB, % Should not be highlighted
+ 32#101, % Should not be highlighted
+ 32#ABTR, % Should not be highlighted
+
+ %% Variables
+ Variables = lists:foo(),
+ _Variables = lists:foo(),
+ AppSpec = Xyz/2,
+ Module42 = Xyz(foo, bar),
+ Module:foo(),
+ _Module:foo(), %
+ FooÅÅ = lists:reverse([tl,hd,tl,hd]), % Should highlight FooÅÅ
+ _FooÅÅ = 42, % Should highlight _FooÅÅ
+
+ %% Bifs
+ erlang:registered(),
+ registered(),
+ hd(tl(tl(hd([a,b,c])))),
+ erlang:anything(lists),
+ %% Guards
+ is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
+ is_function(Fun), is_pid(self()),
+ not_a_guard:is_list([]),
+ %% Other Types
+
+ atom, % not (currently) hightlighted
+ 234234,
+ 234.43,
+
+ [list, are, not, higlighted],
+ {nor, is, tuple},
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/icr b/lib/tools/test/emacs_SUITE_data/icr
new file mode 100644
index 0000000000..8445c1a74d
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/icr
@@ -0,0 +1,157 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of if case receive statements
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+indent_if(1, Z) ->
+ %% If
+ if Z >= 0 ->
+ X = 43 div Z,
+ X;
+ Z =< 10 ->
+ X = 43 div Z,
+ X;
+ Z == 5 orelse
+ Z == 7 ->
+ X = 43 div Z,
+ X;
+ is_number(Z),
+ Z < 32 ->
+ Z;
+ is_number(Z);
+ Z < 32 ->
+ Z * 32;
+ true ->
+ if_works
+ end;
+indent_if(2, Z) ->
+ %% If
+ if
+ Z >= 0 ->
+ X = 43 div Z,
+ X
+ ; Z =< 10 ->
+ 43 div Z
+ ; Z == 5 orelse
+ Z == 7 ->
+ X = 43 div Z,
+ X
+ ; is_number(Z),
+ Z < 32 ->
+ Z
+ ; true ->
+ if_works
+ end.
+
+indent_case(1, Z) ->
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_} when
+ Z =:= 42 -> % line should be indented as a when
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when Z < 10 orelse
+ Z =:= foo -> % Binary op alignment here !!!
+ X = 43 div 4,
+ Bool = Z < 5 orelse % Binary op args align differently after when
+ Z =:= foo, % and elsewhere ???
+ foo(X);
+ {Z,_,_}
+ when % when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end;
+indent_case(2, Z) ->
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_} when
+ Z =:= 42 -> % line should be indented as a when
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_}
+ when Z < 10 -> % when should be indented
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_}
+ when % when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end.
+
+indent_begin(Z) ->
+ %% Begin
+ begin
+ sune,
+ Z = 74234 +
+ foo(8456) +
+ 345 div 43,
+ Foo = begin
+ ok,
+ foo(234),
+ begin
+ io:format("Down here\n")
+ end
+ end,
+ {Foo,
+ bar}
+ end.
+
+indent_receive(1) ->
+ %% receive
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X)
+ ; Z ->
+ X = 43 div 4,
+ foo(X)
+ end,
+ ok;
+indent_receive(2) ->
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z % added clause
+ when Z =:= 1 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z when % added clause
+ Z =:= 2 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
+ after infinity ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok;
+indent_receive() ->
+ receive
+ after 10 ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/macros b/lib/tools/test/emacs_SUITE_data/macros
new file mode 100644
index 0000000000..6c874e9187
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/macros
@@ -0,0 +1,31 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Macros should be indented as code
+
+-define(M0, ok).
+
+-define(M1,
+ case X of
+ undefined -> error;
+ _ -> ok
+ end).
+
+-define(M2(M2A1,
+ M2A2),
+ func(M2A1,
+ M2A2)
+ ).
+
+-define(
+ M3,
+ undefined
+ ).
+
+-ifdef(DEBUG).
+-define(LOG,
+ logger:log(?MODULE,?LINE)
+ ).
+-else().
+-define(LOG, ok).
+-endif().
diff --git a/lib/tools/test/emacs_SUITE_data/records b/lib/tools/test/emacs_SUITE_data/records
new file mode 100644
index 0000000000..241582718c
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/records
@@ -0,0 +1,35 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%% Test that records are indented correctly
+
+-record(record0,
+ {
+ r0a,
+ r0b,
+ r0c
+ }).
+
+-record(record1, {r1a,
+ r1b,
+ r1c
+ }).
+
+-record(record2, {
+ r2a,
+ r2b
+ }).
+
+-record(record3, {r3a = 8#42423 bor
+ 8#4234,
+ r3b = 8#5432
+ bor 2#1010101,
+ r3c = 123 +
+ 234,
+ r3d}).
+
+-record(record5,
+ { r5a = 1 :: integer()
+ , r5b = foobar :: atom()
+ }).
+
diff --git a/lib/tools/test/emacs_SUITE_data/terms b/lib/tools/test/emacs_SUITE_data/terms
new file mode 100644
index 0000000000..352364a73c
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/terms
@@ -0,0 +1,174 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of terms contain builtin types
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+
+list(1) ->
+ [a,
+ b,
+ c
+ ];
+list(2) ->
+ [ a,
+ b, c
+ ];
+list(3) ->
+ [
+ a,
+ b, c
+ ];
+list(4) ->
+ [ a
+ , b
+ , c
+ ].
+
+tuple(1) ->
+ {a,
+ b,c
+ };
+tuple(2) ->
+ { a,
+ b,c
+ };
+tuple(3) ->
+ {
+ a,
+ b,c
+ };
+tuple(4) ->
+ { a
+ , b
+ ,c
+ }.
+
+binary(1) ->
+ <<1:8,
+ 2:8
+ >>;
+binary(2) ->
+ <<
+ 1:8,
+ 2:8
+ >>;
+binary(3) ->
+ << 1:8,
+ 2:8
+ >>;
+binary(4) ->
+ <<
+ 1:8
+ ,2:8
+ >>;
+binary(5) ->
+ << 1:8
+ , 2:8
+ >>.
+
+record(1) ->
+ #record{a=1,
+ b=2
+ };
+record(2) ->
+ #record{ a=1,
+ b=2
+ };
+record(3) ->
+ #record{
+ a=1,
+ b=2
+ };
+record(4) ->
+ #record{
+ a=1
+ ,b=2
+ };
+record(Record) ->
+ Record#record{
+ a=1
+ ,b=2
+ }.
+
+map(1) ->
+ #{a=>1,
+ b=>2
+ };
+map(2) ->
+ #{ a=>1,
+ b=>2
+ };
+map(3) ->
+ #{
+ a=>1,
+ b=>2
+ };
+map(4) ->
+ #{
+ a => <<"a">>
+ ,b => 2
+ };
+map(MapVar) ->
+ MapVar = #{a :=<<"a">>
+ ,b:=1}.
+
+deep(Rec) ->
+ Rec#rec{ atom = 'atom',
+ map = #{ k1 => {v,
+ 1},
+ k2 => [
+ 1,
+ 2,
+ 3
+ ],
+ {key,
+ 3}
+ =>
+ <<
+ 123:8,
+ 255:8
+ >>
+ }
+ }.
+
+%% Record indentation
+some_function_with_a_very_long_name() ->
+ #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b},
+ case dummy_function_with_a_very_very_long_name(x) of
+ #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ ok;
+ Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b};
+ #xyz{
+ a=1,
+ b=2} ->
+ ok
+ end.
+
+some_function_name_xyz(xyzzy, #some_record{
+ field1=Field1,
+ field2=Field2}) ->
+ SomeVariable = f(#'Some-long-record-name'{
+ field_a = 1,
+ 'inter-xyz-parameters' =
+ #'Some-other-very-long-record-name'{
+ field2 = Field1,
+ field2 = Field2}}),
+ {ok, SomeVariable}.
+
+foo() ->
+ [#foo{
+ foo = foo}].
diff --git a/lib/tools/test/emacs_SUITE_data/try_catch b/lib/tools/test/emacs_SUITE_data/try_catch
new file mode 100644
index 0000000000..0005b2003a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/try_catch
@@ -0,0 +1,166 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Try and catch indentation is hard
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+try_catch() ->
+ try
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R
+ when R =:= 42 -> % when should be indented
+ foo(R);
+ error:R
+ when % when should be indented
+ R =:= 42 -> % but unsure about this (maybe 2 more)
+ foo(R);
+ error:R when
+ R =:= foo -> % line should be 2 indented (works)
+ foo(R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile)
+ end;
+try_catch() ->
+ try
+ foo(bar)
+ of
+ X when true andalso
+ kalle ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2);
+ X
+ when false andalso % when should be 2 indented
+ bengt ->
+ gurka();
+ X when
+ false andalso % line should be 2 indented
+ not bengt ->
+ gurka();
+ X ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile),
+ bar(with_long_arg,
+ with_second_arg)
+ end;
+try_catch() ->
+ try foo()
+ after
+ foo(),
+ bar(with_long_arg,
+ with_second_arg)
+ end.
+
+indent_catch() ->
+ D = B +
+ float(43.1),
+
+ B = catch oskar(X),
+
+ A = catch (baz +
+ bax),
+ catch foo(),
+
+ C = catch B +
+ float(43.1),
+
+ case catch foo(X) of
+ A ->
+ B
+ end,
+
+ case
+ catch foo(X)
+ of
+ A ->
+ B
+ end,
+
+ case
+ foo(X)
+ of
+ A ->
+ catch B,
+ X
+ end,
+
+ try sune of
+ _ -> foo
+ catch _:_ -> baf
+ end,
+
+ Variable = try
+ sune
+ of
+ _ ->
+ X = 5,
+ (catch foo(X)),
+ X + 10
+ catch _:_ -> baf
+ after cleanup()
+ end,
+
+ try
+ (catch sune)
+ of
+ _ ->
+ foo1(),
+ catch foo() %% BUGBUG can't handle catch inside try without parentheses
+ catch _:_ ->
+ baf
+ end,
+
+ try
+ (catch exit())
+ catch
+ _ ->
+ catch baf()
+ end,
+ ok.
+
+%% this used to result in 2x the correct indentation within the function
+%% body, due to the function name being mistaken for a keyword
+catcher(N) ->
+ try generate_exception(N) of
+ Val -> {N, normal, Val}
+ catch
+ throw:X -> {N, caught, thrown, X};
+ exit:X -> {N, caught, exited, X};
+ error:X -> {N, caught, error, X}
+ end.
diff --git a/lib/tools/test/emacs_SUITE_data/type_specs b/lib/tools/test/emacs_SUITE_data/type_specs
new file mode 100644
index 0000000000..e71841cc7a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/type_specs
@@ -0,0 +1,110 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%% Tests how types and specs are indented (also that the editor can parse them)
+%% May need improvements
+
+
+-type ann() :: Var :: integer().
+-type ann2() ::
+ 'return'
+ | 'return_white_spaces'
+ | 'return_comments'
+ | 'text' | ann().
+-type paren() ::
+ (ann2()).
+
+-type t6() ::
+ 1 | 2 | 3 |
+ 'foo'
+ | 'bar'.
+
+-type t8() :: {any(),none(),pid(),port(),
+ reference(),float()}.
+
+-type t14() :: [erl_scan:foo() |
+ %% Should be highlighted
+ term() |
+ boolean() |
+ byte() |
+ char() |
+ non_neg_integer() | nonempty_list() |
+ pos_integer() |
+ neg_integer() |
+ number() |
+ list() |
+ nonempty_improper_list() | nonempty_maybe_improper_list() |
+ maybe_improper_list() | string() | iolist() | byte() |
+ module() |
+ mfa() |
+ node() |
+ timeout() |
+ no_return() |
+ %% Should not be highlighted
+ nonempty_() | nonlist() |
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+
+-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
+ <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
+ <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
+ <<_:34>>|<<_:34>>|<<_:34>>].
+
+-type t18() ::
+ fun(() -> t17() | t16()).
+-type t19() ::
+ fun((t18()) -> t16()) |
+ fun((nonempty_maybe_improper_list('integer', any())|
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer())
+ ->
+ nonempty_maybe_improper_list('integer', any())| %% left to col 16?
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer()). %% left to col 16?
+-type t20() :: [t19(), ...].
+-type t25() :: #rec3{f123 :: [t24() |
+ 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())]}.
+-type t26() :: #rec4{ a :: integer()
+ , b :: any()
+ }.
+
+%% Spec
+
+-spec t1(FooBar :: t99()) -> t99();
+ (t2()) -> t2();
+ (t4()) -> t4() when is_subtype(t4(), t24);
+ (t23()) -> t23() when is_subtype(t23(), atom()),
+ is_subtype(t23(), t14());
+ (t24()) -> t24() when is_subtype(t24(), atom()),
+ is_subtype(t24(), t14()),
+ is_subtype(t24(), t4()).
+
+-spec over(I :: integer()) -> R1 :: foo:typen();
+ (A :: atom()) -> R2 :: foo:atomen();
+ (T :: tuple()) -> R3 :: bar:typen().
+
+-spec mod:t2() -> any().
+
+-spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) -> {'noreply', #state{}}.
+
+-spec handle_cast(Cast ::
+ {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) ->
+ {'noreply', #state{}}. %% left to col 10?
+
+-spec all(fun((T) -> boolean()), List :: [T]) ->
+ boolean() when is_subtype(T, term()). % (*)
+
+-spec get_closest_pid(term()) ->
+ Return :: pid() %% left to col 10?
+ | {'error', {'no_process', term()}} %% left to col 10?
+ | {'no_such_group', term()}. %% left to col 10?
+
+-spec add( X :: integer()
+ , Y :: integer()
+ ) -> integer().
+
+-opaque attributes_data() ::
+ [{'column', column()} | {'line', info_line()} |
+ {'text', string()}] | {line(),column()}.
diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index 146c915087..a79572a742 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -30,6 +30,8 @@
t_conflicts/1,
t_locations/1,
t_swap_keys/1,
+ t_implicit_start/1,
+ t_crash_before_collect/1,
smoke_lcnt/1]).
init_per_testcase(_Case, Config) ->
@@ -44,8 +46,8 @@ suite() ->
{timetrap,{minutes,4}}].
all() ->
- [t_load, t_conflicts, t_locations, t_swap_keys,
- smoke_lcnt].
+ [t_load, t_conflicts, t_locations, t_swap_keys, t_implicit_start,
+ t_crash_before_collect, smoke_lcnt].
%%----------------------------------------------------------------------
%% Tests
@@ -149,6 +151,15 @@ t_swap_keys_file([File|Files]) ->
ok = lcnt:stop(),
t_swap_keys_file(Files).
+%% Prior to OTP-14913 this would crash with 'noproc' as the lcnt server hadn't
+%% been started yet.
+t_implicit_start(Config) when is_list(Config) ->
+ ok = lcnt:conflicts().
+
+t_crash_before_collect(Config) when is_list(Config) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:information().
+
%% Simple smoke test of actual lock-counting, if running on
%% a run-time with lock-counting enabled.
smoke_lcnt(Config) ->
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index b9249ae45c..6cafbca6a7 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.11
+TOOLS_VSN = 2.11.1
diff --git a/lib/wx/c_src/wxe_driver.c b/lib/wx/c_src/wxe_driver.c
index 5da71818e5..26ae3564e7 100644
--- a/lib/wx/c_src/wxe_driver.c
+++ b/lib/wx/c_src/wxe_driver.c
@@ -33,6 +33,7 @@
#include <sys/types.h>
#include <fcntl.h>
+#include <assert.h>
#include "wxe_driver.h"
#define TEMP_BINARY_SIZE 512
@@ -234,13 +235,21 @@ standard_outputv(ErlDrvData drv_data, ErlIOVec* ev)
sd->max_bins = max;
}
- if(ev->vsize == 2) {
- binref->base = ev->iov[1].iov_base;
- binref->size = ev->iov[1].iov_len;
- binref->from = driver_caller(sd->port_handle);
- bin = ev->binv[1];
- driver_binary_inc_refc(bin); /* Otherwise it could get deallocated */
- binref->bin = bin;
+ if(ev->size > 0) {
+ assert(ev->vsize == 2 && ev->iov[0].iov_len == 0
+ && "erts changed how the ErlIOVec is structured for outputv");
+ binref->from = driver_caller(sd->port_handle);
+ binref->size = ev->iov[1].iov_len;
+ if(ev->binv[1]) {
+ binref->base = ev->iov[1].iov_base;
+ bin = ev->binv[1];
+ driver_binary_inc_refc(bin); /* Otherwise it could get deallocated */
+ } else {
+ bin = driver_alloc_binary(ev->iov[1].iov_len);
+ memcpy(bin->orig_bytes, ev->iov[1].iov_base, ev->iov[1].iov_len);
+ binref->base = bin->orig_bytes;
+ }
+ binref->bin = bin;
} else { /* Empty binary (becomes NULL) */
binref->base = NULL;
binref->size = 0;
diff --git a/lib/wx/doc/src/Makefile b/lib/wx/doc/src/Makefile
index 23890f47f0..a76740adf1 100644
--- a/lib/wx/doc/src/Makefile
+++ b/lib/wx/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2016. All Rights Reserved.
+# Copyright Ericsson AB 2008-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ ModsNoExt = $(ErlMods:%.erl=%) $(GenMods:%.erl=%)
XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = $(ErlMods:%.erl=%.xml) $(GenMods:%.erl=%.xml)
-XML_PART_FILES = part.xml part_notes.xml
+XML_PART_FILES = part.xml
XML_CHAPTER_FILES = chapter.xml
XML_NOTES_FILES = notes.xml
diff --git a/lib/wx/doc/src/fascicules.xml b/lib/wx/doc/src/fascicules.xml
deleted file mode 100644
index 154c8a3b6d..0000000000
--- a/lib/wx/doc/src/fascicules.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
-</fascicules>
-
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 599b5b64fd..69ea906ec0 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 1.8.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ wx crashes in otp 20.1 if empty binaries was sent down as
+ arguments.</p>
+ <p>
+ Own Id: OTP-14688</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 1.8.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/wx/doc/src/part_notes.xml b/lib/wx/doc/src/part_notes.xml
deleted file mode 100644
index 5a5a6494c1..0000000000
--- a/lib/wx/doc/src/part_notes.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2009</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>wxErlang Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p>The <em>wxErlang</em> application is an api for writing graphical user
- interfaces with wxWidgets.
- </p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index 039fae322e..7da4529c98 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.8.2
+WX_VSN = 1.8.3
diff --git a/lib/xmerl/doc/src/Makefile b/lib/xmerl/doc/src/Makefile
index 2465217e8e..7d0b0b2392 100644
--- a/lib/xmerl/doc/src/Makefile
+++ b/lib/xmerl/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2016. All Rights Reserved.
+# Copyright Ericsson AB 2004-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -59,9 +59,8 @@ XMERL_XML_FILES = $(XMERL_MODULES:=.xml)
XML_REF3_FILES = $(XMERL_XML_FILES) \
xmerl_sax_parser.xml
-XML_PART_FILES = \
- part.xml \
- part_notes.xml
+XML_PART_FILES = \
+ part.xml
XML_REF6_FILES =
@@ -107,10 +106,10 @@ TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -120,14 +119,14 @@ $(HTMLDIR)/%.gif: %.gif
docs: pdf html man
-$(TOP_PDF_FILE): $(XML_FILES)
+$(TOP_PDF_FILE): $(XML_FILES)
pdf: $(TOP_PDF_FILE)
html: gifs $(HTML_REF_MAN_FILE)
-$(XMERL_XML_FILES):
- escript $(DOCGEN)/priv/bin/xml_from_edoc.escript $(XMERL_DIR)/$(@:%.xml=%.erl)
+$(XMERL_XML_FILES):
+ escript $(DOCGEN)/priv/bin/xml_from_edoc.escript $(XMERL_DIR)/$(@:%.xml=%.erl)
man: $(MAN3_FILES) $(MAN6_FILES)
@@ -135,15 +134,15 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
xml: $(XMERL_XML_FILES)
-debug opt:
+debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(MAN6DIR)/*
rm -f $(XMERL_XML_FILES)
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
+ rm -f errs core *~
info:
@@ -158,7 +157,7 @@ info:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -177,6 +176,3 @@ release_spec:
release_tests_spec:
-
-
-
diff --git a/lib/xmerl/doc/src/fascicules.xml b/lib/xmerl/doc/src/fascicules.xml
deleted file mode 100644
index 37feca543f..0000000000
--- a/lib/xmerl/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index 1162561225..f62a8dc53d 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -32,6 +32,21 @@
<p>This document describes the changes made to the Xmerl application.</p>
+<section><title>Xmerl 1.3.16</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Removed all old unused files in the documentation.
+ </p>
+ <p>
+ Own Id: OTP-14475 Aux Id: ERL-409, PR-1493 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Xmerl 1.3.15</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/xmerl/doc/src/part_notes.xml b/lib/xmerl/doc/src/part_notes.xml
deleted file mode 100644
index 4ed441c7d4..0000000000
--- a/lib/xmerl/doc/src/part_notes.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2004</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Xmerl Release Notes</title>
- <prepared>Bertil Karlsson</prepared>
- <docno></docno>
- <date>>2004-12-15</date>
- <rev></rev>
- <file>part_notes.xml</file>
- </header>
- <description>
- <p>The <em>Xmerl</em> application
- contains modules with support for processing of xml files compliant to XML 1.0.</p>
- <p>There are also release notes for
- <url href="notes_history.html">older versions</url>.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index 2e9c9061d9..ddff0c8894 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.3.15
+XMERL_VSN = 1.3.16